背景
Vue 支持动态组件编译,使用原生组件定义方式创建对象:
// 动态创建一个 Vue 组件
const MyComponent = Vue.component('componentId', {
template: templateStr,
data: eval(dataStr),
methods: methodObj,
}
);
这个 methods
怎么动态生成呢?
JS 的 new Function
方案:可以使用 new Function
创建一个函数,但是它是一个匿名函数,为了让 Vue 能够使用这些函数,必须创建对应的具名函数,怎么实现呢?
在匿名函数的方法体中,返回一个具名函数,然后再调用一次这个匿名函数。
例如,一个弹框组件,提供一个 onClose
方法,动态生成:
const closeFuncDef = 'function onClose() {\n' +
' this.isShow = false;\n' +
' this.close();\n' +
' },';
再定义一个 methods
对象,传入这个字符串配置创建的方法对象:
// 定义一个匿名函数,它返回一个具名函数
const anonymousFuncDef = new Function(`return ${closeFuncDef}`);
// 调用这个匿名函数,得到那个具名函数对象
const withNameFuncDef = anonymousFuncDef();
// 设置这个函数属性的值为那个具名函数
methods = {
onClose: withNameFuncDef
}
这样就能被 Vue 动态编译并使用了。
函数对象如何反序列化并解析
正向思路是 methods
函数属性都通过读取文件获取字符串后,再解析出来,那么问题来了,JSON.parse
没法解析函数对象,怎么办呢?
另外 Vue.component
方式创建组件,怎么传递自定义的样式,以及 mounted/created
挂载事件呢?能不能支持复杂组件的定义呢?
要支持定制化前端文件,还差一点。
迂回解决办法,定义一个返回 methods
对象生成的匿名函数,两次调用后得到这个 methods
对象:
// methods 的配置
const methodStr = '{\n' +
' onSubmit() {\n' +
' this.isShow = false;\n' +
' },\n' +
' onCancel() {\n' +
' this.isShow = false;\n' +
' },\n' +
' }';
// 定义一个匿名函数,它返回一个有名函数
const funcDef = new Function(`return function generateCode() { return ${methodStr}}`);
// 调用这个匿名函数,得到那个有名函数对象
const methodDef = funcDef();
// 调用有名函数,得到 methods 对象
const methods = methodDef();
// 使用配置,构建 Vue 组件
Vue.component(componentId, {
template: templateStr,
data: eval(dataStr),
methods: methods,
mounted() {
},
created() {
},
destroyed() {
}
});
mounted/created
挂载事件可以直接作为初始化组件的参数。
启示录
纯定制 Vue 组件的各部分,有外部配置传入,技术上是成熟的,参考:《Vue 运行时编译组件》,这位网友还提供了在线测试路径,受这篇文章的启发很大。
本文描述的动态编译生成组件的方式,存在两个缺陷:
- 不能定义复杂组件:因为复杂组件引用的组件也需要动态编译的方式生成,编码有难度。
- 不能手动指定 css 样式 ,
Vue.component
这种方式的缺陷,《Vue.component 没有动态设置组建样式的能力》