webpack知识点总结(基础应用篇)

一、为什么需要webpack

1.为什么使用webpack

①传统的书写方式,加载太多脚本会导致网络瓶颈,如不小心改变JavaScript文件加载顺序,项目会崩溃,还会导致作用域问题、js文件太大无法做到按需加载、可读性和可维护性太低的问题。

②当今JavaScript、css的语法规范不断更新,但是浏览器的兼容性却不能同步的更新,开发者可以通过 webpack 预处理器进行编译,自由的使用 JS、CSS 等语言的新语法。

③Vue 脚手架vue-cli、React 脚手架create-react-app、Taro 脚手架taro-cli都是使用webpack,开发者掌握 webpack后,可以自由配置脚手架,根据项目需要,去调整 webpack 配置,以提高项目性能。

④拥有依赖管理、动态打包、代码分离、按需加载、代码压缩、静态资源压缩、缓存等配置;扩展性强,插件机制完善,开发者可自定义插件、loader;webpack 社区庞大,更新速度快,轮子丰富;

1.由于浏览器解析html的顺序是从上至下,引入第三方库时便会存在先后顺序问题;作用域是由于存在多个JS文件时,window下有可能挂载多个相同或不同变量

→解决方案:使用立即调用函数表达式 IIFE 来创建独立作用域

2.js文件过于庞大时无法做到按需加载,如引用第三方工具库lodash等

→解决:

①(依赖nodeJS实现)使用commonJs的module.exports来抛出模块代码,使用require来引入模块

②(不依赖nodeJS实现)可以使用browserify、requireJS等打包工具实现

  • 在没有各个 webpack 搭建的脚手架(create-react-app、vue-cli 等等)之前,我们通过在 HTML5 文件里引入一个个 Javascript 文件来进行开发,这就可能导致并行请求数量过多、存在重复代码等问题。
  • 而通过 webpack,我们可以使用 import、require 来进行模块化开发。
  • 在 webpack 中一切皆模块,js、css、图片、字体都是模块,而且支持静态解析、按需打包、动态加载、代码分离等功能,帮助我们优化代码,提升性能。

2.什么是webpack?

Webpack 是一个前端资源加载/打包工具。它将根据模块的依赖关系进行静态分析,然后将这些模块按照指定的规则生成对应的静态资源。

  • Webpack是一个模块打包器。
  • 在Webpack中会将前端的所有资源文件(js/json/css/img/less/...)都作为模块处理。
  • 它将根据模块的依赖关系进行分析,生成对应的资源

3.五个核心概念:

  1. 【入口(entry)】:指示 webpack 应该使用哪个模块,来作为构建其内部依赖图的开始。
  2. 【输出(output)】:在哪里输出文件,以及如何命名这些文件。
  3. 【Loader】:处理那些非 JavaScript 文件(webpack 自身只能解析 JavaScript和json)。
  4. 【插件(plugins)】执行范围更广的任务,从打包到优化都可以实现。
  5. 【模式(mode)】,有生产模式production和开发模式development
  • 对loader的理解:webpack 本身只能处理JS、JSON模块,如果要加载其他类型的文件(模块),就需要使用对应的loader 。它本身是一个函数,接受源文件作为参数,返回转换的结果。loader 一般以 xxx-loader 的方式命名,xxx 代表了这个 loader 要做的转换功能,比如 css-loader。
  • 对plugins的理解:插件可以完成一些loader不能完成的功能。

4.配置文件

  • webpack.config.js : 用于存储webpack配置信息。

二、webpack与竞品

Webpack :为处理资源管理和分割代码而生,可以包含任何类型的文件。灵活,插件 多。

Parcel :是 0 配置工具, 用户一般无需再做其他配置即可开箱即用。

Rollup:用标准化的格式(es6)来写代码,通过减少死代码尽可能地缩小包体积。 一般只用来打包JS。

构建一个简单的应用并让它快速运行起来?使用 Parcel。

构建一个类库只需要导入很少第三方库?使用 Rollup。

构建一个复杂的应用,需要集成很多第三方库?需要代码分拆,使用静态资源文件, 还有 CommonJS 依赖?使用 webpack。

Vite 将成 为 Vue 的现代标配。甚至最近新推出的 Petite Vue 从开发、编译、发布、Demo几 乎全都是使用 Vite 完成。Webpack、Vite 作为前端热门的工程化构建工具,它们都有各自的适用场景。

