1️⃣ 安装和使用
初始化项目包
1. `**npm init**`
安装 webpack 和 webpak-cli
1. `**npm i webpack@4 webpack-cli@3 -D**`
创建 webpack 配置文件
1. webpack.config.js
module.exports = {mode: 'development', // 模式配置entry: '', // 入口配置output: { }, // 出口配置module: { // loader 配置rules: []},plugins: [ // plugins 配置],}
1️⃣ 打包环境
"scripts": {"build": "webpack --mode=production","dev": "webpack --mode=development"},或者module.exports = {mode: 'development', // 开发模式mode: 'production', // 生产模式}
1️⃣ 入口配置
module.exports = {// >>> string 形式// >>>>>> 单入口, 打包形成一个chunk, 输出一个 bundle 文件, 此时 chunk 的名称默认是 mainentry: './src/index.js',// >>> array 形式// >>>>>> 多入口, 所有入口文件最终只会形成一个 chunk, 输出出去只有一个 bundle 文件// >>>>>> 作用: 只有在 HMR 功能中让 html 热更新生效entry: ['./src/index.js', './src/one.js'],// >>> object 形式// >>>>>> 多入口: 有几个入口文件就形成几个 chunk, 同时输出出去就有几个 bundle 文件, 此时 chunk 的名称是 key// >>>>>> 作用: 只有在 HMR 功能中让 html 热更新生效entry: {main: "./src/index.js", // 键: chunk 名 值: 入口模块one: "./src/one.js"},// >>> 特殊用法entry: {main: "./src/index.js",one: ["./src/one.js", "./src/two.js"]},}
1️⃣ 出口配置
var path = require("path"); // 导出一个对象, 用以 __dirname 配置绝对路径module.exports = {output: {// ---------- 配置输出地址 ----------// >>> 配置输出文件输出的文件地址 ( 将所有资源打包输出到的文件夹 ), 必须配置一个绝对路径path: path.resolve(__dirname, 'builder'), // 将所有资源打包输出到 builder 文件里 ( 不配置默认为 dist 文件 )// ---------- 配置打包后的 js 文件名 ----------// >>> 静态规则filename: 'jsPackName.js', // 打包后的 js 文件名为 jsPackName.js// >>> 动态规则// >>>>>> [name]: 在入口为多入口时 [name] 会在输出时替换为 chunk名, [name] 想要生效如果必须以对象的形式配置// >>>>>> [hash]: 取 hash 作为名字一部分的原因是, 浏览器会有缓存, 当浏览器生成缓存后, 如果文件名不变浏览器始终会使用缓存的 js 文件, 如果使用 hash 当文件内容有变化时, 最终打包生成的 js 文件的名字也会变化, 这样在内容更新后,浏览器请求时发现文件名不同, 就会重新请求 js 文件就不会使用缓存的文件了// >>>>>>>>> [hash:5]: 取最终的 hash 值前 5 位( 缺点: 如果统一使用最终的 hash 那么只要有一个文件有所变化所有的文件名都会变化, 浏览器则需要请求所有的 js 文件 )// >>>>>>>>> [chunkhash:5]: 取各自 chunk 的 hash 前 5 位( 优点: 各自的 js 文件使用各自的 hash 值, 文件内容更新只会影响自己的文件名, 不会影响其他的文件名, 浏览器不会重新请求不需要请求的文件 )filename: '[name].[chunkhash:5].js', // 打包后的 js 文件名为 [chunk名].[总的hash名的前5位].js// ---------- 所有资源在引入时的公共路径前缀 - 一般用于生产环境 ----------// 在出口处配置会影响所有用到 publicPath 的 loader 如果 loader 用到的路径不同应当配置在自己 loader 里publicPath: './',// ---------- js 暴露的全局赋值给谁 ----------libraryTarget: 'window', // 变量名添加到 window 上// libraryTarget: 'global', // 变量名添加到 global 上// libraryTarget: 'commonjs', // 变量名添加到 commonjs 上},}
1️⃣ HTML 配置
npm i -D html-webpack-plugin
const HtmlWebpackPlugin = require('html-webpack-plugin');plugins: [new HtmlWebpackPlugin({template: "./src/html/html.html", // 选择生成 html 文件的模板filename: "html/webJHtml.html", // 输出的地址和名字title: "HTML文件标题", // 设置生成的 html 文件的标题chunks:[one], // 指定生成的 html 文件引入哪些 js(chunk名) 不指定将会全部引入所有的入口文件minify: {removeComments: true, // 清除所有注释collapseWhitespace: true,// 清除所有空格removeRedundantAttributes: true, // 移除无用的标签useShortDoctype: true, // 使用短的文档声明removeEmptyAttributes: true, // 移除空标签removeStyleLinkTypeAttributes: true, // 移除 rel="stylesheel"keepClosingSlash: true, // 自结束minifyJS: true, // 压缩行内 jsminifyCSS: true, // 压缩行内 cssminifyURLs: true, // 压缩行内 url}}),],
2️⃣ 多页面打包
// 多页面打包配合多入口设置entry: {common: ['./node_modules/jquery/dist/jquery.js', './src/js/common.js'],index1: './src/js/index1.js',index2: './src/js/index2.js',index3: './src/js/index3.js',},// 配置多页面打包new HtmlWebpackPlugin({template: './src/index.html',filename: 'index1.html',chunks: ['common', 'index1']}),new HtmlWebpackPlugin({template: './src/index2.html',filename: 'index2.html',chunks: ['common', 'index2']}),new HtmlWebpackPlugin({template: './src/index3.html',filename: 'index3.html',chunks: ['common', 'index3']}),
1️⃣ CSS 配置
2️⃣ style 样式内联打包
**npm i -D style-loader css-loader less less-loader**
module: {rules: [{test: /\.css$/, // 检查文件是否以 .less 结尾( 检查是否是 less 文件 )use: ['style-loader', // 创建 style 标签,将 css 代码写在页面中的 style 标签中'css-loader?modules', // 将 css 以 commonjs 方式整合到 js 文件中 >>> modules 开启 css-loader 的 module 模式'less-loader' // 将 less 文件解析成 css 文件]},]},
2️⃣ 提取 CSS 成单独文件
**npm install -D mini-css-extract-plugin**
const MiniCssExtractPlugin = require("mini-css-extract-plugin");module: {rules: [{test: /\.less$/,use: [MiniCssExtractPlugin.loader, // 负责记录要生成的 css 文件的内容'css-loader','less-loader']},]},
plugins: [new MiniCssExtractPlugin({filename: "css/[name].[contenthash:5].css", // 在什么地方是输出 css 文件, 名字是什么})],
2️⃣ css 的兼容性处理
**npm i -D postcss postcss-loader@4 postcss-preset-env**
webpack4 需要使用 postcss-loader@4
const MiniCssExtractPlugin = require("mini-css-extract-plugin");module: {rules: [{test: /\.css$/,use: [MiniCssExtractPlugin.loader, // 负责记录要生成的 css 文件的内容'css-loader','postcss-loader']},]},
配置 postcss , 创建 **.postcss.config.js** 配置
module.exports = {plugins: {// 自动厂商前缀"postcss-preset-env": {} // {} 中可以填写插件的配置}}
配置 **css** 兼容, 创建 **.browserslistrc** 文件 ( .browserslistrc 文件配置同样适用于 js 文件的兼容 )
last 2 version> 1% in CNnot ie <= 8
2️⃣ 压缩 css
**npm install optimize-css-assets-webpack-plugin --save-dev**
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');// 精简版new OptimizeCssAssetsPlugin()// 配置版new OptimizeCssAssetsPlugin({cssProcessorPluginOptions: {preset: ['default', { discardComments: { removeAll: true } }], // 移除所有的css注释},cssProcessorOptions: { // 解决没有 source map 的问题 ( 不配置则不会生成源码地图 )map: {inline: false,annotation: true,}}})
1️⃣ JS 配置
2️⃣ 语法检查
js语法检测
1. 语法检测: 依赖库 eslint-loader eslin2. 设置检测规则:推荐使用 airbnb 规则,依赖库 eslin eslint-plugin-import eslint-config-airbnb-base1. 需要设置在 package.json 中 eslintConfig 中设置
安装 loader
1. npm install eslint-loader eslint eslint-plugin-import eslint-config-airbnb-base --save-dev1. 备注 1:在:eslint.org 网站 -> userGuide -> Configuring ESLint 查看如何配置2. 备注 2:在:eslint.org 网站 -> userGuide -> Rules 查看所有规则
配置 loader
module: {rules: [{test: /\.js$/, // 只检测js文件exclude: /node_modules/, // 排除 node_modules 文件夹loader: 'eslint-loader',options: {fix: true // 自动修复 eslint 错误},enforce: "pre", // 优先执行/*正常来说,一个文件只能被一个loader处理当一个文件要被多个loader处理,那么一定要指定loader执行的先后顺序先执行 eslint 在执行 babel所有在这里设置 enforce: "pre" 优先执行语法检测 在进行兼容性处理*/},]}
方法一:不使用airbnb规则:修改 package.json
"eslintConfig": {"parserOptions": {"ecmaVersion": 6, // 支持es6"sourceType": "module" // 使用es6模块化},"env": { // 设置环境"browser": true, // 支持浏览器环境: 能够使用window上的全局变量"node": true // 支持服务器环境: 能够使用node上global的全局变量},"globals": { // 声明使用的全局变量, 这样即使没有定义也不会报错了"$": "readonly" // $ 只读变量},"rules": { // eslint检查的规则 0 忽略 1 警告 2 错误"no-console": 0, // 不检查console"eqeqeq": 2, // 用==而不用===就报错"no-alert": 2 // 不能使用alert},"extends": "eslint:recommended" // 使用 eslint 推荐的默认规则 https://cn.eslint.org/docs/rules/},
方法二:使用 airbnb 规则:修改 package.json
// 使用 airbnb 规则时可以搭配其他规则使用"eslintConfig": {"extends": "airbnb-base" // 继承 airbnb规则},
2️⃣ babel
**npm i -D @babel/core @babel/cli @babel/preset-env regenerator-runtime core-js**
# 按文件编译babel 要编译的文件 -o 编辑结果文件# 按目录编译babel 要编译的整个目录 -d 编译结果放置的目录
3️⃣ .browserslistrc - 浏览器兼容
last 3 version> 1%not ie <= 8
3️⃣ .babelrc - babel 预设
{"presets": [["@babel/preset-env", {"useBuiltIns": "usage","corejs": 3}]]}
3️⃣ webpack 使用 babel
module: {rules: [{ test: /\.js$/, use: "babel-loader" }]}
2️⃣ 压缩
1️⃣ TS 配置
ts-loader 官方包 - 一般都会使用官方包
1. `yarn add ts-loader@8 --dev` webpack4 支持的是 ts-loader 的 8 版本
awesome-typescript-loader 民间包
1. `npm install awesome-typescript-loader --save-dev`
loader 需要 typescript 的支持,需要安装 typescript 包
1. `yarn add -D typescript`
module: {rules: [{test: "/.ts$/", loader: 'ts-loader' // 使用 ts-loader 来解析 .ts 文件}]},resolve: {extensions: ['ts', 'js'] // 使用模块化时导入的文件没有后缀名,webpack 默认解析 js 文件不认识 ts 文件,这里配置省略后缀名的解析规则}
1️⃣ 图片配置
2️⃣ 打包样式中的图片
npm install file-loader url-loader --save-dev
补充:url-loader 是对象 file-loader 的上层封装,使用时需配合 file-loader 使用。
module: {rules: [{test: /\.(png)|(gif)|(jpg)$/,use: {// 使用 file-loader 来处理 js 中引用的图片, 打包生成引用的图片loader: "url-loader",options: {// 为打包的图片命名name: '[name].[hash:8].[ext]',// 打包的图片保存在的文件夹outputPath: 'img',// 图片的 url 地址publicPath: '../img',// 如果 js 使用 CommonJS 导入图片// >>> 不设置 esModule ( html标签为 <img src = "[object module]" > )// >>> 设置后 ( html标签为 <img src="..img/1.7asd5fgh.jpg"> )// 如果 JS 使用 ES6 模块化语法可以不设置, 但是推荐始终设置// 出现问题的原因是 webpack 会吧 require() 中的代码当做 JS 执行// webpack 使用 CommonJS 语法解析而 url-loader 使用 ES6 语法解析// 设置 esModule: false 就是禁用 url-loader 使用 ES6 转而使用 CommonJSesModule: false}}}]},
2️⃣ 打包 html 中的 img 图片
npm i -D html-withimg-loader
webpack4 使用 html-loader 的话要使用 html-loader@1.3.2 版本 2 版本是不兼容 webpack4 的, html-withimg-loader 可以使用最新版
module: {rules: [{test: /\.(html)$/,loader: 'html-withimg-loader' // 或 loader: 'html-loader'},]}
1️⃣ 其他文件配置
npm install file-loader --save-dev
概述:其他资源 webpack 不能解析,需要借助 loader 编译解析
添加字体文件
1. src/media/iconfont.eot2. src/media/iconfont.svg3. src/media/iconfont.ttf4. src/media/iconfont.woff5. src/media/iconfont.woff2
修改样式 iconfont.css
@font-face {font-family: 'iconfont';src: url('../media/iconfont.eot');src: url('../media/iconfont.eot?#iefix') format('embedded-opentype'),url('../media/iconfont.woff2') format('woff2'),url('../media/iconfont.woff') format('woff'),url('../media/iconfont.ttf') format('truetype'),url('../media/iconfont.svg#iconfont') format('svg');}.iconfont {font-family: "iconfont" !important;font-size: 16px;font-style: normal;-webkit-font-smoothing: antialiased;-moz-osx-font-smoothing: grayscale;}
修改 html,添加字体
js 入口文件引入
import ‘../css/iconfont.css’;
配置 loader
{test: /\.(eot|svg|woff|woff2|ttf|mp3|mp4|avi)$/, // 处理其他资源loader: 'file-loader',options: {outputPath: 'builder', // 输出的文件地址name: '[hash:8].[ext]' // [hash:8]hash值保留8位 [ext] 使用原来的扩展名}}
1️⃣ devtool
module.exports = {devtool: 'cheap-module-eval-source-map', // 开发环境推荐devtool: 'cheap-module-source-map' // 生产环境推荐}
1️⃣ 常用扩展
2️⃣ 清除输出目录
npm install --save-dev clean-webpack-plugin
const { CleanWebpackPlugin } = require('clean-webpack-plugin');module.exports = {plugins: [new CleanWebpackPlugin(),]}
2️⃣ 自动生成页面
npm i --save-dev html-webpack-plugin@4
1. webpack4 要安装4版本的 html-webpack-plugin2. webpack5 要安装5版本的 html-webpack-plugin
const HtmlWebpackPlugin = require('html-webpack-plugin')module.exports = {plugins: [new HtmlWebpackPlugin({// 以 src 文件夹里的 html 文件夹里的 index.html 文件为 html 模板template: "./src/html/index.html",// 在打包文件夹里的 html 文件夹里生成 webJHtml.html 文件filename: "./html/webJHtml.html"})],}
2️⃣ 复制静态资源
npm install copy-webpack-plugin@6 --save-dev
copy-webpack-plugin 插件和 webpack 有版本兼容问题 webpack4版本 和 copy-webpack-plugin6版本 是兼容的
module.exports = {plugins: [new CopyPlugin({patterns: [// 将 src 文件夹里的 img 文件夹里的全部文件复制到打包文件的 img 文件夹里{ from: "src/img", to: "img" }],}),]}
2️⃣ 开发服务器
npm i -D webpack-dev-server
webpack-dev-server 有兼容性 webpack-dev-serve 只兼容 webpack4 和 webpack-cli3 最高版本是兼容的
// package.json"devDependencies": {"webpack": "^4.46.0","webpack-cli": "^3.3.12","webpack-dev-server": "^3.11.2"}"scripts": {"server": "webpack-dev-server"},
// 基本配置module.exports = {devServer: {open: true, // 自动打开浏览器compress: true, // 启动gzip压缩port: 3000, // 端口号}}// 详细配置 - 适用于开发环境module.exports = {devServer: {index: "index.html", // 默认的要打开的 html 文件contentBase: resolve(__dirname, 'build'), // 运行代码的目录publicPath: "/xuni/", // 虚拟打包路径 ( 配置后文件并不会真正的被打包, 而是会生成一个虚拟的打包文件, 常用于开发阶段不用生成打包文件的情况 ) 这个设置同样可以配置在出口中watchContentBase: true, // 监视 contentBase 目录下的所有文件, 一旦文件变化就会 reload 重载watchOptions: { // 配置忽略文件ignored: /node_modules/},open: true, // 自动打开浏览器compress: true, // 启动gzip压缩port: 4000, // 端口号hot: true , // 开启热模替换功能 HMRhost: 'localhost', // 域名clientLogLevel: 'none', // 不要显示启动服务器的日志信息quiet: true, // 除了一些基本的启动信息外其他信息都不显示overlay: false, // 如果出错了, 不要全屏提示proxy: { // 服务器代理 >>> 解决开发环境的跨域问题'/api': {// 当请求 http://localhost:9000/api/xxx 的地址时会更改为 http://www.baidu.com/api/xxx// 将 /api 之前的协议域名端口号更改为 target 的协议域名端口号target: 'http://www.baidu.com',// 如果不希望 /api 包含其中可以做如下配置结果为: http://www.baidu.com/api/xxx ( 将 /api 替换为 '' )pathRewrite: { '^/api': '' }}},stats: { // stats 控制的是构建过程中控制台的输出内容// 对于 webpack-dev-server,这个属性要放在 devServer 对象里。// devServer 的 starts 配置同总体的 starts 设置相同}}}
2️⃣ 内置插件
// 插件配置const webpack = require("webpack")plugins:[new webpack.插件名(options)]
3️⃣ DefinePlugin - 全局常量定义
new webpack.DefinePlugin({PI: `Math.PI`, // PI = Math.PIVERSION: `"1.0.0"`, // VERSION = "1.0.0"DOMAIN: JSON.stringify("全局变量")})// js 中使用console.log(PI); // 打包结果 console.log(Math.PI)console.log(VERSION); // 打包结果 console.log("1.0.0")console.log(DOMAIN); // 打包结果 console.log("全局变量")
3️⃣ BannerPlugin - 生成的文件头部添加
new webpack.BannerPlugin({banner: `hash:[hash]chunkhash:[chunkhash]name:[name]author:chencorporation:baidu`})
3️⃣ ProvidePlugin - 自动加载模块
new webpack.ProvidePlugin({$: 'jquery',_: 'lodash'})// js 中使用$('#item'); // <= 起作用_.drop([1, 2, 3], 2); // <= 起作
1️⃣ 其他配置
2️⃣ target - 打包运行环境
module.exports = {target:"web" // 默认值}
设置打包结果最终要运行的环境,常用值有
1. web:打包后的代码运行在 web 环境中2. node:打包后的代码运行在 node 环境中3. 其他:[https://www.webpackjs.com/configuration/target/](https://www.webpackjs.com/configuration/target/)
2️⃣ module.noParse - 不解析匹配的模块
不解析正则表达式匹配的模块,通常用它来忽略那些大型的单模块库,以提高构建性能
解释 :大型的单文件库, 比如 jquery , jquery 的代码我们在使用时打包不需要再次经过 webpack 的解析过程, 就可以设置 noParse:/jquery/就可以配置 webpack 在打包时忽略 jquery 的引用, jquery 的代码直接省略掉 webpack 的解析过程, 直接打包
module.exporets = {noParse:/jquery/}
2️⃣ resolve - 控制模块解析过程
resolve 的相关配置主要用于控制模块解析过程
module.exports = {resolve: {alias: {"@": path.resolve(__dirname, 'src'),"_": __dirname}extensions: ['.js','.json'] // 省略后缀名 webpack 会检查哪些文件,这样配置的话要比免不同类型的文件同名modules: [resolves(__dirname, '../../node_modules'), 'node_modules']}}
3️⃣ alias - 导入模块的路径别名
配置导入模块的路径别名
1. 优点:简写路径2. 缺点:写路径没有提示
alias: {"@": path.resolve(__dirname, 'src'),"_": __dirname}
有了alias(别名)后,导入语句中可以加入配置的键名,例如**require("@/abc.js")**,webpack 会将其看作是**require(src的绝对路径+"/abc.js")**
在大型系统中,源码结构往往比较深和复杂,别名配置可以让我们更加方便的导入依赖
3️⃣ extensions - 省略文件路径的后缀名
配置省略文件路径的后缀名 - 有同名文件时不推荐使用因为文件名一样时会出错
extensions: [".js", ".json"] // 默认值
当解析模块时,遇到无具体后缀的导入语句,例如**require("test")**,会依次测试它的后缀名
1. 导入的 test 是 test.js 文件吗?2. 如果是导入 test.js 如果不是3. 导入的 test 是 test.json 文件吗?4. 如果是导入 test.json 如果不是报错
如果有其他的后缀省略会依次遍历测试
3️⃣ modules - 解析模块时的目录
modules: ["node_modules"] // 默认值
当解析模块时,如果遇到导入语句,**require("test")**,webpack 会从下面的位置寻找依赖的模块
1. 当前目录下的`**node_modules**`目录2. 上级目录下的`**node_modules**`目录
modules 中的目录可更改,更改后 webpack 在解析时就会在更改的目录寻找依赖
2️⃣ externals - 排除掉配置的配置的源码
module.exports = {externals: {jquery: "$",lodash: "_"}}
从最终的bundle中排除掉配置的配置的源码,例如,入口模块是
//index.jsrequire("jquery")require("lodash")
生成的bundle是:
(function(){...})({"./src/index.js": function(module, exports, __webpack_require__){__webpack_require__("jquery")__webpack_require__("lodash")},"jquery": function(module, exports){//jquery的大量源码},"lodash": function(module, exports){//lodash的大量源码},})
但有了上面的配置后,则变成了
(function(){...})({"./src/index.js": function(module, exports, __webpack_require__){__webpack_require__("jquery")__webpack_require__("lodash")},"jquery": function(module, exports){module.exports = $;},"lodash": function(module, exports){module.exports = _;},})
这比较适用于一些第三方库来自于外部CDN的情况,这样一来,即可以在页面中使用CDN,又让 bundle 的体积变得更小,还不影响源码的编写
2️⃣ stats - 控制台的输出内容
stats控制的是构建过程中控制台的输出内容
https://webpack.docschina.org/configuration/stats/#root
module.exports = {//...stats: {colors: true, // 告知 stats 是否输出不同的颜色( 控制台的输出带颜色 )}};
1️⃣ 性能优化
2️⃣ 减少模块解析
module:{noParse:/jquery/, // 不去解析 jquery 中的依赖库}
2️⃣ 优化 loader 性能
module.exports = {module: {rules: [{test: /\.js$/,exclude: /lodash/, // 排除 lodash 库 有些会设置排除 /none_modules/use: ['cache-loader','......loader'] // 缓存 loader 下次打包不改变时不会在打包}]}}
2️⃣ HMR 热更新
3️⃣ 配置 webpack.config.js
module.exports = {devServer:{hot:true // 开启HMR},plugins:[// 可选new webpack.HotModuleReplacementPlugin()]}
3️⃣ 修改入口文件
// 在入口文件的头部加入这段代码 就表示接受了热更新 检测到后会执行代码的热更新if(module.hot){ // 是否开启了热更新module.hot.accept() // 接受热更新}
2️⃣ ESLint
2️⃣ tree shaking
webpack2 开始就支持了 tree shaking,只要是生产环境,tree shaking 自动开启
2️⃣ 懒加载
当满足某些条件后加载 js ,下边是使用例子,语法为懒加载语法
// 动态加载 importdocument.getElementById('ljz').onclick = function () {import( /*webpackChunkName:JS2*/ '../js/JS2.js').then(({fun}) => {fun();}).catch(() => {console.log('文件加载失败');});}
2️⃣ 包分析器 bundle analyzer
使用插件来分析包的大小根据分析来优化包
const WebpackBundleAnalyzer = require("webpack-bundle-analyzer").BundleAnalyzerPlugin;module.exports = {mode: "production",plugins: [new WebpackBundleAnalyzer()]};
2️⃣ gzip
const CmpressionWebpackPlugin = require("compression-webpack-plugin")plugins: [new CmpressionWebpackPlugin({test: /\.js/,minRatio: 0.5})]
