webpack
- 模块打包
- 模块加载器(Loader)---- 编译转化
- 代码拆分
- 资源模块 (Asset Module)
简单使用
- 安装
yarn add webpack webpack-cli --dev
配置文件 webpack.config.js
-
运行在node.js中
-
工作模式:
- 默认production (自动压缩)
- development (调试优化)
- none
```js
module.exports{
mode: 'development '
entry: './xxx',
output: {
filename: 'xxx.js',
path: path.join(__dirname, 'output')
}
}
```
常用加载器分类
-
编译转换类(转化未js形式工作)
- css-loader
- babel-loader
-
文件操作类(通常将文件拷贝到输出目录,同时导出文件访问路径)
- file-loader
-
代码检查类(统一代码风格)
Webpack模块加载方式
- 遵循ES Modules标准的import声明
- 遵循CommonJS标准的require函数
- 遵循AMD标准的define函数和require函数
- 样式代码中的@import指令和url函数
- HTML代码中图片标签的src属性
描述 Webpack 打包的整个过程
-
根据配置找到打包入口(.js)
-
根据import或require语句解析推断文件依赖的资源模块,分别解析每个资源模块对应的依赖,最终得出依赖树关系
-
递归依赖树,找到每个节点对应的资源文件
-
根据配置文件中的rules属性找到对应的模块加载器处理,最后模块加载器将结果放到bundle.js文件中
Loader 和 Plugin 的不同
Plugin比Loader拥有更宽的能力范围
Loader
- Loader是Webpack的核心特性
- 借助于Loader可以加载任何类型的资源
- 负责资源文件从输入到输出的转换
- 对于同一个资源可以依次使用多个Loader (类似管道)
开发Loader
- 接收资源文件的内容作为参数: source
- 输出是处理后的结果 :作为返回值输出 JS代码
- 在配置使用规则中,配置test / use
markdown-loader.js
module.exports = source =>{
const html = marked(source)
// return `module exports = ${JSON.stringify(html)}`
//return `export default = ${JSON.stringify(html)}` // JSON.stringify 将换行符等转义保留
//返回html交给其他loader处理
return html
}
webpack.config.js
module.exports = {
module: {
rules: [
{
test: /.md$/,
use: [ //由后往前执行
'html-loader',
'./markdown-loader'
]
}
]
}
}
Plugin
它能增强Webpack自动化能力,解决其他自动化工作,常见有自动清除dist目录,拷贝静态文件至输出目录,压缩输出代码
一般导出的是一个类型
开发 Plugin 的思路
-
Plugin它是一个函数或者是一个包含apply方法的对象
-
通过在生命周期的钩子中挂载函数实现扩展
class MyPlugin { apply(compiler) { compiler.hooks.emit.tap('MyPlugin',compilation => { for(const name in compilation.assets){ if(name.endsWith('.js')){ const contents = compilation.assets[name],source() const withoutComments =contents.replace(/\/\*\*+\*\//g, '') compilation.assets[name] = { source: () => withoutComments, size:() => withoutComments.length } } } }) } }
常用插件
-
clean-webpack-plugin
-
html-webpack-plugin
-
通过Webpack输出HTML文件(动态注入,确保了涉及的路径正确);默认导出类型HtmlWebpackPlugin
-
可同时输出多个html文件
-
-
copy-webpack-plugin
- 将文件拷贝到输出目录中
- 上线阶段使用
plugin: [ new CleanWebpackPlugin(), new HtmlWebpackPlugin ({ title: 'xxx', meta: { viewport: 'width-device-width' }, template: './src/index.html' }), new HtmlWebpackPlugin ({ filename: 'other.html', }), new CopyWebpackPlugin([ 'public/**' ]) ]
开发配置
-
watch 监听文件变化,自动重新打包
-
Webpack Dev Server
- 静态资源访问
- 代理API (开发阶段跨域问题)
devServer: { contentBase: './public', //静态资源访问路径 proxy: { '/api': { //代理请求前缀 target: 'https://api.github.com', pathRewrite: { //代理重写 '^/api': '' }, changeOrigin: true } } }
**自动刷新的问题:自动刷新导致的页面状态丢失**
解决:模块热替换(运行时即时替换模块,应用运行状态不受影响)== HMR特性
-
HMR特性
-
集成在webpack-dev-serve中
-
不可以开箱即用,需手动处理JS模块更新后的热-替换
-
框架下的开发,每种文件是有规律的,通过脚手架创建的项目内部都集成了HMR方案
const webpack = require('webpack') devServer: { hot: true }, plugins: [ new webpack.HotModuleReplacementPlugin() ] main.js module.hot.accept('./editor',() => { console.log('更新了editor模块,不会自动刷新了') })
-
-
source Map
定位错误
devtool: 'source-map'
devtool模式选择使用
-
开发模式
cheap-module-source-map
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vGTrU9MC-1621672397267)(C:/Users/86150/AppData/Roaming/Typora/typora-user-images/image-20210519080907774.png)]
-
生产模式
none (不暴露源代码内容)
生产环境优化
-
注重运行效率
-
为不同的工作环境创建不同的配置
-
DefinePlugin webpack内置对象,可应用注入全局对象
-
Tree Shaking 移除未使用代码;生产模式自动开启;代码是ESM的才能用
webpack.config.js module.exports = { optimization: { usedExports: true, //负责标记“枯树叶” //minimize: true //负责“摇树叶” concatenateModules: true, sideEffects: true //开启功能 } } package.json //"sideEffects": false //确保代码无副作用 "sideEffects": [ './src/xxx.js' //有副作用文件,最终会被打包 ]
-
合并模块 concatenateModules : 尽可能将所有模块合并输出到一个函数中,提升效率,减少体积(因为作用域提升了,节省了开销)
-
sideEffects 一般用于npm包标记是否有副作用;生产模式自动开启
-
代码分割,分包,按需加载
-
多入口打包
用于多页应用程序;公共模块提取
entry: { index: './src/index.js', about: './src/about.js' }, output: { filename: '[name].bundle.js' }, plugins: [ new HtmlWebpackPlugin ({ title: 'xxx', meta: { viewport: 'width-device-width' }, template: './src/index.html', filename: 'index.html', chunks: ['index'] //注入指定的js文件 }), new HtmlWebpackPlugin ({ title: 'xxx', template: './src/about.html', filename: 'about.html', chunks: ['about'] }), ]
//公共模块提取 module.exports = { optimization: { splitChunks: { chunks: 'all' } } }
-
动态导入实现按需加载,导入的模块会被自动分包
if(true){ //魔法注释会将相同名称的包打包在一个文件中 import(/* webpackChunkName: 'a' */'./xxx/A').then({default: A} => { body.appendChild(A()) }) }
-
MiniCssExtractPlugin 提取css到单个文件中
plugins: [ new MiniCssExtractPlugin(), //使用后不需要style-loader将css注入到js中了(替换为MiniCssExtractPlugin.loader),适用于大于150kb的css文件提取 new MiniCssExtractPlugin('[name].bundle.css') //此处任何情况都工作 ] module.exports = { optimization: { splitChunks: { chunks: 'all' }, minimizer: [ new TerserWebpackPlugin() new MiniCssExtractPlugin() //只在生产模式压缩css,但会影响js的自带压缩,需重新配置js压缩TerserWebpackPlugin ] } }
-
OptimizeCssAssetsWebpackPlugin 压缩css文件
-
生产模式下,文件名使用Hash
防止客户端静态资源缓存时间过短或过长更新替换未能及时刷新的问题
//1.hash 项目级别,只要改变,全部文件都更改 output: { filename: '[name]-[hash].bundle.js' } plugins: [ new MiniCssExtractPlugin('[name]-[hash].bundle.css') ] //2.chunkhash 同一路的则是相同的hash,更精确 output: { filename: '[name]-[chunkhash].bundle.js' } plugins: [ new MiniCssExtractPlugin('[name]-[chunkhash].bundle.css') ] //3.contenthash 文件级别,解决缓存问题最佳方式,可指定hash占位符长度 output: { filename: '[name]-[contenthash:8].bundle.js' } plugins: [ new MiniCssExtractPlugin('[name]-[contenthash:8].bundle.css') ]
-
le.js’
}
plugins: [
new MiniCssExtractPlugin(’[name]-[chunkhash].bundle.css’)
]
//3.contenthash 文件级别,解决缓存问题最佳方式,可指定hash占位符长度
output: {
filename: '[name]-[contenthash:8].bundle.js'
}
plugins: [
new MiniCssExtractPlugin('[name]-[contenthash:8].bundle.css')
]
```