前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >19 vue 模板语法及简要实现原理

19 vue 模板语法及简要实现原理

作者头像
LIYI
发布2020-02-13 11:56:51
2.9K0
发布2020-02-13 11:56:51
举报
文章被收录于专栏:艺述论专栏艺述论专栏
代码语言:javascript
复制
目录

模板中的插值
    文本插值
    v-html与原始HTML插值
    属性(Attribute)插值
    插值中的 JavaScript 表达式
指令与参数
    动态属性
    修饰符
    常用指令略写

模板中的插值

模板中的插值,包括文本插值与属性插值。在插值表达式中,还可以包涵一行简单的js代码。

文本插值

示例:

代码语言:javascript
复制
<!-- 使用Mustache语法的文本插值 -->
<div>{{message}}</div>

Mustache的直译是小胡子,因为花括号放倒像小胡子,所以这叫做Mustache语法。

文本插值vue是如何实现的?

在vue源码中是通过解析template并建立虚拟dom实现的。以{{message}}为例,将{{message}}解析为一个独立的DocumentFragment节点,当message变化时更新该节点的值。

v-html与原始HTML插值

示例:

代码语言:javascript
复制
<!-- 插入html -->
<p>Using v-html directive: <span v-html="rawHtml"></span></p>

如果直接将rawHtml以文本插值的方式嵌入template中,显示是纯文本,不是html。

运行效果:

查看编译后的html源码,发现v-html会在其所属的节点内部生成新节点,内容即是rawHtml,不会将所属节点替换。

有教程写使用三个花括号插入html源码:

代码语言:javascript
复制
<div>{{{ rawHtml }}}</div>

官方文档也是这么写的。但是vue2 已经废弃了这种语法,在vue2中需要使用 v-html。

既然v-html可以非转义显示html,有人就想有没有可能使用它进行动态绑定,例如:

代码语言:javascript
复制
<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,使用方法及原理与之类似。

属性(Attribute)插值

示例:

代码语言:javascript
复制
<!-- 属性插值 -->
<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。

插值中的 JavaScript 表达式

示例:

代码语言:javascript
复制
<!-- 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这样的全局函数。

模板表达式只接受单行代码,不接受多行逻辑代码,例如:

代码语言:javascript
复制
<div>{{ if (isButtonDisabled) { return message } else { return '' } }}</div>

这样的代码虽然写在一行里,也不行。如果表达式中涉入到条件控制,只能使用三目运算符:

代码语言:javascript
复制
<div>{{ isButtonDisabled ? message : 'none' }}</div>

但是将这行代码加个分号,改一下:

代码语言:javascript
复制
<div>{{ let a = 1;isButtonDisabled ? message : 'none' }}</div>

不行,vue编译时就报错了:

代码语言:javascript
复制
avoid using JavaScript keyword as property name: "let"
  Raw expression: {{ let a = 1;isButtonDisabled ? message : 'none' }}

找一下源码,原来错误是从这里抛出来的:

代码语言:javascript
复制
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执行一段多行代码,却是可以正常执行的:

代码语言:javascript
复制
new Function(`let a=1;return a+1`)()
2

指令与参数

v-bind与v-if、v-for、v-on这些都是指令。

v-bind:id接受的是一个属性的插值,并且该插值可以使用js表达式。

动态属性

如果绑定时不知道要绑定哪个属性,还可以使用动态属性。示例:

代码语言:javascript
复制
<!-- 动态参数 -->
<a v-bind:[attributeName]="url"> link </a>
...
data: ()=>({
    ...
    attributeName:"href",
    url:"https://cn.vuejs.org"
}),

修饰符

在指令参数后面,可以加一些修饰符,以达到特殊的效果。例如,.prevent 修饰符告诉 v-on 指令对于触发的事件调用 event.preventDefault()

代码语言:javascript
复制
<form v-on:submit.prevent="onSubmit">...</form>

前面已经用过的sync也是一个修饰符,它要求子组件一个名称为update:xx的事件派发,用于自动更新目标变量:

代码语言:javascript
复制
<SyncComponent1 :value.sync="text1" ></SyncComponent1>

native是为了把事件加在原生html元素上,如果不这样做,类似下面这样的在自定义组件上的事件监听就没有效果:

代码语言:javascript
复制
<自定义组件 @click="方法" />

html元素本身具有事件属性,组件也有自己的事件系统,vue处理这两种事件的逻辑是不同的,所以在自定义组件上添加事件监听,vue必须要知道是加在哪里的,.native 充当了一个区分的标识。

常用指令略写

attributeName是一个属性名称变量,可以是任何合法的html元素的属性名称,可以在运行时修改。

v-bind:可以略为:;v-on:可以略写为:。例如:

代码语言:javascript
复制
<!-- 完整语法 -->
<a v-bind:href="url">...</a>
<!-- 缩写 -->
<a :href="url">...</a>
<!-- 完整语法 -->
<a v-on:click="doSomething">...</a>
<!-- 缩写 -->
<a @click="doSomething">...</a>

参考链接

  • https://github.com/vuejs/vue
  • https://segmentfault.com/q/1010000006834306?sort=created
  • https://www.cnblogs.com/greatdesert/p/11128344.html
  • https://blog.csdn.net/qiphon3650/article/details/73692446
  • https://www.zhihu.com/question/318103114
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-01-15,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 艺述论 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 模板中的插值
    • 文本插值
      • v-html与原始HTML插值
        • 属性(Attribute)插值
          • 插值中的 JavaScript 表达式
          • 指令与参数
            • 动态属性
              • 修饰符
                • 常用指令略写
                • 参考链接
                领券
                问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档