该篇笔记是Vue全家桶笔记,该篇笔记包含最初的基础语法,脚手架创建项目,组件,请求库,集中式状态管理,路由,还有涉及到少数的vue3知识
结合视频加上自身的理解,写成了这一篇笔记
视频地址
https://www.bilibili.com/video/BV1Zy4y1K7SH?spm_id_from=333.999.0.0
vue是一套构建用户界面的渐进式JavaScript框架
构建用户界面:将所拿到的数据变成界面
渐进式:从简单到复杂
组件化:将每一个功能和模块进行封装起来,使得代码有更高的复用率和维护性
声明式编码:传统中的数据是通过命令式编码,下达命令操作dom来进行数据渲染,而在vue中是使用声明式编码,完全不需要操作dom元素
虚拟Dom:虚拟Dom就是将内存中的数据渲染成界面
M:模型(model)对应data中的数据
V:视图(view)模板
VM:视图模型(ViewModel)vue实例对象
开发版本会有警告和调试模式,开发版本的报错信息可以找到,也会提示的相当明确
生产版本不会有警告,一般用于项目上线,报错信息一般就是底层报错信息,开发者根本看不懂哪里错了,所以在开发项目的时候使用开发版本的vue,等到项目要打包上线部署的时候再切换成生产版本的vue
//开发环境
//生产环境
在开发或者学习的过程中,会出现当前是开发环境,就会出现警告,可以通过该命令取消警告
Vue.config.productionTip = false
该文件为vue2的开发者工具,在Chrome浏览器打开右上角三个点里面的更多工具,点击扩展程序,然后将所需要安装的包拖动到里面即可安装
百度网盘链接: https://pan.baidu.com/s/17hzWJeFcN-JDsg9Cjs-KTg
提取码: fgt6
首先要使用vue就必须先new一个vue实例
el为挂载点,可以挂载id,也可以挂载class,标签,一个el挂载点只能管理一个容器
data为数据,在后续可以使用该数据
容器和实例之间对应的关系为1对1,不可以1对多,或者多对多
new Vue({}) 创建vue
el: "" 挂载点
vm.$mount("") 和el用法一样,vm是vue创建的实例,$mount("")里面写的是挂载的容器
data: {} 数据对象
{{name}} 使用name数据,里面也可以写一些js表达式
data:function(){return{}} 第二种写法,函数写法,必须return出去,在这个里面的this指向的是vue实例,不可以使用箭头函数,否则指向就会变成全局window
动态绑定,绑定一些标签属性的,可以将所绑定的属性作为js表达式使用,如果不加则会当成字符串进行解析
同时v-bind是单向数据绑定,假如有一个input标签和一个属性value,value改变input框中值也会改变,但是input框中值改变value却不会改变
//完整写法
//简写
动态绑定,一般绑定在表单元素,或者有value的标签之中,用户可以输入的标签之中,当value改变的时候这个输入框内容也会改变的,当输入框内容改变的时候value也会进行改变的,对于多选框绑定的时候,需要使用一个数组来进行接受
lazy //当输入框失去焦点或者回车的时候才可以绑定数据,一般用在节流防抖
trim //开头空格和结尾空格忽略
number //只能输入数字
事件处理中的this其实就是vue实例vm,如果使用的是箭头函数,那么this指向的就是window
其次每一个事件都有一个默认参数event,这个里面有很多信息,比如event.target就可以拿到触发事件的那个元素
事件如果使用v-on的话可以带小括号,也可以不带;但是作为插值语法,也就是{{}}的时候必须要使用小括号
注意,调用方法的时候如果没有传递参数那么是有event的,但是如果传递的话就没有,因此需要在传递参数的时候还需要用到event,那么就需要写上$event来进行占位,同时事件后面可以进行简单的写一些代码,对于偏多的代码就不允许在@事件后面书写了
//普通事件
clickHandle(){ //方法
alert("ok")
}
//传参事件
clickHandle(value){ //方法
alert("value")
}
//传参并且保留event
clickHandle(event,value){ //方法
alert("value")
}
事件修饰符就是对一些事件进行一些修饰,可以多个使用
例如prevent,stop使用在a标签上面,既可以阻止冒泡,也可以阻止默认事件
.prevent 阻止默认行为
.stop 阻止事件冒泡
.once 事件只能触发一次
.passive 事件立即执行,无需等待回调执行完毕
键盘抬起事件
键盘按下事件
获取按键名称和键码,键盘绑定的事件中进行获取键码和键名
e.key 获取键名
e.keyCode 获取键码
可以多个结合使用
.delete 退格删除
.enter 回车
.esc 退出
.space 空格
.tab tab(使用于keydown)
.up 上
.down 下
.left 左
.right 右
定义修饰符别名
huiche为定义的修饰符名称,13是对应键盘上面的键码
Vue.config.keyCodes.huiche = 13
属性:属性指的就是data中的数据,所有data中的数据都叫做属性
计算属性:是将数据进行处理然后返回,从而生成新的数据,这种情况下的数据叫做计算属性
所有的计算属性都写在computed里面,以函数的方式进行计算和使用,不需要带括号
实现逻辑:第一次执行的时候会调用数据代理的get方法,然后将结果存储到缓存中,后续再有调用的时候就直接从缓存中取出来,同时里面所依赖的值发生改变的时候这个get方法也会从新被调用,修改的时候会调用set方法进行修改
完整写法
computed:{
fullName:{ //定义计算属性
get(){
return "读取"
},
set(value){
return "修改"
}
}
}
简写,只能用于读取,不可以用作修改
computed:{
fullName(){ //定义计算属性
return "小猪佩奇"
}
}
使用
{{fullName}}
//直接当成属性去使用即可
监听属性的变化,可以监听data的的属性,也可以监听计算属性生成的属性
首先所有的监视属性要写在watch里面
其次info就是检测的属性名称或者计算属性名称
里面有一个handler函数,对于两个值,newValue和oldValue,一个为新值,一个为旧值
immediate为设置是否初始化,即上来就进行一下监视,上来刚监视的时候oldValue为undefined,因为此时oldValue没有值
watch:{
info:{
immediate:true,
handler(newValue,oldValue){
console.log(`值发生了改变,新的值为${newValue},旧的值为${oldValue}`)
}
}
}
如果需要监听多级下面的属性就需要用到""来括起来要监听的属性
'dataForm.a':{
handler(newValue,oldValue){
console.log(`值发生了改变,新的值为${newValue},旧的值为${oldValue}`)
}
}
深度监听
通过deep属性来开启深度监视,默认是不开启的,开启之后就可以检测到里面的属性了
dataForm:{
deep:true,
handler(newValue,oldValue){
console.log(`值发生了改变`)
}
}
简写模式,不需要配置项的时候只有handler时就可以进行简写
info(newValue,oldValue){
console.log("只发生了改变,新值为:",newValue);
},
computed能够完成的功能,watch 都可以完成,只不过有一些繁琐,但是watch能完成的功能,computed不一定可以完成
例如watch能够完成异步任务,而computed就完成不了
两个重要的原则:
所有被vue和组件所管理的函数,最好写成普通函数,这样this指向的时vm或者组件实例
所有不被vue所管理的函数最好写成箭头函数,这样this就是指向的vm或者组件实例
一般用于类名样式确定,但是不知道该元素是否使用该类名,或者是点击切换样式
第一种写法是直接写在标签里面,使用键值对的方式进行写,true和false为是否使用该类名
第二种方法是boxStyle里面的键值对,键名就是样式名称,然后对应的是布尔值,根据false或者true来判断是否使用该类名,然后直接动态绑定类名对象即可
//data中的写法
boxStyle:{
backgroundBlue:false,
backgroundRed:false,
backgroundGreen:true,
borderRadius:true
}
第三种绑定,绑定多个的时候需要加上[],单个绑定的时候可以去掉
111
动态绑定样式使用:来进行绑定,同时里面的内容使用对象键值对的方式来进行书写
第一种方法是写在标签里面,使用键值对的方式进行书写
001
第二种是抽离出来,然后通过动态绑定即可
002
//data中的写法
pStyleObj:{
color:'#ffffff',
border:'1px solid red',
backgroundColor:'plum',
width:'100px'
}
条件语句判断是根据是否满足条件来进行显示和隐藏或者其他操作
v-if在使用的时候中间不可以进行打断,否则后面都不奏效,同时v-if是直接移除
v-show使用于切换频率较高的时候,而v-if使用于切换频率较低的时候
显示隐藏
包裹元素的,但是不会破坏结构
用于展示列表数据
可以遍历对象,数组,字符串,对象和数组比较常用
key是唯一表示,相当于人的身份证
在遍历对象的时候,第一个值是值,第二个是键名,第三个是下标
在遍历数组的时候,第一个是值,第二个是下标
在循环渲染列表的时候这个会作为唯一标识,即相当于人的身份证,但是绑定key的时候需要注意两个问题,就是一般绑定分为两种,一种是使用数据的唯一标识id,一种是使用渲染的下标index,不建议使用index,因为会存在问题
index下标作为唯一下标标识的时候,一般用于不会破坏数据结构的时候,一旦破坏数据结构就会出现问题,在这里说一下key的工作原理,在第一次渲染的时候,会生成虚拟dom,然后渲染到界面上面,如果此时往前面添加一个元素,那么再次会生成一个新的虚拟dom,此时vue会进行diff算法进行比较,会和旧的虚拟dom进行比较,将不同部分进行替换,相同部分就直接拿旧的渲染上去,因此在使用index作为唯一标识的时候需要在不破坏结构的时候使用,否则就会出现问题,而且还会影响效率问题,因为会将不同的数据进行替换,从而新生成数据渲染到界面,影响性能,index用于在不破坏数据结构的时候
id作为唯一下标的时候以上index产生的问题就都得到了解决,因为id是唯一的,即使破坏结构,id也是唯一标识,例如现在新增了一条数据,此时vue通过diff算法对比,发现除了新增的数据,其他数据和之前的旧数据都想同,此时会将内存中的原有数据直接渲染上去,在此基础上再将新增的属性格外添加上去
没有设置key的时候vue会默认使用下标index来作为唯一标识
通过一个对象代理对另一个对象中属性的操作(读/写)
let obj = {x:100}
let obj2 = {y:200}
//往obj2里面添加一个x
Object.defineProperty(obj2,'x',{
//当读取这个数值的时候就将x进行返回
get(){
return obj.x
}
//当修改的时候会将修改的值从新赋值
set(value){
obj.x = value
}
})
首先通过vm来代理data对象中属性的操作(读/写)
更加方便的操作data中的数据
通过Object.defineProperty()把data中所有属性添加到vm上
为每一个添加到vm上的属性,都指定一个getter/setter
在getter/setter中去操作data中的数据(读/写)
对象
数组
添加属性,但是只能添加在data里面的属性上面,无法添加在data身上
可以往对象身上添加,也可以往数组身上添加,数组身上的key就是下标
vue.set(要添加到那个元素上面,要添加的key,要添加的value)
就是要将所显示的数据进行特定的修改,从而得到新的数据渲染到界面,不会影响到原数据
一般使用在进行简单处理的,对于复杂处理的结果就需要使用到computed或者watch
过滤器只能用于插值语法和v-bind绑定的数据
过滤器分为全局过滤器和局部过滤器,全局过滤器都可以进行使用,局部过滤器只能在当前进行使用
私有过滤器定义,与data,methods同级,time为定义的过滤器名称,return后为处理的结果
filters:{
time(){
return "处理的结果"
}
}
全局过滤器定义,time为名称,return后为处理的结果
Vue.filter('time',function(){
return "处理的结果"
})
使用,也可以进行传递参数,也可以进行多个过滤器使用
{{123 | time}}
多个传参使用,在传递的时候第一个参数为123,第二个参数为456,第三个参数为789
{{123 | time(456,789)}}
插入文本,例如在这里name为张三,那么此时渲染到界面的内容就是张三,同时如果p标签里面此时有值,那么会将值全部覆盖掉,与{{}}插值语法来对比的话,插值语法更加的好用,该方法用于较少
和v-text一样,但是于之不一样的是v-html可以解析html模板
但是存在很严重的安全问题,例如xxs攻击
一般用于开发者设置的模板结构,一定要是可以信任的,千万不要用在用户输入的结构上面使用,会导致xxs攻击
在vue模板没有渲染之前这个会存在,但是渲染之后就会消失,可以用在模板未渲染之前做一些事情
例如我们可以给有v-cloak这个属性的盒子隐藏,在vue渲染完毕的时候再进行显示,防止用户看到初始的数据,未经过处理的数据
在第一次动态渲染之后就变成静态内容
一般用需要初始数据的地方,例如num初始值为0,现在有一个按钮,每次点击的时候,这个num++,但是现在我需要这个num的初始值,不让它跟随这个值的变化而变化,此时就可以使用v-once
{{num}}
直接渲染,不进行任何编译,提高效率,用在不是响应式的地方,例如下面代码,直接渲染的就是{{n}}
{{n}}
像v-model,v-bind等等之类的都是vue帮你弄好的指令,用户也可以自己定义一个指令
自定义指令的第一种方法,在这里styleColor是定义的指令名称,element是获取的dom元素,你在谁身上使用,那么element就是那个元素,binding就是一些数据,例如binding.value就可以拿到所传递的值,例如我在使用的时候使用v-styleColor=“‘red’”,那么在使用的时候,binding.value就是red
directives:{
'styleColor'(element,binding){
el.style.color=binding.value
}
}
自定义指令的第二种方法,bind函数是元素与指令绑定的时候就触发,即一上来就触发,inserted函数是指令所在的元素被插入到界面的时候触发,updata函数是指令所在模板被重新解析的时候触发
directives:{
'styleColor':{
bind(element,binding){
},
inserted(element,binding){
},
updata(element,binding){
}
}
}
全局指令
全局定义指令的写法,bind就是指令名称,后面就是指令操作了,如果是函数就直接写成函数,如果有多个就写成对象格式
Vue.directive('bind',{
bind(element,binding){
},
inserted(element,binding){
},
updata(element,binding){
}
})
即叫做生命周期函数,又叫做生命周期回调函数,也叫钩子函数
生命周期是什么,简单来说就是一些函数,只不过在vue中是在什么时间调用指定的函数
生命周期的函数是vue帮你定义好的,名称是固定的
其次生命周期函数里面的this指向的vue实例
第一个环节和生命周期函数
环节:初始化,生命周期函数,事件,数据代理还为开始
函数:此时这个函数还不能访问到data中的数据和methods中的方法
beforeCreate(){}
第二个环节和生命周期函数
环节:初始化,生命周期函数,事件,数据代理都已经完成
函数:此时这个函数可以访问到data中的数据和methods中的方法
created(){}
第三个环节和生命周期函数
环节:vue开始解析模板生成虚拟dom,但是此时的虚拟dom存在于内存中,并未渲染到界面,此时界面还是未经渲染的界面
函数:此时这个函数对dom的操作最终都不奏效
beforeMount(){}
第四个环节和生命周期函数
环节:vue将内存中的虚拟dom拿出来渲染到界面
函数:在此绑定事件,渲染初始化数据
mounted(){}
第五个环节和生命周期函数
环节:数据更新,此时数据是新的,但是界面还未从新渲染,页面数据还是旧的
beforeUpdate(){}
第六个环节和生命周期函数
环节:数据更新,并且界面渲染完毕
updated(){}
第七个环节和生命周期函数
环节:销毁vue实例,销毁之前,此时data,methods等等都还可以用,但是不会再次触发updata的方法了
beforeDestroy(){}
第八个环节和生命周期函数
环节:彻底销毁vue实例
destroyed(){}
脚手架是基于vue开发的一个快速创建项目结构的工具
安装,安装之前需要准备node,-g使用全局下载,后续只需要使用即可
npm install @vue/cli -g
使用命令行进行创建项目,vue_project01为创建的项目名称,npm run serve运行这个项目,在运行完成项目之后,会自动开启一个服务,然后访问服务地址即可查看到项目创建的界面
vue create vue_project01
npm run serve
启动可视化界面需要vue/cli版本在3.0以上才可以进行开启,直接打开一个终端或者cmd命令窗口输入该命令即可打开一个可视化的界面
vue ui
├── babel.config.js ES5转ES5
├── package-lock.json 文件说明以及版本信息
├── package.json 文件说明以及版本信息
├── README.md 项目使用
├── node_modules 所依赖的所有包
├── src 主要目录
│ ├── main.js 整个项目的入口文件
│ ├── assets 放置静态资源,全局css,全局js等等
│ ├── components 组件放置文件及
│ └── App.vue 大哥,管理众多组件
├── public 界面文件夹
│ ├── index.html 主要界面
│ └── icon.vsg 网站的图标
在开发项目的时候后续需要用到webpack进行打包,打包完成的文件浏览器可以看懂,所有就不需要vue进行模板解析了,因此我们在开发项目的时候引入的vue是不完整的,没有模板解析,没有模板解析此时就需要用到render函数来进行将模板渲染
render是一个函数,里面接受一个参数,这个参数是一个函数,调用这个函数的时候需要将模板进行传入,然后进行解析,将解析完成的结果进行返回,下面就是简写模式
render:h => h(App)
首先在根目录下创建一个vue.config.js文件
然后就可以在里面进行修改默认配置了
所有的默认配置都需要写在module.exports{}里面
例如在开发项目的时候可以将阻止代码检测关闭掉,阻止开发效率,等开发完成之后再进行代码检测
module.exports = {
lintOnSave: false //阻止代码检测
}
很多时候请求是做不到同源策略的,因此就会出现跨域问题,后端也可以进行解决跨域问题,同时前端也可以处理这个问题,我们可以使用vue-cli中的配置代理即可完成跨域
配置的时候会优先在前端进行匹配,如果没有匹配到就会去进行配置代理
在根目录创建文件夹vue.config.js,所有的代码写在这个里面
我们在请求的时候只需要请求地址跟我们的网站地址一致就可以,后续的请求会通过代理发送到所配置的代理身上去进行请求,这种方法用于单个请求,只能配置一个
module.exports = {
devServer: {
proxy: 'http://10.1.183.45:1010'
}
}
配置多个代理,分别匹配以/request8080开头的请求和以/request8081开头的请求进行处理
当我请求的端口后面是request8080的时候,会去请求http://10.1.183.45:8080下面的接口
当我请求的端口后面是request8081的时候,会去请求http://10.235.18.45:8081下面的接口
pathRewrite设置将配置的请求判断取消掉,否则就会带着这个发送出去请求,则无法访问到接口
配置代理功能很常用,例如这个案例,是去请求不同的接口根路径,我们不需要创建多个axios实例来进行请求不同根路径下面的接口,现在就可以直接使用配置代理,根据请求头判断是要请求那个根路径下面的接口
module.exports = {
devServer: {
proxy: {
'/request8080':{
target: 'http://10.1.183.45:8080',
changeOrigin: true,
pathRewrite: {'^/request8080':''}
},
'/request8081':{
target: 'http://10.235.18.45:8081',
changeOrigin: true,
pathRewrite: {'^/request8081':''}
}
}
}
}
模块:指的就是js文件,形成一个一个的js文件,然后需要的时候进行引入,这种方法就叫做模块
组件:指定一块的功能,因为一个界面的功能及其复杂,其次就是复用性高,简化编码,提高开发效率
模块化:把一个大型的js文件拆分成几个小的js文件,每个js文件对应指定功能,这种情况叫做模块化开发
组件化:就是一个大型界面,按照区域及逆行拆分,最后拼接形成一个界面,这个就叫做组件化开发
非单文件组件:一个文件里面包含多个组件,这个文件叫做非单文件组件
单文件组件:后缀名以vue结尾
组件的三大步骤,定义组件,注册组件,使用组件
定义组件,option和之前写vue一样,但是没有el挂载点,同时data需要写成一个函数,然后返回,从而实现组件的复用性,使用template定义模板,name为名称,这个名称方便在vue浏览器开发者工具中更加直观的看到你使用的是那个组件
const name = Vue.extend(
template:`
我的名字是:{{name}}
`,
name:'namel',
data(){
return{
name:'三七'
}
}
)
注册组件,局部使用组件,names为定义的名称,name为模板,注册组件的时候names为名称,但是如果定义成myName,那么在使用的时候就需要使用my-name进行使用,其实myName也可以使用,但是只能用在脚手架里面
new Vue({
el:'#box',
components:{
names:name
}
})
全局注册组件
Vue.component("names",name)
使用
简写模式,只能用在脚手架里面
组件里面的this指向的是VueComponent,简称vc,VueComponent是一个构造函数,是Vue.extend生成的
每次调用这个Vue.extend的时候都会从新生成一个VueComponent构造函数
其次创建组件生成的vc和创建vue生成的vm不一样,它们有99%相同,但是有一些是不一样的,例如vue创建的vm可以有el挂载点,data可以写成一个函数或者对象,而组件创建的vc就没有,简单来说,vc有的vm都有,vm有的vc不一定有
组件生成的vc原型对象和Vue生成的vm原型对象相等
VueComponent.prototype.__proto__ == Vue.prototype
这么做的好处就是vue生成的vm身上有很多方法和属性。在组件生成的vc上面也可以进行使用这些方法和属性
在原型对象上面找不到的时候应该是去Object身上进行最后的查找,但是此时Vue做了一件事情,它没有让这个vc直接去最后的Object身上找,而是去vm的原型对象上找,如果找不到,最后再去Object身上进行最后的查找
单文件组件是将组件进行抽离出去,后续通过引入注册即可进行使用
单文件组件时以.vue进行结尾,里面分别分为三块,一个是template模板区域,定义结构,一个是script交互区域,进行交互,一个是style样式区域,进行样式设置
//模板区域
用来给元素或者子组件引用信息(替换了原有的id,可以拿到当前元素,组件的话可以拿到当前组件的实例对象)
例如使用ref定义,然后名称为div01,后续就可以通过this.$refs.div01拿到当前dom元素
ref属性
用在父组件给子组件进行传值,只需要在通过标签的时候往上面自定义属性名称和值就可以进行传递,自定义的名称不要和vue所带的起名一样,例如ref和key等等,传递的时候也可以使用:进行动态传递,在接受的时候有三种方法,第一种就是普通的接受,不在乎类型,第二种就是规定类型的接受,第三种就是可以设置默认值
prop传递过来的数据不要直接进行修改,可以在data中定义新名称,将传递过来的数值进行重新赋值,然后改这个定义的值,在props中只进行了浅检测,并没有进行深度检测,简单来说不要直接修改就不会报错,例如改对象里面的元素,就不会报错,但是如果直接修改这个对象,就报错,不建议直接修改props拿到的值
export default ({
props:{ //第一种方法,规定所传递过来的类型
name:String,
age:Number,
sex:String
},
props:["name","age","sex"], //第二种类型,直接拿过来,不在乎类型
props:{
name:{
type:String, //第三种类型,可以设置是否必传和默认值
required"true,
default:'老王'
},
age:{
type:Number, //类型
required"true, //是否必传
default:19 //默认参数
},
}
})
可以把多个组件所拥有的共同配置提取成一个混入对象,一般用在有公共代码的时候,就是目前有十个组件,我这十个组件里面都有一个logThis方法,并且里面都是输出this,我此时就没必要每个组件里面写一下了,就可以通过混合进行抽离定义,然后在需要的地方进行使用,如果都有用到,就直接在全局mian.js里面进行引入
定义一个js,在里面进行编写,编写完成之后进行导出,然后通过import进行导入
export const mixin01 = {
data(){
return{
x:100,
y:100
}
}
}
import {mixin01} from "./mixin.js"
export default{
mixins:[mixin01]
}
用于增加Vue
包含一个install方法的一个方法,install的第一个参数是Vue,后面就是所传递的参数
在这个里面定义了一个全局指令,一个全局过滤器,还有一个方法
在后续使用的时候进行正常使用即可,其实也可以在mian.js中直接进行定义这些指令,过滤器,方法等等,但是这样的话就显得太过于乱了,不易于维护,所以可以进行抽离,然后封装成一个插件,然后需要的时候引入注册即可
//定义
export default {
install(Vue) {
// 定义全局指令
Vue.directive("colorG01", function (element, binding) {
element.style.color = binding.value
})
// 定义全局过滤器
Vue.filter("timeG01", function (n) {
return "我是全局过滤器01处理的结果:" + n
})
//往原型身上挂载方法
Vue.prototype.hello = (e) => {
console.log("我是全局定义的方法,你所传递过来的参数为:"+e)
// alert("哈哈哈")
}
}
}
//注册,在main.js中进行注册
import plugins from "./plugins/plugins"
Vue.use(plugins)
//使用
//调用插件中定义的指令
111111111
//调用插件中定义的过滤器
学校时间:{{school.time | timeP}}
//调用插件中定义的方法
设置每个组件之间的样式都只能在自身组件里面使用,其他组件不可以使用改样式,设置作用域
在vue项目里面,建议安装指定版本,否则就会因为版本过高而导致报错
npm install [email protected] [email protected] --save-dev
规定样式类型为less类型
还是使用props方法,只不过这个不只可以传递数据,也可以将一个函数方法传递过去
在使用这个组件的时候通过props的方式进行方法传递,addData这个方法例如是添加一条数据,需要传递参数,那么就可以在子组件里面,先使用props进行接受,然后定义一个事件,点击这个事件的时候调用传递过来的方法,然后往里面传递值,这样就将数据传递过去了,同时调用父组件里面的方法,将数据添加到列表中,实现了子向父传递数据
//父组件
data() {
return {
dataList:[
{
name:'张三',
id:'001'
},
{
name:'李四',
id:'002'
}
]
}
},
methods: {
addData(e){
this.dataList.unshift(e)
}
}
//子组件
props:['addData'],
methods: {
add(){
this.addData({name:'秘密',id:'005'})
}
},
首先第一点就是拆分静态组件,根据功能点进行拆分
第二点就是实现动态组件,考虑好组件放置的地方,是一个再用,还是多个再用
第三点就是实现交互
自定义一个事件,也可以用在子向父传递参数,但是与之不同的是不需要使用props进行接受了,所创建的自定义事件,在实例销毁后就不可以进行使用了
例如下面,自定义了一个事件atc,调用这个事件的时候就会调用customEvents这个事件,在customEvents这个事件里面接受传递过来的参数进行打印,在子组件里面,我们定义了一个按钮,点击的时候调用这个send方法,然后在这个send方法里面通过this找到vc实例,再通过$emit进行调用里面atc方法和传递参数
customEvents(name){
console.log(name);
}
send(){
this.$emit("atc","张三")
}
将所绑定的自定义事件进行解绑,一般在组件销毁之前进行解绑所有自定义事件
//单个解绑,只能解绑一个,解绑的事件名称为atc
this.$off('atc')
//多个解绑,解绑的事件名称为atc,atd
this.$off(['atc','atd'])
//将所有的自定义事件解绑
this.$off()
native修饰符,用在组件上面,在组件上面使用原生的事件是使用不了的,它会把原生事件当成自定义事件进行使用,在使用的时候需要通过事件,然后调用this.$emit(“事件名称”)才可以进行使用,因此可以在这里使用native修饰符
安装全局事件总线,后续使用的时候直接使用 b u s 上 面 的 bus上面的 bus上面的on和$emit即可
new Vue({
router,
render: h => h(App),
beforeCreate(){
Vue.prototype.$bus = this
}
}).$mount('#app')
定义方法接受数据,往$bus身上挂载一个方法,这个方法名称为hello,这个方法可以拿到所传递过来的e值,并且给当前data中的msg从新赋值改变数据,这里使用箭头函数,否则this指向是有问题的,其次就是这个挂载方法建议在挂载完毕的时候定义全局总线事件,还有在定义方法的文件里面,在生命周期函数销毁之前,解绑该事件
mounted() {
this.$bus.$on("hello", e => {
this.msg = e
})
},
beforeDestroy() {
this.$bus.$off('hello')
}
传递数据,就是调用$bus上面所定义的hello方法,然后就是要传递的数据
accept(){
this.$bus.$emit('hello','我是demo08传递过去的数据')
}
总之来说和全局事件总线差不多
第一步安装第三方库
npm install pubsub-js
第二步往入口文件main.js里面进行引入,然后往vm身上挂载一个方法
// 引入消息订阅
import pubsub from 'pubsub-js'
// 往vm身上挂载一个消息订阅方法
Vue.prototype.$pubsub = pubsub
订阅,接受数据,通过回调拿到数据,因为里面有两个参数,第一个为事件名称,第二个为数据,因此在这里就可以使用_来进行占位,在最后组件销毁之前记得取消这个订阅
mounted() {
this.pid = this.$pubsub.subscribe("hello",(_,data) => {
console.log("hello事件被触发了,触发者是:"+data)
this.msg = data
})
}
beforeDestroy() {
this.$pubsub.unsubscribe(pid)
},
发布,提供数据,调用publish身上的hello方法,然后传递数据
accept() {
this.$pubsub.publish('hello','我是demo08传递过去的数据')
}
当事件中的事情都执行完毕的时候进行执行$nextTick的回调函数,也算是vue的第九个生命周期函数
accept() {
this.$pubsub.publish('hello','我是demo08传递过去的数据')
this.$nextTick(function(){
console.log(this);
})
}
由于后续项目中很少用到动画的效果,在这里这篇笔记就暂时不记了,也没有什么太大的作用,在这里介绍一个第三方的库animate.css库来实现动画效果,具体可以参考官方文档,后续自己写动画效果完全是在浪费时间
Animate类名参考文档地址
https://animate.style
动画标签,appear是设置是否上来默认执行一次,transition用在包裹一个元素的时候
你好呀!!!
transition-group用在包裹多个动画的地方,但是里面的元素必须有一个key值,key值不可以重复出现
你好呀01!!!
你好呀02!!!
下载第三方包
npm install animate.css --save
在main.js文件进行一下引入即可
// 引入animate.css动画库
import "animate.css"
使用的时候还是使用vue所带的标签,但是标签里面必须有三个属性,name是固定的,enter-active-class为出现的时候,然后设置动画类名即可,leave-active-class为隐藏的时候
name="animate__animated animate__bounce"
enter-active-class="animate__bounce"
leave-active-class="animate__flash"
首先先说一下插槽是干什么的吧,现在有一个需求,要求展示三个盒子,每个盒子里面都有一个标题,样式都一致,下面一个展示图片,一个展示列表,一个展示视频,我们就可以把这个封装成一个组件,然后渲染三次就可以,标题通过父向子传递进行动态渲染
默认插槽,通过slot进行占位,然后将使用组件时候里面所有的东西填充到该地方,如果没有占位就会显示默认的东西
//定义,使用 进行占位,后续所传递过来的数据会将这个标签进行替换掉
{{title}}
我是插槽
//使用,为传递的数据,后续会替换掉组件中的
具名插槽,可以定义多个插槽,传递数据的时候就可以选择数据需要渲染到那个插槽里面
//定义,使用 进行占位,使用name进行定义多个插槽名称,例如这边有两个插槽,
{{title}}
我是插槽01
我是插槽02
//使用,将会替换掉slot01插槽,你好呀
会替换掉slot02插槽
你好呀
//使用,将会替换掉slot01插槽,会替换掉slot02插槽
你好呀
你好呀!
你好呀!!
作用域插槽
数组是在插槽里面,但是布局结构需要由使用这个插槽的人来决定,在这里就可以使用到作用域插槽,同时作用域插槽也可以定义多个起name
//定义,这里的title是传递过来的数据,dataList是自身的数据
{{title}}
默认显示
//我们在使用作用域插槽的时候,必须使用template模板,必须使用,里面通过slot-scope来获取数据,由于我传递的数据名称是dataList,我在接收的时候也就使用这个名称进行接受数据,就不重新进行命名了,然后就可以渲染数据,然后拿到数据之后就可以根据自己设置样式进行渲染了
- {{item}}
下载这个第三方库
npm install vue-resource
在main.js里面进行引入注册
import VueResource from 'vue-resource'
Vue.use(VueResource)
使用,和axios使用方法几乎一致,只不过axios变成了$http
methods(){
async getStudentList(){
const res = await this.$http.get("http:125.12.34.167:8080/student")
}
}
简单来说vuex就是一个仓库,用于管理状态(数据),共享数据,同时也是响应式的,只要仓库中的值进行改变,那么对于所用到该仓库值的组件内容也会发生改变,传统的方法中,组件之间如果想传递一个值需要很多步骤,很麻烦
例如父组件像子组件传值需要使用v-bind来进行传值,子组件通过props来进行接受所传递过来的值
例如子组件想要给父组件传值,需要通过this.$emit(事件名,子组件给父组件传递参数),父组件通过v-on事件绑定方法来进行调用子组件传递的方法,然后在方法里面拿到值再赋值到自身
兄弟组件之间传值需要用到全局事件总线bus, o n 接 受 数 据 的 那 个 组 件 , on接受数据的那个组件, on接受数据的那个组件,emit发送数据的那个组件
例如现在1想要向3传值,那么需要先把值传递到2,2再传递到3,,而在vuex中,只需要将1的值放到store中,谁如果需要用到之间去store里面拿就可以
下载vuex插件
npm install vuex
然后在src目录下创建一个文件夹,建议名称是store,里面创建一个文件index.js,先进性导入vue和vuex,再将vuex进行注册,因为是插件,所以要进行一下注册,然后new一个vuex里面的store,里面传入配置项,最后进行导出
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
},
mutations: {
},
actions: {
},
modules: {
}
})
使用,在main.js里面进行导入,然后在new Vue的时候传入进去即可,现在在所有的vm和vc身上都可以看见一个$store
import store from './store'
new Vue({
router,
store,
render: h => h(App)
}).$mount('#app')
定义公共值,state,不可以直接修改state的值,虽然在自身中可以修改,但是不合法,这样的话在vue开发者工具里面没法具体的跟踪到是那个组件里面修改的该值,所以只可以在mutaions里面定义方法进行修改,其他组件通过调用这个方法进行修改
定义值:
state: {
num:0
},
使用方法一,直接通过this.$store.state来找到这个实例下面的state,再通过.进行获取,用于数据量较小的时候
{{this.$store.state.num}}
使用方法二,先进行导入,通过计算属性解构出来所需要的值,然后就可以直接像使用自身值一样进行使用,一般用于数据量较大的时候,比较常用
import { mapState } from 'vuex'
computed: {
...mapState({n1:'num01',n2:'num02'}), //第一种写法,对象写法
...mapState(['num01','num02']), //第二种写法,数组写法
},
{{ num01 }}
定义方法,mutaions,定义方法的作用就是修改数据,因为不允许其他组件使用该仓库数据的时候直接修改,因此只能在当前实例下创建方法,通过调用方法更好的进行追踪,其实在这个里面写异步操作也可以,比如需要发送到服务器进行判断,然后判断完成之后再进行处理数据,但是假如现在有很多,此时就需要用到actions进行封装,来进行更加方便的处理
定义方法:
mutations:{
addNumPass(state,i){
state.num+=i
},
}
使用方法一,定义完方法之后在所需使用的地方添加一个事件方法,在方法里面通过指向实例来调用里面的方法,需要传递参数的话就往后添加所需要传递的参数,如果不需要则直接将参数去掉即可,在这边展示的代码是需要拆掉你参数的:
this.$store.commit('addNumPass',10)
使用方法二,先将方法进行导入,然后对象解构出来所需要的方法,然后就可以像使用自身方法进行使用,用于方法较多的时候:
import { mapMutations } from 'vuex'
methods: {
...mapMutations({a1:'addNumPass01',a2:'addNumPass02'}), //第一种写法,对象写法
...mapMutations(['addNumPass01','addNumPass02']), //第二种写法,数组写法
},
定义处理异步操作,actions,对于一些异步的方法,在mutaions里面使用时不管用的,因此就需要用到actions来将处理的方法再次进行包装成异步的方法,最后调用actions里面的方法来继续使用,其次就是进行处理数据,判断逻辑,然后根据逻辑调用方法
然后说一下这个context,这个是一个简洁版的store,里面有commit,dispatch,state,先说一下这个三个最常用的,dispatch的作用是当逻辑太过于复杂的时候可以进行拆开,然后异步执行完成之后接着执行下一步逻辑判断,直接通过context.dispatch再次调用里面的方法,state是一些时候需要根据数据进行判断,此时就可以通过context.state拿到数据进行判断,commit就是调用mutaions里面定义的方法
定义方法:
addNumPassAsync(context,i){
setTimeout(() => {
context.commit('addNumPass',i)
},2000)
},
使用方法一:
this.$store.dispatch('addNumPassAsync',20)
使用方法二:
import { mapActions } from 'vuex'
methods: {
...mapActions({r1:'reduceNum01',r2:'reduceNum02'}), //第一种写法,对象写法
...mapActions(['reduceNum01','reduceNum02']), //第二种写法,数组写法
},
geeter,将数据处理后返回,不会影响到原数据,简单来说就是我有一个数据为0,但是想要给到你手上的时候这个数据是10,在这里就需要进行处理,处理的方法就是元数据+10然后进行返回,后续数据变动的时候,这个数据也会在变更数据基础上加10然后返回给你
定义处理数据:
getters: {
showNum(state){
return state.num+10
}
}
使用方法一:
拿到经过geeter处理后的值:{{this.$store.getters.showNum}}
使用方法二:
import { mapGetters } from 'vuex'
computed: {
...mapGetters({s1:'showNum01',s2:'showNum02'}), //第一种写法,对象写法
...mapGetters(['showNum01','showNum02']), //第二种写法,数组写法
},
拿到经过geeter处理后的值:{{showNum01}},{{showNum02}}
vuex在真正开放项目的时候是抽离化开发,比如现在有一组用户数据和商品数据,就不能全部放在一个里面,一是难以维护,二是代码合并的时候防止冲突
例如在这边我们将vuex中的数据操作进行了抽离成User和Commodity,然后引入,通过modules进行模块管理,User里面就是管理用户数据和操作,Commodity里面就是管理商品数据和操作,这样的话就层次分明,每个管理对应的数据和操作,
import User from './user'
import Commodity from './commodity'
export default new Vuex.Store({
modules: {
User,
Commodity
}
})
User文件预览,和之前定义的一样,只不过进行了抽离,需要将数据进行导出,namespaced指的就是后续在组件里面使用的时候根据名称进行使用,抽离出来还有一个好处,就是方法名和数据名称都可以和其他抽离的重复,且不会出错,namespaced必须开启,否则后面是拿不到数据的,因为有多个文件,不通过名称就不知道该去拿那个里面的数据和方法
export default {
namespaced:true,
state: {
userList: ['张三','李四']
},
mutations: {
AddUser(state, value) {
state.userList.push(value)
},
},
actions: {
addUser(context, value) {
setTimeout(() => {
context.commit('AddUser', value)
}, 1000)
}
},
getters: {
userLength(state){
return state.userList.length
}
}
}
拿数据和处理过后的数据,在这里就可以很明确的知道自己要拿的是User模块下面的userList数据和处理完成的userLength
//第一种写法
computed:{
...mapState('User',['userlist']),
...mapGetters('User',['userLength'])
}
//第二种写法
this.$store.state.User.userList
this.$store.getters['User/userLength']
拿方法,在这里就可以很明确的知道自己要拿的是User模块下面的AddUser方法和addUser方法
//第一种写法
methods:{
...mapMutations('User',['AddUser']),
...mapActions('User',['addUser']),
}
//第二种写法
this.$store.commit('User/AddUser')
this.$store.dispatch('User/addUser')
vue-router也叫做路由,是通过路径改变从而切换组件渲染界面,实现单文件界面应用
同时vue-router是一个插件库,专门实现SRA应用
SRA应用:单页面,整个应用只有一个完整的页面,点击界面中的导航不会刷新界面,只会做界面的局部更新,数据需要通过ajax进行获取
安装
npm install vue-router
在src文件夹下面直接新建一个文件夹router,里面创建一个index.js文件,文件里面内容如下
因为vue-router依赖于vue,因此需要先引入vue再引入vue-router,然后注册VueRouter,然后需要new VueRouter,里面传入配置项routes,此时我们配置多个路由,使用对象的方式进行配置,path为路径,component为要渲染的组件,最后将整个进行导出
mode为路由模式,路由的模式分为hash模式和history模式,后面会有详细介绍,这里只是告诉你可以在这里设置路由模式为hash还是history模式
import Vue from 'vue'
import VueRouter from 'vue-router'
import Demo01 from '../components/demo01'
import Demo02 from '../components/demo02'
Vue.use(VueRouter)
export default new VueRouter({
mode:'hash',
routes: [
{
path: '/demo01',
component: Demo01
},
{
path: '/demo02',
component: Demo02
}
]
})
然后在main.js文件里面导入并且在new Vue的时候传递进去
import Vue from 'vue'
import App from './App.vue'
import router from './router'
Vue.config.productionTip = false
new Vue({
router,
store,
render: h => h(App)
}).$mount('#app')
作用和a标签一致,最后就会转换成a标签,to为要跳转的路径,这个路径和所配置的路由规则路径要一致,如果是嵌套路由,那么需要加上父亲的路径地址,active-class="activeStyle"为激活时候的类名
跳转到Home主页
就是访问一个地址的时候默认跳转到另外一个地址
{ path: '/',component: Demo10,redirect:'/Demo10' },
路由组件一般放在pages文件夹里面,普通组件放在components文件夹
组件之间的切换,都会销毁上一个组件,需要的时候再进行挂载
每个组件都有自己的$route属性,里面存储自己的路由信息
整个应用只有一个 r o u t e r 属 性 , 可 以 通 过 router属性,可以通过 router属性,可以通过router进行获取到
路由嵌套使用的是children,然后里面接着写路由规则即可,但是里面的子路由路径就不需要写/了
routes: [
{
path: '/demo01',
component: Demo01
},
{
path: '/demo02',
component: Demo02,
children: [
{
path:'new',
component: New
},
{
path:'message',
component: Message
}
]
}
]
router-link方式传递参数
跳转到Home主页
接受参数
this.$route.query
一般不用,用在多级路由嵌套的时候,比较繁琐的情况下,可以简化代码,在路由规则的时候加上一个name即可,然后台哦转的时候指定这个name
跳转到Home主页下面DMO下面的vck下面的wr
跳转到Home主页下面DMO下面的vck下面的wr
一般不用,不过多介绍,建议使用query这种方式,而且使用params这种方式的时候,在配置路由规则的时候需要使用占位,例如path:‘message/:id/:name’
下面这种写法不可以使用path,只能使用name
跳转到Home主页
第一种写法,数据是死的,写在路由里面,接受使用props进行接受使用
{
name:'demos05',
path: '/demo05',
component: Demo05,
props:{a:1,b:'hello'}
},
props:['a','b'],
第二种写法,只能用在params里面,之间将params所传递过来的参数进行接受,例如传递过来两个参数,id和title,在这里直接进行接受,但是只能用于params传递参数
props:true
props:['id','title'],
第三种写法,也是最常用的方法,函数写法,route就是当前路由配置项,取到里面所传递过来的id和title,从新赋值返回出去,这里是query传参方式
{
name:'demos05',
path: '/demo05',
component: Demo05,
props(route){
return{
id:route.query.id,
title:route.query.title
}
}
}
props:['id','title'],
作用:控制路由跳转时候的浏览器记录模式
浏览器记录模式分为push和replace,push是追加历史记录,replace是替换当前记录
开启replace模式
跳转到Home主页
不需要依赖于router-link实现路由切换,更加灵活
//push跳转
this.$router.push({
path:'/home',
query:{
id:'001',
name:'张三'
}
})
//replace跳转
this.$router.replace({
path:'/home',
query:{
id:'001',
name:'张三'
}
})
//前进
this.$router.forward()
//后退
this.$router.back()
//可前进可后退,正数代表前进几步,负数代表后退几步,0的话就是当前界面,也可以理解成当前界面刷新
this.$router.go(3)
用于缓存组件,组件之间切换的时候会销毁组件,假如现在有一个注册组件,我写了一些数据,但是一不小心切到其他组件了,我再次切回来的时候什么也没有了,注册组件从新渲染了,又的从新弄,体验就很不好,因此我们不希望注册组件切换的时候销毁,就需要依赖缓存了
在路由占位的外面包裹上一个keep-alive标签即可实现路由缓存,include指定缓存的路由名称,名称为路由定义的名称,在vue文件里面,export default({})里面除了常用的data,methods等等之类,还有一个name,绑定的名称就是这个name,例如现在有一个路由组件,这个路由组件在里面定义的name为Demo01,在这个我就想缓存这个组件
组件出现的时候执行,即从没有出现在你面前变成出现在你面前
函数:activated() {}
activated() {
console.log('组件被激活了');
}
组件消失的时候执行,即从出现在你面前变成消失在你面前,组件切走的时候执行
函数:deactivated() {}
deactivated() {
console.log('组件失活了');
}
作用:对路由的权限进行控制
分类:全局守卫,独享守卫,组件内守卫
全局路由守卫
前置路由守卫,指的就是在跳转之前进行处理,to为要访问的路径信息,from为当前路径信息,next是执行下一步,在这里可以进行一些判断,比如判断是否登录了,如果没有登录就不执行next(),跳转到登录界面,让用户进行登录,就是判断进行一些拦截
router.beforeEach((to,from,next) => {
console.log(to);
console.log(from);
next()
})
后置路由守卫,指的就是在跳转之后进行处理,to为要访问的路径信息,from为当前路径信息,在这里可以进行一些设置,比如改变网页标题
router.afterEach((to,from) => {
console.log(to);
console.log(from);
document.title = from.meta.title
})
我们可以在配置路由规则的时候传递一些参数,后续进行使用
{
path: '/demo04',
component: Demo04,
meta:{title:'Demo04界面'}
}
独享路由守卫
只配置单个路由的守卫,在这个路由规则里面进行配置,只有前置路由守卫,没有后置
{
path: '/demo04',
component: Demo04,
meta:{title:'Demo04界面'},
router.beforeEnter((to,from,next) => {
console.log(to);
console.log(from);
next()
})
}
组件内守卫
组件内守卫分为进入守卫和离开守卫,所谓的进入守卫就是当通过路由规则进入该组件时候调用,离开守卫就是当离开该组件的时候进行调用
beforeRouteEnter (to, from, next) {
console.log('进入组件');
next()
},
beforeRouteLeave (to, from, next) {
console.log('离开组件');
next()
}
对于url来说,有#的就是hash模式,#后面的就是hash值,这些值不会带到HTTP请求中
hash模式中有#,地址看起来不太美观,后续手机app校验比较严格,有可能不合法
兼容性较好
history地址干净,没有#
兼容性没有hash好
部署的时候需要后端人员支持,否则会报错404找不到
简单来说路由懒加载也叫延迟加载,即在需要的时候进行加载。
当打包构建应用时,JavaScript 包会变得非常大,影响页面加载。
如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样就更加高效了。
// 官网可知:下面没有指定webpackChunkName,每个组件打包成一个js文件。
const Student = () => import('../components/student')
const Teacher = () => import('../components/teacher')
// 下面2行代码,指定了相同的webpackChunkName,会合并打包成一个js文件。
const Student = () => import(/* webpackChunkName: 'School' */ '../components/student')
const Teacher = () => import(/* webpackChunkName: 'School' */ '../components/teacher')
基于移动端的组件库
Vant https://vant-contrib.oschina.io/vant
Cube Ul https://didi.github.io/cube-ui
Mint Ul https://mint-ui.github.io
基于PC端的Ul组件库
Element Ul https://element.eleme.cn
Iview Ul https://www.iviewui.com/
分为生产和发布模式,-d指的就是生产版本,-s指的就是发布版本
npm install element-ui -d
npm install element-ui -s
2020年9月18日,vue发布了3.0版本
性能比vue2打包快41%,初次渲染块55%,更新渲染快133%,内存减少54%
源码升级,使用Proxy代替defineProperty,重新虚拟DOM的实现(优化了diff算法,更加快和简介)和Tree-Shaking(不再进行全部引入了,需要什么引入什么)
支持TypeScript
特性升级
vue/cli版本必须在4.5.0以上
使用命令行进行创建项目,vue3为创建的项目名称,在安装的时候选择vue3.0即可,npm run serve运行这个项目,在运行完成项目之后,会自动开启一个服务,然后访问服务地址即可查看到项目创建的界面
//创建项目
vue create vue3
//运行项目
npm run serve
vite:下一代前端构建工具
优势:开发环境无需打包操作,可快速的冷启动
轻量快速热重载
按需编译,不需要等待整个编译完成再运行
webpack是将所以的操作执行完成的时候才给你准备一个端口号,vite是先准备一个端口号,你需要那个路由和组件就去加载哪一个
该文件为vue3的开发者工具,在Chrome浏览器打开右上角三个点里面的更多工具,点击扩展程序,然后将所需要安装的包拖动到里面即可安装
链接: https://pan.baidu.com/s/1baZ8gx2grwzZRkLsMFym8g
提取码: 4jss
vue3中一个新的配置项,值为一个函数
组件中所用到的数据,方法等等,都需要在setup中配置
返回结果有两种,一种是对象,一种是渲染函数
尽量不要与vue2中的data,methods等等混用,vue2中可以访问到setup中的数据,方法,但是vue3中的setup访问不到vue2中的数据和方法
setup正常情况下不能是一个async函数,后续可以返回一个Promise函数,但是需要使用到Suspense和异步组件的配合
定义一个响应式的数据
创建一个响应式数据,数据可以是基本数据类型,也可以是对象数据类型,基本数据类型是靠js中的get,set数据劫持进行实现响应式的,而对象数据类型是求助了一个新函数reactive,这个函数里面封装了一个Proxy
修改数据的时候需要通过数据名称.value才可以操作数据
setup() {
let name = ref('张三')
let age = ref(18)
let job = ref({
type:'前端工程师',
salary:'30k'
})
function xiugaiJob() {
job.value.type = 'Ul工程师'
job.value.salary = '60k'
}
return {
name,
age,
job,
xiugaiJob
}
}
定义一个响应式数据,基本类型不要用它
接受一个对象或者数组,返回一个代理对象,Proxy实例对象
reactive函数处理的响应式数据是深层次的
内部是基于Proxy实现的,通过代理对象操作源对象内部数据进行操作
在后续的操作数据的时候不需要通过数据名称.value进行操作,之间数据名称.进行操作即可
let job = reactive({
type:'前端工程师',
salary:'30k'
})
let arrList = reactive(['1','2','3'])
function xiugaiJob() {
job.type = 'Ul工程师'
job.salary = '60k'
}
vue2中的响应式原理是通过Object.defineProperty()中的get和set进行读和改的,但是添加和删除界面不会刷新,数组也没法之间通过下标进行修改,需要依赖于Vue.set来进行修改
vue3的实现原理是通过Proxy(代理)和Reflect(反射)来进行实现的
通过Proxy进行拦截对象中任意属性的变化,包括属性的读写,修改添加,删除等操作,然后通过Reflect来对属性进行操作
new Proxy(data,{
//读取时候的操作,读取的是那个属性上面的什么值,target是原数据,prop是读取的属性名称
get(target,prop){
//通过反射中的get来读取数据
return Reflect.get(target,prop)
},
//修改添加时候的操作,target是原数据,prop是修改的属性名称,value是要修改成的数据
set(target,prop,value){
//通过反射中的set来修改和添加数据
return Reflect.set(target,prop,value)
},
//删除时候的操作,target是原数据,prop是删除的属性名称
delectProperty(target,prop){
//通过反射中的delectProperty来删除数据
return Reflect.delectProperty(target,prop)
}
})
定义数据角度对比
ref定义的是基本数据类型;reactive定义的是对象或者数组;ref也可以定义对象和数组,内部会自动求助reactive再次进行处理
原理角度对比
ref通过Object.defineProperty()中的get和set来进行响应式(数据劫持);reactive是通过Proxy来进行响应式,并且通过Reflect来操作原数据内部的变化
从使用角度来对比
ref定义的数据需要.value拿到,读取数据的时候不需要;reactive定义的数据读取和拿到都不需要.value
执行时机在beforeCreate之前
setup可以接受的两个参数
props:值为对象,输出的是使用组件者所传递过来的值,且在当前组件内部进行了props接受
context:attres和vue2中的 a t t r e s 一 致 , s l o t s 和 v u e 2 中 的 attres一致,slots和vue2中的 attres一致,slots和vue2中的slots一致,emit和vue2中的$emit一致
和vue2用法差不多,但是需要引入一下这个computed方法
data.Name = computed(() => {
return data.firstName+'-'+data.lastName
})
vue3中的监视,需要引入这个watch方法,里面接受三个配置项,第一个要监视的属性,第二个是回调,第三个是配置项
检测ref函数所生成数据的时候一切都正常,检测reactive所生成的数据时候,第三个配置项里面配置的deep深度监视将无效,会自动开启深度监视的,如果data数据中还有一个对象,此时想检测这个对象中数据的改变。就需要使用到deep
//检测多个ref定义的属性
watch([num,msg],(newValue,oldValue) => {
console.log(`数据改变了,新值为:${newValue},旧值为:${oldValue}`);
},{immediate:true})
//检测多个reactive定义的属性
watch([()=>data.num,()=>data.age],(newValue,oldValue) => {
console.log(`数据改变了,新值为:${newValue},旧值为:${oldValue}`);
},{immediate:true})
//检测reactive定义属性中的对象,如果需要检测到其中的改变就需要使用到deep来开启深度监视
watch(()=>data.student,(newValue,oldValue) => {
console.log(`数据改变了,新值为:${newValue},旧值为:${oldValue}`);
},{immediate:true,deep:true})
不指明检测那个属性,里面用到那个属性就检测那个属性,上来就会执行一次,和computed有点像,同样上拉直接调用一次,同样所依赖的数据发生改变的时候从新调用,但是computed里面是注重的是返回值,需要return出去,而watchEffect注重的过程
watchEffect(()=>{
const x1 = data.name
const x2 = data.age
console.log("回调执行了")
})
vue3中可以继续使用vue2里面的生命周期函数,但是有两个改变的
boforeDestroy改名为beforeUnmount
destroyed改名为unmounted
vue3提供的一些组合式API,需要先进行引入,组合式API的执行时机要早于配置项生命周期
beforeCreate() === setup()
created() === setup()
beforeMount() === onBeforeMount()
mounted() === onMounted()
beforeUpdate() === onBeforeUpdate()
updated() === onUpdated()
beforeUnmount() === onBeforeUnmount()
unmounted() === onUnmounted()
与vue2中的mixin混合使用一样,就是将所公共部分拿出来,在需要的时候只需要引用使用即可
在src目录下面创建文件夹hocks文件夹,所以的封装放在这个文件夹里面
具体封装,例如这里面的数据,操作进行封装,最后将所需要的进行返回出去
import {
ref,
reactive,
onMounted
} from 'vue'
export default function(){
let num = ref(0)
let student = reactive({
name:'张三',
age:18
})
onMounted(() => {
setInterval(() => {
num.value+=1
}, 2000);
})
return{
num,
student
}
}
使用,先进行引入,然后调用函数拿到返回值,由于返回的不是一个,因此在这里需要结构赋值全部进行返回
import usePoint from '../hocks/usePoint'
setup() {
const point = usePoint()
return {
...point
}
}
创建一个ref对象,里面的值指向的是另外一个对象里面的某个属性
应用在要将响应式对象里面的某个值单独给外部使用的时候
例如name改变的时候data中的name也会进行改变,因为新生成的这个name和dat中的name有绑定,一方变。另一方也会改变
//单个
const name = toRef(data,'name')
//多个
const data = toRef(data)
shallowReactive只处理最外层响应式,内部不处理,浅层次处理数据,用在数据结构比较深,但是只修改第一层的数据的时候,不操作到里面深层次的时候
此时修改name,age都可以进行修改,但是要修改school里面的name和schoolAge就不能修改
let student = shallowReactive({
name:'张三',
age:19,
school:{
name:'河北软件职业技术学院',
schoolAge:"大二"
}
})
shallowRef只处理基本数据的响应式,不处理对象类型的响应式,用在后续不修改里面属性的时候,可以使用新生属性进行替换,定义的数据修改是不管用的,没有响应式
let x = shallowRef({
y:10
})
readonly让数据变成只读的,深度,就是里面嵌套再多层次,都是只读的,修改里面的任意一个属性都不可以
let student = reactive({
name:'张三',
school:{
name:'河北软件职业技术学院',
schoolAge:"大二"
}
})
student = readonly(student)
shallowReadonly让数据变成只读的,浅度,就是只有第一层只读,再往里面深层次的可以修改,例如school里面的name和schoolAge就可以进行修改
let student = reactive({
name:'张三',
age:19,
sex:'男',
school:{
name:'河北软件职业技术学院',
schoolAge:"大二"
}
})
student = shallowReadonly(student)
toRaw是将一个由reactive所生成的响应式数据转换成普通数据,修改的时候不会引起界面的变化,用在只是读取数据的时候,增加性能
student是由reactive生成的响应式数据,在这里进行转换成普通对象
student = markRaw(student)
markRow是标记一个对象,使其不会成为响应式数据,有些值不需要做响应式的时候应用,例如大数据,只需要进行展示,不需要进行修改,就可以使用markRow来进行处理,以此来提升性能
例如往student上面添加一个nums属性,但是添加的这个数据是一个大数据,且不需要进行修改,此时就可以使用markRaw来跳过i响应式提升性能
student.nums = markRaw(nums)
自定义ref,用在防抖上面,由于比较难懂,直接全部代码,界面上面有一个p标签展示输入的内容,一个输入框进行输入内容,然后下面定义一个value,这个value是我们自己使用myRef生成的,并且返回出去,然后就是这个myRef里面的各种逻辑,首先先定义一个Time,用于接受定时器,后续进行清除,然后return出去一个customRef,里面是一个回调函数,有两个参数可以使用,track参数是通知vue去解析模板,trigger是告诉那个值被跟踪了,在这里里面再进行return出去两个方法,get读取数据的时候直接返回出去传递过来的数据,set设置数据的时候给这个value从新赋值,并且通知要去从新解析模板,在get中告诉value被跟踪了,并且每一次修改的时候将定时器清除从新开启
{{value}}
主要实现祖和后代组件之间的传值通信,也可以实现父与子,但是父与子一般使用props传递值,不建议使用这种
祖组件中使用,car是数据,然后使用provide这个方法传递,方法名称为carInfo,所传递的参数为car
setup(){
let car = reactive({name:'奔驰',price:'20w'})
provide('carInfo',car)
}
后代组件中接受,之间使用inject进行接受,carInfo为拿取数据的方法名,然后返回出去
setup(){
const car = inject('carInfo')
return car
}
isRef:检查是否为一个ref对象
isReactive:检查一个对象是否是由reactive所创建的响应式代理
isReadonly:检查一个对象是否由readonly创建的只读代理
isProxy:检查一个对象是否是由reactive或者readonly方法创建的代理
在vue2中我们采用的是配置项API,在vue3中我们采用的是组合式API
配置项API:例如data,methods,watch,computed,现在有四个功能,data里面就有四个功能的数据,methods里面就有四个功能的方法,watch里面就有四个功能的监听,computed里面就有四个功能的计算属性,这样的话就太多了,不易于维护,找起来代码也麻烦
组合式API:接着上面说,现在有四个功能,就可以定死四个函数,每个函数里面对于自身的数据,方法,监听,计算属性,让各自的功能代码都在一起,更加美观和易于查找维护
将html移动到指定位置的技术
001
等待异步组件时渲染一些额外内容,让应用有更好的用户体验
import {defineAsyncComponent} from 'vue'
const Child = defineAsyncComponent(() => import('../components/Child.vue'))
加载中,请稍等!
第一个就是所以以Vue开头的,例如Vue.use(),Vue.component()都变成了app开头的,例如app.use()
第二个就是data必须是一个函数
第三个就是动画,这个就不详细说了,一般都是第三方库之间使用
第四个就是不能自定键盘事件了,例如之前可以时@keyup.13来指定按的时Enter键,还可以设置
第五个就是移除了@click.native,之前我们在组件上面绑定事件的时候使用到了,现在在vue3中不可以使用了
第六个就是移除了filter过滤器,因为在vue3中官方认为过滤器打破了大括号内只是js表达式,其次就是使用的时候还需要自己去写一个过滤器再进行使用
更多变化可以查看官方文档
https://v3.cn.vuejs.org/