webpack
webpack 核心配置
-
Entry
-
入口(Entry)指示webpack 以哪个文件为入口起点开始打包,分析构建内部依赖图。
单入口
sring:指定文件的入口文件,分析构建 。 值为一个路径字符串多入口
Array:所有入口文件最终形成一个chunk,输出出去的只有一个bundle文件.(只有在HMR功能中让html 热更新生效)多入口
object:有多少个入口,最终输出多少个bundle,
-
-
Output
-
输出(Output)指示webpack打包后资源 bundles 输出到哪里去,以及如何命名。
filename:文件名称,指定名称+目录path:输出文件目录,将来所有文件资源输出的公共目录pubilicPath:所有资源引入公共路径前缀chunkFilename:非入口chunk的名称library:整个库向外暴露的变量名libraryTarget:变量名添加到那个上 browser,node ,commonjs
-
-
loader
-
loader 让webpack 能够去处理那些非JavaScript 文件(webpack 本身只理解JavaScript)
exclude:排除某些不执行include:只检查指定目录下的文件enforce:是否优先loader:单loader 使用options:选择属性可以修改loader 的用途oneof: oneof 下的配置只会生效一个
-
-
Plugins
- 插件(Plugins)可以用于执行范围更广的任务。插件的范围包括,从打包优化和压缩,一直到重新定义环境中的变量等。
-
Mode
- 模式(Mode) 指示webpack 使用相对应的模式的配置
-
resolve
-
解析模块的规则
alias: 配置解析模块路径别名:优点简写路径,缺点路径没有提示extensions:配置省略文件路径的后缀名modules:告诉webpack 解析模块是去哪里找
-
-
devServer
contentBase运行代码的目录watchContentBase监视 contentBase 目录下的文件,一旦文件变化就会reloadwatchOptions忽略文件compress启动gzip压缩port端口号host开启HMR功能open自动打开浏览器clientLogLevel不要显示启动服务器日志信息quiet除了一些基本的启动信息以外,其他内容都不要显示overlay如果错误了,不要全屏提示~proxy动态代理解决跨域问题
| 选项 | 描述 | |
|---|---|---|
| development | 会将process.env.NODE_ENV 的值设为 development。 启用 NameChunksPlugins和NameModulesPlugin。 | 开发模式:让代码在本地调试运行的环境 |
| production | 会将process.env.NODE_ENV的值设为 production, 启用 FlagDependencyUsagePlugin,FlagIncludedChunksPlugin, ModuleConcatenationPlugin, NoEmitOnErrorsPlugin, OccurrenceOrderPlugin,SideEffectsFlagPlugin 和UglifyJsPlugin | 生产环境:让代码优化上线运行的环境。 |
webpack.config.js
// webpack 要是用commonjs 暴露的方式
const {
resolve
} = require('path')
module.exports = {
// webpack配置
// 入口
entry: './src/index.js',
// 输出
output: {
// 输出的文件名
filename: 'build.js',
// 输出的文件路径
// __dirname nodejs 中的变量 ,代表当前文件的绝对路径
path: resolve(__dirname, 'build')
},
// loader 需要加载的配置
module: {
rules: [
//详细的loader
{
//test匹配那些文件,使用的正则表达式的方式.
test: /\.css$/,
use: [
// use 数组中loader 执行顺序为 右到左的方式,依次执行
// style-loader 创建style 标签,将js的样式资源插入到head中生效
//css-loader 将css文件变成commonjs模块 加载到js中,里面的内容是样式字符串
'style-loader', 'css-loader'
]
},
{
test:/\.less$/,
use:[
'style-loader',
'css-loader',
'less-loader'
]
}
]
},
// plugins 的配置
plugins: [],
// 模式
mode: 'development'
}
webpack plugins 使用 (HtmlWebpackPlugin)
html-webpack-plugin 之前 需要 导入该模块
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exprots ={
plugins:[
new HtmlWebpackPlugin({
// 辅助当前目录下的index.html 并且自动引入打包输出的所有资源(JS/CSS)
template:'选择文件模板'
})
]
}
webpack 打包其他资源(其他资源为 除了 html/css/js 都为其他资源)
module: {
rules: [
// 打包除了其他资源(除了HTML/CSS/JS资源以外的资源)
{
exclude:/\.(css|js|html)$/,
loader:'file-loader'
}]
},
webpack devServer (自动保存更新服务器)
使用devServer 首先安装webpack-dev-server 模块
devServer:{
// 项目构建后的路径
contentBase:resolve(__dirname,'build'),
// 是否使用gzip压缩
compress:true,
// 设置端口号
port:3000,
// 是否自动打开浏览器
open:true
}
webpack 单独打包CSS样式 (css 会以link 的形式 引入到html中)
- 使用的mini-css-extract-plugin 这个插件来进行处理
const {
resolve
} = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
module.exports = {
// 入口文件 指定主文件
entry: './src/js/index.js',
// 输出:
output: {
filename: 'js/built.js',
path: resolve(__dirname, 'build')
},
module: {
rules: [{
test: /\.css$/,
// loader: ExtractTextWebpackPlugin.extract('style-loader', 'css-loader')
use: [
// style-loader 是将js中的css 样式以style 标签的形式引入
// 'style-loader',
//使用 MiniCssExtractPlugin 来进行单独打包css
MiniCssExtractPlugin.loader,
// css-loader:是将css样式 转换成js
'css-loader'
]
}]
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html'
}),
new MiniCssExtractPlugin({
// 给输出的css 重命名
filename:'css/build.css'
})
],
mode: 'development'
}
webpack css样式兼容性
- 需要引入两个模块 postcss-loader postcss-preset-env
module.exports ={
module: {
rules: [{
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
// 配置postcss-loader
/*
帮助postcss找到package.js 中的browserslist 里面的配置,通过配置加载指定的css兼容样式。
*/
{
loader: 'postcss-loader',
options: {
ident: 'postcss',
plugins: () => [
// 使用post-css的插件
require('postcss-preset-env')
]
}
}
]
}]
},
}
- package.js 配置 browserslist属性
"browserslist":{
"development":[
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
],
"production":[
">0.1%",
"not dead",
"not op_mini all"
]
}
webpack 压缩css
- webpack中压缩css 需要引入一个插件:optimize-css-assets-webpack-plugin
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin')
module.exports ={
plugins:[
//使用压缩css 样式插件
new OptimizeCssAssetsWebpackPlugin();
]
}
webpack 使用eslint 语法检查
- webpack 使用eslint 需要在package.js进行配置
"eslintConfig":{
"extends":"airbnb-base"
}
- 使用eslint 需要在添加一个loader进行语法规范检查
rules: [
{
// 使用的eslint 语法检查,需要导入 eslint-loader 和 的eslint
// 使用 eslint-config-airbnb-base 要引入 eslint 和 eslint-plugin-import
// 想要使用eslint 对代码进行管理需要在package.js中引入eslint
test: /\.js$/,
exclude: 'node_modules',
enforce:'pro',
loader: 'eslint-loader',
options: {
fix: true,
// eslint-disable-next-line linebreak-style
},
},
],
webapck 中JS代码兼容性处理
- js兼容性处理 babel-loader @babel/core
- 1.基本js兼容性处理 -> @babel/preset-env
- 问题:只能处理基本语法,无法对promise 和高级语法无法兼容
- 2.全部js兼容性处理 -> @babel/polyfill
- 问题:我们只需要解决部分兼容问题,但是将所有的兼容性处理代码全部引入,体积太大~
- 3.需要做兼容性处理的就做:按需加载 -> @core-js
- 1.基本js兼容性处理 -> @babel/preset-env
rules: [
/*
js兼容性处理 babel-loader @babel/core
* 1.基本js兼容性处理 -> @babel/preset-env
* 问题:只能处理基本语法,无法对promise 和高级语法无法兼容
* 2.全部js兼容性处理 -> @babel/polyfill
* 问题:我们只需要解决部分兼容问题,但是将所有的兼容性处理代码全部引入,体积太大~
* 3.需要做兼容性处理的就做:按需加载 -> @core-js
*/
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader',
options: {
presets: [
['@babel/preset-env', {
// 按需加载
useBuiltIns: 'usage',
// 指定corejs 版本 为3个版本
corejs: {
version: 3
},
// 指定兼容性需要做到那个浏览器
targets: {
chrome: '60',
firefox: '60',
ie: '9',
safari: '10',
edge: '17'
}
}]
]
}
}
]
webpack HMR模块热替换
hot module replacement 模块热替换
作用: 一个模块发生变化,只会重新打包这一个模块,而不是打包所有模块,极大提升构建速度
css文件:可以使用HMR功能呢,因为style-loader 已经实现了
js文件:默认不能使用HMR功能,需要修改js代码,添加支持HMR更新的代码
注意:HMR功能只对JS的处理,只能处理非入口js文件和其他文件
if(module.hot){
module.hot.accept('./print.js',function(){ //告诉 webpack 接受热替换的模块
console.log('Accepting the updated printMe module!')
})
}
html文件:默认不会使用HMR功能,同事会导致问题:HTML文件不能热更新了
解决:修改entry入口,将html文件引入
webapck sourecemap
source-map:会生成map格式的文件,里面包含映射关系
inline-source-map:不会生产map格式的文件,包含映射关系的代码会放在打包后的代码中
注意:使用iline-source-map 的时候,map文件就会变成一个很长的base64的字符串 被放在main.js的底部和source-map的区别是直接 map文件打包到了main文件内
cheap-inline-source-map:cheap有两种作用,一是将错误只定位到行,不定位到列。二是映射业务代码,不映射loader和第三方库等。会提升打包构建速度。
cheap-module-inline-source-map:业务代码,loader和第三方库代码都会映射
eval:打包速度最快的一种方式,针对于比较复杂的代码时 eval 提示出来的内容可能并不是很全面。
最佳实践:
devtool:'cheap-module-source-map'
开发环境:使用 cheap-module-eval-source-map 提示出来的错误比较全,打包速度是比较快的
生产环境:使用cheap-module-source-map
webpack oneof 优化生产环境下打包速度
oneof 下每个loader 只会执行一次
注意:同一种文件类型的文件,oneof 只会执行一次 所以不能再oneof 下处理同一种文件
module.exports ={
module:{
rlues:[
{
oneof:[
{
loader
}
]
}
]
}
}
webpack cache 缓存
babel 缓存
{
test:/\.js$/,
exclude:/node_modules/,
laoder:"babel-loader",
options:[
presets:[
['@babel/preset-env',{
useBuiltIns:'usage',
corejs:{vsersion:3},
targets:{
chrome:'60',
firefox:'50'
}
}]
],
cacheDirectory:true
]
}
文件资源缓存
hash:每次打包webpack构建时会生成一个唯一的hash值。
问题:因为js和css 同时使用一个hash值,如果重新打包,会导致所有缓存失效,
chunkhash:根据chunkhash生成的hash值,如果打包来源与同一个chunk,那么hash值就一样
问题:js和css 值还是一样的,因为css 是在js中引入的,所以同属于一个chunk
contenthash:根据文件的内容生成的hash值。不同文件hash值,肯定不一样
webpack tree shaking
前提:1,必须使用es6模块化 2.开始production 环境
作用:减少代码的体积
在package.js文件中配置
"sideEffects":false 所有代码都没有副作用(都可以进行tree shaking)
问题:可能会把css / @babel/polyfill (副作用) 文件干掉
"sideEffects":["*.css"," *.less"]
webpack split code 代码分割
第一种方法:配置多入口,多页面
注意:如果有公共代码块,那么打包之后的两个bundle里面都会包含重复的模块,以及不够灵活,不能被webpack通过splitChunks的规则灵活地优化。
const path = require('path');
module.exports = {
mode: 'development',
entry: {
index: './src/index.js',
another: './src/another-module.js',
},
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist'),
},
};
第二种方法:optimization.splitChunks (entry 单入口)
可以将node_modules 中的代码单独打包一个chunk最终输出,
自动分析多入口chunk中,有没有公共文件。如果有会打包成一个chunk
optimization:{
splitChunks:{
chunks:'all'
}
}
第三种方法:import() 动态导入 (entry 单入口)
Dynamic Imports:动态加载,在函数中使用import()语法,webpack使用SplitChunk将import()加载的module分割为一个单独的chunk,并在函数执行时加载改chunk对应的JS文件。
import ('./test').then(({mothod1,mothod2})=>{
console.log(mothod1(2,4));
}).catch(()=>{
console.log("文件加载失败了。");
})
webpack 懒加载 和预加载
懒加载:当文件需要的时候在加载。
import Test from './test';
document.getElementById('btn').onclick = funcation (){
import('./test').then((Test) => {
console.log(Test.mul(4, 5))
}).catch(() => {
})
}
预加载:(当主代码加载完成后,预加载引入异步代码,等到异步代码触发时,会再次加载,但此时缓存中已经加载过,所以执行效率会变高)
注意:当使用预加载的时候 需要加上/* webpackPrefetch:true */
import Test from './test';
document.getElementById('btn').onclick = funcation (){
import(/* webpackPrefetch:true */'./test').then((Test) => {
console.log(Test.mul(4, 5))
}).catch(() => {
})
}
Webpack PWA (渐进式网络程序)
添加 workbox-webpack-plugin 插件,并调整webpack.config.js配置
new WorkboxWebapckPlguin.GenerateSW({
// clientsClaim:让ServiceWorkbox 快速启动
// skipWaiting:删除旧的ServiceWorkbox
clientsClaim:true,
skipWaiting:true
})
注册workbox在主入口中
if('serviceWorker' in navigator){
window.addEventListener('load',()=>{
navigator.serviceWorker.register('/service-worker.js').then(()=>{
console.log("sw注册成功了")
}).catch((registrationError)=>{
console.log("sw注册失败了",registrationError)
})
})
}
thread-loader (多进程打包)
使用thread-loader 对babel -loader 进行多线程打包
{
test:/\.js$/,
exclude:/node_modules/,
use:[
{
//开启多线程打包
loader:'thread-loader',
options:{
workers:2 //两个进程
}
}
]
}
externals (拒绝打包)
在webpack.config.js 文件
externals:{
jquery:'jQurey'
}
dllPlugin 打包
DLLPlugin 和 DLLReferencePlugin 用某种方法实现了拆分 bundles,同时还大大提升了构建的速度。
context (optional):manifest 文件中请求上下文(context)默认值为webpack的上下文(context)
name :暴露出去的DLL的函数名
path:manifest.json文件的绝对路径
new webpack.DllPlugin({
// 暴露出的dll函数名
name:'[name]_[hash]',
// manifest.json 文件的绝对路径(输出文件)
path:resolve(__dirname,'dll/manifest.json')
})