文章目录
- 监视属性 watch
- 深度监视
- computed 和 watch 之间的区别
- 绑定样式(class style)
- 条件渲染
- 列表渲染
- 基本列表
- key的原理
- 列表过滤
- 列表排序
- 收集表单中的数据 v-model
- 过滤器(Vue3已移除)
监视属性 watch
- 当被监视的属性变化时,回调函数自动调用,进行相关操作
- 监视的属性必须存在,才能进行监视
- 监视的两种写法:
a. new Vue时传入watch配置
b. 通过vm.$watch监视
| <body> |
| <div id="root"> |
| <h2>今天天气很{{info}}</h2> |
| <button @click="changeWeather">切换天气</button> |
| </div> |
| |
| |
| </body> |
| <script type="text/javascript"> |
| Vue.config.productionTip = false; |
| |
| const vm = new Vue({ |
| el: '#root', |
| data: { |
| isHot: true |
| }, |
| computed: { |
| info() { |
| return this.isHot ? '炎热' : '凉爽' |
| } |
| }, |
| methods: { |
| changeWeather() { |
| this.isHot = !this.isHot |
| } |
| }, |
| |
| |
| watch:{ |
| |
| isHot: { |
| immediate: true, |
| |
| handler(newValue, oldValue){ |
| console.log('newValue:', newValue); |
| console.log('oldValue:', oldValue); |
| }, |
| } |
| |
| |
| isHot(newValue,oldValue){ |
| console.log('isHot被修改了') |
| } |
| } |
| }) |
| |
| |
| |
| vm.$watch('isHot',{ |
| immediate: true, |
| |
| handler(newValue, oldValue){ |
| console.log('newValue:', newValue); |
| console.log('oldValue:', oldValue); |
| }, |
| }) |
| |
| |
| vm.$watch('isHot',(newValue,oldValue){ |
| console.log('isHot被修改了',newValue) |
| }) |
| </script> |
复制
深度监视
- Vue中的Watch默认不监测对象内部值的改变(一层)
- 配置deep:true可以监测对象内部值改变(多层)
- 备注:
a. Vue自身可以监测对象内部值的改变,但Vue提供的watch默认不可以
b. 使用watch时数据的具体结构,决定是否采用深度监视
| <body> |
| <div id="root"> |
| <h2>今天天气很{{info}}</h2> |
| <button @click="changeWeather">切换天气</button> |
| <hr> |
| <h3>a的值是:{{numbers.a}}</h3> |
| <button @click="numbers.a++">点我让a+1</button> |
| <br> |
| <h3>b的值是:{{numbers.b}}</h3> |
| <button @click="numbers.b++">点我让b+1</button> |
| </div> |
| </body> |
| <script type="text/javascript"> |
| Vue.config.productionTip = false; |
| |
| const vm = new Vue({ |
| el: '#root', |
| data: { |
| isHot: true, |
| numbers:{ |
| a: 1, |
| b: 1 |
| } |
| }, |
| computed: { |
| info() { |
| return this.isHot ? '炎热' : '凉爽' |
| } |
| }, |
| methods: { |
| changeWeather() { |
| this.isHot = !this.isHot |
| } |
| }, |
| watch:{ |
| isHot: { |
| |
| |
| handler(newValue, oldValue){ |
| console.log('newValue:', newValue); |
| console.log('oldValue:', oldValue); |
| }, |
| }, |
| |
| 'numbers.a':{ |
| |
| handler(){ |
| console.log('a被改变了'); |
| }, |
| }, |
| |
| |
| numbers: { |
| deep: true, |
| handler() { |
| console.log('a被改变了'); |
| }, |
| } |
| |
| |
| vm.$watch('isHot',{ |
| |
| }) |
| } |
| }) |
| </script> |
复制
computed 和 watch 之间的区别
- computed能完成的功能,watch都可以完成
- watch能完成的功能,computed不一定能完成,例如:watch可以进行异步操作
两个重要的小原则: - 所被Vue管理的函数,最好写成普通函数,这样this的指向才是vm 或 组件实例对象
- 所有不被Vue所管理的函数(定时器的回调函数、ajax的回调函数等),最好写成箭头函数,这样this的指向才是vm 或 组件实例对象
绑定样式(class style)
- class样式
写法:class=“xxx” xxx可以是字符串、对象、数组
字符串写法适用于:类名不确定,要动态获取
对象写法适用于:要绑定多个样式,个数不确定,名字也不确定
数组写法适用于:要绑定多个样式,个数确定,名字也确定,但不确定用不用 - style样式
:style=“{fontSize:xxx}” 其中xxx是动态值
:style=“[a,b]” 其中a、b是样式对象
| <div id="root"> |
| |
| <div class="basic" :class="mood" @click="changeMood">{{name}}</div><br><br> |
| |
| <div class="basic" :class="arr" @click="changeMood">{{name}}</div> <br><br> |
| |
| <div class="basic" :class="arr" @click="changeMood">{{name}}</div><br><br> |
| |
| <div class="basic" :style="styleObj">{{name}}</div><br><br> |
| |
| <div class="basic" :style="styleArr">{{name}}</div> |
| </div> |
| |
| <script type="text/javascript"> |
| Vue.config.productionTip = false; |
| |
| new Vue({ |
| el: '#root', |
| data: { |
| name: '檀健次', |
| mood: 'normal', |
| arr: ['attjc1', 'attjc2', 'attjc3'], |
| classObj: { |
| attjc1: false, |
| attjc2: true, |
| attjc3: true |
| }, |
| styleObj: { |
| color: 'white', |
| backgroundColor: 'orange' |
| }, |
| styleArr: [ |
| { |
| fontSize: '30px' |
| } |
| ] |
| }, |
| methods: { |
| changeMood() { |
| |
| const arr = ['happy', 'sad', 'normal'] |
| this.mood = arr[Math.floor(Math.random() * arr.length)] |
| } |
| } |
| }) |
| </script> |
复制
条件渲染
- v-if
写法:
(1) v-if=“表达式”
(2) v-else-if=“表达式”
(3) v-else=“表达式”
适用于:切换频率较低的场景
特点:不展示的DOM元素直接被移除
注意:v-if可以和:v-else-is、v-else一起使用,但要求结构不能被打断 - v-show
写法:v-show=“表达式”
适用于:切换频率较高的场景
特点:不展示的DOM元素未被移除,仅仅是使用样式隐藏掉 - 备注:使用v-if的时候,元素可能无法获取到,而使用v-show一定可以获取到
| <div id="root"> |
| <h2>当前值是:{{n}}</h2> |
| <button @click="n++">点我n+1</button> |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| <div v-if="n === 1">tjc</div> |
| <div v-else-if="n === 2">txd</div> |
| <div v-else-if="n === 3">tlq</div> |
| <div v-else>哈哈</div> |
| </div> |
| |
| <script type="text/javascript"> |
| Vue.config.productionTip = false; |
| |
| new Vue({ |
| el: '#root', |
| data: { |
| name: '檀健次', |
| n: 1 |
| } |
| }) |
| </script> |
复制
列表渲染
基本列表
v-for指令
- 用于展示列表数据
- 语法:v-for=“(item,index) in xxx” :key=“yyy”
- 可遍历:数组、对象、字符串(用的很少)、指定次数(用的很少)
| <div id="root"> |
| |
| <h2>人员列表(遍历数组)</h2> |
| <ul> |
| <li v-for="(p,index) in persons" :key="index"> |
| {{p.name}}--{{p.age}} |
| </li> |
| </ul> |
| |
| <h2>汽车信息</h2> |
| <ul> |
| <li v-for="(value,k) of car" :key="k"> |
| {{value}}--{{k}} |
| </li> |
| </ul> |
| |
| <h2>循环指定次数</h2> |
| <ul> |
| <li v-for="n in 5" :key="n"> |
| {{n}} |
| </li> |
| </ul> |
| </div> |
| |
| <script type="text/javascript"> |
| Vue.config.productionTip = false; |
| |
| new Vue({ |
| el: '#root', |
| data: { |
| persons:[ |
| {id: '001',name: '张三',age: 18} |
| ,{id: '002',name: '李四',age: 19} |
| ,{id: '003',name: '王五',age: 20} |
| ], |
| car:{ |
| name: '奔驰', |
| price: '100万', |
| color: '黑色' |
| } |
| } |
| }) |
| </script> |
复制
key的原理

