组件测试及发布
单元测试
在组件开发完成并发布之前,需要对组件进行单元测试,单元测试是使用断言的方式判断实际的输出与预测的输出是否相同,目的是发现可能存在的问题;组件的单元测试是指使用单元测试工具对组件的各种状态及行为进行测试,确保组件发布后在使用过程中不会出现错误。
-
组件的单元测试有很多好处:
- 提供描述组件行为的文档
- 节省手动测试的时间
- 减少研发新特性时产生的 bug
- 改进设计
- 促进重构
-
用 Jest 测试单文件组件
首先需要安装 Jest 和 Vue Test Utils
yarn add jest @vue/test-utils -D -W
然后需要在
package.json
中定义一个单元测试的脚本// package.json { "scripts": { "test": "jest" // 修改test为jest } }
为了告诉 Jest 如何处理
*.vue
文件,需要安装和配置vue-jest
预处理器:yarn add vue-jest -D -W
创建jext.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-jest
处理es6语法yarn add babel-jest -D -W
babel配置文件
// babel.config.js module.exports = { presets: [ [ '@babel/preset-env' ] ] }
Babel 的桥接
yarn add babel-core@bridge -D -W
-
Jest常用API
-
全局函数
describe(name, fn) 把相关测试组合在一起
test(name, fn) 测试方法
expect(value) 断言
-
匹配器
toBe(value) 判断值是否相等
toEqual(obj) 判断对象是否相等
toContain(value) 判断数组或字符串是否包含
-
快照
toMatchSnapshot()
-
-
vue-jest常用API
- mount() 创建一个包含被挂载和渲染的Vue组件的Wrapper
- Wrapper
- vm wrapper包裹的组件实例
- props() 返回Vue实例选项中的props对象
- html() 组件生成的HTML标签
- find() 通过选择器返回匹配的组件中的DOM元素
- trigger() 触发DOM原生事件,自定义事件wrapper.vm.$emit()
- ...
-
创建
packages/input/__tests__/input.test.js
文件@vue/test-utils提供API用于挂载组件,Jest不需要导入因为测试文件是被jest加载执行的
import input from '../src/input.vue' import { mount } from '@vue/test-utils' // 创建代码块 将input相关测试都添加到这里 describe('wang-input', () => { test('input-text', () => { // 挂载组件 只是内存中的挂载 返回一个包裹器 const wrapper = mount(input) // 测试生成的html中是否包含type=text expect(wrapper.html()).toContain('input type="text"') }) })
-
使用
image-20210412082210659.pngyarn test
测试
-
添加更多测试
... test('input-password', () => { const wrapper = mount(input, { propsData: { type: 'password', }, }) expect(wrapper.html()).toContain('input type="password"') }) test('input-password', () => { const wrapper = mount(input, { propsData: { type: 'password', value: 'admin', }, }) expect(wrapper.props('value')).toBe('admin') }) // 快照 test('input-snapshot', () => { const wrapper = mount(input, { propsData: { type: 'password', value: 'admin', }, }) // 快照 第一次运行会将wrapper.vm.$el的内容存储在./__snapshots__/input.test.js.snap中 expect(wrapper.vm.$el).toMatchSnapshot() }) ...
如果以后生成的快照和第一次生成的不同,会测试失败,如下所示:
image-20210412082559674.png
可以通过yarn test -u
删除旧的快照文件,并重新生成
Rollup打包
-
特点
- Rollup是一个模块打包器
- Rollup支持Tree-shaking
- 打包结果比webpack小
- 开发框架/组件库使用Rollup更合适,如Vue、React等
-
安装
- Rollup
- rollup-plugin-terser 对代码进行压缩
- Rollup-plugin-vue@5.1.9 将单文件组件编译成js代码,最新版本适用于vue3.x,内部需要使用到vue-template-compiler
- vue-template-compiler
-
设置Rollup配置文件
import { terser } from 'rollup-plugin-terser' import vue from 'rollup-plugin-vue' module.exports = [ { input: 'index.js', output: [ { file: 'dist/index.js', format: 'es' } ], plugins: [ vue({ // Dynamically inject css as a <style> tag css: true, // Explicitly convert template to render function compileTemplate: true }), terser() ] } ]
-
单独打包button组件
在button组件中添加
rollup.config.js
配置
package.json
添加脚本"build": "rollup -c"
使用yarn workspace执行所有包中的命令
yarn workspace wang-button run build
打包完成,可以看到dist中生成的js文件
image-20210412083944432.png
-
打包所有组件
需要额外安装的插件
@rollup/plugin-json 让rollup可以把json文件作为模块加载
rollup-plugin-postcss
@rollup/plugin-node-resolve 打包过程会将依赖的第三方包进行打包
执行命令
yarn add @rollup/plugin-json rollup-plugin-postcss @rollup/plugin-node-resolve -D -W
-
项目根目录创建rollup.config.js
import fs from 'fs' import path from 'path' import json from '@rollup/plugin-json' import vue from 'rollup-plugin-vue' import postcss from 'rollup-plugin-postcss' import { terser } from 'rollup-plugin-terser' import { nodeResolve } from '@rollup/plugin-node-resolve' const isDev = process.env.NODE_ENV !== 'production' // 公共插件配置 const plugins = [ vue({ // Dynamically inject css as a <style> tag css: true, // Explicitly convert template to render function 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, '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 } })
-
配置package.json添加build命令
-
对每个packages中的package.json设置
main
和module
,打包的出口,使用包的入口... "main": "dist/cjs/index.js", "module": "dist/es/index.js", ...
-
使用
yarn build
进行打包打包完成,可以看到在packages中每个组件都生成了cjs和es模块
image-20210412084909815.png
-
设置环境变量
yarn add cross-env -D -W
配置package.json
"build:prod": "cross-env NODE_ENV=production rollup -c", "build:dev": "cross-env NODE_ENV=development rollup -c"
分别执行不同的打包模式,dev模式下是不会进行代码压缩的
-
清理所有包中的node_modules和dist
删除node_module直接使用
image-20210412085521460.pnglerna clean
即可
删除dist需要使用第三方包rimraf
安装
yarn add rimraf -D -W
在每个组件的package.json中添加del命令
"del": "rimraf dist"
执行yarn workspaces run del
发布
使用yarn plop
新增link组件
-
发布之前需要先添加测试文件,测试组件所有状态和对外公布方法
import { mount } from '@vue/test-utils' import link from '../src/link.vue' describe('Wang-Link', () => { test('link-disabled-underlined', () => { // 挂载组件 只是内存中的挂载 返回一个包裹器 const wrapper = mount(link, { propsData: { disabled: true, }, }) expect(wrapper.html()).toContain('class="disabled underlined"') }) test('link-disabled', () => { const wrapper = mount(link, { propsData: { disabled: true, underlined: true, }, }) expect(wrapper.html()).toContain('class="disabled"') }) test('link-a', () => { const wrapper = mount(link, { propsData: { href: 'www.baidu.com', }, }) expect(wrapper.props('href')).toBe('www.baidu.com') }) test('link-snapshot', () => { const wrapper = mount(link, { propsData: { href: 'www.baidu.com', disabled: true, }, }) // 快照 第一次运行会将wrapper.vm.$el的内容存储在./__snapshots__/link.test.js.snap中 expect(wrapper.vm.$el).toMatchSnapshot() }) })
-
运行生成环境打包
-
发布之前检查npm登陆状态
npm whoami
-
使用
yarn lerna
进行发布发布完成,打开npm
image-20210412094537562.png