webpack初体验

220 阅读4分钟

开发环境

处理css


...

module:{

rules:[

{

test: /\.css$/,

// style-loader生产style标签,使得html可以引入css。

// css-loader,将css翻译成js可以认识的文件

// use是一个数组,调用顺序也是类似栈的调用顺序,先进后出,也就是先右后左,或先下后上。

use:['style-loader','css-loader']

}

]

}

...

处理less


...

module:{

rules:[

{

test: /\.less$/,

// style-loader生产style标签,使得html可以引入css。

// css-loader,将css翻译成js可以认识的文件

// use是一个数组,调用顺序也是类似栈的调用顺序,先进后出,也就是先右后左,或先下后上。

// 需要同时下载less-loader和less2个包

use:['style-loader','css-loader','less-loader']

}

]

}

...

处理html


...

module:{

plugins:[

new HtmlWebpackPlugin(

// 改插件会根据public下的index.hmtl为模板生产index.html输出到dist目录

template:'./public/index.html'

)

]

}

...

处理图片


...

module:{

rules:[

// 默认处理不了html内的图片

{

test: /\.(jpg|png|gif)$/

// 需要下载url-loader和file-loader

use:'url-loader',

options:{

// 图片小于8kb时候,就会被base64处理

// 优点:减少请求数量(减轻服务器压力)

// 缺点:图片体积会变大

limit:8*1024

// 因为url-loader默认使用es6模块化解析,而html-loader引入图片是commonjs

// 所以要关闭url-loader的es6模块化,使用commonjs解析

esModule:false,

// [hash:10]取图片hash的前10位

// [ext]取源文件的拓展名

name:'[hash:10].[ext]',

// 设置输出文件目录

outputPath:'imgs'

}

},

{

test:/\.html$/,

// 处理html文件的img图片(负责引入img,从而能被url-loader处理)

use:'html-loader'

}

]

}

...

处理其他类型文件

例如iconfont的字体文件


...

module:{

rules:[

{

// 排除css/js/html资源

exclude:/\.(css|js|html)$/,

loader:'file-loader',

options:{

name:'[hash:10].[ext]',

outputPath:'media'

}

},

]

}

...

devServer

用来自动化(自动编译,自动打开浏览器,自动刷新浏览器)


...

// 特点:只会在内存中编译打包,不会输出到磁盘

// 启动命令npx webpack-dev-server

// 需要安装webpack-dev-server包

devServer:{

contentBase:resolve(__dirname,'build'),

//启动gzip压缩

compress:true,

// 端口号

port:3000// 自动打开浏览器

open:true

}

...

生产环境

提取css为单独文件

安装mini-css-extract-plugin


...

module:{

rules:[

{

test:/\.css$/,

use:[

//'style-loader',

// 使用这个loader取代style-loader

// 用来提取js中css为单独文件

MiniCssExtractPlugin.loader

'css-loader'

]

}

]

},

plugins:[

new MiniCssExtractPlugin({

// 指定输出文件位置

filename:'css/build.css'

})

]

...

css兼容性处理

需要安装 postcss postcss-loader postcss-preset-env

postcss-preset-env 帮助postcss找到package.json中browserslist里面的配置,通过配置加载指定的css兼容性样式


// webpack.config.js

...

process.env.NODE_ENV='development'// 为了使用browserslist 的开发环境配置

module:{

rules:[

{

test:/\.css$/,

use:[

//'style-loader',

// 使用这个loader取代style-loader

// 用来提取js中css为单独文件

MiniCssExtractPlugin.loader

'css-loader',

// 需要配置loader 需要这样写

{

loader:'postcss-loader',

options:{

ident:'postcss',

plugins:()=>[

require('postcss-preset-env')

]

}

}

]

}

]

},

plugins:[

new MiniCssExtractPlugin({

// 指定输出文件位置

filename:'css/build.css'

})

]

...


// package.json

...

"browserslist":{

"development":[

"last 1 chrome version",

],

"production":[

">0.2%",

"not dead",

"not op_mini all"

]

}

