乐趣区

【手牵手】搭建前端组件库(一)

手牵手搭建前端组件库
本文梳理如何搭建和构建前端组件库.

了解几个问题
为何需要组件化?
大部分项目起源都是源于业务方的各种各样的奇葩需求。随着公司的业务发展,公司内部开始衍生出很多的 B2C 系统、后台系统,前端部门也疲于应对越来越多同质化的项目,这些项目在很多基础模块层、源代码存在不小的相似,甚至存在相似的业务模块。
笔者曾经所在的一个电商团队,前端成员基本每个人多做过登录注册、购物车、支付、微信登录 …… 大量重复的业务代码。由于组内技术没有强制规范
本质上相同的东西,重复的去 code 就显得浪费.
分析这些问题发现:日渐增多的业务场景需求
前端资源有限,无法支持所有项目的快速迭代
公司内部诸多产品业务混乱、体验不统一
于是开发底层的工具去服务不同业务就很有必要:设计一套公司内部的基础组件库支撑各个前端项目,提升项目和业务的可用性和一致性。
一个前端团队拥有大量的业务场景和业务代码,相似的页面和代码层出不穷,如何管理和抽象这些相似的代码和模块,绝大多数团队会遇到这样的问题。不断的拷代码?修改代码?还是抽象成组件?显然后者更高效。所以在多项目存在高度的可控、底层依赖的情况下,前端实现组件库是最好的选择。
组件化,又或者组件抽离的目的是为了功能共享方便维护,其能够带来的好处是少写代码,统一管理、统一维护。一套基础组件代码千锤百炼精而又精,从而起到快速支撑业务迭代,提升开发效率的目的。
业务型组件库
前端组件库百花齐放,antd、element ui 这些基础组件库已经很强大,使用于各种业务场景。但是这些基础组件的粒度是基于单个交互,而在交互与产品之间隔着各种各样的模块和业务场景,产品的汇聚源于各种基础组件在业务逻辑的沾粘下集成为一个个项目,一个团队或多或少会有项目或模块存在功能、交互流程的重复、本质上的同质化。
所以 antd、element ui 这类组件库是基于单个非连续性的交互组件,一个组件代表着一次人机无副作用的操作与响应,其不思考实体、用户、终端的状态,最小化的暴露和响应组件内部状态。对于连续性的交互通常来说与特点的业务场景有关,存在诸多的外部依赖,目前都是在各个业务模块由用户(coder)自行编写。
有没有一种方法解决连续性交互流程的共用问题?
解决的办法是组件封装包含业务场景的连续性交互流程,利用组件化将内部依赖通过接口映射到外部。
前端架构部门为业务部门提供业务型组件库能够有效提高开发效率.
组件库设计思路
组件是对一些具有相同业务场景和交互模式、交互流程代码的抽象,组件库首先应该保证各个组件的视觉风格和交互规范保持一致。组件库的 props 定义需要具备足够的可扩展性,对外提供组件内部的控制权,使组件内部完全受控。支持通过 children 自定义内部结构,预定义组件交互状态。保持组件具有统一的输入和输出,完整的 API.
组件库的开发我们需要考虑:

组件设计思路、需要解决的场景
组件代码规范
组件测试
组件维护,包括迭代、issue、文档、发布机制

一个完整强大的组件库需要多方面努力,回归正题.
使用到的基础技术

vue cil 3
npm
webpack
rollup(v1.2.2)

Demo
下面就手把手搭建一个前端偏业务性的组件库。
组件库包括:

message 组件:一个封装用于呈现后台通过 websocket 推送到前台页面的实时消息模块;
pay 组件:一个封装用于实现商品支付的模块
share 组件:一个封装用于实现商品、文章、视频在各社交平台分享的模块

只抛出一个栗子,组件内部实现略~
这里注意组件抽取的粒度,组件的抽离以一个完整的连续性交互为目地。
组件依赖数据、交互事件、控制权的暴露需要考虑全面,不同的上层业务部门都有自己对组件可配置的不同渴望。需要权衡,不能把配置化给捣鼓的永无止境到很难堪的局面。笔者曾经就参与一个项目的组件化,组件抽离的面目全非,各种依赖、环境、状态的配置,导致最后只有组件编写人员在看文档加回忆的情况下才能搞清楚其来龙去脉.
从简单的开始~
1、初始化组件库目录
创建一个空项目
// 新建一个项目
vue create qw-ui
经过 vue cil3 初始化后的 qw-ui 目录:
├─docs

