初识webpack(一)

128 阅读7分钟

这是我参与2022首次更文挑战的第17天,活动详情查看:2022首次更文挑战」。

什么是webpack?

webpack 是一个用于现代 JavaScript 应用程序的 静态模块打包工具。当 webpack 处理应用程序时,它会在内部从一个或多个入口点构建一个 依赖图(dependency graph),然后将你项目中所需的每一个模块组合成一个或多个 bundles,它们均为静态资源,用于展示你的内容。

基于nodejs的构建工具,万物皆模块

不同版本

  • 5.x 2020 年 10 月 10 日,Webpack 正式发布了 5.0 版本。
    Webpack 5 至少需要 Node.js 10.13.0 (LTS);
    安装 webpack核心包, webpack-cli命令行工具
  • 4.x Node.js 安装升级到 Node.js v6 或更高版本。
    CLI 已移至单独的包:webpack-cli
    安装 webpack,webpack-cli
  • 3.x webpack

webpack安装

npm init -y // 初始化npm配置文件
npm install --save-dev webpack // 安装核心库
npm install --save-dev webpack-cli // 安装命令行工具

// 安装最新的4.x稳定版本 
npm i -D webpack@4.44.0

// 安装指定版本
npm i -D webpack@<version>

4.x 零配置

不是很实用,还是需要写配置,但是配置门槛低了,(以下内容都是针对4x)

牛刀小试
不写配置文件,在项目下创建src文件夹,src下创建index.js文件。

执行 npx webpack 命令打包,会看到命令行里输出如下内容:

image.png

Hash: 本次打包唯一标志hash

Version: 当前使用的webpack版本

Time: 本次打包消耗的时间

Built at: 构建资源

Asset(输出资源)Size(文件大小)ChunksChunk Names
main.js952 bytes0 [emitted](emitted是一个标识:生成的意思)main

Entrypoint main = main.js 表示入口点是main.js文件
[0] ./src/index.js 22 bytes {0} [built] 这个的意思是当前打包输出的mian.js文件的内容来源于src下的index.js

最后有个黄色警告,翻译过来就是:
尚未设置“模式”选项,对于此值,webpack将退回到“生产”。将“模式”选项设置为“开发”或“生产”,为每个环境启用默认值。
还可以将其设置为“无”以禁用任何默认行为。了解更多信息:webpack.js.org/configurati…

简单说就是,如果没有设置mode配置项, 默认是production生产模式。

生产模式,就是项目真实上线后的,webpack会对代码做一些优化,比如代码压缩。

webpack配置文件

默认配置文件名:webpack.config.js, 在根目录下创建

如何配置:(还原零配置文件)
配置文件就是一个对象,webpack基于nodejs核心api

// webpack.config.js
const path = require("path");
module.exports = {
    entry: "./src/index.js", // 执行打包任务的入口位置,默认在src/index.js
    mode: "production", // 打包模式,默认production; 其他模式:development 、 none
    output: { // 输出资源信息(存储位置,文件名)
        path: path.resolve(__dirname, "./dist"), // 打包文件的存储位置,绝对路径
        filename: "main.js" // 打包文件名称
    }
}

再执行npx webpack: image.png 可以发现黄色警告没有了, 因为在webpack.config.js中设置了mode配置项。

如果自己创建了webapck配置文件,就用自定义的,如果没有就用默认的。

修改配置文件及index.js,如下:

// webpack.config.js
const path = require("path");
module.exports = {
    entry: "./src/index.js", // 执行打包任务的入口位置,默认在src/index.js
    mode: "development", // 打包模式,默认production; 其他模式:development 、 none
    output: { // 输出资源信息(存储位置,文件名)
        path: path.resolve(__dirname, "./dist"), // 打包文件的存储位置,绝对路径
        filename: "main.js" // 打包文件名称
    }
}
// src/index.js
import other from './other'
console.log('webpack')
// src/other.js
export const name = 'otherjs';
  • 如果设置modedevelopment后可以看到编译打包的main.js变大了。
  • 两个js之间存在依赖关系时。(从入口文件index.js开始分析依赖关系),构建输出如下 image.png

打包后的main.js

image.png

如图所示,代码折叠起来后,从代码结构中可以明显看到,这实际上就是一个立即执行函数

(function(形参){
})(参数)

自定义配置文件名

除了创建默认配置文件名的方式,也可以随意自定义配置文件名。如下:

// 自定义一个叫做test.config.js的文件
const path = require("path");
module.exports = {
    entry: "./src/index.js", // 执行打包任务的入口位置,默认在src/index.js
    mode: "development", // 打包模式,默认production; 其他模式:development 、 none
    output: { // 输出资源信息(存储位置,文件名)
        path: path.resolve(__dirname, "./test"), // 打包文件的存储位置,绝对路径
        filename: "test.js" // 打包文件名称
    }
}
// 在package.json文件中,添加运行脚本指令webpackTest
"scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "webpackTest": "webpack --config ./test.config.js"
  },

