动力节点Vue笔记第二章 Vue核心技术
20/100
发布文章
assdfgdfgjhtdo
未选择任何文件
new
2 Vue核心技术
2.1 事件处理
2.1.1 事件处理的核心语法
- 指令的语法格式:<标签 v-指令名:参数=”表达式”></标签>
- 事件绑定的语法格式:v-on:事件名。例如鼠标单击事件的绑定使用v-on:click。
- 绑定的回调函数需要在Vue实例中使用methods进行注册。methods可以配置多个回调函数,采用逗号隔开。
- 绑定回调函数时,如果回调函数没有参数,( )可以省略。
- 每一个回调函数都可以接收一个事件对象event。
- 如果回调函数有参数,并且还需要获取事件对象,可以使用$event进行占位。
- v-on:click可以简写为@click。简写的语法格式:@事件名
- 回调函数中的this是vm。如果回调函数是箭头函数的话,this是window对象,因为箭头函数没有自己的this,它的this是继承过来的,默认这个this是箭头函数所在的宿主对象。这个宿主对象其实就是它的父级作用域。而对象又不能构成单独的作用域,所以这个父级作用域是全局作用域,也就是window。
- 回调函数并没有在vm对象上,为什么通过vm可以直接调用函数呢?尝试手写Vue框架。
- 可以在函数中改变data中的数据,例如:this.counter++,这样会联动页面上产生动态效果。
2.1.2 事件修饰符
.stop - 调用 event.stopPropagation()。
*<div** @click="san"**>**
3. **<div** @click.stop="er"**>**
4. **<button** @click="yi"**>**{{name}}**</button>**
5. **</div>**
6. **</div>**
.prevent - 调用 event.preventDefault()。
**<a** href="http://www.bjpowernode.com" @click.prevent="yi"**>**
3. {{name}}
4. **</a>**
.capture - 添加事件侦听器时使用 capture 模式。
**<div** @click.capture="san"**>**
3. **<div** @click.capture="er"**>**
4. **<button** @click="yi"**>**{{name}}**</button>**
5. **</div>**
6. **</div>**
注意:只有添加了capture修饰符的元素才会采用捕获模式。(或者说带有capture修饰符的优先触发)
.self - 只当事件是从侦听器绑定的元素本身触发时才触发回调。
**<div** @click="san"**>**
1. **<div** @click.self="er"**>**
2. **<button** @click="yi"**>**{{name}}**</button>**
3. **</div>**
4. **</div>**
.once - 只触发一次回调。
5.
**<button** @click.once="yi"**>**
6. {{name}}
7. **</button>**
.passive - (2.3.0) 以 { passive: true } 模式添加侦听器
1.无需等待,直接继续(立即)执行事件默认行为。(对wheel事件有效果)
2. .passive 和 .prevent 修饰符不能共存。
2.1.3 按键修饰符
常用的按键修饰符包括:
.enter
.tab (只能配合keydown使用)
.delete (捕获“删除”和“退格”键)
.esc
.space
.up
.down
.left
.right
可以直接将 KeyboardEvent.key 暴露的任意有效按键名转换为 kebab-case 来作为修饰符。
<input type=”text” @keyup.page-down=”getInfo”>
可以通过全局 config.keyCodes 对象自定义按键修饰符别名
Vue.config.keyCodes.huiche = 13
2.1.4 系统修饰键
系统修饰键包括4个
.ctrl
.alt
.shift
.meta
系统修饰键在使用时应注意:
-
只有当系统修饰键和其他键组合使用,并且组合键释放时,才会触发keyup事件。
-
只要按下系统修饰键,就会触发keydown事件。
小技巧 <input type=”text” @keyup.ctrl.c=”getInfo”/>
2.2 计算属性
- 案例:用户输入信息,然后翻转用户输入的字符串。
- 插值语法可以实现,但是有三个问题
- 代码可读性差
- 代码不可复用
- 代码难以维护
- 可以使用methods方式实现,存在1个问题
- 效率低,即使数据没有发生变化,但每一次仍然会调用method。
- 使用计算属性可以解决以上问题。
- 什么是计算属性?
data中的是属性。用data的属性经过计算得出的全新的属性就是计算属性。
- 计算属性的使用
**<div** id="app"**>**
3. **<h1>**{{msg}}**</h1>**
4. 输入的信息:**<input** type="text" v-model="info"**><br>**
5. 反转的信息:{{reversedInfo}} **<br>**
6. 反转的信息:{{reversedInfo}} **<br>**
7. 反转的信息:{{reversedInfo}} **<br>**
8. 反转的信息:{{reversedInfo}} **<br>**
9. **</div>**
10. **<script>**
11. const vm = new Vue({
12. el : '#app',
13. data : {
14. msg : '计算属性-反转字符串案例',
15. info : ''
16. },
17. computed : {
18. reversedInfo:{
19. get(){
20. console.log('getter被调用了');
21. return this.info.split('').reverse().join('')
22. },
23. set(val){
24. //this.reversedInfo = val // 不能这样做,这样会导致无限递归
25. this.info = val.split('').reverse().join('')
26. }
27. }
28. }
29. })
30. **</script>**
- 计算属性需要使用:computed
- 计算属性通过vm. d a t a 是无法访问的。计算属性不能通过 v m . data 是无法访问的。计算属性不能通过vm. data是无法访问的。计算属性不能通过vm.data访问。
- 计算属性的getter/setter方法中的this是vm。
- 计算属性的getter方法的调用时机:
- 第一个时机:初次访问该属性。
- 第二个时机:计算属性所依赖的数据发生变化时。
- 计算属性的setter方法的调用时机:
- 当计算属性被修改时。(在setter方法中通常是修改属性,因为只有当属性值变化时,计算属性的值就会联动更新。注意:计算属性的值被修改并不会联动更新属性的值。)
- 计算属性没有真正的值,每一次都是依赖data属性计算出来的。
- 计算属性的getter和setter方法不能使用箭头函数,因为箭头函数的this不是vm。而是window。
- 计算属性的简写形式
只考虑读取,不考虑修改时,可以启用计算属性的简写形式。
computed : {
2. reversedInfo(){
3. console.log('getter被调用了');
4. **return** **this**.info.split('').reverse().join('')
5. }
6. }
2.3 侦听属性的变化
- 侦听属性的变化其实就是监视某个属性的变化。当被监视的属性一旦发生改变时,执行某段代码。
- 监视属性变化时需要使用watch配置项。
使用watch实现:比较数字大小的案例
**<div** id="app"**>**
2. **<h1>**{{msg}}**</h1>**
3. 数值1:**<input** type="text" v-model="number1"**><br>**
4. 数值2:**<input** type="text" v-model="number2"**><br>**
5. 比较大小:{{compareResult}}
6. **</div>**
7. **<script>**
8. const vm = new Vue({
9. el : '#app',
10. data : {
11. msg : '侦听属性的变化',
12. number1 : 1,
13. number2 : 1,
14. compareResult : ''
15. },
16. watch : {
17. number1 : {
18. immediate : true,
19. handler(newVal, oldVal){
20. let result = newVal - this.number2
21. if(result **>** 0){
22. this.compareResult = newVal + '**>**' + this.number2
23. }else if(result **<** **0**){
24. this.compareResult = newVal + '**<**' + this.number2
25. }else{
26. this.compareResult = newVal + '=' + this.number2
27. }
28. }
29. },
30. number2 : {
31. immediate : true,
32. handler(newVal, oldVal){
33. let result = this.number1 - newVal
34. if(result **>** 0){
35. this.compareResult = this.number1 + '**>**' + newVal
36. }else if(result **<** **0**){
37. this.compareResult = this.number1 + '**<**' + newVal
38. }else{
39. this.compareResult = this.number1 + '=' + newVal
40. }
41. }
42. }
43. }
44. })
45. **</script>**
运行效果:
- 如何深度监视:
- 监视多级结构中某个属性的变化,写法是:’a.b.c’ : {}。注意单引号哦。
- 监视多级结构中所有属性的变化,可以通过添加深度监视来完成:deep : true
- 如何后期添加监视:
- 调用API:vm.$watch(‘number1’, {})
- watch的简写:
- 简写的前提:当不需要配置immediate和deep时,可以简写。
- 如何简写?
- watch:{ number1(newVal,oldVal){}, number2(newVal, oldVal){} }
- 后期添加的监视如何简写?
- vm.$watch(‘number1’, function(newVal, oldVal){})
- computed和watch如何选择?
- 以上比较大小的案例可以用computed完成,并且比watch还要简单。所以要遵守一个原则:computed和watch都能够完成的,优先选择computed。
- 如果要开启异步任务,只能选择watch。因为computed依靠return。watch不需要依赖return。
- 关于函数的写法,写普通函数还是箭头函数?
- 不管写普通函数还是箭头函数,目标是一致的,都是为了让this和vm相等。
- 所有Vue管理的函数,建议写成普通函数。
- 所有不属于Vue管理的函数,例如setTimeout的回调函数、Promise的回调函数、AJAX的回调函数,建议使用箭头函数。
2.4 class与style绑定
数据绑定的一个常见需求场景是操纵元素的 CSS class 列表和内联样式。因为 class 和 style 都是 attribute,我们可以和其他 attribute 一样使用 v-bind 将它们和动态的字符串绑定。但是,在处理比较复杂的绑定时,通过拼接生成字符串是麻烦且易出错的。因此,Vue 专门为 class 和 style 的 v-bind 用法提供了特殊的功能增强。除了字符串外,表达式的值也可以是对象或数组。
2.4.1 class绑定
2.4.1.1 绑定字符串
适用于样式的名字不确定,需要动态指定。
. <!DOCTYPE html**>**
2. **<html** lang="en"**>**
3. **<head>**
4. **<meta** charset="UTF-8"**>**
5. **<title>**class绑定字符串形式**</title>**
6. **<script** src="../js/vue.js"**></script>**
7. **<style>**
8. .static{
9. border: 1px solid black;
10. background-color: beige;
11. }
12. .big{
13. width: 200px;
14. height: 200px;
15. }
16. .small{
17. width: 100px;
18. height: 100px;
19. }
20. **</style>**
21. **</head>**
22. **<body>**
23. **<div** id="app"**>**
24. **<h1>**{{msg}}**</h1>**
25. **<div** class="static" :class="c1"**></div>**
26. **</div>**
27. **<script>**
28. const vm = new Vue({
29. el : '#app',
30. data : {
31. msg : 'class绑定字符串形式',
32. c1 : 'small'
33. }
34. })
35. **</script>**
36. **</body>**
37. **</html>**
运行效果:
使用vue开发者工具修改c1的small为big:
通过测试可以看到样式完成了动态的切换。
2.4.1.2 绑定数组
适用于绑定的样式名字不确定,并且个数也不确定。
<!DOCTYPE html**>**
2. **<html** lang="en"**>**
3. **<head>**
4. **<meta** charset="UTF-8"**>**
5. **<title>**class绑定数组形式**</title>**
6. **<script** src="../js/vue.js"**></script>**
7. **<style>**
8. .static{
9. border: 1px solid black;
10. }
11. .active{
12. background-color: green;
13. }
14. .text-danger{
15. color: red;
16. }
17. **</style>**
18. **</head>**
19. **<body>**
20. **<div** id="app"**>**
21. **<h1>**{{msg}}**</h1>**
22. **<div** class="static" :class="['active','text-danger']"**>**数组形式**</div>**
23. **<br><br>**
24. **<div** class="static" :class="[activeClass,errorClass]"**>**数组形式**</div>**
25. **<br><br>**
26. **<div** class="static" :class="classArray"**>**数组形式**</div>**
27. **</div>**
28. **<script>**
29. const vm = new Vue({
30. el : '#app',
31. data : {
32. msg : 'class绑定数组形式',
33. activeClass : 'active',
34. errorClass : 'text-danger',
35. classArray : ['active', 'text-danger']
36. }
37. })
38. **</script>**
39. **</body>**
40. **</html>**
运行效果:
使用vue开发者工具删除数组中的一个样式:
2.4.1.3 绑定对象
适用于样式名字和个数都确定,但是要动态决定用或者不用。
. <!DOCTYPE html**>**
2. **<html** lang="en"**>**
3. **<head>**
4. **<meta** charset="UTF-8"**>**
5. **<title>**class绑定对象形式**</title>**
6. **<script** src="../js/vue.js"**></script>**
7. **<style>**
8. .static{
9. border: 1px solid black;
10. }
11. .active{
12. background-color: green;
13. }
14. .text-danger{
15. color: red;
16. }
17. **</style>**
18. **</head>**
19. **<body>**
20. **<div** id="app"**>**
21. **<h1>**{{msg}}**</h1>**
22. **<div** class="static" :class="{active : true, 'text-danger' : true}"**>**对象形式**</div>**
23. **<br><br>**
24. **<div** class="static" :class="classObject"**>**对象形式**</div>**
25. **</div>**
26. **<script>**
27. const vm = new Vue({
28. el : '#app',
29. data : {
30. msg : 'class绑定对象形式',
31. classObject : {
32. active : true,
33. 'text-danger' : false
34. }
35. }
36. })
37. **</script>**
38. **</body>**
39. **</html>**
运行效果:
使用vue开发者工具修改text-danger为true:
2.4.2 style绑定
2.4.2.1 绑定对象
**<div** id="app"**>**
2. **<h1>**{{msg}}**</h1>**
3. <!-- 静态写法 -->
4. **<div** class="static" style="font-size: 20px;"**>**对象形式**</div><br><br>**
5. <!-- 动态写法1 -->
6. **<div** class="static" :style="{fontSize: 40 + 'px'}"**>**对象形式**</div><br><br>**
7. <!-- 动态写法2 -->
8. **<div** class="static" :style="styleObject"**>**对象形式**</div><br><br>**
9. **</div>**
10. **<script>**
11. const vm = new Vue({
12. el : '#app',
13. data : {
14. msg : 'style绑定对象形式',
15. styleObject : {
16. fontSize : '40px'
17. }
18. }
19. })
20. **</script>**
2.4.2.2 绑定数组
**<div** id="app"**>**
2. **<h1>**{{msg}}**</h1>**
3. <!-- 静态写法 -->
4. **<div** class="static" style="font-size: 40px; color: red;"**>**数组形式**</div><br><br>**
5. <!-- 动态写法1 -->
6. **<div** class="static" :style="[{fontSize:'40px'},{color:'red'}]"**>**数组形式**</div><br><br>**
7. <!-- 动态写法2 -->
8. **<div** class="static" :style="styleArray"**>**对象形式**</div><br><br>**
9. **</div>**
10. **<script>**
11. const vm = new Vue({
12. el : '#app',
13. data : {
14. msg : 'style绑定对象形式',
15. styleArray : [
16. {fontSize:'40px'},
17. {color:'red'}
18. ]
19. }
20. })
21. **</script>**
2.5 条件渲染
2.5.1 v-if
指令用于条件性地渲染一块内容。这块内容只会在指令的表达式返回true时才被渲染
**<div** id="app"**>**
2. **<h1>**{{msg}}**</h1>**
3. 温度:**<input** type="number" v-model="temprature"**><br>**
4. 天气:
5. **<span** v-if="temprature <= 10"**>**寒冷**</span>**
6. **<span** v-if="temprature > 10 && temprature <= 25"**>**凉爽**</span>**
7. **<span** v-if="temprature > 25"**>**炎热**</span>**
8. **</div>**
9. **<script>**
10. const vm = new Vue({
11. el : '#app',
12. data : {
13. msg : '条件渲染',
14. temprature : 10
15. }
16. })
17. **</script>**
运行效果:
2.5.2 v-else-if、v-else
顾名思义,v-else-if 提供的是相应于 v-if 的“else if 区块”。它可以连续多次重复使用。
一个使用 v-else-if 的元素必须紧跟在一个 v-if 或一个 v-else-if元素后面。
你也可以使用 v-else 为 v-if 添加一个“else 区块”,当然,v-else元素也是必须紧跟在一个 v-if 或一个 v-else-if元素后面。
**<div** id="app"**>**
2. **<h1>**{{msg}}**</h1>**
3. 温度:**<input** type="number" v-model="temprature"**><br>**
4. 天气:
5. **<span** v-if="temprature <= 10"**>**寒冷**</span>**
6. **<span** v-else-if="temprature <= 25"**>**凉爽**</span>**
7. **<span** v-else**>**炎热**</span>**
8. **</div>**
9. **<script>**
10. const vm = new Vue({
11. el : '#app',
12. data : {
13. msg : '条件渲染',
14. temprature : 10
15. }
16. })
17. **</script>**
2.5.3 与v-if
因为 v-if 是一个指令,他必须依附于某个元素。但如果我们想要切换不止一个元素呢?在这种情况下我们可以在一个 元素上使用 v-if,这只是一个不可见的包装器元素,最后渲染的结果并不会包含个 元素。v-else 和 v-else-if 也可以在 上使用。
**<div** id="app"**>**
2. **<h1>**{{msg}}**</h1>**
3. 温度:**<input** type="number" v-model="temprature"**><br>**
4. 天气:
5. **<template** v-if="temprature <= 10"**>**
6. **<span>**寒冷**</span>**
7. **</template>**
8. **<template** v-else-if="temprature <= 25"**>**
9. **<span>**凉爽**</span>**
10. **</template>**
11. **<template** v-else**>**
12. **<span>**炎热**</span>**
13. **</template>**
14. **</div>**
15. **<script>**
16. const vm = new Vue({
17. el : '#app',
18. data : {
19. msg : '条件渲染',
20. temprature : 10
21. }
22. })
23. **</script>**
2.5.4 v-show
另一个可以用来按条件显示一个元素的指令是 v-show。其用法基本一样:
**<div** id="app"**>**
2. **<h1>**{{msg}}**</h1>**
3. 温度:**<input** type="number" v-model="temprature"**><br>**
4. 天气:
5. **<span** v-show="temprature <= 10"**>**寒冷**</span>**
6. **<span** v-show="temprature > 10 && temprature <= 25"**>**凉爽**</span>**
7. **<span** v-show="temprature > 25"**>**炎热**</span>**
8. **</div>**
9. **<script>**
10. const vm = new Vue({
11. el : '#app',
12. data : {
13. msg : '条件渲染',
14. temprature : 10
15. }
16. })
17. **</script>**
不同之处在于 v-show 会在 DOM 渲染中保留该元素;v-show 仅切换了该元素上名为 display 的 CSS 属性。
v-show 不支持在 元素上使用,也不能和 v-else 搭配使用。
2.5.5 v-if VS v-show
v-if 是“真实的”按条件渲染,因为它确保了在切换时,条件区块内的事件监听器和子组件都会被销毁与重建
v-if 也是惰性的:如果在初次渲染时条件值为 false,则不会做任何事。条件区块只有当条件首次变为 true 时才被渲染。
相比之下,v-show 简单许多,元素无论初始条件如何,始终会被渲染,只有 CSS display属性会被切换。
总的来说,v-if 有更高的切换开销,而 v-show 有更高的初始渲染开销。因此,如果需要频繁切换,则使用 v-show 较好;如果在运行时绑定条件很少改变,则 v-if 会更合适。
2.6 列表渲染
语法格式:v-for指令。该指令用在被遍历的标签上。
v-for="(element, index) in elements" :key="element.id"
或者
v-for="(element, index) of elements" :key="element.id"
2.6.1 遍历数组、对象、字符串、指定次数
**遍历数组**
2. **<div** id="app"**>**
3. **<h1>**{{msg}}**</h1>**
4. **<h2>**遍历数组**</h2>**
5. **<ul>**
6. **<li** v-for="(product, index) in products" :key="product.id"**>**
7. 商品名称:{{product.name}},单价:{{product.price}}元/千克,下标:{{index}}
8. **</li>**
9. **</ul>**
10. **</div>**
11. **<script>**
12. const vm = new Vue({
13. el : '#app',
14. data : {
15. msg : '列表渲染',
16. products : [
17. {id:'111',name:'西瓜',price:20},
18. {id:'222',name:'苹果',price:10},
19. {id:'333',name:'香蕉',price:30}
20. ]
21. }
22. })
23. **</script>**
运行效果:
- 遍历对象
*<div** id="app"**>**
3. **<h1>**{{msg}}**</h1>**
4. **<h2>**遍历对象**</h2>**
5. **<ul>**
6. **<li** v-for="(propertyValue, propertyName) of dog" :key="propertyName"**>**
7. {{propertyName}}:{{propertyValue}}
8. **</li>**
9. **</ul>**
10. **</div>**
11. **<script>**
12. const vm = new Vue({
13. el : '#app',
14. data : {
15. msg : '列表渲染',
16. dog : {
17. name : '拉布拉多',
18. age : 3,
19. gender : '雄性'
20. }
21. }
22. })
23. **</script>**
运行结果:
- 遍历字符串
**<div** id="app"**>**
3. **<h1>**{{msg}}**</h1>**
4. **<h2>**遍历字符串**</h2>**
5. **<ul>**
6. **<li** v-for="char,index of str" :key="index"**>**
7. {{index}}:{{char}}
8. **</li>**
9. **</ul>**
10. **</div>**
11. **<script>**
12. const vm = new Vue({
13. el : '#app',
14. data : {
15. msg : '列表渲染',
16. str : '动力节点'
17. }
18. })
19. **</script>**
运行结果:
- 遍历指定次数
**<div** id="app"**>**
3. **<h1>**{{msg}}**</h1>**
4. **<h2>**遍历指定次数**</h2>**
5. **<ul>**
6. **<li** v-for="number,index of 10" :key="index"**>**
7. 下标:{{index}},数字:{{number}}
8. **</li>**
9. **</ul>**
10. **</div>**
11. **<script>**
12. const vm = new Vue({
13. el : '#app',
14. data : {
15. msg : '列表渲染'
16. }
17. })
18. **</script>**
运行结果:
2.6.2 虚拟dom和diff算法
所谓的虚拟dom就是内存当中的dom对象。vue为了提高渲染的效率,只有真正改变的dom元素才会重新渲染。
2.6.3 v-for的key的作用以及实现原理
- 用index作为key
<!DOCTYPE html**>**
3. **<html** lang="en"**>**
4. **<head>**
5. **<meta** charset="UTF-8"**>**
6. **<title>**key的原理**</title>**
7. **<script** src="../js/vue.js"**></script>**
8. **</head>**
9. **<body>**
10. **<div** id="app"**>**
11. **<h1>**{{msg}}**</h1>**
12. **<button** @click="addFirst"**>**在数组第一个位置添加tom**</button>**
13. **<button** @click="addLast"**>**在数组最后位置添加vue**</button>**
14. **<table>**
15. **<tr>**
16. **<th>**序号**</th>**
17. **<th>**姓名**</th>**
18. **<th>**邮箱**</th>**
19. **<th>**选择**</th>**
20. **</tr>**
21. **<tr** v-for="(vip,index) of vips" :key="index"**>**
22. **<td>**{{index + 1}}**</td>**
23. **<td>**{{vip.name}}**</td>**
24. **<td>**{{vip.email}}**</td>**
25. **<td><input** type="checkbox"**></td>**
26. **</tr>**
27. **</table>**
28. **</div>**
29. **<script>**
30. const vm = new Vue({
31. el : '#app',
32. data : {
33. msg : 'key原理(虚拟dom与diff算法)',
34. vips : [
35. {id:'100',name:'jack',email:'jack@123.com'},
36. {id:'200',name:'lucy',email:'lucy@123.com'},
37. {id:'300',name:'james',email:'james@123.com'}
38. ]
39. },
40. methods : {
41. addFirst(){
42. this.vips.unshift({id:'400',name:'tom',email:'tom@123.com'})
43. },
44. addLast(){
45. this.vips.push({id:'500',name:'vue',email:'vue@123.com'})
46. }
47. }
48. })
49. **</script>**
50. **</body>**
51. **</html>**
运行效果:
全部选中:
添加tom:
可以看到错乱了。思考这是为什么?
- 用vip.id作为key
运行和测试结果正常,没有出现错乱。为什么?
- key的作用
key存在于虚拟dom元素中,代表该虚拟dom元素的唯一标识(身份证号)。
- diff算法是如何比较的?
新的虚拟dom和旧的虚拟dom比较时,先拿key进行比较:
- 如果key相同:则继续比较子元素:
- 子元素不同:直接将新的虚拟dom元素渲染到页面生成新的真实dom元素。
- 子元素相同:直接复用之前的真实dom。
- 如果key不同:直接将新的虚拟dom元素渲染到页面生成新的真实dom元素。
- index作为key存在两个问题
- 效率较低。
- 对数组的非末尾元素进行增删时,容易错乱。
- index作为key和vip.id作为key对比
当index作为key时:
当vip.id作为key时:
2.7 列表过滤
使用watch和computed分别进行实现:
<!DOCTYPE html**>**
2. **<html** lang="en"**>**
3. **<head>**
4. **<meta** charset="UTF-8"**>**
5. **<title>**列表过滤**</title>**
6. **<script** src="../js/vue.js"**></script>**
7. **<style>**
8. table,tr,th,td{
9. border: 1px solid blue;
10. }
11. **</style>**
12. **</head>**
13. **<body>**
14. **<div** id="app"**>**
15. **<h1>**{{msg}}**</h1>**
16. **<input** type="text" placeholder="请输入搜索关键词" v-model="keyword"**>**
17. **<table>**
18. **<tr>**
19. **<th>**序号**</th>**
20. **<th>**姓名**</th>**
21. **<th>**邮箱**</th>**
22. **</tr>**
23. **<tr** v-for="(vip,index) of filterVips" :key="vip.id"**>**
24. **<td>**{{index+1}}**</td>**
25. **<td>**{{vip.name}}**</td>**
26. **<td>**{{vip.email}}**</td>**
27. **</tr>**
28. **</table>**
29. **</div>**
30. **<script>**
31. const vm = new Vue({
32. el : '#app',
33. data : {
34. keyword : '',
35. msg : '列表过滤',
36. vips : [
37. {id:'100',name:'jack',email:'jack@123.com'},
38. {id:'200',name:'lucy',email:'lucy@123.com'},
39. {id:'300',name:'james',email:'james@123.com'}
40. ],
41. //filterVips : []
42. },
43. /* watch : {
44. keyword : {
45. immediate : true,
46. handler(newValue, oldValue){
47. this.filterVips = this.vips.filter((v) =**>** {
48. return v.name.indexOf(newValue) **>**= 0
49. })
50. }
51. }
52. }, */
53. computed : {
54. filterVips(){
55. return this.vips.filter((v) =**>** {
56. return v.name.indexOf(this.keyword) **>**= 0
57. })
58. }
59. }
60. })
61. **</script>**
62. **</body>**
63. **</html>**
2.8 列表排序
<!DOCTYPE html**>**
2. **<html** lang="en"**>**
3. **<head>**
4. **<meta** charset="UTF-8"**>**
5. **<meta** http-equiv="X-UA-Compatible" content="IE=edge"**>**
6. **<meta** name="viewport" content="width=device-width, initial-scale=1.0"**>**
7. **<title>**列表排序**</title>**
8. **<script** src="../js/vue.js"**></script>**
9. **<style>**
10. table,tr,td,th{
11. border:1px solid black;
12. }
13. **</style>**
14. **</head>**
15. **<body>**
16. **<div** id="app"**>**
17. **<h1>**{{msg}}**</h1>**
18. **<input** type="text" placeholder="输入关键字搜索" v-model="keyword"**><br>**
19. **<button** @click="type = 1"**>**按照名字升序**</button><br>**
20. **<button** @click="type = 2"**>**按照名字降序**</button><br>**
21. **<button** @click="type = 0"**>**按照名字原始顺序**</button><br>**
22. **<table>**
23. **<tr>**
24. **<th>**序号**</th>**
25. **<th>**姓名**</th>**
26. **<th>**邮箱**</th>**
27. **<th>**操作**</th>**
28. **</tr>**
29. **<tr** v-for="(vip, index) in filterVips" :key="vip.id"**>**
30. **<td>**{{index+1}}**</td>**
31. **<td>**{{vip.name}}**</td>**
32. **<td>**{{vip.email}}**</td>**
33. **<td><input** type="checkbox"**></td>**
34. **</tr>**
35. **</table>**
36. **</div>**
37. **<script>**
38. const vm = new Vue({
39. el : '#app',
40. data : {
41. msg : '列表排序',
42. vips : [
43. {id:'100',name:'jack',email:'jack@123.com'},
44. {id:'200',name:'lucy',email:'lucy@123.com'},
45. {id:'300',name:'james',email:'james@123.com'},
46. {id:'400',name:'lilei',email:'lilei@123.com'},
47. ],
48. keyword : '',
49. type : 0
50. },
51. computed : {
52. filterVips(){
53. // 筛选
54. let arr = this.vips.filter((vip) =**>** {
55. return vip.name.indexOf(this.keyword) **>**= 0
56. })
57. // 根据排序类型进行排序
58. if(this.type){
59. arr.sort((v1, v2) =**>** {
60. console.log('@')
61. return this.type == 1 ? v1.name.localeCompare(v2.name) : v2.name.localeCompare(v1.name)
62. })
63. }
64. // 返回
65. return arr
66. }
67. }
68. })
69. **</script>**
70. **</body>**
71. </html>
2.9 收集表单数据
<!DOCTYPE html**>**
2. **<html** lang="en"**>**
3. **<head>**
4. **<meta** charset="UTF-8"**>**
5. **<title>**收集表单数据**</title>**
6. **<script** src="../js/vue.js"**></script>**
7. **</head>**
8. **<body>**
9. **<div** id="app"**>**
10. **<h1>**{{msg}}**</h1>**
11. **<form** @submit.prevent="send"**>**
12. **<label** for="username"**>**用户名:**</label>**
13. **<input** id="username" type="text" v-model.trim="user.username"**><br><br>**
14. 密码:**<input** type="password" v-model="user.password"**><br><br>**
15. 年龄:**<input** type="number" v-model.number="user.age"**><br><br>**
16. 性别:
17. 男**<input** type="radio" name="gender" v-model="user.gender" value="1"**>**
18. 女**<input** type="radio" name="gender" v-model="user.gender" value="0"**><br><br>**
19. 爱好:
20. 运动**<input** type="checkbox" name="interest" value="sport" v-model="user.interest"**>**
21. 旅游**<input** type="checkbox" name="interest" value="travel" v-model="user.interest"**>**
22. 唱歌**<input** type="checkbox" name="interest" value="sing" v-model="user.interest"**><br><br>**
23. 学历:
24. **<select** v-model="user.grade"**>**
25. **<option** value=""**>**请选择学历**</option>**
26. **<option** value="zk"**>**专科**</option>**
27. **<option** value="bk"**>**本科**</option>**
28. **<option** value="ss"**>**硕士**</option>**
29. **</select><br><br>**
30. 简介:**<textarea** cols="30" rows="10" v-model.lazy="user.introduce"**></textarea><br><br>**
31. **<input** type="checkbox" v-model="user.isAgree"**>**阅读并接受协议**<br><br>**
32. **<button>**注册**</button>**
33. **</form>**
34. **</div>**
35. **<script>**
36. const vm = new Vue({
37. el : '#app',
38. data : {
39. msg : '收集表单数据',
40. user : {
41. username : '',
42. password : '',
43. age : '',
44. gender : '0',
45. interest : ['sport'],
46. grade : 'ss',
47. introduce : '',
48. isAgree : ''
49. }
50. },
51. methods : {
52. send(){
53. console.log(JSON.stringify(this.user))
54. }
55. }
56. })
57. **</script>**
58. **</body>**
59. **</html>**
页面展示效果:
运行结果:
2.10 过滤器
过滤器filters适用于简单的逻辑处理,例如:对一些数据进行格式化显示。他的功能完全可以使用methods,computed来实现。过滤器可以进行全局配置,也可以进行局部配置:
- 全局配置:在构建任何Vue实例之前使用Vue.filter(‘过滤器名称’, callback)进行配置。
- 局部配置:在构建Vue实例的配置项中使用filters进行局部配置。
过滤器可以用在两个地方:插值语法和v-bind指令中。
多个过滤器可以串联:{{msg | filterA | filterB | filterC}}
过滤器也可以接收额外的参数,但过滤器的第一个参数永远接收的都是前一个过滤器的返回值。
2.11 Vue的其它指令
2.11.1 v-text
将内容填充到标签体当中,并且是以覆盖的形式填充,而且填充的内容中即使存在HTML标签也只是会当做一个普通的字符串处理,不会解析。功能等同于原生JS中的innerText。
2.11.2 v-html
将内容填充到标签体当中,并且是以覆盖的形式填充,而且将填充的内容当做HTML代码解析。功能等同于原生JS中的innerHTML。
v-html不要用到用户提交的内容上。可能会导致XSS攻击。XSS攻击通常指的是通过利用网页开发时留下的漏洞,通过巧妙的方法注入恶意指令代码到网页,使用户加载并执行攻击者恶意制造的网页程序。这些恶意网页程序通常是JavaScript。
例如:用户在留言中恶意植入以下信息:
其他用户上当了:如果点击了以上的留言,就会将cookie发送给恶意的服务器。
2.11.3 v-cloak
v-cloak配置css样式来解决胡子的闪现问题。
v-cloak指令使用在标签当中,当Vue实例接管之后会删除这个指令。
这是一段CSS样式:当前页面中所有带有v-cloak属性的标签都隐藏起来。
[v-cloak] {
display : none;
}
2.11.4 v-once
初次接触指令的时候已经学过了。只渲染一次。之后将被视为静态内容。
2.11.5 v-pre
使用该指令可以提高编译速度。带有该指令的标签将不会被编译。可以在没有Vue语法规则的标签中使用可以提高效率。不要将它用在带有指令语法以及插值语法的标签中。
2.12 vue的自定义指令
函数式:
directives : {
‘text-reverse’ : function(element, binding){
// element 是真实dom对象(可以通过 element instanceof HTMLElement 判断)
// binding 是绑定的对象
element.innerText = binding.value.split(‘’).reverse().join(‘’)
}
}
函数调用时机:
第一时机:模板初次解析时(元素与指令初次绑定)。
第二时机:模板重新解析时。
对象式:可以使用对象式完成更加细致的功能。
directives : {
‘bind-parent’ : {
// 元素与指令初次绑定时自动调用。
bind(element, binding){},
// 元素已经被插入页面后自动调用。
inserted(element, binding){},
// 模板重新解析时被自动调用。
update(element, binding){}
}
}
自定义指令的函数中的this是window。
以上是局部指令,全局指令怎么定义:
对象式:
Vue.directive(‘bind-parent’, {
bind(element, binding){},
inserted(element, binding){},
update(element, binding){}
})
函数式:
Vue.directive(‘text-reverse’, function(element, binding){})
2.13 响应式与数据劫持
- 什么是响应式?
修改data后,页面自动改变/刷新。这就是响应式。就像我们在使用excel的时候,修改一个单元格中的数据,其它单元格的数据会联动更新,这也是响应式。
- Vue的响应式是如何实现的?
数据劫持:Vue底层使用了Object.defineProperty,配置了setter方法,当去修改属性值时setter方法则被自动调用,setter方法中不仅修改了属性值,而且还做了其他的事情,例如:重新渲染页面。setter方法就像半路劫持一样,所以称为数据劫持。
- Vue会给data中所有的属性,以及属性中的属性,都会添加响应式。
- 后期添加的属性,不会有响应式,怎么处理?
- Vue.set(目标对象, ‘属性名’, 值)
- vm.$set(目标对象, ‘属性名’, 值)
- Vue没有给数组下标0,1,2,3…添加响应式,怎么处理?
- 调用Vue提供的7个API:
push()
pop()
reverse()
splice()
shift()
unshift()
sort()
或者使用:
Vue.set(数组对象, ‘index’, 值)
vm.$set(数组对象, ‘index’, 值)
2.14 Vue的生命周期
2.14.1 什么是生命周期
所谓的生命周期是指:一个事物从出生到最终的死亡,整个经历的过程叫做生命周期。
例如人的生命周期:
- 出生:打疫苗
- 3岁了:上幼儿园
- 6岁了:上小学
- 12岁了:上初中
- …
- 55岁了:退休
- …
- 临终:遗嘱
- 死亡:火化
可以看到,在这个生命线上有很多不同的时间节点,在不同的时间节点上去做不同的事儿。
Vue的生命周期指的是:vm对象从创建到最终销毁的整个过程。
- 虚拟DOM在内存中就绪时:去调用一个a函数
- 虚拟DOM转换成真实DOM渲染到页面时:去调用一个b函数
- Vue的data发生改变时:去调用一个c函数
- …
- Vue实例被销毁时:去调用一个x函数
在生命线上的函数叫做钩子函数,这些函数是不需要程序员手动调用的,由Vue自动调用,程序员只需要按照自己的需求写上,到了那个时间点自动就会执行。
2.14.2 掌握Vue的生命周期有什么用
研究Vue的生命周期主要是研究:在不同的时刻Vue做了哪些不同的事儿。
例如:在vm被销毁之前,我需要将绑定到元素上的自定义事件全部解绑,那么这个解绑的代码就需要找一个地方写一下,写到哪里呢?你可以写到beforeDestroy()这个函数中,这个函数会被Vue自动调用,而且是在vm对象销毁前被自动调用。像这种在不同时刻被自动调用的函数称为钩子函数。每一个钩子函数都有对应的调用时间节点。
换句话说,研究Vue的生命周期主要研究的核心是:在哪个时刻调用了哪个钩子函数。
2.14.3 Vue生命周期的4个阶段8个钩子
Vue的生命周期可以被划分为4个阶段:初始阶段、挂载阶段、更新阶段、销毁阶段。
每个阶段会调用两个钩子函数。两个钩子函数名的特点:beforeXxx()、xxxed()。
8个生命周期钩子函数分别是:
- 初始阶段
- beforeCreate() 创建前
- created() 创建后
- 挂载阶段
- beforeMount() 挂载前
- mounted() 挂载后
- 更新阶段
- beforeUpdate() 更新前
- updated() 更新后
- 销毁阶段
- beforeDestroy() 销毁前
- destroyed() 销毁后
8个钩子函数写在哪里?直接写在Vue构造函数的options对象当中。
Vue官方的生命周期图:
翻译后的生命周期图:
2.14.4 初始阶段做了什么事儿
做了这么几件事:
- 创建Vue实例vm(此时Vue实例已经完成了创建,这是生命的起点)
- 初始化事件对象和生命周期(接产大夫正在给他洗澡)
- 调用beforeCreate()钩子函数(此时还无法通过vm去访问data对象的属性)
- 初始化数据代理和数据监测
- 调用created()钩子函数(此时数据代理和数据监测创建完毕,已经可以通过vm访问data对象的属性)
- 编译模板语句生成虚拟DOM(此时虚拟DOM已经生成,但页面上还没有渲染)
该阶段适合做什么?
beforeCreate:可以在此时加一些loading效果。
created:结束loading效果。也可以在此时发送一些网络请求,获取数据。也可以在这里添加定时器。
2.14.5 挂载阶段做了什么事儿
做了这么几件事:
- 调用beforeMount()钩子函数(此时页面还未渲染,真实DOM还未生成)
- 给vm追加 e l 属性,用它来代替” e l ”, el属性,用它来代替”el”, el属性,用它来代替”el”,el代表了真实的DOM元素(此时真实DOM生成,页面渲染完成)
- 调用mounted()钩子函数
该阶段适合做什么?
mounted:可以操作页面的DOM元素了。
2.14.6 更新阶段做了什么事儿
做了这么几件事:
- data发生变化(这是该阶段开始的标志)
- 调用beforeUpdate()钩子函数(此时只是内存中的数据发生变化,页面还未更新)
- 虚拟DOM重新渲染和修补
- 调用updated()钩子函数(此时页面已更新)
该阶段适合做什么?
beforeUpdate:适合在更新之前访问现有的 DOM,比如手动移除已添加的事件监听器。
updated:页面更新后,如果想对数据做统一处理,可以在这里完成。
2.14.7 销毁阶段做了什么事儿
做了这么几件事:
- vm.$destroy()方法被调用(这是该阶段开始的标志)
- 调用beforeDestroy()钩子函数(此时Vue实例还在。虽然vm上的监视器、vm上的子组件、vm上的自定义事件监听器还在,但是它们都已经不能用了。此时修改data也不会重新渲染页面了)
- 卸载子组件和监视器、解绑自定义事件监听器
- 调用destroyed()钩子函数(虽然destroyed翻译为已销毁,但此时Vue实例还在,空间并没有释放,只不过马上要释放了,这里的已销毁指的是vm对象上所有的东西都已经解绑完成了)
该阶段适合做什么?
beforeDestroy:适合做销毁前的准备工作,和人临终前写遗嘱类似。例如:可以在这里清除定时器。