目录
三、Mode模式分 development 和 production
一、什么是webpack
Webpack是一个前端资源加载/打包工具。它将根据模块的依赖关系进行静态分析,然后将这些模块按照指定的规则生成对应的静态资源
二、五个核心概念
- Entry 入口指示 Webpack以那个文件为入口起点开始打包,分析构建内部依赖图
- Output 输出指示webpack打包后的资源bundles输出到哪里去,以及如何命名
- Loader 让webpack能够去处理哪些非js文件
- Plugins 插件可以用于执行范围更广的任务,插件的范围包括:从打包优化和压缩。一直到重新定义环境中的变量等
- Mode 模式mode指示webpack使用相应模块的配置
三、Mode模式分 development 和 production
development
webpack.config.js 配置:
//开发环境的配置 :能让代码运行起来
const { resolve } = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
entry: './src/js/index.js',
output: {
filename: 'js/built.js',
path: resolve(__dirname, 'build'),
},
module: {
rules: [
//loader配置
{
test: /\.less$/,
use: ['style-loader', 'css-loader', 'less-loader']
},
{
//处理css资源
test: /\.css$/,
use: ['style-loader', 'css-loader']
},
{
//处理图片资源是es6解析
test: /\.(jpg|png|gif)$/,
loader: 'url-loader',
options: {
limit: 8 * 1024,
name: '[hash:10].[ext]',
//关闭es6模块化 因为两个模块解析不一样所以关闭es6模版
esModule: false,
outputPath:'images'
}
},
{
//处理html中img资源是commmjs解析
test: /\.html$/,
loader:'html-loader'
},
{
//处理其他资源
exclude: /\.(html|css|js|less|jpg|png|gif)/,
//file-loader与url-loader的区别:前者就是将文件打包,后者还带有限制条件 比如limit
loader: 'file-loader',
options: {
name: '[hash:10].[ext]',
outputPath:'media'
}
}
]
},
plugins: [
//plugins的配置
new HtmlWebpackPlugin({
template:'./src/index.html'
})
],
mode:'development',
devServer: {
contentBase: resolve(__dirname, 'build'),
compress: true,
port: 3000,
open:true
}
}
production
webpack.config.js 配置:
const { resolve } = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
// 定义nodejs环境变量:决定使用browserslist的哪个环境
process.env.NODE_ENV = 'production';
// 复用loader
const commonCssLoader = [
MiniCssExtractPlugin.loader,
'css-loader',
{
// 还需要在package.json中定义browserslist
loader: 'postcss-loader',
options: {
ident: 'postcss',
plugins: () => [require('postcss-preset-env')()]
}
}
];
module.exports = {
entry: './src/js/index.js',
output: {
filename: 'js/built.js',
path: resolve(__dirname, 'build')
},
module: {
rules: [
{
test: /\.css$/,
use: [...commonCssLoader]
},
{
test: /\.less$/,
use: [...commonCssLoader, 'less-loader']
},
/*
正常来讲,一个文件只能被一个loader处理。
当一个文件要被多个loader处理,那么一定要指定loader执行的先后顺序:
先执行eslint 在执行babel
*/
{
// 在package.json中eslintConfig --> airbnb
test: /\.js$/,
exclude: /node_modules/,
// 优先执行
enforce: 'pre',
loader: 'eslint-loader',
options: {
fix: true
}
},
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader',
options: {
presets: [
[
'@babel/preset-env',
{
useBuiltIns: 'usage',
corejs: {version: 3},
targets: {
chrome: '60',
firefox: '50'
}
}
]
]
}
},
{
test: /\.(jpg|png|gif)/,
loader: 'url-loader',
options: {
limit: 8 * 1024,
name: '[hash:10].[ext]',
outputPath: 'imgs',
esModule: false
}
},
{
test: /\.html$/,
loader: 'html-loader'
},
{
exclude: /\.(js|css|less|html|jpg|png|gif)/,
loader: 'file-loader',
options: {
outputPath: 'media'
}
}
]
},
plugins: [
new MiniCssExtractPlugin({
filename: 'css/built.css'
}),
new OptimizeCssAssetsWebpackPlugin(),
new HtmlWebpackPlugin({
template: './src/index.html',
minify: {
collapseWhitespace: true,
removeComments: true
}
})
],
mode: 'production'
};
四、性能优化配置
4.1、开发环境性能优化
优化打包构建速度 HMR
HMR: hot module replacement 热模块替换 / 模块热替换
作用:一个模块发生变化,只会重新打包这一个模块(而不是打包所有模块)
极大提升构建速度
//开启HMR功能
//当修改了webpack配置,新配置想要生效,必须重启webpack服务
hot:true
- 样式文件:可以使用HMR功能 :因为style-loader内部实现了~
- js文件:默认不能使用HMR功能 -->需要修改js代码,添加支持HMR功能的代码 【HMR功能对js的处理,只能处理非入口文件的配置,对入口文件中引入的其他文件,入口文件不能使用】
if (module.hot) { //一旦module.hot =true ,说明开启了HMR功能 ---让HMR功能代码生效 module.hot.accept('./print.js', function () { //方法会监听 print.js文件的变化,一旦发生变化,其他模块默认不会重新打包构建 //会执行后面的回调函数 print(); }) }
- html文件:默认不能使用HMR功能,同时会导致问题--->html文件不能热更新了~(不用做HMR功能)【解决:修改entry入口,将html文件引入】
entry: ['./src/js/index.js','./src/index.html'],
优化代码调试 source-map
source-map:一种提供源代码到构建代码映射技术(如果构建后代码出错了,通过映射可以追踪源代码错误)
devtool:'eval-source-map'
devtool :里面的参数配置还有:[inline-|hidden-|eval-] [nosources-] [cheap-[module-]]source-map
* 内联和外部的区别
1、外部生成了文件,内联没有
2、内联构建速度更快
- source-map:外部
提示错误代码准确信息 和源代码的错误位置
- inline-source-map :内联
只生成一个内联source-map 【built.js里面】
提示错误代码准确信息 和源代码的错误位置
- hidden-source-map :外部
提示错误代码错误原因,但是没有错误位置
不能追踪源代码错误,只能提示到构建后代码的错误位置
- eval-source-map :内联
每一个文件都生成source-map 都在eval中
提示错误代码准确信息 和源代码的错误位置
- nosources-source-map:外部
提示错误代码准确信息 但是没有任何源代码信息
- cheap-source-map:外部
错误代码准确信息 和 源代码的错误位置
只能精确到行
- cheap-module-source-map:外部
错误代码准确信息 和 源代码的错误位置
module会将loader的source map加入
使用场景
* 开发环境:速度快,调试更友好
速度快(eval>inline>cheap>...)
eval-cheap-source-map
eval-source-map
调试更友好
source-map
cheap-module-source-map
cheap-source-map
最终的结果:eval-source-map / eval-cheap-module-source-map
* 生产环境:源代码要不要隐藏?调试要不要更友好
内联会让代码体积变得非常大 所以生产环境下不用内联
隐藏源代码:
nosources-source-map 全部隐藏
hidden-source-map 只隐藏源代码,会提示构建后代码错误信息
调试最好用:
source-map
速度更快用:
cheap-module-source-map
4.2、生产环境性能优化
优化打包构建速度
oneOf :一个文件被一个loader处理好了,下面的loader就不用看了
//以下loader只会匹配一次 让loader处理的性能更好
//注意:不能有两个配置处理同一种类型文件
oneOf: [
{
test: /\.css$/,
use: [...commonCssLoader]
},
{
test: /\.less$/,
use: [...commonCssLoader, 'less-loader']
},
/*
正常来讲,一个文件只能被一个loader处理。
当一个文件要被多个loader处理,那么一定要指定loader执行的先后顺序:
先执行eslint 在执行babel
*/
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader',
options: {
presets: [
[
'@babel/preset-env',
{
useBuiltIns: 'usage',
corejs: { version: 3 },
targets: {
chrome: '60',
firefox: '50'
}
}
]
]
}
},
{
test: /\.(jpg|png|gif)/,
loader: 'url-loader',
options: {
limit: 8 * 1024,
name: '[hash:10].[ext]',
outputPath: 'imgs',
esModule: false
}
},
{
test: /\.html$/,
loader: 'html-loader'
},
{
exclude: /\.(js|css|less|html|jpg|png|gif)/,
loader: 'file-loader',
options: {
outputPath: 'media'
}
}
]
babel缓存
//开启babel缓存
//第二次构建时,会读取之前的缓存
cacheDirectory:true,
output: {
filename: 'js/built.[contenthash:10].js',
path: resolve(__dirname, 'build')
},
文件资源缓存: 让代码上线运行缓存更好使用
- hash:每次js和css同时使用会产生一个唯一hash值
问题:因此js和css同时使用一个hash值。如果重新打包,会导致所有缓存失效,(可能却只改动了一个文件)
- chunkhash:根据chunk生成的hash值。如果打包来源于同一个chunk,那么hash值就一样
问题:因此js和css同时使用一个hash值,因为css是在js中引入的,所以同属于一个chunk
- contenthash:根据文件的内容生成的hash值。不同文件hash值一定不一样
多进程打包
优点:开启多进程打包
缺点:进程开启是有时间的 进程开启大概为600ms,进程通信也有开销
用于使用到一些运用消耗时间比较长的,js
thread-loader 来针对 eslint-loader进行优化
如果构建时间很短,使用多进程打包,会变的慢{ loader: 'thread-loader', options: { workers:2 //进程2个 } },
让某些库不打包
dll
使用dll技术,对某些库(第三方库:jquery、react、vue...)进行单独打包
只需要打包一次webapck.config.js:
plugins: [ new HtmlWebpackPlugin({ template: './src/index.html' }), // 告诉webpack哪些库不参与打包,同时使用时的名称也得变~ new webpack.DllReferencePlugin({ manifest: resolve(__dirname, 'dll/manifest.json') }), // 将某个文件打包输出去,并在html中自动引入该资源 new AddAssetHtmlWebpackPlugin({ filepath: resolve(__dirname, 'dll/jquery.js') }) ],
webpack.dll.js:
/* 使用dll技术,对某些库(第三方库:jquery、react、vue...)进行单独打包 当你运行 webpack 时,默认查找 webpack.config.js 配置文件 需求:需要运行 webpack.dll.js 文件 --> webpack --config webpack.dll.js */ const { resolve } = require('path'); const webpack = require('webpack'); module.exports = { entry: { // 最终打包生成的[name] --> jquery // ['jquery'] --> 要打包的库是jquery jquery: ['jquery'], }, output: { filename: '[name].js', path: resolve(__dirname, 'dll'), library: '[name]_[hash]' // 打包的库里面向外暴露出去的内容叫什么名字 }, plugins: [ // 打包生成一个 manifest.json --> 提供和jquery映射 new webpack.DllPlugin({ name: '[name]_[hash]', // 映射库的暴露的内容名称 path: resolve(__dirname, 'dll/manifest.json') // 输出文件路径 }) ], mode: 'production' };
externals
:拒绝一个包被打包,如果要用这个包的话,就在html里面引进这个包,可以提高这个打包速度
该程序用外联引入就使用externals打包//先拒绝jquery打包 externals: { //忽略库名 ---npm包名 拒接jquery这个包被打包进来 jquery:'jQuery' } //再在html里面引进jquery就可以使用但不打包 CDN连接 <script src="https://cdn.bootcss.com/jquery/1.12.4/jquery.min.js"></script>
优化代码运行的性能
tree shaking
tree shaking:去除应用程序中没有使用过的无用代码
前提:1、必须使用es6模块化 2、开启production环境
作用:减少代码体积:比如说两个js文件但是只使用了一个宁一个就会被树摇掉
在package.json中配置
“sideEffects”:false 所有代码都没有副作用(都可以进行tree shaking)
问题:可能会吧css | @babel/polyfill 文件干掉
解决方法:"sideEffects":["*.css","*.less"]写在里面的就不会被tree shaking干掉
code split
代码分割 就可以进行按需加载,也就是并行加载
三种方案:
1⃣️ :多入口optimization
//可以将node_modules中代码单独打包一个chunk最终输出 //自动分析多入口chunk中,有没有公共的文件(文件大小是有要求的),如果有,会打包成单独的一个chunk optimization: { splitChunks: { chunks: 'all' } },
2⃣️: import 导入的文件
import导入的文件都可以按需加载,都可以拆分打包
import(/* webpackChunkName:'test' */'./test') .then(({mul,count}) => { //文件加载成功 // eslint-disable-next-line // console.log(result); console.log(mul(1,2)); }) .catch(() => { // eslint-disable-next-line console.log('加载文件失败~'); })
3⃣️ :多入口拆分
一个入口拆分成一个chunk
懒加载/预加载
document.getElementById('btn').onclick = function () {
// console.log(mul(1, 2));
//懒加载:当文件需要使用时才加载
//懒加载 当前test.js不会被加载,只有在点击按钮之后才会加载test.js[文件会分割]
// import(/*webpackChunkName:'test' */'./test').then(({ mul }) => {
// console.log(mul(2, 2));
// })
//预加载prefetch:会在使用之前,提前加载js文件,
//正常加载可以认为是并行加载(同一时间加载多个文件)
//预加载prefetch:等其他资源加载完毕,浏览器空闲了,在偷偷加载资源
import(/*webpackChunkName:'test',webpackPrefetch:true */'./test').then(({ mul }) => {
console.log(mul(2, 2));
})
}
PWA
渐进式网络开发形式
new WorkboxWebpackPlugin.GenerateSW({
/**
* 1、帮助serviceworker快速启动
* 2、删除旧的 serviceworker
*
* 文件~
*/
clientsClaim: true,
skipWaiting:true
})
index.js
/**
* 1、eslint不认识 window、navigater全局变量
* 解决:需要修改package.json中eslintConfig配置
* "env":{
* "browser":true //支持浏览器端全局变量
* }
* 2、sw代码必须运行在服务器上
* ---nodejs (直接写服务器)
* --->下载包 运行指令
* npm i serve -g
* serve -s build 启动服务器,将bulid目录下所有资源作为静态资源暴露出去
*/
// 注册兼容性问题
// 处理兼容性问题
if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker
.register('/service-worker.js')
.then(() => {
console.log('sw注册成功了');
})
.catch(() => {
console.log('sw注册失败了');
});
});
}
以上就是webpack的基础配置啦
更多配置详解请查阅:概念 | webpack 中文文档