element 源码学习二 —— 简单组件学习

上一篇博客中学习了项目的结构,这篇博客来学几个简单的组件的实现。

在上一篇博客中我们提到了组件的源码都是存放在 packages 目录下的,所以我们从中挑一些组件来学习。先从简单的入手,来学习 button、radio、checkbox和InputNumber这四个组件的源码~

Button

找到 packages/button/ 目录下,先看看 index.js 做了什么?

import ElButton from './src/button';

// 注册全局组件
ElButton.install = function(Vue) {
  Vue.component(ElButton.name, ElButton);
};

export default ElButton;

其实就是获取组件,然后全局注册组件。很好理解,我们自定义组件也经常这么干。看看组件内容吧~





代码已注释~其实 script 部分很简单:通过inject和props获取数据,计算方法计算尺寸,事件处理点击事件。关键点在于button中的几个class。说白了,其实这就是个原生的button组件,只是样式上有所不同。
样式文件都是存在 packages/theme-chalk/ 目录下的。所以 button 的样式目录位于 packages/theme-chalk/src/button.scss。由于自己 CSS 非常渣,所以这步先跳过~

Radio

来看下Radio。嗯……目录位置在 packages/radio/ 目录下。index.js 文件和 button 是一样的 —— 导入组件、定义全局注册组件方法、导出。所以这边来看看 radio.vue 文件。




从代码中可以看到,radio不止是个input那么简单。后面还会显示的文本内容。script 部分的逻辑是:通过 inject 和 props 获取传参,data记录 radio 的 focus 状态。compute 中计算一些属性值;methods中处理onchange事件。radio 中多了些关于 radioGroup 的处理。具体我都写在注释中了。
另外要注意的还是 template 中的各类 class,radio的css文件想必也猜到了,文件位于 packages/theme-chalk/src/radio.scss到这里就会发现:这些简单组件主要是样式上面的处理,对于逻辑上处理并不大。
在 radio 中导入了 element-ui/src/mixins/emitter.js 文件,来看看它的作用是什么?

// src/mixins/emitter.js
// 广播
function broadcast(componentName, eventName, params) {
  // 遍历子组件
  this.$children.forEach(child => {
    // 组件名
    var name = child.$options.componentName;

    if (name === componentName) {
      // 触发事件
      child.$emit.apply(child, [eventName].concat(params));
    } else {
      // 执行broadcast方法
      broadcast.apply(child, [componentName, eventName].concat([params]));
    }
  });
}
export default {
  methods: {
    dispatch(componentName, eventName, params) {
      // 父级组件及其组件名
      var parent = this.$parent || this.$root;
      var name = parent.$options.componentName;

      // 有父级组件 同时 没有name 或者 name 不等于组件名
      while (parent && (!name || name !== componentName)) {
        // parent 向上获取父级组件
        parent = parent.$parent;

        if (parent) {
          name = parent.$options.componentName;
        }
      }
      // 触发 eventName 事件
      if (parent) {
        parent.$emit.apply(parent, [eventName].concat(params));
      }
    },
    broadcast(componentName, eventName, params) {
      broadcast.call(this, componentName, eventName, params);
    }
  }
};

由注释可见,dispatch 方法向上获取父级组件并触发 eventName 事件。broadcast 方法向下遍历子组件触发 eventName 事件。

至于 Vue 的 mixins 属性是干嘛的?

混入 (mixins) 是一种分发 Vue 组件中可复用功能的非常灵活的方式。混入对象可以包含任意组件选项。当组件使用混入对象时,所有混入对象的选项将被混入该组件本身的选项。

好啦,至于 radio 的 scss 解析?抱歉,暂时对scss不熟,之后补上~本篇关键将逻辑吧。

checkbox

找到 checkbox 的项目目录,index.js 逻辑是一样的~所以只需看看 checkbox.vue 文件




其实和radio逻辑差不多。从上面的内容中已知 emitter.js 用于触发子组件或父组件的事件,而参数上与radio也差不多。不同点有,在显示checkbox时,如果有true-babel 或 false-babel 属性和没有这两个属性显示的是不同的 checkbox(v-if,v-else)。其他都差不多。
具体代码注释即可。

InputNumber



先看HTML,这个组件由两个span按钮当做加减按钮,一个 el-input 组件显示数字结果。但是,对于最后两个 template 的作用不是很明白,不知道何时使用。
再看JS,代码中导入了 el-input 组件、 Focus 方法和 RepeatClick 方法。这两个方法之后详述。看到代码中还是以props、inject、compute方法来处理传入的数据。和前三个组件不同的地方在于,一是引用了组件需要处理组件,在 mounted 和 updated 方法执行时修改 el-input 组件中 input 的属性;二是加入了加法和减法的方法逻辑:求精度、做加减法、处理最大最小值限制。
从CSS角度上来说,要处理加法、减法按钮的位置和样式等功能……SCSS方面的内容暂且一概不论。

顺便看看 Focus 和 RepeatClick 方法~

// src/directives/repeat-click.js
import { once, on } from 'element-ui/src/utils/dom';
// on 添加监听事件
// once 监听一次事件

export default {
  bind(el, binding, vnode) {
    let interval = null;
    let startTime;
    // 执行表达式方法
    const handler = () => vnode.context[binding.expression].apply();
    // 清除interval
    const clear = () => {
      if (new Date() - startTime < 100) {
        handler();
      }
      clearInterval(interval);
      interval = null;
    };

    // 监听 mousedown 鼠标点击事件
    on(el, 'mousedown', (e) => {
      if (e.button !== 0) return;
      startTime = new Date();
      once(document, 'mouseup', clear);
      clearInterval(interval);
      // setInterval() 方法可按照指定的周期(以毫秒计)来调用函数或计算表达式。
      interval = setInterval(handler, 100);
    });
  }
};

其中导入的on和once方法类似于vue的方法,on用于监听事件,once监听一次事件。监听 mousedown 事件。如果触发每 100 毫秒执行一次 handler,如果监听到 mousedown 判断时间间隔如果短于100毫秒,执行 handler 方法。取消计时器并清空 interval。从而实现了重复点击的效果。

// src/mixins/focus.js
export default function(ref) {
  return {
    methods: {
      focus() {
        this.$refs[ref].focus();
      }
    }
  };
};

很简单的方法,就是用于定位一个元素,执行它的 focus 方法。

所以说:src 中除了 locale 目录用于国际化和 utils 工具目录外,其他目录下都是用于组件的一些配置上的.如 mixins 目录用于 mixins 项,directives 目录下的用于 directives 项。

最后

额……由于对于 CSS 比较菜,所以暂且不误人子弟,待我学成后再补上这部分内容。
总结下几个组件给我的感觉:其实 UI 框架主要在 UI 二字上。所以其实讲 UI 框架重点应该放在 CSS 上。逻辑部分其实蛮简单的。毕竟是组件不是整个项目,组件就是要易用性、可扩展性。
所以说组件的主要逻辑都是进行传参的处理和计算。
element的组件模式上其实是定义好 [component].vue 组件文件,然后导入到用户项目 Vue 实例的 components 项中来使用,其实和自定义 component 基本原理差不多。
下一篇,来看看一些复杂的组件的代码逻辑实现~~

打个广告

上海链家-链家上海研发中心需求大量前端、后端、测试,需要内推请将简历发送至 [email protected]

你可能感兴趣的:(element 源码学习二 —— 简单组件学习)