--config: 指定执行配置文件
在命令行中执行 npm run webpackTest,结果如下:

image.png

npx webpacknpm run webpackTest,都是找当前项目依赖webpack运行(也就是node_modules下的)。

webpack的核心概念

entry

执行打包任务的入口位置,默认在src/index.js;它支持字符串,数组,对象类型的参数。

module.exports = {
    // spa(单入口)  mpa(多入口)
    // 支持string array object
    // entry: "./src/index.js",
    entry: {
        main: "./src/index.js"
    },
     // 上面,字符串格式与对象格式是等价的。
    mode: "development", // 打包模式,默认production; 其他模式:development 、 none
    output: { // 输出资源信息(存储位置,文件名)
        path: path.resolve(__dirname, "./dist"), // 打包文件的存储位置,绝对路径
        filename: "[name].js" // 打包文件名称  [name]占位符
    }
}

上面代码中,打包输出的文件名使用[name]占位符,entry配置中main,输出文件名就是main.js;字符串格式的默认是main,对象格式可以自定义修改为其他名称。(如果filename值固定,不使用占位符的话,就按固定的来,通常不会这么做的。)

修改一下上面栗子,改成多入口的话,会是什么效果?

const path = require("path");
module.exports = {
    entry: {
        main: "./src/index.js",
        login: "./src/login.js",
        list: "./src/list.js"
    },
    mode: "development", // 打包模式,默认production; 其他模式:development 、 none
    output: { // 输出资源信息(存储位置,文件名)
        path: path.resolve(__dirname, "./dist"), // 打包文件的存储位置,绝对路径
        filename: "index.js" // 打包文件名称  [name]占位符
    }
}

上面栗子中,entry改成多文件入口,这时output.filename 没有使用[name]占位符,来看下会怎么样:

image.png 如图上所示,会报错,冲突:多个块将资源发送到同一文件名index.js

修改为filename: "[name].js"就正常了。 image.png

理解chunk

组块,代码片段。
从上面内容中,可以看到,chunks,chunknames字样, 发现,modeproduction时,命令行输出资源列表中的chunks显示0,1,2modedevelopment时显示组块名称。

  • bundle, 构建产生的资源文件称为bundle文件
  • bundle 对应一个 chunks
  • 一个chunks对应至少一个module
  • 一个module至少一个chunk

plugins插件

  • 作用于webpack打包整个过程
  • webpack的打包过程是有(生命周期概念)钩子 plugin 可以在webpack运行到某个阶段的时候,帮你做一些事情,类似于生命周期的概念 扩展插件,在 Webpack 构建流程中的特定时机注入扩展逻辑来改变构建结果或做你想要的事情。 作用于整个构建过程
html-webpack-plugin

html-webpack-plugin为例,先安装插件,这里需要注意的是,这个插件默认适配的版本是webpack5x,要安装对应webpack4x版本的html-webpack-plugin,只需要在后面加一个@4,如下:

npm install html-webpack-plugin@4 -D

html-webpack-plugin插件作用:自动生成html文件,引入bundle文件,压缩html等

安装完插件后,如何使用?

  • 引入插件
  • 配置plugins
const path = require("path");
const htmlWebpackPlugin = require('html-webpack-plugin'); // 引入插件
module.exports = {
    // entry: "./src/index.js", // 执行打包任务的入口位置,默认在src/index.js
    entry: {
        main: "./src/index.js",
        login: "./src/login.js",
        list: "./src/list.js"
    },
    mode: "none", // 打包模式,默认production; 其他模式:development 、 none
    output: { // 输出资源信息(存储位置,文件名)
        path: path.resolve(__dirname, "./dist"), // 打包文件的存储位置,绝对路径
        filename: "[name].js" // 打包文件名称  [name]占位符
    },
    plugins: [
        new htmlWebpackPlugin()
    ]
}

配置完成后,运行webpack, 构建结果:

image.png

html-webpack-plugin配置:

plugins: [
    new htmlWebpackPlugin({
      filename: "index.html", //输出的 HTML 文件名,默认是 index.html, 也可以直接配置带有子目录。
      template: "./src/index.html", //模板文件路径,支持加载器
      chunks: ["main", "login"], // 精准加载指定组块
    }),
    new htmlWebpackPlugin({ // 多页面
      filename: "login.html", 
      template: "./src/index.html", 
      chunks: ["login"]
    }),
    new htmlWebpackPlugin({ 
      filename: "list.html", 
      template: "./src/index.html", 
      chunks: ["list"]
    })
]