Why learn Vue's template syntax

1. Understand the role of Vue template syntax

In fact, there is a lot of knowledge in Vue's template syntax, especially after understanding the characteristics of template syntax, it has a great effect on learning the source code behind Vue. This article combines a few simple examples to better understand the following knowledge:

  1. Clearly understand which Vue template syntax is legal and which is illegal and the logic behind it;
  2. Understand the simple logic behind Vue's directives;
  3. Why can't Vue interpolation expressions use global variables like React? Here is a limited list of core concepts;
  4. How to simply dynamically bind property names, method names and quickly unbind;
  5. Why Vue selectively ignores null and undefined, and how we can use this feature to develop code;

2. Understand the background knowledge of Vue template syntax

What is template syntax?

In addition to its own features, there are Vue features in the html string, such as text, expressions, properties and directives.

How is Vue's template syntax different from React's jsx?

The biggest difference is that Vue's template syntax is based on HTML, rather than its own template syntax. It has a powerful html parsing system behind it (described later) , which converts non-native html (for example: v-if @click, etc.) into native html after parsing the template, or directly uses JS to express. And the HTML language written directly in the template can be parsed by the HTML parser.

Vue template compilation system process

  1. The developer writes the template

  2. Parse html string

  3. Convert to AST tree

  4. Use the AST tree to convert expressions, custom attributes, and instructions on the template (for example, v-for loops back and splices some html to finally convert the instructions into html)

  5. virtual dom tree

  6. Finally rendered into real dom

Why do you need a virtual dom ?

simple example

<p>hello</p>
// 通过js对标签进行操作 p.innerText = 'hello'
复制代码

At this time, modifying the internal text of the p tag is the same as before, but the browser will still render it, which will cause meaningless performance waste. If we compare it before rendering, if it is different, then update it, which will save performance, but it is very troublesome to compare the real DOM, so change the real DOM into data form for comparison, and then update if there is a difference.

3. Template Syntax - Interpolation Expressions

因为表达式的方式{{}}像一个胡子, 所以也成为胡子表达式Mustache

起源于mustache.js一个库

这里感兴趣可以去github上看github.com/janl/mustac…, 它是一个0依赖的库, vue虽然没有使用这个库但是有很多地方都有借鉴。

插值表达式使用规范

  1. 在插值表达式中只能是JS表达式, 不能是语句(如: if else)、功能模块、函数、变量声明以及变量声明并赋值(如:let a = 10)。

  2. 插值表达式中可以使用JS api

data() {
    retrun {
        x: 1,
        y: 2    
    }
}
// 合法的表达式
{{ x + y }}   
{{ x > 2 ? x : y }}
{{ x + y + 'str' }}
{{ x || y }}
{{ x && y }}
{{ String(x) + String(y) }}
{{ x = 10 }} // 赋值表达式可以,因为能计算出一个结果10

// 不合法的表达式
{{ if(x){ return x } }}
{{ var a = x }} // 声明式表达式不行
复制代码

插值表达式受限列表概念

Vue的插值表达式与React有一个很大的区别在于Vue的插值内容不能使用全局变量, 如果这样做会报错 :Property "str" was accessed during render but is not defined on instance.

let str = 'is a string';
const App = {
    template: `
       <div>
            <h1>{{ str }}</h1> 
       </div>`,
}
createApp(App).mount('#app')
复制代码

因为Vue强大的编译系统背后有很多规范进行支撑的, 可以检查你写的东西到底是否合法, 例如插值变量能使用的全局的类型就在核心库的globalsWhitelist.ts中, 下面这些可以在插值中直接使用, 我们可以这些内容自己进行扩展, 这些技巧后期会进行说明。

image.png

使用h函数来代替模板语法

使用插值表达式的写法

let { createApp } = Vue;
const Todo = {
    data() {
        return {
            name: 'xiaoming',
            age: 18
        }
    },
    template: `
        <div>
            <h1>{{ name }}</h1>
            <p>{{ age }}</p>
        </div>
    `
}
createApp(Todo).mount('#app')
复制代码

使用h函数也同样可以来渲染dom,先混一个脸熟后面会详细说明这个

let { createApp, h } = Vue;
const Todo = {
    data() {
        return {
            name: 'xiaoming',
            age: 18
        }
    },
   render() {
        return h(
            'div', // 标签名
            {}, // 标签属性 
            [
                h('h1', {}, this.name),
                h('p', {}, this.age)
            ] // 子标签
        )
   }
}
createApp(Todo).mount('#app')
复制代码

4. 模板语法 - 指令

这里特别要注意深度学习指令, 先简单的看一下各个指令的使用有什么技巧,这样后期才能根据指令的特点来进行深度分析和重写

指令的含义和作用:

所有在vue中模板上属性v-* 都是指令,通过指令我们可以规定模板应该按照怎样的逻辑进行渲染或绑定行为。