三、安装webpack

1.本地安转

bash 复制代码
npm install --save-dev webpack
 # 或指定版本
npm install --save-dev webpack@<version>

如果你使用 webpack v4+ 版本,并且想要在命令行中调用 webpack,你还需要安装 CLI。

bash 复制代码
npm install --save-dev webpack-cli

**对于大多数项目,我们建议本地安装。这可以在引入重大更新(breaking change)版本时,更容易分别升级项目。**通常会通过运行一个或多个 npm scripts 以在本地 node_modules 目录中查找安装的 webpack, 来运行 webpack:

javascript 复制代码
"scripts": {
 "build": "webpack --config webpack.config.js"
 }

2.全局安装

bash 复制代码
npm install --global webpack

不推荐 全局安装 webpack。这会将你项目中的 webpack 锁定到指定版本,并且在使用不同的 webpack 版本的项目中, 可能会导致构建失败。

  • 安装node.js https://nodejs.org/zh-cn/ 最新LTS版本
  • 验证node版本 node -v
  • 验证npm版本 npm -v
  • (可选)执行 npm i webpack webpack -cli --global (全局)安装webapck
  • 在当前文件目录下执行 webpack -v 验证版本
  • npm init -y 初始化package配置文件
  • npm install webpack webpack-cli --save-dev 本地安装webpack及webpack-cli

