目录
模板中的插值
文本插值
v-html与原始HTML插值
属性(Attribute)插值
插值中的 JavaScript 表达式
指令与参数
动态属性
修饰符
常用指令略写
模板中的插值,包括文本插值与属性插值。在插值表达式中,还可以包涵一行简单的js代码。
示例:
<!-- 使用Mustache语法的文本插值 -->
<div>{{message}}</div>
Mustache的直译是小胡子,因为花括号放倒像小胡子,所以这叫做Mustache语法。
文本插值vue是如何实现的?
在vue源码中是通过解析template并建立虚拟dom实现的。以{{message}}为例,将{{message}}解析为一个独立的DocumentFragment节点,当message变化时更新该节点的值。
示例:
<!-- 插入html -->
<p>Using v-html directive: <span v-html="rawHtml"></span></p>
如果直接将rawHtml以文本插值的方式嵌入template中,显示是纯文本,不是html。
运行效果:
查看编译后的html源码,发现v-html会在其所属的节点内部生成新节点,内容即是rawHtml,不会将所属节点替换。
有教程写使用三个花括号插入html源码:
<div>{{{ rawHtml }}}</div>
官方文档也是这么写的。但是vue2 已经废弃了这种语法,在vue2中需要使用 v-html。
既然v-html可以非转义显示html,有人就想有没有可能使用它进行动态绑定,例如:
<script>
let vm = new Vue({
data: ()=>({
html: "html"
})
})
window.onload = function () {
vm.$mount(document.body)
setTimeout(function () {
vm.html = '<div v-text="str"></div>'
}, 1000)
setTimeout(function () {
vm.html = '<button v-el:button v-on:click="click">click me</button>'
}, 2000)
}
</script>
<body v-html="html">
在window.onload的回调函数中,vm.html即data对象的html,将被定时器轮番修改。注意html的值含有vue指令。
这是不可以的,v-html会忽略解析属性值中的数据绑定。不能使用 v-html 复合局部模板,Vue 不是基于字符串的模板引擎,无法进一步解析属性值中的模板内容。
在vue源码内部,解析模板时将检查是否以v-开头,如果是,这表明是自己的指令,需要处理。接下来再看,v-后面是不是html,如果是,则交给src/platforms/web/compiler/directives/html.js处理:
从源码中可以看出,v-html是通过给原生组件添加一个innerHTML属性实现的。在这里不涉及对innerHtml做二次解析,所以针对v-html指令实现模板的动态绑定,行不通。
与v-html相仿的插值指令是v-text,使用方法及原理与之类似。
示例:
<!-- 属性插值 -->
<div v-bind:id="dynamicId"></div>
<button v-bind:disabled="isButtonDisabled">Button</button>
属性插值实现的是对html元素具有属性值的绑定。内容值是通过mustache模板语法、v-text、v-html完成的;属性值的绑定,要使用v-bind完成。
属性插值v-bind可以简写为:
,例如v-bind:id
可以简写为:id
。
使用属性插值需要特别注意布尔类型的插值。
disabled在html元素上是一个特殊的值,只要存在,就代表真,否则代表假。在上面代码中,如果isButtonDisabled为false,在最终渲染出来的button的html源码中,看不到disabled属性的存在:
在vue源码中有一个setAttr函数:
当通过isBooleanAttr判断属性是一个布尔属性值,如果布尔值为false,直接removeAttribute,否则才是正常的setAttribute。
示例:
<!-- js表达式 -->
<div>{{ message.split('').reverse().join('')+ ":"+Math.round(10.1) + `:${new Date().getTime()}` }}</div>
<div v-bind:id="'list-' + dynamicId+`:${new Date().getTime()}`"></div>
可以在文本插值和属性插值中使用js表达式,并且还可以使用像Math、Date、RegExp、JSON这样的全局js对象,以及像parseInt这样的全局函数。
模板表达式只接受单行代码,不接受多行逻辑代码,例如:
<div>{{ if (isButtonDisabled) { return message } else { return '' } }}</div>
这样的代码虽然写在一行里,也不行。如果表达式中涉入到条件控制,只能使用三目运算符:
<div>{{ isButtonDisabled ? message : 'none' }}</div>
但是将这行代码加个分号,改一下:
<div>{{ let a = 1;isButtonDisabled ? message : 'none' }}</div>
不行,vue编译时就报错了:
avoid using JavaScript keyword as property name: "let"
Raw expression: {{ let a = 1;isButtonDisabled ? message : 'none' }}
找一下源码,原来错误是从这里抛出来的:
function checkExpression (exp: string, text: string, warn: Function, range?: Range) {
try {
new Function(`return ${exp}`)
} catch (e) {
const keywordMatch = exp.replace(stripStringRE, '').match(prohibitedKeywordRE)
if (keywordMatch) {
warn(
`avoid using JavaScript keyword as property name: ` +
`"${keywordMatch[0]}"\n Raw expression: ${text.trim()}`,
range
)
} else {
warn(
`invalid expression: ${e.message} in\n\n` +
` ${exp}\n\n` +
` Raw expression: ${text.trim()}\n`,
range
)
}
}
}
vue中js表达式的运算,不是通过eval完成的,而是通过new Function达成的。
但是如果我们直接使用new Function执行一段多行代码,却是可以正常执行的:
new Function(`let a=1;return a+1`)()
2
v-bind与v-if、v-for、v-on这些都是指令。
v-bind:id接受的是一个属性的插值,并且该插值可以使用js表达式。
如果绑定时不知道要绑定哪个属性,还可以使用动态属性。示例:
<!-- 动态参数 -->
<a v-bind:[attributeName]="url"> link </a>
...
data: ()=>({
...
attributeName:"href",
url:"https://cn.vuejs.org"
}),
在指令参数后面,可以加一些修饰符,以达到特殊的效果。例如,.prevent 修饰符告诉 v-on 指令对于触发的事件调用 event.preventDefault()
<form v-on:submit.prevent="onSubmit">...</form>
前面已经用过的sync也是一个修饰符,它要求子组件一个名称为update:xx的事件派发,用于自动更新目标变量:
<SyncComponent1 :value.sync="text1" ></SyncComponent1>
native是为了把事件加在原生html元素上,如果不这样做,类似下面这样的在自定义组件上的事件监听就没有效果:
<自定义组件 @click="方法" />
html元素本身具有事件属性,组件也有自己的事件系统,vue处理这两种事件的逻辑是不同的,所以在自定义组件上添加事件监听,vue必须要知道是加在哪里的,.native 充当了一个区分的标识。
attributeName是一个属性名称变量,可以是任何合法的html元素的属性名称,可以在运行时修改。
v-bind:可以略为:
;v-on:可以略写为:
。例如:
<!-- 完整语法 -->
<a v-bind:href="url">...</a>
<!-- 缩写 -->
<a :href="url">...</a>
<!-- 完整语法 -->
<a v-on:click="doSomething">...</a>
<!-- 缩写 -->
<a @click="doSomething">...</a>