├─public

├─src
│ .gitignore
│ babel.config.js
│ package-lock.json
│ package.json
│ README.md
│ vue.config.js

此时为了方便组件库的代码管理,将目录结构修改为:
├─src // 用作示例 Demo

├─packages // 新增 packages 用于编写存放组件

├─lib // 新增 lib 用于存放编译后的输出文件
│ .gitignore
│ babel.config.js
│ package-lock.json
│ package.json
│ README.md
│ vue.config.js

目录结构可以更具需要调整.
2、修改 vue.config.js 配置
vue cli3 提供一个可选的 vue.config.js 配置文件。这个文件存在则他会被自动加载,所有的对项目和 webpack 的配置,都在这个文件中。
修改 vue.config.js 配置的目的主要是:

使 Demo 可访问,实现对 src 目录的编译处理;
提供对 package 的编译、构建处理

做以下两处修改:
修改项目的入口
entry 字段为项目入口
入口修改使用 Vue CLI 3 的 page 属性来配置:
module.exports = {
pages: {
index: {
// page 的入口
entry: ‘src/main.js’,
// 模板来源
template: ‘public/index.html’,
// 在 dist/index.html 的输出
filename: ‘index.html’
}
}
}
添加对 packages 目录的编译处理
packages 是我们后来新增的一个目录,默认是不被 webpack 处理的,所以需要通过添加配置对该目录的编译支持。
新增编译处理目录,需要通过 webpack 的链式操作 chainWebpack 函数实现:
module.exports = {
pages: {
index: {
// page 的入口
entry: ‘examples/main.js’,
// 模板来源
template: ‘public/index.html’,
// 在 dist/index.html 的输出
filename: ‘index.html’
}
},
chainWebpack: config => {
// packages 和 examples 目录需要加入编译
config.module
.rule(‘js’)
.include.add(/packages/)
.end()
.include.add(/src/)
.end()
.use(‘babel’)
.loader(‘babel-loader’)
.tap(options => {
// 修改它的选项 …
return options;
});
}
}
执行 npm run vue-cli-service serve , 实现对 Demo 的访问.
3、编写 packages 组件库
创建一个 message 组件

创建组件

