详解Vue 动态添加模板的几种方法


Posted in Javascript onApril 25, 2017

以下方法只适用于 Vue1.0 版本,推荐系数由高到低排列。

通常我们会在组件里的 template 属性定义模板,或者是在 *.vue 文件里的 template 标签里写模板。但是有时候会需要动态生成模板的需求,例如让用户自定义组件模板,或者设置组件的布局。

例如要做一个类 select 的组件,用户传入 options 数据,通过 value prop 获取选中值,最基本的原型如下。

Vue.component('XSelect', {
 template: `
 <div class="select">
 <input :value="value" readonly />
 <div
 class="option"
 v-for="option in options"
 @click="value = option.value">
 <span v-text="option.label"></span>
 </div>
 </div>`,

 props: ['value','options']
})

如果此时需要增加一个 API 支持让用户自定义 option 部分的模板。此处用 slot 并不能解决问题。

通过 $options.template 修改

通过打印组件对象可以获得一个信息,在 $options 里定义了一个 template 属性,写在 template 标签里的模板,最终编译后也会在 $options.template 里。通过文档的生命周期 可以得知,在 created 的时候, 实例已经结束解析选项, 但是还没有开始 DOM 编译 也就是说,如果用户通过 prop 的数据我们可以获得,但是模板其实还没有渲染成 DOM。经过测试,在 created 修改 this.$options.template 是可以改变最终生成的 DOM 的,同时也能拿到 props 的内容。

那么我们可以修改下代码,使其支持自定义模板

Vue.component('XSelect', {
 props: [
'value',
'options',
 {
 name: 'template',
default:'<span v-text="option.label"></span>'
 }
 ],

 created() {
varoptionTpl =this.template

this.$options.template =`
 <div class="select">
 <input :value="value" readonly />
 <div
 class="option"
 v-for="option in options"
 @click="value = option.value">
${optionTpl}
 </div>
 </div>`
 }
})

用户使用是就可以传入模板了

<x-select
:value.sync="value"
template="<span>标签: {{ option.label }}, 值: {{ option.value }}</span>"
:options="[
 {value: 1, label: 'a'},
 {value: 2, label: 'b'},
 {value: 3, label: 'c'}
 ]">
</x-select>

可能存在的问题

我们知道 Vue 在内部帮我们做了许多优化,但是在这里可能会由于某些优化导致动态拼接的模板无法渲染成功。例如这里我们不使用 v-for 而是手工遍历 options 生成需要的 HTML

consttpl = options.map(opt =>`<div>${this.optionTpl}</div>`)

this.$options.template =`
 <div class="select">
 <input :value="value" readonly>
${tpl}
 </div>`

这里会导致一个 BUG,如果一个页面有多个 x-select 组件,并且 options 长度不一样,会导致长的 options 的组件后面几个选项渲染不出来。究其原因是 Vue 会帮我们缓存模板编译结果。翻看代码可以找到 vue/src/instance/internal/lifecycle.js 里有做优化,同时提供的 _linkerCachable 本意是给 内联模板 使用。我们可以通过设置 this.$options._linkerCachable = false 达到我们的目的。

这样我们就可以开发让用户自定义布局的组件了,用户传入布局参数,通过手工拼接模板,设置了 _linkerCachable = false 也不会被缓存。

通过 $options.partials 动态添加 partial

使用 partials 也能达到添加自定义模板的目的,但是通常的做法是要全局注册 partial,这么做并不优雅。 vue-strap 就是这么做的。如果重名了会被覆盖(初次渲染不会,但是数据更新重新渲染 DOM 时就会被覆盖)。

通过文档我们知道可以在组件内部通过 partials 属性注册局部的 partial,因此自然而然也可以在 this.$options.partials 去动态添加了。

Vue.component('XSelect', {
 template: `
 <div class="select">
 <input :value="value" readonly />
 <div
 class="option"
 v-for="option in options"
 @click="value = option.value">
 <partial name="option"></partial>
 </div>
 </div>`,

 props: ['template','value','options'],

 partials: {
 option: '<span v-text="option.label"></span>'
 },

 created() {
if(this.template) {
this.$options.partials.option =this.template
 }
 }
})

用 interpolate 渲染模板

这种方式就略显蛋疼,而且效率最差。 interpolate 也是我最开始做动态渲染模板想到的方式,不推荐使用。

Vue.component('XSelect', {
 template: `
 <div class="select">
 <input :value="value" readonly />
 <div
 class="option"
 v-for="option in options"
 @click="value = option.value"
 v-html="renderOption(option)">
 </div>
 </div>`,

 props: [
'value',
'options',
 {
 name: 'template',
default:'<span v-text="option.label"></span>'
 }
 ],

 methods: {
 renderOption(option) {
this.option = option
returnthis.$interpolate(this.template)
 }
 }
})

Vue2.0

目前并没有找到合适的解决方案。2.0 的 Vue 将 compile 工作提前,并且 compiler 也是单独一个包(除非你直接引用的是 vue.js 文件,包含 compiler 和 runtime,那么第一种方法是适用的),那么并不能动态的生成模板。除非用户传入的是 render 能识别的 DOM tree。

按照这样的思路,其实可以让用户传入的模板预先编译好,传入到组件内,拼接 DOM tree 看起来也能解决问题。那么可以这么玩。

看看就好, 性能太渣

首先要安装 Vue JSX 的 相关插件

组件

