Vue.js 定义组件模板的 7 种方式

作者简介:
李中凯
八年多工作经验 前端负责人,
擅长JavaScript/Vue。
掘金文章专栏:https://juejin.im/user/57c7cb8a0a2b58006b1b8666/posts
公众号:1024译站

定义 Vue.js 组件模板有多种方式,据我所知,至少有7种不同的方法。
本文将逐一介绍每种方法的示例,并讨论其优缺点,以便你了解在特定情况下使用哪种方法最好。

普通字符串
定义 Vue 组件模板最快也是最容易的方式就是给组件定义加上一个 template 属性,它的值就是包含 HTML 标签的普通字符串。

但是,这种方法实际上只在代码示例或快速原型中使用,因为模板过于简单,不太实用。
app.js

Vue.component('my-checkbox', {
template: '<div class="checkbox-wrapper" @click="check"><div :class="{ checkbox: true, checked: checked }"></div><div class="title">{{ title }}</div></div>',
data() {
return { checked: false, title: 'Check me' }
},
methods: {
check() { this.checked = !this.checked; }
}
});

模板字符串
从 ES2015 开始,可以用反引号定义一种特殊的字符串,叫做模板字面量。跟普通字符串不用,它可以内嵌表达式,并支持多行。

支持多行的特性让它在定义组件模板时比普通字符串方便多了,因为可以让多行标签可读性更好。
app.js

Vue.component('my-checkbox', {
template: <div class="checkbox-wrapper" @click="check"> <div :class="{ checkbox: true, checked: checked }"></div> <div class="title">{{ title }}</div> </div>,
data() {
return { checked: false, title: 'Check me' }
},
methods: {
check() { this.checked = !this.checked; }
}
});

较老的浏览器可能不支持 ES2015 特性,因此需要用 Babel 转换代码。

x-template
这种方式是将模板定义在 HTML 文件里的 script 标签里。script 标签需要指定type="text/x-template ",并通过 id 属性在组件里引用。

好处是可以在 HTML 文件里写模板标记,非常直观。坏处就是模板和组件的其他部分分离了,查找起来不太方便。
app.js

Vue.component('my-checkbox', {
template: '#checkbox-template',
data() {
return { checked: false, title: 'Check me' }
},
methods: {
check() { this.checked = !this.checked; }
}
});

index.html

<div id="app">...</div>
<script type="text/x-template" id="checkbox-template">
<div class="checkbox-wrapper" @click="check">
<div :class="{ checkbox: true, checked: checked }"></div>
<div class="title">{{ title }}</div>
</div>
</script>

inline template
用这个方法可以在父模板中定义组件的模板。需要给标签加上inline-template 属性,以便让 Vue 知道在哪找到它。

这个方法跟 x-template 的优缺点差不多,不过有一点区别,由于模板是定义在文档 body 里的,因此它的内容可以被爬虫识别,有利于 SEO。
app.js

Vue.component('my-checkbox', {
data() {
return { checked: false, title: 'Check me' }
},
methods: {
check() { this.checked = !this.checked; }
}
});

index.html

<div id="app">
...
<my-checkbox inline-template>
<div class="checkbox-wrapper" @click="check">
<div :class="{ checkbox: true, checked: checked }"></div>
<div class="title">{{ title }}</div>
</div>
</my-checkbox>
</div>

inline templates 和 x-template 可以与后端框架(例如Laravel Blade)中的模板引擎结合使用。

render 函数
render 函数要求你用纯 JavaScript 定义模板。具体语法可以参考 Vue 文档 ,大体就是通过调用 createElement(tag, options, childElements)创建模板节点。

这样做的好处是,它不需要编译,并且你可以完全利用 JavaScript 的能力,而不是局限于指令提供的功能。例如,模板里的循环只能通过v-for,但在 JavaScript 里你可以用任何数组方法。

但是,与其他模板选项相比,render 函数更加冗长和抽象,我估计没人愿意用这种方法写完整个应用。
app.js

Vue.component('my-checkbox', {
data() {
return { checked: false, title: 'Check me' }
},
methods: {
check() { this.checked = !this.checked; }
},
render(createElement) {
return createElement(
'div', {
attrs: {
'class': 'checkbox-wrapper'
},
on: {
click: this.check
}
},
[
createElement(
'div', {
'class': {
checkbox: true,
checked: this.checked
}
}
),
createElement(
'div', {
attrs: {
'class': 'title'
}
},
[this.title]
)
]
);
}
});

JSX
JSX 是 JavaScript 的一个扩展,它可以在 JavaScript 代码里使用特殊的模板语法。

JSX 本来是 React 的一个特性,在 Vue 的模板中使用它一直备受争议,因为一些开发人员认为它丑陋、不直观,跟 Vue 的气质不符。

但是,用 JSX 写 Vue render 函数,可读性高得多,也没那么抽象。不过它需要编译,因为浏览器可不认识 JSX。
app.jsx

Vue.component('my-checkbox', {
data() {
return { checked: false, title: 'Check me' }
},
methods: {
check() { this.checked = !this.checked; }
},
render() {
return <div class="checkbox-wrapper" onClick={ this.check }>
<div class={{ checkbox: true, checked: this.checked }}></div>
<div class="title">{ this.title }</div>
</div>
}
});

单文件组件
Vue.js 最受欢迎的特性之一就是 单文件组件(SFC)。它可以将模板和组件定义全部放在一个文件里。里面的模板会被 vue-loader 编译成 render 函数,因此也可以获得最佳的运行时性能。

单文件组件是一个.vue文件,比如Checkbox.vue,在 template 标签中定义模板, script 标签中定义组件属性和逻辑。然后就可以在应用中导入使用了。

只要你习惯使用 Vue CLI 或者其他构建工具,单文件组件是个不错的选择。
Checkbox.vue

<template>
<div class="checkbox-wrapper" @click="check">
<div :class="{ checkbox: true, checked: checked }"></div>
<div class="title">{{ title }}</div>
</div>
</template>
<script>
export default {
data() {
return { checked: false, title: 'Check me' }
},
methods: {
check() { this.checked = !this.checked; }
}
};
</script>

谁是王者
吧啦这么多,到底哪种方法最好?本文的答案是:单文件组件,因为它是几乎所有场景中最通用、最强大的选择。

实际上,以上每一种方法在特定的场景中都有优势,应该根据你的实际情况进行判断。熟悉每一种用法,说不定哪天就用上了呢!

作者简介:
李中凯
八年多工作经验 前端负责人,
擅长JavaScript/Vue。
掘金文章专栏:https://juejin.im/user/57c7cb8a0a2b58006b1b8666/posts
公众号:1024译站

本文已经获得李中凯老师授权转发,其他人若有兴趣转载,请直接联系作者授权。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,937评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,503评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,712评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,668评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,677评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,601评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,975评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,637评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,881评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,621评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,710评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,387评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,971评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,947评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,189评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,805评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,449评论 2 342