Vue2.0基础

264 阅读11分钟

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的写法有两种:

  1. 在创建Vue实例时直接配置el属性。
el:'#root'
  1. 先创建Vue实例,之后再通过vm.$mount('#root')指定el的值
vm.$mount('#root')

data的写法也有两种:

  1. 对象式
data:{
    name:"喵喵"
}
  1. 函数式,在组件中必须使用函数式写法,为了避免组件复用时数据耦合。
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只能应用在表单类元素(输入类元素:inputselect等有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实例对象

MVVM模型.jpeg

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传入的数据,它的实现原理是:

  1. data中的数据传递给_data,并做了数据劫持(是指当我们访问或设置对象的属性的时候,都会触发相对应的函数,我们可以在触发函数的时候实现一些功能比如刷新页面DOM。)Vue中的数据劫持是为了实现页面的响应式。
  2. 通过Object.defineProperty()方法把_data中所有属性添加到实例vm中,并且给每个属性设置gettersetter,完成数据代理。

数据代理.jpg

数据监测原理

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','男')
    }
}

监测数组

数组中的元素不是依靠访问器gettersetter实现监测的,而是靠重写数组方法实现监测的。

Vue构造函数包装了数组的变更方法,添加了一些模板解析的方法,所以数组更新会触发视图更新,这些被包装的方法如下:

  • push()
  • pop()
  • shift()
  • unshift()
  • splice()
  • sort()
  • reverse()

以上这些方法都是修改原数组的方法,也就是对原数组进行修改后将其返回,比如以下代码就可以实现页面的响应式变化。

vm._data.student.hobby.push('学习');

但是注意如果通过索引值修改数组中的元素,是无法触发响应式的,比如以下代码无法触发视图更新。

vm._data.student.hobby[0] = '学习';

事件处理

事件的基本使用:

  1. 语法:v-on,简写@
  2. 事件中的回调函数需要写在配置项methods中,最终会挂在vm中。
  3. 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.xxx xxx是按键别名
  • @keydown.xxx xxx是按键别名
<input type="text" placeholder="按下回车提示输入" @keyup.enter="showInfo">

Vue常用的按键别名

  • 回车 enter
  • 删除 delete
  • 退出 esc
  • 空格 space
  • 换行 tab (必须配合keydown使用)
  • up
  • down
  • left
  • right

Vue中未提供别名的按键,可以使用按键原始的key值绑定,注意要小写和短横线使用。

  • 比如 CapsLock 需要写成 capslock

系统修饰键:ctrlaltshiftmeta

  • 配合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-ifv-show

v-if 不展示的DOM元素会被移除,v-ifv-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更新,界面出问题。

微信截图_20230206213415.png

开发中如何选择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()函数 常用于清除定时器、解绑自定义事件,取消订阅消息等收尾工作。

VUE 生命周期.png

vm.$destroy()方法 完全销毁一个实例,清理它与其它组件实例的链接,解绑它的全部指令及事件监听器。

关于销毁流程

  • 销毁后借助Vue开发者工具看不到任何信息
  • 销毁后自定义事件会失效,但原生DOM事件依然有效。
  • 一般不会在beforeDestroy阶段操作数据,因为即便操作数据,也不会再触发更新流程了。