《Vue3 二》Vue 的模板语法
在 React 中,想要编写 HTML,是使用 JSX,之后通过 Babel 将 JSX 编译成 React.createElement 函数调用;在 Vue 中,也支持 JSX 的开发模式,但大多数情况下都是使用基于 HTML 的模板语法,在模板中允许开发者以声明式的方式将 DOM 和底层组件实例的数据绑定在一起,在底层的实现中,Vue 会将面编译成虚拟 DOM 渲染函数。
Mustache 插值语法:
Mustache 插值语法:可以通过双大括号将 data 中的数据、methods 中的函数或者 JavaScript 表达式显示到 template 模板中。
<div id="app"><!-- data 中的数据 --><div>当前数字:{{count}}</div><!-- methods 中的函数 --><div>{{formatCount(count)}}</div><!-- JavaScript 表达式 --><div>三倍当前数字:{{count * 3}}</div><!-- JavaScript 表达式只能是单个表达式,下面的语法会报错 --><div>{{ const count = 10 }}</div>
</div><script>const app = Vue.createApp({data() {return {count: 10,}},methods: {formatCount(argu) {return `双倍当前数字:${argu*2}`},}})app.mount('#app')
<script>
常见的基本指令:
指令都是用在元素的属性上。
v-bind:
v-bind:用于动态绑定一个或多个属性值,或者向另一个组件传递 props 值。语法糖是 :
。
<div id="app"><img v-bind:src="imgUrl" />
</div><script>
const app = Vue.createApp({data() {return {imgUrl: 'http://gips2.baidu.com/it/u=195724436,3554684702&fm=3028&app=3028&f=JPEG&fmt=auto?w=1280&h=960',}},
})
app.mount('#app')
</script>
使用 v-bind 动态绑定 class 属性值:
动态绑定的 class 和普通静态的 class 是可以一起使用的,不会被覆盖。
- 绑定对象:可以给
:class
绑定一个对象来动态切换 class。对象中的属性名是 class 名,属性值是布尔类型,只有属性值为 true 时才会渲染对应的 class。<!-- 只有当 isActive 为 true 时才会显示 active 样式 --> <div v-bind:class="{ active: isActive }"></div>
- 绑定数组:可以给
:class
绑定一个数组来渲染多个 class。<div v-bind:class="[activeClass, errorClass]"></div>
使用 v-bind 动态绑定 style属性值:
- 绑定对象:属性名可以使用驼峰式或者用引号括起来的短横线分割来命名。
<div v-bind:style="{ color: activeColor, fontWeight: activeFontWeight, 'font-size': activeFontSize + 'px' }"></div>
- 绑定数组:可以将多个样式对象应用到同一个元素上。
<div :style="[baseStyles, {color: activeColor}]"></div>
使用 v-bind 动态绑定属性名:
如果属性名称不是固定的,可以使用 v-bind:[属性名]="属性值"
的格式来定义。
<~-- name 是动态的 -->
<div v-bind:[name]="value"></div>
使用 v-bind 直接绑定一个对象:
常用于给组件传递 props 属性值。
<div id="app"><!-- 会自动地遍历对象中的所有属性,将它们作为属性添加到当前的元素上 --><div v-bind="props">Hello Vue</div>
</div><script>
const app = Vue.createApp({data() {return {props: {name: 'Lee',height: 1.88,},}},
})
app.mount('#app')
</script>
v-on:
v-on:用于绑定事件。语法糖是 @
。
<div id="app"><div v-on:click="handleClick">Hello Vue</div>
</div><script>const app = Vue.createApp({methods: {handleClick() {console.log('click')}}})app.mount('#app')
</script>
传递参数:
- 只有默认参数:如果在绑定事件时,没有传递任何参数,那么 event 对象会被默认传递进来。
<div id="app"><div v-on:click="handleClick">Hello Vue</div> </div><script>const app = Vue.createApp({methods: {handleClick(event) {console.log('click', event)}}})app.mount('#app') </script> ``` ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/4aa258c96a1347a1aa771cf3ecaf5873.png)
- 只有明确传递的参数:如果在绑定事件时,有显式地传递参数,那么只有明确传递的参数会被传递进来。
<div id="app"><div v-on:click="handleClick('Lee')">Hello Vue</div>
</div><script>const app = Vue.createApp({methods: {handleClick(name) {console.log('click', name)}}})app.mount('#app')
</script>
- 既有明确传递的参数,也有默认参数:如果在绑定事件时,有显式地传递参数和
$event
,那么明确传递的参数和 event 对象都会被传递进来。<div id="app"><div v-on:click="handleClick('Lee', $event)">Hello Vue</div> </div><script>const app = Vue.createApp({methods: {handleClick(name, event) {console.log('click', name, event)}}})app.mount('#app') </script>
修饰符:
v-on 支持修饰符,相当于是对事件进行一些特殊的处理。
.stop
:调用event.stopPropagation()
阻止事件冒泡。.prevent
:调用event.preventDefault()
阻止默认事件。.capture
:添加事件侦听器时使用 capture 捕获模式。.passive
:添加事件侦听器时使用{passive: true}
模式,提前告知浏览器不要阻止事件的默认行为。.self
:只有当事件是从侦听器绑定的元素本身触发时才触发回调。.{keyAlias}
:只有当事件是从特定键触发时才触发回调。.left
:只有当点击鼠标左键时才触发回调。.right
:只有当点击鼠标右键时才触发回调。.middle
:只有当点击鼠标中键时才触发回调。.once
:只触发一次回调。
v-text:
v-text:用于设置元素的 textContent 文本内容。
<divv-text="message"></div>
<!-- v-text 会直接替换掉元素中的内容 -->
<divv-text="message">你好,Vue</div><!-- 和Mustache 插值语法的效果一样,只不过Mustache 插值语法更灵活 -->
<div>{{message}}</div>
v-html:
v-html:默认情况下,如果显示的内容本身是 HTML,Vue 是不会对其进行解析的;如果想要解析出来,需要使用 v-html。
<div id="app"><!-- 会直接将字符串显式出来 --><div>{{content}}</div><!-- 会解析 HTML --><divv-html="content"></div>
</div><script>const app = Vue.createApp({data() {return {content: `<span style="color:red">Hello Vue</span>`,}},})app.mount('#app')
</script>
v-pre:
v-pre:用于跳过元素及其子元素的编译过程,显示原始 Mustache 标签。可以跳过不需要编译的节点,加快编译速度。
<div id="app"><!-- Vue 会跳过对其的编译过程,将其直接显示出来 --><divv-pre>{{message}}</div>
</div><script>const app = Vue.createApp({data() {return {message: 'Hello Vue',}},})app.mount('#app')
</script>
v-cloak:
v-cloak:这个指令会保持在元素上直到关联的实例结束编译。和 CSS 规则例如 [v-cloak]{display:none}
一起用时,可以用于隐藏未编译的 Mustache 标签直到实例编译完成。
<div id="app"><!-- 初始会显示 {{message}},直到三秒后才显示正确的效果 --><div>{{message}}</div>
</div><script>setTimeout(() => {const app = Vue.createApp({data() {return {message: 'Hello Vue',}},})app.mount('#app')},3000)
</script>
<style>[v-cloak] {display: none;}
</style><div id="app"><!-- 由于有 v-cloak 指令,叠加设置的 CSS 属性,初始什么都不会显示;直到三秒后,Vue 实例编译完成,v-cloak 指令会被 Vue 自动移除,显示正确的效果 --><divv-cloak>{{message}}</div1>
</div><script>setTimeout(() => {const app = Vue.createApp({data() {return {message: 'Hello Vue',}},})app.mount('#app')},3000)
</script>
v-once:
v-once:只渲染元素/组件一次,当数据发生变化时,元素/组件及其所有的子节点将被视为静态内容直接跳过,不会重新渲染。可以用于优化更新性能。
<div id="app"><!-- 点击按钮也不会改变 message 显示的内容,因为只会渲染一次 --><divv-once>{{message}}</div><button @click="handleMessageChange">改变 Message</button>
</div><script>const app = Vue.createApp({data() {return {message: 'Hello Vue',}},methods: {handleMessageChange() {this.message = 'Hello World'},}})app.mount('#app')
</script>
v-memo:
v-memo:缓存一个模板的子树。在元素和组件上都可以使用。为了实现缓存,该指令需要传入一个固定长度的依赖值数组进行比较,如果数组里的每个值都与最后一次的渲染相同,那么整个子树的更新将被跳过。可以用于优化更新性能。
v-memo
传入空依赖数组v-memo="[]"
将与 v-once 效果相同。
<!-- 当组件重新渲染,如果 valueA 和 valueB 都保持不变,div 及其子项的所有更新都将被跳过。 -->
<div v-memo="[valueA, valueB]">...
</div>
条件渲染:
v-if
、v-else
、v-else-if
、v-show
:根据条件决定是否显示元素或组件。类似 JavaScript 中的条件语句。
<div id="app"><div v-if="Object.keys(info).length"><p>姓名:{{info.name}}</p><p>年龄:{{info.age}}</p></div><div v-else><p>暂无个人信息</p><p>请填写后查看</p></div>
</div><script>const app = Vue.createApp({data() {return {info: {name: 'Lee',age: 18,},}}})app.mount('#app')
</script>
v-if
和 v-show
的区别:
v-show
和 v-if
类似,但二者也有一些区别:
- 用法上的区别:
v-show
不可以和v-else
一起使用,不支持在 template 上使用。 - 本质上的区别:
v-show
无论条件是 true 还是 false,它的 DOM 都是实际存在的,只不过是通过 CSS 的 display 属性来进行切换。
v-if
只有当条件为 true 时,才会渲染条件块中的元素内容,当条件为 false 时,条件块中的元素内容完全不会被渲染。
列表渲染:
v-for
:用来遍历一组数据。类似 JavaScript 中的 for 循环 。基本格式是 v-for="(item, index) in 数据"
,其中 item 和 index 分别是每项元素的自定义别名和自定义索引。
<div id="app"><ul><li v-for="movie in movies">{{movie}}</li></ul>
</div><script>const app = Vue.createApp({data() {return {movies: ['大话西游', '少年派', '盗梦空间']}}})app.mount('#app')
</script>
v-for
遍历支持的类型:
其实只要是可迭代对象,v-for
都可以遍历。
- 数组:基本格式是
v-for="(item, index) in 数组"
。Vue 对被侦听的数组的变更方法进行了包裹,所以使用它们也会触发视图更新,这些方法包括 push()、pop()、shift()、unshift()、splice()、sort()、reverse()。
可以发现,上面的方法都是会修改到原数组的;如果是不会修改到原数组的方法,是无法触发视图更新的,例如 map() 、filter() 等。 - 对象:基本格式是
v-for="(value, key, index) in 对象"
。 - 字符串:基本格式是
v-for="(str, index) in 字符串"
。 - 数字:基本格式是
v-for="(num, index) in 数字"
。
v-for
中的 key 属性:
在使用 v-for
进行列表渲染时,经常会给元素或者组件绑定一个 key 属性,属性值要求是唯一的。
<div id="app"><ul><li v-for="movie in movies" v-bind:key="item">{{movie}}</li></ul>
</div><script>const app = Vue.createApp({data() {return {movies: ['大话西游', '少年派', '盗梦空间']}}}) app.mount('#app')
</script>
key 的作用:
key 属性主要用于 Vue 中的虚拟 DOM 算法,在新旧 nodes 对比时辨识 VNodes。
- 如果不使用 key,Vue 会使用一种最大限制减少动态元素并且尽可能尝试就地复用/修改相同类型元素的算法。在源码中是使用 patchUnkeyedChildren() 方法。
- 如果使用 key,它会基于 key 的变化重新排列元素顺序,并且会移除/销毁 key 不存在的元素。在源码中是使用 patchkeyedChildren() 方法。
<div id="app"><ul><li v-for="movie in movies">{{movie}}</li></ul>
</div><script>const app = Vue.createApp({data() {return {movies: ['大话西游', '少年派', '盗梦空间']}}}) app.mount('#app')
</script>
假设要在大话西游后面插入夏洛特烦恼,原本是大话西游、少年派、盗梦空间,现在是大话西游、夏洛特烦恼、少年派、盗梦空间。
4. 不使用 key 的话:Vue 只能一个一个对比对应位置的元素。会发现第一个元素一样,可以直接复用;但是第二个元素就不一样了,原本是少年派,现在是夏洛特烦恼,因此需要修改其中的值;第三个元素也不一样,需要修改其中的值;还需要创建一个新的第四个元素。
5. 使用 key 的话:Vue 可以通过 key 来比较是否是同一个元素。会发现大话西游、少年派、盗梦空间的元素都一样,可以直接复用,只需要创建一个新的元素插入即可。
template 元素:
template 元素作为一个不可见的包裹元素,最终是不会被浏览器渲染出来的,与条件渲染和列表渲染结合使用可以减少元素层级的嵌套。
<div id="app"><template v-if="Object.keys(info).length"><p>姓名:{{info.name}}</p><p>年龄:{{info.age}}</p></template><template v-else><p>暂无个人信息</p><p>请填写后查看</p></template>
</div><script>
const app = Vue.createApp({data() {return {info: {name: 'Lee',age: 18,},}}
})
app.mount('#app')
</script>
可以看到,p 元素外面并不会再嵌套一层多元的元素。