[前端系列]Vue入门02

79 阅读5分钟

深入了解Vue

上一节,我们简单入门了以下Vue,并且体验了一些最基本的功能,本节我们将深入了解Vue

计算属性

属性:Vue认为data里面的东西就可以叫做属性

涉及到属性的处理,我们可以借助于计算属性的方法,也就是借助于computed

注意:计算属性要用已有的属性

computed里面,我们可以写计算得出的属性名以及对应值(对应值必须是对象,且里面必须有get方法)

底层其实就是用到了Object.defineProperty

计算出的属性名不在data上,但仍然再vm上面

get调用的时机:

1.初次读取计算属性时

2.计算属性发生变化时

有get就有set,如果计算属性会被修改,就会调用set

示例代码:

<body>
    <div id="root">
        姓:<input type="text" v-model="firstName"><br/>
        名:<input type="text" v-model="lastName"><br/>
        姓名:<span>{{fullName}}</span>
    </div>
​
    <script>
        new Vue({
            el:'#root',
            data:{
                firstName:'张',
                lastName:'三'
            },
            computed:{
                fullName:{
                    get(){
                        return this.firstName+'-'+this.lastName
                    },
                    set(val){
                        const arr=val.split('-')
                        this.firstName=arr[0]
                        this.lastName=arr[1]
                    }
                }
            }
        })
    </script>
</body>

简写:

如果我们需要的计算属性中,不涉及修改,那么可以按如下简写,这里面的function就当作get用:

computed:{
    fullName:function(){
       return this.firstName+'-'+this.lastName
    }
}

监视属性

监视属性需要借助watch,里面写你要监视的对象名,该对象的值应该是一个包含handler函数的对象

handler函数在监视的属性变换时调用,其可以接受两个参数,分别是新值和旧值

代码实现:

<body>
    <div id="root">
        <h2>今天天气很{{info}}</h2>
        <button @click="change">切换天气</button>
    </div>
​
    <script>
        new Vue({
            el:'#root',
            data:{
                isHot:true
            },
            methods:{
                change(){
                    this.isHot=!this.isHot
                }   
            },
            computed:{
                info:function(){
                    return this.isHot?'炎热':'凉爽'
                }
            },
            watch:{
                isHot:{
                    handler(newVal,oldVal){
                        console.log('isHot被修改了',newVal,oldVal)
                    }
                }
            }
        })
    </script>
</body>

当然监视属性的值除了handler以外,还有别的配置

除了data里的属性可以被监视,计算属性也可以被监视

我们还可以不用watch实现监视属性:

vm.$watch('isHot',{
    handler(){
        console.log('isHot被修改了',newVal,oldVal)
    }
})

深度监测

我们看一个案例:

<body>
    <div id="root">
        <h2>今天天气很{{info}}</h2>
        <button @click="change">切换天气</button>
        <hr/>
        <h3>a的值是:{{numbers.a}}</h3>
        <button @click="numbers.a++">a加一</button>
    </div>
​
    <script>
        const vm=new Vue({
            el:'#root',
            data:{
                isHot:true,
                numbers:{
                    a:1,
                    b:1
                }
            },
            methods:{
                change(){
                    this.isHot=!this.isHot
                }   
            },
            computed:{
                info:function(){
                    return this.isHot?'炎热':'凉爽'
                }
            },
            watch:{
                isHot:{
                    handler(newVal,oldVal){
                        console.log('isHot被修改了',newVal,oldVal)
                    }
                }
            }
        })
    </script>
</body>

我现在希望监视a,要用如下方式:

'numbers.a':{
    handler(newVal,oldVal){
        console.log('a变化了',newVal,oldVal)
    }
}

如果我希望监视整个numbers里面的所有内容,直接监视numbers是不行的,因为对象表面不变

所以我们希望“深度”监视numbers,我们要借助deep属性

numbers:{
    deep:true,
    handler(){
        console.log('numbers变化了')
    }
}

监视的简写形式(只有handler时才可以)

