九、项目整体搭建
1、导学
* 第一部分:相关准备知识
- 前端工具链的相关工具介绍
- 脚手架工具的使用和对比
~ Imooc CLI
~ Vue CLI
~ Vite
- 编码规范
~ ESLint
~ Prettier
~ 产出**编码规范文档**
- 项目结构规范
~ 产出**项目结构规范文档**
- Git操作规范
~ Git Flow
* 第二部分:搭建项目整体框架
- Ant Design Vue
- Vue Router
- Vuex
* **技术规范**文档-必须落实到文档中,别放在脑子里
* Ant Design Vue
* Vue Router和Vuex
* Typescript-用了TS,一定要培养使用的习惯,看到任何变量都想到它的类型
* 对比学习:更进一步,不同工具也可以横向对比,这样可以收获的更多
* 必须亲自实践:请不要因为比较简单就不写代码,眼高手低。**无他,唯手熟尔**
* 不要教条主义:很多过程没有标准答案,适合自己的项目的方案才是最优解。
* 学会看**文档**:好的文档中有大部分的知识点。
2、大话前端工具链
* 简介
- 前端的飞速发展:带来了更复杂的项目
- 项目的常见需求 - 依赖管理,预编译,构建打包,压缩合并等
- 随着项目越来越复杂 - 诞生了**前端工程化**
- 随着工程化的产生 - 产生了对应的**前端工具链**
* 静态类型语言
- 动态语言的弊端
~ **typescript**
~ flow
* 代码风格检查Linter
- 多人协作的弊端,风格各异,维护和扩展的困难
~ eslint
* 包管理器
- npm
- yarn - 兼容npm registry
* 转译器Traspiler
- 非JS或不同版本的JS翻译成符合平台要求的等价代码
~ Babel
* 开发服务器
- live reload
- HMR
* 打包工具Bundler
- 将源代码转换成符合生产环境的代码
~ Webpack - Loader,Plugin,大而全的功能
~ Rollup - 专注于打包输出多种格式
~ Parcel - 零配置
* 任务管理工具Task Runner
- 自动执行项目所需的重复任务
~ css预处理
~ 优化图片
~ 合并压缩javascript
~ 文件处理(拷贝,删除)
~ 监听文件变化
~ Gulp - 流式管道写法组合多个任务
~ Webpack - 通过插件的方式
~ npm scripts或者Shell脚本
* 脚手架Scaffolding tools
- **将工具链聚合在一个工具内**简单,快速,零配置
~ Vue - Vue CLI,Vite,Imooc CLI
~ React - create-react-app
~ Angular - Angular CLI
3、使用Imooc CLI新建项目
* 不是针对某个前端框架的脚手架,而是一个**大而全更加针对业务的脚手架**
- 支持各种模板,可扩展性
- 云构建
- 预发布和正式发布
- 项目回滚
- 远程Git操作
* 安装
- npm install -g @imooc-cli/core
- imooc-cli init
* 依赖**cnpm**
- npm install -g cnpm
4、Vue CLI vs Vite
* **Vite比Vue CLI快10-100倍?**
* Vue CLI的功能
- 工程脚手架
- 开发服务器
- 插件系统
- 用户UI界面
* Vue CLI构建是基于**Webpack**的。主要耗时都在Webpack的性能上。
* Vite
- 利用浏览器的**原生ES模块**,基于**Rollup**进行构建
- 处于测试阶段,不是一体化的工具,目的就是一个**快速**的开发服务器和**简单的**构建工具
* 对比时刻
* 它为什么这么快?
* Vite的缺点
- 测试阶段
- 只支持新版支持ES modules的浏览器
- 第三方库也需要都支持ES modules
- CommonJS支持有限
- 开发构建属于两套系统,可能导致生产和开发不一致行为
* Vite的前辈
- [Snowpack](https://www.snowpack.dev/)
**一个利用浏览器中JavaScript模块的开发服务器项目**
5、ESLint
* **规范的代码格式可以让整个工作的效率在一定程度上提升到最高**
* 没有规范可能出现的问题
- 代码难以读懂
- 代码提交的时候会有很多格式问题的修改,造成不必要的事件消耗
* ESLint是什么?
- 是一个开源的JavaScript的linting工具,使用espree将JavaScript代码解析成抽象语法树
(AST),然后通过AST来分析我们代码。
* 命令行工具
- npx eslint --version
- npx eslint --ext ts,vue src/**
* 编辑器插件
- [ESLint插件](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint)
- [Vetur插件](https://marketplace.visualstudio.com/items?itemName=octref.vetur)
6、ESLint配置文件
* Rules
- [ESLint可用的Rules](https://eslint.org/docs/rules/)
* Extends
* 自动修正错误
7、小花絮:Prettier
* **Prettier**顾名思义,将代码变漂亮
* ESLint的功能
- 代码质量问题
- 代码风格问题
* Prettier的理念
- **An opinionated code formatter**,格式很重要,但是很重要,让我来帮你搞定!
- Fewer Options:Prettier还给予了一部分配置项,可以通过.prettierrc文件修改。
* ESLint prettier的工作原理
- 禁用所有和Prettier冲突的ESLint的代码格式规则
- 将所有Prettier的规则和修改导入ESLint中,在ESLint统一的显示这些错误
8、项目结构规范
* **代码结构**针对单个文件的书写格式
* **项目结构**针对这些文件应该以怎样的标准进行存放和管理
* [React项目文件结构](https://reactjs.org/docs/faq-structure.html)
- 按照按功能或路由组织,也就是所说的feature
- **按照文件类型**
* 注意事项
- 避免多层嵌套
- 不要过度思考
* 项目结构举例
/assets
image.png
logo.png
/components
ColorPicker.vue
Dropdown.vue
...
/views
Home.vue
...
/router
index.ts
...
/store
index.ts
editor.ts
user.ts
...
/hooks
useURLloader.ts
...
/plugins
hotKeys.ts
...
/test
ColorPicker.spec.ts
App.vue
main.ts
...
9、Git标准操作流程:Git Flow
* 所有的这些规范都是针对特定的多人设定的,意在让多人协作的过程更顺畅,更简单,
减少不必要的冲突和时间的浪费。
* Git Flow
- 预设两个分支
~ master只能用来包括产品代码。你不能直接工作在这个master分支上
~ develop是你进行任何新的开发的基础分支
~ **这两个分支被称之为长期分支**
- 功能开发feature
~ 整合回到develop
~ 等待更全面的测试
~ 等待和develop一起进行发布
- 管理release
~ 新功能已经添加,bug已经修复
~ 代码已经被测试
~ release分支使用版本号命名的
- bug修复hotfix
~ 针对master分支
- 优点:清晰可控
- 缺点:相对复杂,不适合持续发布
* Github Flow
- 根据需求,从master拉出分支
- 激烈的开发阶段,提交commit
- 开发完毕,发起PR(pull request)
- 代码评审(很重要!)
- 部署,并且测试
- 没问题,merge到master!
- **Github flow的最大优点就是简单,对于“持续发布”的产品,可以说是最合适的流程**
* 两大规则
- branch命名
~ feature开头代表功能开发
~ hotfix开发代码bug修复
- commit信息,必须**言之有物**,杜绝update,fix bug这类废话
10、ant-design-vue组件库
* 两大解决方案
- ***[ant-design-vue](https://2x.antdv.com/docs/vue/getting-started-cn/)*
- [element-plus](https://element-plus.gitee.io/)
* 安装方式
- npm安装配合webpack等打包工具使用
~ npm install ant-design-vue@next --save
- 直接引入js和css(不推荐)
~ <!-- 引入样式 -->
<link rel="stylesheet" href="https://unpkg.com/element-plus/lib/theme-chalk/index.css">
<!-- 引入组件库 -->
<script src="https://unpkg.com/element-plus/lib/index.full.js"></script>
* 开始使用
- // main.ts
import Antd from 'ant-design-vue';
import 'ant-design-vue/dist/reset.css';
app.use(Antd)
* 关于全局API
- Vue2的时代,都是全局API和配置
~ Vue.component('button-counter', {})
~ Vue.directive('focus', {})
- Vue2没有“app”的概念,我们定义的应用只是通过new Vue()创建的根Vue实例。从同一个Vue
构造函数创建的每个根实例共享相同的全局配置。
~ 在测试期间,全局配置很容易意外地污染其他测试用例
~ 全局配置使得在同一页面上的多个“app”之间共享同一个Vue副本非常困难
~ Vue.mixin({
/* ... */
})
const app1 = new Vue({el: '#app-1'})
const app2 = new Vue({el: '#app-2'})
- **Vue3的做法:createApp**
~ import {createApp} from 'vue'
const app = createApp({})
~ 应用实例暴露当前全局API的子集,任何全局改变Vue行为的API现在都会移动到**应用实例**上,
现在就不会产生vue2的这些冲突了。
11、SPA应用路由的基本原理
* SPA和普通网页应用的区别
- 普通网页
~ 跳转到新页面,每次重新加载所有资源
~ HTML内容是后端直接渲染的
- SPA应用
~ 不跳转,JS拦截跳转,修改URL
~ JS动态渲染DOM内容
* SPA路由的实现方式
- History API
~ [pushState](https://developer.mozilla.org/zh-CN/docs/Web/API/History/pushState)
~ [codesandbox](https://codesandbox.io/s/optimistic-cookies-wgxhc?file=/src/index.js)
- URL Hash
* SPA优点
- 速度快
- 体验好
- 为前后端分离提供了实践场所
* Vue Router4
- https://router.vuejs.org/zh/
12、状态管理工具
* 什么是状态管理工具
- 首先搞清楚,是否真的需要状态管理工具
- 它随着SPA的出现,而浮出水面。客户端需要处理复杂的状态数据。
- 多个组件需要共享的一系列数据,称之为全局数据。
* 解决方案
- 方案一:单向数据流,从父组件传递到子组件
- 方案一缺点:
~ 多层传递非常繁琐
~ 中间传递层有可能根本不需要这个数据
~ 根组件压力太大,逻辑代码会非常繁杂
- 方案二:使用全局对象
- 方案二缺点:
~ 数据非响应式
~ 修改无法追踪
~ 直接从组件获取数据是一种反模式
* 状态管理工具三杰
- Vuex
- Redux
- Mobx
* 状态管理工具的特点
- store,神奇的全局数据结构:single source of truth
- 不能随意修改,调用特殊的方法来实现数据修改
- 变化可追溯,可预测(predictable)
13、Vuex安装和基础
* Vue的数据流
- Actions(methods)
↗ ↘
View(template) ← State(data)
* 出现问题:多组件共享状态
- 根组件多层传递的困境
- 多组件同步数据的繁琐
* Vuex的解决方案
- Vue Components → (dispatch)Actions
↑ ↓
State ← (commit)Mutations
* Vuex的特点
14、总结
* 知识点
- 脚手架
~ Imooc CLI
~ Vue CLI
~ Vite
- 代码规范
~ ESLint简介,使用和配置文件
~ Prettier的定位和作用以及使用
~ 产出:代码规范文档:https://www.yuque.com/imooc-lego/elp4vn/wtg4vo
- 项目文件结构和命名规范
~ 产出:文件结构和命名规范文档:https://www.yuque.com/imooc-lego/elp4vn/igcosu
- Git Flow
~ 标准Git Flow
~ **Github Flow**
~ 产出Git操作规范文档:https://www.yuque.com/imooc-lego/elp4vn/cwu0z1
- Ant Design Vue
~ 安装,全局导入和使用
~ 多看文档,多用
- Vue Router
~ SPA router的简单原理
~ 安装,全局引入
~ 添加路由,使用内置组件router-view和router-link
~ 使用钩子函数useRoute和useRouter获取路由信息和跳转
- Vuex
~ 全局状态工具的定义
~ 安装,概念:**store,state,mutations**,使用commit提交mutations
~ 使用钩子函数useStore
~ 使用module将store拆分成多个模块
* 要点
- **文档化**标准
- **Typescript**深入编码理念中
* 作业
- 根据我的介绍,写自己的**技术规范文档**,你可以根据我的简介和自己的偏好,
写出适合自己项目的文档
十、编辑器基本布局及业务组件库初步开发
1、导学
* 主要内容
- 完成第一个业务组件LText的书写
- 完成业务组件属性和表单组件的显示和实时更新
- 代码升级:支持vNode的在vue template中的展示
* 将收获什么
- 渐进式的开发方式
- 组件属性和重用性的设计和实现
- Typescript结合实际项目的高度实战
- Vue template和JSX写法的异同和优劣
* 学习方法
- 渐进式的开发方法:小步快跑,一步步完善最终的需求。
- 对比学习:多种方法完成一个任务,更能了解它们的优劣。
- 流程图:以流程图的方式来描述代码的逻辑过程或者数据流动。
- 技术文档:以技术文档为指引。
2、uuid
// npm i uuid @types/uuid
import { v4 as uuidv4 } from 'uuid'
console.log(uuidv4())
3、lodash
// npm i lodash-es @types/lodash
import { pick, mapValues, without, reduce } from 'lodash-es'
const obj = { a: '1', b: '2', c: '3' }
const arr = [1, 2, 3, 4, 5]
// 创建一个从obj中选中的属性的对象。
console.log(pick(obj, ['a']))
// 创建一个对象,属性与obj相同,属性值为箭头函数的返回值
console.log(
mapValues(obj, (item) => {
return {
type: item.constructor,
default: item
}
})
)
// 创建一个剔除所有给定值的新数组
console.log(without(arr, 4, 5))
// 参数1:迭代集合;参数2:迭代调用的函数;参数3:初始值。
console.log(
reduce(
obj,
function (result, value, key) {
result[key] = value
return result
},
{} as { [p: string]: string }
)
)
4、typescript
interface IPerson {
name: string
age: number
}
const person: Readonly<IPerson> = {
name: '骨法',
age: 18
}
// 这里会报错,Readonly不可修改
person.name = '末日'
console.log(person)
interface IPerson {
name?: string
age?: number
}
// 这里会报错,Required确定的类型
const person: Required<IPerson> = {}
console.log(person)
interface IPerson {
name: string
age: number
}
// 这里不会报错,Partial可选类型
const person: Partial<IPerson> = {}
console.log(person)
十一、掌握测试基本工具,给组件添加单元测试
1、导学
* 主要内容
- Jest的基础以及进阶用法
- Vue Test Utils的基础以及进阶用法
- 使用TDD的方式进行业务代码的开发
* 将收获什么
- 测试的定义,分类以及重要性
- 常用通用测试框架的分类和特点
- Jest和Vue Test Utils的基础和进阶全覆盖
- TDD,一种全新的开发方式
* 关键词
- 单元测试
- Mock
- TDD
* 学习方法
- 转变思维方式-认识到测试的重要性,在以后的代码中有机会也可以写测试
2、Jest
* 通用测试框架
- [Mocha](https://mochajs.org/)
- [Jasmine](https://jasmine.github.io/)
- **[Jest](https://jestjs.io/)**
* 测试框架的几大功能
- 断言
~ Jest内置
~ Mocha需要另外安装[Chai](https://www.chaijs.com/)或者其他断言库
- 异步支持
- Mock
~ Jest内置
~ Mocha需要另外安装[Sinon](https://sinonjs.org/)
- 代码覆盖率
~ Jest内置
~ Mocha需要另外安装[Istanbul](https://istanbul.js.org/)
* Jest的特点
- 开箱即用,零配置
- **快!**
- 内置代码覆盖率
- Mocking很容易
* 安装(全局也要安装,不然会报错):npm i -S jest
* 查看版本:npx jest --version
* 命令:npx jest
- 执行一系列以.test.js结尾的测试用例
* 命令:npx jest ./async.test.js --watch
- 热更新替换
* webstorm编辑器jest代码类型提示
- File | Settings | Languages & Frameworks | JavaScript | Libraries
- Download -> jest
3、使用
test('test common matcher', () => {
expect(2 + 2).toBe(4)
expect(2 + 2).not.toBe(5)
})
test('test not equal', () => {
expect(2 + 2).not.toBe(5)
})
test('test to be true or false', () => {
expect(1).toBeTruthy()
expect(0).toBeFalsy()
})
test('test number', () => {
expect(4).toBeGreaterThan(3)
expect(2).toBeLessThan(3)
})
test('test object', () => {
expect({name: 'viking'}).toEqual({name: 'viking'})
})
// callback
const fetchUser = (cb) => {
setTimeout(() => {
cb('hello')
}, 100)
}
it('test callback', (done) => {
fetchUser((data) => {
expect(data).toBe('hello')
done()
})
})
// promise
const userPromise = () => Promise.resolve('hello')
it('test Promise', () => {
return userPromise().then(data => {
expect(data).toBe('hello')
})
})
it('test with async', async () => {
const data = await userPromise()
expect(data).toBe('hello')
})
it('test with expect', () => {
return expect(userPromise()).resolves.toBe('hello')
})
const rejectPromise = () => Promise.reject('error')
it('test with expect reject', () => {
return expect(rejectPromise()).rejects.toBe('error')
})
const getUserName = require('./user')
const axios = require('axios')
// 方式一
// jest.mock('axios')
// 用法1
// axios.get.mockImplementation(() => {
// return Promise.resolve({data: {username: 'viking'}})
// })
// 用法2
// axios.get.mockReturnValue(Promise.resolve({data: {username: 'viking'}}))
// 用法3
// axios.get.mockResolvedValue({data: {username: 'viking'}})
// 方式二:项目根目录新建__mocks__目录,__mocks__目录下新建axios.js文件
function mockTest(shouldCall, cb) {
if (shouldCall) {
return cb(42)
}
}
it('test with mock modules', () => {
return getUserName(1).then(name => {
console.log(name)
expect(axios.get).toHaveBeenCalled()
expect(axios.get).toHaveBeenCalledTimes(1)
})
})
it('test with mock function', () => {
const mockCb = jest.fn()
mockTest(true, mockCb)
expect(mockCb).toHaveBeenCalled()
expect(mockCb).toHaveBeenCalledWith(42)
expect(mockCb).toHaveBeenCalledTimes(1)
})
it('test mock with implementation', () => {
// const mockCb = jest.fn(x => x * 2)
const mockCb = jest.fn().mockReturnValue(20)
mockTest(true, mockCb)
})
const axios = require('axios')
module.exports = function getUserName(id) {
return axios.get(`https://jsonplaceholder.typicode.com/users/${id}`).then(resp => {
return resp.data.username
})
}
const axios = {
get: jest.fn(() => Promise.resolve({data: {username: 'viking'}}))
}
module.exports = axios
const fetchUser = (cb) => {
setTimeout(() => {
cb('hello')
}, 1000)
}
const loopFetchUser = (cb) => {
setTimeout(() => {
cb('one')
setTimeout(() => {
cb('two')
}, 2000)
}, 1000)
}
jest.useFakeTimers()
it('test the callback after 1 sec', () => {
const callback = jest.fn()
fetchUser(callback)
expect(callback).not.toHaveBeenCalled()
expect(setTimeout).toHaveBeenCalledTimes(1)
jest.runAllTimers()
expect(callback).toHaveBeenCalled()
expect(callback).toHaveBeenCalledWith('hello')
});
it('test the callback in timeout loops', () => {
const callback = jest.fn()
loopFetchUser(callback)
expect(callback).not.toHaveBeenCalled()
jest.runOnlyPendingTimers()
expect(callback).toHaveBeenCalledTimes(1)
expect(callback).toHaveBeenLastCalledWith('one')
jest.runOnlyPendingTimers()
expect(callback).toHaveBeenCalledTimes(2)
expect(callback).toHaveBeenLastCalledWith('two')
});
it('test the callback with advance timer', () => {
const callback = jest.fn()
loopFetchUser(callback)
expect(callback).not.toHaveBeenCalled()
jest.advanceTimersByTime(500)
jest.advanceTimersByTime(500)
expect(callback).toHaveBeenCalledTimes(1)
expect(callback).toHaveBeenLastCalledWith('one')
jest.advanceTimersByTime(2000)
expect(callback).toHaveBeenCalledTimes(2)
expect(callback).toHaveBeenLastCalledWith('two')
});
4、Vue Test Utils
* 配置开发环境
- 手动配置?(不推荐)
- vue cli是基于插件架构的,插件可以:
~ 安装对应功能的依赖
~ 修改内部配置
~ 注入命令
- 在现有项目中安装插件
~ vue add 插件名称
* 添加测试插件
- 插件地址:https://www.npmjs.com/package/@vue/cli-plugin-unit-jest
- 命令:vue add unit-jest
* 插件运作的过程
- 安装的依赖
~ vue-test-utils
~ vue-jest
~ 注入了新的命令
- vue-cli-service test:unit
~ Any files in tests/unit that end in .spec.(js|jsx|ts|tsx)
~ Any js(x)/ts(x) files inside __tests__ directories
- vue-jest转换
~ 将vue SFC格式文件转换成对应的ts文件
~ 将ts通过presets/typescript-babel转换成对应的js文件
5、测试组件
* 文档地址:https://vue-test-utils.vuejs.org/v2/api/
* 运行jest本身的命令:npm run test:unit -- --watch
* 测试内容
- 渲染组件
~ mount和shallowMount
~ 传递属性
- 元素是否成功的显示
~ 查找元素的不同写法
~ get、getAll
~ find、findAll
~ findComponent和getComponent
- 触发事件
~ trigger方法
- 测试界面是否更新
~ 特别注意DOM更新是个异步的过程
~ 使用async await
- 更新表单
~ setValue方法
- 验证事件是否发送
~ wrapper.emitted()
* mount和shallowMount的区别
- mount一股脑全都渲染
- shallowMount只渲染组件本身,外来的子组件都不渲染
- shallowMount更快,更适合单元测试
* find和get的区别
- 找不到的时候,find返回null,case不会出错,get throw错误,case报错
- 通用规则:总是使用get,除了你想判断一些元素不存在的时候,这种情况下使用find文档链接
* getComponent的意义
- 不必测试子组件里面的内容,只要判断是否渲染了子组件,是否传递了正确的属性就可以了,
这就是单元测试的意义,独立,互不影响的模块。
6、测试异步请求
* 不要依赖第三方库,模拟第三方库的实现我们应该已经掌握
* 使用flushPromises将所有Promise pending状态都改为完成
- 安装地址:https://www.npmjs.com/package/flush-promises
* 小知识点
- 将mock对象断言为特定类型,使用jest.Mocked<>
- 使用only跑一个单独的case
7、测试准备和结束
* 一次性完成测试准备
- beforeAll
- afterAll
- 文档地址
* 每个测试测试准备
- beforeEach
- afterEach
- 文档地址
* 建议
- 如果一个测试失败,要注意的是
- 它是否是唯一在运行的测试,使用it.only进行隔离
- 使用beforeEach或者afterEach清空一些共享状态
* 小tip
- 使用only只运行一个用例
- 使用skip跳过一个用例
8、替换全局组件
* 遇到难题
- 全局组件
- 外部模块
* 测试行为
- ant-design-vue -> message -> success()
- vue-router -> useRouter() -> push()
- vuex -> useStore() -> commit()
9、测试vuex
* 打破惯性思维,前端工程师的任务不仅仅是和界面打交道
- vuex store天生是脱离组件,独立开来的。它是一个特殊的数据结构,
使用特定的方法,更新其中的状态。
* 测试一个store是否有必要
- 非常有必要交互变得复杂之后,你可以脱离界面对数据的改动做测试,最大限度的保证功能的正常运行。
* 测试过程
- 检查初始state是否正常
- 触发mutations或者actions,对于每个mutations可以写一个case
- 检查修改后的state是否正常
- 测试getters
10、什么是TDD开发方式?
* 一个实际组件的开发场景
- 开发ColorPicker组件,在很多场景(背景颜色,字体颜色)下使用。
* 原型图
- 点击左侧颜色框之前
- 点击之后
* 需求
- 显示
~ 左侧显示当前传入的颜色
~ 右侧显示十种常用的颜色
~ 右侧最后一个是透明,点击以后清除效果
- 点击
~ 点击左侧,显示颜色选择框,在颜色框中点击或者在input中修改值以后,将
新的值以事件的形式发射出去
~ 点击右侧的任何颜色,将新的值以事件的形式发射出去
- 特别注意:
~ 左侧可以使用<input type="color" />-文档地址
~ 遵守业务组件设计原则:以属性value为传入值,以事件change发射出新的要
改变的值
* TDD的开发方式
- Test Driven Development:测试驱动开发-维基百科链接
~ 先根据需求写测试用例
~ 测试用例全部失败
~ 开始写代码实现
~ 将测试用例由失败变成通过
11、TDD的特点
* 开发过程(TDD Cycle)
- 不可运行(TEST FAILS)
- 可运行(TEST PASSES)
- 重构(REFACTOR)
* 动机
- 控制编程过程可能出现的忧虑感
- 将编程过程任务化,可以对进度做到更加精确的把握。
12、本周回顾
* 过程回顾和要点
- Jest-测试框架
~ Jest vs Mocha
~ 安装和断言
~ 异步测试-回调和Promise
~ mock函数-jest.fn()
~ mock第三方模块-jest.mock()
~ mock Timers-jest.runTimers()...
- Vue Test Utils-Vue测试工具
~ 渲染-mount和shallowMount
~ 获取元素-get、find
~ 表单-更新表单setValue,特别注意await
~ 事件-触发DOM事件trigger,验证组件的自定义事件emitted
~ 异步请求-结合jest和VTU一起
- 高级技巧UserProfile组件测试
~ mock全局组件
~ mock第三方模块的实现
~ 在实例中添加真实的Vuex store
~ 单独测试Vuex store
- TDD的开发方式:开发ColorPicker组件
~ 先写测试用例,所有用例失败
~ 根据用例写实现代码,让用例通过
~ 根据实际情况进行测试,如果有需要可以进行重构
* 单元测试的重要性
- 开发人员编写,使用mocks等工具确保单元测试直接相互独立
- 不需要使用教条主义的眼光来看测试
~ 没有必要追求100%的coverage
~ 有些组件其实根本没有必要写测试
- 让测试真的能够帮助你,为你减少Bug和工作量
* 作业
- 完成IconSwitch的组件开发,并且为其写测试
~ 自己撰写需求
~ 可以使用ant-design-vue提供的一些组件,比如a-tooltip,也可以自研
~ 这个组件其实并不能很好的体现单元测试的优越,但是可以比较好的练习学习的内容