- 虚拟DOM中key的作用:
key是虚拟DOM对象的标识,当数据发生变化时,Vue会根据【新数据】生成【新的虚拟DOM】 ,随后Vue进行【新虚拟DOM】与【旧DOM】的差异比较,比较规则如下: - 对比规则:
a. 旧虚拟DOM中内容没变,直接使用之前的真实DOM
ⅰ. 若虚拟DOM中内容没变,直接使用之前的真实DOM
ⅱ. 若虚拟DOM中内容变了,则生成新的真实DOM,随后替换掉页面中之前的真实DOM
b. 旧虚拟DOM中未找到与新虚拟DOM相同key,创建新的真实DOM,随后渲染到页面 - 用index作为key可能会引发的问题:
a. 若对数据进行:逆序添加、逆序删除等破坏顺序操作:
b. 如果结构中还包含输入类DOM:会产生错误DOM更新==> 界面有问题 - 开发中如何选择key?:
a. 最好使用每条数据的唯一标识作为key,比如id、手机号、身份证号、学号等唯一值
b. 如果不存在对数据的逆序添加、逆序删除等破坏顺序的操作,仅用于渲染列表用于展示,使用index作为key是没有问题的
列表过滤
| <div id="root"> |
| |
| <h2>人员列表</h2> |
| <input type="text" placeholder="请输入名字" name="" id="" v-model="keyWord"> |
| <ul> |
| <li v-for="(p,index) in filPersons" :key="index"> |
| {{p.name}}--{{p.age}} |
| </li> |
| </ul> |
| </div> |
| |
| <script type="text/javascript"> |
| Vue.config.productionTip = false; |
| |
| |
| new Vue({ |
| el: '#root', |
| data: { |
| keyWord: '', |
| persons: [ |
| { id: '001', name: '檀健次', age: 18 } |
| , { id: '002', name: '赵丽颖', age: 19 } |
| , { id: '003', name: '成毅', age: 20 } |
| ], |
| filPersons: [] |
| }, |
| watch: { |
| keyWord: { |
| immediate: true, |
| handler(val) { |
| this.filPersons = this.persons.filter((p) => { |
| return p.name.indexOf(val) !== -1 |
| }) |
| } |
| } |
| } |
| }) |
| |
| |
| new Vue({ |
| el: '#root', |
| data: { |
| keyWord: '', |
| persons: [ |
| { id: '001', name: '檀健次', age: 18 } |
| , { id: '002', name: '赵丽颖', age: 19 } |
| , { id: '003', name: '成毅', age: 20 } |
| ] |
| }, |
| computed:{ |
| filPersons(){ |
| return this.persons.filter((p) => { |
| return p.name.indexOf(this.keyWord) !== -1 |
| }) |
| } |
| } |
| }) |
| </script> |
复制
列表排序
| <div id="root"> |
| |
| <h2>人员列表</h2> |
| <input type="text" placeholder="请输入名字" name="" id="" 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}} |
| </li> |
| </ul> |
| </div> |
| |
| <script type="text/javascript"> |
| Vue.config.productionTip = false; |
| |
| new Vue({ |
| el: '#root', |
| data: { |
| keyWord: '', |
| sortType: 0, |
| persons: [ |
| { id: '001', name: '檀健次', age: 18 } |
| , { id: '002', name: '檀小呆', age: 2 } |
| , { id: '003', name: '赵丽颖', age: 19 } |
| , { id: '004', name: '成毅', age: 20 } |
| , { id: '005', name: '虞书欣', age: 20 } |
| ] |
| }, |
| computed: { |
| filPersons() { |
| const arr = this.persons.filter((p) => { |
| return p.name.indexOf(this.keyWord) !== -1 |
| }) |
| if (this.sortType) { |
| arr.sort((a, b) => { |
| return this.sortType === 1 ? b.age - a.age : a.age - b.age |
| }) |
| } |
| return arr |
| } |
| } |
| }) |
| </script> |
复制