isHot(newVal,oldVal){
      console.log('isHot被修改了',newVal,oldVal)
}

如果是另一种监视方式,可以这样简写(不要写箭头函数):

vm.$watch('isHot',function(){
    console.log('isHot被修改了',newVal,oldVal)
})

绑定样式

class样式绑定

对于要改变的样式,我们要按如下形式写:

<div :class="abc">AAA</div>

接下来我们可以把abc加入属性中去:

data:{
    abc:'basic'
}

注意这里的basic是一个已有样式,abc可以看作是变量

上述方法适用于样式类名不确定,需要动态指定的绑定class选择器的情况

上面的abc值除了可以是一个字符串,还可以是数组,用于实现多个样式叠加

如果样式的名字和个数都确定,只是用不用不确定,可以用对象写法:

data:{
    abc:{
        basic1:true,
        basic2:false,
    }
}

style样式绑定

我们可以这样写:

<div :style="{fontSize: fsize+'px';}">AAA</div>

这样我们就可以把fsize写在data属性里面去操作它了

当然,我们也可以直接维护一个对象:

<div :style="styleObJ">AAA</div>

此时data里就可以这样:

data:{
    styleObj:{
        fontSize:'40px'
    }
}

当然绑定style样式也支持数组写法,但是不常用

条件渲染

v-show="布尔值表达式或布尔值变量"可以控制某一元素隐藏或者显示(底层是改变display属性)

v-if也可以做条件渲染,这里的不显示会连结构都去除,后面也是加布尔值变量或表达式

如果某个节点变化频率很高,推荐用v-show

注意:也有v-else-if和v-else这种构成选择语句(要与v-if搭配,不能被打断)

对于一些条件一样的判断,我们可以用template标签包起来(注意只能与v-if配合用)

列表渲染

我们可以借助v-for实现一种列表元素的遍历渲染

案例:

<body>
    <div id="root">
        <h2>人员列表</h2>
        <ul>
            <li v-for="p in persons" :key="p.id">{{p.name}}-{{p.age}}</li>
        </ul>
    </div>
​
    <script>
        const vm=new Vue({
            el:'#root',
            data:{
                persons:[
                    {id:'001',name:'John',age:18},
                    {id:'002',name:'Jeson',age:19},
                    {id:'003',name:'Perl',age:20},
                ]   
            }
        })
    </script>
</body>

注意:一定要配置key这个特殊属性,值应该是id

不用id还可以借助数组的索引值(一般不用):

<li v-for="(p,idx) in persons" :key="idx">{{p.name}}-{{p.age}}</li>

v-for除了遍历数组,还可以遍历对象:

<body>
    <div id="root">
        <h2>人员列表</h2>
        <ul>
            <li v-for="(v,k) in car" :key="k">{{k}}-{{v}}</li>
        </ul>
    </div>
​
    <script>
        const vm=new Vue({
            el:'#root',
            data:{
                car:{
                    name:'奥迪A8',
                    price:'200万',
                    color:'白色'
                }
            }
        })
    </script>
</body>

当然v-for还可以遍历字符串

key作用与原理

key就是一个标识的作用

key不能用index的原因:

因为虚拟DOM的diff算法,首先对比的就是key,如果key相等,就导致,里面的元素只要一样就会复用,不一致才新生成节点

这里面就会出现匹配不一致的问题,如果key不等,里面元素就不比较了,直接重新生成节点

所以key一定要用唯一标识符

列表过滤

所谓列表过滤就是从列表元素中,过滤出符合条件的元素

我们先借助监视属性实现这个功能:

<body>
    <div id="root">
        <h2>人员列表</h2>
        <input type="text" v-model="keyWord">
        <ul>
            <li v-for="p in filpersons" :key="p.id">{{p.name}}-{{p.age}}</li>
        </ul>
    </div>
