在日常开发过程中,构建组件库是必不可少的一环,此篇文章就是形容如何搭建一个残缺的组件库,解决组件库开发公布过程中的如下问题:
- 如何在起码的依赖下疾速开发一个 vue 组件。
- 如何将所有的包搁置在一个 git 仓库内。
- 如何将 git 仓库内的所有包一键公布。
- 如何治理所有包的依赖,缩小包的体积。
- 如何疾速创立组件示例。
- 如何打包组件,webpack?
疾速原型开发
开发组件和开发我的项目是不一样的,在开发组件的时候,咱们心愿可能有一种工具可能疾速针对某个 vue 文件搭建开发环境,并且在公布的时候可能对其进行打包编译,此时咱们能够应用 @vue/cli-service-global。
- 全局装置
此包必须全局装置:
npm install -g @vue/cli-service-global
- 创立 vue 文件
在根目录下创立 App.vue 文件:
<template>
<h1>Hello!</h1>
</template>
- 启动开发服务器
在命令行中执行:
vue serve
入口能够是 main.js、index.js、App.vue 或 app.vue 中的一个。你也能够显式地指定入口文件:
vue serve App.vue
- 执行打包
vue build
打包实现后,打包后果会放到 dist 目录下,默认状况下,会打包生成一个利用,该利用蕴含 html 和资源文件,能够间接部署为动态站点。然而通常状况下,咱们须要将组件打包成一个库,以便公布后供我的项目应用。
打包成库须要指定构建指标:
vue build –target lib
增加构建指标后,执行打包,dist 目录中蕴含各种标准的 js 文件和一个 demo 示例 html。
目前为止,疾速开发 vue 组件曾经实现,咱们能够高兴的开发各种组件,然而,当所需开发的组件缓缓变多之后,文件的组织形式成为咱们须要思考的事件。
能够想到有以下三种形式组织文件构造:
- 每一个组件都是一个独自的仓库。
- 一个仓库中蕴含多个组件 vue 文件,作为一个包公布。
- 一个仓库中蕴含多个组件包,每个组件包独自公布。
第一种形式,每一个组件都是一个独自仓库,尽管有利于组件开发,然而组件保护起来比拟麻烦。组件越多,须要保护的仓库也就越多,当其中局部组件依赖的如 lodash 须要降级时,咱们须要一个个进行降级,比拟麻烦。
第二种形式,将所有的组件作为一个包公布,尽管保护比拟不便,然而公布后,他人只想应用其中的一个组件时,会须要把整个组件库引入,如果不提供按需加载,那么会造成我的项目中引入很多不必要的代码。
第三种形式可参考下文。
monorepo
当咱们查看 vue3 源码时,能够看到,仓储构造如下:
packages
├── compiler-core
├──_tests_ #单元测试
├──src #源文件目录
├──package.json
├── compiler-dom
├──_tests_ #单元测试
├──src #源文件目录
├──package.json
package.json
这个就是典型的monorepo
,monorepo 是我的项目代码的一种治理形式,指在一个仓库中治理多个模块 / 包。
monorepo 谋求的是在一个仓库中治理多个模块,每个模块有独立的 package.json 治理各自依赖,同时在我的项目根目录下能够通过命令装置或降级模块依赖,并提供了一个模块共享的 node_modules。
yarn workspace
yarn workspace 是实现 monorepo 的一种形式。
应用 yarn workspace 要求在根目录的 package.json 中增加如下属性:
{
"private": true,
"workspaces": ["packages/*"]
}
private 属性指定根目录是公有的,不会被公布工具公布到 npm 上。
workspace 属性指定组件所在文件夹,反对通配符。
批改完 package.json 之后,依照 vue-next 的我的项目构造在 packages 文件夹下创立 input 测试组件。
假如,自定义的 input 组件依赖 dayjs 包,能够在根目录下执行如下命令装置:
yarn workspace m-input add dayjs
其中 m -input 并不是 packages 下组件文件夹的名称,而是组件文件夹下 package.json 中的 name 属性值。
装置实现后,dayjs 会主动增加到 input 组件的 package.json 下,然而包下载到了根目录下的 node_modules 文件夹中,这样做能够更好的治理多组件包的依赖。如果以后组件依赖的包版本和其余组件依赖的包版本不一样,如其余组件依赖 lodash@4,以后组件依赖 lodash@3,此时依赖包会被下载到以后组件文件夹下的 node_modules 中。
通过 yarn workspace 能够执行某个组件下的 npm scripts,如给 input 组件增加一个 build 命令,能够在根目录下通过如下命令执行 build:
yarn workspace m-input run build
对于 build 这种命令,简直所有组件都须要,那么 yarn workspace 提供了一个快捷命令,能够一键执行所有组件包的 build 命令:
yarn workspaces run build
storybook
目前为止,仓库的整体文件构造和组件库的依赖包治理都曾经实现了,能够欢快的开发组件了, 当组件开发实现后,个别开发人员都会编写相应的应用文档,文档中蕴含相应的应用示例.
storybook 是可视化的组件治理展现平台,反对在隔离的开发环境中,以交互式的形式展现组件,反对 vue,react 等。
装置应用:
npx -p @storybook/cli sb init –type vue
yarn add vue -W
yarn add vue-loader vue-template-compiler –dev -W
批改配置:
装置实现之后,在根目录的.storybook 文件夹下寄存着 storybook 应用的所有配置文件,批改 main.js 中 stories 属性,将其指向 packages 所有组件下的.stories.js 文件。
"stories": [
"../packages/**/*.stories.mdx",
"../packages/**/*.stories.@(js|jsx|ts|tsx)"
]
增加组件示例:
在 input 组件包中增加 Input.stories.js
文件:
import MInput from './index'
export default {
title: 'MInput',
component: MInput
};
export const Text = () => ({components: { MInput},
template: '<m-input />',
});
export const Password = () => ({components: { MInput},
template: '<m-input type="password"placeholder=" 请输出明码 "/>',
});
其中默认导出是 storybook 页面左侧导航栏,每一个具名导出都是一个样例。
最终执行 yarn storybook,关上站点:
lerna
lerna 是 babel 团队开源的用于治理多包仓库的工具,也能够用于实现 monorepo。
装置 lerna:
npm install lerna -g
初始化 lerna:
lerna init
会在我的项目根目录下增加 lerna.json 配置文件。
能够应用 lerna 治理我的项目依赖:
如果以后 form 自定义组件依赖 input 自定义组件,能够应用:
lerna add input –scope=form
还能够应用 import 命令导入本地包:
lerna import <path-to-external-repository>
通过 exec 和 run 执行包外面的相干命令
lerna run –scope my-component test
lerna exec — rm -rf ./node_modules
通过 clean 命令一键革除所有包的 node_modules 目录:
lerna clean
learn 最次要的性能是一键公布所有包的 npm 上:
lerna publish
公布包到 npm 须要登录,能够通过 npm whoami
查看以后登录用户,通过 npm login
进行登录。
单元测试
单元测试是组件化开发中必不可少的局部
装置依赖:
npm i jest @vue/test-utils vue-jest babel-jest -D
- 增加 jest 配置文件 jest.config.js
module.exports = {"testMatch": ["**/_tests_/**/*.[jt]s?(x)"],
"moduleFileExtensions": [
"js",
"json",
// 通知 Jest 解决 `*.vue` 文件
"vue"
],
"transform": {
// 用 `vue-jest` 解决 `*.vue` 文件
".*\\.(vue)$": "vue-jest",
// 用 `babel-jest` 解决 js
".*\\.(js)$": "babel-jest"
}
}
- 增加 babel 配置文件 babel.config.js
module.exports = {
presets: [
['@babel/preset-env']
]
}
- 增加测试命令
“test”: “jest”
- 增加测试文件
在组件包的_tests_文件夹下增加相干 js 文件,如 input 包上面增加 input.test.js
import input from '../src/index.js'
import {mount} from '@vue/test-utils'
describe('m-input', () => {test('input-text', () => {const wrapper = mount(input)
expect(wrapper.html()).toContain('input type="text"')
})
})
- 执行测试命令
yarn test
测试能够在命令行中看到单元测试执行后果:
rollup 打包
rollup 是一个基于 ESM 的模块打包工具,和 webpack 相比,其打包后果更小,因而适宜打包框架或者组件库。
装置必须的依赖:
npm i rollup rollup-plugin-terser rollup-plugin-vue@5.1.9 vue-template-compiler -D
须要留神的是装置 vue 时须要指定版本,否则会装置 vue3。
- 单组件打包
- 增加配置文件
在组件中增加 rollup.config.js 文件,该文件是 rollup 打包的配置文件,指定起始文件,输入文件地位及格局,插件。
import {terser} from 'rollup-plugin-terser'
import vue from 'rollup-plugin-vue'
module.exports = {
input: 'src/index.js',
output: [
{
file: 'dist/index.js',
format: 'es'
}
],
plugins: [
vue({
css: true,
compileTemplate: true
}),
terser()]
}
- 增加可执行命令
在 package.json 文件的 scripts 属性下增加打包命令:
“build”: “rollup -c”
- c 指的是应用以后我的项目目录下的配置文件 rollup.config.js
- 执行命令
yarn build
执行结束之后,能够看到打包后果。
- 多组件打包
尽管能够用上述单组件打包的形式为每一个组件打包,然而这样比拟麻烦,能够在我的项目根目录下通过一个配置文件打包所有组件。
此时须要增加额定依赖:
npm i @rollup/plugin-json rollup-plugin-postcss @rollup/plugin-node-resolve cross-env -D
- 为组件指定入口文件
在每个包下的 package.json 文件中增加 main 和 module 属性:
“main”: “dist/cjs/index.js”,
“module”: “dist/es/index.js”,
- 设置环境变量
利用 cross-env 设置环境变量,辨别开发环境和生产环境:
“build:prod”: “cross-env NODE_ENV=production rollup -c”,
“build:dev”: “cross-env NODE_ENV=development rollup -c”
- 增加配置文件
在我的项目的根目录下增加 rollup.config.js 文件,该文件会遍历 packages 文件夹下的所有文件夹并打包:
import fs from 'fs'
import path from 'path'
import json from '@rollup/plugin-json'
import vue from 'rollup-plugin-vue'
import {terser} from 'rollup-plugin-terser'
import postcss from 'rollup-plugin-postcss'
import {nodeResolve} from '@rollup/plugin-node-resolve'
const isDev = process.env.NODE_ENV !== 'production'
// 公共插件配置
const plugins = [
vue({
css: true,
compileTemplate: true
}),
json(),
nodeResolve(),
postcss({
// 把 css 插入到 style 中
// inject: true,
// 把 css 放到和 js 同一目录
extract: true
})
]
// 如果不是开发环境,开启压缩
isDev || plugins.push(terser())
// packages 文件夹门路
const root = path.resolve(__dirname, 'packages')
module.exports = fs.readdirSync(root)
.filter(item => fs.statSync(path.resolve(root, item)).isDirectory())
.map(item => {
// 获取每个包的配置文件
const pkg = require(path.resolve(root, item, 'package.json'))
return {input: path.resolve(root, item, 'src/index.js'),
output: [
{
exports: 'auto',
file: path.resolve(root, item, pkg.main),
format: 'cjs'
},
{
exports: 'auto',
file: path.join(root, item, pkg.module),
format: 'es'
},
],
plugins: plugins
}
})
此时执行打包命令,能够一次性为所有组件包打包。
当初有个问题,每次打包的时候须要删除上次打包后果,因而须要增加删除命令:
装置依赖包:
npm i -D rimraf
为每个组件包增加 del 命令:
“del”: “rimraf dist”
在根目录下增加 clean 命令:
“clean”: “yarn workspaces run del”
此时执行 yarn clean 就能够革除所有包的 dist 目录。
plop 模版
截止到目前为止,我的项目的整体构造曾经实现,接下来就是无休止的增加组件了,然而思考到每个组件的初始化有很多雷同的工作须要手动实现,此时能够通过 plop 将这部分工作交给机器。
装置依赖:
npm i plop -D
- 创立模版文件
在我的项目中增加 plop-template/component 文件夹,此文件夹下搁置创立组件用的所有模版文件。
- 增加 plopfile.js
该文件是 plop 插件执行的入口文件:
module.exports = plop => {
plop.setGenerator('component', {
description: 'create a custom component',
prompts: [
{
type: 'input',
name: 'name',
message: 'component name',
default: 'MyComponent'
}
],
actions: [
{
type: 'add',
path: 'packages/{{name}}/src/{{name}}.vue',
templateFile: 'plop-template/component/src/component.hbs'
}
]
})
}
为 plop 增加一个可执行的命令,该命令会询问用户组件的名称,而后将模版中所有的文件拷贝到 packages 相干文件夹内。
- 增加 scripts 命令
“plop”: “plop”
此时在命令行中执行 yarn plop component 就能够创立组件了。