框架搭建&持续集成(二)
之前每次测试都需要使用parcel运行项目,打开浏览器和控制台,这样太麻烦了!
能不能只输入一行命令(自动打开浏览器,自动刷新),就能知道代码有没有问题?==>使用自动化测试
一、使用Karma做自动化测试
工具介绍
- Karma(
[ˈkɑrmə]
卡玛)是一个测试运行器,它可以自动呼起浏览器,自动加载测试脚本,然后自动运行测试用例 - Mocha(
[ˈmoʊkə]
摩卡)是一个单元测试框架/库,它可以用来写测试用例 - Sinon(西农)是一个 spy / stub / mock 库,用以辅助测试(使用后才能理解)
步骤
1. 安装各种工具:
npm i -D karma karma-chrome-launcher karma-mocha karma-sinon-chai mocha sinon sinon-chai karma-chai karma-chai-spies
2. 创建 karma 配置
在项目根目录下创建karma.conf.js
// 新建 karma.conf.js,内容如下
module.exports = function (config) {
config.set({
// base path that will be used to resolve all patterns (eg. files, exclude)
basePath: '',
// frameworks to use
// available frameworks: https://npmjs.org/browse/keyword/karma-adapter
frameworks: ['mocha', 'sinon-chai'],
client: {
chai: {
includeStack: true
}
},
// list of files / patterns to load in the browser
files: [
'dist/**/*.test.js',
'dist/**/*.test.css'
],
// list of files / patterns to exclude
exclude: [],
// preprocess matching files before serving them to the browser
// available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
preprocessors: {},
// test results reporter to use
// possible values: 'dots', 'progress'
// available reporters: https://npmjs.org/browse/keyword/karma-reporter
reporters: ['progress'],
// web server port
port: 9876,
// enable / disable colors in the output (reporters and logs)
colors: true,
// level of logging
// possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
logLevel: config.LOG_INFO,
// enable / disable watching file and executing tests whenever any file changes
autoWatch: true,
// start these browsers
// available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
browsers: ['ChromeHeadless'],
// Continuous Integration mode
// if true, Karma captures browsers, runs the tests and exits
singleRun: false,
// Concurrency level
// how many browser should be started simultaneous
concurrency: Infinity
})
}
3. 将测试用例搬到test/button.test.js 文件
// 创建 test/button.test.js 文件
const expect = chai.expect;
import Vue from 'vue'
import Button from '../src/button'
Vue.config.productionTip = false
Vue.config.devtools = false
describe('Button', () => {
it('存在.', () => {
expect(Button).to.be.ok
})
it('可以设置icon.', () => {
const Constructor = Vue.extend(Button)
const vm = new Constructor({
propsData: {
icon: 'setting'
}
}).$mount()
const useElement = vm.$el.querySelector('use')
expect(useElement.getAttribute('xlink:href')).to.equal('#i-settings')
vm.$destroy()
})
it('可以设置loading.', () => {
const Constructor = Vue.extend(Button)
const vm = new Constructor({
propsData: {
icon: 'setting',
loading: true
}
}).$mount()
const useElements = vm.$el.querySelectorAll('use')
expect(useElements.length).to.equal(1)
expect(useElements[0].getAttribute('xlink:href')).to.equal('#i-loading')
vm.$destroy()
})
it('icon 默认的 order 是 1', () => {
const div = document.createElement('div')
document.body.appendChild(div)
const Constructor = Vue.extend(Button)
const vm = new Constructor({
propsData: {
icon: 'setting',
}
}).$mount(div)
const icon = vm.$el.querySelector('svg')
expect(getComputedStyle(icon).order).to.eq('1')
vm.$el.remove()
vm.$destroy()
})
it('设置 iconPosition 可以改变 order', () => {
const div = document.createElement('div')
document.body.appendChild(div)
const Constructor = Vue.extend(Button)
const vm = new Constructor({
propsData: {
icon: 'setting',
iconPosition: 'right'
}
}).$mount(div)
const icon = vm.$el.querySelector('svg')
expect(getComputedStyle(icon).order).to.eq('2')
vm.$el.remove()
vm.$destroy()
})
it('点击 button 触发 click 事件', () => {
const Constructor = Vue.extend(Button)
const vm = new Constructor({
propsData: {
icon: 'setting',
}
}).$mount()
const callback = sinon.fake();
vm.$on('click', callback)
vm.$el.click()
expect(callback).to.have.been.called
})
})
4. 创建测试脚本
在 package.json 里面找到 scripts 并改写 scripts
"scripts": {
"dev-test": "parcel watch test/* --no-cache && karma start",
"test": "parcel build test/* --no-cache --no-minify && karma start --single-run"
},
这里被卡了很久,着实被恶心到了
windows中的script中的多个脚本使用“&&” 连接,但是只能执行前面一句
尝试了使用个各种方法后:
偶然的一次,删除node_modules
使用yarn istall
再运行npm run test
就可以了,我也不知道发生了什么,就是很离谱
5. 运行测试脚本
- 要么使用
npm run test
一次性运行
这样我们就实现了只要敲一行代码,就能进行自动化测试
上图显示6个测试用例已经跑通了
当我们运行npm run test
这句话的时候:
-
先帮我们打包
test/button.test.js
中的测试用例,到dist/
目录下,生成打包后的浏览器能识别的js文件和css文件 -
帮我们打开一个chrome浏览器
-
输入网址
-
运行测试用例,即打包后的文件
-
关闭浏览器
处理一下报错,报错是因为在button组件中使用了g-icon
组件,因此我们需要注册这个组件
一种是全局注册
import Icon from './icon.vue'
Vue.component('g-icon',Icon)
另外一种方法是使用局部组件
import vue from 'vue'
import Icon from './icon.vue'
export default {
components:{
'g-icon':Icon
}
...
}
如果运行npm run test
报错找不到vue,那么就要使用
rm -rf .cache dist
,删除掉这2个目录,重新打包,因为每次打包完都会有“残渣”
整理一下思路
- 运行
npm run test
后会运行package.json
中的script脚本 parcle build test/* --no-cache --no-minify
的意思是让parcle打包test目录下的所有文件,不要缓存,不要压缩- 接着
karma start --single-run
就启动karma只运行一次 - karma如何运行,就要在
karma.conf.js
karma的配置文件中进行描述 - 其中
files: [
'dist/**/*.test.js',
'dist/**/*.test.css'
],
就是要告诉karma,我们的测试用例在哪里,其中dist/**
表示加载dist目录下的所有子文件(如dist/a/b/c/d/test.js),如果只用一个星dist/*
就表示,只加载dist目录下的一级文件。如果不加test.css
那么运行测试用例时,如果测试用例中用到了样式,而又没有加载默认的css就会报错
也就是说parcel打包的时候会将css和js分开打包
browsers: ['ChromeHeadless'],
这个就是告诉karma要打开哪个浏览器
ChromeHeadless
无头浏览器的意思就是没有界面的浏览器,因为我们做测试的时候只要看浏览器中的log信息就可以了,因此不需要界面
=============================分割=============================
备注:
- 如果没有安装
parcel
但是想要使用parcl单独打包文件,可以使用npx parcel build test/* --no-cache --no-minify
- 为什么要打包?因为
test/button.test.js
测试文件中,使用import
格式引入了Vue、Button等模块,这句语法浏览器是不认识的,打包会把浏览器不认识的语法变成浏览器能认识的语法,比如拷贝vue的源代码,把vue做成变量暴露出来
打包前的js文件只有81行代码
但是打包后的js文件就变成了2万多行了
其实打包后的js文件,大部分都是引入的vue的源代码及引入的其他文件
二、使用Mocha& Chai做单元测试
之前的测试用例
看下之前是如何做测试的
我们使用的chai中的断言expect,使用“{}”来做作用域隔离,结合spies来mock一个假的函数,来做单元测试的
单元测试中最重要的两件事就是作用域隔离和断言
import chai from 'chai'
import spies from 'chai-spies'
chai.use(spies)
// 单元测试icon
{
const Constructor = Vue.extend(Button);
const vm = new Constructor({
propsData: {
icon: 'setting'
}
});
// mount到内存中
vm.$mount();
let useElement = vm.$el.querySelector('use');
let href = useElement.getAttribute('xlink:href');
expect(href).to.eq('#i-setting');
vm.$el.remove();
vm.$destroy();
}
// 单元测试loading
{
const Constructor = Vue.extend(Button);
const button = new Constructor({
propsData: {
icon: 'setting',
loading: true
}
});
// mount到内存中
button.$mount();
let useElement = button.$el.querySelector('use');
let href = useElement.getAttribute('xlink:href');
expect(href).to.eq('#i-loading');
button.$el.remove();
button.$destroy();
}
// 单元测试svg的样式order
{
const div = document.createElement('div');
document.body.appendChild(div);
const Constructor = Vue.extend(Button);
const vm = new Constructor({
propsData: {
icon: 'setting'
}
});
// mount到内存中
vm.$mount(div);
let svg = vm.$el.querySelector('svg');
let order = window.getComputedStyle(svg).order;
expect(order).to.eq('1');
vm.$el.remove();
vm.$destroy();
}
// 单元测试iconPosition
{
const div = document.createElement('div');
document.body.appendChild(div);
const Constructor = Vue.extend(Button);
const vm = new Constructor({
propsData: {
icon: 'setting',
iconPosition: 'right'
}
});
// mount到内存中
vm.$mount(div);
let svg = vm.$el.querySelector('svg');
let order = window.getComputedStyle(svg).order;
expect(order).to.eq('2');
vm.$el.remove();
vm.$destroy();
}
// 单元测试 监听button组件的click事件
{
const Constructor = Vue.extend(Button);
const vm = new Constructor({
propsData: {
icon: 'setting',
iconPosition: 'right'
}
});
vm.$mount();
let spy = chai.spy(function (){})
vm.$on('click', spy);
let button = vm.$el;
button.click();
expect(spy).to.have.been.called()
}
改用用mocha和chai 做测试
再来看下现在的是如何做测试的
karma是负责打开浏览器
使用karma后,mocha
和 sino-chai
会自动引入,因为这2个函数是挂window上的全局函数,所以不用手动的去引
有了mocha,就可以用describe ... it...
sino-chai
就是同时引入 sino
和 chai
,sino
就是用来做fake函数。chai
是用来实现expect
sino-chai
提供了calledwith
API,sino-chai
将sino
和chai
拉到一起合作
我们看到的命令行中的字都是reporters
来做的
import sinon from 'sinon';
const expect = chai.expect;
import Vue from 'vue';
import Button from '../src/button';
Vue.config.productionTip = false;
Vue.config.devtools = false;
describe('Button', () => {
it('存在.', () => {
expect(Button).to.be.ok;
});
it('可以设置icon.', () => {
const Constructor = Vue.extend(Button);
const vm = new Constructor({
propsData: {
icon: 'setting'
}
}).$mount();
const useElement = vm.$el.querySelector('use');
expect(useElement.getAttribute('xlink:href')).to.equal('#i-setting');
vm.$destroy();
});
it('可以设置loading.', () => {
const Constructor = Vue.extend(Button);
const vm = new Constructor({
propsData: {
icon: 'setting',
loading: true
}
}).$mount();
const useElements = vm.$el.querySelectorAll('use');
expect(useElements.length).to.equal(1);
expect(useElements[0].getAttribute('xlink:href')).to.equal('#i-loading');
vm.$destroy();
});
it('icon 默认的 order 是 1', () => {
const div = document.createElement('div');
document.body.appendChild(div);
const Constructor = Vue.extend(Button);
const vm = new Constructor({
propsData: {
icon: 'setting',
}
}).$mount(div);
const icon = vm.$el.querySelector('svg');
expect(getComputedStyle(icon).order).to.eq('1');
vm.$el.remove();
vm.$destroy();
});
it('设置 iconPosition 可以改变 order', () => {
const div = document.createElement('div');
document.body.appendChild(div);
const Constructor = Vue.extend(Button);
const vm = new Constructor({
propsData: {
icon: 'setting',
iconPosition: 'right'
}
}).$mount(div);
const icon = vm.$el.querySelector('svg');
expect(getComputedStyle(icon).order).to.eq('2');
vm.$el.remove();
vm.$destroy();
});
it('点击 button 触发 click 事件', () => {
const Constructor = Vue.extend(Button);
const vm = new Constructor({
propsData: {
icon: 'setting',
}
}).$mount();
const callback = sinon.fake();
vm.$on('click', callback);
vm.$el.click();
expect(callback).to.have.been.called;
});
});
每个it
的作用就是将不同的测试用例分开(使用it中的函数进行分隔),并给每个测试用例起名字,函数中代码是测试用例的代码,这里面的代码和之前测试的代码基本使一样.只不过是将之前的两个“{}”,换成了it
为什么用describe
和it
呢?
这种测试方法叫BDD,行为驱动测试
看下伪代码
我们的测试用例,就是表述了button
组件的一系列行为,它存在、它可以设置icon、它可以设置loading,同描述人类一样,他有一个头、他有2个眼、他可以跑... ...
解释一下下面这个测试用例
it('点击 button 触发 click 事件', () => {
const Constructor = Vue.extend(Button);
const vm = new Constructor({
propsData: {
icon: 'setting',
}
}).$mount();
const callback = sinon.fake();
vm.$on('click', callback);
vm.$el.click();
expect(callback).to.have.been.called;
});
下面的测试用例,就是测试g-button 组件中的butto按钮有click点击事件。
做测试的时候,我们就要主动触发click点击事件,看下点击事件的函数有没有执行
如果我们用普通的匿名函数作为callbak可以吗?
it('点击 button 触发 click 事件', () => {
const Constructor = Vue.extend(Button);
const vm = new Constructor({
propsData: {
icon: 'setting',
}
}).$mount();
const callback = function(){};
vm.$on('click', callback);
vm.$el.click();
expect(callback).to.have.been.called;
});
是不可以的,从技术方面来说,我们是没法知道function(){}
被调用了
将好比,只给我们一个变量函数f1,我们有什么方法能知道f1什么时候被调用了吗?
怎么办呢?
我们需要借助别人写好的库叫sinon
(相当于之前用的spies"间谍"),sinon.fake
就是sinon提供的假的函数,这个函数有个特点,就是它知道自己有没有被调用过,也知道自己被调用的时候传了什么参数
所以才能有后面的断言 expect(callback).to.have.been.called;
--> 这个假的函数被调用了
目前为止三个文件的功能我们已经搞清楚了,这三个文件分别对应package.json(主要是配置脚本)、button.test.js(测试用例)和karma.conf.js(karma自动测试的配置文件)
具体测试用例的语法怎么写?
记住最常用的语法
expect(xxx).to.equal(yyy)
意思是期待xxx = yyy,其中equal
可以简写为eq
eg:期待某个变量是数组
expect(xxx instancof Array).to.eq(true)
前面放期待的东西,后面放true,就可以做很多测试
测试用例的很多语法都是chai.js 提供的
图中蓝色部分的语法,是我们在写测试用例可能要用到的语法
橙色部分的相当于语气词,没有实际的意义,可以省略,不会影响测试语法的准确性,只是加上读取来更加顺口
重点说一下deep
的用法
expect([1,2]).to.eq([1,2]) // false, 这里对象的比较,比较的是对象的地址,2个不同的对象地址不同
expect([1,2]).to.deep.eq([1,2]) // true,这里比较的是对象的内容
类似于深拷贝和浅拷贝
备注
it
和describe
来源于mocha
库,也就是说行为驱动测试BDD是mocha
引入的expect
是从来源于chai
库,也就说具体的断言是由chai.js
引入的
自动测试
改用watch而不是parcel
目前做到的效果是,每次修改代码,还要运行npm run test
重新打包和测试
能不能做到,每次改代码不要重新运行npm run test
?
// package.json
"scripts": {
"dev-test": "start parcel watch test/* --no-cache && karma start",
"test": "parcel build test/* --no-cache --no-minify && karma start --single-run"
},
我们改用dev-test
来跑代码
dev-test
和test区别:
- 将build改为parcel
- 之前karma只运行一次
--single-run
,现在是不中断运行
这样只要改了代码,parcle就会重新打包代码,karma重新测试
Windows 用户运行 npm run dev-test
时会出现 BUG,解决办法是:
将 dev-test 对应的命令 parcel watch test/* --no-cache & karma start
分别运行,运行方式如下
- 新开一个 Git Bash 窗口运行
npx parcel watch test/* --no-cache
- 再开一个 Git Bash 窗口运行
npx karma start
或者尝试在第一条脚本语句前加一个start
windows中不支持多条脚本语句,最好还是npm run test
到这里,我们只需要专注于写代码,其他的交给parcel自动打包 + karma 自动测试
还有没有优化的余地呢?
能不能做到,不运行npm run dev-test
也能自动的完成测试呢?
也就是说能不能什么都不运行,就能帮我们完成测试呢?
三、使用CircleCI做持续集成
CircleCI
我们是不是可以尝试雇一台机器帮我们运行npm run dev-test
这一行代码?
这台机器每天要做的事情就是,帮我们把要测试的代码copy过去,然后运行测试用例
这台机器叫CircleCI:持续集成服务
(TravisCI
已经不免费使用了)
这样就表示测试通过了!
这里埋一个坑
我们需要在.circleci/config.yml
中自定义自己的持续测试配置文件,这里暂时还不知道怎么写这个配置文件
我们后续只要往github中项目push代码就可以了,测试交给CircleCI
,它会接管这个项目,持续的自动的测试我们的代码
这就是持续集成中的一部分-- 持续测试
持续集成包括持续测试、持续交付、持续部署等
持续集成的重点在“持续”
为什么要持续呢?
拿持续测试举例子,持续的意思就是每天要做的事,持续的好处就是尽早发现bug,防止在有bug的代码上继续开发,导致bug积累bug,最终无法找到解决bug的办法
这样,每天写完代码就push一次,做一次持续测试,如果有问题,当天就修改完。这样在每一天早上,开发新代码的时候,代码都是正确的、没有bug的
同样的如果是多人合作的项目,项目组中的每个成员都单独的push,单独的做持续测试,这样持续的往项目中集成东西,这样就会让项目运行稳定,而不会出现一合并全是bug
或者使用github
中的GitHub Actions和gitlab
做持续集成
四、使用npm发布自己的包
发布包
如何让项目能被全世界的程序员使用?我们需要将项目发布到npm
1. 确保你的代码测试通过了
npm run test
全部是绿色才行,如果有问题请不要发布你的代码
2. 上传代码到npmjs.org
-
更新 package.json
- 在 package.json 里将版本号改为 0.0.1,等我们把项目全部做完了才将版本号改为 1.0.0
- 创建 index.js,在 index.js 将你想要导出的内容告诉npm,导出想要导出的全部组件,index.js就是作为发布到npm中项目的入口文件
import Button from './src/button.vue'
import GButtonGroup from './src/g-button-group.vue';
import Icon from './src/icon.vue'
export {Button,GButtonGroup,Icon}
-
在
package.json
中的main指定为index.js作为入口文件 -
去 www.npmjs.com/ 注册一个账户,username才是你的登录名
-
确认一下邮箱(必须)
-
在你的项目根目录运行
npm adduser
- 如果错误提示里面含有 registry.npm.taobao.org 则说明你的 npm 源目前为淘宝源,需要更换为 npm 官方源
nrm use npm
-
运行 npm publish(是从本地发布)
看到这样就表示发布到npm成功了
我们尝试下载上传到npm中代码npm i g-design-of-vue
我们的代码放在/node_modules/g-design-of-vue中
那我们怎么知道我们的代码别人是不是可以使用呢?
因此我们需要自己用一下自己的包,来确保别人也可以使用
使用自己的包
因此,我们得猜比人如何使用我们的包?
我们的包是vue的UI组件,因此使用我们组件的人就是vue的用户,那么vue的用户会有三种方法来使用我们的包
0.预测其他使用你的包的人会怎么使用
- 使用 vue-cli
- 使用 webpack
- 使用 parcel
1.分别使用这些方式来使用自己的包(我们只以 vue-cli 为例)
- vue的使用者会在空目录中运行
npm install -g @vue/cli@4.0.0
(本项目适用于vue2,vue/cli在4.5.0版本以下都是vue2.0),安装vue vue create hello-world
创建一个hello world
项目cd hello-world
npm run serve --no-cache
- 访问
http://localhost:8080/
- 安装我们的项目
npm i g-design-of-vue
,我们的项目会安装到用户的node_modules/d-design-of-vue
目录下
- 用户就到他自己项目中的
main.js
中引入我们的UI组件库,并使用我们的组件
import {Button,GButtonGroup,Icon} from 'g-design-of-vue'
<g-button>UI组件库</g-button>
使用过程中我们发现报错说 import 语法有问题,那是因为 node 目前确实不支持 import,我们需要用 babel 转译 import
你可以要求使用者自己用 babel 来转译
你也可以转义好了再给他用
回到我们的项目目录下运行
npx parcel build index.js --no-minify --no-cache
将index.js变成不含import的index.js(本来不应该加 --no-minify 的,奈何不加会有 bug,HTML 压缩把 slot 标签全删了)修改 package.json 的 main 由index.js改为打包编译后的浏览器能认识的语法的dist/index.js
即使我们项目的
.gitignore
中不提交dist
目录,但是不影响发布到npm,npm能看到dist
目录的文件
每次重新发布到npm前,一定要修改package.json中的版本号 发布包到npm之前记得将npm的源切换到npm而不是使用taonbao源
- 用户回到他的项目中重新安装我们的包
npm i g-design-of-vue@latest
用户成功使用了我们的组件
但是当前的组件没有样式,为什么没有样式呢?
因为我们的项目在打包的时候,将index打包成了2个文件,一个是index.js
,另外一个是index.css
(导出的组件的样式)
因此我们需要手动的引入css
能不能自动引入?最好不要,因为如果css放到js中,js会变得很大,分开更容易缓存
- 用户引入css文件
import 'g-design-of-vue/dist/index.css'
原因是css样式中用到都是变量都是自定义的
因此我们要在文档中告知用户,要复制这些样式
站在使用者的角度,目前的组件库还是有很多问题的
- 用户在style标签中粘贴这些样式后,组件也有样式了
// 用户使用组件
<template>
<div id="app">
<img alt="Vue logo" src="./assets/logo.png">
<g-button>UI组件库</g-button>
</div>
</template>
<script>
import { GButton, GButtonGroup, GIcon } from 'g-design-of-vue'
import 'g-design-of-vue/dist/index.css'
export default {
name: 'App',
components: {
'g-button': GButton
}
}
</script>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
html {
--button-height: 32px;
--font-size: 14px;
--button-bg: white;
--button-active-bg: #eee;
--border-radius: 4px;
--color: #333;
--border-color: #999;
--border-color-hover: #666;
--border-active-bg: #d3d2d2;
}
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}</style>
至此,别人就可以使用我们的组件了
用户要使用我们的组件只需要做三件事
- 引入想要使用的组件
- 引入g-design-of-vue/dist/css
- 引入样式变量
后面会将样式变量改为scss
目前,每改一行代码,都要上传到npm,太麻烦了
如何不上传到npm,就能测试出代码在被人使用的时候会不会出bug??
五、npm link 的使用
我们的轮子并不知道怎么才是最好的,但是如果我们能加快造轮子的效率,就可以让我们的轮子达到更好的状态
当前的效率瓶颈是,每次修改完代码都得npm publish
,用户都得npm install
我们能不能先自己调好了再给用户?
我们不用升级package.json中的版本,就可以自己测自己
如果,我们修改了项目的代码,如在g-icon组件中加了一个reagen样式,本机的用户如何得到最新的代码呢?
- 我们要在自己的项目中运行
npm link
- 用户(本机模仿自己是用户)在自己的目录中
npm link g-design-of-vue
看来是可以了
下次每次我们的项目改完代码就不用再npm link
和 用户的项目也不用 npm link g-design-of-vue
了
改完代码,我们只需要使用parcel
重新打包编译我们项目中的文件即可,用户那边会自动更新(如果每次改完代码不打包编译,用户是拿不到可以运行的版本)
npx parcel build index.js --no-cache --no-minify
这样我们在写完代码后,就不需要找一个人去install我们的项目,再去测试了。我们自己就当做用户
可以实时的检查自己的代码是否在用户那可用了,就能够尽早的发现问题,解决问题,提高效率和用户体验
知识点总结
1. 单文件组件(vue)
2. Parcel(打包)
3. 单元测试({...}{...})
4. 自动测试
- karma 测试运行器
- Mocha(BDD风格,
describe...it
) 测试框架 - chai,断言库,
expect(xxx).to.equal(yyy)
5. 持续集成(Travis CI / Circle CI)
6. 发布到npm
配置package.json
发布(dist/index.js
dist/index.css
)
7. npm link / yarn link
加快造轮子过程
转载自:https://juejin.cn/post/7254549436626550842