​
    <script>
        const vm=new Vue({
            el:'#root',
            data:{
                keyWord:'',
                persons:[
                    {id:'001',name:'John',age:18},
                    {id:'002',name:'Jeson',age:19},
                    {id:'003',name:'Perl',age:20},
                ],
                filpersons:[]
            },
            watch:{
                keyWord:{
                    immediate:true,
                    handler(newVal){
                        this.filpersons=this.persons.filter((p)=>{
                            return p.name.indexOf(newVal)!==-1
                        })
                    }
                }
            }
        })
    </script>
</body>

我们再借助计算属性实现:

<body>
    <div id="root">
        <h2>人员列表</h2>
        <input type="text" v-model="keyWord">
        <ul>
            <li v-for="p in filpersons" :key="p.id">{{p.name}}-{{p.age}}</li>
        </ul>
    </div>
​
    <script>
        const vm=new Vue({
            el:'#root',
            data:{
                keyWord:'',
                persons:[
                    {id:'001',name:'John',age:18},
                    {id:'002',name:'Jeson',age:19},
                    {id:'003',name:'Perl',age:20},
                ]
            },
            computed:{
                filpersons(){
                    return this.persons.filter((p)=>{
                        return p.name.indexOf(this.keyWord)!==-1
                    })
                }
            }
        })
    </script>
</body>

因为计算属性在一开始就会执行一次,所以用computed更好

列表排序

假设我们希望按年龄进行排序

代码可以这样写:

<body>
    <div id="root">
        <h2>人员列表</h2>
        <input type="text" v-model="keyWord">
        <button @click="sortType=1">年龄降序</button>
        <button @click="sortType=2">年龄升序</button>
        <button @click="sortType=0">原顺序</button>
        <ul>
            <li v-for="p in filpersons" :key="p.id">{{p.name}}-{{p.age}}</li>
        </ul>
    </div>
​
    <script>
        const vm=new Vue({
            el:'#root',
            data:{
                keyWord:'',
                sortType:0,//0表示原顺序 1表示降序 2表示升序
                persons:[
                    {id:'001',name:'John',age:19},
                    {id:'002',name:'Jeson',age:17},
                    {id:'003',name:'Perl',age:20},
                ]
            },
            computed:{
                filpersons(){
                    const arr=this.persons.filter((p)=>{
                        return p.name.indexOf(this.keyWord)!==-1
                    })
                    if(this.sortType){
                        //这里箭头函数放在了普通函数内部,箭头函数的this就是普通函数的this
                        //事实上箭头函数的this取决于函数作用域
                        arr.sort((a,b)=>{
                            return this.sortType===1?b.age-a.age:a.age-b.age
                        })
                    }
                    return arr
                }
            }
        })
    </script>
</body>

监视数据原理

监视对象数据改变的原理

事实上,我们写完data的内容时,vm在将其存入_data中的时候,将属性变成了get,set的形式

其具体过程我们简单地可以理解为下面这样:

1.创建一个观察者类型,并且创建一个观察者实例

2.定义观察者类型的构造函数,构造函数会遍历data中的属性,并且通过Object.defineProperty的方式赋予观察者实例

3.让vm._data与data都等于观察者实例

如此一来,就实现了对数据的监视,数据只要发生改变,set方法就会被调用,这样看似麻烦,事实上解决了如果直接把属性定义在data上出现反复调用导致爆栈的问题,又保证了_data与data的一致性

如果想要后添加的属性也有响应式(有相应的get,set),要用Vue.set这个API,用法见下面的例子:

Vue.set(vm.student,'sex','男')

但是注意:这个API不能直接给data增加属性

监视数组数据改变的原理

如果响应式对象是一个数组,我们直接改了数组里面某个索引的值,Vue并不会监测到

Vue对数组的监视,是看是不是调用了push,pop,shift,unshift,splice,sort,reverse方法

我们调用Vue管理的数据的push等函数,实际上是Vue自己定义的push,它多了一步重新解析模板的步骤

除了这样,我们借助Vue.set API也可以实现对数组元素改变影响视图更新

Vue.set(vm.student.hobby,index,'xxx')