Vue.component({
 name: 'XSelect',

 render(h) {
// 这里获得的 this.template 其实是一个函数,调用该函数返回 DOM
// 因此这里的关键代码是拼接一个新的函数,接受 `option` 参数以及上下文
// 使用 new Function 创建一个新函数

return(
<divclass="select">
<inputvalue={this.value}readonly/>
 {
 this.options.map(option =>
<div
on-click={() => this.$emit('input', option.value) }
 class="option">
 { new Function('option', 'return ' + this.template)(option)(h) }
</div>
 )
 }
</div>)
 },

 props: ['template', 'value', 'options']
})

入口文件

newVue({
 el: '#app',

 data () {
return{
 value: ''
 }
 },

 created() {
// 初始化需要传入的模板,这里会被 Vue 的 JSX 插件转成 DOM tree
this.template = h =><span>标签: { option.label }, 值: { option.value }</span>
 },

 render(h) {
return(
<x-select
v-model="value"
:template="template"
:options="[
 {value: 1, label: 'a'},
 {value: 2, label: 'b'},
 {value: 3, label: 'c'}
 ]">
</x-select>)
 }
})

综上,在 Vue 1.x 里不存在 预编译 的概念,所以想动态修改模板还是有许多方式的,甚至还可以结合 <slot></slot> 取到 slot 里的内容拼接进模板里。但 2.0 就麻烦了,并找不到理想的方法。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
Javascript 继承机制的实现
Aug 12 Javascript
JQuery记住用户名和密码的具体实现
Apr 04 Javascript
JQuery实现鼠标滚轮滑动到页面节点
Jul 28 Javascript
js限制文本框只能输入中文的方法
Aug 11 Javascript
Angularjs 滚动加载更多数据
Mar 17 Javascript
JS中artdialog弹出框控件之提交表单思路详解
Apr 18 Javascript
Vue.js动态添加、删除选题的实例代码
Sep 30 Javascript
javascript实现获取指定精度的上传文件的大小简单实例
Oct 25 Javascript
浅谈Angular HttpClient简单入门
May 04 Javascript
在vue中实现某一些路由页面隐藏导航栏的功能操作
Sep 21 Javascript
原型和原型链 prototype和proto的区别详情
Nov 02 Javascript
vue自定义右键菜单之全局实现
Apr 09 Vue.js
详解vue-cli + webpack 多页面实例应用
Apr 25 #Javascript
基于Vue实现timepicker
Apr 25 #Javascript
VueJS如何引入css或者less文件的一些坑
Apr 25 #Javascript
详解Angular 4.x 动态创建组件
Apr 25 #Javascript
Angular 4.x中表单Reactive Forms详解
Apr 25 #Javascript
Angular 4.x 动态创建表单实例
Apr 25 #Javascript
AngularJS动态菜单操作指令
Apr 25 #Javascript
You might like
基于MySQL体系结构的分析
2013/05/02 PHP
基于php在各种web服务器的运行模式详解
2013/06/03 PHP
php 7新特性之类型申明详解
2017/06/06 PHP
PHP读取Excel内的图片(phpspreadsheet和PHPExcel扩展库)
2019/11/19 PHP
js判断输入是否为正整数、浮点数等数字的函数代码
2010/11/17 Javascript
JavaScript 数组详解
2013/10/10 Javascript
JavaScript设计模式之单件模式介绍
2014/12/28 Javascript
jQuery动画显示和隐藏效果实例演示(附demo源码下载)
2015/12/31 Javascript
AngularJS基础 ng-keypress 指令简单示例
2016/08/02 Javascript
AngularJS实践之使用ng-repeat中$index的注意点
2016/12/22 Javascript
js实现无缝滚动图
2017/02/22 Javascript
ES6入门教程之Class和Module详解
2017/05/17 Javascript
jquery加载单文件vue组件的方法
2017/06/20 jQuery
javascript实现Java中的Map对象功能的实例详解
2017/08/21 Javascript
微信小程序授权登录及解密unionId出错的方法
2018/09/26 Javascript
详解基于vue-cli3.0如何构建功能完善的前端架子
2018/10/09 Javascript
vue实现codemirror代码编辑器中的SQL代码格式化功能
2019/08/27 Javascript
js实现坦克移动小游戏
2019/10/28 Javascript
详解Vue后台管理系统开发日常总结(组件PageHeader)
2019/11/01 Javascript
基于JS正则表达式实现模板数据动态渲染(实现思路详解)
2020/03/07 Javascript
如何在vue-cli中使用css-loader实现css module
2021/01/07 Vue.js
详解在Python中处理异常的教程
2015/05/24 Python
Python机器学习之K-Means聚类实现详解
2018/02/22 Python
flask中过滤器的使用详解
2018/08/01 Python
浅谈Python描述数据结构之KMP篇
2020/09/06 Python
python爬虫beautifulsoup解析html方法
2020/12/07 Python
运动鞋、街头服装、手表和手袋的实时市场:StockX
2020/11/25 全球购物
Tomcat的缺省是多少,怎么修改
2014/04/09 面试题
家长评语大全
2014/01/22 职场文书
银行开户授权委托书格式
2014/10/10 职场文书
销售辞职信范文
2015/03/02 职场文书
捐书活动倡议书
2015/04/27 职场文书
525心理健康活动总结
2015/05/08 职场文书
2016年小学“感恩教师”主题队日活动总结
2016/04/01 职场文书
与Windows10相比Windows11有哪些改进?值不值得升级?
2021/11/21 数码科技
CSS三大特性继承性、层叠性和优先级详解
2022/01/18 HTML / CSS