一路繁花似锦绣前程
失败的越多,成功才越有价值

导航

 

十二、通用上传组件开发以及使用

1、导学
* 开发通用上传组件
    - 通过TDD的方式,开发一个通用上传组件,然后将组件添加到编辑器中进行使用,从
      这个过程中衍生出很多的相关知识点
* 主要内容
    - 模拟真实开发场景,使用TDD的方式,一步步开发一个通用上传组件
    - 分析Element Plus中Uploader的源代码
    - 将上传组件应用到编辑器中
    - 对于知识点的发散和总结
        ~ Vue3中实例的类型
        ~ Vue3中组件通信方法
        ~ 预览本地图片的两种方法
        ~ HTMLImageElement家族的一系列关系
        ~ JSDOM是什么?jest是怎样采用它模拟浏览器环境的
* 关键词
    - TDD
    - 上传组件
    - 代码重构
    - ELement Plus源代码
    - 发散和总结的思维方式
* 学习方法
    - 真实场景,拥抱TDD和普通流程的混合开发方式,感受它的奇妙
    - 对于一个知识点的发散学习,追根溯源很有意思
2、上传组件需求以及开发流程
* 现在面临的问题
    - 没有很好的体现测试的优势
    - 缺少复杂组件的开发经验
* 解决办法
    - 一个通用上传组件
    - 原因:
        ~ 复杂的逻辑交互
        ~ 属性、方法、生命周期都有很多
        ~ 适合测试驱动开发
* 上传组件的定义
    - 上传是将信息通过网页或者上传工具发布到远程服务器上的过程。
    - 传统的form表单的方式
########
<form>
<input type="file" name="myFile"/>
<button type="submit">提交</button>
</form>
########
    - ajax发送异步请求的方式
* 将任务拆分
    - 将大块任务,量化成一系列Todo List,使用测试驱动,然后把任务划掉
    - 对工作目标非常清晰,并且可进行细微的调整
    - 随着时间的进行,对完成时间越来越清晰可控
* 上传组件的需求
    - 基本上传流程-点击按钮选择,完成上传
    - 支持上传文件列表
        ~ 显示文件名称
        ~ 状态
        ~ 可删除
        ~ 显示上传进度
        ~ 有可能有更丰富的显示支持?
    - 自定义模板
        ~ 支持初始容器自定义
        ~ 支持上传完毕后自定义
    - 支持一系列生命周期钩子事件,上传事件
        ~ beforeUpload
        ~ onProgress
        ~ onSuccess
        ~ onError
        ~ onChange
    - 拖拽上传支持
    - 等等
3、上传文件的基本方式
* 传统模式
########
<form method="post" action="http://local.test:7001/api/upload" enctype="multipart/form-data">
  <input type="file">
  <button type="submit">Submit</button>
</form>
########
* 推荐一个抓包工具mac下
    - https://proxyman.io/
    - 特别注意enctype
        ~ 表单默认:application/x-www-form-urlencoded
        ~ 如果要有二进制数据:multipart/form-data
* 从Input获得Files
    - e.target.files是FileList对象
      https://developer.mozilla.org/zh-CN/docs/Web/API/FileList
        ~ 它是一个array-like object,不是真的数组
    - files[索引]拿到对应的文件,它是File独享
      https://developer.mozilla.org/zh-CN/docs/Web/API/File
    - FormData针对XHR2设计的数据结构,可以完美模拟HTML的<form>
      https://developer.mozilla.org/zh-CN/docs/Web/API/FormData
4、uploader重构的基本步骤
* 需求带动重构
    - 现在需要手动触发上传
    - 上传过程和DOM事件是耦合在一起的
    - 一开始想设计一个完美的代码结构是不可能的
* 用流程图来理清代码逻辑
* 重构的基本要点
    - 变量命名-准确,言简意赅
    - 代码逻辑-抽象通用逻辑,更细粒度
5、扩展知识,Vue3中的实例
  • Vue2
* 每个Vue应用都是new Vue函数创建的一个新的实例
* 创建的时候将data作为property添加到响应式系统中
* https://v2.cn.vuejs.org/v2/guide/instance.html
  • Vue3-Application Instance
* createApp创建一个**Application Instance**
* 应用实例用来注册应用中的全局内容
* 大多数方法支持链式调用,返回**应用实例**
* https://cn.vuejs.org/guide/essentials/application.html
* 为什么有这样的修改?
########
const app = createApp(App)
########
  • Vue3-Component Instance
