Webpack相关知识
Webpack并不会对ES6语法进行转换
- 需要通过插件babel-loader
Webpack模块加载方式
遵循ES Modules标准的import声明
遵循CommonJS标准的require函数
遵循AMD标准的define函数和require函数
Loader加载的非javascript也会触发资源加载
- css-loader加载的css文件
- 样式代码中的@import指令和部分属性的url函数
- html代码中图片标签的src属性
WebPack的核心工作原理
- Loader机制是Webpack的核心
- Loader可以实现各种各样资源的加载
WebPack开发一个Loader
- Loader负责资源文件从输入(Source)到输出(return)的转换
- Loader是一个管道的概念,对于同一个资源可以依次使用多个Loader
WebPack插件机制(核心特性)
- 两个核心:loader和插件机制
- loader专注实现资源模块加载
- plugin解决其他自动化工作
plugin
- 拷贝静态文件至输出目录
- 压缩输出代码
WebPack自动清除输出目录插件
- clean-webpack-plugin
- 使用步骤:
- yarn add clean-webpack-plugin --dev
WebPack常用插件
clean-webpack-plugin
html-webpack-plugin
- 自动生成使用bundle.js的HTML
- 不同于clean-webpack-plugin,它自己导出的就是一个插件的类型,不再需要解构。
- 示例
// --------webpack.config.js文件-------
const {CleanWebpackPlugin} = require('clean-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
出现js路径引用错误
- 检查publicpath:"",将其注释掉
自定义输出html文件内容的方式
- 实例对象中传参
- 创建index.html模板,让插件根据模板来生成html文件
同时输出多个页面的文件
- 通过添加多个实例,达到输出多个文件的目的
- 每个对象来负责生成一个html文件
- 演示
plugins:[
// 生成index.html
new HtmlWebpackPlugin({
title:'Webpack Plugin Sample',
meta:{
viewport: 'width=device-width'
},
template:'./src/index.html'
}),
// 生成about.html
new HtmlWebpackPlugin({
filename:'about.html'
})
]
copy-webpack-plugin
- 将静态文件同步拷贝到打包目录下
- 注意:开发阶段最好不要使用这个插件
- 实例
new CopyWebpackPlgin([
'public'
// 可以写文件夹 或者 相对路径 public/**
])
Webpack开发一个插件
- plugin通过钩子机制实现
- 插件必须是:一个函数 或者 一个包含apply方法的对象
- 去掉文件中的注解,可以使用正则表达式来解决
- 通过在生命周期的钩子中挂载函数实现扩展
增强Webpack开发体验
- 常规:编写源代码 -> 打包 -> 运行应用 -> 刷新浏览器
Webpack自动编译
- watch工作模式:监听文件变化,自动重新打包
- 操作:yarn webpack --watch
WebPack自动刷新浏览器
- BrowserSync: 实现自动刷新浏览器
- 步骤
- 关闭http的server
- 运行 browser-sync dist --files "**/*"
- 原理:webpack自动打包源代码到dist目录,dist文件的变化被browser-sync监听,就会实现自动编译并且自动刷新浏览器。
该方法的弊端
- 但是这样就会产生两次磁盘读写操作,效率不高
- 并且需要同时使用两个工具
Webpack Dev Server
- 集成自动编译和自动刷新浏览器等功能
优点
- 为了提高效率,并没有将打包内容放在目录下,而是暂时放在了内存当中。(减少磁盘操作)
- 浏览器从缓存中读取信息,进行加载。
代理API服务
- 跨域请求问题,使用跨域资源共享(CORS)
- 前提是 API要支持CORS
问题:开发阶段接口跨域问题
目标:将github的API代理到本地的开发服务器
- 查阅:主机名是HTTP协议中的相关概念
问题(访问静态资源文件)
- 除了打包的资源能够被访问以外,如果还想访问一些静态资源文件,该怎么办呢?
- 通过配置contentBase,路径,就可以访问到静态资源了。(额外为开发服务器指定查找资源目录)
问题(自动刷新导致的页面状态丢失)
- 最好的状态:页面不刷新的情况下,模块也可以及时更新
- Webpack HMR
Webpack HMR(模块热替换/更新)
- 可以在应用程序的运行过程中实时替换某个模块,而应用的运行状态不受影响.
热更新和自动刷新之间的差异
- 自动刷新会导致页面状态丢失
- 热替换/更新只将修改的模块实时替换至应用中,不会完全刷新应用
Webpack 开启 HMR
- HMR已经集成在webpack-dev-server中,不需要单独安装模块
步骤
- 在运行webpack-dev-server时,在后面添加--hot,开启热更新功能
webpack-dev-server --hot
- 或者在配置文件中添加配置,开启热更新功能
- 问题:如何实现所有模块的热更新替换(按上述步骤后,只有html可以实现,js等不能实现)
HMR并不可以开箱即用
- 因为js脚本文件,不像html只是做一个简单的替换,其还具有自身的逻辑,因此需要我们自己去书写逻辑替换语句,才可以实现.
- 在使用脚手架框架中,也不用自己去写,是因为框架的文件本身就有一定的规则,并且其也会自身集成HMR.
Source Map 源代码地图
- 映射转换后的代码和源代码之间的关系
解决的问题
- 在前端方向引入了构建编译之类的概念之后,导致源代码与运行的代码不一样,所产生的调试的问题。
WebPack配置Source Map
- Webpack支持12种不同的方式,每种方式的效果和效率各不相同。
Webpack生产环境优化
- 生产环境注重运行效率
- 开发环境中注重开发效率
- 为不同的工作环境创建不同的配置
Webpack不同环境下的配置
- 1.配置文件根据环境不同导出不同配置
-
执行命令: yarn webpack --env production
-
返回生产模式下的配置
-
2.一个环境对应一个配置文件(适用于大型项目)
- 开发模式配置 webpack.dev.js
- 生产模式配置 webpack.prod.js
- 公共配置(抽象两者之间公共的配置)webpack.common.js
- 使用步骤:assign()方法会将配置文件同名的属性直接重写,因此不行。这里就可以使用merge
- yarn webpack-merge --dev
- const common = require('./webpack.common');
- module.exports = merge(.....);
module.export = merge(common,{ mode: 'production', plugins: [ new ClearWebpackPlugin(), new CopyWebpackPlugin(['public']) ] }); - yarn webpack --config webpack.prod.js
- 简化命令,下载scripts标签中
// ------------package.json----------- "scripts": { "build": "webpack --config webpack.prod.js" }
-
模式(mode)
Webpack优化打包结果
DefinePlugin
- 为代码注入全局成员
- 注意:这里的书写是一个符合js的逻辑代码
Tree Shaking
- 摇掉代码中未被引用的部分 -> 未引用代码dead-code
- 在生产模式下,自动开启 mode:production
yarn webpack --mode production
Tree Shaking使用
- none模式
- usedExports负责标记[枯树叶]
- 没有使用的就不导出
- minimize负责[摇掉]它们
- 去除掉没有导出的代码
Webpack合并模块函数 Scope Hoisting
- 还可以追加concatenateModules继续优化输出
- 普通的打包输出,是将我们的每一个模块,单独的放在一个函数中,这样的话,当我们的模块很多,也就意味着会有很多的模块函数。
使用concatenateModules
- 可以将所有的模块,输出到一个函数中
- 优点:
- 尽可能的将所有模块合并输出到一个函数中
- 既提升了运行效率,又减少了代码的体积。
- 额外补充:minimize也可以减少代码的体积,两者可以配合使用。
Webpack Tree Shaking 与 Babel
- Tree Shaking前提是ES Modules组织代码,也就是Webpack打包的代码必须是使用ESM
- Webpack打包之前,先是将模块根据配置交给不同的loader去处理,最后将所有loader处理的结果打包到一起。
- 而为了转换代码中ECMAScript新特性,会使用babel-loader,这个loader在转换的过程中就有可能将ES Modules 转换成 CommonJS,总的来说:这取决于我们有没有使用转换ES Modules的插件
- 在我们使用的preset-env这个集合里面,就有这个插件。
解决方法
Webpack4中新增了sideEffects(副作用)
- 允许通过配置的方式去标识代码是否有副作用,从而为Tree Shaking提供更大的压缩空间
副作用:模块除了导出成员之外所作的事情。
- sideEffects一般用于npm包标记是否有副作用
- 在production模式下,同样也会自动开启
- 没有副作用的模块,就不会被打包
步骤
- 开启功能
- 标识代码是否有副作用
注意事项
- 如果不表明有副作用,webpack打包的时候会自动忽略有副作用的代码.
- 举例:模块的原型方法 \ css文件
- 引用进来即可
Webpack代码分割 Code Splitting
- 所有的代码最终都被打包到一起,bundle体积就会过大.但是并不是每个模块在启动时都是必要的.
问题
- 会浪费掉很多的流量和带宽
- 解决办法
- 按照一定的规则,将其分离到多个bundle中,根据应用的需要去加载模块.
- 提高应用的响应速度,运行效率.
实际的解决方法
多入口打包 Multi Entry
- 适用于:传统的多页应用程序
- 一个页面对应一个打包入口,不同页面之间公共部门单独提取.
- 在输出的html中指定需要注入的bundle
提取公共模块
- 不同的入口中肯定会有公共模块
- 将所有公共的模块提取到一个bundle中
- 开启功能
动态导入 Dynamic Imports
- 按需加载: 需要用到某个模块时,再加载这个模块.
- 动态导入的模块会被自动分包
- 步骤:
- 无需配置任何地方,前提需要根据ES Module动态导入成员的方式,去导入模块
- webpack会自动分包
比较
- 动态导入比多入口打包更灵活
- 可以更灵活的去控制需不需要加载某个模块
- 或者什么时候需要加载这个模块
Webpack魔法注释 Magic Comments
- 如果名称相同,最后会被打包到一起
Webpack MiniCssExtractPlugin
- 提取css到单个文件
- 实现css的按需加载
使用步骤
- yarn add mini-css-extract-plugin --dev
- 导入插件模块 webpack.config.js
- 修改使用的loader
注意
- 只有当css文件超过了150kb才可以考虑是否需要提取到单个的css文件
- 因为当文件较小的时候,嵌入到文件中,就可以减少一次请求,效果会更好.
Webpack OptimizeCssAssetsWebpackPlugin
- 压缩输出的css文件
使用步骤
- yarn add optimize-css-assets-webpack-plugin --dev
- 在minimizer的管理下,使用这个插件.(只有开启minimizer服务,才会运行这个插件,生产模式下默认开启minimizer)
出现问题
- js文件没有压缩成功
- 解决方法: 添加js压缩插件 TerserWebpackPlugin
Webpack输出文件名Hash (substitutions)
- hash
- chunkhash:是同一路的输出文件,hash相同
- contenthash: 根据不同的文件就会生成不同的hash值
总结
- contentHash是解决缓存问题,最好的方式.
- 因为其精确的定位到了文件级的hash,只有文件发生了变化才会更新文件名
指定hash的长度
- 如果觉得20位hash比较长
- 指定缓存问题最好的选择
VSCode 中折叠代码的快捷键
- Ctrl + K,Ctrl + 0 (macOS:Command + K,Command + 0)