这篇文章上次修改于 215 天前,可能其部分内容已经发生变化,如有疑问可询问作者。
Vue (发音为 /vjuː/,类似 view) 是一款用于构建用户界面的 JavaScript 框架。它基于标准 HTML、CSS 和 JavaScript 构建,并提供了一套声明式的、组件化的编程模型,帮助你高效地开发用户界面。无论是简单还是复杂的界面,Vue 都可以胜任。
Vue 1简介 1.入门
前端框架,便于书写
基于MVVM的思想,实现数据的双向绑定,将编程的关注点放在数据上
1 <script src ="js/vue.min.js" > </script >
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 <!DOCTYPE html > <html > <head > <meta charset ="UTF-8" > <title > </title > <script src ="js/vue.js" > </script > </head > <body > <div id ="app" > {{message}} - {{age}} </div > <script > const app = new Vue ({ el :"#app" , data :{ message :"Lani" , age :10 } }); </script > </body > </html >
2.双大括号
1.表达式,一个表达式会生成一个值,可以放在任何一个需要值的地方
(1)a
(2)a+b
(3)demo()
(4) x==y?x:y
2.JS代码
(1)if(){}
(2)for(){}
3.注意事项 Vue实例和容器里的代码为Vue模板
真实开发只有一个Vue实例
2.Vue2 1.vue的创建 1 2 3 4 5 6 7 8 9 10 const vm = new Vue ({ data :{ name :"za" , adress :"sh" } }); vm.$mount("#root" );
1 2 3 4 5 6 7 8 9 10 11 12 data :{ name :"za" , adress :"sh" } data :function ( ){ return { name :"za" , adress :"sh" } }
2.数据代理 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 let number = 18 let person ={name:'张' ,sex:'男' }Object .defineProperty (person,'age' ,{ get ( ){ console .log ('有人读取age属性了“) return number } //当有人修改person的age属性时,set函数(setter)就会被调用,且会收到修改的具体值 set(value){ console.log(‘有人修改了age属性,且值是’,value) number = value } }
1 2 3 4 5 6 1.Vue中的数据代理: 通过vm对象来代理data对象中属性的操作(读/写) 2.Vue中数据代理的好处: 更加方便的操作data中的数据 3.基本原理: 通过Object.defineProperty()把data对象中所有属性添加到vm上。为每一个添加到vm上的属性,都指定一个getter/setter。在getter/setter内部去操作(读/写)data中对应的属性。
3.事件处理
事件的基本使用:
1.使用v-on:xxx或@xxx绑定事件,其中xxx是事件名;
2.事件的回调需要配置在methods对象中,最终会在vm上;
3.methods中配置的函数,不要用箭头函数!否则this就不是vm了;
4.methods中配置的函数,都是被Vue所管理的函数,this的指向是vm 或 组件实例对象;
5.@click=”demo”利@click=”demo($event)”效果一致,但后者可以传参;
4.事件修饰符 1.prevent:阻止默认事件(常用)
2.stop:阻止事件冒泡(常用)
3.once:事件只触发一次(常用)
4.capture:使用事件的捕获模式
5.self:只有event.target是当前操作的元素时才触发事件
6.passive:事件的默认行为立即执行,无需等待事件回调执行完毕
5.键盘事件 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 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <meta name ="viewport" content ="width=device-width, initial-scale=1.0" > <title > Document</title > <script src ="../vue.min.js" > </script > </head > <body > <div id ="root" > <input type ="text" placeholder ="输入" @keyup.delete ="show" > </div > <script > const vm = new Vue ({ el :"#root" , data :{ name :"za" , adress :"sh" }, methods :{ show (event ){ console .log (event.target .value ); } } }); </script > </body > </html >
回车=>enter
删除=>delete(捕获“删除”和“退格”键)
退出 =>esc
空格=>space
换行=>tab
上=>up
下 => down
左=> left
右=>right
2.Vue未提供别名的按键,可以使用按键原始的key值去绑定,但注意要转为kebab-case(短横线命名)
3.系统修饰健(用法特殊):ctrl、alt、shift、meta
(1)配合keyup使用:按下修饰键的同时,再按下其他键,随后释放其他键,事件才被触发
(2)配合keydown使用:正常触发事件
4.也可以使用keyCode去指定具体的按键(不推荐)例如13是回车
5.Vue.config.keyCodes.自定义键名=键码,可以去定制按键别名
6.计算属性 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 computed :{ fullname :{ get ( ){ return this .name +'-' +this .nname ; }, set (value ){ console .log ("set" +value); } } } computed :{ info ( ){ return this .isHot ? "热" :"冷" ; } }
1.定义:要用的属性不存在,要通过己有属性计算得来
2.原理:底层借助了0bjcet.defineproperty方法提供的getter和lsetter
3.get函数什么时候执行?
(1)初次读取时会执行一次
(2)当依赖的数据发生改变时会被再次调用
4.优势:与methods实现相比,内部有缓存机制(复用),效率更高,调试方便
5.备注:
1.计算属性最终会出现在vm上
直接读取使用即可
2.如果计算属性要被修改,那必须写set函数去响应修改,且set中要引起计算时依赖的数据发生改变的处理逻辑
7.监视属性 @xxx=”yyy”可以执行一些简单案例
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 const vm = new Vue ({ el :"#root" , data :{ isHot :true }, methods :{ change ( ){ console .log (1 ); this .isHot =!this .isHot ; } }, computed :{ info ( ){ return this .isHot ? "热" :"冷" ; } }, watch :{ isHot :{ immediate :true , handler (newValue,oldValue ){ console .log ("修改1" ); } } } }); vm.$watch('isHot' ,{ handler (newValue,oldValue ){ console .log ("修改2" ); } })
8.深度监视 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 watch :{ 'numbers.a' :{ handler (newValue,oldValue ){ console .log ("aaaa" ); } }, numbers :{ handler (newValue,oldValue ){ console .log ("bbbb" ); } } }
监视的简写
1 2 3 isHot (newValue,oldValue ){ console .log ("修改333" ); }
1 2 3 vm.$watch('isHot' ,function (newValue,oldValue ){ console .log ("修改2" ); })
9.computed和watch之间的区别: 1.computed能完成的功能,watch都可以完成
2.watch能完成的功能,computed不一定能完成,例如:watch可以进行异步操作
两个重要的小原则
1.所有被Vue管理的函数,最好写成普通函数,这样this的指向才是vm 或 组件实例对象。2.所有不被Vue所管理的函数(定时器的回调函数、ajax的回调函数等),最好写成箭头函数,这样this的指向才是vm 或组件实例对象
10.vue绑定class和style 绑定样式:
1.class样式
写法 :class=”xxx”xxx可以是字符串、对象、数组。
字符串写法适用于:类名不确定,要动态获取。
对象写法适用于:要绑定多个样式,个数不确定,名字也不确定。
数组写法适用于:要绑定多个样式,个数确定,名字也确定,但不确定用不用。
2.style样式
:style=”{fontSize:xxx}”其中xxx是动态值。
:style=”[a,b]”其中a、b是样式对象。
字符串写法:适用于样式的类名不太确定,需要动态指定的情况。代码中的backgroundColor是变量名
1 <div class ="basic" :class ="backgroundColor" > hello</div >
数组写法:适用于样式的数量不太确定,名字也不太确定,需要后期动态添加或删减的。
1 <div class ="basic" :class ="classArr" > hello</div >
对象写法:适用于样式的数量确定,名字确定,但要动态决定用不用。
1 <div class ="basic" :class ="classObj" > hello</div >
3.Vue指令 v-bind
Vue模板语法有2大类:
1.插值语法:
功能:用于解析标签体内容。
写法:,xxx是js表达式,且可以直接读取到data中的所有属性。
2.指令语法:
功能:用于解析标签(包括:标签属性、标签体内容、绑定事件….)。举例:v-bind:href=”xxx”或 简写为:href=”xxx”,xxx同样要写js表达式,且可以直接读取到data中的所有属性。
备注:Vue中有很多的指令,且形式都是:v-????
<a v-bind:href="url"> 1 </a>
v-model <input type="text" v-model="url">
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <body > //v-bind将超链接改为url <div id ="app" > <a v-bind:href ="url" > 超链接1</a > <a :href ="url" > 超链接2</a > //事实绑定修改url,a标签里的url也会随着改变 <input type ="text" v-model ="url" > </div > </body > <script > new Vue ({ el :"#app" , data :{ url :"https://baidu.com" } }) </script >
必须在数据模型中声明,例如上面的url
v-on
(为HTMl标签绑定事件)
v-on:事件
或者简写为@事件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <body > <div id ="app" > <input type ="text" v-on:click ="handle" > 输入 </div > </body > <script > new Vue ({ el :"#app" , data :{ }, methods :{ handle :function ( ){ alert ("哈" ); } } }) </script >
v-if
根据条件是否渲染
中间不能被其他语句断开
v-else-if
v-else
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <body > <div id ="app" > <span v-if ="age>=45" > old</span > <span v-else-if ="age<45 && age>=18" > mid</span > <span v-if ="age<18" > low</span > <input type ="text" v-model ="age" > 输入 </div > </body > <script > new Vue ({ el :"#app" , data :{ age :0 } }) </script >
v-show
与v-if的区别为display属性的值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <body > <div id ="app" > <input type ="text" v-model ="age" > 输入 <span v-show ="age>=45" > old</span > <span v-show ="age<45 && age>=18" > mid</span > <span v-show ="age<18" > low</span > </div > </body > <script > new Vue ({ el :"#app" , data :{ age :0 } }) </script >
v-for
列表渲染,遍历容器的元素或者对应的属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <body > <div id ="app" > <div v-for ="addr in addrs" > {{addr}} </div > <div v-for ="(addr,index) in addrs" > {{index+1}}:{{addr}} </div > </div > </body > <script > new Vue ({ el :"#app" , data :{ addrs :["一" ,"二" ,"三" ,"四" ] }, }) </script >
1.虚拟DOM中key的作用:(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是没有问题的。
v-once
在初次渲染后就视为静态内容
v-pre
用于没有指令语法、没有使用插值语法的节点
提高编译速度
4.vue2技术 1.列表过滤 使用watch
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 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <meta name ="viewport" content ="width=device-width, initial-scale=1.0" > <title > Document</title > <script src ="../vue.js" > </script > </head > <body > <div id ="root" > <h1 > 人员</h1 > <input type ="text" placeholder ="请输入" v-model ="keyWord" > <ul > <li v-for ="(p,index) of filperson" :key ="index" > {{p.name}}---{{p.sex}} </li > </ul > </div > <script > const vm = new Vue ({ el :"#root" , data :{ keyWord :"" , person :[ { id :"001" ,name :"爱坤" ,sex :"女" }, { id :"002" ,name :"树枝" ,sex :"男" }, { id :"003" ,name :"荔枝" ,sex :"女" }, { id :"004" ,name :"坤黑子" ,sex :"男" } ], filperson :[] }, watch :{ keyWord :{ immediate :true , handler (val ){ this .filperson = this .person .filter (p => { return p.name .indexOf (val) !== -1 }) } } } }) </script > </body > </html >
使用computed
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 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <meta name ="viewport" content ="width=device-width, initial-scale=1.0" > <title > Document</title > <script src ="../vue.js" > </script > </head > <body > <div id ="root" > <h1 > 人员</h1 > <input type ="text" placeholder ="请输入" v-model ="keyWord" > <ul > <li v-for ="(p,index) of filperson" :key ="index" > {{p.name}}---{{p.sex}} </li > </ul > </div > <script > const vm = new Vue ({ el :"#root" , data :{ keyWord :"" , person :[ { id :"001" ,name :"爱坤" ,sex :"女" }, { id :"002" ,name :"树枝" ,sex :"男" }, { id :"003" ,name :"荔枝" ,sex :"女" }, { id :"004" ,name :"坤黑子" ,sex :"男" } ], }, computed :{ filperson ( ){ return this .person .filter ((p )=> { return p.name .indexOf (this .keyWord ) !== -1 ; }); } } }) </script > </body > </html >
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 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <meta name ="viewport" content ="width=device-width, initial-scale=1.0" > <title > Document</title > <script src ="../vue.js" > </script > </head > <body > <div id ="root" > <h1 > 人员</h1 > <input type ="text" v-model ="keyWord" placeholder ="请输入" > <button @click ="sortType=2" > 按年龄升序</button > <button @click ="sortType=1" > 按年龄降序</button > <button @click ="sortType=0" > 原顺序</button > <ul > <li v-for ="(p,index) of filperson" :key ="index" > {{p.name}}---{{p.sex}}---{{p.age}} </li > </ul > </div > <script > const vm = new Vue ({ el :"#root" , data :{ keyWord :"" , sortType :0 , person :[ { id :"001" ,name :"爱坤" ,sex :"女" ,age :100 }, { id :"002" ,name :"树枝" ,sex :"男" ,age :34 }, { id :"003" ,name :"荔枝" ,sex :"女" ,age :88 }, { id :"004" ,name :"坤黑子" ,sex :"男" ,age :90 } ], }, computed :{ filperson ( ){ const arr=this .person .filter ((p )=> { return p.name .indexOf (this .keyWord ) !== -1 }) if (this .sortType ){ arr.sort ((p1,p2 )=> { return this .sortType === 1 ? p2.age -p1.age :p1.age -p2.age ; }) } return arr; } } }) </script > </body > </html >
3.vue监测数据的原理 vm实例的name和address其实都来自_data,_data中的数据来自于我们所传入的配置项data,也就是vm._data=data
加工我们传入的data数据(黄色框)
将我们data中的每一组key-value形成getter和setter
vm._data=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 let data = { name :"zd" , age :"33" } const obs = new Observer (data);let vm={};vm._data = data=obs; function Observer (obj ){ const keys= Object .keys (obj); keys.forEach ((k )=> { Object .defineProperty (this ,k,{ get ( ){ console .log ("获取" ); return obj[k]; }, set (value ){ console .log ("修改" ); obj[k]=value; } }); }) }
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.使用这些ÃPI:push()、pop()、shift()、unshift()、splice()、sort()、reverse()
2.Vue.set()或vm.$set()
特别注意:Vue.set()
和vm.$set()不能给vm 或vm的根数据对象 添加属性! 例如data等
4.Vue.set()
Vue.set( target, propertyName/index, value )
收集表单数据 若:<input type=”text”/>,则v-model收集的是value值,用户输入的就是value值。
若:<input type=”radio”/>,则v-model收集的是value值,且要给标签配置value值。
若:<input type=”checkbox”/> 1.没有配置input的value属性,那么收集的就是checked(勾选or未勾选,是布尔值)
2.配置epsevalue属性:
(1)v-model的初始值是非数组,那么收集的就是checked(勾选or 未勾选,是布尔值)
(2)v-model的初始值是数组,那么收集的的就是value组成的数组
备注:v-model的三个修饰符:
1azy:失去焦点再收集数据
number:输入字符串转为有效的数字
trim:输入首尾空格过滤
在v-model后面例如
v-model.lazy
v-model.number
v-model.trim
5.v-text和v-html
v-text:向其所在节点中渲染文本内容
v-text会替换掉节点里的内容
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <div id ="root" > <span v-text ="str" > </span > <span v-text ="str2" > </span > <span v-html ="str" > </span > <span v-html ="str2" > </span > <div v-fbind ="" > </div > </div > <script > new Vue ({ el :"#root" , data :{ str :"hello" , str2 :"<h3>hello</h3>" } }) </script >
v-html指令:
1.作用:向指定节点中渲染包含html结构的内容。
2.与插值语法的区别:
(1).v-html会替换掉节点中所有的内容,则不会。
(2).v-html可以识别html结构。
3.v-html有安全性问题
(1).在网站上动态渲染任意HTML是非常危险的,容易导致XSS攻击。
(2).一定要在可信的内容上使用v-htm2,永不要用在用户提交的内容上
6.自定义指令 自定义指令总结
一、定义语法:
(1)局部指令:
1 2 3 4 5 6 7 8 9 10 11 new Vue ({ directives :{指令名:配置对象} }) new Vue ({ directives{指令名:回调函数} })
(2).全局指令:
Vue.directive(指令名,配置对象)或 Vue.directive(指令名,回调函数)
二、配置对象中常用的3个回调:
(1)bind:指令与元素成功绑定时调用
(2)inserted:指令所在元素被插入页面时调用
(3)update:指令所在模板结构被重新解析时调用
三、备注:
1.指令定义时不加v-,但使用时要加v-:
2.指令名如果是多个单词,要使用kebab-case命名方式,不要用camelCase命名。
函数写法
1 2 3 4 5 fbind (element,binding ){ const input = document .createElement ("input" ); document .body .appendChild (input) input.focus (); }
对象写法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 fbind :{ bind (element,binding ){ element.value = binding.value ; }, inserted (element,binding ){ element.focus (); }, update (element,binding ){ element.value = binding.value ; } }
如果有-可以使用引号包裹
例:’input-number’:{
}
7.过滤器 使用管道符进行运算 |
1 2 3 4 5 6 Vue .filter ('my-filter' , function (value ) { }) getter,返回已注册的过滤器 var myFilter = Vue .filter ('my-filter' )
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 var vm = new Vue ({ el : '#app' , data : { msg : 'filter' }, methods : {}, filters : { dataFormat (msg,a ) { return msg+a; } } }); <!--html部分--> <div id ="app" > <p > {{ msg | dataFormat("你好")}}</p > </div >
全局过滤器
1 2 3 4 Vue .filter ('toDouble' , function (msg ) { return msg < 10 ? msg : "0" +msg })
5.Vue生命周期 mouted:挂载完成,Vue初始化成功,HTMl页面渲染成功。(发送请求到服务端,加载数据)
状态
阶段周期
beforeCreate
创建前
created
创建后
beforeMount
载入前
mounted
挂载完成
beforeUpdate
更新前
updated
更新后
beforeDestroy
销毁前
destroyed
销毁后
1 2 3 4 5 6 mounted ( ) { setInterval (()=> { this .opacity -= 0.01 ; if (this .opacity < 0 ) this .opacity =1 ; },16 ) },
常用的生命周期钩子:
1.mounted:发送ajax请求、启动定时器、绑定自定义事件、订阅消息等【初始化操作】。
2.beforeDestroy:清除定时器、解绑自定义事件、取消订阅消息等【收尾工作】。
关于销毁Vue实例
1.销毁后借助Vue开发者工具看不到任何信息。
2.销毁后自定义事件会失效,但原生DOM事件依然有效。
3.一般不会再beforeDestroy操作数据,因为即便操作数据,也不会再触发更新流程了。
6.组件 1.对组件的理解
实现应用中局部功能代码和资源的集合
非单文件组件
一个文件中包含n个组件
单文件组件
一个文件只包含一个组件
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 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <meta name ="viewport" content ="width=device-width, initial-scale=1.0" > <title > Document</title > <script src ="../vue.js" > </script > </head > <body > <div id ="root" > <school > </school > <student > </student > </div > <script > /* 组件1 */ const school ={ template:` <div > <h2 > 名称: {{name1 }} </h2 > <span > 地址: {{adress }} </span > </div > `, data(){ return { name1:"g", adress:"gl" } } } const student = { template:` <div > <h2 > 姓名: {{name2 }} </h2 > <span > 年龄: {{age }} </span > </div > `, data() { return { name2:"rc", age:30 } } } const vm = new Vue({ el:"#root", components:{ school:school, student:student } }) </script > </body > </html >
2.组件的注意点
1.关于组件名:
一个单词组成:
第一种写法(首字母小写):school第二种写法(首字母大写):School多个单词组成:
第一种写法(kebab-case命名):my-school
第+种写法(CamelCase命名):MySchool(需要Vue脚手架支持)
(1).组件名尽可能回避HTML中己有的元素名称,例如:h2、H2都不行。
(2).可以使用name配置项指定组件在开发者工具中呈现的名字。
备注:
2.关于组件标签:
第一种写法:<school></school>
第二种写法:<school/>
备注:不用使用脚手架时,<school/>会导致后续组件不能渲染。
3.一个简写方式:
const school = Vue.extend(options)可简写为:const school = options
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 const school = { data ( ) { return { name :'zs' } }, } const student = { data ( ) { return { name :"ls" } }, components :{ school :school } }
3.VueComponent Vue中使用组件的三大步骤
一、定义组件(创建组件)
二、注册组件
三、使用组件(写组件标签)
一、定义一个组件
使用Vue.extend(options)创建,其中options和new Vue(options)时传入的那个options几乎一样,但也有点区别;
区别如下:
1.el不要写— 最终所有的组件都要经过一个vm的管理,由vm中的e1决定服务哪个容器。
2.data必须写成函数—— 避免组件被复用时,数据存在引用关系。
备注:使用template可以配置组件结构。
二、注册组件
1.局部注册:靠new Vue的时候传入components选项
2.全局注册:靠Vue.component(‘组件名’,组件)
三、编写组件标签:
<school></school>
关于VueComponent:
1.school组件本质是一个名为VueComponent的构造函数,且不是程序员定义的,是Vue.extend生成的。
2.我们只需要写 或 ,Vue解析时会帮我们创建school组件的实例对象,即vue帮我们执行的:new VueComponent(options)。
3.特别注意:每次调用Vue.extend,返回的都是一个全新的VueComponent!!!!
4.关于this指向:
(1).组件配置中:
data函教、methods中的函数、watch中的函数、computed中的函数 它们的this均是【VueComponent实例对象】
(2).new Vue(options)配置中:
data函数、methods中的函数、watch中的函数、computed中的函数 它们的this均是【Vue实例对象】
5.VueComponent的实例对象,以后简称vc(也可称之为:组件实例对象) Vue的实例对象,以后简称vm 准各好一个容器–>
重要的内置关系:
VueComponent.prototype._proto _ ===Vue.prototype
4.单文件组件 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 <template> <div class="demo"> <h2>名称:{{name1}}</h2> <span @click="demo">地址:{{adress}}</span> </div> </template> <script> /* const school ={ data(){ return { name1:"g", adress:"gl" } } } export default school; */ export default { name:"school", data:{ data(){ return { name1:"g", adress:"gl" } } }, methods: { demo(){ alert("1"); } }, } </script> <style> .demo{ background-color: red; border:1px solid black; } </style>
5.创建vue脚手架
vue cli
npm i -g @vue/cli
在文件路径内输入
vue create richu
启动服务
npm run serve
7.Vue脚手架 1.ref ref属性
1.被用来给元素或子组件注册引用信息(id的替代者)
2.应用在html标签上获取的是真实DOM元素,应用在组件标签上是组件实例对象(vc)
3.使用方式:
打标识:<h1 ref=”xxx”>…..</h1>或<School ref=”xxx”></School>
获取:this.$refs.xxx
2.配置项props
功能:让组件接收外部传过来的数据
传递数据:
<Demo name=”xxx”/>
接收数据:
第一种方式(只接收):
props:[‘name’]
第二种方式(限制类型):
props:{
name:Number
第三种方式(限制类型、限制必要性、指定默认值):
props:{
name:{
type:String,//类型
required:true,//必要性
default:’老王’//默认值
备注:props是只读的,Vue底层会监测你对props的修改,如果进行了修改,就会发出警告
若业务需求确实需要修改,那么请复制props的内容到data中一份,然后去修改dat的数据
3.mixin 功能:可以把多个组件共用的配置提取成一个混入对象
使用方式:
第一步定义混合,例如:
data(){….},
methods:{….}
第二步使用混入,例如:
(1).全局混入:Vue.mixin(xxx)
(2).局部混入:mixins:[‘xxx’]
4.插件 功能:用于增强vue
本质:包含install方法的一个对象,instal1的第一个参数是Vue,第二个以后的参数是插件使用者传递的依据。
定义插件:
对象.install = function(Vue,options){
1.添加全局过滤器Vue.filter(….)
2.添加全局指令
Vue.directive(….)
3.配置全局混入(合)
Vue.mixin(….)
4.添加实例方法
Vue.prototype.$myMethod = function () {…]
Vue.prototype.$myProperty = xXxx
}
使用插件:Vue.use()
5.scoped
让样式在局部生效,防止冲突
写法:<style scoped>
6.组件-自定义事件 1.一种组件间通信的方式,适用于:子组件===>父组件
2.使用场景:A是父组件,B是子组件,B想给A传数据,那么就要在A中给B绑定自定义事件(事件的回调在A中)。
3.绑定自定义事件:
1.第一种方式,在父组件中:
2.第二种方式,在父组件中:
1 2 3 4 <Demo @richu="test"/> mounted(){ this.$refs.xxx.$on('richu',this.test) }
3.若想让自定义事件只能触发一次,可以使用once修饰符,或$once方法
4.触发自定义事件:
5.解绑自定义事件 this.$off(‘richu’)
6.组件上也可以绑定原生DOM事件,需要使用native修饰符。
7.注意:通过this.$refs.xxx.$on(‘richu’,回调)绑定自定义事件时,回调要么配置在methods中,要么用箭头函数,否则this指向会出问题!
APP.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 <template> <div id="app"> <HeaderTop @richu="getrichu"/> <BodyTop ref="richu2"/> </div> </template> <script> import HeaderTop from './components/HeaderTop.vue' import BodyTop from './components/BodyTop.vue'; export default { name: 'App', components: { HeaderTop, BodyTop }, methods: { getrichu(mes){ console.log("APP收到了",mes); }, getrichu2(tit){ console.log("APP收到了",tit); } }, mounted() { this.$refs.richu2.$on('richu',this.getrichu2); }, } </script> <style> #app { font-family: Avenir, Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; text-align: center; color: #2c3e50; margin-top: 60px; } </style>
Header.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 <template> <div> <h1>{{mes}}</h1> <button @click="Receiverichu">传mes</button> </div> </template> <script> export default { name:"HeaderTop", data(){ return { mes:"HL" } }, methods: { Receiverichu(){ this.$emit('richu',this.mes); } }, } </script> <style lang="less" scoped> </style>
Body.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 <template> <div> <h1>{{tit}}</h1> <button @click="Receiverichu2">传title</button> </div> </template> <script> export default { name:"BodyTop", data(){ return { tit:"Body" } }, methods: { Receiverichu2(){ this.$emit('richu',this.tit); } }, } </script> <style lang="less" scoped> </style>
解绑
this.$off(‘richu’);
this.$off([‘richu’,’richu2’]);
解绑所有事件
this.$off()
<BodyTop ref=”richu2” @click.native=”show”/>
使用原生的事件
7.全局事件总线
全局事件总线(GlobalEventBus)
任意组件间通信
1.一种组件间通信的方式,适用于任意组件间通信。
2.安装全局事件总线:
1 2 3 4 5 new Vue ({ beforeCreate ( ){ Vue .prototype .$bus = this }, })
3.使用事件总线:
1.接收数据:A组件想接收数据,则在A组件中给$bus绑定自定义事件,事件的回调留在A组件自身。
1 2 3 4 5 6 7 methods ( ){......}demo (data ){......}mounted ( ) { this .$bus .$on('xxxx' ,this .demo ) }
2.提供数据:[this.$bus.$emit(‘xxxx’,数据)]
4.最好在beforeDestroy钩子中,用$off去解绑当前组件所用到的事件。
8.消息订阅与发布
消息订阅与发布(pubsub)
1.一种组件间通信的方式,适用于任意组件间通信。
2.使用步骤:
1.安装pubsub:
npm i pubsub-js
2.引入:
import pubsub from ‘pubsub-js’
3.接收数据:A组件想接收数据,则在A组件中订阅消息,订阅的回调留在A组件自身。
methods(){
demo(data){……}
mounted(){
this.pid = pubsub.subscribe(‘xxx’,this.demo)//订阅消息
4.提供数据:
pubsub.publish(‘xxx’,数据)
5.最好在beforeDestroy钩子中,用PubSub.unsubscribe(pid)去<span style=”color:red”>取消订阅。</span>
9.动画效果 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 <template> <div> <button @click="isShow = !isShow">显示/隐藏</button> <transition> <div v-show="isShow" class="demo">RICHU</div> </transition> </div> </template> <script> export default { name:'ItemList1', data(){ return { isShow : true } } } </script> <style> .demo{ background-color: orange; } .v-enter-active{ animation: richu 1s linear; } .v-leave-active{ animation: richu 1s linear reverse; } @keyframes richu{ from{ transform: translateX(-100%); } to{ transform: translateX(0px); } } </style>
10.过渡效果 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 <template> <div> <button @click="isShow = !isShow">显示/隐藏</button> <transition> <div v-show="isShow" class="demo">RICHU</div> </transition> </div> </template> <script> export default { name:'ItemList1', data(){ return { isShow : true } } } </script> <style> .demo{ background-color: orange; } /* 进入的起点和离开的终点 */ .v-enter,.v-leave-to{ transform: translateX(-100%); } .v-enter-active,v-leave-active{ transition: 0.5s linear; } /* 进入的终点和离开的起点 */ .v-enter-to,.v-leave{ transform: translateX(0px); } </style>
1 2 3 <transition-group> </transition-group>
多组过渡效果
元素进入的样式
1.v-enter
2.v-enter-active
3.v-enter-to
元素离开的样式
1.v-leave
2.v-leave-active
3.v-leave-to
11.组件化编码流程:
(1)).拆分静态组件:组件要按照功能点拆分,命名不要与html元素冲突。
(2).实现动态组件:考虑好数据的存放位置,数据是一个组件在用,还是一些组件在用:
1).一个组件在用:放在组件自身即可。
2).一些组件在用:放在他们共同的父组件上(状态提升)。
(3).实现交互:从绑定事件开始。
2.props适用于:
(1).父组件==>子组件 通信
(2).子组件==>父组件通信(要求父先给子一个函数)
3.使用v-model时要切记:v-model绑定的值不能是props传过来的值,因为props是不可以修改的!
4.props传过来的若是对象类型的值,修改对象中的属性时Vue不会报错,但不推荐这样做。
8Vue2技术2 1.$nextTick
在dom下一次更新后调用回调
2.配置代理
方式一
1 proxy :'http://localhost:9000'
方式二
1 2 3 4 5 6 7 8 proxy :{ '/api' :{ target :"http://localhost:9000" , pathRewrite :{ '^/api' :'' }, ws :true , changeOrigin :false } }
3.vue-resource
npm i vue-resource
发ajax请求
4.slot插槽 1 2 3 <CategoryRi :datas="datas"> <img src=""> </CategoryRi>
1 <slot>设置默认值,当没有传递具体结构的时候会出现</slot>
具名插槽
如果在封装组件时需要预留多个插槽节点,则需要为每个 <slot>
插槽指定具体的 name 名称。
这种带有具体名称的插槽叫做“具名插槽”。
注意:没有指定 name 名称的插槽,会有隐含的名称叫做 “default”。
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 <template> <div> <h3>Com 组件</h3> <p>----------插槽开始----------</p> <!-- 为组件的使用者预留的区域 --> <slot name="header"></slot> <!-- 没有指定 name,该插槽的 name 值默认为 default --> <slot></slot> <!-- <slot name="default"></slot> --> <slot name="bottom"></slot> <p>----------插槽结束----------</p> </div> </template> <script> export default { name: 'Com' } </script> <template v-slot:插槽的name> 需要向插槽中放入的内容 </template>
4.1 作用域插槽的使用 在封装组件的过程中,可以为预留的<slot> 插槽绑定 props 数据,这种带有 props 数据的<slot> 叫做“作用域插槽”。
作用域插槽,要显示的数据已经在组件中,以什么样的样式显示数据(用什么标签和标签的样式),可以由组件的使用者进行指定。
为作用域插槽指定插槽内的元素必须使用 <template>
标签。
作用域插槽也能取名
5.Vuex 1.创建 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 import vuex from 'vuex' import Vue from 'vue' const actions = {}const mutations = {}const state = {}Vue .use (vuex);export default new vuex.Store ({ actions :actions, mutations :mutations, state :state })
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 38 39 40 41 42 43 44 45 46 47 48 import vuex from 'vuex' import Vue from 'vue' const actions = { addodd (context,value ){ if (context.state .sum % 2 == 1 ){ context.commit ('Add' ,value); } }, addwait (context,value ){ setTimeout (()=> { context.commit ('Add' ,value) },500 ) } } const mutations = { Add (state,value){ state.sum +=value; }, Del (state,value){ state.sum -=value; } } const state = { sum :0 } Vue .use (vuex);export default new vuex.Store ({ actions :actions, mutations :mutations, state :state })
组件读取vuex中的数据:$store.state.sum
组件中修改vuex中的数据:$store.dispatch('actions中的方法名',数据)
或$store.commit(mutations中的方法名,数据)
3.getter配置项 1 2 3 4 5 6 const getters ={ muladd ( ){ return state.sum * 10 ; } }
4.mapState与mapGetters 1 2 3 4 5 get ( ){ return this .$store .state .title ; }, ...mapState ({get :'title' }) ...mapState (['title' ])
1 2 3 4 muladd ( ){ return this .$store .getters .muladd }, ...mapGetters (['muladd' ]),
5.mapActions与mapMutations
mapMutations
生成与mapMutations对话的方法
1 2 3 4 5 6 7 8 increate ( ){ this .$store .commit ('Add' ,this .n ) }, decreate ( ){ this .$store .commit ('Del' ,this .n ) }, ...mapMutations ({increate :'Add' ,decreate :'Del' }), ...mapMutations (['Add' ,'Del' ]),
mapActions
生成与mapActions对话的方法
1 2 3 4 5 6 7 8 increateodd ( ){ this .$store .dispatch ('addodd' ,this .n ) }, sumwait ( ){ this .$store .dispatch ('addwait' ,this .n ) }, ...mapActions ({increateodd :'addodd' ,sumwait :'addwait' }), ...mapActions (['addodd' ,'addwait' ])
可以实现多组件共享数据
5.vuex模块化 1 2 3 4 5 6 7 8 9 10 export default new vuex.Store ({ actions :actions, mutations :mutations, state :state, getters :getters modules :{ a :a, b :b } })
6.模块化加命名空间 1.开启命名空间后,组件中读取state数据:
1 2 3 4 5 this .$store .state .personAbout .list ...mapState ('countAbout' ,['sum' ,'school' ,'subject' ]),
2.开启命名空间后,组件中读取getters数据:
1 2 3 4 this .$store .getters ['personAbout/firstPersonName' ]...mapGetters ('countAbout' ,['bigSum' ])
3.开启命名空间后,组件中调用dispatch
1 2 3 4 this .$store .dispatch ('personAbout/addPersonWang' ,person)...mapActions ('countAbout' ,{incrementOdd :'jia0dd' ,incrementWait :'jiaWait' })
4.开启命名空间后,组件中调用commit
1 2 3 4 this .$store .commit ('personAbout/ADD_PERSON' ,person)...mapMutations ('countAbout' ,{increment :'JIA' ,decrement :'JIAN' }),
9.路由 1.路由的基本使用
npm i vue-router 使用createRouter和createWebHistory
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 import {createRouter,createWebHistory} from 'vue-router' import About from '../views/About' import Home from '../views/Home' const router = new createRouter ({ history :createWebHistory (), routes : [ { path : '/about' , component : About , }, { path : '/home' , component : Home } ] }) export default router
2.路由的使用 1.实现切换(active-class可配置高亮样式)
<router-link active-class=”active” to=”/about”>About</router-link>
2.指定展示位置
<router-diew></router-view>
几个注意点
1.路由组件通常存放在pages文件夹,一般组件通常存放在components文件夹。
2.通过切换,“隐藏”了的路由组件,默认是被销毁掉的,需要的时候再去挂载。
3.每个组件都有自己的$route属性,里面存储着自己的路由信息。
4.整个应用只有一个router,可以通过组件的srouter 属性获取到。
3.路由的query 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 <template > <div > <ul class ="list" > <li v-for ="item of data" :key ="item.id" > <router-link class ="link" :to ="{ path:'/home/message/mes', query:{ id:item.id, title:item.mes } }" > {{item.mes}}</router-link > </li > </ul > <hr > <router-view > </router-view > </div > </template > <script > export default { name :'HomeChild1' , data ( ) { return { data :[ {id :1 ,mes :"消息1" }, {id :2 ,mes :"消息2" }, {id :3 ,mes :"消息3" } ] } }, } </script > <style scoped > .list { margin-left :80px ; } .link { color : orange; text-decoration : none; background-color : skyblue; } </style >
接收参数
{{$route.query.id}}
4.命名路由 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 routes :[ { path :'/about' , component :AboutBody }, { path :'/home' , component :HomeBody , children :[ { path :'news' , component :HomeChild }, { path :'message' , component :HomeChild1 , children :[ { name :'richu' , path :'mes' , component :HomeMessage } ] } ] } ]
使用
:to="{
name:''
path:'/home/message/mes',
query:{
id:item.id,
title:item.mes
}
}"
5.params参数的使用 1.声明接收
1 2 3 4 5 6 7 children :[ { name :'richu' , path :'mes/:id/:title' , component :HomeMessage } ]
2.传递
1 2 3 4 5 6 7 <li v-for ="item of data" :key ="item.id" > <router-link class ="link" :to ="`/home/message/mes/${item.id}/${item.mes}`" > {{item.mes}} </router-link > </li >
3.接收
1 2 <li > 编号{{$route.params.id}}</li > <li > 标题{{$route.params.title}}</li >
6.props的使用 1 2 3 4 5 6 7 8 9 10 11 12 7. 路由的props配置作用:让路由组件更方便的收到参数 name :'xiangqing' ,path :'detail/:id' ,component :Detail ,props (route ){return {id :route.query .id ,title :route.query .title
<router-link>的replace属性
1.作用:控制路由跳转时操作浏览器历史记录的模式
2.浏览器的历史记录有两种写入方式:分别为 push和replace,默认为push
3.如何开启replace 模式:
push是追加历史记录,
replace 是替换当前记录[路由跳转时候<router-link replace>News</router-link>]
7.编程式路由导航 1.作用:不借助router-link实现路由跳转,让跳转更加灵活 2.具体编码:
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 (3 );
8.缓存路由组件
让不展示的路由组件保持挂载,不被销毁
具体编码
1 2 3 <keep-alive include ="news" > <router-view > </router-view > </keep-alive >
include里面写模块名,用于保存指定的模块
9.新生命周期钩子
作用:路由组件独有的,用于捕获路由组件的激活状态
activated 路由组件被激活时触发
deactivated 路由组件失活时触发
10.路由守卫 1.前置路由守卫 1 2 3 4 5 6 7 8 9 route.beforeEach ((from ,to,next )=> { if (to.meta .isAuth ){ alert ("1" ); next (); }else { next (); } })
2.后置路由守卫 1 2 3 4 5 route.afterEach ((from ,to )=> { console .log (to); document .title =from .meta .title ; })
3.独享路由守卫 1 2 3 4 5 6 7 8 { path :'news' , component :HomeChild , meta :{title :"新闻" }, beforeEnter : (from ,to,next )=> { } },
独享路由守卫只有前置路由守卫没有后置路由守卫
4.组件内路由守卫 1 2 3 4 5 6 7 8 beforeRouteEnter (to, from , next) { }, beforeRouteLeave (to, from , next) { }
11.模式 在route实例化的文件对象内编写
1 2 mode :'history' ,mode :'hash'
1.对于一个url来说,什么是hash值?——#及其后面的内容就是hash值.
2.hash值不会包含在HTTP请求中,即:hash值不会带给服务器。
hash模式:
1.地址中永远带着#号,不美观。
2.若以后将地址通过第三方手机app分享,若app校验严格,则地址会被标记为不合法。
3.兼容性较好。
history模式:
1.地址干净,美观。
2.兼容性和hash模式相比略差。
3.应用部署上线时需要后端人员支持,解决刷新页面服务端404的问题。
打包npm run build
12.element-ui
npm i element-ui -S
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 import { ref, onMounted, onUnmounted } from 'vue' export function useMouse ( ) { const x = ref (0 ) const y = ref (0 ) function update (event ) { x.value = event.pageX y.value = event.pageY } onMounted (() => window .addEventListener ('mousemove' , update)) onUnmounted (() => window .removeEventListener ('mousemove' , update)) return { x, y } }