Vue是一套用于构建用户界面的渐进式JavaScript框架,它基于标准 HTML、CSS 和 JavaScript 构建,并提供了一套声明式、组件化的编程模型,使用虚拟DOM+Diff算法尽可能复用DOM节点,帮助我们高效地开发用户界面。
初始化Vue
Vue.config 是一个对象,包含Vue的全局配置,修改其中的productionTip属性,关闭生产提示。
// 关闭 vue 启动时生成生产提示。
Vue.config.productionTip = false;
Vue本质是一个构造函数,需要用new关键字实例化。
- 实例化时需要传入一个
对象形式的参数(配置对象)。 - 配置对象中有多个配置项(
el|data|methods等)。 - 容器中的代码被称为Vue模板。
- Vue实例和容器是一一对应的。
{{xxx}}中要写JS表达式,即能产生一个值的特殊js代码。{{xxx}}可以直接读取data中的所有属性,data中的数据发生改变时页面也会自动更新。
<!-- 准备好一个容器 -->
<div id="root">
<h1>{{message}}</h1>
</div>
<script>
Vue.config.productionTip = false;
// 创建Vue实例
const vm = new Vue({
// el用于指定当前Vue实例为哪个容器服务,值通常为css选择器字符串。
el: '#root',
// data中用于存储数据,数据供el所指定的容器使用。
data:{
message:"Hello world"
}
})
</script>
el与data
el的写法有两种:
- 在创建Vue实例时直接配置el属性。
el:'#root'
- 先创建Vue实例,之后再通过
vm.$mount('#root')指定el的值
vm.$mount('#root')
data的写法也有两种:
- 对象式
data:{
name:"喵喵"
}
- 函数式,在组件中必须使用函数式写法,为了避免组件复用时数据耦合。
data(){
return{
name:"喵喵"
}
}
v-bind语法
v-bind指令语法 用于动态绑定元素属性,可以简写为冒号:,它属于单向数据绑定,数据只能从data流向页面。
<div id="app">
<img :src="message" alt="">
</div>
<script>
const app = new Vue({
el: '#app',
data: {
message: 'https://www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png'
},
})
</script>
使用v-bind动态绑定元素class属性可以动态切换class样式,并且有三种语法如下:
- 字符串写法,适用于样式的类名不确定,需要动态指定。
<div id="app">
<h2 :class="mood">HelloWorld</h2>
</div>
<script>
const app = new Vue({
el: '#app',
data: {
mood: 'happy'
},
methods: {
changeMood(){
// 切换样式类名为sad
this.mood = 'sad'
}
}
})
</script>
- 对象语法,适用于要绑定的样式个数确定、名字也确定,但是需要动态决定是否使用。
<div id="app">
<h2 :class="mood">HelloWorld</h2>
</div>
<script>
const app = new Vue({
el: '#app',
data: {
mood: {
happy: true,
sad: false
}
},
methods: {
changeMood(){
// 开启样式类名sad
this.mood.sad = true;
}
}
})
</script>
- 数组语法,适用于要绑定的样式个数不确定,名字也不确定。
<div id="app">
<h2 :class="mood">HelloWorld</h2>
</div>
<script>
const app = new Vue({
el: '#app',
data: {
mood: ['happy', 'sad']
},
methods: {
goodMood(){
// 添加good样式
this.mood.push('good');
}
}
})
</script>
使用v-bind动态绑定style属性,注意style属性名要用小驼峰命名法:
- 对象语法
<div id="root">
<h2 :style="styleObj">{{message}}</h2>
</div>
<script>
Vue.config.productionTip = false
const vm = new Vue({
el:"#root",
data: {
message: 'HelloWorld',
styleObj: {
fontSize: '40px',
color: 'red'
}
},
})
</script>
- 数组语法
<div id="root">
<h2 :style="styleObj">{{message}}</h2>
</div>
<script>
Vue.config.productionTip = false
const vm = new Vue({
el:"#root",
data: {
message: 'HelloWorld',
styleObj: {
fontSize: '40px',
color: 'red'
},
styleObj: [
{
fontSize: '40px',
color: 'red'
},
{
backGroundColor: 'gray'
}
]
},
})
</script>
v-model语法
v-model指令语法 用于数据绑定,它属于双向数据绑定,是数据在data和页面间双向流动。默认收集的是元素的value属性值,此时可以将v-model:value="demo"简写为v-model="demo"。
v-model只能应用在表单类元素(输入类元素:input、select等有value属性的。),根据input类型不同v-model也起到不同的作用。
使用v-model绑定text类型的input,value属性是用户的输入值。
使用v-model绑定radio类型的input,可以控制选中哪个按钮,比如以下代码默认选中电子邮件按钮。
<script type='text/javascript' src='../js/vue.js'></script>
<div id='root'>
<form>
<fieldset>
<legend>请选择首选的联系方式:</legend>
<div>
<input type="radio" id="contactChoice1" name="contact" value="email" v-model="way" />
<label for="contactChoice1">电子邮件</label>
<input type="radio" id="contactChoice2" name="contact" value="phone" v-model="way" />
<label for="contactChoice2">电话</label>
<input type="radio" id="contactChoice3" name="contact" value="mail" v-model="way" />
<label for="contactChoice3">邮件</label>
</div>
<div>
<button type="submit">提交</button>
</div>
<h3>用户已选择{{way}}方式</h3>
</fieldset>
</form>
<pre id="log"></pre>
</div>
<script>
Vue.config.productionTip = false
const vm = new Vue({
el:'#root',
data: {
way: 'email'
},
})
</script>
使用v-model绑定checkbox类型的input,如果没有配置value属性,收集的就是checked属性的状态。
-
如果v-model的初始值是
非数组,默认收集的就是checked属性值(布尔值)。如果v-model的初始值是数组,收集的就是value值组成的数组。 -
在checkbox中使用了v-model之后,checkbox自带的checkd属性会失效
-
以下代码中fruits是一个
数组,当前checkbox被选中时,v-model会将value属性值push进fruits数组,当前checkbox被取消时,会将当前的value值从fruits数组中pop出去。
<div id='root'>
<input type="checkbox" name="fruit" value="苹果" v-model="fruits">苹果
<input type="checkbox" name="fruit" value="香蕉" v-model="fruits">香蕉
<input type="checkbox" name="fruit" value="橙子" v-model="fruits">橙子
<input type="checkbox" name="fruit" value="猕猴桃" v-model="fruits">猕猴桃
<h2>你喜欢的水果有{{fruits}}</h2>
</div>
<script>
Vue.config.productionTip = false
const vm = new Vue({
el:'#root',
data: {
fruits:[]
},
})
</script>
v-model修饰符的使用
lazy 让数据在失去焦点时或按回车键时再收集数据
number 让输入框中输入的字符串转为有效的数字
trim 过滤内容首尾空格
MVVM模型
MVVM(Model-view-viewmodel)是一种软件架构模式,Vue虽然没有完全遵循MVVM模型,但是也一定程度上参考了该模型,所以经常使用vm这个变量名表示Vue实例,在Vue中:
M(Model)模型 表示data中的数据V(View)视图 表示模板(DOM结构)VM(ViewModel)视图模型 表示Vue实例对象
vm相当于是data数据和模板之间的中间人,data中所有的属性,最后都挂在了vm中,所以模板中可以使用,比如插值语法{{}}。
<h2>{{$options}}</h2>
Vue原型中的所有的属性,在模板中也可以直接使用。
数据代理与劫持
Object.defineProperty(obj, attr, vlaue)方法 在指定对象中添加属性,参数obj表示指定的对象,attr表示添加的属性名,参数value表示配置项,可以对属性值做具体的设置。
let number = 18;
let person = {
name: '张三',
sex: '男'
}
Object.defineProperty(person, 'age', {
value: number, // 传入的值
// enumerable: true, // 控制属性是否可以枚举,默认值是false。
// writable: true, // 控制属性是否可以修改,默认值是false。
// configurable: true // 控制属性是否可以删除,默认值是false。
// 当有人读取person的age属性时,get方法(getter)就会被调用,且返回值就是age的值。
get(){
console.log('有人读取了age属性了');
return number;
},
// 当有人修改person的age属性时,set方法(setter)就会被调用,且返回值就是age的值。
set(value){
console.log('有人修改了age属性');
number = value;
}
})
console.log(Object.keys(person));
console.log(person);
以下代码是一个简单的数据代理过程:
let obj = {x: 100},
obj2 = {y: 200};
Object.defineProperty(obj2, 'x', {
get(){
return obj.x;
},
set(value){
obj.x = value;
}
})
console.log(obj2.x); // 100
obj2.x = 300;
console.log(obj.x); // 300
Vue中的数据代理是为了更方便的操作data传入的数据,它的实现原理是:
- 将
data中的数据传递给_data,并做了数据劫持(是指当我们访问或设置对象的属性的时候,都会触发相对应的函数,我们可以在触发函数的时候实现一些功能比如刷新页面DOM。)Vue中的数据劫持是为了实现页面的响应式。 - 通过
Object.defineProperty()方法把_data中所有属性添加到实例vm中,并且给每个属性设置getter和setter,完成数据代理。
数据监测原理
Vue为了实现页面的响应式,需要监测数据的变化,其中所用到的最底层原理就是访问器getter和setter。
监测对象
以下代码是简单模拟了一下监测对象的原理,其中用到以下两个重要方法:
-
Object.keys(obj)方法 返回一个指定对象的所有可枚举属性的字符串数组,数组中属性名的排列顺序和正常循环遍历该对象时返回的顺序一致。 -
Array.forEach()方法 对数组的每个元素执行一次给定的函数。
let data = {
name: '小松',
age: 18,
sex: '女',
hobby: '游泳'
};
// 定义一个vm空对象
let vm = {};
// 定义一个监视器构造函数
function Observer(obj){
// 收集对象中所有的属性形成一个数组
const keys = Object.keys(obj);
keys.forEach((k)=>{
Object.defineProperty(this, k, {
// keys数组中的每一项执行一次添加get,set方法的函数。
enumerable: true,
get(){
return obj[k];
},
set(value){
console.log(`${k}被修改了,需要生成新的虚拟DOM。`)
obj[k] = value;
}
})
})
}
// 将vm中的__data属性作为构造器的实例对象,用于监视data中属性的变化。
vm.__data = new Observer(data);
// 将vm.__data中的数据传递给vm
const keys = Object.keys(vm.__data);
keys.forEach((k)=>{
Object.defineProperty(vm, k, {
get(){
return vm.__data[k];
},
set(value){
vm.__data[k] = value;
}
})
});
console.log(vm.name); // 小松
Vue.set(target, property, value)方法 为指定对象添加响应式属性,参数target不能是Vue实例或Vue实例的根数据对象(data和_data)。property表示需要添加的属性名, value表示属性值.
methods: {
// 为数据student添加sex属性
addSex(){
Vue.set(this.student,'sex','男')
// vm.$set(this.student,'sex','男')
}
}
监测数组
数组中的元素不是依靠访问器getter和setter实现监测的,而是靠重写数组方法实现监测的。
Vue构造函数包装了数组的变更方法,添加了一些模板解析的方法,所以数组更新会触发视图更新,这些被包装的方法如下:
push()pop()shift()unshift()splice()sort()reverse()
以上这些方法都是修改原数组的方法,也就是对原数组进行修改后将其返回,比如以下代码就可以实现页面的响应式变化。
vm._data.student.hobby.push('学习');
但是注意如果通过索引值修改数组中的元素,是无法触发响应式的,比如以下代码无法触发视图更新。
vm._data.student.hobby[0] = '学习';
事件处理
事件的基本使用:
- 语法:
v-on,简写@。 - 事件中的回调函数需要写在配置项
methods中,最终会挂在vm中。 - methods中配置的函数,都是被Vue管理的函数,
this的指向是Vue实例对象或组件实例对象。不要使用箭头函数,否则this指向会出错。
当回调函数不需要参数时,函数调用可以省略小括号。如果回调函数本身需要传入一个参数, 而函数调用时没有写小括号,那么Vue会默认将浏览器生成的event事件对象作为参数传入到函数中。
如果写了小括号,但是没有传入实参则输出undefined,如果传入了实参输出的就是实参。
<div id='root'>
<button @click="btn1Click">按钮1</button> // 'HelloWorld'
<button @click="btn2Click">按钮2</button> // PointerEvent{...}
<button @click="btn3Click()">按钮3</button> // undefined
<button @click="btn4Click(1)">按钮4</button> // 1
</div>
<script>
Vue.config.productionTip = false
const vm = new Vue({
el:'#root',
data: {
message: 'HelloWorld'
},
methods: {
btn1Click(){
console.log(this.message);
},
btn2Click(arg){
console.log(arg);
},
btn3Click(arg){
console.log(arg);
},
btn4Click(arg){
console.log(arg);
}
}
})
</script>
如果回调函数既需要event对象,又需要传入其它参数,那么函数调用时,需要传入实参$event.
<script type='text/javascript' src='../js/vue.js'></script>
<div id='root'>
<button @click="btn3Click(message, $event)">按钮3</button> //'HelloWorld' PointerEvent{...}
</div>
<script>
Vue.config.productionTip = false
const vm = new Vue({
el:'#root',
data: {
message: 'HelloWorld'
},
methods: {
btn3Click(arg, event){
console.log(arg, event);
},
}
})
</script>
事件修饰符
prevent修饰符 阻止默认事件(常用)
<a href="http://www.baidu.com" @click.prevent = "showInfo">点我提示信息</a>
stop修饰符 阻止事件冒泡(常用)
<a href="http://www.baidu.com" @click.stop = "showInfo">点我提示信息</a>
once修饰符 事件只触发一次(常用)
<a href="http://www.baidu.com" @click.once = "showInfo">点我提示信息</a>
修饰符可以连写
@click.stop.prevent
capture修饰符 使用事件的捕获模式
self修饰符 只有event.target是当前操作的元素时才触发事件
passive修饰符 事件的默认行为立即执行,无需等待事件回调执行完毕
键盘事件 语法
@keyup.xxxxxx是按键别名@keydown.xxxxxx是按键别名
<input type="text" placeholder="按下回车提示输入" @keyup.enter="showInfo">
Vue常用的按键别名
- 回车
enter - 删除
delete - 退出
esc - 空格
space - 换行
tab(必须配合keydown使用) - 上
up - 下
down - 左
left - 右
right
Vue中未提供别名的按键,可以使用按键原始的key值绑定,注意要小写和短横线使用。
- 比如
CapsLock需要写成capslock。
系统修饰键:ctrl、alt、shift,meta。
- 配合
keyup使用时:按下修时键同时,再按下其它键,随后释放其它键,事件才能触发。 - 配合
keydown使用时:正常触发事件。 - 配合其它键形成唯一性:
@keyup.ctrl.y
Vue.config.keyCodes.xxx = yyy 可以自定义按键别名
计算属性
computed是Vue的一个配置项,可以通过已有的属性计算出新的属性。相比于methods而言,它具有缓存机制(复用),效率更高,调试方便。
底层使用了Object.definedproperty()方法提供的getter和setter,形成了自己的get()和set()方法。
get()方法 用于读取数据,初次读取时会执行一次,当依赖的数据发生改变时会被再次调用。
set()方法 用于修改计算属性,使用时要更新对应依赖的数据。
<div id="root">
姓:<input type="text" v-model="firstName"><br><br>
名:<input type="text" v-model="lastName"><br><br>
全名:<span>{{fullName}}</span><br><br>
</div>
<script>
Vue.config.productionTip = false
const vm = new Vue({
el:'#root',
data:{
firstName:'张',
lastName:'三'
},
computed:{
fullName:{
get(){
console.log("get被调用了")
return this.firstName + this.lastName
},
set(value){
console.log("set被调用了")
const arr = value.split("-")
this.firstName = arr[0]
this.lastName = arr[1]
}
}
}
})
当不需要set()方法时,计算属性可以简写为以下形式:
computed:{
fullName(){
console.log('get被调用了')
return this.firstName + '-' + this.lastName
}
}
监视属性
watch配置项,在Vue实例中用于监视属性的变化,属性必须存在才能被监视,当被监视的属性变化时,回调函数自动调用。
watch:{
isHot:{
// 监视属性初始化时是否调用handler,默认false。
immediate:true,
// 当isHot发生改变时调用的函数
handler(newValue,oldValue){
console.log("isHot被调用了",newValue,oldValue)
}
}
}
也可以在Vue实例写完后通过vm.$watch监视。
vm.$watch('isHot',{
immediate:true,
handler(newValue,oldValue){
console.log("isHot被调用了",newValue,oldValue)
}
})
deep配置项 是否启用深度监视,Vue中的watch默认不监测对象内部值的改变。启用之后可以监视多层结构的所有属性。- 对象属性名是字符串,监视多层结构的属性时须写成标准形式
'xxx.yy',比如'numbers.a'。
watch:{
numbers:{
// 监视多级结构中所有属性的变化
deep:true,
handler(){
console.log('numbers改变了')
}
}
当不需要immediate配置项和deep配置项时,监视属性可以简写。
watch:{
isHot(newValue,oldValue){
console.log('isHot被修改了',newValue,oldValue)
}
}
vm.$watch('isHot',function(newValue,oldValue){
console.log('isHot被修改了',newValue,oldValue)
})
计算与监视的区别
computed和watch的区别
- computed能完成的功能,watch都可以完成。
- watch能完成的功能,computed不一定能完成,比如watch可以执行
异步操作,因为watch是命令式编码。
重要的原则
- Vue管理的函数,最好写成普通函数,这样this的指向才是vm或组件实例对象。
- 不被Vue管理的函数(定时器的回调函数,ajax的回调函数。Promise的回调函数等),最好写成箭头函数,这样this的指向才是vm或组件实例对象。
- 比如此处的定时器回调函数因为是箭头函数,没有自己的this,往父级函数找this,即firstNmae函数的this,也就是vm。
watch:{
firstName(val){
setTimeout(() => {
console.log(this)
this.fullName = val + '-' + this.lastName
},1000)
},
lastName(val){
this.fullName = this.firstName + '-' + val
}
}
条件渲染
条件渲染指当元素满足一定条件时才会渲染页面,有两种方式v-if和v-show。
v-if 不展示的DOM元素会被移除,v-if 和 v-else-if v-else一起使用时,结构不能被打断。如果需要控制多个元素,可以配合template标签使用。
<div id='root'>
<h2 v-if="isShow">{{message}}</h2>
<h2 v-else>isShow为false时显示</h2>
</div>
<script>
Vue.config.productionTip = false
const vm = new Vue({
el:'#root',
data: {
message: 'HelloWorld',
isShow: true
}
})
</script>
v-show 不展示的DOM元素不会被移除,依旧在DOM结构中。
列表渲染
列表渲染用于展示列表数据,可以遍历数组、对象等。
v-for="(item, index) in list" :key="yyy" 根据遍历的数据类型不同,参数item和index表示的数据也不同,但是key是固定用法,用于动态绑定数据id,也就是是每一条数据的唯一标识符,主要作用是为了高效更行虚拟DOM。
如果没有设置key的值,Vue会默认用索引值作为key。
- 遍历数组
<!-- 遍历数组 -->
<h2>人员信息</h2>
<ul>
<!-- p是数组的每一项 index是每一项的索引值 index作为数据id -->
<li v-for="(p, index) in persons" :key="index">
{{p.id}}--{{p.name}}--{{p.age}}
</li>
</ul>
- 遍历对象
<!-- 遍历对象 -->
<h2>汽车信息</h2>
<ul>
<!-- value是属性值 k是属性名 属性名作为数据id -->
<li v-for="(value, k) in car" :key="k">
{{k}}--{{value}}
</li>
</ul>
- 遍历字符串
<!-- 遍历字符串 -->
<h2>遍历字符串</h2>
<ul>
<!-- char是每一个字符 index是索引值 索引值作为数据id -->
<li v-for="(char,index) in str" :key="index">
{{index}}--{{char}}
</li>
</ul>
- 遍历指定次数
<!-- 遍历指定次数 -->
<h2>汽车信息</h2>
<ul>
<!-- number是数字 index是索引值 索引值作为数据id -->
<li v-for="(number,index) in 5" :key="index">
{{index}}--{{number}}
</li>
</ul>
虚拟DOM中key的作用:
- key是虚拟DOM对象的表示,当数据变化时,Vue会根据【新数据】生成【新的虚拟DOM】,随后Vue进行【新虚拟DOM】与【旧虚拟DOM】的差异对比,对比算法如下:
在旧虚拟DOM中找到了与新虚拟DOM相同的key
- 若新虚拟DOM中节点内容没变,直接使用之前的真实DOM节点。
- 若新虚拟DOM中节点内容变了,则生成新的真实DOM节点,随后替换掉页面中旧的真实DOM节点。
旧虚拟DOM中未找到新虚拟DOM相同的key
- 创建新的真实DOM,随后渲染到页面
用索引值作为key可能会引发的问题:
- 若对数据进行:逆序添加、逆序删除等破坏顺序操作。
- 会产生没必要的真实DOM更新,效率低。
- 若结构中包含输入类的DOM,会产生错误DOM更新,界面出问题。
开发中如何选择key。
- 最好使用每条数据的唯一标识符作为key,比如id、手机号、身份证号、学号等唯一值。
- 如果不存在对数据的逆序添加、逆序删除等破坏顺序操作、仅用于渲染列表用于展示,使用索引值作为key没问题。
列表过滤
用watch实现列表过滤,需要用到以下两个数组方法。
filter()方法 创建指定数组一部分的浅拷贝并返回,通过所提供的回调函数进行过滤。indexOf()方法 返回在数组中可以找到给定元素的第一个索引,如果不存在,则返回 -1。
<div id="root">
<h2>人员列表</h2>
// v-model绑定搜索框的值
<input type="text" placeholder="请输入名字" v-model="keyWord">
<ul>
// v-for遍历的是新数组
<li v-for="(p,index) in filPersons" :key="index">
{{p.name}}--{{p.age}}--{{p.sex}}
</li>
</ul>
</div>
<script>
Vue.config.productionTip = false;
new Vue({
el:'#root',
data:{
keyWord:'', // keyWord初始值为空
persons:[
{id:'001',name:'马冬梅',age:18,sex:'女'},
{id:'002',name:'周冬雨',age:19,sex:'女'},
{id:'003',name:'周杰伦',age:20,sex:'男'},
{id:'004',name:'温兆伦',age:21,sex:'男'}
],
// 定义一个新数组接收原数组过滤的结果
filPersons:[]
},
watch: {
keyWord: {
immediate: true, //初始化时调用handler
handler(newvalue){
// filter方法过滤数组
this.filPersons = this.persons.filter((p) => {
// indexOf方法返回的是元素出现的位置,如果不存在即为-1。
return p.name.indexOf(newvalue) !== -1;
})
}
}
}
})
</script>
用computed实现列表过滤。
computed:{
// 创建一个新数组,依赖原数组的过滤。
filPersons(){
return this.persons.filter((p)=>{
return p.name.indexOf(this.keyWord) !== -1
})
}
}
列表排序
用watch实现列表排序,需要用到一个数组的方法。
sort()方法 用原地算法对数组的元素进行排序,并返回该数组。
<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="index">
{{p.name}}--{{p.age}}--{{p.sex}}
</li>
</ul>
</div>
<script>
Vue.config.productionTip = false
new Vue({
el:'#root',
data:{
keyWord:'',
sortType: 0,
persons:[
{id:'001',name:'马冬梅',age:20,sex:'女'},
{id:'002',name:'周冬雨',age:19,sex:'女'},
{id:'003',name:'周杰伦',age:18,sex:'男'},
{id:'004',name:'温兆伦',age:21,sex:'男'}
],
filPersons:[]
},
watch: {
keyWord: {
immediate: true, //初始化时调用handler
handler(newvalue){
// filter方法过滤数组
this.filPersons = this.persons.filter((p) => {
// 返回的是过滤的条件
// indexOf方法返回的是元素出现的位置,如果不存在即为-1。
return p.name.indexOf(newvalue) !== -1;
})
}
},
sortType(newvalue){
if(newvalue){
this.filPersons = this.filPersons.sort((p1, p2) => {
return newvalue === 1 ? p1.age-p2.age : p2.age-p1.age;
})
}
else{
this.filPersons = this.persons;
}
}
}
})
</script>
用computed实现列表排序
<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}}
</li>
</ul>
</div>
<script>
Vue.config.productionTip = false
new Vue({
el:'#root',
data:{
keyWord:'',
sortType:0, // 0原顺序 1降序 2升序
persons:[
{id:'001',name:'马冬梅',age:30,sex:'女'},
{id:'002',name:'周冬雨',age:31,sex:'女'},
{id:'003',name:'周杰伦',age:18,sex:'男'},
{id:'004',name:'温兆伦',age:19,sex:'男'}
]
},
computed:{
filPersons(){
const arr = this.persons.filter((p) => {
return p.name.indexOf(this.keyWord) !== -1;
})
// 判断是否需要排序
if(this.sortType){
arr.sort((p1, p2) => {
return this.sortType === 1 ? p1.age-p2.age : p2.age-p1.age;
})
}
return arr;
}
}
})
</script>
内置指令
v-text 向其所在的节点中渲染文本内容,与插值语法的区别:
- v-text会替换节点中的所有内容,{{xxx}}不会。
v-html 向指定节点中渲染包含html结构的内容,与插值语法的区别:
- v-html会替换节点中所有的内容,{{xxx}}不会。
- v-html可以识别HTML结构。
- v-html有安全性问题,在网站上动态渲染任意HTML是非常危险的,容易导致XSS攻击。 一定要在可信的内容上使用v-html,不要用在用户提交的内容上!
<div id="app">
<h2 v-html="url"></h2>
</div>
<script>
const app = new Vue({
el: '#app',
data: {
url: '<a href="http://www.baidu.com">百度一下</a>'
},
})
</script>
v-cloak 是一个特殊属性,Vue实例创建完毕并接管容器后,会删除v-cloak属性。使用css配合v-cloak可以解决网速慢时页面展示出{{xxx}}的问题。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
[v-cloak] {
display: none;
}
</style>
</head>
<body>
<script type="text/javascript" src="../js/vue.js"></script>
<div id="app">
<h2 v-cloak>{{message}}</h2>
</div>
<script>
setTimeout(function(){
const app = new Vue({
el: '#app',
data: {
message: 'HelloWorld'
},
})
}, 1000)
</script>
</body>
</html>
v-once v-once所在的节点在初次动态渲染后,就视为静态内容了。以后数据的改变不会引起v-once所在结构的更新,可以用于优化性能。
<h2 v-once>当前计数:{{counter}}</h2>
v-pre 跳过其所在节点的编译过程。可利用它跳过:没有使用指令语法、没有使用插值语法的节点,可以加快编译。
自定义指令
Vue中的指令本质上就是把原生操作DOM进行了一次封装,所以我们也可以自定义指令。
- 指令定义时不加
v-,但使用时要加v-。 - 指令名如果是多个单词,要使用kebab-case命名方式,不要用cameCase命名
比如我们定义一个v-big指令,和v-text指令类似,但是会将绑定的数据放大10倍。在Vue中我们可以通过directives配置项进行自定义指令,指令的定义有两种方式,对象式和函数式。
这里我们采用函数式定义big指令,其中的element参数表示绑定的元素,binding参数表示绑定的数据。big函数会在指令与元素成功绑定时,或者指令所在的模板被重新解析时调用。
const vm = new Vue({
el:"#root",
data: {
n: 1
},
directives: {
big(element, binding){
// 通过value属性可以获取数据的值
element.innerText = binding.value * 10;
}
},
})
再比如我们定义一个v-fbind指令,和v-bind指令类似,但可以让其所绑定的input元素默认获取焦点。这里我们采用对象式定义fbind指令,该形式中可以使用多个回调函数,常用的有以下三个:
bind函数 指令与元素成功绑定时调用inserted函数 指令所在元素被插入页面时调用update函数 指令所在模板被重新解析时调用
const vm = new Vue({
el:"#root",
data: {
n: 1
},
directives: {
fbind: {
bind(element, binding){
console.log('bind调用了');
element.value = binding.value;
},
inserted(element, binding){
console.log('inserted调用了');
element.focus();
},
update(element, binding){
console.log('update调用了');
element.value = binding.value;
}
}
},
})
注意以上指令都是局部指令,只能在当前Vue实例中使用,如果想让每一个Vue实例都能使用该指令,则需要配置成全局指令,和局部一样,可以用对象式或函数式定义全局指令。
- Vue.directives(指令名,配置对象)
- Vue.directives(指令名,回调函数)
生命周期
Vue在关键时刻帮我们调用的一些特殊名称的函数(生命周期函数 | 生命周期钩子)。
- 生命周期函数的名字不可更改,但函数的具体内容是程序员根据需求编写的。
- 生命周期函数中的
this指向vm或组件实例对象。
生命周期可以分为四个流程:
- 创建流程
- 挂载流程
- 更新流程
- 销毁流程
常用的生命周期函数:
mountded()函数 常用于发送ajax请求、启动定时器、绑定自定义事件、订阅消息等初始化操作。beforeDestroy()函数 常用于清除定时器、解绑自定义事件,取消订阅消息等收尾工作。
vm.$destroy()方法 完全销毁一个实例,清理它与其它组件实例的链接,解绑它的全部指令及事件监听器。
关于销毁流程
- 销毁后借助Vue开发者工具看不到任何信息
- 销毁后自定义事件会失效,但原生DOM事件依然有效。
- 一般不会在beforeDestroy阶段操作数据,因为即便操作数据,也不会再触发更新流程了。