* createApp传递的那个组件,称之为root component
* mount方法用来:
    - 将应用实例挂载到DOM节点上
    - 返回的不是**应用实例**,而是**组件实例**(和Vue2那个一样)
########
const vm = app.mount('#app')
console.log(vm)
########
* 关于组件实例
    - 组件中的所有属性(methods,props,computed,setup...)都会在实例上平铺展示
    - 还有一系列内置或者全局的属性,比如($attrs,$refs)内置,($message,$confirm)全局注册
* 所以测试中的wrapper.vm属于组件实例
* 通过ref拿到的子组件实例属于组件实例
  • 组件内部实例-Internal Component Instance
* getCurrentInstance()
* 这是一个神奇的混合实例
* proxy属性-可以拿到**组件实例**上面的内容
* appContext-可以拿到**应用实例**上的部分属性
########
setup() {
  const internal = getCurrentInstance()
  console.log(internal?.proxy)
  console.log(internal?.appContext)
}
########
6、组件之间互相访问的方法
  • 引言
* 这里的实例说的都是组件实例(component instance)
  • 父组件访问子组件实例
* $refs
* 在composition API中,使用template refs,在setup中创建ref对象返回,
  在template中添加同名的ref属性
* https://v3.vuejs.org/guide/composition-api-template-refs.html#template-refs
  • 子组件访问父组件实例
* 在当前组件实例上,有一个特殊的属性称之为$parent,它可以拿到父组件实例
* **注意**,直接调用父组件上的方法和属性,这是一种很不好的做法,应该保持单向数据流,子
  组件发送特定的事件去触发父组件的改变
* $parent可以继续往上嵌套调用比如$parent.$parent一直可以到rootComponent
  • 使用Provide/Inject完成子组件到父组件的多级访问
* 属性传递和$parent访问在多级传递的时候都会非常繁琐
* 使用provide和inject完成跨级传递
* 响应式对象也可以被provide
* 文档地址:https://v3.vuejs.org/guide/component-provide-inject.html
  • 使用事件监听器完成父子组件的通信
* 有一些特殊的情形,我们需要使用事件监听器完成父子通讯
* 比如:父组件中有slot,子组件是以slot形式存在的,没法添加ref
########
// parent.vue
<div><slot></slot></div>

// app.vue
<parent>
  <child/>
</parent>
########
* 在vue2中的$on,$off等事件监听器,已经被废除,需要使用第三方库。
* 官方推荐mitt作为事件监听器,https://github.com/developit/mitt
7、Element Plus Uploader源码分析
  • 引言
* 面对一个知识点,动用发散性思维,总结和他相关的一系列知识点
  • 可以有的重构
* 代码实现冗长的时候,逻辑代码的拆分
* 组件结构上的拆分
    - UploadList
    - Dragger
  • 分析element plus Uploader的源代码
* https://github.com/element-plus/element-plus/blob/dev/packages/upload/src
  • template Ref的运行机制
* https://v3.vuejs.org/guide/composition-api-template-refs.html
* vnode ref属性等于render对象中的响应式对象
* 对应的DOM节点或者组件实例就会被赋值给这个响应式对象
* 在patch或者mount时候发生的,所以初次渲染完毕才能拿到这个值
* 举例来看看
8、图片预览的方式
  • 引言
* 不要等上传完毕再显示,需要一种快速本地预览图片的方法
  • URL.createObjectURL()
* 一个静态方法,创建一个DOMString,返回一个URL,URL和document绑定,表示指定的File对象
* 文档地址:https://developer.mozilla.org/zh-CN/docs/Web/API/URL/createObjectURL
  • FileReader.readAsDataURL()
* 一个FileReader上面的实例方法,读取指定的File对象,读取完成的时候触发回调,返回URL
  格式的字符串(base64)
* 文档地址:https://developer.mozilla.org/zh-CN/docs/Web/API/FileReader/readAsDataURl
  • 异同
* 返回值
    - FileReader.readAsDataURL(file)可以得到一段base64的字符串
    - URL.createObjectURL(file)可以得到当前文件的一个内存URL
* 执行机制
    - FileReader.readAsDataURL(file)通过回调的形式返回,异步执行
    - URL.createObjectURL(file)直接返回,同步执行
* 内存清理
    - FileReader.readAsDataURL(file)依照JS垃圾回收机制自动从内存中清理
    - URL.createObjectURL(file)存在于当前document内,清除方式只有unload()事件或
      revokeObjectURL()手动清除
  • 总结一下