四、运行webpack

  • 在终端中输入 webpack 回车会生成dist文件夹即打包后的文件 (使用全局webpack)
  • webpack --status detailed 查看详细打包信息(使用全局webpack
  • npx webpack 使用当前目录webpack

进入项目目录,运行webpack,执行指令:

bash 复制代码
npx webpack

生成了一个main.js文件

五、自定义webpack配置

实际上, webpack-cli 给我们提供了丰富的终端命令行指令,可以通过npx webpack --help 查看帮助信息。

  • npx webpack --entry ./src/index.js --mode production 设置入口文件

可是命令行不方便也不直观,而且还不利于保存配置的内容,可以采取配置config的方式。因此,webpack 还给我们提供了通过配置文件,来自定义配置参数的能力。

  • 可以在当前目录下新建webpack.config.js文件,用commonJs写法抛出配置模块:
javascript 复制代码
const { resolve } = require('path'); //node内置核心模块,用来设置路径。

module.exports = {
    //入口是指依赖关系图的开始,从入口开始寻找依赖,打包构建。webpack 允许一个或多个入口配置。
    entry: './src/js/app.js', // 入口文件配置(精简写法)
    /*完整写法:
		entry:{
			main:'./src/js/app.js'
		}
		*/
    //输出则是用于配置 webpack 构建打包的出口,如打包的位置,打包的文件名等等。
    output: { //输出配置
        clean: true, //打包前清理 dist 文件夹,在每次构建前清理 /dist文件夹,这样只会生成用到的文件。
        filename: 'bundle.js',//输出文件名
        path: resolve(__dirname, './dist')//输出文件路径(绝对路径),require的path为nodeJS自带,__dirname可直接匹配当前目录
    },
    mode: 'development'   //开发环境(二选一) webpack编译模式,区分生产测试环境 
    //mode: 'production'   //生产环境(二选一)
    // 文件监视改动,自动产出bundle.js
    devtool: 'inline-source-map'//精准定位代码行数,便于查看。在开发模式下追踪代码
    watch: true //观察模式,自动检测变化,但需要手动刷新浏览器,如果其中一个文件被更新,代码将被重新编译,所以不必再去手动运行整个构建。
};

使用 webpack-dev-server

**webpack-dev-server支持文件打包后的本地代码实时更新,提高webpack开发效率。**提供了一个基本的 web server,并且具有 live reloading(实时重新加载) 功能。可以自动检测文件变化,从而重新编译,帮助我们自动实现浏览器的刷新。

先安装:

javascript 复制代码
npm install --save-dev webpack-dev-server

也可以通过npx webpack命令后缀添加--watch 实现编译时自动检测文件变化(需要手动刷新浏览器)

本地安装: npm install webpack-dev-server -D

修改配置文件,告知 dev server,从什么位置查找文件:

javascript 复制代码
module.exports={
	...
    devServer:{
      static:'./dist'
    },
}

以上配置告知 webpack-dev-server,将 dist 目录下的文件作为 web 服务的根目录。

执行命令:

bash 复制代码
npx webpack serve --open
  • 执行 npx webpack --dev-server --watch
  • 也可以 npx webpack serve

webpack-dev-server 在编译之后不会写入到任何输出文件。而是将 bundle 文件保留在内存中,然后将它们 serve 到 server 中,就好像它们是挂载在 server 根路径上的真实文件一样。

六、自动引入资源

1.什么是插件?

随着应用程 序增长,如果继续手动管理,就会变得困难起来。然而,webpack在打包流程中产生的编译过程,可以借助插件实现某些功能,通过一些插件可以使这个过程更容易管控。

插件是 webpack 的核心功能。插件可以用于执行一些特定的任务,包括:打包优化,资源管理,注入环境变量等。Webpack自身也是构建于你在webpack 配置中用到的相同的插件系统之上。

想要使用一个插件,你只需要 require() 它,然后把它添加到 plugins 数组中。 多数插件可以通过选项(option)自定义。你也可以在一个配置文件中因为不同目的而 多次使用同一个插件,这时需要通过使用 new 操作符来创建一个插件实例。

2.使用html-webpack-plugin

本地安装:

javascript 复制代码
npm install html-webpack-plugin -D

config中添加配置:

javascript 复制代码
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports={
  entry:'./src/index.js',
  output:{
    // 打包前清理 dist 文件夹
    clean: true,
    filename:'bundle.js',
    path:path.resolve(__dirname,'./dist')
  },
  mode:'development',
  devtool: 'inline-source-map' ,
  //插件配置集合
  plugins:[
    new HtmlWebpackPlugin()
  ],
}

new HtmlWebpackPlugin() 中也可以添加配置项

javascript 复制代码
    plugins:[
      ...
      new HtmlWebpackPlugin({
        template:'./index.html',//指向的html
        filename:'app.html',//被打包后的html文件名
        inject:'body'// js打包后的生成位置
      }),
    ]

七、引入资源

1.Resource-发送单独文件并导出Url

修改webpack.config.js配置:

javascript 复制代码
const path = require('path')
 const HtmlWebpackPlugin = require('html-webpack-plugin')
 module.exports = {
 //...
 // 配置资源文件
 module: {
     rules: [{
     test: /\.png/,
     type: 'asset/resource'
    }]
 },
 //...
}

在module中设置文件类型及资源类型,output端中增加打包后的资源文件夹配置:

javascript 复制代码
    output:{
      filename:'bundle.js',
      path:path.resolve(__dirname,'./dist'),
      clean:true,
      assetModuleFilename:'images/[contenthash][ext]',
    },

2.inline-导出资源的Data Url

javascript 复制代码
module:{
	rules:[
		...
		{
		test:/\.svg$/,
		type:'asset/inline'
		}
	]
}

实际展示出的文件的url为base64格式

3.source-导出资源的源代码

javascript 复制代码
module:{
	rules:[
		...
		{
		test:/\.txt$/,
		type:'asset/source'
		}
	]
}

4.asset-通用资源类型-自动选择data url或源文件

javascript 复制代码
module:{
	rules:[
		...
        {
          test:/\.jpg$/,
          type:'asset',
          parser:{
            dataUrlCondition:{
              maxSize:4 * 1024 * 1024 //当图片大小大于4M时生成资源文件,否则为base64 url
            }
          }
        }
	]
}

可以通过配置临界值实现自动切换资源格式,更加灵活可控(默认大小为8kb)

八、管理资源

除了以上四个资源模块,我们还可以通过webpack-loader引入其他类型的文件。webpack只能解析JS、JSON文件,这是 webpack 开箱可用的自带能力。loader 让 webpack 能够去处理其他类型的文件,并将它们转换为有效模块,通过loader可以实现在js上加载css等其他文件。

在 webpack 的配置中,loader 有两个属性:

    1. test 属性,识别出哪些文件会被转换。
    1. use 属性,定义出在进行转换时,应该使用哪个 loader。
javascript 复制代码
 const path = require('path');
 module.exports = {
     output: {
         filename: 'my-first-webpack.bundle.js',
     },
     module: {
         rules: [{ test: /\.txt$/, use: 'raw-loader' }],
     },
 };
//以上配置中,对一个单独的 module 对象定义了 rules 属性,里面包含两个必须属性:
//test 和 use。这告诉 webpack 编译器(compiler) 如下信息:
//"嘿,webpack 编译器,当你碰到「在 require()/import 语句中被解析为'.txt' 的路径」时,
//在你对它打包之前,先 use(使用) raw-loader 转换一下。

1.加载CSS

  • 本地安装css-loader及style-loader npm install css-loader -D
  • 并在规则中添加css
javascript 复制代码
module:{
	rules:[
		...
        {
          test:/\.css$/,
          use:['style-loader','css-loader']
        }
	]
}

应保证 loader 的先后顺序: 'style loader' 在前,而 'css loader' 在后。如果 不遵守此约定,webpack 可能会抛出错误。

2.抽离和压缩CSS

在多数情况下,我们也可以进行压缩CSS,以便在生产环境中节省加载时间,同时还可以将CSS文件抽离成一个单独的文件。

抽离

实现这个功能,需要mini-css-extract-plugin这个插件来帮忙,本插件会将 CSS 提取到单独的文件中,为每个包含 CSS 的 JS 文件创建一个 CSS 文件,并且支持 CSS 和 SourceMaps 的按需加载。

  • 本地安装mini-css-extract-plugin : npm install mini-css-extract-plugin -D
  • 基于webpack5构建,需要webpack5环境下才能正常使用
  • 和style-loader不同,MiniCssExtractPlugin会生成link标签从而引入外部css
html 复制代码
<!DOCTYPE html>
 <html lang="en">
 <head>
 <meta charset="UTF-8">
 <meta http-equiv="X-UA-Compatible" content="IE=edge">
 <meta name="viewport" content="width=device-width, initial scale=1.0">
 <title>Webpack5学习指南</title>
 <link href="main.css" rel="stylesheet"></head>
 <body>
 <script defer src="bundle.js"></script></body>
 </html>

将 loader 与 plugin 添加到你的 webpack 配置文件中。通过require引入后在plugins里添加引用,同时在原有css规则中替换掉style-loader,npx webpack打包后生效。

javascript 复制代码
plugins:[
      new HtmlWebpackPlugin({
        template:'./index.html',
        filename:'app.html',
        inject:'body'
      }),
      new MiniCssExtractPlugin()
]
javascript 复制代码
module:{
	rules:[
		...
		{
          test:/\.css$/,
          use:[MiniCssExtractPlugin.loader,'css-loader']
        }
	]
}

在plugins里调用MiniCssExtractPlugin时也可以添加filename来自定义生成目录及文件名。

contenthash会随机生成字符串的文件名:

javascript 复制代码
    plugins:[
    	  ...
          new MiniCssExtractPlugin({
            filename:'styles/[contenthash].css'
          }),
    ],

压缩

  • 本地安装css-minimizer-webpack-plugin: npm install css-minimizer-webpack-plugin -D
  • 和其他插件不同,需在optimization中添加配置,并且mode要切换为production
javascript 复制代码
//优化配置
optimization:{
      minimizer:[
        new CssMinimizerPlugin(),
      ],
}
javascript 复制代码
//生产模式
mode:'production'

3.加载images图像

假如,现在我们正在下载 CSS,但是像 background 和 icon 这样的图像,要如何处理呢?在 webpack 5 中,可以使用内置的 Asset Modules,可以将这些内容混入我们的系统中,在 css文件里也可以直接引用文件,修改style.css 和入口 index.js,依赖于资源模块

javascript 复制代码
module:{
      rules:[
        ...
        {
          test:/\.png$/,
          type:'asset/resource',
          generator:{
            filename:'images/[contenthash][ext]'
          }
        }
      ]
}

打包后:

javascript 复制代码
import './style.css'

4.加载fonts字体

?使用 Asset Modules 可以接收并加载任 何文件,然后将其输出到构建目录。这就是说,我们可以将它们用于任何类型的文 件,也包括字体。

javascript 复制代码
module:{
	rules:[
		...
		{
          test:/\.(woff|woff2|eot|ttf|otf)$/,
          type:'asset/resource'
        }
	]
}

在css中引用并创建字体图标库

javascript 复制代码
@font-face {
  font-family: 'iconfont';
  src: url('../icon/iconfont.ttf') format('truetype');
}

5.加载数据

可以加载的有用资源还有数据,如 JSON 文件,CSV、TSV 和 XML。类似于 NodeJS,JSON 支持实际上是内置的,也就是说 import Data from loader 和 './data.json' 默认将正常运行。

  • 根据数据格式安装不同的loader模块,比如要导入 CSV、TSV 和 XML,可以使用csv-loader和xml-loader。
  • 本地安装 csv-loader xml-loader : npm install csv-loader xml-loader -D
  • 在项目中引入csv、xml等数据格式时会自动转化为对象、数组等JS数据格式
javascript 复制代码
module:{
	rules:[
		...
		{
          test:/\.(csv|tsv)$/,
          use:'csv-loader'
        },
        {
          test:/\.xml$/,
          use:'xml-loader'
        }
	]
}
//现在,你可以 import 这四种类型的数据(JSON, CSV, TSV, XML)中的任何一种,
//所导入的 Data 变量,将包含可直接使用的已解析 JSON

6.自定义JSON模块parser

通过使用自定义 parser 替代特定的webpack loader,可以将任何toml、yaml或json5文件作为JSON导入。

本地安装 toml yaml json5 :npm install toml yaml json5 -D

引入模块:

javascript 复制代码
const toml = require('toml')
const yaml = require('yaml')
const json5 = require('json5')

添加规则:

javascript 复制代码
module:{
	rules:[
		...
		{
          test:/\.xml$/,
          use:'xml-loader'
        },
        {
          test:/\.toml$/,
          type:'json',
          parser:{
            parse:toml.parse
          }
        },
        {
          test:/\.yaml$/,
          type:'json',
          parser:{
            parse:yaml.parse
          }
        },
        {
          test:/\.json5$/,
          type:'json',
          parser:{
            parse:json5.parse
          }
        }
	]
}

7.使用babel-loader

webpack 自身可以自动加载JS文件,就像加载JSON文件一样,无需任何 loader。但加载的JS文件会原样输出,即使你的JS文件里包含ES6+的代码,也不会做任何转化。这时我们就需要Babel来帮忙。Babel 是一个 JavaScript 编译器,可以将 ES6+转化成ES5,将js代码转化以提供兼容度支持。在Webpack里使用Babel,需要使用babel-loader。

  • babel-loader: 在webpack里应用 babel 解析ES6的桥梁
  • @babel/core: babel核心模块
  • @babel/preset-env: babel预设,一组 babel 插件的集合

本地安装: babel-loader @babel/core @babel/preset-env : npm install babel-loader @babel/core @babel/preset-env -D

在 webpack 配置中,添加babel-loader到module中:

javascript 复制代码
module:{
	rules:[
		...
		{
          test:/\.js$/,
          exclude:/node_modules/,
          use:{
            loader: 'babel-loader',
            options:{
              presets:['@babel/preset-env']
            }
          }
        }
	]
}

注意:

  • 需在exclude中排除node_modules包
  • 如需兼容async/await语法则还需要添加regeneratorRuntime模块。regeneratorRuntime是webpack打包生成的全局辅助函数,由babel生成,用于兼容async/await的语法。
  • npm install --save @babel/runtime【这个包中包含了regeneratorRuntime,运行时需要】
  • npm install --save-dev @babel/plugin-transform-runtime【这个插件会在需要regeneratorRuntime的地方自动require导包,编译时需要】
javascript 复制代码
module:{
	rules:[
		...
		{
          test:/\.js$/,
          exclude:/node_modules/,
          use:{
            loader: 'babel-loader',
            options:{
              presets:['@babel/preset-env'],
              plugins:[
                [
                  '@babel/plugin-transform-runtime'
                ]
              ]
            }
          }
        }
	]
}

九、代码分离

代码分离是 webpack 中最引人注目的特性之一。此特性能够把代码分离到不同的 bundle 中,然后可以按需加载或并行加载这些文件。项目中若存在多个入口文件时,则需要代码分离;若存在多个模块共用的代码时,也需要分离代码来防止重复打包。

代码分离可以用于获取更小的 bundle,以及控制资源加载优先级,如果使用合理,会极大影响加载时间。常用的代码分离方法有三种:

  • 入口起点:使用 entry 配置手动地分离代码。
  • 防止重复:使用 Entry dependencies 或者 SplitChunksPlugin 去重和分离 chunk。
  • 动态导入:通过模块的内联函数调用来分离代码。

1.入口起点

javascript 复制代码
module.exports = {
 entry: {
    //将其他包命名在entry中
     index: './src/index.js',
     another: './src/another-module.js',
 },
 output: {
    //并在output输出端中直接配置[name]对应entry中的key
     filename: '[name].bundle.js'
 //...
 },
 //...}

执行npx webpack后便会提示相应包名被分别打包且都被引入

存在一些隐患:

  • 如果入口 chunk 之间包含一些重复的模块,那些重复模块都会被引入到各个 bundle 中。
  • 这种方法不够灵活,并且不能动态地将核心应用程序逻辑中的代码拆分出来。

2.防止重复

Entry dependencies

入口依赖,当图中两个模块共有lodash时,会抽离出来并取名为lodash。配置 dependOn option 选项,这样可以在多个 chunk 之间共享模块:

javascript 复制代码
    entry:{
          index:{
            import:'./src/index.js',
            dependOn:'shared'
          },
          another:{
            import:'./src/another-module.js',
            dependOn:'shared'
          },
          shared:'lodash'
          index:'./src/index.js',
          another:'./src/another-module.js'
    },

打包后多出的shared.budle.js即为定义中的模块,此时lodash是共用的,做到了模块的去重和分离

SplitChunksPlugin

可以将公共的依赖模块提取到已有的入口 chunk 中,或者提取到一个新生成的 chunk。

配置时依旧可以采用独立命名

javascript 复制代码
    entry:{
        index:'./src/index.js',
        another:'./src/another-module.js'
    },

在 optimization 优化配置项中添加 splitChunks

javascript 复制代码
    optimization:{
        ...
        splitChunks:{
          chunks:'all'
        }
     },

执行之后可以发现,使用 optimization.splitChunks 配置选项之后,index.bundle.js 和 another.bundle.js 中已经移除了重复的依赖模块。注意,插件将 lodash分离到单独的chunk,并且将其从main bundle 中移除,减轻了大小。

3.动态导入

当涉及到动态代码拆分时,webpack 提供了两个类似的技术。

  • 第一种,也是推荐选择的方式是,使用符合ECMAScript提案的import()语法来实现动态导入,且不影响其他模块抽离方式。

async-module.js 假设为功能模块:

javascript 复制代码
// async-module.js
function getComponent(){
  return import('lodash').then(({default:_})=>{
    const element = document.createElement('div')
    element.innerHTML = _.join(['hello','webpack'],' ')
    return element
  })
}

getComponent().then((element)=>{
  document.body.appendChild(element)
})

在入口文件中导入:

javascript 复制代码
import './async-module'
  • 第二种,则是webpack的遗留功能,使用 webpack 特定的 require.ensure。

4.懒加载

懒加载或按需加载,是一种很好的优化网页或应用的方式。这种方式实际上是先把代码在一些逻辑断点处分离开,然后在一些代码块中完成某些操作后,立即引用或即将引用另外一些新的代码块。这样加快了应用的初始加载速度,减轻了它的总体体积,因为某些代码块可能永远不会被加载。

依旧通过import直接引入模块,区别在于:何时调用何时加载模块

javascript 复制代码
const button = document.createElement('button')
 button.textContent = '点击执行加法运算'
 button.addEventListener('click', () => {
 import(/* webpackChunkName: 'math' */ './math.js').then(({ add 
}) => {
 console.log(add(4, 5))
 })
})
 document.body.appendChild(button)

加上注释 webpackChunkName:'模块名' 后,可以定义打包后的模块名

javascript 复制代码
import(/* webpackChunkName:'math' */'./math.js')

第一次加载完页面, math.bundle.js不会加载,当点击按钮后,才加载 math.bundle.js文件。

5.预获取/预加载模块

在声明import时,使用下面这些内置指令,可以让webpack输出"resource hint(资源提示)",来告知浏览器:

  • prefetch(预获取):将来某些导航下可能需要的资源 ,即在浏览器网络空闲时再获取资源
  • preload(预加载):当前导航下可能需要资源,和懒加载效果类似

prefetch

依旧在import引入时的注释中添加

javascript 复制代码
const button = document.createElement('button')
button.textContent = '点击执行加法运算'
button.addEventListener('click',()=>{
  import(/* webpackChunkName:'math',webpackPrefetch:true */'./math.js').then(({add})=>{
    console.log(add(4,5));
  })
})
document.body.appendChild(button)

加上注释 webpackPrefetch: true 后,可以告诉 webpack 执行预获取。会生成<link rel="prefetch" href="main.js">并追加到页面头部,指示着浏览器在闲置时间预取 math.js 文件。

我们发现,在还没有点击按钮时,math.bundle.js就已经下载下来了。同时,在 app.html可以看到,点击按钮,会立即调用已经下载好的 math.bundle.js文件中的 add 方法。

preload

  • 与 prefetch 指令相比,preload 指令有许多不同之处:
  • preload chunk 会在父 chunk 加载时,以并行方式开始加载。prefetch chunk 会在父 chunk 加载结束后开始加载。
  • preload chunk 具有中等优先级,并立即下载。prefetch chunk 在浏览器闲置时下载。
  • preload chunk 会在父 chunk 中立即请求,用于当下时刻。prefetch chunk 会用于未来的某个时刻。
  • 浏览器支持程度不同。

十、缓存

打包后的dist文件部署到服务器以后便能被浏览器客户端所访问,因浏览器的解析特性会优先选择缓存资源,我们要确保文件发生更新时浏览器能识别到,所以需要对输出文件的文件名做处理。

1.输出文件的文件名

我们可以通过替换 output.filename 中的 substitutions 设置,来定义输出文件的名称。webpack提供了一种使用 **substitution (可替换模板字符串)**的方式,通过带括号字符串来模板化文件名,其中,[contenthash] substitution 将根据资源内容创建出唯一的hash。当资源内容发生变化时, [contenthash] 也会发生变化。

javascript 复制代码
module.exports = {
 output: {
 filename: '[name].[contenthash].js',
 },
};

2.缓存第三方库

将第三方库 (library) (例如 lodash) 提取到单独的 vendor chunk 文件中,是比较推荐的做法,因为第三方库文件很少像本地源码那样频繁修改,所以我们可以利用client的长效缓存机制,命中缓存来消除请求,并减少向server 获取资源,同时保证client和server的代码一致。

在 optimization.splitChunks 添加cacheGroups 参数并构建:

javascript 复制代码
	optimization:{
      ...
      splitChunks:{
        ...
        cacheGroups:{
          vendor:{
            test:/[\\/]node_modules[\\/]/,
            name:'vendors',
            chunks:'all'
          }
        },
      }
    }

3.将js文件放到一个文件夹中

目前,全部 js 文件都在 dist文件夹根目录下,我们尝试把它们放到一个文件夹中, 这个其实也简单,在输出配置中修改 filename,即output.filename加上scripts前缀即可汇总js文件至指定文件夹下生成。

javascript 复制代码
output:{
    filename:'scripts/[name].[contenthash].js',
    ...
}

十一、拆分开发环境和生产环境配置

1.公共路径

publicPath 配置选项在各种场景中都非常有用,我们可以通过它来指定应用程序中所有资源的基础路径。

javascript 复制代码
output:{
    filename:'scripts/[name].[contenthash].js',
    path:path.resolve(__dirname,'./dist'),
    clean:true,
    assetModuleFilename:'images/[contenthash][ext]',
    publicPath:'http://localhost:8080/'
},
  • 基于环境设置

在开发环境中,我们通常有一个assets/ 文件夹,它与索引页面位于同一级 别。这没太大问题,但是,如果我们将所有静态资源托管至 CDN,然后想在生产环境中使用呢? 想要解决这个问题,可以直接使用一个 environment variable(环境变量)。假设我们有一个变量 ASSET_PATH:

javascript 复制代码
import webpack from 'webpack';
 // 尝试使用环境变量,否则使用根路径
const ASSET_PATH = process.env.ASSET_PATH || '/';
export default {
 output: {
     publicPath: ASSET_PATH,
 },
 plugins: [
    // 这可以帮助我们在代码中安全地使用环境变量
    new webpack.DefinePlugin({
     'process.env.ASSET_PATH': JSON.stringify(ASSET_PATH),
    }),
  ],
};
  • Automatic publicPath

有可能你事先不知道 publicPath 是什么,webpack 会自动根据 import.meta.url、 document.currentScript、 script.src 或者 self.location 变量设置 publicPath。你需要做的是将 output.publicPath 设为 ' auto':

javascript 复制代码
module.exports = {
 output: {
 publicPath: 'auto',
 },
};

注意:在某些情况下不支持 document.currentScript,例如:IE 浏览器,不得不引入一个 polyfill,例如 currentScript Polyfill。

2.环境变量

想要消除 webpack.config.js 在开发环境和生产环境之间的差异,需要环境变量(environment variable)。webpack命令行环境配置的 --env 参数,可以允许传入任意数量的环境变量。而webpack.config.js 中可以访问到这些环境变量。例如,--env production 或--env goal=local。

  • npx webpack --env production 可以通过命令传入变量区分环境

对于我们的 webpack 配置,有一个必须要修改之处。通常,module.exports 指向配置对象。要使用 env 变量,你必须将 module.exports(抛出对象)转换为函数后即可动态传参

javascript 复制代码
//...
 module.exports = (env) => {
 return {
 //...
 // 根据命令行参数 env 来设置不同环境的 mode
 mode: env.production ? 'production' : 'development',
 //...
 }
}

3.拆分配置文件

生产环境和开发环境使用的是一个配置文件,我们需要将这两个文件单独放到不同的配置文件中。如 webpack.config.dev.js(开发环境配置)和 webpack.config.prod.js(生产环境配置)。在项目根目录下创建一个配置文件夹config 来存放他们。

  • 开发环境:执行 npx webpack serve -c config文件路径
  • 生产环境:执行 npx webpack -c config文件路径

4.npm命令

每次打包或启动服务时,都需要在命令行里输入一长串的命令。我们将父目录的 package.json、 node_modules 与 package-lock.json拷贝到当前目录下,

可以在package.json中配置命令来方便使用,配置 npm 脚本来简化命令行的输入。

javascript 复制代码
"scripts": {
    "start": "webpack serve -c ./config/webpack.config.js --env development",
    "build": "webpack -c ./config/webpack.config.js --env production"
},
  • 开发环境运行脚本:npm run start
  • 生产环境运行脚本:npm run build

关闭生产环境打包的文件过大警告提示:

javascript 复制代码
performance:{
    hints:false
}

5.提取公共配置

拆分出的配置文件存在大量重复配置代码时,可以单独创建一个公共配置文件,手动的将这些重复的代码单独提取到一个文件里。创建 webpack.config.common.js,配置公共的内容。

6.合并配置文件

配置文件拆分好后,我们又需要保证配置合并没用的问题,这时候就需要webpack-merge这个工具。

  • 安装 npm install webpack-merge -D
  • 创建新的config文件用以合并
javascript 复制代码
const {merge} = require('webpack-merge')

const commonConfig = require('./webpack.config.common')
const productionConfig = require('./webpack.config.prod')
const developmentConfig = require('./webpack.config.dev')

module.exports=((env)=>{
  switch(true){
    case env.development:
      return merge(commonConfig,developmentConfig)
    case env.production:
      return merge(commonConfig,productionConfig)
    default:
      return new Error('No matching configuration was found')
  }
})
相关推荐
学代码的小前端几秒前
0基础学前端-----CSS DAY9
前端·css
joan_854 分钟前
layui表格templet图片渲染--模板字符串和字符串拼接
前端·javascript·layui
程序猿进阶5 分钟前
深入解析 Spring WebFlux:原理与应用
java·开发语言·后端·spring·面试·架构·springboot
Laofanqie66631 分钟前
电脑丢失bcrypt.dll文件是什么原因?找不到bcrypt.dll文件修复办法来啦!
经验分享·电脑
亦世凡华、34 分钟前
MySQL--》如何在MySQL中打造高效优化索引
数据库·经验分享·mysql·索引·性能分析
m0_7482361136 分钟前
Calcite Web 项目常见问题解决方案
开发语言·前端·rust
Watermelo6171 小时前
详解js柯里化原理及用法,探究柯里化在Redux Selector 的场景模拟、构建复杂的数据流管道、优化深度嵌套函数中的精妙应用
开发语言·前端·javascript·算法·数据挖掘·数据分析·ecmascript
m0_748248941 小时前
HTML5系列(11)-- Web 无障碍开发指南
前端·html·html5
小奥超人1 小时前
RAR压缩算法的文件修复功能详解
windows·经验分享·winrar·办公技巧
m0_748235611 小时前
从零开始学前端之HTML(三)
前端·html