########Vue3基础部分

一:ES6新特性

栈内存:存放基本数据类型的变量名和变量值;复杂(引用)数据类型的变量名和引用地址
堆内存:复杂数据类型的变量值

1.let命令
var 和 let的区别(var的三大bug):

  • 作用域:var变量全局范围内有效,let变量仅在块级作用域内有效
  • 变量提升:var会发生变量提升现象,而let不会,即let必须先定义再使用
  • 变量重复声明:var在相同作用域内可以被重复声明,而let不允许重复声明

2.const命令
是一个只读的常量,声明时,必须赋初值,一旦声明,栈值就不能改变,

  • 对于基本数据类型,声明后值是不能改变的
  • 对于复杂数据类型,栈内的地址不能变,数据值(堆内存)是可以变的

3.变量的结构赋值
包括数组和对象的结构赋值

  • 数组: let [a, b, c] = [10, 20, 30];

  • 对象:let { name1, age1 } = { name1: ‘李四’, age1: 29 };
    4.对象的扩展
    包括属性的简写,方法的简写,扩展运算符,模版字符串

  • 属性的简写::当属性名和值的变量名相同时,触发对象的属性简写let name = ‘张三’; const person ={name}

  • 方法的简写:去掉冒号和function,
    eat() {
    return ‘三顿’
    }

  • 扩展运算符:三个点(…).可遍历属性。.三个点放在对象前,用于对象的合并等
    let z = { a: 10, b: 20 }
    let n = { …z, c: 30 };
    console.log(n) // {a: 10, b: 20, c: 30}

  • 模版字符串:模板字符串用反引号()表示,嵌入变量用${}表示 let idCard = '360424198612121539'; let str = 身份证号码${idCard} `
    5.箭头函数
    主要针对的是函数变量,即带有函数名的函数;使用箭头( => )连接参数列表和函数体
    箭头函数的简化:

  • 参数只有一个时,括号可以省略
    const say = msg => {
    函数体
    }

  • 无参数时,括号不能省略
    const say = () => {
    函数体
    }

  • 参数多个时,括号不能省略
    const say = (x,y) => {
    函数体
    }

  • 可变参数,括号不能省略
    const say = (…args) => {
    函数体
    }

  • 函数体只有一条return时,可以省略大括号和return关键字
    const say2 = x =>x+1

  • 函数体包含多条语句时,不可以省略大括号和return关键字

  • 函数体中有一个对象时,省略大括号和return关键字后需要用小括号
    -const student2 = () => ({ name: ‘张三’, age: 25 })

6.模块语法

  • export:规定模块的对外接口,如果希望外部能够获取模块内的某个变量/函数/类,就必须在模块中使用export输出该变量/函数/类
    const PI = 3.1415
    const add = (a, b) => a + b
    export { PI as pi, add } //可以合在一起导出,也可以分别导出

  • import:引入模块
    //import { pi as PI, add } from ‘./demo1.js’
    import * as abc from ‘./demo1.js’ // 导入全部
    console.log(‘pi的值为:’, abc.pi)
    console.log(‘调用add函数:’, abc.add(40, 50))

  • export default:每个模块【只能够使用一次】,可以导出变量,常量,函数,类,匿名函数
    export default (a, b) => a + b //默认导出的是匿名函数
    对于export default导出的,导入时,不需要{},并且变量名可以随意
    import 你家的孩子 from ‘./demo2.js’
    console.log(‘变量值为:’, 你家的孩子(3, 6))
    7.Promise异步编程
    Promise对象要点:
    原理:异步执行,当接口请求后,可以去做其他事情,等接口返回成功再渲染页面,接口返回失败也会有相应的提示

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>8-1 promise对象.html</title>
</head>

<body>
    <script>
        /*
        Promise对象要点:
        1.Promise对象是用于执行异步操作的
        2.可以使用Promise构造函数来创建实例对象
        3.Promise对象的.then方法调用后,返回的还是一个Promise对象
        4.Promise对象的.catch方法调用后,返回的还是一个Promise对象
        */
        /*
        步骤1:创建Promise对象
        1.调用构造函数,来创建Promise对象
        2.构造函数的参数,是一个函数【参数是函数】,它负责执行业务
        3.该函数接受2个参数作为参数
        4.第一个参数:是resolve(成功的)函数,可以是其他
        5.第二个参数:是reject(拒绝的)函数
        */
        const p1 = new Promise((resolve, reject) => {
            console.log('1.异步请求开始,异步讨债开始')
            // 去讨债开始......
            // ......漫长的过程,....可以去干其它事情....  
            // 讨债结束.....
            const flag = false
            if (flag) { //resolve方法执行后,则执行then的第一个参数
                return resolve('还钱成功!')//异步调用,只支持一个参数(可以是一个对象)
            } else {
                return reject('还钱失败!')//它们前面加上return语句,这样就不会有意外。
            }
        })       
        /*
        要点:
        1.then方法,也接收2个函数作为参数,但第二个参数是可选的
        2.当resolve方法调用时,会触发then的第一个参数
        3.当rreject方法调用时,会触发then的第二个参数
        */
        //步骤2:调用异步执行.then方法
        const p2 = p1.then((a) => {
            // throw new Error('4.人为模拟异常,还的钱是假钱...')
            console.log('3.进来了then的第一个方法,还钱成功了,用钱干什么,买手机。。。', a)

        }
            // , () => {//第二个参数是可选的
            //     console.log('3.进来了then的第二个方法,,还钱失败了,怎么办。。。')
            // }
        )//还可以继续.then

        /*
            要点:
            1.catch方法,也接收1个函数作为参数
            2.当【前面】的方法中有异常时,才会触发catch的参数
            */
        // 步骤3.调用catch方法,then方法有效
        const p3 = p2.catch((e) => {
            console.log(`catch方法被调用了${e}`)
        }).finally(() => { //无论如何都会执行
            console.log('finally')

        })

        console.log('2.底部的代码调用了,将在当前脚本所有同步任务执行完才会执行点then异步代码')

//Promise 新建后立即执行,所以首先输出的是Promise。
//然后,then方法指定的回调函数,将在当前脚本所有同步任务执行完才会执行,所以resolved最后输出。
    </script>
</body>
</html>

二:创建vue3.0项目

1.在本地电脑创建一个文件夹
2.终端进入文件夹,创建项目:npm create vue@3,命名项目名称,出现如下界面
在这里插入图片描述
3.VSCode编辑器中,在终端执行一下命令,初始化并启动项目

  • cd study_vue #进入到项目根目录
  • npm install #安装项目依赖
  • npm run dev #启动项目

4.页面执行顺序
创建一个vue组件,main.js中导入该vue组件,挂载文件index.html 就可以找到该组件并运行在浏览器中

//main.js文件
import { createApp } from 'vue'   //不是路径的话,必须package.json中定义,dependencies
import { createPinia } from 'pinia'//{}是采用export形式导出的

//import App from './App.vue'//export default导出的,App这个名字可以任意取。
import App from './components/CaoQing.vue'
import router from './router' //会找router文件下的index.js

import './assets/main.css'
//创建vue实例化对象
const app = createApp(App) //app名字上随便的
//使用第三方插件/库
app.use(createPinia())
app.use(router)
//把创建的vue实例,挂载到 index.html
app.mount('#app') //括号里的app要和index.html里的<div id="app"></div>id一致

//总结:把开发的vue组件放到index.html里运行
//main.js:入门文件/主文件
//App.vue :根组件
//index.html ://挂载文件
//.vue结尾的都叫一个组件,打包工具会把组件打包成一个js,js会加上export default
//三个地方可以放组件:components:公共组件 views:业务组件  App.vue:src根目录下
//index.html文件
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <link rel="icon" href="/favicon.ico">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Vite App</title>
</head>
<body>
  <div id="app"></div>
  <script type="module" src="/src/main.js"></script>
</body>
</html>

三:vcode常用插件

在这里插入图片描述

四:Vue3的基本指令

创建组件,组件名首字母要大写,以.vue后缀表示组件,装的插件有自动补全功能,所以只需要输入vbase,选择弹出的含有reactive的模版。Vue3的基本指令都是在template模版中使用,以下是模版简介

<template>
  <!-- 第一部分:template模版,相当于html的body部分,是必须的 -->
  <!-- 在模版中可以【直接使用】返回对象中的属性/方法 -->
  <div>响应式对象计数:{{ count }}</div>
  <div>普通对象计数:{{ count1 }}</div>
  <div>响应式变量计数:{{ age }}</div>
</template>

<script>
//第二部分:js部分
import { onMounted, reactive, ref, toRefs } from 'vue'

export default {
  //1.setup函数,是一个生命周期函数,是组合式API的起点
  //它比较特殊,无需从vue中导入,其他的周期函数都需要导入才能使用,而且其他生命周期函数都需要在setup函数使用
  setup() {
    //2.定义一个【响应式】对象 =》页面会变化
    const state = reactive({
      count: 0
    })
    //定义一个【普通】对象 =》页面不会变化
    const state1 = {
      count1: 0
    }
    //2.【响应式】变量 =》页面会变化
    const age = ref(0)
    //3.定义一个函数,方法不存在收否响应
    const addOne = () => {
      state.count += 1
      state1.count1 += 1
      age.value += 1
    }
    //4.生命周期函数:当页面加载完成后,才会执行
    onMounted(() => {
      //5.每隔多久执行某个代码/函数
      setInterval(addOne, 1000)
    })

    //以上定义的属性和方法必须返回,模板中才能使用
    //注意:如果是响应式对象里的变量,就不需返回模版里也可以使用
    return {
      age,
      //toRefs:把一个【响应式】对象,转换成普通对象,该普通对象的每一个属性值都是ref类型
      //三个点是扩展运算符,取出对象的所有可遍历属性
      ...toRefs(state),
      ...state1
    }
  }
}
</script>

<style lang="scss" scoped>
</style>

(1)文本插值
数据绑定最常见的形式就是使用双大括号的文本插值,基本语法:{{ 变量/数组/对象等 }}
(2)v-text指令 和 v-html指令

  • v-text指令,原样式输出文本
  • v-html指令,会将元素当成html标签解析后输出
    (3)v-once指令:用于执行一次性的插值,定义的元素或组件只会被渲染一次,当数据改变时,插值处的数据不会被更新
    (4)v-bind(数据绑定):基本语法 <标签 v-bind:属性=“值”><标签/>
    缩写是冒号: <标签 :属性=“值”><标签/>
    可以绑定的html属性包括:class,src,style,href,disabled等
    (5)v-if与v-show(条件渲染):只会在指令的表达式返回为true时进行渲染内容
    v-if基本语法:v-if v-else-if v-else
  • v-if: 条件判断指令,当不满足条件时,F12看元素被删除。if 更实用90%
  • v-show:条件判断指令,当不满足条件时,F12看元素隐藏,style="display: none
    (6)v-for(遍历):可以遍历数组,对象数组,对象
  • 遍历数组语法 v-for=“(item,index) in arrName” :key=“index”
    其中,item是数组项,index是下标,arrName是待遍历的数组
  • 遍历对象的语法 v-for=“(value,key,index) in obj” :key=“index”
    其中,value是对象的属性值,key是属性名,obj是具体的对象
    (7)v-on(监听事件)
    鼠标常用事件:
  • click:单击鼠标按钮时触发
  • dbclick:双击击鼠标按钮时触发
  • mouseover:当鼠标移到某个元素上方时触发
  • mouseout:当鼠标移出某个元素上方时触发

键盘常用事件:

  • keyup:当用户释放键盘上的健时触发
  • chage:当文本框(input或textarea)中的一个或多个字符触发
  • input:用户输入时触发,非常重要
  • focus:当页面或元素获得焦点时在window及相关元素上面触发
  • blur:当页面或元素失去焦点时在window及相关元素上面触发
  • submit:当用户点击提交按钮在元素上触发
  • reset:当用户点击重置按钮在元素上触发
    事件对象:当用户触发了一个事件后,对该事件的一些描述信息,事件对象必须使用($event)作为实参,e.target可获取整个元素的信息
    事件常用修饰符:.stop 组织同一事件的继续传播,通俗就是互补干扰彼此的执行,在事件名后使用,比如@click.stop
    按键(键盘)常用修饰符:.enter:回车键 .tab:table键盘

v-on用于:基本语法 <标签 v-on:事件名=“事件的处理方法或脚本”><标签/>
缩写是@:<标签 @事件名=“事件的处理方法或脚本”><标签/>
(8)v-model(表单输入绑定)
所谓表单,指的是html的input ,select,textarea等表单的元素,v-model实现和这些标签数据的双向绑定,监听用户的输入事件来更新数据。它本质上是一个语法糖,实际上就是v-bind:value + v-on:input的组合。

双向绑定:用javascript命令改变数据的值时,会在页面中自动重新渲染内容,改变文本框的内容也会自动修改绑定的数据(可在控制台看,以便于存放修改后的数据到数据库)。
可用于文本框,单选框,复选框,下拉列表,多行文本框的绑定

基本语法:<标签 v-model=“响应式数据”><标签/>输入框可以.number 限制只输入数字,.trim 去除左右空格。例如v-model.number和v-model.trim

五:Vue进阶

1.计算属性:
使用computed方法实现,方法参数是一个函数,它必须有return返回值,在该函数中,任意一个响应式数据发生变化,computed方法都将自动重新运算,视图也会同步更新,默认第一次就会调用。
2.侦听属性:
使用watch方法实现,该方法有三个参数:

  • 参数1:表示被侦听的响应式数据,可以是ref变量,ref数组,reactive对象,reactive对象的某个属性,只要操作了参数1中的数据,就会调用watch
  • 一个ref数据:直接写
  • 多个ref数据:数组形式[num1, num2]
  • reactive对象:state
  • reactive对象的某个属性:() => state.count
  • 参数2:是一个回调函数(即被监听数据变更后,需要触发的函数)在回调函数中,就可以做我们的计算了
  • 参数3:watch的额外配置项,对象格式,是可选的。immediate:true/false 是否首次页面加载就执行。默认false
    可以用setTimeout实现异步操作,例如:
    setTimeout(() => {
    num.value = 900
    }, 5000)

3.watchEffect:
和watch功能类似,不同的是 它会自动监听其内部回调函数使用到的数据,其中任意一个数据发生了变化,它都会执行回调函数,首次加载就会执行一次。
只有一个参数,该参数是一个函数
4.总结
计算属性:

  • 不支持异步 操作
  • 必须使用return
  • 监听的数据必须参与计算
  • 无法得到变更前的值
  • 有缓存,即结果存储到一个响应式变量中
    侦听属性:
  • 支持异步操作
  • 可以不使用return
  • 监听的数据可以不参与计算
  • 可以得到变更前的值
  • 计算属性完成的功能,侦听属性都可以完成
  • 没有缓存,即不会有存储结果
  • 默认首次加载不执行,当数据改变时才执行

watchEffect
和watch功能类似,不同的是:

  • 首次就会执行,相当于watch的immediate为true
  • 自动收集数据依赖
  • 得不到变更前的值
  • 可以停止监听,watch不可以

六:组件化开发

1.组件的常用的生命周期
创建-》挂载-》更新-》销毁
以下代码说明:

<template>
  <div class="about">
    <h1>This is an about page(生命周期)</h1>
    <div>
      <input type="radio" name="xb" value="1" v-model="sex" /><input type="radio" name="xb" value="2" v-model="sex" /></div>
  </div>
</template>

<style>
@media (min-width: 1024px) {
  .about {
    min-height: 100vh;
    display: flex;
    align-items: center;
  }
}
</style>
<script>
import {
  onBeforeMount,
  onBeforeUnmount,
  onBeforeUpdate,
  onMounted,
  onUnmounted,
  onUpdated,
  reactive,
  toRefs
} from 'vue'
export default {
  setup() {
    //第一阶段:创建
    console.log('1~2.setup()函数开始了!!!!相当于vue2的(BeforeCreate、Created)')
    const state = reactive({
      count: 0,
      sex: 1
    })
    //阻塞的睡眠函数(delay参数:N毫秒)
    //普通函数
    function sleep2(delay) {
      //当前时间
      const start = new Date().getTime()
      //执行的时间4 小于 设定的等待时间5 就继续等待,如果6大于等待的时间5,则跳出循环不再等待执行下一步骤
      while (new Date().getTime() - start < delay) {
        //继续,调用while
        continue
      }
    }
    //箭头函数
    const sleep = (delay) => {
      //当前时间
      const start = new Date().getTime()
      //执行的时间4 小于 设定的等待时间5 就继续等待,如果6大于等待的时间5,则跳出循环不再等待执行下一步骤
      while (new Date().getTime() - start < delay) {
        //继续,调用while
        continue
      }
    }

    //第二阶段:挂载,组件被挂载到index.html上
    //2。挂载前
    onBeforeMount(() => {
      console.log('3.onBeforeMount被调用了!(挂载前。。。。)')
      // 在组件挂载到index.html之前,睡大觉。睡5秒
      sleep(3000)
    })
    onMounted(() => {
      console.log('4.onMounted被调用了!(挂载后。。。。)')
    })

    //第三阶段:数据更新
    onBeforeUpdate(() => {
      console.log('5.onBeforeUpdate被调用了!(更新前。。。。)')
    })
    onUpdated(() => {
      console.log('6.onUpdated被调用了!(更新后。。。。)')
    })
    //第四阶段:销毁阶段,vue组件被销毁,即点击其他组件时,当前组件被销毁
    onBeforeUnmount(() => {
      console.log('7.onBeforeUnmount被调用了(组件失效了。。。)!')
    })
    onUnmounted(() => {
      console.log('8.onUnmounted被调用了!(组件失效且销毁了。。。)')
    })

    return {
      ...toRefs(state)
    }
  }
}
</script>

2.组件的使用步骤

  1. 定义组件
  2. 父组件导入并注册组件
  3. 父组件使用子组件
<template>
  <div>
    <h2>组件的使用步骤</h2>
    <h3>这里是父组件-FatherComp.vue</h3>
    <!-- 步骤3:使用子组件 -->
    <!-- 单标签 -->
    <!-- <ChildComp /> -->
    <!-- 标签对 -->
    <child-comp></child-comp>
    <!-- <ChildComp></ChildComp> -->
  </div>
</template>
<script>
import { reactive, toRefs } from 'vue'
//步骤2-1:导入子组件
import ChildComp from './ChildComp.vue'
export default {
  //步骤2-2:注册子组件
  components: { ChildComp },
  setup() {
    const state = reactive({
      count: 0
    })
    return {
      ...toRefs(state)
    }
  }
}
</script>

<style lang="scss" scoped>
</style>

3.父组件传递数据给子组件(80%以上用这个)
前提是:父子组件已经关联,参考2进行关联

  • 在父组件中定义好数据时,需要在其模版中绑定要传给子组件的数据 ,例如
 <child-comp v-bind:ccount="count" v-bind:cname="name" :clistObj="listObj"></child-comp>
  • 在子组件中用props接收父组件模版中定义的数据,props有两种接收数据的方式,分别是数组和对象,其中对象是常用的,父组件数据变化,子组件也会跟着变。代码如下:
<template>
  <div>
    <h4 style="color: red">这里是子组件-ChildComp.vue</h4>
    <!-- 子组件渲染 -->
    <h5>这里是从父组件传递过来的ccount的值:{{ ccount }}</h5>
    <p>这里是从父组件传递过来的cname的值: {{ cname }}</p>
    <p>这里是从父组件传递过来的clistObj的值: {{ clistObj }}</p>
    <ul>
      <li v-for="(obj, index) in clistObj" :key="index">{{ obj.id }}-{{ obj.realName }}</li>
    </ul>
  </div>
</template>
<script>
import { reactive, toRefs, ref, watch, watchEffect } from 'vue'

export default {
  // props: ['ccount', 'cname', 'clistObj'],
  //Number、String、Array、Object等等
  //导入父组件模版中定义的值
  props: { ccount: Number, cname: String, clistObj: Array },
  setup(props) {
    const state = reactive({
      count: 0,
      ccount: 0 //定义一个响应式变量,接收父组件传过来的变更的值
    })
    //因为只执行一次,所以值一直不变
    // console.log('拿到的props的值为:', props.ccount)
    // 如果ccount 在setup中,也需要有响应式,怎么办?
    // 可以采用:侦听器
    watch(
      () => props.ccount,
      () => {
        console.log('调用了watch方法。')
        state.ccount = props.ccount
        console.log('拿到的props的值为:', state.ccount)
      },
      { immediate: true }
    )
    //父组件值改变,子组件值跟着改变,方便存储到数据库
    // watchEffect(() => {
    //   console.log('进来了watchEffect')
    //   state.ccount = props.ccount
    //   console.log('拿到的props的值为:', state.ccount)
    // })
    return {
      // result,
      ...toRefs(state)
    }
  }
}
</script>
<style lang="scss" scoped>
</style>

4.子组件传递数据给父组件(用的比较少)
前提是:父子组件已经关联,参考2进行关联

  • 在子组件中定义好数据时,定义一个箭头函数,用emit函数包裹住要传给子组件的数据,它两个参数:1.事件名,由父组件的模版调用;2.要传递的数据,子组件数据变化,父组件也会跟着变
  /*
  setup接收2个参数,第一个是props,它为响应式对象。第二个参数context,它是非响应式对象
  context对象包括三个属性:emit(方法)、slots(插槽对象)、attrs(attribute对象)
       context:{ emit:()=>{}, slots:{},  attrs:{}  }
       我们也可以对它进行解构
  */
  setup(props, { emit }) {
    const state = reactive({
      count: 10000,
      name: '小马云',
      obj: { name: '张三', age: 29 },
      objList: [
        { id: 1, name: '张三', age: 22 },
        { id: 2, name: '李四', age: 23 },
        { id: 3, name: '王五', age: 25 },
        { id: 4, name: '李世民', age: 27 },
        { id: 5, name: '朱元璋', age: 30 }
      ]
    })
    const sendMsg = () => {
      console.log('调用了子组件的传值方法。。。')
      //传给父组件
      //两个参数:1.事件名,由父组件的模版调用;2.要传递的数据
      emit('caoqq', state.count, state.name, state.obj, state.objList)
    }
  • 在父组件中定义一个箭头函数用来接收子组件传过来的数据,然后在该组件的模版中利用子组件中emit的事件名输出数据,代码如下:
<template>
  <div>
    <h2 style="color: pink">【子组件传递数据给父组件】</h2>
    <h3 style="color: green">这里是父组件-FatherComp.vue</h3>
    <!-- 步骤3:使用子组件 -->
    <!-- 标签对 -->
    <!-- caoqq是子组件中emit函数的事件名 -->
    <child-comp @caoqq="getData"></child-comp>
  </div>
</template>
<script>
import { reactive, toRefs } from 'vue'
//步骤2-1:导入子组件
import ChildComp from './ChildComp.vue'
export default {
  //步骤2-2:注册子组件
  components: { ChildComp },
  setup() {
    const state = reactive({
      count: 0
    })
    //方法里的参数要和子组件中emit的一致,也可以用可变参数接收
    const getData = (data, name, obj, objList) => {
      console.log(
        '父组件接受了子组件传过来的值:count的值为:',
        data,
        'name的值:',
        name,
        'obj的值:',
        obj,
        'objList的值:',
        objList
      )
      // state.count = data
    }

七:组件进阶

1.动态组件
场景:在一个父组件中同时引用三个子组件,我们想一次只显示一个组件,当点击某个按钮时,切换显示不同的组件,要实现这种组件动态显示效果,vue提供了 compoent标签,语法如下:

<compoent :is="要显示的组件名" ></compoent> //采用v-bind指令绑定is属性
<template>
  <div>
    <h3>这里是父组件</h3>
    <!-- 使用动态使用子组件 -->
    <!-- component:is ,is的值是哪个组件的名称就显示哪个组件 -->
    <component :is="selectTab"></component>
    <!-- 选择需要显示的组件 -->
    <select v-model="selectTab">
      <option value="TabOne">选项卡一</option>
      <option value="TabTwo">选项卡二</option>
      <option value="TabThree">选项卡三</option>
    </select>
    <div>选项内容:{{ selectTab }}</div>
  </div>
</template>

<script>
import { reactive, toRefs } from 'vue'
//导入子组件
import TabOne from './TabOne.vue'
import TabTwo from './TabTwo.vue'
import TabThree from './TabThree.vue'

export default {
  //注册子组件
  components: { TabOne, TabTwo, TabThree },
  setup() {
    const state = reactive({
      count: 0,
      selectTab: 'TabOne'
    })
    return {
      ...toRefs(state)
    }
  }
}
</script>

2.插槽的使用
分为默认插槽(不具名插槽)和具名插槽
插槽:在子组件中留插槽,想让内容显示在什么位置,就把插槽放在哪里,插槽显示的内容是在父组件中写的。如果子组件定义了默认插槽,其他内容将显示在默认插槽中,如果定义了具名插槽具名插槽的内容将显示在具名插槽中,如果没有定义默认插槽,其他内容将不会显示

  • 默认插槽(不具名插槽):子组件模版中插槽语法格式:<slot></slot>
  • 具名插槽:子组件模版中插槽语法格式: <div><slot name="zhangsan"></slot></div> 其中name,是父组件中定义的插槽名,父组件模版中需要定义以下格式: <template v-slot:zhangsan>具名插槽内容-章三</template>, v-slot: 可以缩写成 #
    父组件内容如下:
    <!-- 在父组件中添加内容,这些内容将展示在子组件的插槽中 -->
    <child-comp>
      <!-- 这是不具名(默认)插槽要显示的内容 -->
      这里是父组件传给子组件展示的默认插槽内容11: {{ count }}
      <!-- 这是具名插槽要显示的内容 -->
      <!--需要用到template标签,  -->
      <template v-slot:zhangsan>具名插槽内容-章三</template>
      <!--需要用到template标签,  -->
      <template #lisi>简写具名插槽内容-李四</template>
    </child-comp>

3.响应式变量的定义
三种方式定义响应式变量

  • reactive函数(通常用于复杂数据类型,比如对象和数组)
  • ref函数(通常用于基本数据类型,字符串,数值,布尔值,null,underfined,Symbol,Bigint)
  • computed计算属性(有return)
    4.toRef和toRefs
  • toRef:toRef接收两个参数:源响应式对象 和 该对象的指定属性名,返回一个ref数据,即单个
  • toRefs:把一个响应式对象转换成普通对象,该普通对象的每个属性都是ref,即批量
  setup() {
    //定义响应式变量state,但是state里的属性是没有响应式的
    const state = reactive({
      count: 0,
      name: '张三'
    })
    //toRef接收两个参数:源响应式对象 和 该对象的属性名(指定属性),返回一个ref数据
    // 采用toRef函数,为响应式对象的某个属性,添加响应式

    const conut2 = toRef(state, 'count')

    setInterval(() => {
      state.count += 1
    }, 1000)
    //return中的变量在模版中可以直接用
    return {
      age: 22, //也属性响应式变量,可直接使用
      conut2,
      count3: toRef(state, 'count'),
      //  1.toRefs,用来把响应式对象中的属性,变成响应式
      ...toRefs(state)
      //   ...state //count就没有响应式了
    }
  }

5.模版引用
可以获取元素的具体信息

  • 第一步:定义一个响应式变量,并返回
  • 第二步:在模版中给需要的元素加上ref属性,并绑定第一步定义的响应式变量
  • 第三步:当页面加载完后,用响应式变量.value可以获取元素的值

########前端项目实战

一:路由的使用步骤

  1. 路由的安装:npm i vue-router@next
  2. 路由的开启:在程序入口文件main.js中全局配置路由
    import router from ‘./router’ //即router 文件夹下的 index.js 文件
  3. 路由的配置:项目中的 router/index.js 配置路由信息
//...1.从vue-router中导入createRouter, createWebHistory
import { createRouter, createWebHashHistory, createWebHistory } from 'vue-router'
//。。。2.导入HomeView组件
import Home from '../views/Home.vue'
//。。。。。3,使用createRouter方法创建一个路由器对象
const router = createRouter({
  //路由模式1:history模式(参数:导入环境变量中的基础URL),也可以不写括号里的内容
  // history: createWebHistory(import.meta.env.BASE_URL),
  history: createWebHistory(),
  //路由模式2:hash模式
  // history: createWebHashHistory(),

  routes: [
    //每一个路由,都是一个对象,它包括三个属性:path,【name】,[component/redirect二选一]
    {
      path: '/',//根路由,路由的路径
      name: 'home', //路由的名称 可以省略
      component: Home
    },
    {
      path: '/login',
      // route level code-splitting 路由级代码拆分
      //这将为此路由生成一个单独的模块(About.4543054354dfgdf.js)
      // this generates a separate chunk (About.[hash].js) for this route
      // 当访问路由时,是懒加载的
      // which is lazy-loaded when the route is visited.
      // 总结:路由懒加载方式,项目打包时,一个组件打包为一个js文件。访问时才加载组件。
      // component: () => import('../views/AboutView.vue')
      component: () => import('../views/Login.vue')

    }
  ]
})
export default router //导出路由器

  1. 路由的使用

路由是用来实现页面导航的,导航分为两种:
声明式导航:在模板中声明路由链接,最终形成a标签导航
编程式导航:通过调用 JavaScript 形式的API实现导航,适合页面重定向,比 如登录成功后重定向到主页
(1)路由链接的代码如下(声明式导航的实现):

<!-- 作用:RouterLink会被渲染成一个a标签,它的to属性,会被渲染成a元素的href属性 -->
<!-- 好处:可以给to属性绑定变量(对象),to的值等于路由配置中的path -->
<RouterLink to="/about">About</RouterLink>
 <RouterLink :to="{ path: '/', query: { a: '123', b: '456' } }">Home</RouterLink>

(2)路由填充位(路由占位符 )
通过路由规则匹配到的组件后,将组件的内容 渲染到路由占位符所在的位置(想在哪个组件里显示内容就在该组件留路由占位符,比如点击A组件中的【按钮】希望A组件内容在B组件展示,那么A组件和B组件就需要有关系,在同一Web网友中)。路由占位符的代码如下:

 <RouterView /><router-view /><router-view></router-view>

总结:前端页面访问的路由在根组件App.vue里找,找到后去index.js,找到对应的组件,将组件的内容在路由占位符中显示

二:Element+组件库

官网:https://element-plus.gitee.io/

  1. Element Plus安装: npm install element-plus
  2. main.js中全局配置这个库
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'  //样式
import zhCn from 'element-plus/es/locale/lang/zh-cn'

const elementConfig = { size: 'default', local: zhCn, zIndex: 3000 }
app.use(ElementPlus, elementConfig)

  1. ElementPlus库的使用
    包括:表单,输入框,添加数据校验等多个组件,具体组件及用法参考官网。需要注意的点:
    1.表单验证时:
    ref:模版引用,用于获取页面元素
    model:数据绑定对象
    rules:绑定传入约定的验证规则
    prop:prop属性设置为需要校验的字段名

三:配置全局样式

main.js中全局导入自定义样式:import ‘./assets/css/common.scss’

四:全局注册 ICON图标库

  1. 安装库:npm i @element-plus/icons-vue
  2. 全局注册icon组件:
import * as Icons from '@element-plus/icons-vue'
// 1.注册全局elementplus icon组件 Object.keys(Icons).forEach((key) => {
    app.component(key, Icons[key])
})
  1. 使用:在Element Plus官网中找到需要的图标,直接复制即可
<el-icon><CirclePlus /></el-icon >                     

五:ElMessage消息组件

  1. 导入:import { ElMessage } from ‘element-plus’
  2. 使用:
import { ElMessage } from 'element-plus'
# 用法一
ElMessage.type('消息内容')
// 其中:type可替换为:success、warning、info、error
# 用法二 ElMessage({
message: '消息内容',
type: 'success',// 其中:type值可为:success、warning、info、 error
})

六:localStorage本地存储

跟缓存类似,本地存储(新增、获取、删除、清空全部)
在浏览器的Applicationd的Local Storage下的地址中查看

  // A.新增
      // localStorage.setItem('userName', 'caoqq')
      // localStorage.setItem('userId', '123')
      // localStorage.setItem('token', 'wfffdsfd')
      // B.获取
      // console.log('userId====', localStorage.getItem('userId'))
      // C.删除
      // localStorage.removeItem('userName')
      // // D.清空所有
      // localStorage.clear()

七:Axios概述

Axios是一个基于Promise的HTTP库,可以用在浏览器和node.js中。用于向服 务器后端发起Ajax请求,并在请求的过程中可以进行很多控制,其主要特性如下:

  • 可以在浏览器中发送XMLHttpRequests
  • 可以在Node.js中发送HTTP请求
  • 支持Promise API
  • 可以拦截请求和响应
  • 转换请求数据和响应数据
  • 取消请求 自动转换JSON数据
  • 客户端可以防止XSRF攻击

Axios的安装:$ npm install axios
Axios的使用:两种方式,代码如下:

// 第一步:导入axios库
import axios from 'axios'
// 第二步:编写请求配置
const config = {
    url: '/token/',
    method: 'post',
    baseURL: 'http://地址/api',
    data: {
        username: "账号",
        password: "密码",
        captcha: "9999",
        captchaKey: 1157
    }
}
//第三步:发送网络请求
axios(config).then((res) => {
    console.log('res的值为:', res)
    console.log('res对象中的data为:', res.data)
}).catch((e) => {
    console.log('异常响应对象为:', e.response)
    console.log('异常响应状态码为:', e.response.status)
    console.log('请求异常')
})
// 第一步:导入axios库
import axios from 'axios'
// 第二步:编写请求配置
const config = {
    baseURL: 'http://地址/api',
}
// 第三步:采用别名的方式发送请求
//语法格式:axios.post(url[, data[, config]])
axios.post('/token/',
    {
        username: "账号",
        password: "密码",
        captcha: "9999",
        captchaKey: 1157
    }, config
).then((rep) => {
    console.log('res对象中的data为.data', rep.data)
}).catch()

总结:在实际应用中会封装Axios
定义一个js文件,包括

  • 默认配置,比如URL地址和请求超时时间
  • 定义常用的请求方法(post、get、delete、put)
  • 定义一个方法,里面封装各种请求的参数,比如处理请求头,处理config对象,请求路径的统一处理,params参数和data参数的特殊处理
  • 定义业务接口(项目的所有接口)
    例如:
    //每一个接口都是一个箭头函数,传参params是一个对象,然后导出
    export const login = params => axiosPost({ url: ‘token/’, params }) // Token登录

八:编程式导航基础router

通过调用 JavaScript 形式的API实现导航,适合页面重定向

  1. 导入路由器:
    import { useRouter } from ‘vue-router’ //导入router对象
  2. 创建路由器对象-必须在setup函数中创建
    const router = useRouter()
  3. 使用 router 对象提供的方法
    点击某个链接时,想要导航到不同的位置,可以使用 router.push 方法
//...1.参数为路径字符串
router.push('/about')
//...2.参数为对象-path属性
router.push({ path: '/about' }) //...3.参数为对象-name属性
router.push({ name: 'About' }) //...4.参数为对象-带查询参数
router.push({ name: 'About', query: { a: 'Hello' } })

九:主页组件的开发思路

1.主页布局
引用Element Plus布局,主页布局我们采用 左右结构 ,右边采用 上下结构
效果图如下:
在这里插入图片描述

  • 在 src/components 目录,再创建 顶栏 NavBar组件,侧边栏SideBar.vue组件,多个Tab页组件(内容)MutiTabs.vue组件
  • 在 src/components 目录,创建布局 Layout.vue 组件,内容参考Element Plus布局中的代码,把 顶栏,侧边栏,多个Tab页组件放入布局Layout.vue 组件中
  • 在 Home父组件 中引入 Layout子组件
    2.主页组件包括的内容
    顶栏:左和右区域
    左:折叠按钮/这里放面包屑(点击按钮时路径拼接显示)
    右:全屏按钮/用户图像/用户名/下拉退出登陆和用户设置
    左侧边栏:菜单,菜单下面有子菜单,点击按钮可以对菜单进行折叠
    多个Tab页:点击左边侧边栏按钮会展示对应的内容

3.左侧边栏组件注意内容
在这里插入图片描述
点击接口自动化下的各子组件时MutiTabs.vue组件会展示对应的内容

  • el-menu组件,默认情况下菜单路由功能是关闭的。可以设置 el-menu 组件 的 属性为 true
    来开启菜单路由功能,el-menu-item 组件的index 属性将被用来作为 path 进行路由跳转。
  • 路由嵌套,在src/views下创建 Cases 、 Requests 、 Plans 、 Reports 四个业务组件。
  • 再在 router/index.js 给Home组件配置四个字路由,因为流程是先进入主页组件,在主页组件的某一个角落显示业务组件的内容
 //父子路由
      children: [
        {
          path: '/cases', // 根路由,路由的路径
          name: '测试用例',
          component: () => import('../views/sqtp/Cases.vue')
        },
        {
          path: '/requests',
          name: 'web接口',
          component: () => import('../views/sqtp/Requests.vue')
        },
        {
          path: '/plans',
          name: '测试计划',
          component: () => import('../views/sqtp/Plans.vue')
        },
        {
          path: '/reports',
          name: '测试报告',
          component: () => import('../views/sqtp/Reports.vue')
        }
  • 点击接口自动化下的各子组件时MutiTabs.vue组件会展示对应的内容,所以可以在MutiTabs.vue组件中留一个路由占位符,也可以在 Layout.vue 组件中留一个路由占位符(Layout.vue中有MutiTabs组件 )

十:状态管理

pinia 都是状态管理库,用于跨组件(页面)进行状态共享,应用中,存在某些变量需要跨组件共享时,应该使用状态管理库。一个 Store 是一个存储实体,它有 state 、 getters 和 actions ,相当于组件中的 响应式数据 、 计算属性 和 方法 。

  • 安装:npm install pinia
  • 在 main.js 中,添加 pinia 的根存储,关键代码如下:
import { createPinia } from 'pinia'
app.use(createPinia())
  • 在stores下定义一个状态管理文件,以js结尾,关键代码如下:
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'

// Setup方式定义Store
export const useUserStore = defineStore('user2', () => {
    // 1.定义响应式变量
    const count = ref(40)
    const name = ref('张三')
    // 2.定义计算属性
    const doubleCount = computed(() => count.value * 2)
    // 3.定义方法
    const addOne = () => { count.value++ }

    // 4.返回
    return { count, name, doubleCount, addOne }
})

  • 导入与调用:
import { useUserStore } from '@/stores/user' // 1.导入一个store
export default {
  setup() {
    const store = useUserStore()
return {
} }
}
  • 获取状态

// 采用storeToRefs()、toRefs()函数解构,保留响应式 。记得要导出
const { count, name, doubleCount, addOne } = toRefs(store)

  • 修改状态
// $patch()方法修改(批量修改) 
store.$patch({
      count: store.count + 1,name: '李四' })

十一:左侧菜单的展示

需求:根据当前登录的用户,从后端动态获取对应的菜单,进行格式转换后,存到本地存储中
(1)menuTree:左侧菜单数据
2)menuList: 按钮权限数据,类似:
“menuPermission”:
[“Retrieve”, “Update”, “Search”,“Create”, “Build”, “Delete”]

十二:全局路由守卫

碰到的问题:用户在未登录的情况下,仍然可以访问Home主页。这在实际项 目中是不允许的。如何在请求每条路由链接前,判断用户是否登录呢? Vue-rout提供了全局路由守卫来监听路由的进入与离开。程序员可以通过 Vue-router 提 供的 beforeEach (前置守卫)、 afterEach (后置守卫)两个钩子函数,来搞事 情。它们分别在路由改变前和改变后触发
在项目的 router/index.js 中,添加前置路由守卫,代码如下:

/* 前置路由守卫
  to表示要跳转到哪里
  from 表示从哪里来
  next表示动作 让进入
*/
router.beforeEach((to, from, next) => {
  // 进度条开始
  NProgress.start()
  const userId = localStorage.getItem('userId')
  if (to.path === '/login') {//1.跳转到登录界面,放行
    next(true) //或者next() 
  } else if (userId) { // 2.有登录的,放行
    next()
  } else {//其他情况(无登录,且非登录界面)都跳转到登录界面
    next('/login') //进入登陆界面
  }
})
// 后置路由守卫
router.afterEach(() => {
  // 进度条结束
  NProgress.done()
})

// 进度条的配置项:ease可以设置css3动画,如ease,linear;speed是进度条从开始到结束的耗时
NProgress.configure({ ease: 'linear', speed: 500 })

十三:XEUtils通用函数库

xe-utils 提供了一套实用的基础函数、任意格式的日期转换函数,浏览器相 关操作函数等。提供了100多个函数,应有尽有。总之,如果你有判断、处理、转 换的一些需求,尽管来这里找找看。
官网地址:https://toscode.gitee.com/x-extends/xe-utils api
文档:https://x-extends.github.io/xe-utils/

  • 安装与导入:
npm install xe-utils
import XEUtils from 'xe-utils'
  • 常用API:
    searchTree(array, iterate): 从树结构中根据回调查找数据。根据某一个子菜单查找到它所属的父菜单
    toTreeArray (array, options):将一个树结构转成数组列表
    toArrayTree (array, options):一个高性能的树结构转换函数,将一个带层级的数据列表转成树结构
    cookie (name, value, options):常用操作
XEUtils.cookie('name') // 根据name获取 
XEUtils.cookie('name', 'value', {expires: '7d'}) //指定7天后期 
XEUtils.cookie('name', null, {expires: -1}) // 删除 XEUtils.cookie.remove(name) // 删除

十四:面包屑组件的开发

思路:

  • 在 Element Plus 官网上,找到面包屑组件,拷贝过来
  • 根据 route对象 ,获取当前 url,从本地存储中,获取 menuTree (因为我们需要的icon和菜单名称都在菜单树 中)
  • 用监听器,监听 route.path 的变化
  • 在监听器中,根据 route.path 到菜单树中查找匹配的数组(这里要用到 XEUtils 库的 searchTree 函数)
    将匹配的树形数组,转换为数组列表,返回到 template 中(这里要用到 XEUtils 库的 toTreeArray 函数)
    在 template 中,对匹配的数组列表进行循环,显示菜单的ICON和菜单的名称

十五: 多Tab页选项卡的实现

利用状态管理,定义两个变量,左边菜单栏点击菜单,重复点击的菜单不重复展示

// 1.当前激活的选项卡
     const activeName = ref('')
// 2.当前打开的选项卡数组
    const openTabs = ref([])

十六:内容页布局组件(顶部搜索区、中间数据表格、底部数据分页)

系统中,业务功能菜单展示的内容,大部分布局是:上、中、下三栏布局,即顶部搜索区、中间数据表格、底部数据分页。这三个组件是通用组件
因此,我们可以单独创建一个内容页组件( MainLayout )。在该组件中,再单 独引入 Search 、 Tables 、 Paginator 三个子组件
布局采用:vh+ calc() ( 我们采用 ),即后面会在 Tables 组件中,添加了 height=“calc(100vh - 250px)”

十七:跨组件通讯

使用 Provide (提供)和 Inject (注入)进行数据传递,父组件可以作为所有子组件的依赖项提供数据,而不管组件层次结构有多深。 这种特性有两个部分:父组件有一个 Provide 选项用于提供数据;子组件有一个 I nject 选项用于接收这个数据。例如:
在测试用例组件中利用Provide

  1. 先导入import { provide } from ‘vue’
  2. 定义变量或方法
  3. 向外提供数据
 provide('searchForm', searchForm)
    provide('queryParam', queryParam)
    provide('columns', columns)
    provide('tableData', tableData)
    provide('total', total)
    provide('pageSize', pageSize)
    provide('pageIndex', pageIndex)
    provide('multiple', multiple)
    provide('getData', getData)
  1. 子组件接收数据,导入import { inject } from ‘vue’
   const tableData = inject('tableData')
   const columns = inject('columns')
   const multiple = inject('multiple', false)
Logo

前往低代码交流专区

更多推荐