Webpack 从入门到精通-基础篇

一、webpack 简介

1.1 webpack 是什么

webpack 是一种前端资源构建工具,一个静态模块打包器(module bundler)。 在 webpack 看来, 前端的所有资源文件(js/json/css/img/less/...)都会作为模块处理。 它将根据模块的依赖关系进行静态分析,打包生成对应的静态资源(bundle)。

下面进入正题

1.2 webpack 五个核心概念

1.2.1 Entry

入口(Entry)指示 webpack 以哪个文件为入口起点开始打包,分析构建内部依赖图。

1.2.2 Output

输出(Output)指示 webpack 打包后的资源 bundles 输出到哪里去,以及如何命名。

1.2.3 Loader

Loader:让 webpack 能够去处理那些非 JS 的文件,比如样式文件、图片文件(webpack 自身只理解JS)

1.2.4 Plugins

插件(Plugins):可以用于执行范围更广的任务。插件的范围包括,从打包优化和压缩,一直到重新定义环境中的变量等。

1.2.5 Mode

模式(Mode):指示 webpack 使用相应模式的配置。

选项描述特点
development会将 DefinePlugin 中 process.env.NODE_ENV 的值设置为 development。启用 NamedChunksPlugin 和 NamedModulesPlugin。能让代码本地调试运行的环境
production会将 DefinePlugin 中 process.env.NODE_ENV 的值设置为 production。启用 FlagDependencyUsagePlugin, FlagIncludedChunksPlugin, ModuleConcatenationPlugin, NoEmitOnErrorsPlugin, OccurrenceOrderPlugin, SideEffectsFlagPlugin 和 TerserPlugin。能让代码优化上线运行的环境

二、Webpack 初体验

2.1 初始化配置

  1. 初始化package.json文件 输入指令:npm init
  2. 下载并安装 webpack 输入指令: npm install webpack webpack-cli -g(全局安装)(即使以前安装过,现在也是可以安装的,会对以前的进行更新) npm install webpack webpack-cli -D(本地安装)

2.2 编译打包应用

  1. 创建文件
  2. 运行指令 开发环境指令:webpack src/js/index.js -o build/js/built.js --mode=development 功能:webpack 能够编译打包 js 和 json 文件,并且能将 es6 的模块化语法转换成 浏览器能识别的语法。 生产环境指令:webpack src/js/index.js -o build/js/built.js --mode=production 功能:在开发配置功能上多一个功能,压缩代码。
  3. 结论 webpack 能够编译打包 js 和 json 文件。 能将 es6 的模块化语法转换成浏览器能识别的语法。 能压缩代码。
  4. 问题 不能编译打包 css、img 等文件。 不能将 js 的 es6 基本语法转化为 es5 以下语法。

三、webpack 开发环境的基本配置

3.1 创建配置文件

  1. 创建文件 webpack.config.js
  2. 配置内容如下
 

JS

复制代码

const { resolve } = require('path'); // node 内置核心模块,用来处理路径问题。 module.exports = { entry: './src/js/index.js', // 入口文件 output: { // 输出配置 filename: './built.js', // 输出文件名 path: resolve(__dirname, 'build/js')// 输出文件路径配置 }, mode: 'development'//开发环境 };

  1. 运行指令: webpack
  2. 结论: 此时功能与上节一致

3.2 打包样式资源

  1. 创建文件

  2. 下载安装包 npm i css-loader style-loader less-loader less -D

  3. 修改配置文件webpack.config.js

 

JS

复制代码

/* webpack.config.js webpack的配置文件(src里面写项目代码,webpack写配置代码) 作用: 指示 webpack 干哪些活(当你运行 webpack 指令时,会加载里面的配置) 所有构建工具都是基于nodejs平台运行的~模块化默认采用commonjs。 */ // resolve用来拼接绝对路径的方法 const { resolve } = require('path'); module.exports = { // webpack配置 // 入口起点 entry: './src/index.js', // 输出(输出到build文件夹下面的built.js文件中) output: { // 输出文件名 filename: 'built.js', // 输出路径 // __dirname nodejs的变量,代表当前文件的目录绝对路径(表示的是与当前webpack.config.js平级的build文件夹) path: resolve(__dirname, 'build') }, // loader的配置 module: { rules: [ // 详细loader配置 // 不同文件必须配置不同loader处理 { // 匹配哪些文件 test: /\.css$/, // 使用哪些loader进行处理 use: [ // use数组中loader执行顺序:从右到左,从下到上 依次执行 // 创建style标签,将js中的样式资源插入进行,添加到head中生效 'style-loader', // 将css文件变成commonjs模块加载js中,里面内容是样式字符串 'css-loader' ] }, { test: /\.less$/, use: [ 'style-loader', 'css-loader', // 将less文件编译成css文件 // 需要下载 less-loader和less 'less-loader' ] } ] }, // plugins的配置 plugins: [ // 详细plugins的配置 ], // 模式(表示使用什么模式) mode: 'development', // 开发模式 // mode: 'production' } /* 执行的步骤:只分析less(安装loader:style-loader css-loader less-loader less)(css只需要安装前面两个loader) 1. 根据entry找到入口文件'./src/index.js' 2. 发现入口文件中引入了.less文件,而.less文件不是js或者json文件,去找module的rules里面寻找less的loader 3. 找到test: /\.less$/,执行该对象中的rules数组中的代码(从右到左,从下到上 依次执行) 4. 通过less-loader将less文件编译成css文件 5. 通过css-loader将css文件变成commonjs模块加载js中,里面内容是样式字符串 6. 通过style-loader创建style标签,将js中的样式资源插入进行,添加到head中生效 7. 将代码添加到出口文件与当前webpack.config.js平级的build文件夹下面的built.js文件中 */

  1. 运行指令:webpack

3.3 打包html资源

  1. 创建文件

  2. 下载安装包 npm install --save-dev html-webpack-plugin

  3. 修改配置文件webpack.config.js

 

JS

复制代码

