详解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 相关文章推荐
通过Jquery遍历Json的两种数据结构的实现代码
Jan 19 Javascript
input标签内容改变的触发事件介绍
Jun 18 Javascript
JQuery遍历元素的父辈和祖先的方法
Sep 18 Javascript
jquery网页日历显示控件calendar3.1使用详解
Nov 24 Javascript
JS实现身份证输入框的输入效果
Aug 21 Javascript
浅谈vue引入css,less遇到的坑和解决方法
Jan 20 Javascript
node.js自动上传ftp的脚本分享
Jun 16 Javascript
Angular2中监听数据更新的方法
Aug 31 Javascript
Vue搭建后台系统需要注意的问题
Nov 08 Javascript
vue prop属性传值与传引用示例
Nov 13 Javascript
详细介绍解决vue和jsp结合的方法
Feb 06 Javascript
Vue-Element-Admin集成自己的接口实现登录跳转
Jun 23 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
PHP自定义大小验证码的方法详解
2013/06/07 PHP
详解WordPress中提醒安装插件以及隐藏插件的功能实现
2015/12/25 PHP
浅析Yii2 GridView实现下拉搜索教程
2016/04/22 PHP
ajax调用返回php接口返回json数据的方法(必看篇)
2017/05/05 PHP
PHP 图片合成、仿微信群头像的方法示例
2019/10/25 PHP
基于逻辑运算的简单权限系统(实现) JS 版
2007/03/24 Javascript
利用onresize使得div可以随着屏幕大小而自适应的代码
2010/01/15 Javascript
Javascript call和apply区别及使用方法
2013/11/14 Javascript
解析JavaScript的ES6版本中的解构赋值
2015/07/28 Javascript
JS延时提示框实现方法详解
2015/11/26 Javascript
AngularJS 遇到的小坑与技巧小结
2016/06/07 Javascript
jQuery插件zTree实现的多选树效果示例
2017/03/08 Javascript
使用npm安装最新版本nodejs
2018/01/18 NodeJs
解决vue 打包发布去#和页面空白的问题
2018/09/04 Javascript
对angularJs中2种自定义服务的实例讲解
2018/09/30 Javascript
微信小程序bindinput与bindsubmit的区别实例分析
2019/04/17 Javascript
ZK中使用JS读取客户端txt文件内容问题
2019/11/07 Javascript
JS如何生成随机验证码
2020/03/02 Javascript
python解决字典中的值是列表问题的方法
2013/03/04 Python
python中文件变化监控示例(watchdog)
2017/10/16 Python
解读! Python在人工智能中的作用
2017/11/14 Python
TensorFlow实现非线性支持向量机的实现方法
2018/04/28 Python
Python requests库用法实例详解
2018/08/14 Python
TFRecord格式存储数据与队列读取实例
2020/01/21 Python
python filecmp.dircmp实现递归比对两个目录的方法
2020/05/22 Python
解决Ubuntu18中的pycharm不能调用tensorflow-gpu的问题
2020/09/17 Python
Python 按比例获取样本数据或执行任务的实现代码
2020/12/03 Python
跑鞋、网球鞋、网球拍、服装及装备:Holabird Sports
2016/09/19 全球购物
法国购买隐形眼镜和眼镜网站:Optical Center
2019/10/08 全球购物
Linux文件系统类型
2012/02/15 面试题
DTD的含义以及作用
2014/01/26 面试题
长安大学毕业生自我鉴定
2014/01/17 职场文书
大专生求职信
2014/06/29 职场文书
学习党章的体会
2014/11/07 职场文书
公司市场部岗位职责
2015/04/15 职场文书
数学备课组工作总结
2015/08/12 职场文书