目录
v-for更新监测
目标: 当v-for遍历的目标结构改变, Vue触发v-for的更新
case1: 数组翻转
case2: 数组截取
case3: 更新值
口诀:
改变数组方法, 就会导致v-for更新, 页面更新;
改变数组方法:
pop( 删除数组的最后一个元素)
push(在尾部添加元素,返回的是更新后数组的长度)
shift(删除数组的第一个元素)
unshift(数组第一位元素前面添加元素,返回更新后数组的长度)
splice(替换/添加/删除)
根据不同的参数,有不同的功能:
eg1:删除功能;根据索引删除
用法:array.splice(index,1)
eg2:添加功能:['插入位置',0,’插入的项‘]
用法:array.splice(index,0,insertValue)
eg3:替换功能 ['起始位置',“删除的项数”,’插入的项‘]
用法:array.splice(index,num,insertValue)
reverse(翻转)
sort(排序)按字符ASCII进行排序
非改变数组方法, 返回新数组, 就不会导致v-for更新, 可采用覆盖数组或this.$set()
<template>
<div>
<ul>
<li v-for="(item, index) in arr" :key="index">
{{ item }}
</li>
</ul>
<button @click="fn">reverse</button>
<button @click="fn1">slice</button>
<button @click="update">update</button>
</div>
</template>
<script>
export default {
data() {
return {
arr: [1, 2, 3, 4, 5, 6, 7, 8, 9],
}
},
methods: {
fn() {
//数组反转改变原数组,触发v-for更新
this.arr.reverse()
},
fn1() {
//slice不会触发v-for更新,没有改变原数组
const res = this.arr.slice(1, 3)
this.arr = res
},
update() {
//索引改变数组,不能触发页面刷新,vue是检测不到的
this.arr[0] = 66
//参数1:改变的目标
//参数2:要更新的位置(对于数组就是下标,对于对象就是key)
//参数3:要更新的值
this.$set(this.arr, 0, 666)
},
},
}
</script>
虚拟dom
.vue文件中的template里写的标签, 都是模板, 都要被vue处理成虚拟DOM对象, 才会渲染显示到真实DOM页面上
-
内存中生成一样的虚拟DOM结构(本质是个JS对象)因为真实的DOM属性好几百个, 没办法快速的知道哪个属性改变了比如template里标签结构:
<template>
<div id="box">
<p class="my_p">123</p>
</div>
</template>
对应的虚拟DOM结构
const dom = {
type: 'div',
attributes: [{id: 'box'}],
children: {
type: 'p',
attributes: [{class: 'my_p'}],
text: '123'
}
}
-
vue数据更新
-
生成新的虚拟DOM结构
-
和旧的虚拟DOM结构对比
-
利用diff算法, 找不同, 只更新变化的部分(重绘/回流)到页面 , 也叫打补丁
-
好处1: 提高了更新DOM的性能(不用把页面全删除重新渲染)
好处2: 虚拟DOM只包含必要的属性(没有真实DOM上百个属性)
总结: 虚拟DOM保存在内存中, 只记录dom关键信息, 配合diff算法提高DOM更新的性能
在内存中比较差异, 然后给真实DOM打补丁更新上
diff算法
情况1: 根元素变了, 删除重建
情况2: 根元素没变, 属性改变, ==元素复用==, 更新属性
diff算法-key
情况3: 根元素没变, 子元素没变, 元素内容改变
无key - 就地更新
v-for不会移动DOM, 而是尝试复用, 就地更新,如果需要v-for移动DOM, 你需要用特殊 attribute key
来提供一个排序提示
注意:性能不高, 从第二个li往后都更新了
有key - 值为索引
-
还是就地更新
因为新旧虚拟DOM对比, key存在就复用此标签更新内容, 如果不存在就直接建立一个新的
-
v-for先循环产生新的DOM结构, key是连续的, 和数据对应
-
然后比较新旧DOM结构, 找到区别, 打补丁到页面上;最后补一个li, 然后从第二个往后, 都要更新内容
有key - 值为id(常用)
key的值只能是唯一不重复的, 字符串或数值
v-for不会移动DOM, 而是尝试复用, 就地更新,如果需要v-for移动DOM, 你需要用特殊 attribute key
来提供一个排序提示
新DOM里数据的key存在, 去旧的虚拟DOM结构里找到key标记的标签, 复用标签
新DOM里数据的key存在, 去旧的虚拟DOM结构里没有找到key标签的标签, 创建
旧DOM结构的key, 在新的DOM结构里没有了, 则移除key所在的标签
<template>
<div>
<!-- :key="item.id"使用key作为唯一表示,
(一般选取一个不会改变的属性,index下标会改变,所以这里用id不会改变)
最大限度的复用原来的标签,提高性能 -->
<li v-for="item in userList" :key="item.id">
{{ item.name }}<input type="text" />
</li>
<button @click="add">索引为1地方添加元素</button>
</div>
</template>
注意:不用key也不影响功能(就地更新), 添加key可以提高更新的性能
动态class
目标: 用v-bind给标签class设置动态的值
-
语法:
-
:class="{类名: 布尔值}"
-
-
使用场景: vue变量控制标签是否应该有类名
<template>
<div>
<h2 :class="{ color: bool ,size:bool1}">you see</h2>
</div>
</template>
<script>
export default {
data() {
return {
bool: true,
bool1: true,
}
},
}
</script>
<style>
.color {
color: aquamarine;
}
</style>
动态style
语法
-
:style="{css属性: 值}"
-
动态style的key都是css属性名
<template>
<div>
<h2 :style="{ backgroundColor: color, fontSize: size ,color:'white'}">
我与春风都是过客
</h2>
</div>
</template>
<script>
export default {
data() {
return {
color: 'purple',
size: '30px',
}
},
}
</script>
vue过滤器
目的: 转换格式, 过滤器就是一个函数, 传入值返回处理后的值
过滤器只能用在, 插值表达式和v-bind表达式
Vue中的过滤器场景
-
字母转大写, 输入"hello", 输出"HELLO"
-
字符串翻转, "输入hello, world", 输出"dlrow ,olleh"
语法:
-
全局过滤器:Vue.filter("过滤器名", (值) => {return "返回处理后的值"})
-
局部过滤器:filters: {过滤器名字: (值) => {return "返回处理后的值"}只在当前页面可以使用
例子:
-
局部定义字母都大写的过滤器
-
局部定义字符串翻转的过滤器
<template>
<div>
<p>原来的样子: {{ msg }}</p>
<!-- 2. 过滤器使用
语法: {{ 值 | 过滤器名字 }}
-->
<p>使用翻转过滤器: {{ msg | reverse }}</p>
<p :title="msg | toUp">鼠标长停</p>
</div>
</template>
<script>
export default {
data(){
return {
msg: 'Hello, Vue'
}
},
// 方式2: 局部 - 过滤器
// 只能在当前vue文件内使用
/*
语法:
filters: {
过滤器名字 (val) {
return 处理后的值
}
}
*/
filters: {
toUp (val) {
return val.toUpperCase()
}
}
}
</script>
注意:全局过滤器需要在main.js里面注册全局过滤器
Vue.filter('reverse', (val, s = '') => {
return val.split('').reverse().join(s)
})
总结:把值转成另一种形式, 使用过滤器, Vue3用函数替代了过滤器.
全局注册最好在main.js中注册, 一处注册到处使用
传参和多过滤器
语法:
-
过滤器传参: vue变量 | 过滤器(实参)
-
多个过滤器: vue变量 | 过滤器1 | 过滤器2
// 方式1: 全局 - 过滤器
// 任意的.vue文件内"直接"使用
// 语法: Vue.filter("过滤器名", 值 => 处理结果)
// Vue.filter("reverse", val => val.split("").reverse().join(""))// 过滤器接参数
Vue.filter("reverse", (val, s) => {
return val.split("").reverse().join(s)
})
<template>
<div>
<p>原来的样子: {{ msg }}</p>
<!-- 1.
给过滤器传值
语法: vue变量 | 过滤器名(值)
-->
<p>使用翻转过滤器: {{ msg | reverse('|') }}</p>
<!-- 2.
多个过滤利使用
语法: vue变量 | 过滤器1 | 过滤器2
-->
<p :title="msg | toUp | reverse('|')">鼠标长停</p>
</div>
</template>
<script>
export default {
data(){
return {
msg: 'Hello, Vue'
}
},
filters: {
toUp (val) {
return val.toUpperCase()
}
}
}
</script>
定义过滤器, 把时间用moment模块格式化, 返回我们想要的格式
// 1. 下载moment模块
import moment from 'moment'
// 2. 定义过滤器, 编写内部代码
filters: {
formatDate (val){
return moment(val).format('YYYY-MM-DD')
}
}<!-- 3. 使用过滤器 -->
<td>{{ obj.time | formatDate }}</td>
vue计算属性-computed
语法:computed: {
"计算属性名" () {
return "值"
}
}
- computed有缓存,只要依赖项没有改变,就不会重新计算
- 计算属性优势:有缓存。依赖项不变不会重新计算,
- 依赖性变化计算出新的值重新缓存
场景: 一个变量的值, 需要用另外变量计算而得来
vue计算属性-完整写法
computed: {
"属性名": {
set(值){
},
get() {
return "值"
}
}
}
<template>
<div>
<h1 @click="fn">{{ fullname }}</h1>
<h3 @click="fn">{{ fullname }}</h3>
<h2>{{ getfullname() }}</h2>
<h3>{{ getfullname() }}</h3>
<button @click="changefir">change</button>
</div>
</template>
<script>
export default {
data() {
return {
firstname: 'gui',
lastname: 'gui',
}
},
computed: {
fullName: {
set(val) {
// 不能直接改变fullName的值,但是可以通过fullName的依赖项
// 间接改变fullName
this.firstName = val.split('-')[0]
this.lastName = val.split('-')[1]
},
get() {
return this.firstName + '-' + this.lastName
},
},
},
// computed: {
// fullname() {
// //computed有缓存,只要依赖项没有改变,就不会重新计算
// //计算属性优势:有缓存。依赖项不变不会重新计算,
// //依赖性变化计算出新的值重新缓存
// //只要firstname、lastname发生改变 ,fullname会重新计算
// set(val){
// // 不能直接改变fullName的值,但是可以通过fullName的依赖项
// // 间接改变fullName
// this.firstName = val.split('-')[0]
// this.lastName = val.split('-')[1]
// },
// get(){
// return this.firstName + '-' + this.lastName
// },
// }
// },
methods: {
//指向当前组件实例对象
fn() {
console.log(this)
},
getfullname() {
console.log('getfullname')
return this.firstname + ' の' + this.lastname
},
changefir() {
this.firstname = '11'
},
},
}
</script>
<style></style>
vue侦听器-watch
目标: 可以侦听data/computed属性值改变
语法:
watch: {
"被侦听的属性名" (newVal, oldVal){
}
}
vue侦听器-深度侦听和立即执行
目标: 侦听复杂类型, 或者立即执行侦听函数
语法
watch: {
"要侦听的属性名": {
immediate: true, // 立即执行
deep: true, // 深度侦听复杂类型内变化
handler (newVal, oldVal) {
}
}
}
<template>
<div>
<input type="text " v-model="uname" />
{{ names }}
</div>
</template>
<script>
export default {
data() {
return {
uname: 'harry',
names: '鬼师',
age: 20,
user: {
age: 13,
sex: '男',
},
}
},
watch: {
//key是要监听的属性
//newvalue当前最新值
//oldvalue旧的值
//当被监听的属性发生变化,就会触发此函数
uname(newvalue, oldvalue) {
console.log(newvalue, oldvalue)
},
age(newvalue, oldvalue) {
if (newvalue > 25) {
//当新的age大于25岁,names就会变为kun
this.names = 'kun'
}
},
user: {
//监听属性完整写法
immediate: true, //一进入页面立马执行侦听函数
deep: true, //深度监听复杂数据类型内部变化
//属性发生变化时要执行的函数
handler(newvalue, oldvalue) {
console.log('change')
},
},
},
}
</script>
<style></style>