要看源码就得从最简单的开始,button够简单的了,就从他开始吧。
安装依赖后源码目录在:node_modules/element-ui/packages中,可以看到这里的文件夹命名是不是很熟悉,就是我们平时写的组件名,打开任何一个文件夹,都有一个src文件夹和一个index.js,src文件夹放组件,index.js用于注册组件
下面来看具体的button源码如何写的:
分析从三个方面着手:DOM结构,数据属性,事件
DOM结构:
按钮的DOM结构很简单,要显示成什么样子就由css样式及一些自带的属性决定了,这些就交给数据属性来控制
<button></button>
数据属性:
按钮的样式,大小,显示内容,类型等都需要通过数据属性来控制,这些数据属性分两块:
a、直接引用props
b、引用computed的属性
直接引用props的数据大家都能理解,为什么还用到computed呢?
这里就涉及到provider/inject,
provider/inject:简单的来说就是在父组件中通过provider来提供变量,然后在子组件中通过inject来注入变量。注意官方文档有一句话“向其所有子孙后代注入一个依赖,不论组件层次有多深,并在起上下游关系成立的时间里始终生效”,这样看来就明显了,父组件提供的属性只能在直接子组件中获取到,孙组件就获取不到,provide提供的属性就解决了这个问题。
为什么用到computed?答案就很明显了,防止全局设置/父组件的属性,孙组件获取不到
Vue.use(Element, { size: Cookies.get('size') || 'medium' // set element-ui default size })
有了provider/inject,这里配置的size才能全局起作用。
事件:
这里涉及到父子组件通信,子组件向父组件发消息可以用emit实现,父组件监听即可,一般情况下父组件监听的事件名都是自定义的,这里特殊了点,父组件直接监听了“click”事件,谁让button通常就一个点击事件呢
index.js:
js的作用很简单,注册组件并导出
最后:
源码DOM结构上有这样一句:
<span v-if="$slots.default"><slot></slot></span>
我们知道插槽有具名插槽和不具名插槽,“$slots.default”指代的就是不具名插槽。为什么要这么写,而不是:
<span><slot></slot></span>
这里其实是一个很细心的点,如果<el-button></el-button>不写内容,将会少一个span节点,渲染后的DOM是这样的
<button data-v-06af20c4="" type="button" class="el-button el-button--default el-button--medium"></button>
如果去掉v-if="$slots.default",渲染后将是这样的
<button data-v-06af20c4="" type="button" class="el-button el-button--default el-button--medium"><span></span></button>
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------
除了学知识,我们还可以从源码中学到一点规范:
:class="[ type ? 'el-button--' + type : '', buttonSize ? 'el-button--' + buttonSize : '', { 'is-disabled': buttonDisabled, 'is-loading': loading, 'is-plain': plain, 'is-round': round, 'is-circle': circle }
绑定的class做了归类,boolean类型的归为了一类,放到对象中,我们是不是也可以这样写,让代码更整齐呢