你好,我是杨文坚。

前面几节课,我们讲解了很多Vue.js 3编译相关的内容,了解完Vue.js两个编译打包工具后,我们是时候要开始学习如何使用Vue.js 3进行实际的代码开发了。

这节课,我主要会从Vue.js的两种主要开发语法进行讲解,它们分别是模板语法和JSX语法。从中你不仅能了解到两种开发语法的差异,还可以知道怎么因地制宜地根据需求场景选择合适的语法,从而扩大个人的技术知识储备,更从容地应对用Vue.js 3开发项目过程中遇到的各种问题。

Vue.js从版本1.x到版本3.x,官方代码案例和推荐使用都是模板语法,那么这里我们也根据官方的推荐,优先来了解一下模板语法是怎么一回事。

什么是模板语法?

我们可以把Vue.js的模板语法,直接理解为 HTML语法的一种扩展,它所有的模板节点声明、属性设置和事件注册等都是按照HTML的语法来进行扩展设计的。按照官方的说法就是“所有的 Vue 模板都是语法层面合法的 HTML,可以被符合规范的浏览器和 HTML 解析器解析”。

现在我举个例子,带你了解下模板语法的概念及其不同内容的作用。代码如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<template>
<div class="counter">
<div class="text">Count: {{state.count}}</div>
<button class="btn" v-on:click="onClick">Add</button>
</div>
</template>

<script setup>
import { reactive } from 'vue';
const state = reactive({
count: 0
});
const onClick = () => {
state.count ++;
}
</script>

<style>
.counter {
padding: 10px;
margin: 10px auto;
text-align: center;
}

.counter .text {
font-size: 28px;
font-weight: bolder;
color: #666666;
}

.counter .btn {
font-size: 20px;
padding: 0 10px;
height: 32px;
min-width: 80px;
cursor: pointer;
}
</style>

这是基于 Vue.js 3的模板语法实现的加数器组件,代码基础结构都是基于HTML语法实现的,主要由视图模板、JavaScript脚本代码和CSS样式代码构成的。我们拆分开来具体看看。

首先我们来看看视图层代码:

1
2
3
4
5
6
<template>
<div class="counter">
<div class="text">Count: {{state.count}}</div>
<button class="btn" v-on:click="onClick">Add</button>
</div>
</template>

上述视图代码中,只能有一个最外层的template标签,template内部可以允许存在多个template标签,用来定义模板的“插槽”位置(slot)等更多插槽相关信息,你可以查看官方对插槽的说明了解一下。

Vue.js的模板可以直接使用HTML语法的属性(Attribute),例如class也可以直接在Vue.js的模板中使用,但是Vue.js自己定义了一些属性语法,例如 v-on,这个就是Vue.js模板绑定事件的语法。以此类推,你大概可以猜到大部分 Vue.js自定义的模板属性语法,都是以 “v-”为前缀的。更多Vue.js的模板语法,你可以查看官方文档

接下来我们看看JavaScript脚本代码:

1
2
3
4
5
6
7
8
9
10
<script setup>
import { reactive } from 'vue';
const state = reactive({
count: 0
});
const onClick = () => {
state.count ++;
}
</script>

在模板语法中,JavaScript代码只能放在script标签里,而且同一个文件里只能有一个顶级的script标签。

上述代码使用的是Vue.js的 Composition API,所以必须在script标签中声明 setup属性。我们后续所有内容都默认是基于 Composition API 来讲解Vue.js 3里的JavaScript代码操作。这是因为它是官方推荐的API使用方式,使用起来简单清晰,方便复用逻辑代码,同时这也是Vue.js 3诞生的特色。

最后再来看下CSS样式代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<style>
.counter {
padding: 10px;
margin: 10px auto;
text-align: center;
}

.counter .text {
font-size: 28px;
font-weight: bolder;
color: #666666;
}

.counter .btn {
font-size: 20px;
padding: 0 10px;
height: 32px;
min-width: 80px;
cursor: pointer;
}
</style>

这些代码是模板语法里的CSS样式代码,具体使用方式跟HTML里使用CSS代码一致,唯一不同是可以加上scoped和lang属性。

scoped属性可以在编译Vue.js模板语法代码的时候,用随机数来定义样式选择器名称,保证CSS不会干扰页面上同名的CSS选择器,例如下面代码所示:

