要看源码就得从最简单的开始,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类型的归为了一类,放到对象中,我们是不是也可以这样写,让代码更整齐呢