开言
webpack作为现代前端开发最火的模块打包工具,已经成为了前端工程师必备的技能之一。
是:
前端资源构建工具;
静态模块打包器;
webpack从入口文件开始,根据模块的依赖关系进行分析,然后生成加工后的静态资源;
将高级语法转换成兼容性高的通用语法;
引入chunk块概念–打包–>bundles;
基于nodejs平台的工具,遵循commonjs模块化规范
本章重点讲解:
- webpack配置参数,
- 开发环境配置,
- 生产环境配置,
- 企业级的优化环境配置
- 等等
预备技能
- 基本Nodejs知识和Npm指令;
- 熟悉ES6语法
环境参数
- Nodejs10版本以上;
- webpack 4.26版本以上
知识点
- loader
- plugin
- HMR
- devtool
- resolve
- optimization
- code split
- caching
- lazy loading
- shimming
- library
- dll
- mode
- eslint
- babel
- pwa
- webpack4 /webpack5
webpack的五个核心概念
- entry:webpack从该入口开始分析构建内部依赖图,并打包。
- output:打包后的bundles输出到哪里,以及文件名。
- loaders:webpack本身只认识JS,需要借助loaders将来处理各种非JS资源
- plugins: 打包优化,压缩,开发辅助,服务器启动等等
- mode:不同环境的预设。默认是‘production’。会将值赋值给一个全局变量:process.env.NODE_ENV。webpack根据这个值,启用不同的plugin和loader等等
安装
npm i -D webpack webpack-cli
webpack-cli是做什么的?
如果你使用 webpack v4+ 版本,并且想要在命令行中调用 webpack,你还需要安装 CLI。
通过查看webpack脚本,得出webpack命令,本质就是调用了webpack-cli来实现的。webpack4+将这个功能单独拆解出来了。
const runCli = cli => {
const path = require("path");
const pkgPath = require.resolve(`${
cli.package}/package.json`);
// eslint-disable-next-line node/no-missing-require
const pkg = require(pkgPath);
// eslint-disable-next-line node/no-missing-require
require(path.resolve(path.dirname(pkgPath), pkg.bin[cli.binName]));
};
/** @type {CliOption} */
const cli = {
name: "webpack-cli",
package: "webpack-cli",
binName: "webpack-cli",
installed: isInstalled("webpack-cli"),
url: "https://github.com/webpack/webpack-cli"
};
if (!cli.installed) {
const path = require("path");
const fs = require("graceful-fs");
const readLine = require("readline");
const notify =
"CLI for webpack must be installed.\n" + ` ${
cli.name} (${
cli.url})\n`;
console.error(notify);
// ...
} else {
runCli(cli);
}
执行
在cli中执行入口文件。
# 因为没有全局安装webpack,所以需要制定本地webpack
./ node_modules/.bin/webpack ./src/index.js
# 如果global安装了
webpack ./src/index.js
通过npm脚本执行-package.json:
scripts: {
// npm 会主动去查找环境中的webpack
"webpack": "webpack ./src/index.js"
}
上面全部使用webpack的默认配置。
自定义配置
在目录下新增:webapck.config.js,webpack默认会在项目根目录下查找这个文件,当作自定义配置项。
module.exports = {
mode: 'development'
}
如果放在其他的地址,需要在脚本中显式指定。如webpack/dev.js
scripts: {
"webpack": "webpack --config ./webpack/dev.js ./src/index.js"
}
其他的配置项,可以放到配置文件,也可以当作脚本参数传入。【推荐:复杂的放到配置文件】
测试打包后资源是否可用
原始代码:
// demo1.js
const greeting = 'Hello World!';
console.log(greeting);
// index.js
import './demo1'
执行脚本webpack后:
npm run webpack
默认情况下,输出文件为:dist/main.js
node dist/main.js
# 成功输出
# Hello World!
结论:打包js成功了!
webpack默认支持js/json文件,可以省略文件后缀。
如果想要打包css文件呢?
// style1.less
body {
color: red}
// index.js
import './style'
报错如下:
[no extension] src/style1 doesn't exist
[.js] src/style1.js doesn't exist
[.json] src/style1.json doesn't exist
[.wasm] src/style1.wasm doesn't exist
[as directory] src/style1 doesn't exist
用所有默认支持的格式去匹配,都找不到该文件。
然后,指明文件后缀:
import './style1/css'
报错如下:
Module parse failed: Unexpected token.
You may need an appropriate loader to handle this file type...
结论:需要安装loader
打包其他资源
css资源
需要借助loader,配置webpack.config.js
module: {
rules: [
{
test: /\.css$/,
use: [
'style-loader',
'css-loader'
]
}
]
}
当webpack匹配到css结尾的模块时:
- 先使用css-loader将css文件处理成commonjs模块,整合到js中;
- 再用style-loader,创建一个style标签,将上面处理后的css代码插入标签中,当有html文件使用到这个打包文件时,将style标签,插入到head尾部
html资源
使用html-webpack-plugin插件:
npm i -D html-webpack-plugin
使用:
const htmlWebpackPlugin = require('html-webpack-plugin')
// plugin: 下载 引入 使用
plugins: [
// html-webpack-plugin
new htmlWebpackPlugin()
]
执行:npm run webpack
结果:在output指定目录下,生成了一个index.html文件。并且将bundle文件–main.js插入到了script标签中,默认将script放在head中。
图片资源
如果在css中引入了图片资源:
background-image: url(./images/image.png);
那么需要用到url-loader(用来处理css文件中url相关图片文件的)和file-loader【看情况安装】:
{
test: /\.png$/,
loader: 'url-loader',
options: {
// 默认情况limit没有限制;下面限制为:当图片小于1kb,才会用url-loader进行处理,将图片转化为base64编码字符串【优点:可以减少http请求;缺点:加大js文件体积】
// 当图片大雨1kb时,url-loader就需要依赖file-loader包,将图片直接copy到dist目录下,默认资源名字会变成hash名字
limit: 1 * 1024
}
}
如果在html中引入image标签,上面的处理就会出问题了,因为,例如file-loader会修改图片的名字,此时,html会找不到图片资源,我们需要用到html-loader。
{
test: /\.html$/,
// 处理html中的img标签资源,负责将img资源引入,然后才能被url-loader处理。
// html-loader引入图片是es6规范的,解析时会报错
// 解决办法:将html-loader的esModule设置为false
loader: 'html-loader',
options: {
esModule: false
}
}
其他资源(字体等)
不需要做压缩等处理,只需要,复制并输出。
比如字体文件:iconfont.ttf iconfont.svg
方案:用排除法匹配其他资源,然后用file-loader处理。匹配到的资源,将会用hash重命名后,输出到dist目录
rules: [
{
exclude: /\.(html|css|js)/,
loader: 'file-loader'
}
]
devServer
开发服务器:用来自动构建、打开浏览器,并自动刷新等功能。大大提升了开发效率。
// 开发服务器只在内存中编译打包,没有任何输出
// 用npx webpack serve
devServer: {
contentBase: './dist',
// 启动gzip
compress: true,
port: 3000
}
需要安装webpack-dev-server。
npm i -D webpack-dev-server
当你改动源代码的时候,webpack就会自动打包,并更新页面
不同环境的配置(开发/生产/测试)
一般情况下,webpack已经针对不同环境进行了默认的配置配置,只需要用mode去开启就行了。
所以脚本上,我们需要传入不同的参数,或者是建立不同mode的相关config文件。
// 如果只有一个webpack.config.js文件,那么我们可以通过给cli传入参数
"dist": "webpack --env=dist"
"dev": "webpack --env=dev"
// 使用
const {
env} = require('minimist')(process.argv.slice(2), {
string: ['env'],
});
// 然后根据env给webpack的配置项传入不同的值。
// 或者可以将配置文件指定为:dev.config.js/dist.config.js等,然后在脚本中使用不同的文件
"dist": "webpack --config=webpack/dist.config.js"
"dev": "webpack --config=webpack/dev.config.js"
针对不同环境,我们可以借助一些分析工具,如:
- webpack-bundle-analyzer 开发环境分析工具
- webpack-dev-server 开发服务器
- webpack-manifest-plugin 生产环境,可能还需要借助这个工具
- optimize-css-assets-webpack-plugin 生产环境css文件压缩
- mini-css-extract-plugin 拆分css
- 等等
此外我们还可以考虑:
- css/js 兼容不同浏览器的处理工具,如css前缀添加工具【postcss-loader,它会去package.json中查找browserList配置,然后生成不同的兼容性前缀,需要手动设置process.env_NODE_ENV】等
- 将css/js生成不同的文件【那就不能用style-loader来创建style标签,而要用mini-css-extract-plugin.loader提取出css,然后生成main.css,用link引入】;或者将文件css+js整合成一个文件【css-loader–将css整合到js文件中】
- splitChunk: 将依赖的文件,如jquery,antd等大文件,排除不打包到main.js
- 或者用externals属性,去排除一些包,然后用script的方式手动引入CDN中的代码
JS的语法检查:
常用的语法检查工具是eslint。我们可将eslint嵌到webpack中,用eslint-loader【以来esling】
rules: [
{
// 注意:只检查源代码,需要排除node_modules中的代码
test: /\.js$/,
exclude: /node_modules/,
loader: 'eslint-loader',
// 手动配置不同的规则,或者使用第三方的规则,如:airbnb
// npm i -D eslint-config-airbnb-base eslint-config-import
// 将airbnb配置到package.json中:eslintConfig: {extends: 'airbnb-base'}
options: {
// 自动修复
fix: false
}
}
]
然后执行webpack,如果js代码中有些语法不符合airbnb的规范,那么webpack操作就会失败,并输出:错误
如果将fix设置为true,那么错误会自动处理,webpack大部分情况不会被打断,除非eslint处理不了。
JS兼容性处理,如兼容IE10:
正常情况下,源代码如果使用es6及以上语法,webpack并不会进行转换,此时若将bundle直接放到低版本浏览器(IE)中运行,脚本会报错,如:不认识const等等。
那么我们需要用到babel:
npm i -D babel-loader @babel/core @babel/preset-env @babel/polyfill core-js
rules: [
{
test: /\.js$/,
excludes: /node_modules/,
loader: 'babel-loader',
options: {
// 预设:指示babel做什么样的兼容性处理,preset-env就是基本处理,将语法转化为es5及以下兼容性强的语法【基本语法】
// presets: '@babel/preset-env'
// 如果用到promise等语法,那上面的preset-env并不能处理,就需要其他的包:@babel/polyfill 在源代码入口引入就可以了。
// 问题:我只需要做一部分兼容性处理,但引入@babel/polyfill体积太大了。。。。那么就需要做按需加载---core-js。然后不需要引入:@babel/polyfill
presets: [
'@babel/preset-env',
{
// 按需加载
useBuiltIns: 'usage',
// 指定版本
corejs: {
version: 3},
// 指定兼容到哪个版本的浏览器
targets: {
chrome: '60', firefox: '60', ie: '9'}
}
]
}
}
]
所以最终方案为:@babel/preset-env + core-js
JS压缩
webpack开启:
mode: 'prodcution'
HTML压缩
配置插件:
plugins: [
// html-webpack-plugin
new htmlWebpackPlugin({
template: './index.html',
title: 'Demo',
inject: 'body',
minify: {
// 如:移除空格等等
collapseWhitespace: true,
}
})
]
优化
需要考虑哪些点呢?需要考虑如下两类:
1. 开发环境优化
- 优化打包构建速度
- devServer也是一种优化
- HMR(只打包修改了的模块–模块热更新) ,在devServer中设置:hot: true来开启。
- css文件可以使用HMR功能;
- JS和html不能使用,并且导致html文件不能热更新了,
- 那么需要修改entry,将html文件引入。
- 如果修改html,那么项目就会全部刷新。。
- 那么html只有一个:需要进行热更新吗???? 不需要
- JS需要HMR吗?需要,那就在入口js文件中加入热更新代码
- 只能处理非入口js文件,入口文件修改,肯定会全部打包
- 开启缓存
- babel-loader设置缓存 cacheDirectory: true 【第二次构建时才会读取缓存】
- 优化代码调试功能借用source-map技术
- sourcemap是:一种源代码到构建后代码的映射技术 ,能提供准确的错误信息,及错误在源代码发生的位置
- inline-source-map
- 只生成一个内联sourcemap文件【速度更快】
- hidden-source-map
- 外部【速度相对较慢】
- eval-source-map
- 为每个文件都生成一个内联的sourcemap文件
- 直接使用webpack的devtools
- 速度:eval>inline>cheap>…
- 调试最友好:source-map>cheap-module-source-map
- 中和速度和友好度,推荐选择:eval-source-map
// html不需要HMR
entry: ['./src/index.js', './index.html']
devtool: 'inline-source-map'
// index.js
if(module.hot) {
// 如果开启了HMR,则监听下面模块,如果变了,就只打包下面模块
module.hot.accept('./...js', function(){
})
}
2. 生产环境优化
- 优化打包构建速度
- 开启缓存
- babel缓存 cacheDirectory: true 【第二次构建时才会读取缓存】
- 多进程打包:一般是给babel-loader用,使用tread-loader
- 开启缓存
- 优化代码运行的性能【进程开启600ms、进程通信都需要开销只有工作消耗时间长,才用】
- 文件资源缓存 启动一个server。或者设置 static静态资源 的maxAge: 10000强制缓存时间为10000ms。同时配合hash值来替换进行资源比较【记得区分css和js的hash生成规则】
- tree-shaking:这个概念的意思就是:删除未使用到的代码,从而达到减少bundle体积的目的。【必须:es模块化+production】【自动开启】
- 如何package.json中设置了sideEffect: false ,表示源代码中没有副作用,那么所有相关代码都会被shaking掉,从而,引发问题,可能会把import ‘xxx’这样方法引入的文件删掉。需要将配置改为:sideEffects: [x.css] ,面对不同版本的webpack,tree-shaking的功能可能会有差异,如果需要开启该功能,建议将sideEffects指明
- code-split:优点:代码并行加载;减小单个文件的大小;是按需加载的基础。实现方式:
- 多入口。输出多个bundles。【一般用于多页面应用的场景】
- 配置optimization.splitChunk
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: [
// 开启多进程打包
'tread-loader',
{
loader: 'babel-loader', options: {
presets: []}}
]
}
]
optmization : {
splitChunk: {
// all的意思:将node_modules中代码打包成一个单独文件:vendor.js
// 多入口文件,会共用vendor.js
// 我们还可以将node_modules中包,拆分成单独的包,例如xlsx太大,可以单独引入
chunks: 'all'
}
}
- lazy-loading:使用es6的import()方法实现。需要设置webpackChunkName将其打包成单独的文件才能支持懒加载。
- prefetch: 设置 webpackPrefetch: true实现延迟加载的包预加载。当再使用代码import()时,读取的就是预加载进来的缓存【code: 200】【prefetch:等其他资源加载结束后,浏览器空闲了才加载,不会阻塞正常资源的加载;移动端、IE兼容性差—不推荐】
- PWA:service work + cache,兼容性差;
- 检测一个站点有么有使用pwa技术:f12–network-offline,然后刷新页面。
- offline时,资源请求成功的状态为:200 (from service worker)
- 怎么实现?使用workbox-webpack-plugin
plugins: [
new workboxWebpackPlugin.GenerateSW({
// serviceWorker快速启动
// 删除旧的serviceWorker
// 生成一个serviceWorker配置文件
clientClaim: true,
skipWaiting: true
})
]
// 入口文件 index.js中注册serviceWorker
// 1. eslint默认不认识navigator/window全局变量,需要改package.json中eslintConfig.env: {browser: true},表示支持浏览器的变量
// 2. ==SW的代码必须运行在服务器上==,而不能直接通过file://协议访问。
if('serviceWorker' in navigator) {
window.addEventListener('load', ()=> {
// service-worker.js这个文件就是上面webpack生成的配置文件
navigator.serviceworker.register('/service-worker.js')
.then((success) => {
console.log(success)
})
.catch((error) => {
console.log(error)
})
})
}
- externals:排除掉一些包不进行打包,而用cdn的方式等引入
externals: {
// 库名: npm包名
jquery: 'jQuery'
}
- dll技术:动态链接库。指定某些库不进行打包。并将某个包打包成多个文件,比splitChunk: all更优化。webpack.DllPlugin()【没研究】
- 生产环境源代码要不要隐藏呢??
- 肯定不能用 内联 的sourcemap,会让代码体积过大
转载:https://blog.csdn.net/u010682774/article/details/117221097