在 packages 目录下,所有的单个组件都以文件夹的形式存储,这里创建一个目录 message 文件夹;
在 message/ 目录下创建 src/ 目录存储组件源码,所有 message 依赖的除第三方资源都存放与该目录下;
在 /message 目录下创建 index.js` 文件对外提供对组件的引用

示例代码:
message/index.js 对外提供应用
// message/index.js

import message from ‘./src/message ‘

message .install = function (Vue) {
Vue.component(message .name, message)
}

export default message

// message/src/message .js
<template>
<div class=”message”>
<el-row class=”message-test”>
<el-col :span=”12″ class=”message-row”><p class=”text”>hello {{message}}</p></el-col>
<el-col :span=”6″>
<img src=”./st.png”/>
</el-col>
</el-row>
</div>
</template>

<script>
import ‘./index.scss’
export default {
name: ‘v-message’, // 申明组件的 name 属性
props: {
message: String
}
}
</script>

需要注意的是,组件 mesage 必须声明 name 属性,这个 name 就是组件的标签,如:
<v-message><v-message/>
packages/message 目录结构如下:
packages/message
├─index.js

├─src
│ message.vue
│ st.png // 组件依赖的图片
│ index.scss // 组件依赖的样式文件
导出 packages 组件库
修改 /packages/index.js 文件,整合所有组件,并对整个组件库进行导出:
// 导入组件
import hello from ‘./hello’

// 存储组件列表
const components = [
hello
]

// 定义 install 方法,接收 Vue 作为参数。如果使用 use 注册插件,则所有的组件都将被注册
const install = function (Vue) {
// 判断是否安装
if (install.installed) return
// 遍历注册全局组件
components.map(component => Vue.component(component.name, component))
}

// 判断是否是直接引入文件
if (typeof window !== ‘undefined’ && window.Vue) {
install(window.Vue)
}

export default {
// 导出的对象必须具有 install,才能被 Vue.use() 方法安装
install,
// 以下是具体的组件列表
hello
}

到此,构建组件库的环境准好好了

### 4、发布组件库到 npm

packages 目录的编译打包
在 package.json 的 scripts 字段中新增一下命令:
“lib”: “vue-cli-service build –target lib –name kui –dest lib packages/index.js”
vue cil3 提供了 [库模式](https://cli.vuejs.org/zh/guide/build-targets.html#%E5%BA%93) 来打包第三方库的开发,packages 的编译打包需要使用库模式

–target: 构建目标,默认为应用模式。这里修改为 lib 启用库模式。
–dest : 输出目录,默认 dist。这里我们改成 lib
[entry]: 最后一个参数为入口文件,默认为 src/App.vue。这里我们指定编译 packages/ 组件库目录。
在 vue cil3 库模式中,Vue 是外置的。这意味着包中不会有 Vue,即便你在代码中导入了 Vue。如果这个库会通过一个打包器使用,它将尝试通过打包器以依赖的方式加载 Vue;否则就会回退到一个全局的 Vue 变量。
配置好了后,执行编译命令:
npm run lib
稍后控制台输出,即编译完成:
DONE Compiled successfully in 5988ms16:05:35

File Size Gzipped

lib\kui.umd.min.js 8.08 KiB 4.55 KiB
lib\kui.umd.js 17.78 KiB 7.31 KiB
lib\kui.common.js 17.41 KiB 7.19 KiB
lib\kui.css 0.10 KiB 0.10 KiB

Images and other types of assets omitted.

Total task duration: 8.71s
“`

package.json 配置

name: 包名,该名字是唯一的。可在 npm 官网搜索名字。

version: 版本号,每次发布至 npm 需要修改版本号,不能和历史版本号相同。

description: 描述。

main: 入口文件,该字段需指向我们最终编译后的包文件。

keyword:关键字,以空格分离希望用户最终搜索的词。

author:作者

private:是否私有,需要修改为 false 才能发布到 npm

license:开源协议
参考配置:
{
“name”: “qw-ui”,
“version”: “0.1.0”,
“private”: false,
“main”: “lib/kui.umd.min.js”,
“description”: “qw-ui”,
“keyword”: “qw-ui”,
“author”:”luojh”,
“scripts”: {
“serve”: “vue-cli-service serve”,
“build”: “vue-cli-service build”,
“lint”: “vue-cli-service lint”,
“lib”: “vue-cli-service build –target lib –name kui –dest lib packages/index.js”
}
}

添加 .npmignore 文件
发布时,只有编译后的 lib 目录、package.json、README.md 才需要被发布。所以通过配置.npmignore 文件忽略不需要提交的目录和文件。
# 忽略目录
examples/
packages/
public/

# 忽略指定文件
vue.config.js
babel.config.js
*.map

# 本地文件
.env.local
.env.*.local

# 日志文件
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# 编辑器缓存文件
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw*

发布到 npm
首先需要在 npm 官网上注册一个账号,通过 npm adduser 命令创建一个账户,或者在 npm 官网注册
注册完成后在本地命令行中登录:
npm login
执行发布命令,发布到 npm
npm publish

npm 淘宝镜像不支持 publish 命令,如果设置了淘宝镜像,publish 前需将镜像设置会 npm :
npm config set registry http://registry.npmjs.org

npm publish 时,本地 cmd 终端需通过管理员运行

### 5. 使用组件库
安装发布的组件库:
npm i qw-ui
使用组件:
# 在 main.js 引入并注册
import qwui from ‘qw-ui’
Vue.use(qwui)

# 在组件中使用
<template>
<v-message message=” hello 333 :: 使用 kui 组件库 ”></v-message>
</template>
<script>
export default {
data () {
return {
}
}
}
</script>
完!

退出移动版