v-once
用于指定元素或者组件只渲染一次,一般可用作性能优化
<div id="app">
<!-- 指令: v-once 点击事件后我的子节点内容都没变-->
<h2 v-once>
{{ message }}
<span>数字: {{counter}}</span>
</h2>
<!-- 点击事件后我会变 -->
<h1>{{message}}</h1>
<button @click="changeMessage">改变message</button>
</div>
<script src="https://unpkg.com/vue@next"></script>
<script>
// 1.创建app
const app = Vue.createApp({
data: function () {
return {
message: "Hello Vue",
counter: 100,
};
},
methods: {
changeMessage: function () {
this.message = "你好";
this.counter += 100;
console.log(this.message, this.counter);
},
},
});
// 2.挂载app
app.mount("#app");
</script>
v-model
-
主要应用于表单元素或者组件上;
-
本质:value 属性和 input 事件的一个合体;
-
修饰符:
- v-model.lazy:没有 lazy 使用的是 input 事件,使用了 lazy 使用的是 change 事件;
- v-model.number:将输入的内容转换成数字;
- v-model.trim:自动去掉首位空格;
-
v-model 原理:内部会根据标签的不同解析出不同的语法,例如:
- 文本框会被解析成 value + input 事件;
- 复选框会被解析成 checked + change 事件;
- …
v-text
-
v-text 能展示对应 data 中数据内容,也能在数据基础上做运算;
-
v-text 会把标签中的内容替换;
<div id="app">
<h2>aa {{message}} bbb</h2>
<!-- 内容都替换了 -->
<h2 v-text="message">aaa</h2>
<!-- 数据基础上做运算 -->
<h2 v-text="message+'zzzzzz'"></h2>
</div>
<script src="https://unpkg.com/vue@next"></script>
<script>
// 1.创建app
const app = Vue.createApp({
data: function () {
return {
message: "Hello Vue",
};
},
});
// 2.挂载app
app.mount("#app");
</script>
v-html
等同于原生的 innerHtml (一旦使用,后边跟的内容一定是可信任的内容)
<div id="app">
<h2>{{ content }}</h2>
<h2 v-html="content"></h2>
</div>
<script src="https://unpkg.com/vue@next"></script>
<script>
// 1.创建app
const app = Vue.createApp({
data: function () {
return {
content: `<span style="color: red; font-size: 30px;">哈哈哈</span>`,
};
},
});
// 2.挂载app
app.mount("#app");
</script>
v-cloak
-
这个指令使用在元素上直到关联实例结束编译;
-
因为指令生命周期的特殊性,常备用来解决大胡子语法闪烁问题;
-
这个指令可以隐藏未编译的 Mustache 标签直到实例准备完毕,和 CSS 规则如 [v-cloak] { display: none } 一起用时;
<style>
[v-cloak] {
display: none;
}
</style>
<div id="app">
<h2 v-cloak>{{message}}</h2>
</div>
<script src="https://unpkg.com/vue@next"></script>
<script>
setTimeout(() => {
// 1.创建app
const app = Vue.createApp({
data: function () {
return {
message: "Hello Vue",
};
},
});
// 2.挂载app
app.mount("#app");
}, 3000);
</script>
v-pre
-
优化型指令,当代码中出现了一个结构中不包含 vue 变量,则不需要 vue 编译,浪费性能,则加上 v-pre,不用 vue 解析;
-
v-pre 用于跳过元素和它的子元素的编译过程,显示原始的 Mustache 标签;
<div id="app">
<div v-pre>
<h2>{{ message }}</h2>
<p>当前计数: {{ counter }}</p>
<p>{{}}</p>
</div>
</div>
<script src="https://unpkg.com/vue@next"></script>
<script>
// 1.创建app
const app = Vue.createApp({
data: function () {
return {
message: "Hello Vue",
counter: 0,
};
},
});
// 2.挂载app
app.mount("#app");
</script>
v-memo(新)
-
数组里的每个值都与最后一次的渲染相同,那么整个子树的更新将被跳过;
-
下面案例中点击改变信息,内容不会更改,因为 v-memo 中 name,age 没有发生改变,因此这个节点中即使 height 改变了也不会渲染视图;
<div id="app">
<div v-memo="[name, age]">
<h2>姓名: {{ name }}</h2>
<h2>年龄: {{ age }}</h2>
<h2>身高: {{ height }}</h2>
</div>
<button @click="updateInfo">改变信息</button>
</div>
<script src="https://unpkg.com/vue@next"></script>
<script>
// 1.创建app
const app = Vue.createApp({
data: function () {
return {
name: "www",
age: 18,
height: 1.88,
};
},
methods: {
updateInfo: function () {
this.name = "www";
this.age = 18;
this.height = 10000000;
},
},
});
// 2.挂载app
app.mount("#app");
</script>
v-if
-
vue2.x 中:v-for 优先级大于 v-if,vue3.x 中:v-if 的优先级大于 v-for;
-
有比较大的切换开销,有比较小的初始加载开销 (条件渲染,如果 if 的表达式是 true,则会插入到虚拟 dom 中);
v-show
-
v-show 只是单纯的控制 display 样式的显示或隐藏;
-
有比较大的初始加载开销,比较小的切换开销 (无论条件是 true 还是 false,始终会插入到虚拟 dom 中);
v-for
-
语法上 v-for 指令需要使用 item in items 形式的特殊语法,items 是源数据数组并且 item 是数组元素迭代的别名;
-
支持循环 Array、Object、number、string (也可以遍历其他 可迭代对象 (Iterable));
<div v-for="(item, index) in items"></div>
<div v-for="(val, key) in object"></div>
<div v-for="(val, key, index) in object"></div>
<div v-for="item in 10"></div> <!--注意循环数字是从1开始->
<div id="app">
<p v-for="item in createIterator">{{item}}</p>
</div>
<script src="https://unpkg.com/vue@next"></script>
<script>
// 1.创建app
const app = Vue.createApp({
data: function () {
return {
createIterator: {
items: [1, 2, 3],
*[Symbol.iterator]() {
for (let item of this.items) {
yield item;
}
},
},
};
},
});
// 2.挂载app
app.mount("#app");
</script>
v-key
最理想的 key 就是这条数据的唯一 id,可以提升虚拟 dom 的编译效率;通常与 v-for 或者表单一起使用;
v-bind
-
动态 key:和 es6 一样在 vue 模板时候也可以动态 key 去和 v-bind 进行绑定;
<button v-bind:[key]="value"></button> <h2 :[name]="'aaaa'">Hello World</h2> <img :src="imageSrc" />
-
展开对象:可以将一组属性定义为对象;
<div v-bind="{ id: someProp, 'other-attr': otherProp }"></div>
-
class:还支持下面三种形式 ‘对象’,‘数组’,‘数组对象’;
<!--动态绑定的 class 是可以和普通的 class 同时的使用 --> <div :class="{ red: isRed, active: isRed, zz: true }"></div> <div :class="[classA, classB,"zz"]"></div> <div :class="[classA, { classB: isB, classC: isC }]"></div> <h2 :class="['abc', className, isActive? 'active': '']"></h2>
-
style:除了正常写法也支持 ‘对象’ 和 ‘数组’,要注意如果 key 是类似 ‘font-size’ 属性需要驼峰写法,如果不驼峰写法需要用引号包裹;
<div :style="{ fontSize: size + 'px' }"></div> <div :style="[styleObjectA, styleObjectB, { backgroundColor: 'purple' }]"></div>
v-on
-
事件处理器的值可以有两种表现形式,内联事件处理器 和 方法事件处理器,是专门用来处理事件的指令,语法糖:v-on 简写为
@
;- 内联事件处理器:事件被触发时执行的内联 JavaScript 语句 (最通俗理解,调用方法时候有括号,直接使用以声明属性) 实际上是将其执行的是一段 js 代码;
- 方法事件处理器:一个指向组件上定义的方法的属性名或是路径 (简单理解直接方法名字);
-
常见使用
<!-- 动态事件 --> <button v-on:[event]="doThis"></button> <!-- 动态事件缩写 --> <button @[event]="doThis"></button> <!-- 对象语法 --> <button v-on="{ mousedown: doThis, mouseup: doThat }"></button> <!-- 一个事件绑定多个方法 --> <button @click="one($event), two($event)">Submit</button>
-
关于 event:在 vue 中使用 也可以将 event 对象会传递进来,在 内联事件处理器 和 方法事件处理器 传递形式不同;
<div id="app"> <!-- 1.默认传递event对象 --> <button @click="btn1Click">按钮1</button> <!-- 2.自己的参数和event对象 --> <!-- 在模板中想要明确的获取event对象: $event --> <button @click="btn3Click('zzz', $event)">按钮3</button> </div> <script src="https://unpkg.com/vue@next"></script> <script> // 1.创建app const app = Vue.createApp({ methods: { // 1.默认参数: event对象 // 总结: 如果在绑定事件的时候, 没有传递任何的参数, 那么event对象会被默认传递进来 btn1Click(event) { console.log('btn1Click:', event) }, // 2.明确参数+event对象 btn3Click(name, event) { console.log('btn3Click:', name, event) }, }, }); // 2.挂载app app.mount("#app"); </script>
-
事件修饰符:
- .stop:调用 event.stopPropagation() 阻止事件冒泡;
- .prevent:调用 event.preventDefault() 阻止默认事件从里到外;
- .capture:添加事件侦听器时使用 capture 模式,实现捕获触发事件的机制从外到里;
- .self:只会阻止自己身上的行为,使用修饰符时,顺序很重要 (用 @click.prevent.self 会阻止元素本身及其子元素的点击的默认行为,而 @click.self.prevent 只会阻止对元素自身的点击的默认行为);
- .{keyAlias}:仅当事件是从特定键触发时才触发回调 (键盘简写);
- .once:只触发一次回调;
- .left:只当点击鼠标左键时触发;
- .right:只当点击鼠标右键时触发;
- .middle:只当点击鼠标中键时触发;
- .passive:{ passive: true } 模式添加侦听器;
面试题
v-if 和 v-for 哪个优先级更高
-
在 Vue2 中 v-for 优先于 v-if 被解析;但在 Vue3 中则完全相反,v-if 的优先级高于 v-for;
-
文档中明确指出永远不要把 v-if 和 v-for 同时用在同一个元素上,工程项目中也不应该把它们放一起,因为哪怕只渲染列表中一小部分元素,也得在每次重渲染的时候遍历整个列表;
-
通常有两种情况下导致我们这样做:
- 为了过滤列表中的项目 (比如 v-for=“user in users” v-if=“user.isActive”),建议直接返回过滤后的列表再去渲染列表;
- 为了避免渲染本应该被隐藏的列表 (比如 v-for=“user in users” v-if=“isShowUsers”),建议把 v-if 放到外面一层标签上;
-
vue2 输出的渲染函数:可以看出会先执行循环再判断条件
HTMLJavaScript<div id="app"> <!-- 过滤列表中项目 --> <div v-for="item in items" :key="item.id" v-if="item.isActive"> {{ item.name }} </div> </div> <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script> <script> const app = new Vue({ el: '#app', data() { return { items: [ { id: 1, name: '张三', isActive: true }, { id: 2, name: '李四', isActive: false } ] } }, }) console.log(app.$options.render); </script>
// vue2 的渲染函数,先执行循环再判断条件 ƒ anonymous() { with (this) { // _l 是 for 循环,首先 v-for 循环 return _c('div', { attrs: { "id": "app" } }, _l((items), function (item) { // 然后再 v-if 判断 isActive return (item.isActive) ? _c('div', { key: item.id }, [_v("\n " + _s(item.name) + "\n ")]) : _e() }), 0) } }
-
vue3 输出的渲染函数:可以看出会先判断条件再执行循环
HTMLJavaScript<div id="app"> <!-- 控制台会报错: Uncaught TypeError: Cannot read properties of undefined (reading 'isActive') --> <!-- v-if 的优先级高于 v-for,v-if 执行时调用的变量还不存在,所以控制台报错 --> <!-- <div v-for="item in items" :key="item.id" v-if="item.isActive"> {{ item.name }} </div> --> <!-- 避免了渲染本应该被隐藏的列表 --> <div id="app"> <div v-for="item of items" :key="item.id" v-if="isShowUsers"> {{ item.name }} </div> </div> <script src="http://unpkg.com/vue@3"></script> <script> const app = Vue.createApp({ data() { return { isShowUsers: true, items: [ { id: 1, name: '张三', isActive: true }, { id: 2, name: '李四', isActive: false } ] } }, }).mount('#app') console.log(app.$options.render); </script>
// vue3 的渲染函数, (function anonymous() { const _Vue = Vue return function render(_ctx, _cache) { with (_ctx) { const { renderList: _renderList, Fragment: _Fragment, openBlock: _openBlock, createElementBlock: _createElementBlock, toDisplayString: _toDisplayString, createCommentVNode: _createCommentVNode } = _Vue // 先判断条件再执行循环 return isShowUsers ? (_openBlock(true), _createElementBlock(_Fragment, { key: 0 }, _renderList(items, (item) => { return (_openBlock(), _createElementBlock("div", { key: item.id }, _toDisplayString(item.name), 1 /* TEXT */ ) ) }), 128 /* KEYED_FRAGMENT */)) : _createCommentVNode("v-if", true) } } })
v-once 的使用场景有哪些
-
v-once 是 vue 的内置指令,作用是仅渲染指定组件或元素一次,并跳过未来对其更新;
-
如果有一些元素或者组件在初始化渲染之后不再需要变化,这种情况下适合使用 v-once,这样哪怕这些数据变化,vue 也会跳过更新,是一种代码优化手段,只需要作用的组件或元素上加上 v-once 即可;
-
vue3.2 之后,又增加了 v-memo 指令,可以有条件缓存部分模板并控制它们的更新,可以说控制力更强了;
-
编译器发现元素上面有 v-once 时,会将首次计算结果存入缓存对象,组件再次渲染时就会从缓存获取,从而避免再次计算;
-
示例代码:
htmlJavaScript<template> <h1 v-once>{{ msg }}</h1> <input v-model="msg" /> </template> <script setup> import { ref } from "vue"; const msg = ref("Hello World!"); </script>
// vue3 的渲染函数 return (_ctx, _cache) => { return (_openBlock(), _createElementBlock(_Fragment, null, [ // 从缓存获取vnode _cache[0] || ( _setBlockTracking(-1), _cache[0] = _createElementVNode("h1", null, [ _createTextVNode(_toDisplayString(msg.value), 1 /* TEXT */) ]), _setBlockTracking(1), _cache[0] ) // ... ])) }
v-for 为什么要加 key
为了在比对过程中进行复用
剑指 Offer 37.序列化二叉树
上一篇