* URL.createObjectURL(file)得到本地内存容器的URL地址,同步使用,比较方便快捷,多次使用
  需要注意手动释放内存的问题,性能优秀。
* FileReader.readAsDataURL(file)胜在直接转为base64格式,可以直接用于业务,无需二次转换
  格式。
9、图片获取真实大小的方法
  • 平平无奇的
* 它的DOM节点有个神奇的类型称之为HTMLImageElement,它是标准WebAPI的一部分,还
  有很多类似的HTML标签类型
    - HTMLInputElement
    - HTMLDivElement
    - 文档地址:https://developer.mozilla.org/zh-CN/docs/Web/API/HTMLImageElement
* 追根溯源:HTMLImageElement继承了HTMLElement
* 还能往上吗?:HTMLElement继承了Element
    - HTMLElement
    - SVGElement
* 停不下来:Element继承了Node
    - 一个基本的抽象类
    - 它是一个抽象类,所以没有一个真正的Node对象
    - 所有对象实现的都是基于它的子类
        ~ Document
        ~ Element
        ~ DocumentFragment
* 最终的头目:EventTarget
    - 是一个最基本的DOM接口
    - 可以接受事件,创建监听器等实现
        ~ Element,Document,Window
        ~ XHLHttpRequest
  • 用图来表述
* HTMLImageElement → HTMLElement → Element → Node → EventTarget
  • Event家族的关系
* 请自己参阅文档https://developer.mozilla.org/zh-CN/docs/Web/API/MouseEvent
10、通过Image构造函数获取图片的原始大小
export const getImageDimensions = (url: string | File) => {
  return new Promise<{ width: number; height: number }>((resolve, reject) => {
    const img = new Image()
    img.src = typeof url === 'string' ? url : URL.createObjectURL(url)
    img.addEventListener('load', () => {
      const { naturalWidth: width, naturalHeight: height } = img
      resolve({ width, height })
    })
    img.addEventListener('error', () => {
      reject(new Error('There was some problem with the image.'))
    })
  })
}
11、本周总结
  • 过程回顾和要点
* Uploader真实开发流程-TDD和普通开发混合
    - 文件基本上传原理
    - 组件实现基本上传流程(TDD)
    - 上传列表的实现(TDD)
    - 自定义模本的实现(TDD)
    - 自定义事件的实现(TDD)
    - 拖动文件上传
    - 添加实例方法编码
    - 添加图片列表的图片预览功能
* 扩展知识
    - Vue3的三种实例
        ~ App Instance应用实例-createApp
        ~ Component Instance组件实例-ref或者app.mount()返回
        ~ Component Internal Instance组件内部实例-getInternalInstance()
    - Vue3组件通信的四种方法
        ~ 父组件实例中获取子组件实例-ref
        ~ 子组件实例中获取父组件实例-$parent
        ~ 使用Provider/Inject自上而下跨层传递数据
        ~ 使用emitter事件发射器的方式跨层传递数据
    - Element Plus Upload源码分析
        ~ 组件的分割-Index,Uploader,UploadList,Dragger
        ~ 数据操作的分割-useHandler
    - 本地读取图片并且展示的两种方法
        ~ URL.createObjectURL()
        ~ FileReader.readAsDataURL()
    - Jest运行的环境之谜:使用JSDOM
* 上传组件添加到编辑器,并且完成对应功能
    - 添加LImage组件
    - 添加至左侧组件列表并且相应上传成功
    - 根据上传数据
* 扩展知识
    - HTMLImageElement家族传承
        ~ HTMLImageElement → HTMLElement → Element → Node → EventTarget
    - 获取图片真实大小的方法
  • TDD的感受
* 选择正确的开发方式,来完成对应的功能
* 对之前的功能更有信心,不会怕新功能搞坏了老功能
* 重构更方便了
  • 作业
* 按照自己的想法,继续完成对Uploader组件的开发过程
* 根据MDN文档,分析Event对象的家族关系
* 完成ImageProcesser的组件开发,并且为其写测试
    - 处理图片组件的Src属性,传入一个图片地址,可以重新上传改变图片地址
    - 注意组件属性处理组价的标准

十三、业务组件库打包、发布、添加CI-CD

1、导学
* 目前的组件库是和主项目混在一起的,这不是完美的形态,业务组件库作为要为两
  个项目所服务的公共组件。是时候将它抽取出来发布成至npm,让两个项目可以共
  享。组件库打包有很多新的知识点,让我们来一起学习。
