element组件分析
如何使用vue写一个组件库
组件以插件的形式引入使用,当然,也可以直接在页面引入组件文件,两者按需使用。
安装插件:
vue.install源码:
通过源码可知,vue不会重复安装同一个插件。以第一次安装为准
现在,可以在代码中使用组件啦~
我是按钮按钮
以上,是一个非常简单的组件库实现。 现在来看看element组件库是如何实现的。
element组件项目结构
这里重点说下packages目录和src目录
整个目录结构非常清晰。
button模块解析
button模块目录,有一个index.js作为模块入口
ElButton.install = function(Vue) {
Vue.component(ElButton.name,ElButton);
};
export default ElButton;
在index.js文件中,对组件进行拓展,添加Install方法。
element组件入口文件解析
定义一个install方法
const install = function(Vue,opts = {}) {
locale.use(opts.locale);
locale.i18n(opts.i18n);
将所有的功能模块进行注册。
components.map(component => {
Vue.component(component.name,component);
});
注册插件
Vue.use(Loading.directive);
const ELEMENT = {};
ELEMENT.size = opts.size || '';
绑定Vue实例方法
Vue.prototype.$message = Message;
};
if (typeof window !== 'undefined' && window.Vue) {
install(window.Vue);
}
最后,将所有功能模块和install方法一起导出。这样当引入element-ui时,便可以使用vue.use(element-ui)进行注册,即将所有的功能组件进行全局注册。
module.exports = {
version: '2.3.8',locale: locale.use,i18n: locale.i18n,install,Button,}
module.exports.default = module.exports;
我写的组件与elemnet组件有什么不同
代码实现
1.html语义化
element组件实现时,html基本实现了语义化标签。
- 这样在无CSS样子时也容易阅读,便于阅读维护和理解。
- 便于浏览器、搜索引擎解析。 利于爬虫标记、利于SEO
标记组件。 Badge 标记组件部分源码:
ps: 自己写代码都是div span
2.兼容 v-model
element组件基本都兼容了v-model绑定值,组件使用起来更加舒适~ 兼容v-model需要做一下几点:
- props中要定义value属性。
- 数据变化后,通过事件触发父组件更新数据,同时传递变更后的值。
(如text元素使用input事件来改变value属性 和 checkbox使用的change事件来改变check属性)
input组件源码:
3.组件之间传递数据
vue中,存在几种组件之间数据传递的方案:
- props
- attrs
- provide / inject
- this.$parent/$this.$children
在日常开发中,父子组件之间数据传递用到比较多的方案是props。当组件层次比较深,就使用attrs来透传数据:
element组件,在父子组件传递数据也是使用props,但是当组件层次比较深,或者不清楚组件层次时,使用的是:provide / inject
关于provide / inject:
“这对选项需要一起使用,以允许一个祖先组件向其所有子孙后代注入一个依赖,不论组件层次有多深,并在起上下游关系成立的时间里始终生效” --vue文档简单来说,就是父组件通过provide来提供变量,子组件通过inject来引用变量。 vue的inject源码:
provide是向下传递数据,先获取provide内容,然后传递给vm._provided设置成全局数据。inject会根据选项的 key 数组一层层向上遍历,拿到结果。
provide 相对于props,实现了跨层级提供数据。需要注意的是provide不是响应式的。
4.组件通信
emit/props传递函数
两者都是通知父组件执行事件的方法,但是有一定的区别:
- emit执行的是异步方法,props传递的函数在子组件中执行作为同步函数的形式执行的。
- emit无法返回函数结果,props传递的函数可以返回函数结果。
发布订阅
对于组件嵌套过深,element自己实现了一个简易版的发布订阅方式:
{
# 获取组件名称
var name = child.$options.componentName;
# 如果组件名称和要触发的事件组件名称相同
if (name === componentName) {
# 当前子组件,调用$emit方法
child.$emit.apply(child,[eventName].concat(params));
} else {
# 如果没有相等,那就继续查找当前子组件的子组件
broadcast.apply(child,[componentName,eventName].concat([params]));
}
});
}
组件设计
1.扁平化参数
- 传入的参数尽量设计简单点,避免复杂的对象。过于复杂的数据,在watch或者update的情况下,影响性能
- 扁平化的props也可以更好的更新数据,重置数据。其次,复杂的数据变更,外部可能会监听不到数据变化。
- 如果定义传入的传入数据是一个对象,那组件内部就要做大量的工作,来判断外部擦混入的对象的属性值是否正确,并找出需要的数据内容,增加了组件工作量,也不便组件的后续维护。
2.良好的api接口设计
- 保持组件外部提供接口的精简,不要过于泛滥的提供接口。
- 组件可定制,如果常量变为 props 能应对更多的情况,那么就可以作为 props从父组件引入。原有的常量可作为默认值。
按钮组件的样式存在默认样式,但是可以通过type传入类型,定制button组件样式,使组件可以适用更多场景。
3.可扩展性
组件在使用过程中,会不断的优化添加功能,但是组件的内部变更不能影响组件的使用,这就需要组件有很好的扩展性,在一开始,能够提供足够比较友好的接口。
如何实现?
- 预留“锚点”
在组件中预留一些“插槽”,使用组件的时候,可以再“插槽”中注入自定义的内容,从而改变组件渲染结果。element组件库在这方面做得很好。 input组件部分源码:
Input组件预留了四个“插槽”,允许使用者在前后位置都可以插入内容。
- 提供丰富的钩子函数,使用者在数据变化时,能对数据进行相应处理
element组件提供了丰富的钩子函数:
4.错误处理
组件要能接受一定的错误使用,能针对可预知的错误使用进行处理。
- 给props属性设置多个数据类型,同时保证传入和传出的数据类型相同。
- 如果组件中,某个字段是父组件一定要传入的,需要把props属性的require设置为true。
- 给重要的prop属性设置默认数据。
- 兜底:数据展示或者使用父组件传入内容之前,要先判断数据是否存在。
(编辑:李大同)
【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!
|