前言
Vue
(读音 /vjuː/,类似于 view)
是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是,Vue
被设计为可以自底向上逐层应用。Vue
的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。另一方面,当与现代化的工具链以及各种支持类库结合使用时,Vue
也完全能够为复杂的单页应用提供驱动。
Vue的引用方式包括两种:
本地引用
在官网下载 [vue.js](https://cn.vuejs.org/v2/guide/installation.html) 文件,并在 <code>.html</code> 文件中的 <code>head</code> 标签中引入:
1
| <script type="text/javascript" src="vue.js"></script>
|
CDN引用
1
| <script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
|
Vue2.x官方文档:Vue.js
一、初识Vue
想让Vue工作,就必须创建一个Vue实例,且要传入一个配置对象:
1 2 3 4 5 6 7 8
| <script> new Vue({ el: '#root', data:{ name:'qzmvc1' } }) </script>
|
其中,el
与data
均有另一种写法:
1 2 3 4 5 6 7 8 9
| <script> new Vue({ data(){ return { name: 'qzmvc1', } } }).$mount('root') </script>
|
二、模板语法
Vue模板语法有2大类:
插值语法
功能:用于解析标签体内容。
写法:{{xxx}}
,其中xxx是js表达式,且可以直接读取到data中的所有属性。
指令语法
功能:用于解析标签(包括:标签属性、标签体内容、绑定事件…..)
举例:v-bind:href=“xxx”
或
简写为:href=“xxx”
,xxx同样要写js表达式,且可以直接读取到data中的所有属性。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| <body> <div id="root"> <h1>插值语法</h1> <h3>hello, {{name}}!</h3> <h1>指令语法</h1> <a :href="school.url">个人博客</a> </div> <script> new Vue({ el: '#root', data:{ name:'qzmvc1', school:{ url:'https://qzmvc1.top/', } } }) </script> </body>
|
三、Vue指令
3.1 v-bind
v-bind
(单向数据绑定):数据只能从data流向页面
1 2 3 4 5
| <input type="text" v-bind:value="name">
<input type="text" :value="name">
|
3.2 v-model
v-model
(双向数据绑定):数据不仅能从data流向页面,还可以从页面流向data。
v-model
一般都应用在表单类元素上(如input,
select等),且默认收集的是标签中的value值。`
1 2 3 4 5
| <input type="text" v-model:value="name">
<input type="text" v-model="name">
|
3.2.1 v-model.trim
trim
:输入首尾空格过滤
1 2
| <input type="text" v-model.trim="name">
|
3.2.2 v-model.number
number
:输入字符串转为有效的数字
1 2
| <input type="number" v-model.number="age">
|
3.2.3 v-model.lazy
lazy
:失去焦点再收集数据
1 2
| <textarea v-model.lazy="other"></textarea>
|
3.3 v-on
使用v-on:xxx
或 @xxx
绑定事件,其中xxx
是事件名,事件的回调需要配置在vue实例中的methods对象中,最终会出现在vm上
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| <body> <div id="root"> <button v-on:click="showInfo1">点我提示信息1</button> <button @click="showInfo1">点我提示信息1</button> </div> <script> new Vue({ el: '#root', methods:{ showInfo1(event){ console.log(event) }, } }) </script> </body>
|
另外,绑定的事件也可以传递参数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| <body> <div id="root"> <button v-on:click="showInfo2(value1, value2)">点我提示信息2</button>
<button @click="showInfo2(value1, value2)">点我提示信息2</button> </div> <script> new Vue({ el: '#root', methods:{ showInfo2(value1, value2, $event){ console.log(value1, value2, $event) }, } }) </script> </body>
|
3.4 v-show
适用于:切换频率较高的场景
特点:不展示的DOM元素未被移除,仅仅是使用样式隐藏掉,即
display:none
,还存在DOM中
1 2
| <h1 v-show="false">hello, {{name}}!</h1> <h2 v-show="1 === 1">欢迎来到{{name}}</h2>
|
3.5 v-if / v-else-if / v-else
适用于:切换频率较低的场景
特点:不展示的DOM元素直接被移除
注意:v-if
可以和v-else-if
、v-else
一起使用,但要求结构不能被“打断”
1 2 3
| <div v-if="n === 1">Angular</div> <div v-else-if="n === 2">React</div> <div v-else-if="n === 3">Vue</div>
|
3.6 v-for
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
| <body> <div id="root"> <h2>人员列表(遍历数组)</h2> <ul> <li v-for="(p,index) in persons" :key="index"> {{p.name}}-{{p.age}} </li> </ul> <h2>汽车信息(遍历对象)</h2> <ul> <li v-for="(value,k) in car" :key="k"> {{k}}-{{value}} </li> </ul> <h2>测试遍历字符串(用得少)</h2> <ul> <li v-for="(char,index) in str" :key="index"> {{char}}-{{index}} </li> </ul> <h2>测试遍历指定次数(用得少)</h2> <ul> <li v-for="(number,index) in 5" :key="index"> {{index}}-{{number}} </li> </ul> </div> <script type="text/javascript"> new Vue({ el:'#root', data:{ persons:[ {id:'001',name:'张三',age:18}, {id:'002',name:'李四',age:19}, {id:'003',name:'王五',age:20} ], car:{ name:'奥迪A8', price:'70万', color:'黑色' }, str:'hello' } }) </script> </body>
|
3.6.1 Demo1: 列表过滤
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
| <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>列表过滤</title> <script type="text/javascript" src="./vue.js"></script> </head> <body> <div id="root"> <h2>人员列表</h2> <input type="text" placeholder="请输入人名" v-model="keyword "> <ul> <li v-for="(p, index) in filpersons" :key="index"> {{p.name}}-{{p.age}}-{{p.sex }} </li> </ul> </div> <script> new Vue({ el: '#root', data:{ keyword:'', persons:[ {id:'001', name:'马冬梅', age: 18, sex:"女"}, {id:'002', name:'周冬雨', age: 19, sex:"女"}, {id:'003', name:'周杰伦', age: 20, sex:"男"}, {id:'004', name:'温兆伦', age: 20, sex:"男"}, ], filpersons: [] }, watch:{ keyword:{ immediate: true, handler(newVal){ this.filpersons = this.persons.filter((p)=>{ return p.name.indexOf(newVal) != -1 }) } } }, }) </script> </body> </html>
|
3.6.2 Demo2: 列表排序
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
| <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>列表排序</title> <script type="text/javascript" src="./vue.js"></script> </head> <body> <div id="root"> <h2>人员列表</h2> <input type="text" placeholder="请输入人名" v-model="keyword "> <button @click="sorttype=2">年龄升序</button> <button @click="sorttype=1">年龄降序</button> <button @click="sorttype=0">原顺序</button> <ul> <li v-for="(p, index) in filpersons" :key="p.id"> {{p.name}}-{{p.age}}-{{p.sex }} </li> </ul> </div> <script> new Vue({ el: '#root', data:{ keyword: '', sorttype: 0, persons:[ {id:'001', name:'马冬梅', age: 18, sex:"女"}, {id:'002', name:'周冬雨', age: 19, sex:"女"}, {id:'003', name:'周杰伦', age: 17, sex:"男"}, {id:'004', name:'温兆伦', age: 20, sex:"男"}, ], }, computed:{ filpersons(){ const arr = this.persons.filter((p)=>{ return p.name.indexOf(this.keyword) != -1 }) if(this.sorttype){ arr.sort((a, b)=>{ return this.sorttype == 1 ? (b.age-a.age) : (a.age-b.age) }) } return arr } } }) </script> </body> </html>
|
3.7 v-text
作用:向其所在的节点中渲染文本内容。
与插值语法的区别:v-text
会替换掉节点中的内容,{{xx}}
则不会
1 2 3 4 5
| <div v-text="str"></div>
<div><h3>你好啊!</h3></div>
|
3.8 v-html
作用:向指定节点中渲染包含html结构的内容。
与插值语法的区别:
v-html
有安全性问题!!!!在网站上动态渲染任意HTML是非常危险的,容易导致XSS攻击
1 2 3 4 5 6 7
| <div v-html="str"></div>
<div> <h3>你好啊!</h3> </div>
|
3.9 v-once
v-once
所在节点在初次动态渲染后,就视为静态内容了
- 以后数据的改变不会引起
v-once
所在结构的更新,可以用于优化性能
1 2 3 4 5
| <div id="root"> <h2 v-once>初始化的n值是:{{n}}</h2> <h2>当前的n值是:{{n}}</h2> <button @click="n++">点我n+1</button> </div>
|
3.10 v-pre
- 跳过其所在节点的编译过程,源代码什么样,网页DOM里面什么样
- 可利用它跳过没有使用指令语法、没有使用插值语法的节点,会加快编译
四、事件修饰符
4.1 prevent
prevent:阻止默认事件
以下案例点击超链接后不会跳转页面。
1
| <a href="https://qzmvc1.top" @click.prevent="showInfo1">点我跳转链接</a>
|
4.2 stop
stop:阻止事件冒泡
若无stop修饰符,点击按钮后,事件会冒泡执行,即先执行button按钮触发的事件,再执行div触发的事件。
添加stop修饰符,button触发的事件执行后,div触发的事件不执行。
1 2 3
| <div class="demo" @click="showInfo1"> <button @click.stop="showInfo1">点我事件冒泡</button> </div>
|
4.3 once
once:事件只触发一次
以下案例点击button按钮事件触发一次后,再点击button按钮无任何反应。
1
| <button @click.once="showInfo1">点我事件只触发一次</button>
|
注:事件修饰符可以连续写
五、键盘事件
1 2 3 4 5
| <input type="text" placeholder="按下回车提示输入" @keyup.enter="showInfo1">
<input type="text" placeholder="按下回车提示输入" @keydown.enter="showInfo1">
|
5.1 常用按键
对于Vue未提供别名的按键,可以使用 $event.key
获取按键的原始key值(在console查看输出),再去绑定,但注意要转为kebab-case
(短横线命名)
1 2 3 4 5 6 7 8 9
| 回车 => enter 删除 => delete 退出 => esc 空格 => space 换行 => tab (特殊,必须配合keydown去使用) 上 => up 下 => down 左 => left 右 => right
|
5.2 系统修饰键
ctrl
, shift
, alt
,
command
1 2
| keyup: 按下上述键后,按下其他键,再松开其他键,事件触发(也可以指定组合键,如@keyup.ctrl.y) keydown: 直接触发
|
六、计算属性 computed
- 定义:要用的属性不存在,通过已有属性计算得来。
- 原理:底层借助了
Objcet.defineproperty
方法提供的
getter
和 setter
。
- get函数什么时候执行?
- 初次读取时会执行一次,读取完毕后会将计算属性存入缓存中。
- 当依赖的数据发生改变时会被再次调用。
- 优势:与methods实现相比,内部有缓存机制(复用),效率更高,调试方便。
- 备注:
- 计算属性最终会出现在vm上,直接读取使用即可。
- 如果计算属性要被修改,那必须写set函数去响应修改,且set中要引起计算时依赖的数据发生改变。
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
| <body> <div id="root"> 姓:<input type="text" v-model:value="name1"><br> 名:<input type="text" v-model:value="name2"><br> 姓名:{{fullName}} </div> <script> const vm = new Vue({ el: '#root', data:{ name1: "", name2: "", }, computed:{ fullName:{ get(){ return this.name1+'-'+this.name2 }, set(value){ const arr = value.split('-') this.name1 = arr[0] this.name2 = arr[1] } } } computed:{ fullName(){ return this.name1+'-'+this.name2 } } }) </script> </body>
|
七、监视属性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
| <body> <div id="root"> <h1>今天天气有点{{info}}</h1> <button @click=changeWeather>切换天气</button> </div> <script> new Vue({ el: '#root', data:{ isHot:true, }, methods: { changeWeather(){ this.isHot = !this.isHot } }, computed:{ info(){ return this.isHot? "炎热" : "凉爽" } }, watch:{ isHot:{ handler(newvalue, oldvalue){ console.log("isHot被修改了", newvalue, oldvalue) } }, isHot(newvalue, oldvalue){ console.log("isHot被修改了", newvalue, oldvalue) } }, }) </script> </body>
|
初始化时让handler调用一下
1 2 3 4 5 6
| watch:{ immediate:true, handler(newvalue, oldvalue){ ... } }
|
7.2 deep
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
| <body> <div id="root"> <h1>a的值是:{{numbers.a}}</h1> <button @click=numbers.a++>点我a++</button> <hr/> <h1>b的值是:{{numbers.b}}</h1> <button @click=numbers.b++>点我b++</button> </div> <script> new Vue({ el: '#root', data:{ numbers:{ a: 1, b: 1, } }, watch:{ 'numbers.a':{ handler(newvalue, oldvalue){ console.log("a被修改了", newvalue, oldvalue) } }, numbers:{ deep: true, handler(){ console.log("numbers被修改了") } }
} }) </script> </body>
|
7.3 计算属性与监视属性区别
computed和watch之间的区别:
- computed能完成的功能,watch都可以完成。
- watch能完成的功能,computed不一定能完成,例如:watch可以进行异步操作。
两个重要的小原则:
- 所被 Vue 管理的函数,最好写成普通函数,这样this的指向才是 vm 或
组件实例对象。
- 所有不被 Vue
所管理的函数(如定时器的回调函数),最好写成箭头函数。
八、绑定样式
8.1 :class
1 2 3 4 5 6 7 8 9 10 11
| <style> .basic{ ... }, .a{ ... }, .b{ ... } </style>
|
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
| <body> <div id="root"> <div class="basic" :class="mood"></div> <div class="basic" :class="classArr"></div> <div class="basic" :class="classObj"></div> </div> <script> new Vue({ el: '#root', data:{ mood: "a", classArr: ['a', 'b'], classObj:{ a: false, b: false, } } }) </script> </body>
|
8.2 :style
1 2
| :style="{fontSize: xxx}" /* 其中xxx是动态值 */ :style="[a,b]" /* 其中a、b是样式对象 */
|
九、收集表单数据
<input type="text"/>
,v-model收集的是value值,而用户输入的就是value值
1 2
| 账号:<input type="text" v-model.trim="name"> 密码:<input type="text" v-model="password">
|
9.2 radio
<input type="radio"/>
,v-model收集的是value值,所以要给标签配置value值。
1 2
| 男<input type="radio" name='sex' value="male" v-model="sex"> 女<input type="radio" name='sex' value="female" v-model="sex">
|
9.3 checkbox
<input type="checkbox"/>
- 没有配置input的value属性,那么收集的就是checked(勾选 or
未勾选,是布尔值)
- 配置了input的value属性:
- v-model的初始值是非数组,那么收集的就是checked(勾选 or
未勾选,是布尔值)
- v-model的初始值是数组,那么收集的的就是value组成的数组
1 2 3 4
| 唱<input type="checkbox" value="sing" v-model="hobby"> 跳<input type="checkbox" value="dance" v-model="hobby"> rap<input type="checkbox" value="rap" v-model="hobby">
|
9.4 select
1 2 3 4 5 6 7
| <select v-model="city"> <option value="">请选择信息</option> <option value="beijing">北京</option> <option value="shanghai">上海</option> <option value="wuhan">武汉</option> <option value="chengdu">成都</option> </select>
|
9.5 textarea
1
| <textarea v-model.lazy="other"></textarea>
|
十、过滤器
过滤器:
- 定义:对要显示的数据进行特定格式化后再显示(适用于一些简单逻辑的处理)
- 语法:
- 注册过滤器:
Vue.filter(name,callback)
或
new Vue{filters:{}}
- 使用过滤器:
{{ xxx | 过滤器名}}
或
v-bind:属性 = "xxx | 过滤器名"
- 备注:
- 过滤器也可以接收额外参数、多个过滤器也可以串联
- 并没有改变原本的数据, 是产生新的对应的数据
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
| <body> <div id="root"> <h3>现在是:{{time | timeformat('YYYY_MM_DD')}}</h3> </div> <script> Vue.filter('func1', function(value){ ... }) new Vue({ el: '#root', data:{ time:142812864812, }, filters:{ timeformat(value, str="YYYY年MM月DD日"){ ... } } }) </script> </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 27 28 29 30
| new Vue({ el:'root', beforeCreate() { console.log('beforeCreate') }, created() { console.log('created') }, beforeMount() { console.log('beforeMount') }, mounted() { console.log('mounted') }, beforeUpdate() { console.log('beforeUpdate') }, updated() { console.log('updated') }, beforeDestroy() { console.log('beforeDestroy') }, destroyed() { console.log('destroyed') }, })
|
关于销毁Vue实例:
- 销毁后借助Vue开发者工具看不到任何信息
- 销毁后自定义事件会失效,但原生DOM事件依然有效
- 一般不会在
beforeDestroy
操作数据,因为即便操作数据,也不会再触发更新流程了
十二、非单文件组件
- 组件本质是一个名为
VueComponent
的构造函数,且不是程序员定义的,是Vue.extend
生成的
- 我们只需要写
<school/>
或<school></school>
,Vue解析时会帮我们创建school组件的实例对象,即Vue帮我们执行的:new VueComponent(options)
- 特别注意:每次调用
Vue.extend
,返回的都是一个全新的VueComponent
!!!!
12.1 创建组件
- 组件定义时,一定不要写el配置项,因为最终所有的组件都要被一个vm管理,由vm决定服务于哪个容器
- data必须写成函数,避免组件被复用时,数据存在引用关系。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
|
const school = Vue.extend({ template: ` <div> <h2>学校的名字是:{{schoolname}}</h2> <h2>学校的地址是:{{address}}</h2> </div> `, data(){ return { schoolname: 'UESTC', address: 'chengdu' } } })
const app = { ... }
|
12.2 注册组件
关于组件名:
- 一个单词组成:
- 第一种写法(首字母小写):school
- 第二种写法(首字母大写):School
- 多个单词组成:
- 第一种写法(kebab-case命名):my-school
- 第二种写法(CamelCase命名):MySchool (需要Vue脚手架支持)
1 2 3 4 5 6 7 8 9 10 11
| Vue.component(componentName, componentvalue) Vue.component('xuexiao', school)
new Vue({ el: '#root', components:{ xuexiao: school, } })
|
12.3 使用组件
关于组件标签:
- 第一种写法:
<school></school>
- 第二种写法:
<school/>
备注:不用使用脚手架时,<school/>
会导致后续组件不能渲染
1 2 3
| <div id="root"> <xuexiao></xuexiao> </div>
|
12.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 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
| <body> <div id="root"> <app></app> </div> <script> const hello = Vue.extend({ template:`<h2>欢迎来到{{place}}</h2>`, data(){ return { place: "sichuan", } } }) const student = Vue.extend({ template: ` <div> <h2>学生的名字是:{{studentname}}</h2> <h2>学生的年龄是:{{age}}</h2> </div> `, data(){ return { studentname: 'qzmvc1', age: 22 } } }) const school = Vue.extend({ template: ` <div> <h2>学校的名字是:{{schoolname}}</h2> <h2>学校的地址是:{{address}}</h2> <xuesheng></xuesheng> </div> `, data(){ return { schoolname: 'UESTC', address: 'chengdu' } }, components:{ xuesheng: student } }) const app = { template: ` <div> <hello></hello> <xuexiao></xuexiao> </div> `, components:{ hello: hello, xuexiao: school } }
new Vue({ el: '#root', components:{ app: app, } }) </script> </body>
|
12.5 一个重要的内置关系
- 一个重要的内置关系:
VueComponent.prototype.__proto__ === Vue.prototype
- 为什么要有这个关系:让组件实例对象(vc)可以访问到
Vue原型上的属性、方法