前言 本文是对vue2.0的重新学习后的补充知识点
v-for使用js实现 vue是声明式编码(声明就行),下方的为命令式编码(一步一步走缺一不可)
1 2 3 4 5 6 7 8 9 <ul></ul> var list = [{str : '第一个' }, {str : '第二个' }]var ul = document .querySelector ('ul' )var li = '' list.forEach (item => { li += `<li>${item.str} </li>` }) ul.innerHTML = li
vue初识 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 初识Vue : 1. 想让Vue 工作,就必须创建一个Vue 实例,且要传入一个配置对象;2. Vue 实例和容器是一一对应的;<div id="root" > <h1 > Hello,{{name}}</h1 > </div> Vue .config .productionTip = false new Vue ({ el :'#root' , data :{ name :'moxie' , } }) 3. {{ xxx }}{{1 +1 }}中要写js表达式,或者到data中的属性;当然只要是vm中的属性都可以写。注意区分:js表达式 和 js代码(语句) 1. 表达式:一个表达式会产生一个值,可以放在任何一个需要值的地方: (1 ). a (2 ). a+b (3 ). demo (1 ) (4 ). x === y ? 'a' : 'b' 2. js代码(语句) (1 ). if (){} (2 ). for (){}
vue的基础补充 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <h2 v-model :x="name" >你好啊</h2> const vm = new Vue ({ data :{ name :'默谐' } }) v.$mount('#root' )
MVVM模型
1 2 3 4 5 6 7 MVVM 模型 1. M:模型(Model ) :data中的数据 2. V:视图(View ) :模板代码 3. VM :视图模型(ViewModel ):Vue 实例 观察发现: 1. data中所有的属性,最后都出现在了vm身上。 2. vm身上所有的属性 及 Vue 原型上所有属性,在Vue 模板中都可以直接使用。
数据代理 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 var person = {}Object .defineProperty (person,'age' ,{ }) Object .defineProperty (person,'age' ,{ get ( ){ console .log ('有人读取age属性了' ) return number }, set (value ){ console .log ('有人修改了age属性,且值是' ,value) number = value } }) let obj = {x :100 }let obj2 = {y :200 }Object .defineProperty (obj2,'x' ,{ get ( ){ return obj.x }, set (value ){ obj.x = value } }) 1. Vue 中的数据代理:通过vm对象来代理data对象中属性的操作(读/写)2. Vue 中数据代理的好处:更加方便的操作data中的数据3. 基本原理: 通过Object .defineProperty ()把data对象中所有属性添加到vm上。 为每一个添加到vm上的属性,都指定一个getter/setter。 在getter/setter内部去操作(读/写)data中对应的属性。
事件处理 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 <div id="root" > <h2 > {{name}}</h2 > <button @click ="showInfo1" > 点我提示信息1(不传参)</button > <button @click ="showInfo2($event,'参数')" > 点我提示信息2(传参)</button > </div> new Vue ({ el :'#root' , data :{ name :'moxie' , }, methods :{ showInfo1 (event ){ console .log (event) }, showInfo2 (event,val ){ console .log (event,val) } } })
计算属性 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 <div id="root" > 姓:<input type="text" v-model="firstName" > 名:<input type="text" v-model="lastName" > 全名:<span>{{fullName}}</span> </div> const vm = new Vue ({ el :'#root' , data :{ firstName :'张' , lastName :'三' , }, computed :{ fullName :{ get ( ){ return this .firstName + '-' + this .lastName }, set (value ){ const arr = value.split ('-' ) this .firstName = arr[0 ] this .lastName = arr[1 ] } } } }) <div id="app" >{{show (message)}}</div> computed :{ fullName ( ){ return this .firstName + '-' + this .lastName } } new Vue ({ el : '#app' , data : { message : '消息' }, computed : { show ( ) { return function (value ) { return '处理后的' + value } } } })
侦听属性 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 watch :{ isHot :{ immediate :true , deep :true , handler (newValue,oldValue ){ console .log ('isHot被修改了' ,newValue,oldValue) } } } vm.$watch('isHot' ,{}) vm.$watch('isHot' ,()=> {})
computed和watch之间的区别:
computed能完成的功能,watch都可以完成。
watch能完成的功能,computed不一定能完成,例如:watch可以进行异步操作。
两个重要的小原则:
所被Vue管理的函数,最好写成普通函数,这样this的指向才是vm 或 组件实例对象。
所有不被Vue所管理的函数(定时器的回调函数、ajax的回调函数等、Promise的回调函数),最好写成箭头函数,这样this的指向才是vm 或 组件实例对象。
绑定样式 1 2 3 4 5 6 7 8 1. class 样式 写法:class ="xxx" xxx可以是字符串、对象、数组。 字符串写法适用于:类名不确定,要动态获取。 对象写法适用于:要绑定多个样式,个数确定,名字也确定,但不确定用不用。 数组写法适用于:要绑定多个样式,个数不确定,名字也不确定。 2. style样式 :style="{fontSize: xxx}" 其中xxx是动态值。 :style="[a,b]" 其中a、b是样式对象。
条件渲染 1 2 3 4 5 6 7 8 9 10 11 12 13 14 1. v-if 适用于:切换频率较低的场景。 特点:不展示的DOM 元素直接被移除。 注意:v-if 可以和:v-else -if 、v-else 一起使用,但要求结构不能被“打断”。 2. v-show 写法:v-show="表达式" 适用于:切换频率较高的场景。 特点:不展示的DOM 元素未被移除,仅仅是使用样式隐藏掉 3. 备注:使用v-if 的时,元素可能无法获取到,而使用v-show一定可以获取到。4. 注意: v-if 只能与template的配合使用,而v-show是并不可以的
列表渲染 1 2 3 4 5 1. 可遍历:数组、对象、字符串(用的很少)、指定次数(用的很少)2. v-for 有两种写法(推荐第一种) v-for ="(item, index) in xxx" :key="yyy" v-for ="(item, index) of xxx" :key="yyy" 3. 注意key值
key的原理 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 1. 虚拟DOM中key的作用: key是虚拟DOM对象的标识,当数据发生变化时,Vue会根据【新数据】生成【新的虚拟DOM】, 随后Vue进行【新虚拟DOM】与【旧虚拟DOM】的差异比较,比较规则如下: 2.对比规则: (1).旧虚拟DOM中找到了与新虚拟DOM相同的key: ①.若虚拟DOM中内容没变, 直接使用之前的真实DOM! ②.若虚拟DOM中内容变了, 则生成新的真实DOM,随后替换掉页面中之前的真实DOM。 (2).旧虚拟DOM中未找到与新虚拟DOM相同的key 创建新的真实DOM,随后渲染到到页面。 3. 用index作为key可能会引发的问题: 1. 若对数据进行:逆序添加、逆序删除等破坏顺序操作: 会产生没有必要的真实DOM更新 ==> 界面效果没问题, 但效率低。 2. 如果结构中还包含输入类的DOM: 会产生错误DOM更新 ==> 界面有问题。 4. 开发中如何选择key?: 1.最好使用每条数据的唯一标识作为key, 比如id、手机号、身份证号、学号等唯一值。 2.如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅用于渲染列表用于展示, 使用index作为key是没有问题的。
举例: 使用index作为key的部分缺陷
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 <div id="root" > <!-- 遍历数组 --> <h2 > 人员列表(遍历数组)</h2 > <button @click.once ="add" > 添加一个老刘</button > <ul > <li v-for ="(p,index) of persons" :key ="index" > {{p.name}}-{{p.age}} <input type ="text" > </li > </ul > </div > <script type ="text/javascript" > Vue .config .productionTip = false new Vue ({ el :'#root' , data :{ persons :[ {id :'001' ,name :'张三' ,age :18 }, {id :'002' ,name :'李四' ,age :19 }, {id :'003' ,name :'王五' ,age :20 } ] }, methods : { add ( ){ const p = {id :'004' ,name :'老刘' ,age :40 } this .persons .unshift (p) } }, }) </script >
vue的监视数据的原理 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Vue监视数据的原理: 1. vue会监视data中所有层次的数据。 2. 如何监测对象中的数据? 通过setter实现监视,且要在new Vue时就传入要监测的数据。 (1).对象中后追加的属性,Vue默认不做响应式处理 (2).如需给后添加的属性做响应式,请使用如下API: Vue.set(target,propertyName/index,value) 或 vm.$set(target,propertyName/index,value) 3. 如何监测数组中的数据? 通过包裹数组更新元素的方法实现,本质就是做了两件事: (1).调用原生对应的方法对数组进行更新。 (2).重新解析模板,进而更新页面。 4.在Vue修改数组中的某个元素一定要用如下方法: 1.使用这些API:push()、pop()、shift()、unshift()、splice()、sort()、reverse() 2.Vue.set() 或 vm.$set() 特别注意:Vue.set() 和 vm.$set() 不能给vm 或 vm的根数据对象添加属性!!!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 let data = { name :'默谐' , } const obs = new Observer (data) console .log (obs) let vm = {}vm._data = data = obs function Observer (obj ){ const keys = Object .keys (obj) keys.forEach ((k )=> { Object .defineProperty (this ,k,{ get ( ){ return obj[k] }, set (val ){ console .log (`${k} 被改了,我要去解析模板,生成虚拟DOM.....我要开始忙了` ) obj[k] = val } }) }) }
v-model表单使用 1 2 3 4 5 6 7 8 9 10 1. 没有配置input的value属性,那么收集的就是checked(勾选 or 未勾选,是布尔值) 2. 配置input的value属性: (1 )v-model的初始值是非数组,那么收集的就是checked(勾选 or 未勾选,是布尔值) (2 )v-model的初始值是数组,那么收集的的就是value组成的数组 lazy:失去焦点再收集数据 number:输入字符串转为有效的数字 trim:输入首尾空格过滤
过滤器补充 1 2 3 4 5 6 7 8 语法: 1. 注册过滤器:Vue .filter (name,callback) 或 new Vue {filters :{}} 2. 使用过滤器:{{ xxx | 过滤器名}} 或 v-bind :属性 = "xxx | 过滤器名" 备注: 1. 过滤器也可以接收额外参数、多个过滤器也可以串联 2. 并没有改变原本的数据, 是产生新的对应的数据 注意:过滤器和methods一样没有缓存,用了几次就会调用几次
内置指令 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 v-cloak指令(没有值): 1. 本质是一个特殊属性,Vue 实例创建完毕并接管容器后,会删掉v-cloak属性。 2. 使用css配合v-cloak可以解决网速慢时页面展示出{{xxx}}的问题。 <style> [v-cloak]{ display :none; } </style> <div v-cloak > {{name}}</div > v-once指令: 1. v-once所在节点在初次动态渲染后,就视为静态内容了。 2. 以后数据的改变不会引起v-once所在结构的更新,可以用于优化性能。 v-pre指令: 1. 跳过其所在节点的编译过程。{{}}等v-bind都不会被解析 2. 可利用它跳过:没有使用指令语法、没有使用插值语法的节点,会加快编译。
自定义指令 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 directives :{ big (element,binding,vNoed ){ console .log ('big' ,this ) element.innerText = binding.value * 10 vNoed.context vNode.context .$nextTick() }, fbind :{ bind (element,binding ){ element.value = binding.value }, inserted (element,binding ){ element.focus () }, update (element,binding ){ element.value = binding.value } } }
声命周期 1 2 3 4 5 6 7 常用的生命周期钩子: 1.mounted: 发送ajax请求、启动定时器、绑定自定义事件、订阅消息等【初始化操作】。 2.beforeDestroy: 清除定时器、解绑自定义事件、取消订阅消息等【收尾工作】。 关于销毁Vue实例: 1.销毁后借助Vue开发者工具看不到任何信息。 2.销毁后自定义事件会失效,但原生DOM事件依然有效。(重要) 3.一般不会在beforeDestroy操作数据,因为即便操作数据,也不会再触发更新流程了。
组件 非单文件组件的使用 1 2 3 4 5 6 7 8 9 10 11 12 13 Vue 中使用组件的三大步骤: ①定义组件(创建组件) ②注册组件 ③使用组件(写组件标签) 一、如何定义一个组件? 使用Vue .extend (options)创建,options和new Vue 传入的那个几乎一样,但也有如下区别: 1. el不能写,所有组件都经过vm管理,由vm中的el决定服务哪个容器。使用template配置组件结构。 2. data必须写成函数,避免组件被复用时,数据存在引用关系。 二、如何注册组件? 1. 局部注册:靠new Vue 的时候传入components选项 2. 全局注册:靠Vue .component ('组件名' ,组件) 三、编写组件标签: <school></school>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 const my-info = Vue .extend ({ template :`<div class="demo">{{name}}</div>` , data ( ){ return { name :'moxie' , } } }) Vue .component ('my-info' ,my-info) new Vue ({ el :'#root' , components :{'my-info' } })
关于VueComponent
每调用Vue.extend,返回的都是一个全新的VueComponent(组件实例对象)
this指向:组件中this是VueComponent实例对象,vue的实例对象是Vue实例对象。
组件实例对象(简称vc);vue的实例对象(简称vm)。
一个重要的内置关系:VueComponent.prototype.proto === Vue.prototype
作用:让组件实例对象(vc)可以访问到 Vue原型上的属性、方法。 默认情况:VueComponent.prototype.__proto__指向的是Object,Vue进行处理了。
非单文件组件与单文件组件
非单文件组件:简单来说就是没有使用脚手架搭建项目,直接引入vue.js在HTML中使用;
单文件组件:使用脚手架搭建项目,一个组件就是一个vue文件,包含了HTML、CSS、js。
Vue脚手架 Vue脚手架index.html的相关注释 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 <!DOCTYPE html > <html lang ="" > <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" > <link rel ="icon" href ="<%= BASE_URL %>favicon.ico" > <link rel ="stylesheet" href ="<%= BASE_URL %>css/bootstrap.css" > <title > 硅谷系统</title > </head > <body > <noscript > <strong > We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue. </strong > </noscript > <div id ="app" > </div > </body > </html >
其他补充
ref属性
props 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 props :['name' ]props :{name :String }props :{ name :{ type :String , required :true , default :'老王' } }
mixin混入
插件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
scoped样式
webStorage 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
自定义事件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 props :['addTodo' ],methods : { add ( ){ this .addTodo (todoObj) } }, <Child :addTodo ="addTodo" > </Child > methods :{ addTodo (value ){ consolelog (value) }
全局事件总线 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 new Vue ({ ...... beforeCreate ( ) { Vue .prototype .$bus = this }, ...... }) methods ( ){ demo (data ){......} } ...... mounted ( ) { this .$bus .$on('xxxx' ,this .demo ) }
消息订阅与发布(pubsub) 所有的框架都可以使用
nextTick 1 2 3 1. 语法:this .$nextTick(回调函数)2. 作用:在下一次 DOM 更新结束后执行其指定的回调。3. 什么时候用:当改变数据后,要基于更新后的新DOM 进行某些操作时,要在nextTick所指定的回调函数中执行。
Vue封装的过度与动画 基本使用 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 <transition name="hello" appear> <h1 v-show ="isShow" > 你好啊!</h1 > </transition> .hello -enter-active{ animation : atguigu 0. 5s linear; } .hello -leave-active{ animation : atguigu 0. 5s linear reverse; } @keyframes atguigu { from { transform : translateX (-100 %); } to{ transform : translateX (0px); } } .hello -enter,.hello -leave-to{ transform : translateX (-100 %); } .hello -enter-active,.hello -leave-active{ transition : 0. 5s linear; } .hello -enter-to,.hello -leave{ transform : translateX (0 ); } <transition-group appear name="hello" > <h1 v-show ="!isShow" key ="1" > 你好啊!</h1 > <h1 v-show ="isShow" key ="2" > 尚硅谷!</h1 > </transition-group> <transition mode ="out-in" > </transition >
使用第三方animate.css 1 2 3 4 5 6 7 8 9 10 11 12 <transition-group appear name="animate__animated animate__bounce" enter-active-class ="animate__swing" leave-active-class ="animate__backOutUp" > <h1 v-show ="!isShow" key ="1" > 你好啊!</h1 > <h1 v-show ="isShow" key ="2" > 尚硅谷!</h1 > </transition-group>
代理配置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
vue-resource的使用 1 2 3 4 5 6 import vueResource from 'vue-resource' Vue .use (vueResource)this .$http .get (url).then ()
插槽补充 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <template slot="center" > <div > html结构1</div > </template> <template v-slot:footer > <div > html结构2</div > </template > <div slot ="center" > </div > <template v-slot:child ="{ msg }" > </template >
Vuex的补充 Vuex的环境 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 import Vue from 'vue' import Vuex from 'vuex' Vue .use (Vuex ) const actions = {}const mutations = {}const state = {}const getters = {}export default new Vuex .Store ({ actions, mutations, state, getters }) import store from './store' new Vue ({ store, })
vuex的简写map…函数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 import {mapState,mapGetters} from 'vuex' computed :{ ...mapState (['sum' ,'school' ,'subject' ]), ...mapGetters (['bigSum' ]) }, import {mapMutations,mapActions} from 'vuex' methods : { ...mapMutations ({increment :'JIA' ,decrement :'JIAN' }), incrementWait ( ){ this .$store .dispatch ('jiaWait' ,this .n ) }, */ ...mapActions ({incrementOdd :'jiaOdd' ,incrementWait :'jiaWait' }) },
vuex模块化+命名空间 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 import countOptions from './count' import personOptions from './person' export default new Vuex .Store ({ modules :{ countAbout :countOptions, personAbout :personOptions } }) export default = { namespaced :true , } export default = { ... } computed :{ ...mapState ('countAbout' ,['sum' ,'school' ,'subject' ]), ...mapState ('personAbout' ,['personList' ]), ...mapGetters ('countAbout' ,['bigSum' ]) }, methods : { ...mapMutations ('countAbout' ,{increment :'JIA' ,decrement :'JIAN' }), ...mapActions ('countAbout' ,{incrementOdd :'jiaOdd' ,incrementWait :'jiaWait' }) }, this .$store .state .personAbout .list this .$store .getters ['personAbout/firstPersonName' ]this .$store .dispatch ('personAbout/addPersonWang' ,person)this .$store .commit ('personAbout/ADD_PERSON' ,person)
vue路由补充 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <router-link :to="`/home/message/detail/${m.id}/${m.title}`" >跳转</router-link> <router-link :to ="{ name:'xiangqing', params:{ id:666, title:'你好' } }" > 跳转</router-link >
路由props属性 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 { name :'xiangqing' , path :'detail/:id' , component :Detail , props ($route ){ return { id :$route.query .id , title :$route.query .title } } } props :['id' ,'title' ]
编程式导航 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 this .$router .push ({ name :'xiangqing' , params :{ id :xxx, title :xxx } }) this .$router .replace ({ name :'xiangqing' , params :{ id :xxx, title :xxx } }) this .$router .forward () this .$router .back () this .$router .go ()
缓存路由组件 1 2 3 4 5 6 7 8 <keep-alive include="News" > <router-view > </router-view > </keep-alive> <keep-alive :include ="[a,b]" > <router-view > </router-view > </keep-alive >
路由组件独有两个新的生命周期 1 2 3 4 5 6 7 8 9 activated ( ) { console .log ('News组件被激活了' ) }, deactivated ( ) { console .log ('News组件失活了' ) clearInterval (this .timer ) },
路由独享守卫 1 2 3 4 5 6 7 8 9 10 11 12 13 14 beforeEnter (to,from ,next ){ console .log ('beforeEnter' ,to,from ) if (to.meta .isAuth ){ if (localStorage .getItem ('school' ) === 'atguigu' ){ next () }else { alert ('暂无权限查看' ) } }else { next () } }
组件内守卫 1 2 3 4 5 6 7 beforeRouteEnter (to, from , next) {}, beforeRouteLeave (to, from , next) {}