必备-9.vue
vue是什么
- 官方网站:
https://cn.vuejs.org/
- Vue是
渐进式框架
:vue.min.js只包含了vue最核心的内容【例如:options api/数据处理、template模板视图的解析等操作】;真实项目中我们还会根据需求导入vuex
:实现公共状态管理vue-router
:实现SPA(single page application)单页面应用- element-ui(饿了么)、antdv(蚂蚁金服)、iview()、vant()、cube…基于这些UI组件库快速创建项目
axios
:实现数据通信@vue/cli
:基于vue的脚手架创建工程化项目
渐进式框架是什么?
- vue是渐进式的,angular不是渐进式的
- vue和angular的区别就好像
- vue:就是手抓饭,你有手就能吃
- angular:就是牛排,你想吃它,需要刀叉配套
vue的作用
Vue框架&&React框架的作用:
- 让开发者放弃直接操作DOM,转为操作数据,当我们把数据处理"明白",框架本身会按照最新的数据帮助我们渲染出想要的视图[操作DOM的事情交给框架内部实现,而且做了性能优化]
vue的设计模式
**Vue是基于MVVM模式构建的框架:**它本身实现了viewModel层去监听数据/视图的变化,从而去渲染视图/修改数据,所以我们学习vue主要考虑两条主线:Model数据层
和View视图层
什么是MVVM设计模式?
- MVVM:(Model-View-Viewmodel):数据-视图-视图模型
- M(Model):数据层->后台,对于前段来说就是后端提供的一个API接口
- V(View):视图层->浏览器
- VM(Viewmodel):视图模型层-> 一个同步View和Model的对象
ViewModel
通过双向数据绑定把View
层和Model
层连接了起来,而View和Model之间的同步工作完全是自动的,无需人为干涉,因此开发者只需要关注业务逻辑,不需要手动操作DOM,不需要关注数据状态的同步问题,复杂的数据状态维护完全由MVVM来统一管理。- 双向数据绑定:
- m->v:数据修改时,会触发 视图的重新渲染
- v->m:视图内容修改时,也会触发数据的重新修改
- 双向数据绑定:
不同框架采用的设计模式?
- vue:MVVM双向数据绑定->(在谷歌的中国人:尤雨溪)
- angular(简称:ng):MVVM ->(谷歌)
- react:MVC->(facebook)
数据层
如何使用vue
官方网站:https://cn.vuejs.org/
-
第一步:使用vue需要在自己项目环境下安装vue包:
yarn @vue/cli
-
第二步:在项目页面html页面中引入对应的vue:
<script src="js/vue.min.js"></script>
-
第三步:Vue是一个构造函数,我们基于vue开发,只需要创建这个类的实例。
-
let vm=new Vue({options API})
-
第四步:{config}是一个对象,我们可以通过在{config}中配置对应属性来实现vue视图层与数据层的交互
-
1、指定视图:
-
let vm=new Vue({ //视图 el:'#computedBox', })
-
-
2、传入数据:data中所有内容都是未来需要在视图中渲染的数据
-
//数据:data中所有内容都是未来需要在视图中渲染的数据 data:{ list: [{ id: 1, count: 0, price: 12.5 }, { id: 2, count: 0, price: 10.5 }] }
-
-
响应式与非响应式
响应式数据和非响应式数据的区别?
- 响应式数据:只要数据层中的数据修改了,Vue就会通知视图层重新渲染。例如:
在vm创建时构造函数中options下data对象中设置的数据
- 非响应式数据:即使数据被修改了,也不会造成视图层的重新渲染。例如:
vm.text="珠峰"
,给vm实例设置的私有属性数据,给它重新赋值:vm.text="aaa"
,这里不会触发视图层的重新渲染。- 但是如果其他因素让视图重新渲染了,它也会跟着渲染,比如:
调用vm.$forceUpdate():强制让视图渲染、响应式数据修改造成视图渲染
- 但是如果其他因素让视图重新渲染了,它也会跟着渲染,比如:
响应式数据VS非响应式数据?
- 都可以修改值
- 响应式数据在值修改之后,会通知视图重新渲染:
- 因为其基于Object.definProperty对其做了get/set劫持,当我们改变响应式数据值时,会触发set函数,在set函数中一方面把值修改,一方面会通知视图重新渲染。
如何判断数据是否为响应式?
- 后期我们在vue的项目中,我们只需要展开实例,看哪些数据做了
get/set
,做了劫持的数据是响应式的!!
什么情况下创建的数据是响应式的?
- 响应式:new Vue的时候,在data中指定的数据,vue默认会对其做劫持处理,这些数据是响应式的
- 非响应式:自己直接挂载到vm实例上的数据是非响应式的数据。
- 在Vue开发中,我们把需要的数据一般都写在data中(哪怕现在用不着,我们也先在data中初始化一下),目的是让这些数据变为响应式数据。
在data中存放的数据有什么特点?
一句话:data里面只要是标准普通对象内的私有属性,都会被做get/set劫持,变成响应式数据
,其他不会做,数组也不会。
- 预先初始化数据:在Vue开发中,我们把需要的数据一般都写在data中(哪怕现在用不着,我们也先在data中初始化一下),目的是让这些数据变为响应式数据。
- 只有默认写在data里面的键值对数据会做get/set劫持,在data外定义的数据不会做劫持
- 只要是Object对象类型,无论在哪里,都会对它里面的键值对做(get/set)劫持
- 对data中所有层级的对象的键值对都做深层次的监听劫持
- 特殊:对数组来说,数组本身会做劫持,但数组内部的各索引项不会做劫持:
- 如果索引项是Object对象类型,则这个索引项里的键值对还是会做劫持
vm.arr=100
:这是修改arr的值,触发重新渲染vm.arr[0]=100
:这是修改arr堆内存中的信息,不会触发重新渲染
- vue为了解决数组各索引项不做劫持的问题,就把Array的七个方法重写了:
push、pop、shift、unshift、splice、sort、reverse
,后期我们基于这七个方法操作数组,都会触发视图重新渲染
- 特殊:对数组来说,数组本身会做劫持,但数组内部的各索引项不会做劫持:
我们想修改非响应式数据也实现页面的重新渲染,怎么做?
- 在Vue中,当我们修改非响应式数据后,默认是不会重新渲染的,但是可以利用方法:
- Vue.prototype:
- 方式一:
vm.$forceUpdate()
:强制视图渲染 - 方式二:
vm.$set([obj],[key],[value])
:将obj对象中[key]属性的值改为[value],并且把[key]属性转换为响应式数据- 针对于data中的某个对象,开始没有初始化某个属性,后期才新加的(这样的属性默认是非响应式的:改值视图不会重新渲染):$set可以在后续把对象只能够的这个属性,设置为响应式的。
- 方式一:
Vue对象创建方法
-
创建vm对象的方式2种:
- 方式一:
let vm=new Vue({options API})
:直接创建vm实例,但是没有指定任何视图 - 方式二:
Vue.createApp([obj]).$mount("#box")
:将obj创建为vm实例,并且挂载到#box节点上(vue3)
- 方式一:
-
指定视图的方法4种:
-
let vm=new Vue({ //方案一:指定页面中存在的DOM元素作为视图 el:`#box`, //方案二:创建视图 template:`<div> //最后把数据渲染在这个视图中 </div>`, //方案四:render渲染视图 render:h=>{ ... } }) //方案三:挂载视图 vm.$mount(('#app'));
-
-
vm的options API对象中的属性:
el:"#box"
:指定视图,将html中的视图元素与vm绑定data:{msg:'hello world'}
:构建数据模型,挂载需要在视图中渲染的数据,这里的数据都是响应式数据- 设置的数据直接挂载到vm对象中,成为vm对象的私有属性msg:值就是’hello world’
- 挂载到实例上的东西能够直接在视图上用
{{msg}}
使用(小胡子语法)
methods:{fn1(){},fn2(){}}
:视图中需要的方法,这里的this自动指向vm- 在new Vue中的methods编写普通方法,这样写方法,不论方法咋执行的,也不论在哪里执行的,方法中的this是vue的实例[vue内部做了处理];所以我们不会把它设置为箭头函数(this->window)
- methods编写的方法会直接挂载到实例上(不做get/set劫持),所以可以直接在视图中使用
computed:{pro1(){},pro2(){}}
:计算属性:【多个值变化,改变一个结果值】依赖于某些数据值,计算出新的值- 【缓存效果:依赖的值没变,会用之前计算的值,只有依赖的值变了,才会重新计算】
- 这里的this也指向vm
watch:{pro1(){},pro2(){}}
:侦听属性:【一个值变化,改变多个结果值】
-
vm实例的方法:
vm.$forceUpdate()
:强制让vm视图渲染vm.$set([obj],[key],[value])
:将obj对象中[key]属性的值改为[value],并且把[key]属性转换为响应式数据- 针对于data中的某个对象,开始没有初始化某个属性,后期才新加的(这样的属性默认是非响应式的:改值视图不会重新渲染):$set可以在后续把对象只能够的这个属性,设置为响应式的。
API
全局API
Vue.filter
-
filters配置的是局部过滤器,只能在自己实例挂载的视图上使用
-
Vue.filter配置的是全局过滤器,可以在任何挂载了new Vue的视图上使用
-
定义方式:
-
//addZero就是全局过滤器名称 //value是调用过滤器的状态值 Vue.filter("addZero",function(value){ return "0"+val; }); //addOne就是全局过滤器名称 //value是调用过滤器的状态值 Vue.filter("addOne",function(value){ return "1"+val; });
-
-
用法:可以使用多个过滤器
-
<div>{{num|addZero|addOne}} </div> ```
-
选项
选项就是能够在new Vue({options}),options中能够配置的属性项,称为选项
选项/数据
computed
-
computed:计算属性【多个值变化,影响到一个结果值变化】
-
计算:以函数的形式定义
-
属性:以属性的形式使用
-
定义方式:可以是直接函数和对象定义的两种方式
-
//函数定义方式 <!-- 以属性的形式使用 --> <div>{{funllname}}</div> <script> let vm=new Vue({ computed:{ // 以函数的形式定义 fullname(){ return this.first+"-"+this.last; } } })
- **特点:**computed里属性的结果会被缓存,除非依赖的响应式属性变化才会重新计算
-
对象定义方式:computed中的计算属性本身其实存在两个方法,只是默认获取,
get()和set
方法,原理:使用了Object.defineProperty()中的get/set劫持 -
//对象定义方式 //计算变量的get和set方法 computed:{ fullname:{ // 对fullname属性获取时拦截,也是函数定义形式使用的默认方法 get(){ //实现多个值变换,改变一个结果值 return this.first+"-"+this.last; }, //对fullname属性赋值时拦截 set(val){//val是想赋给fullname的值,也可以说是心值 let arr=val.split("-"); //实现一个值变化,改变多个结果只 this.first=arr[0]; this.last=arr[1]; } } }
-
watch
-
watch:侦听属性【一个值变化,影响多个结果值变换】
-
侦听:watch中的定义的属性名就是我们监控的vm里的私有属性名,当这个私有属性发生变化是,会自动执行watch中对应方法:可以看作是
computed
和methods
的结合体 -
特点:
watch能够监控路由
,如果不需要监控路由,我们尽可能的用computed的set方法来代替watch,原因是computed有缓存 -
注意:我们监控的对象必须vm里已经存在该私有属性
-
用法:可以是直接函数和定义对象两种用法
- 直接函数的定义方式:不能给对应属性配置参数
- 对象的定义方式:可以给对应属性配置参数:
deep:是否深层侦听
、immediate:页面第一次加载时是否立即侦听一次
- 如果我们侦听的是一个obj对象,对象里面的属性值发生改变时,并不会触发obj的侦听方法,这时我们就用obj的对象定义形式将obj的
deep
配置项设为true
-
watch:{ //fullname就是我们监控的vm实例中的私有属性,存在两个值 //函数式写法 "obj"(newVal,oldVal){ let arr=newVal.split("-"); //实现一个值变化,改变多个结果值 this.first=arr[0]; this.last=arr[1]; }, // 对象式写法 "obj":{ // 这是函数式写法默认用的方法 // handler(newVal,oldVal){...} //设置对obj的属性是否进行深侦听 deep:true, // 设置对obj的属性是否在第一次渲染页面时立即侦听一次 immediate:true } }
-
method
-
methods:方法定义:方法表示一个具体的操作,主要书写业务逻辑;与我们js中函数的写法和用法相同
-
computed、watch、methods的区别?
- computed是属性调用,而methods是函数调用
- computed带有缓存功能,而watch没有
- watch可以监控对象,而computed、methods不能
-
以下写法都是点击的时候才会把handle执行:
@click="handle"=>handle(ev){...}
@click="handle(10,20)"=>handle(10,20){...}
:也不是把handle立即执行,而是点击才执行,只不过点击的时候,会把10/20传递给@click="handle(10,20,$event)"=>handle(10,20,ev){...}
:视图中出现的$event
代表是事件对象
props
-
props 可以是数组或对象,用于接收来自父组件的数据,props 可以是简单的数组,或者使用对象作为替代,对象允许配置高级选项,如类型检测、自定义验证和设置默认值。
-
// 简单语法 Vue.component('props-demo-simple', { props: ['size', 'myMessage'] }) // 对象语法,提供验证 Vue.component('props-demo-advanced', { props: { // 检测类型 height: Number, // 检测类型 + 其他验证 age: { type: Number, default: 0, required: true, validator: function (value) { return value >= 0 } } } })
选项/资源
filters
-
作用:对某些状态值进行处理,并返回符合规则的值
- 能够在filter属性中定义过滤器(局部过滤器),只能在自己vm实例绑定的视图下使用.
- 只能在【小胡子语法{{}}】和【v-bind命令】中使用
-
定义:
-
filters:{ //val是调用过滤器的状态值 addZero(val){ //返回值就是我们视图中真正获取的值 return "0"+val; }, //val是调用过滤器的状态值 addOne(val){ //返回值就是我们视图中真正获取的值 return "1"+val; } }
-
-
使用方式:可以同时使用多个过滤器
-
//如果用的不是全局过滤器,则视图绑定的vm中必须存在想用的局部过滤器 //多个过滤器从左到右执行 <div> {{num|addZero|addOne}} </div>
-
vm实例
实例私有属性
vm.$parent
😗*[vue实例]**获取父元素的整个vm实例- 子组件可以在父组件的任何生命周期函数中获取【父子组件的生命周期】
vm.$children[n]
:获 取第n个子元素的vm实例- 父组件只能在子组件
created
生命周期函数里或之后获取子元素【父子组件的生命周期】
- 父组件只能在子组件
vm.$root
😗*[vue实例]**获取根元素的vm实例(main.js中new 的Vue实例)vm.$refs
😗*[数组]**this的子元素中需要定义ref
属性:比如ref="xxx"
:- 如果ref定义在DOM标签中:
this.$refs.xxx
获取的是DOM对象 - 如果ref定义在子组件标签中:
this.$refs.xxx
获取的是子组件的vm实例
- 如果ref定义在DOM标签中:
实例公有方法
数据
vm.$watch
:还没学到vm.$set(obj,"name","sjh")
:给vm实例data中的obj添加一个name属性,值为sjh,并且把它变为响应式状态值,(默认在data之外添加数据this.obj.name="sjh"
是不会变成响应式数据的,不会触发页面重新渲染的)vm.$delete(obj,"name")
:与$set作用相对应,删除vm实例data中obj对象中的name属性,并且通知页面重新渲染,(默认在data之外删除数据delete this.obj.name
是不会触发页面重新渲染的)
事件
-
vm.$on("myEvent",fn)
:向事件池中添加myEvent
事件,并绑定fn函数,能被调用无数次 -
vm.$once("myEvent",fn)
:向事件池中添加myEvent
事件,并规定被调用一次之后就销毁 -
vm.$emit("myEvent",parm1,parm2,...)
:执行事件池中的myEvent
事件函数,并把第二个及以后的实参赋值给myEvent
绑定的函数 -
vm.$off("myEvent")
:删除掉事件池中的myEvent
事件
生命周期
vm.$mount("#box")
:如果 Vue 实例在实例化时没有收到 el 选项,则它处于“未挂载”状态,没有关联的 DOM 元素。可以使用vm.$mount()
手动地挂载一个未挂载的实例。(只有被vue挂载上的#box标签,内部才能用vue的框架的各种属性)- 这个方法返回实例自身,因而可以链式调用其它实例方法。
vm.$forceUpdate()
:执行该方法会强制刷新页面渲染vm.$nexTick([callback])
将回调函数延迟到下次 DOM 更新循环之后执行vm.$destory()
:触发beforeDestory()、destoryed()
钩子函数阶段
内置组件
<component></component>
:渲染一个“元组件”为动态组件。依is
的值,来决定哪个组件被渲染。- 用法:
<component is="coma"></component>
,它会找==new Vue()==的components中去找coma组件渲染 - 如果想动态绑定值,用v-bind:
<component :is="bindprop"></component>
- 用法:
特殊Attribute
-
is
:需要结合和component标签(内置组件)使用,用于动态组件,且基于 DOM 内模板的限制来工作。- 用法:
<component is="coma"></component>
,它会找==new Vue()==的components中去找coma组件渲染 - 如果想动态绑定值,用v-bind:
<component :is="bindprop"></component>
- 用法:
视图层
视图层的构建
- Vue构建视图是基于“template”模板语法:template中定义了很多vue内置的语法规范,这些规范不能被浏览器直接识别,需要基于vue进行渲染
- 流程:
- 根据数据和template视图,把其识别为一个虚拟DOM对象(vnode)->
DOM-DIFF
->把vnode虚拟DOM变为真实DOM - @1template中存在大量的指令
v-xxx
,用来构建视图和完成相关的效果- vue是根据template语法来构建视图的
- 根据数据和template视图,把其识别为一个虚拟DOM对象(vnode)->
- 流程:
普通指令:v-xxx
v-text/v-html
-
渲染数据的属性2个:
-
v-text/v-html:[string]
:把数据渲染到指定的容器中<h2 v-html="msg"></h2>
:把msg数据渲染到h2标签中,如果数据内容是html字符串【带标签】,会自动进行识别渲染<h2 v-text="msg"></h2>
:把msg数据渲染到h2标签中,如果数据内容是html字符串【带标签】,不会进行识别渲染,与{{}}
小胡子语法相同
v-show
-
控制元素显示隐藏的属性2个:
-
v-show:[boolean]
:控制元素的显示隐藏{原理:控制display:none/block}- 特点:这种方式,无论结果是TRUE还是FALSE,元素都渲染出来了
v-if/v-else-if/v-else
-
基于条件控制DOM元素渲染或销毁3个:
-
v-if:[boolean]
:控制元素的显示隐藏,但是和v-show不一样,如果值是false,元素是不进行渲染的(页面中没有这个元素),只有结果是true,元素才会渲染!! -
两者应该分场景使用:
- 如果元素切换显示隐藏频率不高的情况下,使用
v-if
性能更好 - 如果元素切换显示隐藏频率比较高的情况下,
v-if
会使元素一直销毁和重新渲染,此时使用v-show
更好
- 如果元素切换显示隐藏频率不高的情况下,使用
-
v-else-if:[boolean]/v-else
:与v-if
联合使用,如果三个组合用,用的标签必须挨着,否则无法生效v-else-if:[boolean]
:if条件不成立则判断这个条件是否成立v-else
:if或else-if条件不成立,则判断这个条件
v-for
-
循环渲染元素的attribute1个:
-
v-for="(item,index) in xxx" :key="index"
:循环创建元素,实现内容绑定- 想让哪个元素创建多个,就给哪个元素设置v-for
- 循环的元素要设置唯一值key【最好不要使用索引作为key?】
- xxx可以是数字、字符串、数组类型
<li v-for="(item,index) in 5 :key="index"></li>
:xxx是数字,先隐式转换为数组[1,2,3,4,5]
,再进行逐项循环渲染<li v-for="(item,index) in 'str' :key="index"></li>
:xxx是字符串,先隐式转为数组[s,t,r]
,再逐项循环渲染<li v-for="(item,index) in [arr] :key="index"></li>
:xxx是数组类型,直接逐项循环渲染
v-on
-
给元素绑定事件的attribute1个:
-
v-on:[事件]="要执行的方法"
:缩写为@事件="要执行的方法"
- 原理:基于DOM2事件绑定实现的 addEventLinstener冒泡事件【好处是,后期获取到这个DOM元素对象,再做事件绑定,也不会和@click绑定的方法冲突】
- 如何阻止冒泡传播:
- 原生:函数体内实现
e.stopPropergation()
- vue:使用修饰符
@click.stop=function(){}
,与原生想用
- 原生:函数体内实现
- 如何阻止冒泡传播:
- 要执行的方法:在new Vue中的methods编写普通方法,这样写方法,不论方法咋执行的,也不论在哪里执行的,方法中的this是vue的实例[vue内部做了处理];所以我们不会把它设置为箭头函数(this->window)
- 以下写法都是点击的时候才会把handle执行:
@click="handle"=>handle(ev){...}
@click="handle(10,20)"=>handle(10,20){...}
:也不是把handle立即执行,而是点击才执行,只不过点击的时候,会把10/20传递给@click="handle(10,20,$event)"=>handle(10,20,ev){...}
:视图中出现的$event
代表是事件对象
- 原理:基于DOM2事件绑定实现的 addEventLinstener冒泡事件【好处是,后期获取到这个DOM元素对象,再做事件绑定,也不会和@click绑定的方法冲突】
v-bind
-
给元素的attribute动态绑定值:【存在修饰符】
-
v-bind:xxx="xxx"
:简写为:xxx="xxx"
给元素的attribute动态绑定值-
但凡属性值是一个变量的值或者是一个表达式计算的结果,都需要给属性
v-bind
一下 -
基于属性给子组件传递信息的时候,如果想让属性值是字符串以外的类型,也需要
v-bind
一下 -
动态绑定class:可以是对象或数组
-
对象语法:
:class="{box:true,active:true/false}"
:active是class样式类名,它的值true/false决定元素是否拥有这个样式类名 -
数组语法:
:class="['box',num>10?'active':'']"
:box是肯定有的类名,根据num条件决定是否有active类名
-
-
动态绑定style:样式需要用驼峰命名法,可以是对象或数组
- 对象语法:
:style="{color:'red',fontSize:num>10?'20px':'12px'}"
:元素颜色静态为红色,字体大小根据num决定 - 数组语法:
:style="[color,fontSize]"
:每个数组中的数据项都是一个对象,存在样式名和值
- 对象语法:
-
v-slot
-
vue组件化中,会有插槽的配置,目的是给调用的组件的template设置插槽或者获取组件对应的数据
-
v-slot可以简写为#:#xxx
-
==具名插槽:==组件中预设好多插槽位置,为了后期可以区分插入到哪,我们把插槽设置名字
-
组件内部:
<slot name="xxx">
默认名字是default -
调用组件:需要把v-slot写在template上
-
<template v-slot:xxx> ... </template> <template> ... </template>
-
-
-
- vue 调用组件:需要把v-slot写在template上 <template v-slot:xxx> ... </template>
-
==作用域插槽:==把组件内部定义的数据,拿到调用组件时候的视图中使用
-
组件内部:
-
<slot name="top" :list="list" :msg="msg"></slot>
-
把组件中的list赋值给list属性,把msg赋值给msg属性,插槽中提供了两个作用域属性:list/msg
-
调用组件:
-
<template #top="AAA"></template> 定义一个叫做AAA的变量,来接收插槽中绑定的数据 AAA={ list:[...], msg:... }
-
-
v-model
- 实现视图更新控制状态修改
v-model
:实现视图更新控制状态修改【存在修饰符】- 实现原理:
- @1把状态值赋值给文本框的value属性
- @2基于input或change事件监听文本框内容的改变,当内容改变的时候,去修改对应的状态值
- 实现原理:
能够用v-model的修饰符的标签类型有:input/textarea、select(option)、input[type='radio']、input[type='checkbox']
-
<input v-model="text">、textarea
:- 1、把text状态值赋给value
- 2、当value输入时,触发input事件,把value值赋给text状态值
-
<select v-model="city"></select>
:- 1、拿city状态值和option中的value值做比较,和谁一样,谁默认选中
- 2、监听下拉框的change事件,选中的是谁,就把那个选项的value赋值给city状态
- 3、select标签加
mutiple
属性表示可多选,则状态值需要是一个数组
-
`<input type="radio" name="sex" :value="0" v-model="sex">`男 `<input type="radio" name="sex" :value="1" v-model="sex">`女
- 1、会把
v-model中的sex
状态值相同的作为同一组,一组中只能选中一个【也可以自己设置name】 - 2、根据sex状态值和value值做对比,相同的则默认被选中
- 3、监听每一个radio的change事件,把选中radio的value值赋值给sex状态值
- 1、会把
-
<input type="checkbox" name="hobby" value="jump" v-model="hooby">音乐 <input type="checkbox" name="hobby" value="movie" v-model="hooby">电影 <input type="checkbox" name="hobby" value="music" v-model="hooby">跳舞 <input type="checkbox" name="hobby" value="read" v-model="hooby">阅读
- 1、会把
v-model中的hooby
状态值相同的作为同一组,一组中可以选中多个,所以hobby状态值必须是一个数组 - 2、监听每一个
checkbox
的change事件,只要有选项被选中或取消选中,都会把value值赋值给hobby状态值
- 1、会把
性能优化指令
v-pre
让元素及其后代元素跳过编译:
v-pre
:在视图编译渲染的时候,跳过拥有这个指令的元素及其后代元素[不进行任何的语法的编译,你写的是啥,呈现在视图上就是啥];<span v-pre>{{ this will not be compiled }}</span>
- 真实项目中,我们完全可以把一些“静态内容”不进行任何的编译,以此来优化视图编译渲染的速度!!
v-once
控制元素或组件的编译次数:
v-once
:让元素或者组件只在视图第一次渲染的时候编译一次,当视图重新渲染的时候,这部分内容就不再重新编译了;- 适用于第一次动态绑定完成后,后期不会再更新的内容
v-cloak
延迟显示元素:
-
v-cloak
:在非工程化的vue项目中,在js没有加载出来之前,页面会呈现出原始的{{xxx}}这种内容,只有js加载出来并处理完成,才能把template语法编译为真实DOM渲染到页面中。-
为了防止这种“闪”的效果,我们在js没有加载完成之前让容器隐藏,加载完成之后再让它显示。
-
用法:
-
<style> [v-clock]{ display:none; } </style> <div v-clock></div>
-
-
自定义指令
-
与filter过滤器定义相似,可以定义全局(directive)的或局部的(directives)
-
局部:定义在new Vue()中
-
new Vue({ directives:{ inserted(){} } })
-
-
全局:Vue.directive()
-
修饰符
v-on修饰符:@xx.xxx25个
通用修饰符8个
@click.stop
:阻止事件的冒泡传播=>同理于:e.stopPropergation()
@click.prevent
:阻止事件的默认行为=>同理于:e.preventDefault()
@click.passive
:是否允许阻止事件的默认行为,true表示不允许阻止,false表示允许阻止=>同理于:设置addEventListener第三个参数options中的passive为true
@click.capture
:将事件在捕获阶段触发=>同理于:dom.addEventListener([event],[fun],true)
,[默认都是在冒泡阶段触发]@click.self
:只有点击的事件源就是当前元素本身,绑定的方法才会执行{但是没有阻止冒泡传播,如果点击了它的子元素,则会跳过当前元素,继续向祖先级元素冒泡}@click.once
:只触发执行一次,然后把事件绑定的方法一吃掉=>相当于执行一次后会默认执行dom.removeEventListener([event],fn)
@xxx.ctrl.a.exact
:精确地,让默认的只要按下ctrl+a就触发,变成只有按下ctrl+a才触发@xxx.native
:在父组件中给子组件绑定一个原生的事件,就将子组件变成了普通的HTML标签,不加’. native’父组件绑定给子组件标签的事件是无法触发的
鼠标事件的修饰符3个
@click.left/right/middle
鼠标左中右按键
键盘事件的修饰符13个
键盘事件的触发前提条件是需要一个能够获取焦点的元素
@keydown/@keyup/@keypress
@keydown.13
:直接使用键码也可以,【所有的键码】
-
@keydown.enter
:只要按下enter键时就会触发事件 -
.space/.delete/.esc
-
up/down/left/right
-
ctrl/alt/shift/tab/meta(mac键)
自定义按键修饰符1个
-
Vue.config.keyCodes.a=65;
-
@keydown.a='xxx'
-
@keydown.65='xxx'
-
组合按键
@keydown.ctrl.65="xxx"
:只要按下ctrl+a就会触发- 默认情况下,按下的按键只要包含组合键(==哪怕还按了其他键)==也会触发,但是我们可以基于.exact修饰符,设置精准匹配(只有按下规定的键,其他键不按,才会触发)
@keydown.ctrl.65.exact
:只有按下ctrl+a才会触发@click.exact
:只有只点击了单击按键时触发
v-bind修饰符:attribute.xxx3个
-
:sup-name.camel
:把设定的sup-name
(中杠分格命名法)自动转为camel(驼峰命名)规范=>supName
-
:supName.prop
:把html结构上的attribute,转换为DOM节点对象的私有property- 默认情况下,给元素设置的属性都会呈现在元素的结构上,我们可以基于getAttribute/setAttribute来设置获取;
- 但是设置了prop修饰符,设置的自定义属性不在结构上,不再html结构中显示,而是放到了DOM对象的堆内存中
- 给元素设置自定义属性有两种方式
- 方式一:写在结构上,
<div index="8"></div>
,用setAttribute、getAttribute
- 方式二:写在堆内存中,
box{index:8}
,用box.index=8、box.index
- 方式一:写在结构上,
-
:supName.sync
:语法糖,会扩展成一个更新父组件绑定值的v-on
侦听器
v-model修饰符3个
v-model.lazy
:把监听事件从input换成change- input事件:只要正在输入就触发,不一定输入的内容进入到文本框中(例如:中文输入框)
- change事件:内容已经进入到文本框,摁下enter键时,(value值确认改变时),才会触发
- 所以change比input性能消耗低、但是流畅度也比较低
v-model.number
:规定输入的内容必须是数字格式- 文本框中默认输入的内容都是字符串格式,设置了这个修饰符,vue内部会把输入的内容自动转换为数字(如果出现非有效数字字符,则会把非有效字符及以后的内容干掉)
v-model.trim
:自动去除输入内容的首尾空格
生命周期函数(钩子函数)
我们想看某个阶段的数据情况:直接在new Vue中调用对应的方法,比如查看初始化之前的:beforeCreate(){}
-
生命周期钩子分为了四个阶段:
初始化、模板编译、更新、销毁
-
初始化:init(new Vue的时候走)
berforeCreate
:【初始化之前】组件实例刚刚创建,无任何属性,所以在这里,不能给任何属性赋值created
:【初始化之后】组件实例创建完成,data
和methods
中的数据已完成,el
已经生成了,但是因为需要挂载DOM
节点,而DOM
树还没生成,所以el
还没有被赋值。
-
模板编译:mount(new Vue的时候走)
beforeMount
:【模板编译之前】页面未渲染(没化妆),页面知道了{{msg}}需要vm中msg值,但并没有将vm的msg挂载上去- -----------------------------------------------------------这里是真正渲染页面的阶段--------------------------------------------------------------------
mounted
:【模板编译之后】页面渲染结束(化完妆了),到这里已经向页面中挂载完了数据,已经将vm中的msg获取到,并且挂载到{{msg}}上去
-
更新:update(响应式数据变了才会触发更新)
beforeUpdate
:【更新之前】响应式数据变化了,但页面还没来的及变化updated
:【更新之后】响应式数据变化了,页面也更新了
-
销毁:destroy(调用了
vm.$destroy()
方法,才会触发销毁阶段)beforeDestroy
:【销毁之前】实例销毁之前调用。在这一步,实例仍然完全可用,vm实例与视图的链接还没中断destroyed
:【销毁之后】实例销毁后调用。对应 Vue 的vm实例与视图的所有指令都被解绑,所有的事件监听器被移除,所有的子实例也都被销毁
,但是vm实例仍然存在,仍然可以获取vm中的私有属性,vm与视图的链接完全中断了- 执行了destroy操作,视图中的原来样式保留,后续就不再受vue控制了
-
<script> let vm=new Vue({ el:"div", data:{ msg:[10,20,30] }, beforeCreate(){ // this.msg[0]=100;//报错:因为还没有生成data,vm中不存在msg私有属性 }, created(){ this.msg[0]=100;//vm.msg=[100,20,30],已经生成了data和methods,可以修改vm的私有属性msg }, beforeMount(){ this.msg[1]=200;//vm.msg=[100,200,30],修改了vm的私有属性,但是页面还没有被渲染:<div>{{msg}}</div>=><div>{{msg}}</div> }, //在这两者之间,页面被渲染了:<div>{{msg}}</div>=><div>[100,200,30]</div> mounted(){ this.msg[2]=300;//vm.msg=[100,200,300],但是页面已经渲染了,页面的显示仍然是:<div>{{msg}}</div>=><div>[100,200,30]</div> }, beforeDestroy(){ //执行了vm.$destroy(),在这一步,实例仍然完全可用 }, destroyed(){ //执行了vm.$destroy(),到这一步,Vue的vm实例与视图的所有关系都解除了:指令都被解绑、事件监听器被移除、子实例也都被销毁 } }) vm.$destroy(); console.log(vm); </script>
-
-
除以上8个常用钩子函数之外还有3个不常用的钩子函数:
-
当keep-alive 缓存组件时才会有的生命周期的钩子函数
-
activated
:激活,被 keep-alive 缓存的组件激活时调用。 -
deactivated
:停用,被 keep-alive 缓存的组件失活时调用。 -
<keep-alive> keep-alive:用于缓存组件,如果该组件还会再启用,那么可以使用keep-alive进行组件缓存和优化,提高性能,缓存的组件不需要销毁,也不需要再创建 <component v-bind:is=“one”> //如果是动态属性,那么会去data中取值 </keep-alive>
-
// keep-alive 缓存组件 生命周期的钩子函数 activated deactivated // 生命周期的钩子函数 activated() {undefined // 当缓存组件有被显示出来时,会触发这个钩子函数 console.log(100); }, deactivated() {undefined // 当缓存的组件隐藏时,会触发这个钩子函数; console.log(200); }
-
-
errorCaptured
:错误捕获-
语法:
(err: Error, vm: Component, info: string) => ?boolean
-
说明:当子孙组件出错时,会调用这个钩子函数。此钩子会收到三个参数:错误对象、发生错误的组件实例以及一个包含错误来源信息的字符串。此钩子可以返回
false
以阻止该错误继续向上传播 -
// 当子孙组件出错时,会调用这个钩子函数 errorCaptured(a, b, c) {undefined // 当捕获一个来自子孙组件的错误时被调用。此钩子会收到三个参数:错误对象、发生错误的组件实例以及一个包含错误来源信息的字符串。此钩子可以返回 false 以阻止该错误继续向上传播。 console.log(a); console.log(b); console.log©; console.log(“子组件报错”) },
-
-
vue过渡与动画
过渡
- vue提供了
transition
的内置组件,在下列情形中,可以给任何元素和组件添加进入/离开过渡- 条件渲染(使用
v-if
) - 条件显示(使用
v-show
) - 动态组件
- 组件根节点
- 条件渲染(使用
过渡的类名:
![image-20211211233924356](https://i-blog.csdnimg.cn/blog_migrate/6d0e1c6418572b2b29f62543580f6e23.png)
-
在进入/离开的过渡中,会有6个class切换
-
使用方式:transition标签默认存在六个类名,这些类名都可以在style标签中用类选择器进行样式定义:
-
v-enter
:定义进入过渡的开始状态,在过渡开始之前生效,在DOM元素被插入之后的下一帧移除 -
v-enter-active
:定义进入过渡生效时的状态,在整个过渡的阶段中应用,在过渡/动画完成之后移除【用来定义进入过渡的过程时间,延迟和曲线函数(transition)】 -
v-enter-to
:定义进入过渡的结束状态。在DOM元素被插入之后的下一帧生效,在过渡/动画完成之后移除 -
v-leave
:定义离开过渡的开始状态。在离开过渡被触发时立即生效,下一帧立即移除 -
v-leave-active
:定义离开过渡生效时的状态,在整个离开过渡的阶段中应用,在过渡/动画完成之后移除【用来定义进入过渡的过程时间,延迟和曲线函数(transition)】 -
v-leave-to
:定义离开过渡结束状态。在元素被插入之后,在离开过渡被触发之后下一帧生效 (与此同时v-leave
被删除),在过渡/动画完成之后移除。<div id="demo"> <--!transition标签默认存在--> <transition name="mytran"> <p v-if="show"> hello </p> </transition> </div> <script> new Vue({ el:"#demo", data:{ show:true } }) </script> <style> .mytran-enter-active { transition: all .3s ease; } .mytran-leave-active { transition: all .8s cubic-bezier(1.0, 0.5, 0.8, 1.0); } .mytran-enter, .mytran-leave-to /* .mytran-leave-active for below version 2.1.8 */ { transform: translateX(10px); opacity: 0; } </style>
-
-
**自定义类名:**对于以上6个类名,如果我们直接用
<transition>
标签,则类名都是v-xxx
,如果想与默认的transition区分出来,需要给标签添加name标签<transition name="mytran">
,这时这个标签动画的所有类名就变成了mytran-xxx
-
动画
-
css动画与过渡的区别:就是在动画中
v-enter
类名在节点插入 DOM 元素后不会立即删除,而是在animationend
事件触发时删除。 -
<div id="example-2"> <button @click="show = !show">Toggle show</button> <transition name="bounce"> <p v-if="show">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris facilisis enim libero, at lacinia diam fermentum id. Pellentesque habitant morbi tristique senectus et netus.</p> </transition> </div>
-
new Vue({ el: '#example-2', data: { show: true } })
-
.bounce-enter-active { animation: bounce-in .5s; } .bounce-leave-active { animation: bounce-in .5s reverse; } @keyframes bounce-in { 0% { transform: scale(0); } 50% { transform: scale(1.5); } 100% { transform: scale(1); } }
-
animation:动画名称+动画的持续时间+动画执行顺序
-
@keyframes bounce-in:定义整个动画,按百分比实现动画分阶段状态
多组件、元素过渡
-
多元素过渡用:
v-bind:key
指定唯一标签-
<transition> <button v-if="isEditing" key="save"> Save </button> <button v-else key="edit"> Edit </button> </transition>
-
-
多组件过渡:用
component
标签和is
属性,实现动态组件绑定-
<transition name="component-fade" mode="out-in"> <component v-bind:is="view"></component> </transition>
-
new Vue({ el: '#transition-components-demo', data: { view: 'v-a' }, components: { 'v-a': { template: '<div>Component A</div>' }, 'v-b': { template: '<div>Component B</div>' } } })
-
.component-fade-enter-active, .component-fade-leave-active { transition: opacity .3s ease; } .component-fade-enter, .component-fade-leave-to /* .component-fade-leave-active for below version 2.1.8 */ { opacity: 0; }
-
列表过渡
-
列表过渡常用在v-for的标签里边,这时标签名就要用
<transition-group>
- 不同于
<transition>
,它会以一个真实元素呈现:默认为一个<span>
。你也可以通过tag
attribute 更换为其他元素。 - 过渡模式不可用,因为我们不再相互切换特有的元素。
- 内部元素总是需要提供唯一的
key
attribute 值。 - CSS 过渡的类将会应用在内部的元素中,而不是这个组/容器本身。
- 不同于
-
<div id="list-demo" class="demo"> <button v-on:click="add">Add</button> <button v-on:click="remove">Remove</button> <transition-group name="list" tag="p"> <span v-for="item in items" v-bind:key="item" class="list-item"> {{ item }} </span> </transition-group> </div>
-
new Vue({ el: '#list-demo', data: { items: [1,2,3,4,5,6,7,8,9], nextNum: 10 }, methods: { randomIndex: function () { return Math.floor(Math.random() * this.items.length) }, add: function () { this.items.splice(this.randomIndex(), 0, this.nextNum++) }, remove: function () { this.items.splice(this.randomIndex(), 1) }, } })
-
.list-item { display: inline-block; margin-right: 10px; } .list-enter-active, .list-leave-active { transition: all 1s; } .list-enter, .list-leave-to /* .list-leave-active for below version 2.1.8 */ { opacity: 0; transform: translateY(30px); }
如何启一个项目
-
第一步:安装脚手架
$npm i @vue/cli -g
-
第二步:创建一个项目
$vue create xxx
- 选择各项配置…最后安装成功。。。
-
第三步:.gl文件、readme文件都可以删除,无用的初始文件都可以删掉
-
第四步:把没有安装的模块自己安装一下:
$npm i axios qs blueimp-md5 element-ui
- 生产依赖:
element-ui axios qs blueimp-md5 @babel/polyfill vue vuex. vue-router
- 开发环境:
less less-loader babel-plugin-component -D
(UI组件库按需导入模块)
-
第五步:配置浏览器兼容
-
//package.json "browserslist": [ "> 1%", "last 2 versions", "not dead" ]
-
安装:
@babel/polyfill
在main.js中导入
-
-
第六步:完成基础的配置
-
//vue.config.js const env=proccess.env.NODE_ENV;//获取环境“production”或“development” module.exports={ lintOnSave: env!=='production',//是否开启词法检测 publicPath:"./", productionSourceMap:false, devServer:{ proxy:{ '/api':{ target:"http://127.0.0.1:9999", changeOrigin:true, ws:true, pathRewrite:{ '/api':'' } } } } }
-
-
第七步:element-ui的全量导入或局部导入
-
第八步:在vuex里配置日志检测vuex
-
//index.js ->vuex import Vue from 'vue' import Vuex from 'vuex' import createLogger from 'vuex/dist/logger' Vue.use(Vuex) const env=process.env.NODE_ENV; export default new Vuex.Store({ state: { }, mutations: { }, actions: { }, modules: { }, plugins:env=="production"?[]:[createLogger()] })
-
-
第九步:api文件下创建http.js对axios二次封装
import axios from 'axios'; import qs from 'qs'; import { Message } from 'element-ui'; // 基础配置 axios.defaults.baseURL = "";//设置所有请求前缀;因为拿vue.config.js做了这里就不做了 axios.defaults.timeout = 60000;//设置超时时间 axios.defaults.withCredentials = true;//设置跨域是否携带资源凭证 axios.defaults.transformRequest = data => {//设置对象转为urlencoded格式 if (isPlainObject(data)) return qs.stringify(data); return data; }; axios.defaults.validateStatus = status => {//设置状态码正确范围 return status >= 200 && status < 400; }; // 拦截器 axios.interceptors.request.use(config => {//设置请求拦截器 return config; }); axios.interceptors.response.use(response => {//设置响应拦截器 return response.data; }, reason => { if (reason && reason.response) { // @1 有返回结果,只不过状态码不对 let { status } = reason.response; switch (+status) { case 403: Message({ type: 'error', message: '小主,服务器拒绝了当前请求~~' }); break; case 404: Message.error('小主,您请求的地址是错误的~~'); break; case 500: Message.error('小主,服务器开小差了~~'); break; } } else { // @2 请求超时或者中断 if (reason && reason.code === "ECONNABORTED") { Message.error('小主,当前请求超时了~~'); } // @3 断网 if (!navigator.onLine) { Message.error('小主,当前网络繁忙请稍后再试~~'); } } return Promise.reject(reason); }); export default axios;
- index.js 存放各种api接口
-
第十步:分析需求:
- 分析如何构建路由
- 分析组件如何划分
管理系统项目
- 一:登录和登录态的校验【一般管理系统的项目,如果用户没有登陆,什么都访问不了】
- 方案一:基于cookie加session的校验机制
- 缺点:使用的是存储方案,服务器一重启,所有session都没有了,而且不利于分布式部署
- 解决方案:用redis存储代替session【或者备份一份】
- **方案二:**基于token机制【当前最常用的】
- 方案一:基于cookie加session的校验机制
vue实现图片懒加载
-
借鉴地址:https://www.cnblogs.com/xieli26/p/10057763.html
-
**第一步:**安装插件
-
npm install vue-lazyload --save-dev
-
-
**第二步:**在main.js中引入使用
-
import VueLazyload from 'vue-lazyload' //直接使用: Vue.use(VueLazyload) //或者配置使用 Vue.use(VueLazyload, { preLoad: 1.3, error: 'dist/error.png', loading: 'dist/loading.gif', attempt: 1 })
-
-
**第三步:**修改img标签为懒加载(将 :src 属性直接改为v-lazy)
-
<a href="javascript:;"><img v-lazy="'/static/img/' + item.productImage"></a>
-