/* loader: 1. 下载 2. 使用(配置loader) plugins: 1. 下载 2. 引入 3. 使用 */ const { resolve } = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { entry: './src/index.js', output: { filename: 'built.js', path: resolve(__dirname, 'build') }, module: { rules: [ // loader的配置 ] }, plugins: [ // plugins的配置 // html-webpack-plugin // 功能:默认会创建一个空的HTML,自动引入打包输出的所有资源(JS/CSS) // 需求:需要有结构的HTML文件,需要添加一个template new HtmlWebpackPlugin({ // 复制 './src/index.html' 文件,并自动引入打包输出的所有资源(JS/CSS) template: './src/index.html' }) ], mode: 'development' }; /* 执行的步骤:html(安装plugin:html-webpack-plugin) 1. 根据entry找到入口文件'./src/index.js' 2. 发现入口文件中引入了html文件,而html文件不是js或者json文件,去plugins数组中找到HtmlWebpackPlugin插件 3. 复制template选项中的文件, 4. 通过less-loader将less文件编译成css文件,自动引入打包输出的所有资源(JS/CSS)(JS文件通过script标签引入,CSS文件通过link标签引入)(不需要自己再引用了,否则重复引入会出问题的) */

  1. 运行指令:webpack

3.4 打包图片资源

  1. 创建文件

  2. 下载安装包 npm install --save-dev html-loader url-loader file-loader

  3. 修改配置文件webpack.config.js

 

JS

复制代码

const { resolve } = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { entry: './src/index.js', output: { filename: 'built.js', path: resolve(__dirname, 'build') }, module: { rules: [{ test: /\.less$/, // 要使用多个loader处理用use use: ['style-loader', 'css-loader', 'less-loader'] }, { // 问题:默认处理不了html中img图片 // 处理图片资源 test: /\.(jpg|png|gif)$/, // 使用一个loader // 下载 url-loader file-loader(url-loader依赖于file-loader) loader: 'url-loader', options: { // 图片大小小于8kb,就会被base64处理(通常小图片(8-12kb)使用limit进行这种处理,如果有9kb的图片,我们可以将limit写成10 * 1024) // 优点: 减少请求数量(减轻服务器压力) // 缺点:图片体积会更大(文件请求速度更慢) limit: 8 * 1024, // 问题:因为url-loader默认使用es6模块化解析,而html-loader引入图片是commonjs // 解析时会出问题:[object Module] // 解决:关闭url-loader的es6模块化,使用commonjs解析 esModule: false, // 给图片进行重命名 // [hash:10]取图片的hash的前10位 // [ext]取文件原来扩展名 name: '[hash:10].[ext]' } }, { test: /\.html$/, // 处理html文件的img图片,而不是处理html文件的,html文件是用HtmlWebpackPlugin处理的(负责引入img,从而能被url-loader进行处理) loader: 'html-loader' }] }, plugins: [ new HtmlWebpackPlugin({ template: './src/index.html' }) ], mode: 'development' }; /* 执行的步骤:打包图片资源(安装loader:url-loader file-loader html-loader) 1. 根据entry找到入口文件'./src/index.js' 2. 发现入口文件中引入了jpg|png|gif文件,而jpg|png|gif文件不是js或者json文件,去找module的rules里面寻找jpg|png|gif的loader 3. 找到test: /\jpg|png|gifs$/,执行该对象中的rules数组中的代码 4. 通过options-->limit对体积进行限制 5. 这种方式只能处理样式中引入的图片,不能处理html中引入的图片,如果要处理htnl中引入的资源,需要使用html-loader 6. 处理html中引入的图片需要找到test: /\jpg|png|gifs$/,执行里面的html-loader 7. 但是解析时图片的名称会出现[object Module]问题,这是因为url-loader默认使用es6模块化解析,而html-loader引入图片是commonjs,使用esModule: false关闭url-loader的es6模块化,使用commonjs解析即可 8. 使用name: '[hash:10].[ext]'给图片进行重命名 */

  1. 运行指令:webpack

3.5 打包其他资源

  1. 创建文件

  2. 下载安装包 npm install --save-dev file-loader

  3. 修改配置文件webpack.config.js

 

JS

复制代码

const { resolve } = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { entry: './src/index.js', output: { filename: 'built.js', path: resolve(__dirname, 'build') }, module: { rules: [ { test: /\.css$/, use: ['style-loader', 'css-loader'] }, // 打包其他资源(除了html/js/css资源以外的资源) { // 排除css/js/html资源 exclude: /\.(css|js|html|less)$/, loader: 'file-loader', options: { name: '[hash:10].[ext]' } } ] }, plugins: [ new HtmlWebpackPlugin({ template: './src/index.html' }) ], mode: 'development' };

  1. 运行指令:webpack

3.6 devserver

  1. 创建文件
  2. 修改配置文件webpack.config.js
 

JS

复制代码

const { resolve } = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { entry: './src/index.js', output: { filename: 'built.js', path: resolve(__dirname, 'build') }, module: { rules: [{ test: /\.css$/, use: ['style-loader', 'css-loader'] }, // 打包其他资源(除了html/js/css资源以外的资源) { // 排除css/js/html资源 exclude: /\.(css|js|html|less)$/, loader: 'file-loader', options: { name: '[hash:10].[ext]' } } ] }, plugins: [ new HtmlWebpackPlugin({ template: './src/index.html' }) ], mode: 'development', // 要安装 webpack-dev-server(因为是本地安装,所以需要使用npx webpack-dev-server启动,webpack我们使用的全局安装,所以启动的时候不需要使用npx) // 开发服务器 devServer:用来自动化(自动编译,自动打开浏览器,自动刷新浏览器~~) // 特点:只会在内存中编译打包,不会有任何输出 // 启动devServer指令为:npx webpack-dev-server devServer: { // 项目构建后路径 contentBase: resolve(__dirname, 'build'), // 启动gzip压缩(使得打包后的代码体积更小) compress: true, // 端口号 port: 3000, // 自动打开浏览器 open: true } };

  1. 运行指令:webpack

3.7 开发环境配置(基本模板)

  1. 创建文件

  2. 下载安装包 上面3.2-3.6下载的loader/plugin文件

  3. 修改配置文件webpack.config.js

 

JS

复制代码

/* 开发环境配置:能让代码运行 运行项目指令: webpack 会将打包结果输出出去 npx webpack-dev-server 只会在内存中编译打包,没有输出 所有的代码打包输出到js/built.js文件中 随着我们的资源越来越多,我们还需要将代码分类打包,便是在每一个loader中使用outputPath: build文件夹下面的需要放置打包文件的文件夹名 css、less等文件不需要设置专门的outputPath,因为它们是直接打包成字符串放进js文件中的 */ const { resolve } = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { entry: './src/js/index.js', output: { filename: 'js/built.js', path: resolve(__dirname, 'build') }, module: { rules: [ // loader的配置 { // 处理less资源 test: /\.less$/, use: ['style-loader', 'css-loader', 'less-loader'] }, { // 处理css资源 test: /\.css$/, use: ['style-loader', 'css-loader'] }, { // 处理图片资源 test: /\.(jpg|png|gif)$/, loader: 'url-loader', options: { limit: 8 * 1024, name: '[hash:10].[ext]', // 关闭es6模块化 esModule: false, outputPath: 'imgs' } }, { // 处理html中img资源 test: /\.html$/, loader: 'html-loader' }, { // 处理其他资源 exclude: /\.(html|js|css|less|jpg|png|gif)/, loader: 'file-loader', options: { name: '[hash:10].[ext]', outputPath: 'media' } } ] }, plugins: [ // plugins的配置 new HtmlWebpackPlugin({ template: './src/index.html' }) ], mode: 'development', devServer: { contentBase: resolve(__dirname, 'build'), compress: true, port: 3000, open: true } };

  1. 运行指令:webpack

四、webpack 生产环境的基本配置

4.1 提取 css 成单独文件

  1. 创建文件

  2. 下载插件安装包 npm install --save-dev mini-css-extract-plugin

  3. 修改配置文件webpack.config.js

 

JS

复制代码

const { resolve } = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); //使用这个插件的作用:style样式不是放在style标签中的,而是通过link的方式使用 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$/, use: [ // 'style-loader'作用:创建style标签,将样式放入, // 这个loader取代style-loader。作用:提取js中的css成单独文件 MiniCssExtractPlugin.loader, // 将css文件整合到js文件中 'css-loader' ] }] }, plugins: [ new HtmlWebpackPlugin({ template: './src/index.html' }), new MiniCssExtractPlugin({ // 对输出的css文件进行重命名 filename: 'css/built.css' }) ], mode: 'development' };

  1. 运行指令:webpack

4.2 css 兼容性处理

  1. 创建文件

  2. 下载loader安装包 npm install --save-dev postcss-loader postcss-preset-env

  3. 修改配置文件webpack.config.js

 

JS

复制代码

const { resolve } = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); // 设置nodejs环境变量 // process.env.NODE_ENV = 'development'; module.exports = { entry: './src/js/index.js', output: { filename: 'js/built.js', path: resolve(__dirname, 'build') }, module: { rules: [{ test: /\.css$/, use: [ MiniCssExtractPlugin.loader, 'css-loader', /* css兼容性处理:postcss --> postcss-loader postcss-preset-env(作用:postcss-preset-env帮助postcss识别环境从而加载对应的配置,从而使得代码兼容每一个浏览器的版本) postcss-preset-env: 帮postcss找到package.json中browserslist里面的配置,通过配置加载指定的css兼容性样式 //browserslist要写在package.json中 "browserslist": { // 开发环境 --> 设置node环境变量:process.env.NODE_ENV = development "development": [ "last 1 chrome version",//兼容最近的版本 "last 1 firefox version", "last 1 safari version" ], // 生产环境:默认是看生产环境 "production": [ ">0.2%", "not dead",//不用已经死的浏览器 "not op_mini all"//不用op_mini版本的浏览器 ] } */ // 使用loader的默认配置 // 'postcss-loader', // 修改loader的配置,写成下面的对象的形式 { loader: 'postcss-loader', options: { ident: 'postcss', plugins: () => [ // postcss的插件 require('postcss-preset-env')() ] } } ] }] }, plugins: [ new HtmlWebpackPlugin({ template: './src/index.html' }), new MiniCssExtractPlugin({ filename: 'css/built.css' }) ], mode: 'development' };

  1. 修改package.json
 

JS

复制代码

"browserslist": { // 开发环境 --> 设置node环境变量:process.env.NODE_ENV = development "development": [ "last 1 chrome version",//兼容最近的版本 "last 1 firefox version", "last 1 safari version" ], // 生产环境:默认是看生产环境 "production": [ ">0.2%", "not dead",//不用已经死的浏览器 "not op_mini all"//不用op_mini版本的浏览器 ] }

  1. 运行指令:webpack

4.3 压缩css

  1. 创建文件

  2. 下载插件安装包 npm install --save-dev optimize-css-assets-webpack-plugin

  3. 修改配置文件webpack.config.js

 

JS

复制代码

const { resolve } = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin') // 设置nodejs环境变量 // process.env.NODE_ENV = 'development'; module.exports = { entry: './src/js/index.js', output: { filename: 'js/built.js', path: resolve(__dirname, 'build') }, module: { rules: [ { test: /\.css$/, use: [ MiniCssExtractPlugin.loader, 'css-loader', { loader: 'postcss-loader', options: { ident: 'postcss', plugins: () => [ // postcss的插件 require('postcss-preset-env')() ] } } ] } ] }, plugins: [ new HtmlWebpackPlugin({ template: './src/index.html' }), new MiniCssExtractPlugin({ filename: 'css/built.css' }), // 压缩css new OptimizeCssAssetsWebpackPlugin() ], mode: 'development' };

  1. 运行指令:webpack

4.4 js语法检查

  1. 创建文件

  2. 下载插件安装包 npm install --save-dev eslint-loader eslint eslint-config-airbnb-base eslint-plugin-import

  3. 修改配置文件webpack.config.js

 

JS

复制代码

const { resolve } = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { entry: './src/js/index.js', output: { filename: 'js/built.js', path: resolve(__dirname, 'build') }, module: { rules: [ /* 语法检查: eslint-loader eslint 注意:只检查自己写的源代码,第三方的库是不用检查的 设置检查规则: package.json中eslintConfig中设置~ "eslintConfig": { "extends": "airbnb-base" } airbnb --> eslint-config-airbnb-base eslint-plugin-import eslint */ { test: /\.js$/, exclude: /node_modules/, //排除第三方的代码,只检查自己的代码 loader: 'eslint-loader', options: { // 自动修复eslint的错误 fix: true } } ] }, plugins: [ new HtmlWebpackPlugin({ template: './src/index.html' }) ], mode: 'development' }; /* 1. 语法检查我们需要使用eslint-loader eslint,所以先下载eslint-loader eslint 2. 使用exclude: /node_modules/排除第三方的代码,只检查自己的代码 3. 语法检查通常采用airbnb(可以在网站https://github.com/topics/javascript中找出进行详细的看里面的介绍,我们想要将airbnb库与eslint结合在一起的话,去npmjs网站中搜索elsint找到eslint-config-airbnb-base,点进去根据需求下载安装即可,eslint-config-airbnb是可以检测react代码的,用到react编写代码的时候可以使用这个库),因此需要下载eslint-config-airbnb-base eslint-plugin-import eslint 4. 在package.json中配置语法检查采用的标准 5. 检查出现语法问题的代码使用fix: true实现自动修复eslint出现的语法错误 6. 如果在代码中使用console.log()等语法,eslint会出现提示警告,此时我们可以在console.log()上一行加上一行注释// eslint-disable-next-line表示下一行eslint所有规则都失效(下一行不进行eslint检查) */

  1. 配置package.json文件
 

JS

复制代码

"eslintConfig": { "extends": "airbnb-base", "env": { "browser": true } }

  1. 运行指令:webpack

4.5 js兼容性处理

  1. 创建文件

  2. 下载插件安装包 npm install --save-dev babel-loader @babel/core @babel/preset-env @babel/polyfill core-js

  3. 修改配置文件webpack.config.js

 

JS

复制代码

const { resolve } = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { entry: './src/js/index.js', output: { filename: 'js/built.js', path: resolve(__dirname, 'build') }, module: { rules: [ /* js兼容性处理:安装babel-loader @babel/core 两个库 1. 基本js兼容性处理 --> 安装@babel/preset-env库 问题:只能转换基本语法,如promise高级语法不能转换 2. 需要做兼容性处理的就做:按需加载 --> 安装core-js库并在presets里面作如下配置 前两步骤是我们常用的兼容性处理的方式 3. 全部js兼容性处理 --> 安装babel-loader @babel/core @babel/preset-env @babel/polyfill 不需要进行配置,只需要在主入口文件中引入 @babel/polyfill即可(import '@babel/polyfill') 问题:我只要解决部分兼容性问题,但是将所有兼容性代码全部引入,体积太大了,所以我们不使用这种方式~ */ { test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader', options: { // 预设:指示babel做怎么样的兼容性处理 presets: [ [ '@babel/preset-env', { // 按需加载 useBuiltIns: 'usage', // 指定core-js版本 corejs: { version: 3 }, // 指定兼容性做到哪个版本浏览器 targets: { chrome: '60', firefox: '60', ie: '9', safari: '10', edge: '17' } } ] ] } } ] }, plugins: [ new HtmlWebpackPlugin({ template: './src/index.html' }) ], mode: 'development' };

  1. 运行指令:webpack

4.6 js压缩

  1. 创建文件
  2. 修改配置文件webpack.config.js
 

JS

复制代码

const { resolve } = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { entry: './src/js/index.js', output: { filename: 'js/built.js', path: resolve(__dirname, 'build') }, plugins: [ new HtmlWebpackPlugin({ template: './src/index.html' }) ], // 生产环境下会自动压缩js代码//内部会自动加载一些插件 mode: 'production' };

  1. 运行指令:webpack

4.7 html压缩

  1. 创建文件
  2. 修改配置文件webpack.config.js
 

JS

复制代码

const { resolve } = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { entry: './src/js/index.js', output: { filename: 'js/built.js', path: resolve(__dirname, 'build') }, plugins: [ new HtmlWebpackPlugin({ template: './src/index.html', // 压缩html代码 minify: { // 移除空格 collapseWhitespace: true, // 移除注释 removeComments: true } }) ], mode: 'production' };

  1. 运行指令:webpack

4.8 生产环境配置

  1. 创建文件

  2. 下载插件安装包 下载前面的所有的安装包

  3. 修改配置文件webpack.config.js

 

JS

复制代码

const { resolve } = require('path'); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin'); const HtmlWebpackPlugin = require('html-webpack-plugin'); // 定义nodejs环境变量:决定使用browserslist的哪个环境 process.env.NODE_ENV = 'production'; // 复用loader const commonCssLoader = [ MiniCssExtractPlugin.loader, 'css-loader', { // 还需要在package.json中定义browserslist loader: 'postcss-loader', options: { ident: 'postcss', plugins: () => [require('postcss-preset-env')()] } } ]; module.exports = { entry: './src/js/index.js', output: { filename: 'js/built.js', path: resolve(__dirname, 'build') }, module: { rules: [{ test: /\.css$/, use: [...commonCssLoader] }, { test: /\.less$/, use: [...commonCssLoader, 'less-loader'] }, /* 正常来讲,一个文件只能被一个loader处理。 当一个文件要被多个loader处理,那么一定要指定loader执行的先后顺序: 先执行eslint 在执行babel */ { // 在package.json中eslintConfig --> airbnb test: /\.js$/, exclude: /node_modules/, // 优先执行 enforce: 'pre', loader: 'eslint-loader', options: { fix: true } }, { test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader', options: { presets: [ [ '@babel/preset-env', { useBuiltIns: 'usage', corejs: { version: 3 }, targets: { chrome: '60', firefox: '60', ie: '9', safari: '10', edge: '17' } } ] ] } }, { test: /\.(jpg|png|gif)/, loader: 'url-loader', options: { limit: 8 * 1024, name: '[hash:10].[ext]', outputPath: 'imgs', esModule: false } }, { test: /\.html$/, loader: 'html-loader' }, { exclude: /\.(js|css|less|html|jpg|png|gif)/, loader: 'file-loader', options: { outputPath: 'media' } } ] }, plugins: [ new MiniCssExtractPlugin({ filename: 'css/built.css' }), new OptimizeCssAssetsWebpackPlugin(), new HtmlWebpackPlugin({ template: './src/index.html', minify: { collapseWhitespace: true, removeComments: true } }) ], mode: 'production' };

  1. 运行指令:webpack

五、webpack 优化配置

webpack性能优化
  • 开发环境性能优化
  • 生产环境性能优化
开发环境性能优化
  • 优化打包构建速度
    • HMR
  • 优化代码调试
    • source-map
生产环境性能优化
  • 优化打包构建速度
    • oneOf
    • babel缓存
    • 多进程打包
    • externals
    • dll
  • 优化代码运行的性能
    • 缓存(hash-chunkhash-contenthash)
    • tree shaking
    • code split
    • 懒加载/预加载
    • pwa

5.1 HMR

  1. 创建文件
  2. 修改配置文件webpack.config.js
 

JS

复制代码

/* 1.为什么要启用热更新? 当我们改变页面中的css文件的时候会触发整个文件包括js文件都会重新打包进行更新,这样会造成慢的打包速度,这是我们不想要的结果,所以要进行热更新,只对修改的那个文件进行重新打包,进行更新。 2.HMR: hot module replacement 热模块替换 / 模块热替换(在devServer中将hot设置为true即为开启) 作用:一个模块发生变化,只会重新打包这一个模块(而不是打包所有模块) 极大提升构建速度 样式文件:可以使用HMR功能:因为style-loader内部实现了~ js文件:默认不能使用HMR功能 --> 需要修改js代码,添加支持HMR功能的代码 注意:HMR功能对js的处理,只能处理非入口js文件的其他文件。 html文件: 默认不能使用HMR功能.同时会导致问题:html文件不能热更新了~ (不用做HMR功能,因为只有一个html文件,只要里面的内容发生变化,文件是一定要更新的) 解决:修改entry入口,将html文件引入,这样才可以生效 */ const { resolve } = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { entry: ['./src/js/index.js', './src/index.html'], output: { filename: 'js/built.js', path: resolve(__dirname, 'build') }, module: { rules: [ // loader的配置 { // 处理less资源 test: /\.less$/, use: ['style-loader', 'css-loader', 'less-loader'] }, { // 处理css资源 test: /\.css$/, use: ['style-loader', 'css-loader'] }, { // 处理图片资源 test: /\.(jpg|png|gif)$/, loader: 'url-loader', options: { limit: 8 * 1024, name: '[hash:10].[ext]', // 关闭es6模块化 esModule: false, outputPath: 'imgs' } }, { // 处理html中img资源 test: /\.html$/, loader: 'html-loader' }, { // 处理其他资源 exclude: /\.(html|js|css|less|jpg|png|gif)/, loader: 'file-loader', options: { name: '[hash:10].[ext]', outputPath: 'media' } } ] }, plugins: [ // plugins的配置 new HtmlWebpackPlugin({ template: './src/index.html' }) ], mode: 'development', devServer: { contentBase: resolve(__dirname, 'build'), compress: true, port: 3000, open: true, // 开启HMR功能 // 当修改了webpack配置,新配置要想生效,必须重启webpack服务 hot: true } };

  1. js代码需要在代码中添加
 

JS

复制代码

if (module.hot) { // 一旦 module.hot 为true,说明开启了HMR功能。 --> 让HMR功能代码生效 module.hot.accept('./print.js', function() { // 方法会监听 print.js 文件的变化,一旦发生变化,其他模块不会重新打包构建。 // 会执行后面的回调函数 //此处相当于是监听到print.js发生了变化,执行print()函数 print(); }); }

  1. 运行指令:webpack

5.2 source-map

  1. 创建文件
  2. 修改配置文件webpack.config.js
 

JS

复制代码

const { resolve } = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { entry: ['./src/js/index.js', './src/index.html'], output: { filename: 'js/built.js', path: resolve(__dirname, 'build') }, module: { rules: [ // loader的配置 { // 处理less资源 test: /\.less$/, use: ['style-loader', 'css-loader', 'less-loader'] }, { // 处理css资源 test: /\.css$/, use: ['style-loader', 'css-loader'] }, { // 处理图片资源 test: /\.(jpg|png|gif)$/, loader: 'url-loader', options: { limit: 8 * 1024, name: '[hash:10].[ext]', // 关闭es6模块化 esModule: false, outputPath: 'imgs' } }, { // 处理html中img资源 test: /\.html$/, loader: 'html-loader' }, { // 处理其他资源 exclude: /\.(html|js|css|less|jpg|png|gif)/, loader: 'file-loader', options: { name: '[hash:10].[ext]', outputPath: 'media' } } ] }, plugins: [ // plugins的配置 new HtmlWebpackPlugin({ template: './src/index.html' }) ], mode: 'development', devServer: { contentBase: resolve(__dirname, 'build'), compress: true, port: 3000, open: true, hot: true }, devtool: 'eval-source-map' //使用:直接加上这句话即可 }; /* source-map: 一种 提供源代码到构建后代码映射 技术 (如果构建后代码出错了,通过映射可以追踪源代码错误) [inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map source-map:外部 错误代码准确信息 和 源代码的错误位置 inline-source-map:内联 只生成一个内联source-map 错误代码准确信息 和 源代码的错误位置 hidden-source-map:外部 错误代码错误原因,但是没有错误位置 不能追踪源代码错误,只能提示到构建后代码的错误位置 eval-source-map:内联 每一个文件都生成对应的source-map,都在eval 错误代码准确信息 和 源代码的错误位置 nosources-source-map:外部 错误代码准确信息, 但是没有任何源代码信息 cheap-source-map:外部 错误代码准确信息 和 源代码的错误位置 只能精确的行 cheap-module-source-map:外部 错误代码准确信息 和 源代码的错误位置 module会将loader的source map加入 内联 和 外部的区别:1. 外部生成了文件,内联没有 2. 内联构建速度更快 开发环境:速度快,调试更友好 速度快(eval>inline>cheap>...) eval-cheap-souce-map(速度最快) eval-source-map 调试更友好 souce-map(调试最好) cheap-module-souce-map cheap-souce-map --> eval-source-map > :eval-cheap-module-souce-map 生产环境:源代码要不要隐藏? 调试要不要更友好 内联会让代码体积变大,所以在生产环境不用内联 nosources-source-map 全部隐藏 hidden-source-map 只隐藏源代码,会提示构建后代码错误信息 --> source-map() / cheap-module-souce-map 最终总结: 开发环境使用:eval-source-map 生产环境使用:source-map() */

  1. 运行指令:webpack

5.3 oneOf

  1. 创建文件
  2. 修改配置文件webpack.config.js
 

JS

复制代码

const { resolve } = require('path'); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin'); const HtmlWebpackPlugin = require('html-webpack-plugin'); process.env.NODE_ENV = 'production'; // 复用loader const commonCssLoader = [ MiniCssExtractPlugin.loader, 'css-loader', { loader: 'postcss-loader', options: { ident: 'postcss', plugins: () => [require('postcss-preset-env')()] } } ]; module.exports = { entry: './src/js/index.js', output: { filename: 'js/built.js', path: resolve(__dirname, 'build') }, module: { rules: [{ test: /\.js$/, exclude: /node_modules/, // 优先执行 enforce: 'pre', loader: 'eslint-loader', options: { fix: true } }, { // 以下loader只会匹配一个(这样子便不会只需要一个loader的时候将所有的loader遍历一遍了,可以提高速度) // 注意:不能有两个配置处理同一种类型文件(比如我们的bable-loader和eslint-loader都要使用,那么我们将eslint-loader提取出来放到oneOf前面,这样前面的eslint-loader执行完毕之后再从oneOf里面找到bable-loader执行) oneOf: [{ test: /\.css$/, use: [...commonCssLoader] }, { test: /\.less$/, use: [...commonCssLoader, 'less-loader'] }, /* 正常来讲,一个文件只能被一个loader处理。 当一个文件要被多个loader处理,那么一定要指定loader执行的先后顺序: 先执行eslint 在执行babel */ { test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader', options: { presets: [ [ '@babel/preset-env', { useBuiltIns: 'usage', corejs: { version: 3 }, targets: { chrome: '60', firefox: '50' } } ] ] } }, { test: /\.(jpg|png|gif)/, loader: 'url-loader', options: { limit: 8 * 1024, name: '[hash:10].[ext]', outputPath: 'imgs', esModule: false } }, { test: /\.html$/, loader: 'html-loader' }, { exclude: /\.(js|css|less|html|jpg|png|gif)/, loader: 'file-loader', options: { outputPath: 'media' } } ] }] }, plugins: [ new MiniCssExtractPlugin({ filename: 'css/built.css' }), new OptimizeCssAssetsWebpackPlugin(), new HtmlWebpackPlugin({ template: './src/index.html', minify: { collapseWhitespace: true, removeComments: true } }) ], mode: 'production' };

  1. 运行指令:webpack

5.4 缓存

  1. 创建文件
  2. 修改配置文件webpack.config.js
 

JS

复制代码

const { resolve } = require('path'); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin'); const HtmlWebpackPlugin = require('html-webpack-plugin'); /* 缓存: 1. babel缓存 cacheDirectory: true --> 让第二次打包构建速度更快 2. 文件资源缓存 hash: 每次wepack构建时会生成一个唯一的hash值。 问题: 因为js和css同时使用一个hash值。 如果重新打包,会导致所有缓存失效。(可能我却只改动一个文件) chunkhash:根据chunk生成的hash值。如果打包来源于同一个chunk,那么hash值就一样 问题: js和css的hash值还是一样的 因为css是在js中被引入的,所以同属于一个chunk contenthash: 根据文件的内容生成hash值。不同文件hash值一定不一样 --> 让代码上线运行缓存更好使用(上线代码的性能优化的) 综上所述:开启缓存需要经历两个步骤: 1. 设置cacheDirectory: true 2. 在输出的数组中加上contenthash */ // 定义nodejs环境变量:决定使用browserslist的哪个环境 process.env.NODE_ENV = 'production'; // 复用loader const commonCssLoader = [ MiniCssExtractPlugin.loader, 'css-loader', { // 还需要在package.json中定义browserslist loader: 'postcss-loader', options: { ident: 'postcss', plugins: () => [require('postcss-preset-env')()] } } ]; module.exports = { entry: './src/js/index.js', output: { filename: 'js/built.[contenthash:10].js', //本节修改的地方 path: resolve(__dirname, 'build') }, module: { rules: [{ // 在package.json中eslintConfig --> airbnb test: /\.js$/, exclude: /node_modules/, // 优先执行 enforce: 'pre', loader: 'eslint-loader', options: { fix: true } }, { oneOf: [{ test: /\.css$/, use: [...commonCssLoader] }, { test: /\.less$/, use: [...commonCssLoader, 'less-loader'] }, { test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader', options: { presets: [ [ '@babel/preset-env', { useBuiltIns: 'usage', corejs: { version: 3 }, targets: { chrome: '60', firefox: '50' } } ] ], // 开启babel缓存 // 第二次构建时,会读取之前的缓存 cacheDirectory: true // 本节修改的位置 } }, { test: /\.(jpg|png|gif)/, loader: 'url-loader', options: { limit: 8 * 1024, name: '[hash:10].[ext]', outputPath: 'imgs', esModule: false } }, { test: /\.html$/, loader: 'html-loader' }, { exclude: /\.(js|css|less|html|jpg|png|gif)/, loader: 'file-loader', options: { outputPath: 'media' } } ] }] }, plugins: [ new MiniCssExtractPlugin({ filename: 'css/built.[contenthash:10].css' //本节修改的地方 }), new OptimizeCssAssetsWebpackPlugin(), new HtmlWebpackPlugin({ template: './src/index.html', minify: { collapseWhitespace: true, removeComments: true } }) ], mode: 'production', devtool: 'source-map' };

3. 运行指令:webpack

5.5 tree shaking

  1. 修改配置文件
 

JS

复制代码

const { resolve } = require('path'); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin'); const HtmlWebpackPlugin = require('html-webpack-plugin'); /* tree shaking:去除无用代码,使得体积更小 前提:1. 必须使用ES6模块化 2. 开启production环境 作用: 减少代码体积 在package.json中配置 "sideEffects": false 所有代码都没有副作用(都可以进行tree shaking) 问题:可能会把css / @babel/polyfill (副作用)文件干掉,所以采用下面的一行形式 "sideEffects": ["*.css", "*.less"](加上这句话表示不移除css和less文件) */ process.env.NODE_ENV = 'production'; const commonCssLoader = [ MiniCssExtractPlugin.loader, 'css-loader', { loader: 'postcss-loader', options: { ident: 'postcss', plugins: () => [require('postcss-preset-env')()] } } ]; module.exports = { entry: './src/js/index.js', output: { filename: 'js/built.[contenthash:10].js', path: resolve(__dirname, 'build') }, module: { rules: [{ test: /\.js$/, exclude: /node_modules/, enforce: 'pre', loader: 'eslint-loader', options: { fix: true } }, { oneOf: [{ test: /\.css$/, use: [...commonCssLoader] }, { test: /\.less$/, use: [...commonCssLoader, 'less-loader'] }, { test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader', options: { presets: [ [ '@babel/preset-env', { useBuiltIns: 'usage', corejs: { version: 3 }, targets: { chrome: '60', firefox: '50' } } ] ], cacheDirectory: true } }, { test: /\.(jpg|png|gif)/, loader: 'url-loader', options: { limit: 8 * 1024, name: '[hash:10].[ext]', outputPath: 'imgs', esModule: false } }, { test: /\.html$/, loader: 'html-loader' }, { exclude: /\.(js|css|less|html|jpg|png|gif)/, loader: 'file-loader', options: { outputPath: 'media' } }] }] }, plugins: [ new MiniCssExtractPlugin({ filename: 'css/built.[contenthash:10].css' }), new OptimizeCssAssetsWebpackPlugin(), new HtmlWebpackPlugin({ template: './src/index.html', minify: { collapseWhitespace: true, removeComments: true } }) ], mode: 'production', devtool: 'source-map' };

  1. 运行指令:webpack

5.5 code split

多入口文件
  1. 修改配置文件
 

JS

复制代码

const { resolve } = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { // 单入口打包输出一个文件,多入口打包输出多个文件,有几个入口便打包输出多少个文件 // 单入口(单页面应用程序使用单入口)(开发时单入口应用使用的多) // entry: './src/js/index.js', entry: { // 多入口:有一个入口,最终输出就有一个bundle(多页面应用程序使用多入口) index: './src/js/index.js', test: './src/js/test.js' }, output: { // [name]:取文件名(比如上面的entry中名称为index,那么输出的文件名首部会有index名称) filename: 'js/[name].[contenthash:10].js', path: resolve(__dirname, 'build') }, plugins: [ new HtmlWebpackPlugin({ template: './src/index.html', minify: { collapseWhitespace: true, removeComments: true } }) ], mode: 'production' };

  1. 运行指令:webpack
单入口文件分割多个文件-1
  1. 修改配置文件
 

JS

复制代码

const { resolve } = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { // 单入口 // entry: './src/js/index.js', entry: { index: './src/js/index.js', test: './src/js/test.js' }, output: { // [name]:取文件名 filename: 'js/[name].[contenthash:10].js', path: resolve(__dirname, 'build') }, plugins: [ new HtmlWebpackPlugin({ template: './src/index.html', minify: { collapseWhitespace: true, removeComments: true } }) ], /* 1. 可以将node_modules中代码单独打包一个chunk最终输出(将别人的第三方的东西单独打包,将自己写的东西单独打包) 2. 自动分析多入口chunk中,有没有公共的文件。如果有会打包成单独一个chunk 3. 这种单入口的形式不常使用 */ optimization: { splitChunks: { chunks: 'all' } }, mode: 'production' };

  1. 运行指令:webpack
单入口文件输出多个出口文件-2(常用)
  1. 修改配置文件
 

JS

复制代码

const { resolve } = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { // 单入口 entry: './src/js/index.js', output: { // [name]:取文件名 filename: 'js/[name].[contenthash:10].js', path: resolve(__dirname, 'build') }, plugins: [ new HtmlWebpackPlugin({ template: './src/index.html', minify: { collapseWhitespace: true, removeComments: true } }) ], /* 1. 单入口的这种形式经常使用,实现功能:单入口打包输出多个出口文件,从而使得多个文件并行运行,增加运行速度 2. 这种方式可以将node_modules中代码单独打包一个chunk最终输出,将入口文件打包输出一个出口文件,如果想要将某个单独的文件也打包输出为一个文件,则需要进行以下配置: 1. optimization配置 2.在打包的出口文件中对需要单独打包的文件输入相关代码 */ optimization: { splitChunks: { chunks: 'all' } }, mode: 'production' };

  1. 入口文件输入代码
 

JS

复制代码

function sum(...args) { return args.reduce((p, c) => p + c, 0); } /* 通过js代码,让某个文件被单独打包成一个chunk import动态导入语法:能将某个文件单独打包 webpackChunkName: 'test'的作用是命名输出的打包名称,否则打包名称会根据每次打包输出的id进行命名,每次打包输出的id不一样,名称也不一样 */ import ( /* webpackChunkName: 'test' */ './test') .then(({ mul, count }) => { // 文件加载成功~ // eslint-disable-next-line console.log(mul(2, 5)); }) .catch(() => { // eslint-disable-next-line console.log('文件加载失败~'); }); // eslint-disable-next-line console.log(sum(1, 2, 3, 4));

  1. 运行指令:webpack

5.6 懒加载

  1. 修改配置文件
 

JS

复制代码

const { resolve } = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { // 单入口 entry: './src/js/index.js', output: { // [name]:取文件名 filename: 'js/[name].[contenthash:10].js', path: resolve(__dirname, 'build') }, plugins: [ new HtmlWebpackPlugin({ template: './src/index.html', minify: { collapseWhitespace: true, removeComments: true } }) ], /* 1. 单入口的这种形式经常使用,实现功能:单入口打包输出多个出口文件,从而使得多个文件并行运行,增加运行速度 2. 这种方式可以将node_modules中代码单独打包一个chunk最终输出,将入口文件打包输出一个出口文件,如果想要将某个单独的文件也打包输出为一个文件,则需要进行以下配置: 1. optimization配置 2.在打包的出口文件中对需要单独打包的文件输入相关代码 */ optimization: { splitChunks: { chunks: 'all' } }, mode: 'production' };

  1. 入口文件中输入
 

JS

复制代码

console.log('index.js文件被加载了~'); // import { mul } from './test'; 属于正常加载 document.getElementById('btn').onclick = function() { // 懒加载~:当文件需要使用时才加载~ // 预加载 webpackPrefetch: true:会在使用之前,提前加载js文件 // 正常加载可以认为是并行加载(同一时间加载多个文件) // 预加载 prefetch:等其他资源加载完毕,浏览器空闲了,再偷偷加载资源(兼容性比较差的)(检测是否是预加载了可以通过打开控制台,查看网络看出是否是提前加载了) /* 1. 通常情况下懒加载的第一次加载如果加载的文件比较大的话会加载的时间比较长,给用户造成阻塞的现象 2. 一般不使用预加载,因为兼容性差了一些,一般使用懒加载 3. 懒加载的实现是基于前面的代码分割的基础上的,要进行了前面的代码分割的配置才可以使用懒加载,这个案例实现的功能是点击按钮之后再加载test文件中的js代码 4. 懒加载第一次可能会慢一点,第二次加载便不会慢了,第一次加载会存入缓存中,第二次加载会直接从缓存中加载 */ import ( /* webpackChunkName: 'test', webpackPrefetch: true */ './test').then(({ mul }) => { console.log(mul(4, 5)); }); };

  1. 运行指令:webpack

5.7 pwa

  1. 下载安装包 npm install --save-dev workbox-webpack-plugin

  2. 修改配置文件

 

JS

复制代码

const { resolve } = require('path'); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const WorkboxWebpackPlugin = require('workbox-webpack-plugin'); /* PWA: 渐进式网络开发应用程序(离线可访问) workbox --> workbox-webpack-plugin */ process.env.NODE_ENV = 'production'; module.exports = { entry: './src/js/index.js', output: { filename: 'js/built.[contenthash:10].js', path: resolve(__dirname, 'build') }, module: { rules: [ ] }, plugins: [ new WorkboxWebpackPlugin.GenerateSW({ /* 1. 帮助serviceworker快速启动 2. 删除旧的 serviceworker 生成一个 serviceworker 配置文件~ */ clientsClaim: true, skipWaiting: true }) ], mode: 'production', devtool: 'source-map' };

  1. 入口文件中输入
 

JS

复制代码

import { mul } from './test'; import '../css/index.css'; function sum(...args) { return args.reduce((p, c) => p + c, 0); } // eslint-disable-next-line console.log(mul(2, 3)); // eslint-disable-next-line console.log(sum(1, 2, 3, 4)); /* 1. eslint不认识 window、navigator全局变量 解决:需要修改package.json中eslintConfig配置 "env": { "browser": true // 支持浏览器端全局变量,如果要支持node的全局变量,则写"node":true } 2. sw代码必须运行在服务器上 --> nodejs --> npm i serve -g serve -s build 启动服务器,将build目录下所有资源作为静态资源暴露出去 */ // 注册serviceWorker // 处理兼容性问题 if ('serviceWorker' in navigator) { window.addEventListener('load', () => { navigator.serviceWorker .register('/service-worker.js') .then(() => { console.log('sw注册成功了~'); }) .catch(() => { console.log('sw注册失败了~'); }); }); }

查看注册的service workers

注册成功缓存的离线数据

4. 运行指令:webpack

5.8 多进程打包

  1. 下载安装包 npm install --save-dev thread-loader

  2. 修改配置文件

 

JS

复制代码

const { resolve } = require('path'); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const WorkboxWebpackPlugin = require('workbox-webpack-plugin');// module.exports = { entry: './src/js/index.js', output: { filename: 'js/built.[contenthash:10].js', path: resolve(__dirname, 'build') }, module: { rules: [ { oneOf: [ { test: /\.css$/, use: [...commonCssLoader] }, { test: /\.less$/, use: [...commonCssLoader, 'less-loader'] }, { test: /\.js$/, exclude: /node_modules/, use: [ /* 开启多进程打包。 进程启动大概为600ms,进程通信也有开销。 只有工作消耗时间比较长,才需要多进程打包 */ { loader: 'thread-loader', options: { workers: 2 // 进程2个 } }, { loader: 'babel-loader', options: { presets: [ [ '@babel/preset-env', { useBuiltIns: 'usage', corejs: { version: 3 }, targets: { chrome: '60', firefox: '50' } } ] ], cacheDirectory: true } } ] }, { test: /\.(jpg|png|gif)/, loader: 'url-loader', options: { limit: 8 * 1024, name: '[hash:10].[ext]', outputPath: 'imgs', esModule: false } }, { test: /\.html$/, loader: 'html-loader' }, { exclude: /\.(js|css|less|html|jpg|png|gif)/, loader: 'file-loader', options: { outputPath: 'media' } } ] } ] }, plugins: [ new MiniCssExtractPlugin({ filename: 'css/built.[contenthash:10].css' }), new OptimizeCssAssetsWebpackPlugin(), new HtmlWebpackPlugin({ template: './src/index.html', minify: { collapseWhitespace: true, removeComments: true } }), new WorkboxWebpackPlugin.GenerateSW({ clientsClaim: true, skipWaiting: true }) ], mode: 'production', devtool: 'source-map' };

  1. 运行指令:webpack

5.9 externals

  1. 修改配置文件
 

JS

复制代码

const { resolve } = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { entry: './src/js/index.js', output: { filename: 'js/built.js', path: resolve(__dirname, 'build') }, plugins: [ new HtmlWebpackPlugin({ template: './src/index.html' }) ], mode: 'production', externals: { // 拒绝jQuery被打包进来,作用: 假如说我们的jequery使用的是CDN链接,那么打包的时候jquery不会被打包进来,直接使用CDN链接 jquery: 'jQuery' } };

  1. 运行指令:webpack

5.10 dll

  1. 创建webpack.dll.js文件并输入
 

JS

复制代码

/* 使用dll技术,对某些库(第三方库:jquery、react、vue...)进行单独打包 当你运行 webpack 时,默认查找 webpack.config.js 配置文件 需求:需要运行 webpack.dll.js 文件 --> webpack --config webpack.dll.js */ const { resolve } = require('path'); const webpack = require('webpack'); module.exports = { entry: { // 最终打包生成的[name] --> jquery // ['jquery'] --> 要打包的库是jquery jquery: ['jquery'], }, output: { filename: '[name].js', path: resolve(__dirname, 'dll'), library: '[name]_[hash]' // 打包的库里面向外暴露出去的内容叫什么名字 }, plugins: [ // 打包生成一个 manifest.json --> 提供和jquery映射 new webpack.DllPlugin({ name: '[name]_[hash]', // 映射库的暴露的内容名称 path: resolve(__dirname, 'dll/manifest.json') // 输出文件路径 }) ], mode: 'production' };

  1. 运行指令:webpack --config webpack.dll.js
  2. 修改配置文件webpack.config.js
 

JS

复制代码

const { resolve } = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const webpack = require('webpack'); const AddAssetHtmlWebpackPlugin = require('add-asset-html-webpack-plugin'); module.exports = { entry: './src/index.js', output: { filename: 'built.js', path: resolve(__dirname, 'build') }, plugins: [ new HtmlWebpackPlugin({ template: './src/index.html' }), // 告诉webpack哪些库不参与打包,同时使用时的名称也得变~ new webpack.DllReferencePlugin({ manifest: resolve(__dirname, 'dll/manifest.json') }), // 将某个文件打包输出去,并在html中自动引入该资源 new AddAssetHtmlWebpackPlugin({ filepath: resolve(__dirname, 'dll/jquery.js') }) ], mode: 'production' }; /* 首先要在webpack.dll.js与webpack.config.js中引入webpack插件 1. 在webpack.dll.js文件中的写入我们需要打包的库以及打包的库输出的名字为什么(实现功能:第一次打包之后只要jquery库名称不变,下一次不需要在重新打包了,直接使用,提高构建速度)(不仅仅是jquery库,各种库都要引入) 2. plugin中生成的manifest.json文件表示了jquery的映射关系 3. webpack.config.js中使用DllReferencePlugin告诉webpack哪些文件不需要再重新打包 4. webpack.config.js中使用AddAssetHtmlWebpackPlugin将ebpack.dll.js中打包的资源在html中自动引入 */

  1. 运行指令:webpack

六、webpack配置详情

6.1 entry

 

JS

复制代码

const { resolve } = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); /* entry: 入口起点 1. string --> './src/index.js'(用的多) 单入口 打包形成一个chunk。 输出一个bundle文件。 此时chunk的名称默认是 main 2. array --> ['./src/index.js', './src/add.js'](一般不用这种形式) 多入口 所有入口文件最终只会形成一个chunk, 输出出去只有一个bundle文件。 --> 只有在HMR功能中让html热更新生效~ 3. object(用的多) 多入口 有几个入口文件就形成几个chunk,输出几个bundle文件 此时chunk的名称是 key --> 特殊用法 { // 所有入口文件最终只会形成一个chunk, 输出出去只有一个bundle文件。 index: ['./src/index.js', './src/count.js'], // 形成一个chunk,输出一个bundle文件。 add: './src/add.js' } */ module.exports = { entry: { index: ['./src/index.js', './src/count.js'], add: './src/add.js' }, output: { filename: '[name].js', path: resolve(__dirname, 'build') }, plugins: [new HtmlWebpackPlugin()], mode: 'development' };

6.2 output

 

JS

复制代码

const { resolve } = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { entry: './src/index.js', output: { // 文件名称(指定名称+目录) filename: 'js/[name].js', // 输出文件目录(将来所有资源输出的公共目录) path: resolve(__dirname, 'build'), // 所有资源引入公共路径前缀 --> 'imgs/a.jpg' --> '/imgs/a.jpg' publicPath: '/', chunkFilename: 'js/[name]_chunk.js', // 非入口chunk的名称 // library: '[name]', // 整个库向外暴露的变量名 // libraryTarget: 'window' // 变量名添加到哪个上 browser // libraryTarget: 'global' // 变量名添加到哪个上 node // libraryTarget: 'commonjs' }, plugins: [new HtmlWebpackPlugin()], mode: 'development' };

6.3 module

 

JS

复制代码

const { resolve } = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { entry: './src/index.js', output: { filename: 'js/[name].js', path: resolve(__dirname, 'build') }, module: { rules: [ // loader的配置 { test: /\.css$/, // 多个loader用use use: ['style-loader', 'css-loader'] }, { test: /\.js$/, // 排除node_modules下的js文件 exclude: /node_modules/, // 只检查 src 下的js文件 include: resolve(__dirname, 'src'), // 优先执行 enforce: 'pre', // 延后执行 // enforce: 'post', // 单个loader用loader loader: 'eslint-loader', options: {} }, { // 以下配置只会生效一个 oneOf: [] } ] }, plugins: [new HtmlWebpackPlugin()], mode: 'development' };

6.4 resolve

 

JS

复制代码

const { resolve } = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { entry: './src/js/index.js', output: { filename: 'js/[name].js', path: resolve(__dirname, 'build') }, module: { rules: [{ test: /\.css$/, use: ['style-loader', 'css-loader'] }] }, plugins: [new HtmlWebpackPlugin()], mode: 'development', // 解析模块的规则 resolve: { // 配置解析模块路径别名: 优点简写路径 缺点路径没有提示 alias: { $css: resolve(__dirname, 'src/css') }, // 配置省略文件路径的后缀名 extensions: ['.js', '.json', '.jsx', '.css'], // 告诉 webpack 解析模块是去找哪个目录(不写这个的话,他会一层一层的往上面找,直到找到位置) modules: [resolve(__dirname, '../../node_modules'), 'node_modules'] } };

6.5 devserver

 

JS

复制代码

const { resolve } = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { entry: './src/js/index.js', output: { filename: 'js/[name].js', path: resolve(__dirname, 'build') }, module: { rules: [ { test: /\.css$/, use: ['style-loader', 'css-loader'] } ] }, plugins: [new HtmlWebpackPlugin()], mode: 'development', resolve: { alias: { $css: resolve(__dirname, 'src/css') }, extensions: ['.js', '.json', '.jsx', '.css'], modules: [resolve(__dirname, '../../node_modules'), 'node_modules'] }, devServer: { // 运行代码的目录 contentBase: resolve(__dirname, 'build'), // 监视 contentBase 目录下的所有文件,一旦文件变化就会 reload watchContentBase: true, watchOptions: { // 监视的时候忽略文件 ignored: /node_modules/ }, // 启动gzip压缩,体积小 compress: true, // 端口号 port: 5000, // 域名 host: 'localhost', // 自动打开浏览器 open: true, // 开启HMR功能 hot: true, // 不要显示启动服务器日志信息 clientLogLevel: 'none', // 除了一些基本启动信息以外,其他内容都不要显示 quiet: true, // 如果出错了,不要全屏提示~,只需要在日志中打印即可 overlay: false, // 服务器代理 --> 解决开发环境跨域问题 proxy: { // 一旦devServer(5000)服务器接受到 /api/xxx 的请求,就会把请求转发到另外一个服务器(3000) '/api': { target: 'http://localhost:3000', // 发送请求时,请求路径重写:将 /api/xxx --> /xxx (去掉/api) pathRewrite: { '^/api': '' } } } } };

6.6 optimization

 

JS

复制代码

const { resolve } = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const TerserWebpackPlugin = require('terser-webpack-plugin') module.exports = { entry: './src/js/index.js', output: { filename: 'js/[name].[contenthash:10].js', path: resolve(__dirname, 'build'), chunkFilename: 'js/[name].[contenthash:10]_chunk.js' }, module: { rules: [{ test: /\.css$/, use: ['style-loader', 'css-loader'] }] }, plugins: [new HtmlWebpackPlugin()], mode: 'production', resolve: { alias: { $css: resolve(__dirname, 'src/css') }, extensions: ['.js', '.json', '.jsx', '.css'], modules: [resolve(__dirname, '../../node_modules'), 'node_modules'] }, optimization: { splitChunks: { chunks: 'all' // 默认值,可以不写,基本上不修改~ /* minSize: 30 * 1024, // 分割的chunk最小为30kb,小于30kb的不分割,大于30kb才分割 maxSiza: 0, // 最大没有限制 minChunks: 1, // 要提取的chunk最少被引用1次 maxAsyncRequests: 5, // 按需加载时并行加载的文件的最大数量 maxInitialRequests: 3, // 入口js文件最大并行请求数量 automaticNameDelimiter: '~', // 名称连接符 name: true, // 可以使用命名规则 cacheGroups: { // 分割chunk的组 // node_modules文件会被打包到 vendors 组的chunk中。--> vendors~xxx.js // 满足上面写的公共规则,如:大小超过30kb,至少被引用一次。 vendors: { test: /[\\/]node_modules[\\/]/, // 优先级 priority: -10 }, default: { // 要提取的chunk最少被引用2次 minChunks: 2, // 优先级 priority: -20, // 如果当前要打包的模块,和之前已经被提取的模块是同一个,就会复用,而不是重新打包模块 reuseExistingChunk: true } }*/ }, // 将当前模块的记录其他模块的hash单独打包为一个文件 runtime // 解决:修改a文件导致b文件的contenthash变化 runtimeChunk: { name: entrypoint => `runtime-${entrypoint.name}` }, minimizer: [ // 配置生产环境的压缩方案:js和css new TerserWebpackPlugin({ // 开启缓存 cache: true, // 开启多进程打包 parallel: true, // 启动source-map sourceMap: true }) ] } };

七、webpack5使用

webpack5

此版本重点关注以下内容:

  • 通过持久缓存提高构建性能.
  • 使用更好的算法和默认值来改善长期缓存.
  • 通过更好的树摇和代码生成来改善捆绑包大小.
  • 清除处于怪异状态的内部结构,同时在 v4 中实现功能而不引入任何重大更改.
  • 通过引入重大更改来为将来的功能做准备,以使我们能够尽可能长时间地使用 v5.

下载

  • npm i webpack@next webpack-cli -D

自动删除 Node.js Polyfills

早期,webpack 的目标是允许在浏览器中运行大多数 node.js 模块,但是模块格局发生了变化,许多模块用途现在主要是为前端目的而编写的。webpack <= 4 附带了许多 node.js 核心模块的 polyfill,一旦模块使用任何核心模块(即 crypto 模块),这些模块就会自动应用。

尽管这使使用为 node.js 编写的模块变得容易,但它会将这些巨大的 polyfill 添加到包中。在许多情况下,这些 polyfill 是不必要的。

webpack 5 会自动停止填充这些核心模块,并专注于与前端兼容的模块。

迁移:

  • 尽可能尝试使用与前端兼容的模块。
  • 可以为 node.js 核心模块手动添加一个 polyfill。错误消息将提示如何实现该目标。

Chunk 和模块 ID

添加了用于长期缓存的新算法。在生产模式下默认情况下启用这些功能。

chunkIds: "deterministic", moduleIds: "deterministic"

Chunk ID

你可以不用使用 import(/* webpackChunkName: "name" */ "module") 在开发环境来为 chunk 命名,生产环境还是有必要的

webpack 内部有 chunk 命名规则,不再是以 id(0, 1, 2)命名了

Tree Shaking

  1. webpack 现在能够处理对嵌套模块的 tree shaking
 

js

复制代码

// inner.js export const a = 1; export const b = 2; // module.js import * as inner from './inner'; export { inner }; // user.js import * as module from './module'; console.log(module.inner.a);

在生产环境中, inner 模块暴露的 b 会被删除

  1. webpack 现在能够多个模块之前的关系
 

js

复制代码

import { something } from './something'; function usingSomething() { return something; } export function test() { return usingSomething(); }

当设置了"sideEffects": false时,一旦发现test方法没有使用,不但删除test,还会删除"./something"

  1. webpack 现在能处理对 Commonjs 的 tree shaking

Output

webpack 4 默认只能输出 ES5 代码

webpack 5 开始新增一个属性 output.ecmaVersion, 可以生成 ES5 和 ES6 / ES2015 代码.

如:output.ecmaVersion: 2015

SplitChunk

 

js

复制代码

// webpack4 minSize: 30000;

 

js

复制代码

// webpack5 minSize: { javascript: 30000, style: 50000, }

Caching

 

js

复制代码

// 配置缓存 cache: { // 磁盘存储 type: "filesystem", buildDependencies: { // 当配置修改时,缓存失效 config: [__filename] } }

缓存将存储到 node_modules/.cache/webpack

监视输出文件

之前 webpack 总是在第一次构建时输出全部文件,但是监视重新构建时会只更新修改的文件。

此次更新在第一次构建时会找到输出文件看是否有变化,从而决定要不要输出全部文件。

默认值

  • entry: "./src/index.js
  • output.path: path.resolve(__dirname, "dist")
  • output.filename: "[name].js"

作者:码农增哥
链接:https://juejin.cn/post/6909731086977368078/
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/697680.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

CA证书及PKI

文章目录 概述非对称加密User Case: 数据加密User Case: 签名验证潜在问题 CACA证书的组成CA签发证书流程CA验证签名流程CA吊销证书流程 PKI信任链证书链 概述 首先我们需要简单对证书有一个基本的概念&#xff0c;以几个问题进入了解 ❓ Question1: 什么是证书&#xff1f; 证…

数据可视化——pyecharts库绘图

目录 官方文档 使用说明&#xff1a; 点击基本图表 可以点击你想要的图表 安装&#xff1a; 一些例图&#xff1a; 柱状图&#xff1a; 效果&#xff1a; 折线图&#xff1a; 效果&#xff1a; 环形图&#xff1a; 效果&#xff1a; 南丁格尔图&#xff08;玫瑰图&am…

Mysql的InnoDB介绍

目录 show engines查看搜索殷勤&#xff0c;默认InnoDB。 Mysql为什么使用InnoDB作为默认存储引擎 InnoDB主要包括内存结构和磁盘结构 内存结构包含: 磁盘结构中包括: 为什么设计成内存结构和磁盘结构两部分 使用InnoDB存储引擎创建的表&#xff0c;对应的数据文件在哪里…

Android14之向build.prop添加属性(二百一十九)

简介: CSDN博客专家,专注Android/Linux系统,分享多mic语音方案、音视频、编解码等技术,与大家一起成长! 优质专栏:Audio工程师进阶系列【原创干货持续更新中……】🚀 优质专栏:多媒体系统工程师系列【原创干货持续更新中……】🚀 优质视频课程:AAOS车载系统+AOSP…

uniapp原生插件开发实战——集成Android端的Twitter登陆

Android集成Twitter登陆的官方教程:https://github.com/twitter-archive/twitter-kit-android/wiki 项目创建 首先可以先看下uniapp原生插件开发教程 uniapp原生插件类型分为两种: Module模式:能力扩展,无嵌入窗体的UI控件,类似于功能插件。Component模式:在窗体中内嵌…

php 混合xml js,html 代码报错 ,结束标签关闭, short_open_tag 的作用,php关闭文件结束判断

结束标签关闭, short_open_tag 的作用&#xff0c;php关闭文件结束判断 有时候我们我们会将php&#xff0c;xml&#xff0c;js&#xff0c;html 混合编写 php文件只要开始标签而不要结尾标签? 混合代码看代码 直接运行 yntax error, unexpected version (T_STRING) in php…

STM32智能家居项目esp8266上云OneNet【附源码+详细教程】

目录 一、硬件选材 二、OneNet使用教程 三、代码修改教程 四、添加数据流方法 五、项目工程&#xff08;源码元件清单教程&#xff09; 小白也能做&#xff0c;项目工程在后文可下载。 一、硬件选材 二、OneNet使用教程 拿到代码后肯定是连不上网的&#xff0c;因为源码…

Android.基本用法学习笔记

设置文本的内容 先在strings.xml声明变量 方法1. 方法2. 设置文本的大小 1.单位dp&#xff0c;大家可以去学一下有关的单位换算 2. 设置文本颜色 1. 2. 4.设置文本背景颜色 1. 2. 设置视图的宽高 与上级视图一致&#xff0c;也就是上一级有多宽就有多少 1. 2. 3. 4. 设置视图…

生成式人工智能重置:从初期热潮到战略扩展

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

cs61C | lecture4

cs61C | lecture4 C 语言内存布局 ### Stack 在最顶部&#xff0c;向下增长。包含局部变量和 function frame information。 > Each stack frame is a contiguous block of memory holding the local variables of a single procedure. > A stack frame includes: > …

C语言详解(文件操作)2

Hi~&#xff01;这里是奋斗的小羊&#xff0c;很荣幸您能阅读我的文章&#xff0c;诚请评论指点&#xff0c;欢迎欢迎 ~~ &#x1f4a5;&#x1f4a5;个人主页&#xff1a;奋斗的小羊 &#x1f4a5;&#x1f4a5;所属专栏&#xff1a;C语言 &#x1f680;本系列文章为个人学习…

浅析嵌入式实时系统中信号量的概念

目录 概述 1. 认识信号量 1.1 定义信号量 1.2 信号量的类型 1.2.1 二值信号量 1.2.2 计数信号量 1.2.3 互斥信号量 1.2.3.1 认识互斥信号量 1.2.3.2 互斥信号量的其他特性 2 典型信号量的使用 2.1 等待和信号同步 2.2 多任务等待和信号同步 2.3 信用跟踪同步 2.…

C++ - Clion安装Qt msvc2017版本教程,基础环境配置clion+ Qt5.12.12 msvc2017 + VS2019

背景&#xff1a;平时代码开发使用clion&#xff0c;但使用项目要制定mscv2017版本Qt。先装过mingw版本Qt无法运行&#xff0c;但msvc版本依赖装有Visual Studio&#xff0c;本地装的又是2019版。就出现了这个大坑&#xff0c;需要配置好clion Qt msvc2017 VS2019。 文章目录 …

深度学习中embedding层的理解

Embedding层作用 在深度学习领域中&#xff0c;Embedding层扮演着至关重要的角色&#xff0c;尤其在处理文本数据或类别数据。Embedding层的功能有两个&#xff1a; 1. 将高维稀疏的输入数据&#xff08;如单词、类别标签等&#xff09;转换为低维稠密的向量表示&#xff0c;…

LNMP配置

文章目录 一、相关概念CGI的由来FastCGIPHP-FPM 二、编译安装编译安装nginxyum安装mysql编译安装php配置nginx支持php解析增加数据库安装论坛 一、相关概念 CGI的由来 最早的Web服务器只能简单地响应浏览器发来的HTTP请求&#xff0c;并将存储在服务器上的HTML文件返回给浏览器…

Python | Leetcode Python题解之第144题二叉树的前序遍历

题目&#xff1a; 题解&#xff1a; class Solution:def preorderTraversal(self, root: TreeNode) -> List[int]:res list()if not root:return resp1 rootwhile p1:p2 p1.leftif p2:while p2.right and p2.right ! p1:p2 p2.rightif not p2.right:res.append(p1.val)…

DeepSpeed MoE

MoE概念 模型参数增加很多&#xff1b;计算量没有增加&#xff08;gating小FNN&#xff0c;比以前的大FNN计算量要小&#xff09;&#xff1b;收敛速度变快&#xff1b; 效果&#xff1a;PR-MoE > 普通MoE > DenseTransformer MoE模型&#xff0c;可视为Sparse Model&…

手机投屏到电脑时,手机提示连接失败

前言 注意&#xff0c;本方法建立在你已经通过其他帖子等解决了前置条件的情况下&#xff0c;手机提示连接失败情况下&#xff0c;包括但不限于关闭防火墙、安装无线投屏工具、手机和电脑连接在同一个WiFi频段下、关闭杀毒软件等。 具体操作方法 1、请进入设置 > 系统和…

电脑存储设备,固态硬盘介绍,usb接口

简介 存储设备分为两大类主存和辅存&#xff0c;另外还有专门提供存储服务的网络存储 主存储器 随机存取存储器&#xff08;RAM, Random Access Memory&#xff09; 特点&#xff1a;高速、易失性存储器&#xff0c;断电后数据丢失。用途&#xff1a;临时存储正在使用的数据…

卷积神经网络 convolution neural network

1.数学卷积&#xff1a;滑动窗口 2.图像具有局部相关性和平移不变性&#xff0c;有许多冗余的特征点&#xff0c;如果用全连接的神经网络会很浪费时间。 3.卷积nn&#xff1a;减少参数&#xff0c;滑动提取特征&#xff0c;特征作为下层卷积的输入&#xff0c;然后放到全连接…