* 主要内容
    - javascript模块以及打包工具
        ~ AMD → Common.js → ES modules
        ~ Webpack vs Rollup
        ~ Snowpack
    - 创建业务组件库代码
    - 添加Rollup配置以及完成打包
        ~ Rollup配置文件
        ~ Rollup插件使用
        ~ Rollup插件简单原理
        ~ Element Plus打包过程分析
    - 发布到NPM以及使用Travis CI完成CI/CD
        ~ NPM简介和发布
        ~ 发布前验证代码质量
        ~ Travis完成CI和CD两个流程
* 关键词
    - Module-模块
    - Bundler-打包工具
    - Rollup
    - NPM
    - CI/CD
    - Travis CI
* 学习方法
    - 实践出真知,这节课知识点众多,并且涉及的工具较多,请大家一定要动起手
      来,只有实践以后才能加深理解掌握。
2、javascript模块发展历史
* 模块(modules)是什么?
########
from package import function
########
########
package main
import (
  "fmt"
)
########
* 模块化的优点
    - 可维护性
    - 可复用性
* ES6之前没有模块的年代
########
// 使用backbone.js的方法
<script src="spec/support/jquery.js"></script>
<script src="spec/support/underscore.js"></script>
<script src="spec/support/backbone.js"></script>
<script src="backbone.localStorage.js"></script>
<script src="todos.js"></script>
########
* 全局变量+命名空间(namespace)
########
// IIFE自执行函数,创建一个封闭的作用域,赋值给一个全局变量
var namesCollection = (function() {
  // private members
  var objects = [];
  
  // Public Method
  function addobject(object) {
    objects.push(object);
    printMessage(object);
  }
  
  // Private Method
  function printMessage(object) {
    console.log("Object successfully added:", object);
  }
  // public members, exposed with return statement
  return {
    addName: addObject,
  };
})();
namesCollection.addName('viking')
########
* 缺点
    - 依赖全局变量,污染全局作用域,不安全
    - 依赖约定命名空间来避免冲突,可靠性不高
    - 需要手动管理依赖并控制执行顺序,容易出错
    - 需要在最终上线前手动合并所有用到的模块
* Common.js
########
const bar = require('./bar')
module.exports = function() {
}
########
    - 没法在浏览器里直接运行
* AMD-(Asynchronous module definition)
    - 采用异步方式加载模块
    - 仅仅需要在全局环境定义require与define,不需要其他的全局变量
    - 通过文件路径或模块自己声明的模块名定位模块
    - 提供了打包工具自动分析依赖并合并
    - 配合特定的AMD加载器使用,RequireJS
    - 同时还诞生了很多类似的模块标准CMD
########
define(function(require) {
  // 通过相对路径获取依赖模块
  const bar = require('./bar')
  // 模块产出
  return function() {
  }
})
########
* ES6 modules
########
// 通过相对路径获取依赖模块
import bar from './bar'
// 模块产出
export default function() {
}
########
    - 引入和暴露的方式更加多样
    - 支持复杂的静态分析
3、Bundler是什么?
* 诞生原因
    - 使用import export这种同步加载的方式在大多数浏览器中无法使用
* Bundler-打包工具
    - 将浏览器不支持的模块进行编译,转换,合并最后生成的代码可以在浏览器端良好
      的运行的工具。
* 大家最熟悉的-Webpack
    - 对于web应用来说:一般采用单javascript文件入口
    - https://webpack.js.org/
    - 举例时间
########
npx webpack main.js
########
* 后起之秀-Rollup
    - https://rollupjs.org/guide/en/
########
npx rollup main.js --file dist/bundle.js --format iife
########
4、Webpack vs Rollup
* Webpack
    - 大型SPA项目的模块化构建,也就是我们常说的web应用。
        ~ 通过各种Loader处理各种各样的静态资源
        ~ 通过各种插件Plugins对整体文件进行一些处理。
        ~ Code splitting将公共模块进行提取。
        ~ 提供一个webpack-dev-server,进行本地开发。
        ~ 支持HMR模块热替换。
* Rollup
    - Rollup设计之初就是面向ES module的,构建出结构扁平,性能出众的类库。
    - ES module的规则
        ~ import只能作为模块顶层的语句出现,不能出现在function里面或是if里面。
        ~ ES import的模块名只能是字符串常量。
        ~ 不管import的语句出现的位置在哪里,在模块初始化的时候所有的import都
          必须已经导入完成。
    - 使用工具静态分析的过程
        ~ AST(抽象语法树)
        ~ Tree shaking机制-摇树!让死了的叶子掉下来。
        ~ 目的就是将es modules打包生产特定的JS模块文件,并减小它的体积。
