动力节点老杜全新版Vue教程笔记分享给大家
- 视频教程从Vue2开始讲解,一步一个案例,知识点由浅入深,然后很自然的过度到Vue3版本。Vue3是目前企业中使用最多的一个版本。
- 视频中会把每一个Vue的知识点讲解的非常通透,不但举例告诉你怎么用,还会告诉你底层实现原理。
- 例如:Vue的数据代理机制底层实现原理是什么?本套视频中会从零手写一个Vue的数据代理机制,都是源码级的讲解。
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">
-
**<div** @click.stop="er"**>**
-
**<button** @click="yi"**>**{{name}}**</button>**
-
**</div>**
-
-
.prevent - 调用 event.preventDefault()。
-
<a href="http://www..com" @click.prevent="yi">
-
{{name}}
-
-
.capture - 添加事件侦听器时使用 capture 模式。
-
<div @click.capture="san">
-
**<div** @click.capture="er"**>**
-
**<button** @click="yi"**>**{{name}}**</button>**
-
**</div>**
-
注意:只有添加了capture修饰符的元素才会采用捕获模式。(或者说带有capture修饰符的优先触发)
-
.self - 只当事件是从侦听器绑定的元素本身触发时才触发回调。
-
<div @click="san">
-
**<div** @click.self="er"**>**
-
**<button** @click="yi"**>**{{name}}**</button>**
-
**</div>**
-
-
.once - 只触发一次回调。
-
<button @click.once="yi">
-
{{name}}
-
-
.passive - (2.3.0) 以 { passive: true } 模式添加侦听器
- 无需等待,直接继续(立即)执行事件默认行为。(对wheel事件有效果)
- .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">
-
**<h1>**{{msg}}**</h1>**
-
输入的信息:**<input** type="text" v-model="info"**><br>**
-
反转的信息:{{reversedInfo}} **<br>**
-
反转的信息:{{reversedInfo}} **<br>**
-
反转的信息:{{reversedInfo}} **<br>**
-
反转的信息:{{reversedInfo}} **<br>**
-
-
-
计算属性需要使用:computed
-
计算属性通过vm.$data 是无法访问的。计算属性不能通过vm.$data访问。
-
计算属性的getter/setter方法中的this是vm。
-
计算属性的getter方法的调用时机:
- 第一个时机:初次访问该属性。
- 第二个时机:计算属性所依赖的数据发生变化时。
-
计算属性的setter方法的调用时机:
- 当计算属性被修改时。(在setter方法中通常是修改属性,因为只有当属性值变化时,计算属性的值就会联动更新。注意:计算属性的值被修改并不会联动更新属性的值。)
-
计算属性没有真正的值,每一次都是依赖data属性计算出来的。
-
计算属性的getter和setter方法不能使用箭头函数,因为箭头函数的this不是vm。而是window。
-
计算属性的简写形式
只考虑读取,不考虑修改时,可以启用计算属性的简写形式。
- computed : {
-
reversedInfo(){
-
console.log('getter被调用了');
-
**return** **this**.info.split('').reverse().join('')
-
}
- }
2.3 侦听属性的变化
- 侦听属性的变化其实就是监视某个属性的变化。当被监视的属性一旦发生改变时,执行某段代码。
- 监视属性变化时需要使用watch配置项。
使用watch实现:比较数字大小的案例
- <div id="app">
-
**<h1>**{{msg}}**</h1>**
-
数值1:**<input** type="text" v-model="number1"**><br>**
-
数值2:**<input** type="text" v-model="number2"**><br>**
-
比较大小:{{compareResult}}
运行效果:
- 如何深度监视:
- 监视多级结构中某个属性的变化,写法是:’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 绑定字符串
适用于样式的名字不确定,需要动态指定。
- **
- <html lang="en">
-
**<meta** charset="UTF-8"**>**
-
**<title>**class绑定字符串形式**</title>**
-
**<script** src="../js/vue.js"**></script>**
-
**<style>**
-
.static{
-
border: 1px solid black;
-
background-color: beige;
-
}
-
.big{
-
width: 200px;
-
height: 200px;
-
}
-
.small{
-
width: 100px;
-
height: 100px;
-
}
-
**</style>**
-
**<div** id="app"**>**
-
**<h1>**{{msg}}**</h1>**
-
**<div** class="static" :class="c1"**></div>**
-
**</div>**
-
**<script>**
-
const vm = new Vue({
-
el : '#app',
-
data : {
-
msg : 'class绑定字符串形式',
-
c1 : 'small'
-
}
-
})
-
**</script>**
运行效果:
使用vue开发者工具修改c1的small为big:
通过测试可以看到样式完成了动态的切换。
2.4.1.2 绑定数组
适用于绑定的样式名字不确定,并且个数也不确定。
- **
- <html lang="en">
-
**<meta** charset="UTF-8"**>**
-
**<title>**class绑定数组形式**</title>**
-
**<script** src="../js/vue.js"**></script>**
-
**<style>**
-
.static{
-
border: 1px solid black;
-
}
-
.active{
-
background-color: green;
-
}
-
.text-danger{
-
color: red;
-
}
-
**</style>**
-
**<div** id="app"**>**
-
**<h1>**{{msg}}**</h1>**
-
**<div** class="static" :class="['active','text-danger']"**>**数组形式**</div>**
-
**<br><br>**
-
**<div** class="static" :class="[activeClass,errorClass]"**>**数组形式**</div>**
-
**<br><br>**
-
**<div** class="static" :class="classArray"**>**数组形式**</div>**
-
**</div>**
-
**<script>**
-
const vm = new Vue({
-
el : '#app',
-
data : {
-
msg : 'class绑定数组形式',
-
activeClass : 'active',
-
errorClass : 'text-danger',
-
classArray : ['active', 'text-danger']
-
}
-
})
-
**</script>**
运行效果:
使用vue开发者工具删除数组中的一个样式:
2.4.1.3 绑定对象
适用于样式名字和个数都确定,但是要动态决定用或者不用。
- **
- <html lang="en">
-
**<meta** charset="UTF-8"**>**
-
**<title>**class绑定对象形式**</title>**
-
**<script** src="../js/vue.js"**></script>**
-
**<style>**
-
.static{
-
border: 1px solid black;
-
}
-
.active{
-
background-color: green;
-
}
-
.text-danger{
-
color: red;
-
}
-
**</style>**
-
**<div** id="app"**>**
-
**<h1>**{{msg}}**</h1>**
-
**<div** class="static" :class="{active : true, 'text-danger' : true}"**>**对象形式**</div>**
-
**<br><br>**
-
**<div** class="static" :class="classObject"**>**对象形式**</div>**
-
**</div>**
-
**<script>**
-
const vm = new Vue({
-
el : '#app',
-
data : {
-
msg : 'class绑定对象形式',
-
classObject : {
-
active : true,
-
'text-danger' : false
-
}
-
}
-
})
-
**</script>**
运行效果:
使用vue开发者工具修改text-danger为true:
2.4.2 style绑定
2.4.2.1 绑定对象
- <div id="app">
-
**<h1>**{{msg}}**</h1>**
-
<!-- 静态写法 -->
-
**<div** class="static" style="font-size: 20px;"**>**对象形式**</div><br><br>**
-
<!-- 动态写法1 -->
-
**<div** class="static" :style="{fontSize: 40 + 'px'}"**>**对象形式**</div><br><br>**
-
<!-- 动态写法2 -->
-
**<div** class="static" :style="styleObject"**>**对象形式**</div><br><br>**
2.4.2.2 绑定数组
- <div id="app">
-
**<h1>**{{msg}}**</h1>**
-
<!-- 静态写法 -->
-
**<div** class="static" style="font-size: 40px; color: red;"**>**数组形式**</div><br><br>**
-
<!-- 动态写法1 -->
-
**<div** class="static" :style="[{fontSize:'40px'},{color:'red'}]"**>**数组形式**</div><br><br>**
-
<!-- 动态写法2 -->
-
**<div** class="static" :style="styleArray"**>**对象形式**</div><br><br>**
2.5 条件渲染
2.5.1 v-if
指令用于条件性地渲染一块内容。这块内容只会在指令的表达式返回true时才被渲染
- <div id="app">
-
**<h1>**{{msg}}**</h1>**
-
温度:**<input** type="number" v-model="temprature"**><br>**
-
天气:
-
**<span** v-if="temprature <= 10"**>**寒冷**</span>**
-
**<span** v-if="temprature > 10 && temprature <= 25"**>**凉爽**</span>**
-
**<span** v-if="temprature > 25"**>**炎热**</span>**
运行效果:
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">
-
**<h1>**{{msg}}**</h1>**
-
温度:**<input** type="number" v-model="temprature"**><br>**
-
天气:
-
**<span** v-if="temprature <= 10"**>**寒冷**</span>**
-
**<span** v-else-if="temprature <= 25"**>**凉爽**</span>**
-
**<span** v-else**>**炎热**</span>**
2.5.3 与v-if
因为 v-if 是一个指令,他必须依附于某个元素。但如果我们想要切换不止一个元素呢?在这种情况下我们可以在一个 元素上使用 v-if,这只是一个不可见的包装器元素,最后渲染的结果并不会包含个 元素。v-else 和 v-else-if 也可以在 上使用。
- <div id="app">
-
**<h1>**{{msg}}**</h1>**
-
温度:**<input** type="number" v-model="temprature"**><br>**
-
天气:
-
**<template** v-if="temprature <= 10"**>**
-
**<span>**寒冷**</span>**
-
**</template>**
-
**<template** v-else-if="temprature <= 25"**>**
-
**<span>**凉爽**</span>**
-
**</template>**
-
**<template** v-else**>**
-
**<span>**炎热**</span>**
-
**</template>**
- <script>
-
const vm = new Vue({
-
el : '#app',
-
data : {
-
msg : '条件渲染',
-
temprature : 10
-
}
-
})
- </script>
2.5.4 v-show
另一个可以用来按条件显示一个元素的指令是 v-show。其用法基本一样:
- <div id="app">
-
**<h1>**{{msg}}**</h1>**
-
温度:**<input** type="number" v-model="temprature"**><br>**
-
天气:
-
**<span** v-show="temprature <= 10"**>**寒冷**</span>**
-
**<span** v-show="temprature > 10 && temprature <= 25"**>**凉爽**</span>**
-
**<span** v-show="temprature > 25"**>**炎热**</span>**
- <script>
-
const vm = new Vue({
-
el : '#app',
-
data : {
-
msg : '条件渲染',
-
temprature : 10
-
}
-
})
- </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 遍历数组、对象、字符串、指定次数
- 遍历数组
- <div id="app">
-
**<h1>**{{msg}}**</h1>**
-
**<h2>**遍历数组**</h2>**
-
**<ul>**
-
**<li** v-for="(product, index) in products" :key="product.id"**>**
-
商品名称:{{product.name}},单价:{{product.price}}元/千克,下标:{{index}}
-
**</li>**
-
**</ul>**
- <script>
-
const vm = new Vue({
-
el : '#app',
-
data : {
-
msg : '列表渲染',
-
products : [
-
{id:'111',name:'西瓜',price:20},
-
{id:'222',name:'苹果',price:10},
-
{id:'333',name:'香蕉',price:30}
-
]
-
}
-
})
- </script>
运行效果:
![](/i/ll/?i=img_convert/335af84418f5e652ce76d53d2742bea8.png#averageHue=#e9e9e9&id=f5pkZ&originHeight=220&originWidth=639&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)
- 遍历对象
- <div id="app">
-
**<h1>**{{msg}}**</h1>**
-
**<h2>**遍历对象**</h2>**
-
**<ul>**
-
**<li** v-for="(propertyValue, propertyName) of dog" :key="propertyName"**>**
-
{{propertyName}}:{{propertyValue}}
-
**</li>**
-
**</ul>**
- <script>
-
const vm = new Vue({
-
el : '#app',
-
data : {
-
msg : '列表渲染',
-
dog : {
-
name : '拉布拉多',
-
age : 3,
-
gender : '雄性'
-
}
-
}
-
})
- </script>
运行结果:
![](/i/ll/?i=img_convert/459dbffb3c37f853a02f8708c0e8ca18.png#averageHue=#efefef&id=vftSx&originHeight=228&originWidth=396&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)
- 遍历字符串
- <div id="app">
-
**<h1>**{{msg}}**</h1>**
-
**<h2>**遍历字符串**</h2>**
-
**<ul>**
-
**<li** v-for="char,index of str" :key="index"**>**
-
{{index}}:{{char}}
-
**</li>**
-
**</ul>**
- <script>
-
const vm = new Vue({
-
el : '#app',
-
data : {
-
msg : '列表渲染',
-
str : '动力节点'
-
}
-
})
- </script>
运行结果:
![](/i/ll/?i=img_convert/1f1f1d8436618f17a1df81f0b0690865.png#averageHue=#efefef&id=vXpmJ&originHeight=224&originWidth=249&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)
- 遍历指定次数
- <div id="app">
-
**<h1>**{{msg}}**</h1>**
-
**<h2>**遍历指定次数**</h2>**
-
**<ul>**
-
**<li** v-for="number,index of 10" :key="index"**>**
-
下标:{{index}},数字:{{number}}
-
**</li>**
-
**</ul>**
- <script>
-
const vm = new Vue({
-
el : '#app',
-
data : {
-
msg : '列表渲染'
-
}
-
})
- </script>
运行结果:
![](/i/ll/?i=img_convert/4454677a3c99b0a7065667a74ca208f7.png#averageHue=#ebebeb&id=i3SDC&originHeight=484&originWidth=402&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)
2.6.2 虚拟dom和diff算法
所谓的虚拟dom就是内存当中的dom对象。vue为了提高渲染的效率,只有真正改变的dom元素才会重新渲染。
![](/i/ll/?i=img_convert/179b87727cc81a2a56f494d733a573c0.png#averageHue=#fbfafa&id=z2aZn&originHeight=806&originWidth=1423&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)
2.6.3 v-for的key的作用以及实现原理
- 用index作为key
-
**
- <html lang="en">
-
**<meta** charset="UTF-8"**>**
-
**<title>**key的原理**</title>**
-
**<script** src="../js/vue.js"**></script>**
-
**<div** id="app"**>**
-
**<h1>**{{msg}}**</h1>**
-
**<button** @click="addFirst"**>**在数组第一个位置添加tom**</button>**
-
**<button** @click="addLast"**>**在数组最后位置添加vue**</button>**
-
**<table>**
-
**<tr>**
-
**<th>**序号**</th>**
-
**<th>**姓名**</th>**
-
**<th>**邮箱**</th>**
-
**<th>**选择**</th>**
-
**</tr>**
-
**<tr** v-for="(vip,index) of vips" :key="index"**>**
-
**<td>**{{index + 1}}**</td>**
-
**<td>**{{vip.name}}**</td>**
-
**<td>**{{vip.email}}**</td>**
-
**<td><input** type="checkbox"**></td>**
-
**</tr>**
-
**</table>**
-
**</div>**
-
**<script>**
-
const vm = new Vue({
-
el : '#app',
-
data : {
-
msg : 'key原理(虚拟dom与diff算法)',
-
vips : [
-
{id:'100',name:'jack',email:'[email protected]'},
-
{id:'200',name:'lucy',email:'[email protected]'},
-
{id:'300',name:'james',email:'[email protected]'}
-
]
-
},
-
methods : {
-
addFirst(){
-
this.vips.unshift({id:'400',name:'tom',email:'[email protected]'})
-
},
-
addLast(){
-
this.vips.push({id:'500',name:'vue',email:'[email protected]'})
-
}
-
}
-
})
-
**</script>**
运行效果:
![](/i/ll/?i=img_convert/e21b3e912131dfffa73774dc3890f552.png#averageHue=#e8e6e5&id=KNfxM&originHeight=304&originWidth=678&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)
全部选中:
![](/i/ll/?i=img_convert/7e21d82fe59625be0ec9ab422bfe1f59.png#averageHue=#e7e5e4&id=JJJof&originHeight=291&originWidth=675&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)
添加tom:
![](/i/ll/?i=img_convert/cd37fec364874c01fd107751fd7961ef.png#averageHue=#e9e8e6&id=KngyB&originHeight=337&originWidth=673&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)
可以看到错乱了。思考这是为什么?
- 用vip.id作为key
运行和测试结果正常,没有出现错乱。为什么?
![](/i/ll/?i=img_convert/a9a3b7ebc2b65424c3a03a614c7fab23.png#averageHue=#e9e7e6&id=LX3tj&originHeight=330&originWidth=676&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)
- key的作用
key存在于虚拟dom元素中,代表该虚拟dom元素的唯一标识(身份证号)。
- diff算法是如何比较的?
新的虚拟dom和旧的虚拟dom比较时,先拿key进行比较:
- 如果key相同:则继续比较子元素:
- 子元素不同:直接将新的虚拟dom元素渲染到页面生成新的真实dom元素。
- 子元素相同:直接复用之前的真实dom。
- 如果key不同:直接将新的虚拟dom元素渲染到页面生成新的真实dom元素。
- index作为key存在两个问题
- 效率较低。
- 对数组的非末尾元素进行增删时,容易错乱。
- index作为key和vip.id作为key对比
当index作为key时:
![](/i/ll/?i=img_convert/32b7055500d94097ec32e7611929f22c.png#averageHue=#f4f3f2&id=PdbHx&originHeight=701&originWidth=1874&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)
当vip.id作为key时:
![](/i/ll/?i=img_convert/bae4c2ebe9de7ab1b01be91cdfd0a0d1.png#averageHue=#f3f2f1&id=H5LLf&originHeight=650&originWidth=1882&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)
2.7 列表过滤
使用watch和computed分别进行实现:
-
**
- <html lang="en">
-
**<meta** charset="UTF-8"**>**
-
**<title>**列表过滤**</title>**
-
**<script** src="../js/vue.js"**></script>**
-
**<style>**
-
table,tr,th,td{
-
border: 1px solid blue;
-
}
-
**</style>**
-
**<div** id="app"**>**
-
**<h1>**{{msg}}**</h1>**
-
**<input** type="text" placeholder="请输入搜索关键词" v-model="keyword"**>**
-
**<table>**
-
**<tr>**
-
**<th>**序号**</th>**
-
**<th>**姓名**</th>**
-
**<th>**邮箱**</th>**
-
**</tr>**
-
**<tr** v-for="(vip,index) of filterVips" :key="vip.id"**>**
-
**<td>**{{index+1}}**</td>**
-
**<td>**{{vip.name}}**</td>**
-
**<td>**{{vip.email}}**</td>**
-
**</tr>**
-
**</table>**
-
**</div>**
-
**<script>**
-
const vm = new Vue({
-
el : '#app',
-
data : {
-
keyword : '',
-
msg : '列表过滤',
-
vips : [
-
{id:'100',name:'jack',email:'[email protected]'},
-
{id:'200',name:'lucy',email:'[email protected]'},
-
{id:'300',name:'james',email:'[email protected]'}
-
],
-
//filterVips : []
-
},
-
/* watch : {
-
keyword : {
-
immediate : true,
-
handler(newValue, oldValue){
-
this.filterVips = this.vips.filter((v) =**>** {
-
return v.name.indexOf(newValue) **>**= 0
-
})
-
}
-
}
-
}, */
-
computed : {
-
filterVips(){
-
return this.vips.filter((v) =**>** {
-
return v.name.indexOf(this.keyword) **>**= 0
-
})
-
}
-
}
-
})
-
**</script>**
2.8 列表排序
-
**
- <html lang="en">
-
**<meta** charset="UTF-8"**>**
-
**<meta** http-equiv="X-UA-Compatible" content="IE=edge"**>**
-
**<meta** name="viewport" content="width=device-width, initial-scale=1.0"**>**
-
**<title>**列表排序**</title>**
-
**<script** src="../js/vue.js"**></script>**
-
**<style>**
-
table,tr,td,th{
-
border:1px solid black;
-
}
-
**</style>**
-
**<div** id="app"**>**
-
**<h1>**{{msg}}**</h1>**
-
**<input** type="text" placeholder="输入关键字搜索" v-model="keyword"**><br>**
-
**<button** @click="type = 1"**>**按照名字升序**</button><br>**
-
**<button** @click="type = 2"**>**按照名字降序**</button><br>**
-
**<button** @click="type = 0"**>**按照名字原始顺序**</button><br>**
-
**<table>**
-
**<tr>**
-
**<th>**序号**</th>**
-
**<th>**姓名**</th>**
-
**<th>**邮箱**</th>**
-
**<th>**操作**</th>**
-
**</tr>**
-
**<tr** v-for="(vip, index) in filterVips" :key="vip.id"**>**
-
**<td>**{{index+1}}**</td>**
-
**<td>**{{vip.name}}**</td>**
-
**<td>**{{vip.email}}**</td>**
-
**<td><input** type="checkbox"**></td>**
-
**</tr>**
-
**</table>**
-
**</div>**
-
**<script>**
-
const vm = new Vue({
-
el : '#app',
-
data : {
-
msg : '列表排序',
-
vips : [
-
{id:'100',name:'jack',email:'[email protected]'},
-
{id:'200',name:'lucy',email:'[email protected]'},
-
{id:'300',name:'james',email:'[email protected]'},
-
{id:'400',name:'lilei',email:'[email protected]'},
-
],
-
keyword : '',
-
type : 0
-
},
-
computed : {
-
filterVips(){
-
// 筛选
-
let arr = this.vips.filter((vip) =**>** {
-
return vip.name.indexOf(this.keyword) **>**= 0
-
})
-
// 根据排序类型进行排序
-
if(this.type){
-
arr.sort((v1, v2) =**>** {
-
console.log('@')
-
return this.type == 1 ? v1.name.localeCompare(v2.name) : v2.name.localeCompare(v1.name)
-
})
-
}
-
// 返回
-
return arr
-
}
-
}
-
})
-
**</script>**
-
2.9 收集表单数据
-
**
- <html lang="en">
-
**<meta** charset="UTF-8"**>**
-
**<title>**收集表单数据**</title>**
-
**<script** src="../js/vue.js"**></script>**
-
**<div** id="app"**>**
-
**<h1>**{{msg}}**</h1>**
-
**<form** @submit.prevent="send"**>**
-
**<label** for="username"**>**用户名:**</label>**
-
**<input** id="username" type="text" v-model.trim="user.username"**><br><br>**
-
密码:**<input** type="password" v-model="user.password"**><br><br>**
-
年龄:**<input** type="number" v-model.number="user.age"**><br><br>**
-
性别:
-
男**<input** type="radio" name="gender" v-model="user.gender" value="1"**>**
-
女**<input** type="radio" name="gender" v-model="user.gender" value="0"**><br><br>**
-
爱好:
-
运动**<input** type="checkbox" name="interest" value="sport" v-model="user.interest"**>**
-
旅游**<input** type="checkbox" name="interest" value="travel" v-model="user.interest"**>**
-
唱歌**<input** type="checkbox" name="interest" value="sing" v-model="user.interest"**><br><br>**
-
学历:
-
**<select** v-model="user.grade"**>**
-
**<option** value=""**>**请选择学历**</option>**
-
**<option** value="zk"**>**专科**</option>**
-
**<option** value="bk"**>**本科**</option>**
-
**<option** value="ss"**>**硕士**</option>**
-
**</select><br><br>**
-
简介:**<textarea** cols="30" rows="10" v-model.lazy="user.introduce"**></textarea><br><br>**
-
**<input** type="checkbox" v-model="user.isAgree"**>**阅读并接受协议**<br><br>**
-
**<button>**注册**</button>**
-
**</form>**
-
**</div>**
-
**<script>**
-
const vm = new Vue({
-
el : '#app',
-
data : {
-
msg : '收集表单数据',
-
user : {
-
username : '',
-
password : '',
-
age : '',
-
gender : '0',
-
interest : ['sport'],
-
grade : 'ss',
-
introduce : '',
-
isAgree : ''
-
}
-
},
-
methods : {
-
send(){
-
console.log(JSON.stringify(this.user))
-
}
-
}
-
})
-
**</script>**
页面展示效果:
![](/i/ll/?i=img_convert/03aec1b28a428fd2f21599e80fe0577c.png#averageHue=#f7f6f5&id=jWyYP&originHeight=578&originWidth=305&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)
运行结果:
![](/i/ll/?i=img_convert/8bbd66e346967f46996c6588c03bb707.png#averageHue=#fdfbf7&id=lnxLm&originHeight=47&originWidth=680&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)
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。
例如:用户在留言中恶意植入以下信息:
![](/i/ll/?i=img_convert/db7bfb1cae709ed2615ede219dbe5278.png#averageHue=#fafafa&id=dRrJy&originHeight=191&originWidth=743&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)
其他用户上当了:如果点击了以上的留言,就会将cookie发送给恶意的服务器。
![](/i/ll/?i=img_convert/7fd08bfed1730deaf1cf8a1ec37e65be.png#averageHue=#eff1d2&id=Bz9sb&originHeight=55&originWidth=365&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)
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官方的生命周期图:
![](/i/ll/?i=img_convert/e762b881e0b8c3727d97cfa28d0383cb.png#averageHue=#fefdfb&id=RHU1u&originHeight=3039&originWidth=1200&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)
翻译后的生命周期图:
![](/i/ll/?i=img_convert/5ad1b353b0328df962152399793eb8bd.png#averageHue=#fcfbfb&id=Zf7nQ&originHeight=1754&originWidth=1345&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)
2.14.4 初始阶段做了什么事儿
做了这么几件事:
- 创建Vue实例vm(此时Vue实例已经完成了创建,这是生命的起点)
- 初始化事件对象和生命周期(接产大夫正在给他洗澡)
- 调用beforeCreate()钩子函数(此时还无法通过vm去访问data对象的属性)
- 初始化数据代理和数据监测
- 调用created()钩子函数(此时数据代理和数据监测创建完毕,已经可以通过vm访问data对象的属性)
- 编译模板语句生成虚拟DOM(此时虚拟DOM已经生成,但页面上还没有渲染)
该阶段适合做什么?
beforeCreate:可以在此时加一些loading效果。
created:结束loading效果。也可以在此时发送一些网络请求,获取数据。也可以在这里添加定时器。
2.14.5 挂载阶段做了什么事儿
做了这么几件事:
- 调用beforeMount()钩子函数(此时页面还未渲染,真实DOM还未生成)
- 给vm追加$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:适合做销毁前的准备工作,和人临终前写遗嘱类似。例如:可以在这里清除定时器。
标签:Vue,函数,vm,id,msg,完整版,老杜,name
From: https://www.cnblogs.com/blog12345/p/17352569.html
- <div id="app">
-
**<h1>**{{msg}}**</h1>**
-
温度:**<input** type="number" v-model="temprature"**><br>**
-
天气:
-
**<template** v-if="temprature <= 10"**>**
-
**<span>**寒冷**</span>**
-
**</template>**
-
**<template** v-else-if="temprature <= 25"**>**
-
**<span>**凉爽**</span>**
-
**</template>**
-
**<template** v-else**>**
-
**<span>**炎热**</span>**
-
**</template>**
- <script>
-
const vm = new Vue({
-
el : '#app',
-
data : {
-
msg : '条件渲染',
-
temprature : 10
-
}
-
})
- </script>
2.5.4 v-show
另一个可以用来按条件显示一个元素的指令是 v-show。其用法基本一样:
- <div id="app">
-
**<h1>**{{msg}}**</h1>**
-
温度:**<input** type="number" v-model="temprature"**><br>**
-
天气:
-
**<span** v-show="temprature <= 10"**>**寒冷**</span>**
-
**<span** v-show="temprature > 10 && temprature <= 25"**>**凉爽**</span>**
-
**<span** v-show="temprature > 25"**>**炎热**</span>**
- <script>
-
const vm = new Vue({
-
el : '#app',
-
data : {
-
msg : '条件渲染',
-
temprature : 10
-
}
-
})
- </script>
不同之处在于 v-show 会在 DOM 渲染中保留该元素;v-show 仅切换了该元素上名为 display 的 CSS 属性。 v-if 是“真实的”按条件渲染,因为它确保了在切换时,条件区块内的事件监听器和子组件都会被销毁与重建 语法格式:v-for指令。该指令用在被遍历的标签上。 或者 运行效果: 运行结果: 运行结果: 运行结果: 所谓的虚拟dom就是内存当中的dom对象。vue为了提高渲染的效率,只有真正改变的dom元素才会重新渲染。 运行效果: 运行和测试结果正常,没有出现错乱。为什么? key存在于虚拟dom元素中,代表该虚拟dom元素的唯一标识(身份证号)。 新的虚拟dom和旧的虚拟dom比较时,先拿key进行比较: 当index作为key时: 使用watch和computed分别进行实现: 页面展示效果: 过滤器filters适用于简单的逻辑处理,例如:对一些数据进行格式化显示。他的功能完全可以使用methods,computed来实现。过滤器可以进行全局配置,也可以进行局部配置: 过滤器可以用在两个地方:插值语法和v-bind指令中。 将内容填充到标签体当中,并且是以覆盖的形式填充,而且填充的内容中即使存在HTML标签也只是会当做一个普通的字符串处理,不会解析。功能等同于原生JS中的innerText。 将内容填充到标签体当中,并且是以覆盖的形式填充,而且将填充的内容当做HTML代码解析。功能等同于原生JS中的innerHTML。 v-cloak配置css样式来解决胡子的闪现问题。 初次接触指令的时候已经学过了。只渲染一次。之后将被视为静态内容。 使用该指令可以提高编译速度。带有该指令的标签将不会被编译。可以在没有Vue语法规则的标签中使用可以提高效率。不要将它用在带有指令语法以及插值语法的标签中。 函数式: 修改data后,页面自动改变/刷新。这就是响应式。就像我们在使用excel的时候,修改一个单元格中的数据,其它单元格的数据会联动更新,这也是响应式。 数据劫持:Vue底层使用了Object.defineProperty,配置了setter方法,当去修改属性值时setter方法则被自动调用,setter方法中不仅修改了属性值,而且还做了其他的事情,例如:重新渲染页面。setter方法就像半路劫持一样,所以称为数据劫持。 push() 所谓的生命周期是指:一个事物从出生到最终的死亡,整个经历的过程叫做生命周期。 可以看到,在这个生命线上有很多不同的时间节点,在不同的时间节点上去做不同的事儿。 Vue的生命周期指的是:vm对象从创建到最终销毁的整个过程。 在生命线上的函数叫做钩子函数,这些函数是不需要程序员手动调用的,由Vue自动调用,程序员只需要按照自己的需求写上,到了那个时间点自动就会执行。 研究Vue的生命周期主要是研究:在不同的时刻Vue做了哪些不同的事儿。 Vue的生命周期可以被划分为4个阶段:初始阶段、挂载阶段、更新阶段、销毁阶段。 8个钩子函数写在哪里?直接写在Vue构造函数的options对象当中。 做了这么几件事: 该阶段适合做什么? 做了这么几件事: 该阶段适合做什么? 做了这么几件事: 该阶段适合做什么? 做了这么几件事: 该阶段适合做什么?
v-show 不支持在 元素上使用,也不能和 v-else 搭配使用。
2.5.5 v-if VS v-show
v-if 也是惰性的:如果在初次渲染时条件值为 false,则不会做任何事。条件区块只有当条件首次变为 true 时才被渲染。
相比之下,v-show 简单许多,元素无论初始条件如何,始终会被渲染,只有 CSS display属性会被切换。
总的来说,v-if 有更高的切换开销,而 v-show 有更高的初始渲染开销。因此,如果需要频繁切换,则使用 v-show 较好;如果在运行时绑定条件很少改变,则 v-if 会更合适。2.6 列表渲染
2.6.1 遍历数组、对象、字符串、指定次数
**<h1>**{{msg}}**</h1>**
**<h2>**遍历数组**</h2>**
**<ul>**
**<li** v-for="(product, index) in products" :key="product.id"**>**
商品名称:{{product.name}},单价:{{product.price}}元/千克,下标:{{index}}
**</li>**
**</ul>**
const vm = new Vue({
el : '#app',
data : {
msg : '列表渲染',
products : [
{id:'111',name:'西瓜',price:20},
{id:'222',name:'苹果',price:10},
{id:'333',name:'香蕉',price:30}
]
}
})
**<h1>**{{msg}}**</h1>**
**<h2>**遍历对象**</h2>**
**<ul>**
**<li** v-for="(propertyValue, propertyName) of dog" :key="propertyName"**>**
{{propertyName}}:{{propertyValue}}
**</li>**
**</ul>**
const vm = new Vue({
el : '#app',
data : {
msg : '列表渲染',
dog : {
name : '拉布拉多',
age : 3,
gender : '雄性'
}
}
})
**<h1>**{{msg}}**</h1>**
**<h2>**遍历字符串**</h2>**
**<ul>**
**<li** v-for="char,index of str" :key="index"**>**
{{index}}:{{char}}
**</li>**
**</ul>**
const vm = new Vue({
el : '#app',
data : {
msg : '列表渲染',
str : '动力节点'
}
})
**<h1>**{{msg}}**</h1>**
**<h2>**遍历指定次数**</h2>**
**<ul>**
**<li** v-for="number,index of 10" :key="index"**>**
下标:{{index}},数字:{{number}}
**</li>**
**</ul>**
const vm = new Vue({
el : '#app',
data : {
msg : '列表渲染'
}
})
2.6.2 虚拟dom和diff算法
2.6.3 v-for的key的作用以及实现原理
**<meta** charset="UTF-8"**>**
**<title>**key的原理**</title>**
**<script** src="../js/vue.js"**></script>**
**<div** id="app"**>**
**<h1>**{{msg}}**</h1>**
**<button** @click="addFirst"**>**在数组第一个位置添加tom**</button>**
**<button** @click="addLast"**>**在数组最后位置添加vue**</button>**
**<table>**
**<tr>**
**<th>**序号**</th>**
**<th>**姓名**</th>**
**<th>**邮箱**</th>**
**<th>**选择**</th>**
**</tr>**
**<tr** v-for="(vip,index) of vips" :key="index"**>**
**<td>**{{index + 1}}**</td>**
**<td>**{{vip.name}}**</td>**
**<td>**{{vip.email}}**</td>**
**<td><input** type="checkbox"**></td>**
**</tr>**
**</table>**
**</div>**
**<script>**
const vm = new Vue({
el : '#app',
data : {
msg : 'key原理(虚拟dom与diff算法)',
vips : [
{id:'100',name:'jack',email:'[email protected]'},
{id:'200',name:'lucy',email:'[email protected]'},
{id:'300',name:'james',email:'[email protected]'}
]
},
methods : {
addFirst(){
this.vips.unshift({id:'400',name:'tom',email:'[email protected]'})
},
addLast(){
this.vips.push({id:'500',name:'vue',email:'[email protected]'})
}
}
})
**</script>**
全部选中:
添加tom:
可以看到错乱了。思考这是为什么?
当vip.id作为key时:
2.7 列表过滤
**<meta** charset="UTF-8"**>**
**<title>**列表过滤**</title>**
**<script** src="../js/vue.js"**></script>**
**<style>**
table,tr,th,td{
border: 1px solid blue;
}
**</style>**
**<div** id="app"**>**
**<h1>**{{msg}}**</h1>**
**<input** type="text" placeholder="请输入搜索关键词" v-model="keyword"**>**
**<table>**
**<tr>**
**<th>**序号**</th>**
**<th>**姓名**</th>**
**<th>**邮箱**</th>**
**</tr>**
**<tr** v-for="(vip,index) of filterVips" :key="vip.id"**>**
**<td>**{{index+1}}**</td>**
**<td>**{{vip.name}}**</td>**
**<td>**{{vip.email}}**</td>**
**</tr>**
**</table>**
**</div>**
**<script>**
const vm = new Vue({
el : '#app',
data : {
keyword : '',
msg : '列表过滤',
vips : [
{id:'100',name:'jack',email:'[email protected]'},
{id:'200',name:'lucy',email:'[email protected]'},
{id:'300',name:'james',email:'[email protected]'}
],
//filterVips : []
},
/* watch : {
keyword : {
immediate : true,
handler(newValue, oldValue){
this.filterVips = this.vips.filter((v) =**>** {
return v.name.indexOf(newValue) **>**= 0
})
}
}
}, */
computed : {
filterVips(){
return this.vips.filter((v) =**>** {
return v.name.indexOf(this.keyword) **>**= 0
})
}
}
})
**</script>**
2.8 列表排序
**<meta** charset="UTF-8"**>**
**<meta** http-equiv="X-UA-Compatible" content="IE=edge"**>**
**<meta** name="viewport" content="width=device-width, initial-scale=1.0"**>**
**<title>**列表排序**</title>**
**<script** src="../js/vue.js"**></script>**
**<style>**
table,tr,td,th{
border:1px solid black;
}
**</style>**
**<div** id="app"**>**
**<h1>**{{msg}}**</h1>**
**<input** type="text" placeholder="输入关键字搜索" v-model="keyword"**><br>**
**<button** @click="type = 1"**>**按照名字升序**</button><br>**
**<button** @click="type = 2"**>**按照名字降序**</button><br>**
**<button** @click="type = 0"**>**按照名字原始顺序**</button><br>**
**<table>**
**<tr>**
**<th>**序号**</th>**
**<th>**姓名**</th>**
**<th>**邮箱**</th>**
**<th>**操作**</th>**
**</tr>**
**<tr** v-for="(vip, index) in filterVips" :key="vip.id"**>**
**<td>**{{index+1}}**</td>**
**<td>**{{vip.name}}**</td>**
**<td>**{{vip.email}}**</td>**
**<td><input** type="checkbox"**></td>**
**</tr>**
**</table>**
**</div>**
**<script>**
const vm = new Vue({
el : '#app',
data : {
msg : '列表排序',
vips : [
{id:'100',name:'jack',email:'[email protected]'},
{id:'200',name:'lucy',email:'[email protected]'},
{id:'300',name:'james',email:'[email protected]'},
{id:'400',name:'lilei',email:'[email protected]'},
],
keyword : '',
type : 0
},
computed : {
filterVips(){
// 筛选
let arr = this.vips.filter((vip) =**>** {
return vip.name.indexOf(this.keyword) **>**= 0
})
// 根据排序类型进行排序
if(this.type){
arr.sort((v1, v2) =**>** {
console.log('@')
return this.type == 1 ? v1.name.localeCompare(v2.name) : v2.name.localeCompare(v1.name)
})
}
// 返回
return arr
}
}
})
**</script>**
2.9 收集表单数据
**<meta** charset="UTF-8"**>**
**<title>**收集表单数据**</title>**
**<script** src="../js/vue.js"**></script>**
**<div** id="app"**>**
**<h1>**{{msg}}**</h1>**
**<form** @submit.prevent="send"**>**
**<label** for="username"**>**用户名:**</label>**
**<input** id="username" type="text" v-model.trim="user.username"**><br><br>**
密码:**<input** type="password" v-model="user.password"**><br><br>**
年龄:**<input** type="number" v-model.number="user.age"**><br><br>**
性别:
男**<input** type="radio" name="gender" v-model="user.gender" value="1"**>**
女**<input** type="radio" name="gender" v-model="user.gender" value="0"**><br><br>**
爱好:
运动**<input** type="checkbox" name="interest" value="sport" v-model="user.interest"**>**
旅游**<input** type="checkbox" name="interest" value="travel" v-model="user.interest"**>**
唱歌**<input** type="checkbox" name="interest" value="sing" v-model="user.interest"**><br><br>**
学历:
**<select** v-model="user.grade"**>**
**<option** value=""**>**请选择学历**</option>**
**<option** value="zk"**>**专科**</option>**
**<option** value="bk"**>**本科**</option>**
**<option** value="ss"**>**硕士**</option>**
**</select><br><br>**
简介:**<textarea** cols="30" rows="10" v-model.lazy="user.introduce"**></textarea><br><br>**
**<input** type="checkbox" v-model="user.isAgree"**>**阅读并接受协议**<br><br>**
**<button>**注册**</button>**
**</form>**
**</div>**
**<script>**
const vm = new Vue({
el : '#app',
data : {
msg : '收集表单数据',
user : {
username : '',
password : '',
age : '',
gender : '0',
interest : ['sport'],
grade : 'ss',
introduce : '',
isAgree : ''
}
},
methods : {
send(){
console.log(JSON.stringify(this.user))
}
}
})
**</script>**
运行结果:
2.10 过滤器
多个过滤器可以串联:{{msg | filterA | filterB | filterC}}
过滤器也可以接收额外的参数,但过滤器的第一个参数永远接收的都是前一个过滤器的返回值。2.11 Vue的其它指令
2.11.1 v-text
2.11.2 v-html
v-html不要用到用户提交的内容上。可能会导致XSS攻击。XSS攻击通常指的是通过利用网页开发时留下的漏洞,通过巧妙的方法注入恶意指令代码到网页,使用户加载并执行攻击者恶意制造的网页程序。这些恶意网页程序通常是JavaScript。
例如:用户在留言中恶意植入以下信息:
其他用户上当了:如果点击了以上的留言,就会将cookie发送给恶意的服务器。
2.11.3 v-cloak
v-cloak指令使用在标签当中,当Vue实例接管之后会删除这个指令。
这是一段CSS样式:当前页面中所有带有v-cloak属性的标签都隐藏起来。
[v-cloak] {
display : none;
}2.11.4 v-once
2.11.5 v-pre
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 响应式与数据劫持
pop()
reverse()
splice()
shift()
unshift()
sort()
或者使用:
Vue.set(数组对象, ‘index’, 值)
vm.$set(数组对象, ‘index’, 值)2.14 Vue的生命周期
2.14.1 什么是生命周期
例如人的生命周期:
2.14.2 掌握Vue的生命周期有什么用
例如:在vm被销毁之前,我需要将绑定到元素上的自定义事件全部解绑,那么这个解绑的代码就需要找一个地方写一下,写到哪里呢?你可以写到beforeDestroy()这个函数中,这个函数会被Vue自动调用,而且是在vm对象销毁前被自动调用。像这种在不同时刻被自动调用的函数称为钩子函数。每一个钩子函数都有对应的调用时间节点。
换句话说,研究Vue的生命周期主要研究的核心是:在哪个时刻调用了哪个钩子函数。2.14.3 Vue生命周期的4个阶段8个钩子
每个阶段会调用两个钩子函数。两个钩子函数名的特点:beforeXxx()、xxxed()。
8个生命周期钩子函数分别是:
Vue官方的生命周期图:
翻译后的生命周期图:
2.14.4 初始阶段做了什么事儿
beforeCreate:可以在此时加一些loading效果。
created:结束loading效果。也可以在此时发送一些网络请求,获取数据。也可以在这里添加定时器。2.14.5 挂载阶段做了什么事儿
mounted:可以操作页面的DOM元素了。2.14.6 更新阶段做了什么事儿
beforeUpdate:适合在更新之前访问现有的 DOM,比如手动移除已添加的事件监听器。
updated:页面更新后,如果想对数据做统一处理,可以在这里完成。2.14.7 销毁阶段做了什么事儿
beforeDestroy:适合做销毁前的准备工作,和人临终前写遗嘱类似。例如:可以在这里清除定时器。