...

压缩css

optimize-css-assets-webpack-plugin


// webpack.config.js

...

process.env.NODE_ENV='development'// 为了使用browserslist 的开发环境配置

module:{

rules:[

{

test:/\.css$/,

use:[

//'style-loader',

// 使用这个loader取代style-loader

// 用来提取js中css为单独文件

MiniCssExtractPlugin.loader

'css-loader',

// 需要配置loader 需要这样写

{

loader:'postcss-loader',

options:{

ident:'postcss',

plugins:()=>[

require('postcss-preset-env')

]

}

}

]

}

]

},

plugins:[

new MiniCssExtractPlugin({

// 指定输出文件位置

filename:'css/build.css'

}),

new OptimizeCssAssetsWebpackPlugin()

]

...

语法检查

eslint-loader eslint


// webpack.config.js

...

process.env.NODE_ENV='development'// 为了使用browserslist 的开发环境配置

module:{

rules:[

{

//只检查自己的源代码,第三方库不用检查

test:/.js$/,

exclude:/node_modules/,

loader:'eslint-loader',

options:{

//自动化修复eslint错误

fix:true

}

}

]

},

plugins:[

new MiniCssExtractPlugin({

// 指定输出文件位置

filename:'css/build.css'

}),

new OptimizeCssAssetsWebpackPlugin()

]

...

设置规则

package.json中的eslintConfig中设置

推荐airbnb设置

--> eslint-config-airbnb-base eslint eslint-plugin-import


// package.json

...

"eslintConfig":{

"extends":"airbnb-base"

}

...

js兼容性处理

安装 babel-loader @babel/core

基本的js兼容性 @babel/preset-env

全部的js兼容性 @babel/polyfill 缺点 完全兼容,性能差。 4kb-->400kb😂

按需加载 corejs


// webpack.config.js

...

module:{

rules:[

{

test:/\.js$/,

exclude:/node_modules/,

loader:'babel-loader',

options:{

presets:[

'@babel/preset-env',

{

// 按需加载

useBuiltIns:'usage',

// 指定core-js版本

corejs:{

version:3

},

// 指定兼容性做到哪个版本

target:{

chrome:'60',

ie:'9'

}

}

]

}

}

]

}

js压缩


// webpack.config.js

...

// 生产环境自动压缩js

// 自带uglifyjsPlugin

mode:'production'

...

html压缩


// webpack.config.js

...

plugins:[

new HtmlWebPackPlugin({

template:'./src/index.html',

minify:{

// 移除空格

collapseWhitespace:true,

// 移除注释

removeComments:true

}

})

]

...

loader优先执行

eslint 和babel都是对js进行处理,先执行babel再执行eslint,可能会有问题:babel转译js后,eslint进行语法检查,可能就会报错。

所以eslint要先于babel执行,方法如下:


rules:[

{

test:'/\js$/',

...

enforce:'pre'

}

]

只要在loader中,添加enforce为‘pre’即可。

详见 Rule.enforce

webpack优化配置

HMR

作用:一个模块发生变化。只会重新打包这个模块,提升构建速度

开启:devServer中 hot设置为true

样式文件:可以使用hmr功能:因为style-loader内部实现了Hmr功能。这也是为什么开发环境使用style-loader而线上环境为了性能使用MiniCssExtractPlugin的原因

js文件:默认没有hmr功能

html文件:默认没有hmr功能,同时导致问题:html文件不能热更新了(不需要hmr功能)

解决:修改entry入口,将html文件引入 entry:['./src/js/index.js','./src/index.html']

OneOf

默认情况下 ,文件回经历所有的loader,

所以优化点就是,被其中一个处理过后,就不会继续匹配剩下的loader


rules:[

oneOf:[

...loaders

]

]

source map

source-map :[inline-|hidden=|eval-][nosources-][cheap-[module-]]source-map


...

devtool:'source-map'

...

79a163fd7e61e7e8829049b2878dfe34.png

