在日常开发过程中,构建组件库是必不可少的一环,此篇文章就是形容如何搭建一个残缺的组件库,解决组件库开发公布过程中的如下问题:

  1. 如何在起码的依赖下疾速开发一个vue组件。
  2. 如何将所有的包搁置在一个git仓库内。
  3. 如何将git仓库内的所有包一键公布。
  4. 如何治理所有包的依赖,缩小包的体积。
  5. 如何疾速创立组件示例。
  6. 如何打包组件,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组件曾经实现,咱们能够高兴的开发各种组件,然而,当所需开发的组件缓缓变多之后,文件的组织形式成为咱们须要思考的事件。

能够想到有以下三种形式组织文件构造:

  1. 每一个组件都是一个独自的仓库。
  2. 一个仓库中蕴含多个组件vue文件,作为一个包公布。
  3. 一个仓库中蕴含多个组件包,每个组件包独自公布。

第一种形式,每一个组件都是一个独自仓库,尽管有利于组件开发,然而组件保护起来比拟麻烦。组件越多,须要保护的仓库也就越多,当其中局部组件依赖的如lodash须要降级时,咱们须要一个个进行降级,比拟麻烦。

第二种形式,将所有的组件作为一个包公布,尽管保护比拟不便,然而公布后,他人只想应用其中的一个组件时,会须要把整个组件库引入,如果不提供按需加载,那么会造成我的项目中引入很多不必要的代码。

第三种形式可参考下文。

monorepo

当咱们查看vue3源码时,能够看到,仓储构造如下:

packages├── compiler-core    ├──_tests_ #单元测试    ├──src #源文件目录    ├──package.json├── compiler-dom    ├──_tests_ #单元测试    ├──src #源文件目录    ├──package.jsonpackage.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
  1. 增加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"    }}
  1. 增加babel配置文件babel.config.js
module.exports = {  presets: [    [      '@babel/preset-env'    ]  ]}
  1. 增加测试命令

"test": "jest"

  1. 增加测试文件

在组件包的_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"')  })})
  1. 执行测试命令
yarn test

测试能够在命令行中看到单元测试执行后果:

rollup打包

rollup是一个基于ESM的模块打包工具,和webpack相比,其打包后果更小,因而适宜打包框架或者组件库。

装置必须的依赖:

npm i rollup rollup-plugin-terser rollup-plugin-vue@5.1.9 vue-template-compiler -D

须要留神的是装置vue时须要指定版本,否则会装置vue3。

  • 单组件打包
  1. 增加配置文件

在组件中增加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()    ]}
  1. 增加可执行命令

在package.json文件的scripts属性下增加打包命令:

"build": "rollup -c"

-c指的是应用以后我的项目目录下的配置文件rollup.config.js

  1. 执行命令
yarn build

执行结束之后,能够看到打包后果。

  • 多组件打包

尽管能够用上述单组件打包的形式为每一个组件打包,然而这样比拟麻烦,能够在我的项目根目录下通过一个配置文件打包所有组件。

此时须要增加额定依赖:

npm i @rollup/plugin-json rollup-plugin-postcss @rollup/plugin-node-resolve cross-env -D
  1. 为组件指定入口文件

在每个包下的package.json文件中增加main和module属性:

"main": "dist/cjs/index.js",
"module": "dist/es/index.js",

  1. 设置环境变量

利用cross-env设置环境变量,辨别开发环境和生产环境:

"build:prod": "cross-env NODE_ENV=production rollup -c",
"build:dev": "cross-env NODE_ENV=development rollup -c"

  1. 增加配置文件

在我的项目的根目录下增加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
  1. 创立模版文件

在我的项目中增加plop-template/component文件夹,此文件夹下搁置创立组件用的所有模版文件。

  1. 增加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相干文件夹内。

  1. 增加scripts命令

"plop": "plop"

此时在命令行中执行yarn plop component就能够创立组件了。