Vue3总结(持续更新)
属性的简写::当属性名和值的变量名相同时,触发对象的属性简写let name = ‘张三’;export:规定模块的对外接口,如果希望外部能够获取模块内的某个变量/函数/类,就必须在模块中使用export输出该变量/函数/类。原理:异步执行,当接口请求后,可以去做其他事情,等接口返回成功再渲染页面,接口返回失败也会有相应的提示。export default:每个模块【只能够使用一次】,可以导出变量
########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.组件的使用步骤
- 定义组件
- 父组件导入并注册组件
- 父组件使用子组件
<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可以获取元素的值
########前端项目实战
一:路由的使用步骤
- 路由的安装:npm i vue-router@next
- 路由的开启:在程序入口文件main.js中全局配置路由
import router from ‘./router’ //即router 文件夹下的 index.js 文件 - 路由的配置:项目中的 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 //导出路由器
- 路由的使用
路由是用来实现页面导航的,导航分为两种:
声明式导航:在模板中声明路由链接,最终形成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/
- Element Plus安装: npm install element-plus
- 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)
- ElementPlus库的使用
包括:表单,输入框,添加数据校验等多个组件,具体组件及用法参考官网。需要注意的点:
1.表单验证时:
ref:模版引用,用于获取页面元素
model:数据绑定对象
rules:绑定传入约定的验证规则
prop:prop属性设置为需要校验的字段名
三:配置全局样式
main.js中全局导入自定义样式:import ‘./assets/css/common.scss’
四:全局注册 ICON图标库
- 安装库:npm i @element-plus/icons-vue
- 全局注册icon组件:
import * as Icons from '@element-plus/icons-vue'
// 1.注册全局elementplus icon组件 Object.keys(Icons).forEach((key) => {
app.component(key, Icons[key])
})
- 使用:在Element Plus官网中找到需要的图标,直接复制即可
<el-icon><CirclePlus /></el-icon >
五:ElMessage消息组件
- 导入:import { ElMessage } from ‘element-plus’
- 使用:
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实现导航,适合页面重定向
- 导入路由器:
import { useRouter } from ‘vue-router’ //导入router对象 - 创建路由器对象-必须在setup函数中创建
const router = useRouter() - 使用 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
- 先导入import { provide } from ‘vue’
- 定义变量或方法
- 向外提供数据
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)
- 子组件接收数据,导入import { inject } from ‘vue’
const tableData = inject('tableData')
const columns = inject('columns')
const multiple = inject('multiple', false)
更多推荐
所有评论(0)