webpack 
只能识别 js
概念:
- loader 加载器,主要用于代码转换,比如 JS 代码降级,CSS 预编译、模块化等 
- plugin 插件,webpack 打包流程中每个环节都提供了钩子函数,可以利用这些钩子函数参与到打包生命周期中,更改或增加 webpack 的某些功能,比如生成页面和 css 文件、压缩打包结果等 
- module 模块。webpack 将所有依赖均视为模块,无论是 js、css、html、图片,统统都是模块 
- entry 入口。打包过程中的概念,webpack 以一个或多个文件作为入口点,分析整个依赖关系。 
- chunk 打包过程中的概念,一个 chunk 是一个相对独立的打包过程,以一个或多个文件为入口,分析整个依赖关系,最终完成打包合并 
- bundlewebpack 打包结果 
- tree shaking 优化。在打包结果中,去掉没有用到的代码(es6 静态模块化)。 
- HMR 热更新。是指在运行期间,遇到代码更改后,无须重启整个项目,只更新变动的那一部分代码。 
- dev server 开发服务器。在开发环境中搭建的临时服务器,用于承载对打包结果的访问 hash: 
- hashhash 是跟整个项目的构建相关,只要项目里有文件更改,整个项目构建的 hash 值都会更改,并且全部文件都共用相同的 hash 值 
- chunkhash 每个打包过程单独的 hash 值,如果一个项目有多个 entry,则每个 entry 维护自己的 chunkhash。 
- contenthash 每个文件内容单独的 hash 值,它和打包结果文件内容有关,只要文件内容不变,contenthash 不变。 
treeshaking:
tree-shaking 仅支持 ESM 的静态导入语法,对于 CMJ 或者 ESM 中的动态导入不支持 tree shaking。
- 标记 webpack 在分析依赖时,会使用注释的方式对导入和导出进行标记,对于模块中没有被其他模块用到的导出标记为 unused harmony export
- 删除之后在 Uglifyjs (或者其他类似的工具) 步骤进行代码精简,把标记为无用的代码删除。
打包原理:
- 初始化参数:从配置文件和 Shell 语句中读取与合并参数,得出最终的参数
- 开始编译:用上一步得到的参数初始化 Compiler 对象,加载所有配置的插件,执行对象的 run 方法开始执行编译
- 确定入口:根据配置中的 entry 找出所有的入口文件
- 编译模块:从入口文件出发,调用所有配置的 loader 对模块进行翻译,再把翻译后的内容转换成 AST,通过对 AST 的分析找出该模块依赖的模块,再 递归 本步骤直到所有入口依赖的文件都经过了本步骤的处理
- 完成模块编译:在经过第 4 步使用 loader 翻译完所有模块后,得到了每个模块被翻译后的最终内容以及它们之间的 依赖关系图
- 输出资源:根据入口和模块之间的依赖关系,组装成一个个包含多个模块的 Chunk,再把每个 Chunk 转换成一个单独的文件加入到输出列表,这步是可以修改输出内容的最后机会
- 输出完成:在确定好输出内容后,根据配置确定输出的路径和文件名,把文件内容写入到文件系统
热更新原理:
当开启热更新后,页面中会植入一段 websocket 脚本,同时,开发服务器也会和客户端建立 websocket 通信,当源码发生变动时,webpack 会进行以下处理:
- webpack 重新打包
- webpack-dev-server 检测到模块的变化,于是通过 webscoket 告知客户端变化已经发生
- 客户端收到消息后,通过 ajax 发送请求到开发服务器,以过去打包的 hash 值请求服务器的一个 json 文件
- 服务器告诉客户端哪些模块发生了变动,同时告诉客户端这次打包产生的新 hash 值
- 客户端再次用过去的 hash 值,以 JSONP 的方式请求变动的模块
- 服务器响应一个函数调用,用于更新模块的代码
- 此时,模块代码已经完成更新。客户端按照之前的监听配置,执行相应模块变动后的回调函数。
加快打包速度:
- noParse 很多第三方库本身就是已经打包好的代码,对于这种代码无须再进行解析,可以使用 noParse 配置排除掉这些第三方库
- externals 对于一些知名的第三方库可以使用 CDN,这部分库可以通过 externals 配置不进行打包
- 限制 loader 的范围在使用 loader 的时候,可以通过 exclude 排除掉一些不必要的编译,比如 babel-loader 对于那些已经完成打包的第三方库没有必要再降级一次,可以排除掉
- 开启 loader 缓存可以利用 cache-loader 缓存 loader 的编译结果,避免在源码没有变动时反复编译
- 开启多线程编译可以利用 thread-loader 开启多线程编译,提升编译效率
- 动态链接库对于某些需要打包的第三方库,可以使用 dll 的方式单独对其打包,然后 DLLPlugin 将其整合到当前项目中,这样就避免了在开发中频繁去打包这些库