webpack5整理学习,升级太快了,呜呜呜呜,不学习都跟不上前端步伐了~概念就不多讲了,直接撸
接下来几天我会陆续抽空更新文档,一起督促自己学习吧
git地址:
1.环境安装
不建议直接在全局安装webpack
相关
npm init
npm i webpack webpack-cli -D
那么环境就有了
接下来根目录下创建 main.js 文件,src文件夹放置需要的各种文件(根据自己场景分,我这里就是简单的举例),static文件放置图片和其他资源,index.html 文件,设置打包后文件夹名为build
我们在目录最外层新建 webpack.config.js
文件,基本配置如下
const { resolve } = require('path')
module.exports = {
entry: './main.js', //项目入口文件
//输出
output: {
filename: 'built.js', //打包后js的名称
path: resolve(__dirname, 'build'), //打包输出到当前路径的build文件夹中
publicPath: '/' //注意此处,是服务器下资源引用的根目录
},
//loader配置规则
module: {
rules:[]
},
plugins: [], //插件配置
mode: 'development', //开发模式(开发模式和生产模式)
}
package.json文件中配置script,增加build命令
"scripts": {
"build": "webpack --config webpack.config.js",
}
以上实战中的基本的环境就有了,可以尝试在main.js中写点东西运行 npm run build ,看看build文件夹变化吧~,如果不编写script,直接执行npx webpack,前提是有npx哈,该命令回去node_modules中找
2.处理html文件
npm i html-webpack-plugin -D
插件安装好之后,我们需要配置webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin')
plugins:[
new HtmlWebpackPlugin({
template:'./index.html' //以该目录下的html为模版
})
]
index.html内容自己写~执行npm run build
可以查看build下是否生成一个html文件和main.js
3.打包样式资源
1.处理css
因为webpack不能处理js以外的资源,所以css资源的处理需要 “翻译官” css-loader和style-loader
npm i style-loader css-loader -D
webpack.config.js
中的rules
中配置css, 注意书写顺序从下到上
css-loader->style-loader
module:{
rules:[
{
test: /\.css$/,
use:[
'style-loader', //将js中的css通过style方式注入到html中
'css-loader' // 加载处理css文件
]
}
]
},
main.js
中引入:具体内容根据业务自己加
import './src/index.css'
html中:
运行结果如下:
运行build中的index.html,如果失败,观察一下html中引入的打包js文件路径是否正确,根据情况修改output中的publicPath属性
2.处理less等
npm i less less-loader -D
webpack.config.js
中的rules
中配置less
module:{
rules:[
{
test: /\.less$/,
use:[
'style-loader', //将js中的css通过style方式注入到html中
'css-loader', // 加载处理css文件
'less-loader',
]
}
]
}
同css其他一样,自己敲敲看吧~
3.提取单独的css文件
当我们处理完css或者less的时候,不希望样式通过style的方式引入到html文件中。这个时候需要用到插件mini-css-extract-plugin
npm i mini-css-extract-plugin -D
webpack.config.js
中修改配置
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
module:{
rules:[
{
test: /\.css$/,
use:[
MiniCssExtractPlugin.loader, //替换style-loader
'css-loader' // 加载处理css文件
]
}
]
},
plugins:[
new MiniCssExtractPlugin({
filename:'index.css' //重命名输出的css文件,也可不写默认
})
]
处理less相同,替换style-loader
即可,打包之后观察build文件中的各种变化吧~跟着敲一遍会有收获哒~
4.处理css兼容性
我这里使用的是postcss
npm i postcss-loader postcss-preset-env -D
我当前的版本分别是5.0.0,6.7.0
因为postcss是根据browserslist
配置,设置当前的css兼容性,所以在package.json
文件中增加browserslist
配置
"browserslist": {
"development": [
"last 1 chrome version" //可自己根据开发环境的测试自己增加别的
],
"production": [
">0.2%",
"not dead",
"not op_mini all"
]
}
默认走production,跟配置中的mode没有联系,开发阶段可以增加配置 process
修改配置文件:
const postcssPresetEnv = require('postcss-preset-env')
process.env.NODE_ENV = 'development'
rules: [
{
test: '/\.css$/',
use:[
MiniCssExtractPlugin.loader,
'css-loader',
{
loader: 'postcss-loader',
options:{
postcssOptions:{
plugins:[
postcssPresetEnv()
]
}
}
}
]
}
]
在任意css文件修改有兼容相关的样式:
p{
display: 'flex',
backface-visibility: hidden;
}
执行打包,观察打包后的文件试试吧~
4.处理图片资源
npm i url-loader file-loader -D
配置图片处理rule:
{
test: '/\.(jpg|png|jpeg|gif)$/',
loader: 'url-loader',
options: {
limit: 15*1024,//图片小于15kb,就会被base64处理,可以减少请求,但是体积会变大(请求速度慢),可以8~12kb
outputPath: 'imgs'
}
}
5.处理其他资源(字体等)
{
exclude: /\.(css|less|jpg|png|jpeg|gif|html|js)$/, //打包其他资源
loader: 'file-loader',
options:{
outputPath: 'resource'
}
}
6.配置devServer
webpack-dev-server
为你提供了一个简单的 web 服务器,并且能够实时重新加载。让我们在本地的可以实时更改内容并查看
npm i webpack-dev-server -D
config文件中增加devServer配置
devServer:{
contentBase: './build',
cpmpress: true, //开启gzip压缩
port: 3000,
open: true, //自动打开浏览器
在package.json的script中增加
"dev": "webpack server"
保存执行 npm run dev
之后浏览器即可查看,也可以npx webpack serve
如果运行报错根据错误提示查找问题,这里面坑还是比较多,在保证代码正确的前提下,可能主要的就是版本之间的兼容性
,具体的可百度自行查找。
这里面有个问题我就查询了一天才解决:
更新内容能够自动编译但是浏览器不能自动刷新
,需要手动刷新才更改内容,主要的影响因素就是browserslist这个配置:解决的办法就是在config配置文件增加target
属性
module.exports = {
...,
target: 'web'
}
妈耶,万万没想到是这个地方的问题,啊啊啊啊~
7.压缩文件
在生产环境下,压缩主要针对的文件主要是js、css、html
在我的webpack相关版本的情况下js和html打包是可以自动压缩
的,只要设置以下属性:
mode: "production"
css的压缩需要依赖插件:
npm i optimize-css-assets-webpack-plugin -D
//压缩cssconst OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin')
plugins: [
new OptimizeCssAssetsPlugin()
]
8.js语法检查和兼容性处理
9.性能优化
性能优化主要是从两个方面:development
和production
development:
-
优化打包构建速度
-
优化代码调试
production:
-
优化打包构建速度
-
优化代码运行的性能
10.开发环境-模块热替换(HMR)
webpack 提供的最有用的功能之一。它允许在运行时更新各种模块,而无需进行完全刷新,它应当只在开发环境使用。
启用HMR
,针对css只刷新更改的部分,不需要所以全部更新一遍
devServer:{
+ hot: true
}
此处发现更改样式文件的时候是热替换的,但是html文件更改却没有刷新了,需要在增加入口html,并且类似单页面文件只有一个入口是需要更新的,不存在HMR
+ entry: ['./main.js','./index.html'],
接下来处理非入口js文件的热替换:
-
src下新建print.js文件
function print(){ console.log('hello print') }
export default print
2.main.js中引入
+ import print form './src/print.js'
+ if(module.hot){
+ module.hot.accept('./src/print.js' , () => {
//方法会监听js文件的变化,执行回调函数+ print()
+ })
学习过程中,可以分别在控制台查看log相关的变化哒~
11.source-map
config配置文件中:devtool
控制是否生成,以及如何生成 source map。
文档查看根据构建速度以及调试的简易度综合总结:
- 开发环境:devtool: 'eval-source-map'
- 生产环境: devtool: 'source-map' ,如需隐藏源代码等,可根据业务查看文档修改值(
hidden-source-map
/nosources-source-map
)
12.生产环境-缓存配置
缓存的目的:文件发生变更的时候,只变更修改的文件,不变的资源文件使用缓存
缓存主要是两点配置:
- babel缓存-编译缓存
- 文件资源缓存
为了验证我们的缓存配置的效果,我们需要启动一个node服务
这里我在根目录下创建了一个server.js
const express = require('express')
const app = express()
app.use(express.static('build',{maxAge: 1000*3600}))
app.listen(9000)
node server.js
启动服务 ->预览http://localhost:9000/
1.babel缓存
babel-loader增加配置:"cacheDirectory": true
{
test:/\.js$/, //js 的兼容性处理
exclude: /node_modules/,
loader: 'babel-loader',
options:{
"cacheDirectory": true, //第二次构建会读取缓存
}
}
打包之后,在NetWork的中查看请求的资源,第二次请求查看资源可以发现是缓存中读取的了
尝试修改一下代码重新打包,发现什么了吗?
是的,浏览器没有看到相关的变更。这是因为强制缓存的有效期内,浏览器只会从本地获取缓存,并不会获取当前你重新打包后的资源文件
为了解决这个问题,就尝试从资源文件名称上面做一些改变,接着往下看
2.资源缓存
为了解决babel的诟病,所以打包后的资源增加hash值
- 1.使用hash
因为配置的东西很多了,我贴一段当先完整的config配置
const { resolve } = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
//提取css为单独文件
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
//处理css的兼容性
const postcssPresetEnv = require('postcss-preset-env')
process.env.NODE_ENV = "development"
//压缩css
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin')
module.exports = {
target: 'web',
entry: ['./main.js','./index.html'], //html文件热更新
output: {
filename: 'js/built.[hash:10].js',
path: resolve(__dirname,'build'),
publicPath: '/'
},
//loader配置
module:{
rules:[
//详细的loader配置‘
{
oneOf: [ //匹配到loader之后,就退出相当于break
{
test: /\.css$/,
use:[
// 'style-loader',
// 取代style-loader,提取js中的css为单独文件
MiniCssExtractPlugin.loader,
'css-loader',
// 兼容性处理 postcss,修改相关配置
{
loader: 'postcss-loader',
options:{
postcssOptions: {
plugins:[
postcssPresetEnv()
]
}
}
}
]
},
{
test: /\.less$/,
use:[
MiniCssExtractPlugin.loader,
'css-loader',
// 兼容性处理 postcss,修改相关配置
{
loader: 'postcss-loader',
options:{
postcssOptions: {
plugins:[
postcssPresetEnv()
]
}
}
},
'less-loader'
]
},
{
test:/\.js$/, //js 的兼容性处理
exclude: /node_modules/,
loader: 'babel-loader',
options:{
"cacheDirectory": true, //第二次构建会读取缓存
}
},
{
test: /\.(jpg|png|gif)$/,
loader: 'url-loader', //url-loader file-loader
options:{
limit: 15*1024, //图片小于15kb,就会被base64处理,可以减少请求,但是体积会变大(请求速度慢),可以8~12kb
outputPath:'imgs',
// name: '[hash:10].[ext]'
}
},
{
test: /\.html$/,
loader: 'html-loader' //处理html中的img,才能被url-loader处理
},
{
exclude: /\.(css|less|jpg|png|gif|html|js)$/, //打包其他资源
loader: 'file-loader',
options:{
// name:'[hash:10].[ext]',
outputPath: 'resource'
}
}
]
}
]
},
//插件配置
plugins:[
new HtmlWebpackPlugin({
template: './index.html'
}),
new MiniCssExtractPlugin({
//设置输出的文件路径以及名称
filename: 'css/index.[hash:10].css'
}),
new OptimizeCssAssetsPlugin() //生产环境压缩css
],
//模式
mode: 'production', //生产环境下js和html会被自动压缩了
//自动编译,自动打开浏览器,自动刷新浏览器;只会在内存中编译打包,不会有任何输出
devServer: {
contentBase: './build', //运行的根路径
compress: true , //启动gzip压缩
port: 3000,
open:true,
hot: true, //HMR 只在生产环境,js和html不能使用
},
//devtool: 'eval-source-map', // 生产source-map,暂不使用
}
*[hash:10]为hash值前10位
打包查看build我们可以看出,每一个构建打包都会有一个唯一的hash值
存在的问题:更改一个js或者css文件打包之后,因为js和css使用的同一个hash,所以的文件缓存同时失效。
- 2.尝试使用chunkhash -> 根据chunk生成hash
将所以的hash改为chunkhash打包之后,查看build文件变化
当我们修改css的时候,发现js和css还是同一个chunkhash值,发现原因是css文件是在js中的引入的,同属一个chunk
- 3.使用contenthash -> 根据文件的内容生成hash
打包发现不同文件,hash值不同了
尝试修改js或者css,可以看出只是被修改的文件缓存失效,达到效果
13.tree shaking
官方描述:通常用于描述移除 JavaScript 上下文中的未引用代码(dead-code)
作用:去除项目中没有被使用的代码,以此减小项目体积
前提条件:
- 使用es6模块
- 生产环境下
package.json 中配置
"sideEffects": false // 都可以进行tree shaking ,但是可能会干掉css等文件
解决方法:
"sideEffects": ["*.css","*.less"] //等需要排除的文件
demo
src下新建tree_shaking.js
文件
function addDemo(x,y){
return x+y
}
function unadd(x,y){
return x-y
}
export{
addDemo,
unadd
}
main.js中只引入
import { addDemo } from './src/tree_shaking'
console.log(addDemo(2,3))
在打包后的js文件中搜索unadd,发现并没有被引入
14. code split
之前的学习记录中,我们可以发现打包后的js文件只有一个
code split简单理解就是实现代码分离
新建一个test.js方便测试
import $ from 'jquery' //之前下载过该依赖了
cosnole.log($)
console.log('splitChunk 测试')
config配置文件增加配置:(将node_modules中的代码单独打包为一个chunk)
optimization: {
splitChunks: {
chunks: 'all'
}
}
- demo1
测试将test.js修改为入口文件打包发现:optimization.splitChunks将jquery依赖单独打包为一个文件了
- demo2:多入口文件打包
多入口项目,将entey设置为多入口
注:[name]:输出的文件名称为entey中名称
entry:{
index: './main.js',
print: './test.js'
},
output:{
filename: 'js/[name].[contenthash:10].js',
path: resolve(__dirname,'build'),
publicPath: '/'
}
在main.js中增加代码
import $ from 'jquery'
cosnole.log($)
打包之后发现并没有重复打包jquery依赖
所以多入口文件引入的相同依赖,会提取为一个chunk,不会重复打包
- demo3:动态导入
单入口文件想要实现某个文件单独打包,修改test.js文件为一下内容
export function myname(){
console.log('单独打包某个js')
}
main.js中增加内容:
import(/* webpackChunkName: 'test' */'./test.js').then((res)=>{
res.myname()
}).catch((res)=>{
console.log('加载失败了')
})
打包观察内容即可,/* webpackChunkName: 'test' */指定输出的chunk名
15.懒加载和预加载
实现懒加载:还是使用import动态导入模式
index下新增:
<button id="btn">懒加载demo</button>
main.js增加:
document.getElementById('btn').onclick = function(){
import(/* webpackChunkName: 'test' */'./test.js').then((res)=>{
console.log(res)
}).catch((res)=>{
console.log('加载失败了')
})
}
预加载
main.js中修改懒加载配置:
document.getElementById('btn').onclick = function(){
import(/* webpackChunkName: 'test',webpackPrefetch:true */'./test.js').then((res)=>{
console.log(res)
}).catch((res)=>{
console.log('加载失败了')
})
}
16.多进程打包
npm i thread-loader -D
config文件中修改配置:
{
test:/\.js$/, //js 的兼容性处理
exclude: /node_modules/,
use: [
//开启多进程打包,启动大概600ms,通信也有开销
//只有工作消耗较长,才需要多进程打包
'thread-loader', //开启多进程打包
{
loader: 'babel-loader',
options:{
"cacheDirectory": true, //第二次构建会读取缓存
}
}
]
},
打包中最消耗资源的就是js的处理,因此在该地方使用
thread-loader插件用的好,可以节省打包时间,用的不好增加打包开销
进程开启也是需要时间的,根据自己的业务观察是否适合开启多进程,避免滥用
17. 性能优化总结
1.开发环境
优化打包构建速度:HMR
优化代码调试:source-map
2.生产环境
优化打包构建速度:oneof、babel缓存、多进程打包、externals、dll
优化代码运行性能:contenthash缓存、tree shaking 、code split、懒加载和预加载
以上externals、dll相关可自行查找资料学习
18.拓展
1.引用路径别名
config增加配置:
resolve: {
// 设置路径别名
alias:{
'@': resolve(__dirname,'src'),
}
}
引用:
import '@/index.less'import '@/index.css'
2.CleanWebpackPlugin
打包的时候删除build中被更改文件的chunk原始文件,重新打包相关内容
npm i clean-webpack-plugin -D
配置文件中:
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
plugins:[
new CleanWebpackPlugin(),
]
5.20+的版本下
output: {
clean: true //即可清楚dist的文件
}
19.总结
粘贴一份完整的文件内容
config
const { resolve } = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
//打包先清除build文件
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
//提取css为单独文件
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
//处理css的兼容性
const postcssPresetEnv = require('postcss-preset-env')
process.env.NODE_ENV = "development"
//压缩css
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin')
module.exports = {
target: 'web',
entry: ['./main.js','./index.html'], //html文件热更新
output: {
filename: 'js/[name].[contenthash:10].js',
path: resolve(__dirname,'build'),
publicPath: '/'
},
//loader配置
module:{
rules:[
//详细的loader配置‘
{
oneOf: [ //匹配到loader之后,就退出相当于break
{
test: /\.css$/,
use:[
// 'style-loader',
// 取代style-loader,提取js中的css为单独文件
MiniCssExtractPlugin.loader,
'css-loader',
// 兼容性处理 postcss,修改相关配置
{
loader: 'postcss-loader',
options:{
postcssOptions: {
plugins:[
postcssPresetEnv()
]
}
}
}
]
},
{
test: /\.less$/,
use:[
MiniCssExtractPlugin.loader,
'css-loader',
// 兼容性处理 postcss,修改相关配置
{
loader: 'postcss-loader',
options:{
postcssOptions: {
plugins:[
postcssPresetEnv()
]
}
}
},
'less-loader'
]
},
{
test:/\.js$/, //js 的兼容性处理
exclude: /node_modules/,
use: [
//开启多进程打包,启动大概600ms,通信也有开销
//只有工作消耗较长,才需要多进程打包
// 'thread-loader',
{
loader: 'babel-loader',
options:{
"cacheDirectory": true, //第二次构建会读取缓存
}
}
]
},
{
test: /\.(jpg|png|gif)$/,
loader: 'url-loader', //url-loader file-loader
options:{
limit: 15*1024, //图片小于15kb,就会被base64处理,可以减少请求,但是体积会变大(请求速度慢),可以8~12kb
outputPath:'imgs',
name: '[ckunkhash:10].[ext]'
}
},
{
test: /\.html$/,
loader: 'html-loader' //处理html中的img,才能被url-loader处理
},
{
exclude: /\.(css|less|jpg|png|gif|html|js)$/, //打包其他资源
loader: 'file-loader',
options:{
name:'[ckunkhash:10].[ext]',
outputPath: 'resource'
}
}
]
}
]
},
//插件配置
plugins:[
new HtmlWebpackPlugin({
template: './index.html'
}),
new MiniCssExtractPlugin({
//设置输出的文件路径以及名称
filename: 'css/index.[contenthash:10].css'
}),
new CleanWebpackPlugin(),
new OptimizeCssAssetsPlugin()
],
//模式
mode: 'production', //生产环境下js和html会被自动压缩了
//自动编译,自动打开浏览器,自动刷新浏览器;只会在内存中编译打包,不会有任何输出;启动指令:npx webpack-dev-server
devServer: {
contentBase: './build', //运行的根路径
compress: true , //启动gzip压缩
port: 3000,
open:true,
hot: true, //HMR 只在生产环境,js和html不能使用
},
//devtool: 'eval-source-map', // 生产source-map
//optimization将node_modules中需要的代码单独打包成一个chunk
optimization: {
splitChunks: {
chunks: 'all'
}
},
resolve: {
// 设置路径别名
alias:{
'@': resolve(__dirname,'src'),
}
}
}
main.js
/**
* 入口文件
*/
import '@/index.less'
import '@/index.css'
import './static/font/iconfont.css'
import print from './src/print'
import { add } from './src/tree_shaking'
// const add1 = (x,y) => {
// return x+y
// }
// console.log(add1(1,1))
console.log(243)
console.log(add(1,1))
print()
//import动态导入,将某个文件单独打包
//结合预加载
document.getElementById('btn').onclick = function(){
import(/* webpackChunkName: 'test',webpackPrefetch:true */'./test.js').then((res)=>{
res.myname()
}).catch((res)=>{
console.log('加载失败了')
})
}
//HMR
if(module.hot){ //非入口文件的js实现模块热替换
console.log(111)
module.hot.accept('./src/print.js',() => {
//方法会监听js文件的变化,执行回调函数
print()
})
}
package.json参考:
{
"name": "webpack-text",
"version": "1.0.0",
"description": "",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack --config webpack.config.js",
"dev": "webpack-dev-server"
},
"author": "lxy",
"license": "ISC",
"devDependencies": {
"@babel/core": "^7.13.1",
"@babel/polyfill": "^7.12.1",
"@babel/preset-env": "^7.13.5",
"babel-loader": "^8.2.2",
"clean-webpack-plugin": "^3.0.0",
"core-js": "^3.9.0",
"css-loader": "^5.0.1",
"file-loader": "^6.2.0",
"html-loader": "^1.3.2",
"html-webpack-plugin": "^4.5.1",
"less": "^4.1.0",
"less-loader": "^7.3.0",
"mini-css-extract-plugin": "^1.3.5",
"optimize-css-assets-webpack-plugin": "^5.0.4",
"postcss-loader": "^5.0.0",
"postcss-preset-env": "^6.7.0",
"style-loader": "^2.0.0",
"thread-loader": "^3.0.1",
"url-loader": "^4.1.1",
"webpack": "^5.18.0",
"webpack-cli": "^3.3.12",
"webpack-dev-server": "^3.11.2"
},
"dependencies": {
"jquery": "^3.5.1"
},
"browserslist": {
"development": [
"> 1%",
"last 2 versions",
"not ie <= 8"
],
"production": [
">0.2%",
"not dead",
"not op_mini all"
]
},
"sideEffects": [
"*.css",
"*.less"
]
}