开发环境下我会选择 eval-cheap-module-source-map。

缓存

babel缓存


...

{

test:/\.js$/,

exclude:/node_modules/,

loader:'babel-loader',

options:{

...

// 开启缓存

cacheDirectory:true

}

}

文件缓存

文件服务器(例如nginx)添加一个缓存header

问题:重新部署的文件,没法立即被展示出来,用户还是用的是缓存文件

hash

输出文件加一个hash值

output js/build.[hash:10].js

问题:因为js和css同时使用一个hash值,如果重新打包,会导致所有缓存失效

chunkhash

输出文件加一个chunkhash值

output js/build.[chunkhash:10].js

如果打包来自同一个chunk,那么hash值一样

问题:js和csshash还是一样

因为css是js引入的,所以是同一个chunk

contenthash:

根据文件内容生产hash,不同文件hash不一样

output js/build.[contenthash:10].js

最优方法✅

多进程打包

安装thread-loader

一般给babel-loader用

在babel-loader前加一个 thread-loader

弊端:

进程启动大概600ms,进程通信也有开销

只有工作耗时时间比较长,才需要多进程打包


// webpack.config.js

...

module:{

rules:[

{

loader:'thread-loader',

options:{

workers:2 //进程2个

}

},

{

test:/\.js$/,

exclude:/node_modules/,

loader:'babel-loader',

options:{

presets:[

'@babel/preset-env',

{

// 按需加载

useBuiltIns:'usage',

// 指定core-js版本

corejs:{

version:3

},

// 指定兼容性做到哪个版本

target:{

chrome:'60',

ie:'9'

}

}

]

}

}

]

}

externals

有些文件不需要被打包,需要通过cdn形式加载,需要被webpack忽略


// webpack.config.js

externals:{

//忽略库名:忽略npm包名

jquery:'jQuery'

}

code split

将node_module的文件和自己代码 分开打包


//webpack.config.js

optimization:{

splitChunks:{

chunks:'all'

}

}

自己代码分开打包

import动态导入语法:能将摸狗文件大度打包


document.getElementById('btn').onclick = function(){

import(/*webpackChunkName:'test'*/,'./test').then(({num})=>{

console.log(num(4,5))

})

}

懒加载 预加载

普通模式:


import {num} from './test

document.getElementById('btn').onclick = function(){

console.log(num(4,5))

}

懒加载:


document.getElementById('btn').onclick = function(){

import(/*webpackChunkName:'test'*/,'./test').then(({num})=>{

console.log(num(4,5))

})

}

预加载:


document.getElementById('btn').onclick = function(){

import(/*webpackChunkName:'test',webpackPrefetch:true */,'./test').then(({num})=>{

console.log(num(4,5))

})

}

区别:

懒加载:当文件需要时候才加载

预加载:在使用之前,提前加载(有兼容性问题,ie和移动端不行)

正常加载可以认为是并行加载(同一时间加载多个文件)

预加载:等其他资源加载完毕了,浏览器空闲了,再偷偷加载

tree-shaking

去除无用代码

前提:1、必须使用es6模块化

2、开启production环境

dll

对某些库(第三方库,react、vue、jquery)进行单独打包

运行 指令 weboack --config webpack.dll.js


// webpack.dll.js

entry:{

jquery:['jquery']

},

output:{

filename:'[name].js',

path:resolve(__dirname,'dll'),

library:'[name]_[hash]'

},

plugins:[

new webpack.DllPlugin({

name:'[name]_[hash]',

path:resolve(__dirname,'dll/manifest.json')

})

]

使用dll

安装 add-asset-html-webpack-webpack-plugin


// webpack.config.js

...

plugins:[

new webpack.DllReferencePlugin({

manifest:resolve(__dirname,'dll/manifest.json')

}),

// 告诉webpack不用打包的文件 位置在哪

new AddAssetHtmlWebpackPlugin({

filepath:resolve(__dirname,'dll/jquery.js')

})

]