* Webpack vs Rollup
    - 通过以上的对比可以得出,构建App应用时,webpack比较合适,如果是类库(纯
      js项目),rollup更加适合。
    - Webpack的优势
        ~ 强大的生态插件
        ~ 面向开发应用的特性支持HMR,按需加载,公共模块提取
        ~ 简化Web开发的环节,图片自动转base64,资源的缓存(添加chunkid)
    - Rollup
        ~ 构建高性能的模块文件,这正是类库所需要的。
        ~ 编译出来的代码可读性好,内容更小,执行效率更高。
        ~ 配置比较简单。
5、打包什么类型的文件?
* Commonjs,es6 modules-需要特殊的module bundler支持
* AMD已经有点过时了-需要使用特殊的Loader-require.js
* 浏览器中直接使用-UMD(Universal Module Definition)
    - 通用的一种JavaScript格式
    - 兼容common.js,AMD,浏览器
    - https://github.com/umdjs/umd
    - Vue和React都提供了这样的格式
    - 不是一种推荐的格式,太大了!不支持tree shaking
########
(function (root, factory) {
  if (typeof define === 'function' && define.amd) {
    // AMD. Register as an anonymous module.
    define(['b'], factory);
  } else if (typeof define === 'object' && module.exports) {
    // Node. Does not work with string CommonJS, but
    // only CommonJS-like environments that support module.exports,
    // like Node.
    module.exports = factory(require('b'));
  } else {
    // Browser globals (root is window)
    root.returnExports = factory(root.b);
  }
}(typeof self !== 'undefined' ? self : this, function (b) {
  return {};
}))
########
* 结论
    - 首要格式-ES Modules,并且提供支持typescript的type文件。
    - 备选方案-UMD
6、另辟蹊径-简介Snowpack
* Bundler的问题
    - 当资源越来越多的时候,打包速度越来越慢。
    - 大中型项目,启动时间可能达到好几分钟。
* 另辟蹊径-Snowpack
    - https://www.snowpack.dev/
    - 利用新版浏览器支持es modules的特性。
    - 不会被打包。
    - 每个文件编译一次,永久被缓存。
    - 当一个文件修改的时候,只需要重新build那一个文件。
* 处理Node_modules中的模块
    - 它扫描node_modules中的模块。找到使用的模块。
    - 将每个模块都分别转换成单个js文件。
    - 这些单个文件都是esm模块,可以被最新的浏览器直接使用。
########
node_modules/react/**/*    ->
http://localhost:3000/web_modules/react.js
node_modules/react-dom/**/*    ->
http://localhost:3000/web_modules/react-dom.js
########
* 为生产环节Build代码
    - 默认情况下,和开发环境生成的代码是几乎一致的。
    - 当你想用一个bundler你可以自己选择去用,而不是你必须要用。
    - 提供了插件,生成bundle以后全浏览器兼容的代码。
7、Vue3的插件系统
* 一段代码给vue应用实例添加全局功能。它的格式是一个object暴露出一个
  install()方法,或者一个function
* 它没有严格的限制,一般有以下几种功能
    - 添加全局方法或者属性
    - 添加全局资源:指令,过滤器等
    - 通过全局混入来添加一些一些组件选项
    - 通过config.globalProperties来添加app实例方法
* 写一个插件试试
8、组件库入口文件的设计
* 所有组件一次性全部导入并且作为插件使用
########
import LegoComponents from 'lego-components'
app.use(LegoComponents)
########
    - 建立一个入口文件index.ts
    - 将所有组件导入,作为一个数组,创建一个install函数,循环调用app.component
    - 默认导出一个插件(这个install函数)
* 单个组件导入并且作为插件使用
########
import { LText } from 'lego-components'
app.use(LText)
// 或者
app.component(LText.name, LText)
########
    - 每个组件新建一个文件夹,并且创建一个单独的index.ts文件
    - 每个组件设计成一个插件(一个object拥有install方法)
    - 在全局入口文件导出
9、尝试打包入口文件
* https://www.npmjs.com/package/@rollup/plugin-node-resolve
* npm依赖的分类
    - dependencies
        ~ 运行项目业务逻辑需要依赖的第三方库
        ~ npm install‘模块名称’的时候都会被解析,下载
    - devDependencies
        ~ 开发模式工作流下依赖的第三方库
        ~ 单元测试,语法转换,lint工具,程序构建,本地开发等等
    - peerDependencies
        ~ 需要核心依赖库,不能脱离依赖库单独使用。