- 如何监测数组中的数据
数组更新检测
通过包裹数组更新元素的方法实现,本质就是做了两件事:
a. 调用原生对应的方法对数组进行更新
b. 重新解析模板,进而更新页面
没有get、set方法,不能直接操作数组,通过索引值等修改,Vue不起效
可通过七个方法修改:
1). push 向数组的末尾添加一个或多个元素,并返回新的长度
2). pop 向数组的末尾添加一个或多个元素,并返回新的长度
3). shift 用于把数组的第一个元素从其中删除,并返回第一个元素的值。改变数组的长度
4). unshift 向数组的开头添加一个或更多元素,并返回新的长度。该方法将改变数组的数目
5). splice 用于添加或删除数组中的元素
6). sort 用于对数组的元素进行排序。排序顺序可以是字母或数字,并按升序或降序。默认排序顺序为按字母升序
注意 使用数字排序,你必须通过一个函数作为参数来调用。函数指定数字是按照升序还是降序排列。
7). reverse 颠倒数组中元素的顺序
原理:上述七个方法和原来数组的七个方法有所不同,是Vue继承后重新包装数组中长用的方法

如果不是用上述方法,也可以用Vue.set()和vm.$set()

- Vue.set()
返回值:设置的值
用法:
像响应式对象中添加一个property并确保这个新property同样是响应式的,且触发视图更新。它必须用于响应式对象上添加新property,因为Vue无法探测普通的新增property(比如 this.myObject.newProperty = 'hi)
注意:对象不能是 Vue 实例,或者 Vue 实例的根数据对象。
| <div id="root"> |
| <h1>学校信息</h1> |
| <h2>学校名称:{{name}}</h2> |
| <h2>学校地址:{{address}}</h2> |
| <hr> |
| <h1>学生信息</h1> |
| <button @click="addSex">添加一个性别属性</button> |
| <h2>姓名:{{student.name}}</h2> |
| <h2 v-if="student.sex">性别:{{student.sex}}</h2> |
| <h2>年龄:真实{{student.age.rAge}},对外{{student.age.sAge}}</h2> |
| <h2>朋友</h2> |
| <ul> |
| <li v-for="(f,index) in student.friends" :key="index"> |
| {{f.name}}--{{f.age}} |
| </li> |
| </ul> |
| </div> |
| |
| <script type="text/javascript"> |
| Vue.config.productionTip = false; |
| |
| new Vue({ |
| el: '#root', |
| data: { |
| name: '河南科技学院', |
| address: '新乡', |
| student: { |
| name: '张三', |
| |
| age: { |
| rAge: 18, |
| sAge: 19 |
| }, |
| friends: [ |
| { name: '李四', age: 20 }, |
| { name: '王五', age: 21 }, |
| { name: '赵六', age: 22 } |
| ] |
| } |
| }, |
| methods: { |
| addSex(){ |
| Vue.set(this.student, 'sex','女' ) |
| } |
| } |
| }) |
| </script> |
复制
- 数据劫持
指的是在访问或者修改对象的某个属性时,通过一段代码拦截这个行为,进行额外的操作或者修改返回结果。
比较典型的是Object.defineProperty()和 ES2016 中新增的Proxy对象。数据劫持最著名的应用当属双向绑定,这也是一个已经被讨论烂了的面试必考题。例如 Vue 2.x 使用的是Object.defineProperty()(Vue 在 3.x 版本之后改用 Proxy 进行实现)。 - 大合集
| <div id="root"> |
| <h1>学校信息</h1> |
| <button @click="student.age++">年龄+1岁</button><br> |
| <button @click="addSex">添加性别属性,默认值:女</button><br> |
| <button @click="student.sex = '未知'">修改性别</button><br> |
| <button @click="addFriend">在列表首位添加朋友</button><br> |
| <button @click="updateFriendName">修改第一个朋友的名字为:张三</button><br> |
| <button @click="updateHobby">修改第一个爱好为:开车</button><br> |
| |
| |
| <h2>学校名称:{{name}}</h2> |
| <h2>学校地址:{{address}}</h2> |
| <hr> |
| <h1>学生信息</h1> |
| <h2>姓名:{{student.name}}</h2> |
| <h2>年龄:{{student.age}}</h2> |
| <h2 v-if="student.sex">性别:{{student.sex}}</h2> |
| <ul> |
| <li v-for="item in student.hobby" :key="item"> |
| {{item}} |
| </li> |
| </ul> |
| <h2>朋友</h2> |
| <ul> |
| <li v-for="(f,index) in student.friends" :key="index"> |
| {{f.name}}--{{f.age}} |
| </li> |
| </ul> |
| </div> |
| |
| <script type="text/javascript"> |
| Vue.config.productionTip = false; |
| |
| const vm = new Vue({ |
| el: '#root', |
| data: { |
| name: '河南科技学院', |
| address: '新乡', |
| student: { |
| name: '张三', |
| |
| age: 18, |
| hobby: ['吃饭', '睡觉', '打游戏'], |
| friends: [ |
| { name: '李四', age: 20 }, |
| { name: '王五', age: 21 }, |
| { name: '赵六', age: 22 } |
| ] |
| } |
| }, |
| methods: { |
| addSex(){ |
| Vue.set(this.student, 'sex','女' ) |
| }, |
| addFriend(){ |
| this.student.friends.unshift({name:'张三',age:18}) |
| }, |
| updateFriendName(){ |
| this.student.friends[0].name = '张三' |
| }, |
| updateHobby(){ |
| this.student.hobby.splice(0,1,'开车') |
| |
| } |
| } |
| }) |
| </script> |
复制
收集表单中的数据 v-model
- ,则v-model收集的是value值,用户输入的就是value值
- ,则v-model收集的是value值,且要给标签配置value值
-
a. 没有配置input的value属性,那么收集的就是checked(勾选 or 未勾选,是布尔值)
b. 配置input的value属性:
ⅰ. v-model的初始值是非数组,那么收集的就是checked(勾选 or 未勾选,是布尔值)
ⅱ. v-model的初始值是数组,那么收集的就是value组成的数组
备注:v-model的三个修饰符:
lazy:失去焦点再收集数据
number:输入字符串转为有效的数字
trim:输入首尾空格过滤
| <body> |
| <div id="root"> |
| <form> |
| 账号:<input type="text" v-model.trim="username"><br></br> |
| 密码:<input type="password" v-model="password"><br></br> |
| 年龄:<input type="number" v-model.number="age"><br><br> |
| 性别: |
| <input type="radio" name="sex" v-model="sex" value="男">男 |
| <input type="radio" name="sex" v-model="sex" value="女">女 |
| <br><br> |
| 爱好: |
| <input type="checkbox" v-model="hobby" value="吃饭">吃饭 |
| <input type="checkbox" v-model="hobby" value="睡觉">睡觉 |
| <input type="checkbox" v-model="hobby" value="打游戏">打游戏 |
| <br><br> |
| 所属校区: |
| <select name="" id=""> |
| <option value="">请选择校区</option> |
| |
| <option value="beijing">北京</option> |
| <option value="shanghai">上海</option> |
| <option value="guangzhou">广州</option> |
| <option value="shenzhen">深圳</option> |
| </select><br><br> |
| <button @click.prevent="submit">提交</button> |
| </form> |
| </div> |
| </body> |
| |
| <script type="text/javascript"> |
| Vue.config.productionTip = false; |
| |
| const vm = new Vue({ |
| el: '#root', |
| data: { |
| username: '', |
| password: '', |
| age: '', |
| sex: '', |
| hobby: [], |
| citys: 'beijing' |
| }, |
| methods: { |
| submit() { |
| console.log(this.username, this.password, this.sex, this.hobby); |
| } |
| } |
| }) |
| </script> |
复制
过滤器(Vue3已移除)
定义:对要显示的数据进行特定格式化后再显示(适用于一些简单逻辑的处理)
语法:
- 注册过滤器:Vue.filter(name,callback) 或 new Vue { filters:{} }
- 使用过滤器:{{xxx | 过滤器名}} 或 v-bind:属性 = “xxx | 过滤器名”
备注: - 过滤器也可以接收额外参数、多个过滤器也可以串联
- 并没有改变原本的数据,是产生新的对应的数据