1
2
3
4
5
6
7
8
9
10
11
12
<div class="counter"></div>
<style scoped>
.counter { /*...*/ }
</style>

<!-- 当style加上scoped 后编译成 -->

<div class="counter" data-v-xxxxx></div>
<style>
.counter[data-v-xxxxx] { /*...*/ }
</style>

而lang属性可以赋值声明定义用了其它CSS语法,例如 lang=”less”就是用了Less语法来写的CSS,但是需要在Vite等对应编译配置加上Less编译插件。

上面的整体代码就是用一个Vue.js 3模板语法实现一个组件,如果有一个组件需要引用这个“计数器”组件,就直接用import来引用就好了,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<template>
<div class="app">
<Counter />
</div>
</template>

<script setup>
import Counter from './counter.vue'
</script>

<style>
.app {
width: 200px;
padding: 10px;
margin: 10px auto;
box-shadow: 0px 0px 9px #00000066;
text-align: center;
}
</style>

讲到这里,你是不是反应过来了,其实只要了解过HTML语法,就能很容易上手Vue.js的模板语法。而且,Vue.js从版本1.x到版本3.x,官方代码案例和推荐使用都是模板语法,因为模板语法更加简单易用。

不过,既然Vue.js官方代码案例和推荐使用都是模板语法,为什么官方还要实现一套与模板语法不同的JSX语法呢?

其实这个问题我们可以直接在Vue.js官网找到答案,官网就这么写着:“在绝大多数情况下,Vue 推荐使用模板语法来创建应用。然而在某些使用场景下,我们真的需要用到 JavaScript 完全的编程能力。这时渲染函数就派上用场了。”

这就是说,虽然官方推荐你用模板语法来写Vue.js 3代码,但是有些功能场景用模板语法可能会很难实现,甚至不能实现,那么就需要用到JSX语法来辅助实现了。而且,Vue.js在2.x版本时候已经开始支持JSX语法了。那么,Vue.js 3的JSX语法是怎样的呢?

Vue.js 3的JSX语法是怎样的?

在讲解Vue.js 3之前,我先来给你分享一下,什么是JSX语法。

JSX语法,是JavaScript语法的一种语法扩展,支持在JavaScript直接写类似HTML的模板代码,你可以直接理解为“ HTML in JavaScript ”。从目前在网上能找到的资料来看,JSX语法最早用于React.js,但不是React.js 独有的写法,目前有很多框架支持JSX写法,例如Vue.js和Solid.js(一种类似React.js写法的前端框架)等。

现在,我把上面的Vue.js 3的模板语法实现的“加数器”组件换成JSX语法实现,你可以对比看看这两个语法的实现差异,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import { defineComponent, reactive } from 'vue';

const Counter = defineComponent({

setup() {
const state = reactive({
count: 0
});
const onClick = () => {
state.count ++;
}
return {
state,
onClick,
}
},

render(ctx) {
const { state, onClick } = ctx;
return (
<div class="counter">
<div class="text">Count: {state.count}</div>
<button class="btn" onClick={onClick}>Add</button>
</div>
)
}
});

export default Counter;

现在,我们类比模板语法,逐步分析下这个JSX语法实现的“加数器”组件。
JSX语法其实可以直接看做是纯JavaScript文件代码,在JavaScript文件代码里定义Vue.js 3组件可以通过API defineComponent来进行声明定义:

1
2
3
4
import { defineComponent } from 'vue';
const Counter = defineComponent({
// ...
})

而模板语法有组件视图层相关的代码,类比JSX语法里定义组件中的render方法,如下述代码所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const Counter = defineComponent({
// ...
render(ctx) {
const { state, onClick } = ctx;
return (
<div class="counter">
<div class="text">Count: {state.count}</div>
<button class="btn" onClick={onClick}>Add</button>
</div>
)
}
// ...
});

上述代码中,render函数返回的代码,就是JSX的写法,用来描述HTML模板内容。这里需要注意的是,所有JSX写法中都是用 单大括号“{state.count}”来作为内部变量处理,而Vue.js 3模板语法是通过双大括号来表示“”,单大括号描述变量这个是JSX通用写法,Vue.js的JSX语法也是遵循了这个通用写法。

在模板语法中,模板的