* rollup external字段
    - https://rollupjs.org/guide/en/#external
10、NPM简介,以及package.json的信息更新
* NPM的主要功能
    - 从npm服务器下载别人编写的第三方包到本地,比如vue
    - 从npm服务器下载并安装别人编写的命令行程序到本地使用,比如vue-cli
    - 允许用户将自己编写的包或命令行程序上传到npm服务器供别人使用
* 语义化版本-semver
    - 说明网址:https://semver.org/lang/zh-CN/
    - 版本格式:主版本号.次版本号.修订号(1.0.0),版本号递增规则如下:
        ~ 主版本号:当你做了不兼容的API修改,
        ~ 次版本号:当你做了向下兼容的功能性新增,
        ~ 修订号:当你做了向下兼容的问题修正
* npm files字段
    - 默认忽略掉gitingore中的内容
    - 指示npm publish的时候需要上传的内容
    - package.json/README.md/CHANGLOG.md/LICENSE都会包含在其中
11、npm scripts
* Pre&Post scripts
    - 你script的名称前面加上pre或者post,那么当运行这个命令的时候,pre和
      post会自动在这个命令之前或者之后运行。
########
"scripts": {
  "precompress": "{{ executes BEFORE the `compress` script }}",
  "compress": "{{ run command to compress files }}",
  "postcompress": "{{ executes AFTER `compress` script }}"
}
########
* Life Cycle Scripts
    - prepare
    - prepublish(即将废弃)
    - prePublishOnly
12、CI/CD的概念
* 业务组件库的开发和发布是随着一系列任务进化的
    - 本地commit钩子函数完成commit验证
    - 代码push到远端以后
    - 跑特定的test(不仅仅是本机的unit test,也可能有时间很长的E2E test)
    - test通过以后检查是否有新的tag,假如有就自动publish一个新的版本
    - 甚至还有更多,自动部署文档站点等等。
* 这些任务如果手动操作,费时费力,不是很好的解决方案
* CI(Continuous integration)-持续集成
    - 持续集成指的是,频繁地(一天多次)将代码集成到主干。一旦开发人员对应用所
      做的更改被合并,系统就会通过自动构建应用并运行不同级别的自动化测试(通常
      是单元测试和集成测试)来验证这些更改,确保这些更改没有对应用造成破坏。
        ~ 快速发现错误
        ~ 防止分支大幅偏离主干
    - 持续集成的目的,就是让产品可以快速迭代,同时还能保持高质量。
* CD(Continuous Delivery)-持续交付
    - 持续交付(Continuous delivery)指的是,频繁地将软件的新版本,交付给质量
      团队或者用户,以供评审。
* CD(Continuous Deployment)-持续部署
    - 持续部署(continuous deployment)是持续交付的下一步,指的是代码通过评
      审以后,自动部署到生产环境。
* 两大服务
    - Github Action(https://github.com/features/actions)
    - Travis(https://www.travis-ci.com/)
13、本周总结
* 过程回顾和要点
    - JavaScript模块化历史
        ~ 使用自执行函数挂载到全局对象
        ~ Common.js
        ~ AMD、CMD
        ~ UMD
        ~ ES Modules
    - 打包工具
        ~ 可以使用现代的模块系统进行开发并且兼容浏览器环境
        ~ Webpack
        ~ **Rollup**
            > Tree shaking
        ~ Snowpack
            > Bundleless,不打包,利用浏览器对ESM的支持
    - 配置Rollup
        ~ Rollup配置文件
        ~ Rollup插件
            > vue plugin
            > typescript plugin
            > css-only plugin
        ~ 生成ESM和UMD两种格式
        ~ 分析Element Plus的打包过程
    - NPM发布
        ~ npm scripts中的一些钩子函数
        ~ 发布前运行lint和test
        ~ 使用husky添加提交前检查
        ~ npm publish的原理
    - CI/CD
        ~ CI/CD的概念
        ~ 使用Travis CI完成CI-代码提交后运行测试
        ~ 使用Travis CI完成CD-提交特定tag以后自动发布
* 本周特别注意
    - 工具和平台
    - 看文档
    - 亲手写代码
    - 举一反三
posted on 2023-08-17 23:03  一路繁花似锦绣前程  阅读(34)  评论(0编辑  收藏  举报