Vue提供了大量的内置指令v-if v-else v-html v-once等,开发者也可以给Vue扩展指令, 自定义指令必须v-开头

了解v-once

第一次插值后面数据变了也不更新,** 会影响整个标签内部所有的东西都不会更新**.

const App = {
    data() {
        return {
            name: 'xiaoming',
            age: 18
        }
    },
    template: `<div @click="change" v-once><span>{{ name }}</span></div>`,
    methods: {
        change() {
            this.name = 'xiaohong'
        }
    }
}
createApp(App).mount('#app')
复制代码

了解v-html

作用: 解决插值表达式中无法解析html的一个方案

const App = {
    data() {
        return {
            title: 'h1-title'
        }
    },
    template: `
        <div>{{ '<h1>' + title + '</h1>' }}<div/>
    `
}
复制代码

image.png

页面上渲染还是一个字符串不能解析为标签。

重点理解:为什么会有这样的规定?以及两个对v-html使用不得当的案例

插值表达式不会解析html为了安全, 因为插值是JS表达式没有对dom的操作。

不好的栗子1: 使用v-html做子模板

let show = false;
const App = {
    data() {
        return {
            sub: `<h1 v-if="${show}">title</h1>`
        }
    },
    template: `<div v-html="sub"></div>`
}

createApp(App).mount('#app');
复制代码

image.png

我们发现实际上dom节点上已经有了, v-if 这个指令但是页面还是渲染出来了,因为vue本身有一个底层的模板编译系统,而不是直接使用字符串来渲染。所以使用v-html做子模板不可取会导致很多问题。

不好的栗子2: 安全性问题, 如果内容是用户提供的并且和v-html连用很容易产生xss攻击 现在html做了很多优化防止这种行为, 例如下面这样的代码是不会产生作用的

const App = {
    data() {
        return {
            content: `<script>alert('Surprise!')</script>`
        }
    },
    template: `<div v-html="content"></div>`
}

createApp(App).mount('#app')
复制代码

虽然dom节点渲染了,但是里面的代码没有执行

image.png

但是如果换一个思路将content内容改为: <img src="xxx" onerror="alert('Surprise!')"/>通过一个图片报错的形式进行触发脚本执行

image.png

这些脚本内容可以是各式各样的例如读取cookie session等一些隐私信息, 所以使用v-html需要特别注意。

了解v-bind与v-on

  1. 先了解attribute与property两个属性的区别:

attribute为dom属性是html语言的扩展,例如title、scr、herf通常表示有什么作用。property通常是对象内部存储的数据, 来描述数据结构

  1. 使用[ ]运算符动态绑定属性名和方法
const App = {
    data() {
        return {
            attrType: 'data-id',
            eventType: 'click'
        }
    },
    template: `
       <div>
            <h1 :[attrType]="123">
                <button @[eventType]="handleClick()">点击</button>
            </h1> 
       </div>`,
    methods: {
        handleClick() {
            console.log(123);
        }
    }
}
复制代码

虽然可以使用[]求值,但是不能 <h1 :[ 'data' + attrType ] > 这种形式进行拼接属性, 因为属性名中不允许有引号和空格,这样会报语法错误

Vue为什么会忽略null与undefined ? 以及我们如何利用这个特性

当绑定的属性值为null的时候例如:

渲染成真实dom后并不会有这个属性。这里就体现除了Vue对于这两个值的态度, 因为null与undefined往往是取值发生错误时候,或者设置方法有问题产生的数据结构错误, 可能开发者自己不知道,如果真的渲染上去可能会导致其他的一些问题, 所以会忽略这些属性。 我们可以利用这一特点实现简单的属性切换:

// 快速给h1标签添加和删除id属性
const App = {
    data() {
        return {
            attrType: 'data-id',
        }
    },
    template: `
       <div>
            <h1 :[attrType]="123">
                <button @click="handleClick()">添加或删除属性</button>
            </h1> 
       </div>`,
    methods: {
        handleClick() {
            this.attrType === null ? (this.attrType = 'id') : (this.attrType = null);
        }
    }
}
复制代码

v-if 与 v-show

这里简单说明一下两者实现的区别(之后有简单的源码实现案例): v-if 的语法会在dom上增加一个注释节点进行占位, 通过逻辑判断后把要显示的内容替换到注释节点上。

image.png

v-show是通过样式display: none进行节点的隐藏和显示。 这就是我们常说的如果需要经常切换显示的内容使用v-show会更好, 如果值渲染一次或者大概率不渲染的情况下使用v-if会节约性能。

5. 总结

以上就是一些基础的模板语法特性的说明, 虽然只是大概简单的说了一些但是里面还是有很多知识点在里面, 之后会慢慢选几个指令配合案例来简单的实现, 希望能帮助大家了解一下Vue指令背后的基本逻辑。

Guess you like

Origin juejin.im/post/7085350456783798303