Appearance
全局组件注册
全局组件注册
上篇我们介绍了如何去写一个全局组件,接下来,我们需要了解一下,全局组件如何被模板使用呢? 把代码渲染出来有两个方案
- 通过
注册组件的方式,把代码注册为 vue 实例的组件,注册组件又分全局注册和局部注册两种方式 - 通过挂载点直接挂载vue实例, 即通过
new Vue({ el: '#id' })的方式
注册组件
要想注册组件,vue 提供了2中方式来注册组件,先来看看全局注册:
javascript
Vue.component('my-component-name', {
// ... 选项 ...
})
这些组件是全局注册的。也就是说它们在注册之后可以用在任何新创建的 Vue 根实例(new Vue)的模板中。但是需要注意的点是:
记住全局注册的行为必须在根 Vue 实例 (通过 new Vue) 创建之前发生。
而我们的全局组件以及和模板解耦了,运行时 Vue 实例已经创建完,所以直接通过 Vue.component 的方式可能行不通,那么我们可能需要通过局部注册方式来使用全局组件,根据 Vue 官网的表述,注册局部组件可以这样:
javascript
var ComponentA = { /* ... */ }
var ComponentB = {
components: {
'component-a': ComponentA
},
// ...
}
但是问题在于这样我们就必须提前知道组件的名称,所以有没有其他的方式?是否想到了前面我们再介绍模板组件的时候,提到了动态组件,这里是否可以通过动态组件来实现动态化呢?我们尝试改成这样:
javascript
<template>
<div
v-if="component"
:is="component"
:obj="props"
/>
</template>
export default {
data() {
return {
component: '',
}
},
created() {
this.component = 'component-a';
}
}
这样看起来是可以让组件动态的渲染出来,但是还存在一个问题 component-a 我们是个字符串,并没有描述是个啥玩意,所以我们可能需要动态注册 component-a。除了通过 Vue.component 的方式,我们可以使用 Vue-extend 来创建一个子类,这样就可以构造一个 Vue 组件:
javascript
this.compoent = Vue.extend({
template: '',
data: () => {}
});
再结合我们之前说的,我们的组件以及被编译好了静态的 js umd 和 css 所以,我们只需要把 js 和 css 动态的挂在到 DOM 上,再由 DOM 渲染加载。最后通过 window 的形式加载:
javascript
export default {
// ...
created() {
// 动态添加组件,用于可视化编辑场景
const {
name,
js,
css,
index,
} = this.config;
const component = window[name];
if (!component) {
const script = document.createElement('script');
const link = document.createElement('link');
script.src = js;
link.href = css;
link.rel= 'stylesheet';
// 动态注入 js 和 css
document.head.appendChild(link);
document.body.appendChild(script);
script.onload = () => {
// 加载完成
this.$emit('onRemoteComponentLoad', {
...window[name],
index,
});
this.component = Vue.extend(window[name].Component);
}
} else {
// 非动态化添加,用于server构建场景
this.$emit('onRemoteComponentLoad', {
...window[name],
index,
});
// 先有 props 再挂组件,不然 props 是 null 可能会有错
this.$nextTick(() => {
this.component = Vue.extend(window[name].Component);
});
}
},
}
既然我们的模板开发和组件开发已经解耦,有模板来动态加载渲染远程 js,如果全局组件内部运行出现了错误。那这部分的处理需要通过在容器组件上添加 errorCaptured 这个官方钩子,来捕获子组件的错误:
javascript
errorCaptured(err, vm, info) {
// todo
},
你可以在此钩子中修改组件的状态。因此在捕获错误时,在模板或渲染函数中有一个条件判断来绕过其它内容就很重要;不然该组件可能会进入一个无限的渲染循环。
动态实例
这种方式主要就是利用 Vue 可以动态的实例化到一个节点上 new Vue(component).$mount(component.$el)这种方式。我们看一下官方的介绍:
如果没有提供 elementOrSelector 参数,模板将被渲染为文档之外的的元素,并且你必须使用原生 DOM API 把它插入文档中。
所以这种形式的渲染,就必须要求我们提前感知组件需要插入到模板的具体 DOM 节点,无疑带来了开发成本。所以我们可以放弃这种方案,不做过多介绍,有兴趣的小伙伴可以自行尝试,如果有突破性进展,也可以互相交流学习,当然除了这些还有一些其他的弊端,比如:
- 需要一个稳定的挂载点
- 首次挂载需要等 DOM 流渲染完成后才能实例化
总结
本章我们主要介绍了远程组件的注册方式,回顾一下,我们通过动态注入 js 和 css 将组件的信息注册到了 window 上,然后再从 window中获取组件的信息来注册组件,最后通过动态组件的方式来挂在到页面。
有了这些知识后,我们再来思考之前模板章节提到的一个问题:当组件更新后,如何确保组件更新的影响面可控,也就是组件的更新策略我们该如何设计呢?