在提笔写下这篇文章之前,我查阅了很多的平台的文章,看下大家都怎样把一个话题写好,也学习到不少东西,非常感谢有这样一个平台可以和大家互相交流。
我尽可能写得简单清晰,让大家看完可以马上从零开始,记得要自己动手丰衣足食。
写这篇文章原因是因为刚好年底做项目总结,把公司产品做了规划,把组件做了整合整理,我们用 Vue2+,于是造了*,也借此机会给感兴趣的朋友分享下经验。
前端组件化是当今热议的话题之一,也是我们在项目开发中经常会碰到的问题,目前各个大厂开源了自己的 UI 库,有入 iView、Element 等,但是现实中存在一些问题。
比如每个公司业务组件不尽相同,没有办法完全满足需求,又或者各位 Geek 想通过学习框架,来打造属于自己的一套组件库,那么该如何去做呢?
本话题分为以下 6 部分内容:
-
环境配置;
-
代码组织结构;
-
开始第一个组件;
-
思考全局组件;
-
编写 API 文档;
-
打包发布。
可能需要你会一点 webpack、ES6 和 Vue 的知识,以下代码为了直观,大部分会以截图方式展示,只有特殊地方会以 code 方式出现。
具体代码可以查看线上版本 XMUI:https://github.com/monw3c/xmui。
1. 环境配置
工欲善其事必先利其器,IDE 我推荐用 VSC,必装插件有 Vetur、Eslint、
VSC 里用户设置添加:
"eslint.autoFixOnSave": true, "eslint.validate": [ "javascript",{
"language": "vue",
"autoFix": true
},"html",
"vue" ], "files.associations": {
"*.vue ": "vue" }
使 VSC 可以高亮 .vue 文件,Eslint 可以规范代码。用 Mac 的童鞋,Command Line 建议安装 iTerm2,接下来使用官方的 vue-cli 生成项目结构作为我们的库的结构。
全局安装 vue-cli:
cnpm install -g vue-cli
创建一个基于 webpack 模板的新项目并安装依赖:
vue init webpack projectName cd projectName npm install npm run dev
成功的话会显示 Vue 官方的首页。
第一步(请跟着动手),在 src 目录下建 comps(名字随你喜欢),放置组件库全部文件。在根目录 main.js 文件里加入:
import xmui from ‘./comps/index’
Vue.use(xmui)
router 目录的 index.js 修改如下:
import Vue from ‘vue’
import Router from ‘vue-router’
Vue.use(Router) export default new Router({ routes: [
{
path: '/',
name: 'home',
component: (resolve) => {
require(['@/views/home'], resolve)
}
} ] })
新建 views 目录,新建 home.vue 作为展示页模板。
接着需要对 webpack 配置文件稍作一下调整,在 build 目录新增三个文件:
在根目录 package.json 里添加三个 scripts:
“scripts”: { … “package:dev”: “webpack —config build/package.dev.config.js”, “package:prod”: “webpack —config build/package.prod.config.js”, “package”: “npm run package:prod && npm publish && npm run build” }
需要修改几处地方。
(1)package.config.js
修改组件库的 index.js 为入口文件,这里的 package 不是组件目录,是 npm run package:prod 最终生成的压缩目录。
(2)package.json 的 main 配置是
当从 npm 安装包后,告诉 node_modules 查找引用的路径。
(3)调整 dist 目录生成到 docs 文档内,这样的做法是放到 github pages 上可以同时查看文档和示例:
2. 代码组织结构
先看目录截图:
总体分为 3 块,把各个组件都放在 components 目录内,公共样式放在 styles,index.js 作为入口文件。
首先 components 是所有的组件集合,以 button 组件目录为例:
一般就是 .vue、.scss、.js 三种类型文件组成,具体的实现在第 3 点开始写第一个组件详细介绍。
再看 styles 目录,应该从名字童鞋们可以看懂都是表示什么意思,样式组织我用的是 BEM 规范,分为 normalize(reset 样式)、varibles(各种变量定义,例如字体、背景颜色、按钮颜色等)、icon(图标类),mixin(方法函数)、index 为入口文件,样式写法建议直接看线上 styles 库:https://goo.gl/Tss1Ry,最终会是这样:
这里有个更好的方法,可以通过方法自动生成组件样式的 import,免去每次增加一个组件都需要添加一遍,具体留给童鞋们去实现。
最后看 index.js 入口文件(既 Vue 组件注册文件):
上面这段代码是官方的写法,意思就是把上面的所以组件注入到 Vue 组件对象里,这样可以让组件支持全局引入和按需引入。
到这里基本的框架结构就说完了,准备写第一个组件吧!
3. 开始第一个组件
还是以 button 为例,在上一节提到过,有三种类型文件,在这个组件里,考虑在项目使用中有两种情况:button 和 button group(按钮组),分别建立四个文件 button.vue、button-group.vue、button.scss 和 index.js。
先说 button.vue 的设计,<template></template>
里写入 html 代码:
<button class="xm__btn" @click="handleClick" :style="{backgroundColor: bgColor, color: color, 'border-color':borderColor}" :class="[ 'xm__btn--'+type, {'is-plain': plain, 'is-round': round, 'is-long': long, 'xm__btn--block': block, 'no-radius': noRadius}, iconClass ]" :disabled="disabled" > <i :class="icon" v-if="icon"></i> <i class="xm__icon--loading" v-if="loading && !icon"></i> <slot></slot> </button>
既然是组件,就要考虑到可复用,可扩展,通过 props 传入不同参数和类型,来显示不同的按钮,<script></script>
里写入:
export default { name: 'xm-button', // 组件名,例如这样用 <xm-button> 按钮 </xm-button> props: { // 父组件传入的值 type: { type: String, default: 'default' }, long: Boolean, loading: { type: Boolean, default: false }, noRadius: { type: Boolean, default: false }, bgColor: { type: String, default: '' }, borderColor: { type: String, default: '' }, icon: { type: String, default: '' }, color: { type: String, default: '' }, block: Boolean, disabled: Boolean, plain: Boolean, round: Boolean }, methods: { // 绑定的方法 handleClick (event) { if (this.disabled) return this.$emit('click', event) // 传播方法名为 click,你也可以自定义其他名字 } }, computed: { // 计算属性 iconClass () { if (this.icon !== '') return 'xm__hasIconBtn' } } }
计算属性,可以进行缓存,只有在它的相关依赖发生改变时才会重新求值,可以减少性能开销。
组件调用是这样的:
最终样子:
再来看 button-group.vue 的设计:
<template> <div class="xm__btn--group"><slot></slot></div> </template> <script> export default { name: 'xm-button-group' } </script>
提供 slot 来按需插入,组件调用是这样的:
<xm-button-group class="btn__group"> <xm-button type="warning"> 警告 </xm-button> <xm-button type="primary" @click="btnClick" icon="xm__icon--checked" > 主要 </xm-button> <xm-button type="success" icon="xm__icon--loading"> 成功 </xm-button> </xm-button-group>
最终样子:
样式我用的是 .scss(你也可以用 .less),这里以.xm__btn--default
为例:
命名我用 BEM 的规范,如果不了解可以看这里 BEM:https://goo.gl/UnYbkQ。
最后是 index.js 文件,export 模块:
是不是很简单?!一个组件就写完了。