Webpack工程化实战基础入门

318 阅读9分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第9天,点击查看活动详情

1. webpack简介

Webpack 是⼀个现代 JavaScript 应⽤程序的静态模块打包器(module bundler)。当 webpack 处理应⽤程序时,它会递归地构建⼀个依赖关系图(dependency graph),其中包含应⽤程序需要的每个模块,然后将所有这些模块打包成⼀个或多个 bundle。

Webpack是⼀个打包模块化JavaScript的⼯具,它会从⼊⼝模块出发,识别出源码中的模块化导⼊语句,递归地找出⼊⼝⽂件的所有依赖,将⼊⼝和其所有的依赖打包到⼀个单独的⽂件中。

是⼯程化、⾃动化思想在前端开发中的体现。

2. webpack 安装

环境准备

版本参考官⽹发布的最新版本,可以提升webpack的打包速度。

安装⽅式

  • 局部安装(推荐)
npm init -y # 初始化npm配置⽂件
npm install --save-dev webpack # 安装核⼼库
npm install --save-dev webpack-cli # 安装命令⾏⼯具

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

  • 全局安装(不推荐)
# 安装webpack V4+版本时,需要额外安装webpack-cli
npm install webpack webpack-cli -g

# 检查版本
webpack -v

# 卸载
npm uninstall webpack webpack-cli -g

全局安装webpack,这会将你项⽬中的webpack锁定到指定版本,造成不同的项⽬中因为webpack依赖不同版本⽽导致冲突,构建失败。

3. 启动webpack

webpack默认配置

  • webpack 默认⽀持 JS 模块和 JSON 模块
  • ⽀持 CommonJS Es moudule AMD 等模块类型
  • webpack ⽀持零配置使⽤, 但是很弱,稍微复杂些的场景都需要额外扩展

准备执⾏构建

  1. 新建src⽂件夹
  2. 新建src/index.js、src/index.json、src/other.js
// index.js
const json = require("./index.json"); //commonJS
import { add } from "./other.js"; //es module

console.log(json, add(2, 3));

// index.json
{
  "name": "JSON"
}

// other.js
export function add(n1, n2) {
  return n1 + n2;
}

执⾏构建

# npx⽅式
npx webpack

# npm script
npm run test

修改package.json⽂件:

"scripts": {
    "test": "webpack"
},

原理就是通过shell脚本在node_modules/.bin⽬录下创建⼀个软链接。

构建成功

我们会发现⽬录下多出⼀个 dist ⽬录,⾥⾯有个 main.js ,这个⽂件是⼀个可执⾏的JavaScript⽂件,⾥⾯包含 webpackBootstrap 启动函数。

默认配置

const path = require("path");

module.exports = {
    // 必填 webpack执⾏构建⼊⼝
    entry: "./src/index.js",
    output: {
        // 将所有依赖的模块合并输出到main.js
        filename: "main.js",
        // 输出⽂件的存放路径,必须是绝对路径
        path: path.resolve(__dirname, "./dist")
    }
};

4. webpack配置核⼼概念

  • chunk:指代码块,⼀个 chunk 可能由多个模块组合⽽成,也⽤于代码合并与分割。
  • bundle:资源经过Webpack 流程解析编译后最终结输出的成果⽂件。
  • entry:顾名思义,就是⼊⼝起点,⽤来告诉webpack⽤哪个⽂件作为构建依赖图的起点。webpack会根据entry递归的去寻找依赖,每个依赖都将被它处理,最后输出到打包成果中。
  • output:output配置描述了webpack打包的输出配置,包含输出⽂件的命名、位置等信息。
  • loader:默认情况下,webpack仅⽀持 .js .json ⽂件,通过loader,可以让它解析其他类型的⽂件,充当翻译官的⻆⾊。理论上只要有相应的loader,就可以处理任何类型的⽂件。
  • plugin:loader主要的职责是让webpack认识更多的⽂件类型,⽽plugin的职责则是让其可以控制构建流程,从⽽执⾏⼀些特殊的任务。插件的功能⾮常强⼤,可以完成各种各样的任务。

webpack.config.js 配置基础结构


module.exports = {
    entry: "./src/index.js", //打包⼊⼝⽂件
    output: "./dist", //输出结构
    mode: "production", //打包环境
    module: {
        rules: [
            //loader模块处理
            // webpack5中, 使用MiniCssExtractPlugin.loader代替style-loader
            {
                test: /\.css$/,
                use: ["style-loader", "css-loader"]
            },
            {
                // webpack5 识别静态文件
                test: /.(svg|eot|ttf|woff)$/,
                type: 'asset/resource',
              },
        ]
    },
    plugins: [new HtmlWebpackPlugin()] //插件配置
};

entry:

指定webpack打包⼊⼝⽂件。Webpack 执⾏构建的第⼀步将从 entry 开始,可抽象成输⼊:

// 单⼊⼝ SPA,本质是个字符串
entry:{
    main: './src/index.js'
}

// ==相当于简写===
entry:"./src/index.js"

// 多⼊⼝ entry是个对象
entry:{
    index:"./src/index.js",
    login:"./src/login.js"
}

output:

打包转换后的⽂件输出到磁盘位置。在 Webpack 经过⼀系列处理并得出最终想要的代码后输出结果。

output: {
    filename: "bundle.js",//输出⽂件的名称
    path: path.resolve(__dirname, "dist")//输出⽂件到磁盘的⽬录,必须是绝对路径
},

//多⼊⼝的处理
output: {
    filename: "[name][chunkhash:8].js",//利⽤占位符,⽂件名称不要重复
    path: path.resolve(__dirname, "dist")//输出⽂件到磁盘的⽬录,必须是绝对路径
},

mode

mode ⽤来指定当前的构建环境

  • production
  • development
  • none

设置mode可以⾃动触发webpack内置的函数,达到优化的效果, 开发阶段的开启会有利于热更新的处理,识别哪个模块变化;⽣产阶段的开启会有帮助模块压缩,处理副作⽤等⼀些功能。

loader

模块转换器。⽤于把模块原内容按照需求转换成新内容。webpack是模块打包⼯具,⽽模块不仅仅是js,还可以是css,图⽚或者其他格式。但是webpack默认只知道如何处理js和JSON模块,那么其他格式的模块处理,和处理⽅式就需要loader了。

常⻅的loader:

style-loader
css-loader
less-loader
sass-loader
ts-loader //将Ts转换成js
babel-loader //转换ES6、7等js新特性语法
file-loader //处理图⽚⼦图
eslint-loader

...

moudle

在 Webpack ⾥⼀切皆模块,⼀个模块对应着⼀个⽂件。Webpack 会从配置的 Entry 开始递归找出所有依赖的模块。当webpack处理到不认识的模块时,需要在webpack中的module处进⾏配置,当检测到是什么格式的模块,使⽤什么loader来处理。

loading可以是一个数组,['style-loader', 'css-loader'],解析顺序从右至左。

module:{
    rules:[
        {
            test:/\.xxx$/,//指定匹配规则
            use:{
                loader: 'xxx-load'//指定使⽤的loader
            }
        }
    ]
}

Plugins:webpack的扩展补充

作⽤于webpack打包整个过程。plugin 可以在webpack运⾏到某个阶段的时候,帮你做⼀些事情,类似于⽣命周期的概念。

  • HtmlWebpackPlugin

在打包结束后,⾃动⽣成⼀个html⽂件,并把打包⽣成的js模块引⼊到该html中:

const path = require("path");
const htmlWebpackPlugin = require("html-webpack-plugin");

module.exports = {
    ...
    plugins: [
        new htmlWebpackPlugin({
            title: "My App",
            filename: "app.html",
            template: "./src/index.html"
        })
    ]
};

//index.html
<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <meta http-equiv="X-UA-Compatible" content="ie=edge" />
        <title><%= htmlWebpackPlugin.options.title %></title>
    </head>
    <body>

        <div id="root"></div>
    </body>
</html>

配置项解释

- title: ⽤来⽣成⻚⾯的 title 元素
- filename: 输出的 HTML ⽂件名,默认是 index.html, 也可以直接配置带有⼦⽬录。
- template: 模板⽂件路径,⽀持加载器,⽐如 html!./index.html
- inject: true | 'head' | 'body' | false ,注⼊所有的资源到特定的 template 或者templateContent 中,如果设置为 true 或者 body,所有的 javascript 资源将被放置到 body元素的底部,'head' 将放置到 head 元素中。
- favicon: 添加特定的 favicon 路径到输出的 HTML ⽂件中。
- minify: {} | false , 传递 html-minifier 选项给 minify 输出
- hash: true | false, 如果为 true, 将添加⼀个唯⼀的 webpack 编译 hash 到所有包含的脚本和CSS ⽂件,对于解除 cache 很有⽤。
- cache: true | false,如果为 true, 这是默认值,仅仅在⽂件修改之后才会发布⽂件。
- showErrors: true | false, 如果为 true, 这是默认值,错误信息会写⼊到 HTML ⻚⾯中
- chunks: 允许只添加某些块 (⽐如,仅仅 unit test 块)
- chunksSortMode: 允许控制块在添加到⻚⾯之前的排序⽅式,⽀持的值:'none' | 'default' | {function}-default:'auto'
- excludeChunks: 允许跳过某些块,(⽐如,跳过单元测试的块)
  • clean-webpack-plugin

如何做到dist⽬录下某个⽂件或⽬录不被清空: 使⽤配置项:cleanOnceBeforeBuildPatterns:

cleanOnceBeforeBuildPatterns: ["/*", "!dll", "!dll/"]

!感叹号相当于exclude 排除,意思是清空操作排除dll⽬录,和dll⽬录下所有⽂件。 注意:数组列表⾥的“/*”是默认值,不可忽略,否则不做清空操作。

5. WebpackDevServer

提升开发效率的利器。每次改完代码都需要重新打包⼀次,打开浏览器,刷新⼀次,很麻烦,我们可以安装使⽤webpackdevserver来改善这块的体验。

npm install webpack-dev-server@3.11.0 -D

修改下package.json

"scripts": {
    "server": "webpack-dev-server"
},

配置 webpack.config.js:

devServer: {
    contentBase: "./dist",
    open: true,
    port: 8081,
    hot: true,
    // 热更新模块
    hotOnly:true
},

启动

npm run server

启动服务后,会发现dist⽬录没有了,这是因为devServer把打包后的模块不会放在dist⽬录下,⽽是放到内存中,从⽽提升速度。

webpack5 与 4.x 区别

wp5升级后的新特性,官方说法如下:

  • 通过持久化缓存提高性能,采用更好的持久化缓存算法和默认行为
  1. 内置了 terser-webpack-plugin 插件;
  2. 合并模块:optimization.concatenateModules = true;
  3. 内置缓存
cache: { 
    type: 'filesystem', 
    // 默认缓存到 node_modules/.cache/webpack 中 
    // 1. 可以自定义缓存目录,cache.cacheDirectory 选项仅当 cache.type 被设置成 filesystem 才可用。 
    cacheDirectory:path.resolve(__dirname,'node_modules/.cac/webpack')
    buildDependencies : { 
        // 2. 将配置添加为 buildDependency 以使配置更改时缓存失效 
        config : [__filename] 
        // 3. 如果您有其他构建所依赖的东西你可以在这里添加它们 
        // 请注意,webpack、加载器和从你的配置中引用的所有模块都会自动添加 
    } 
}
  • 通过优化 Tree Shaking 和代码生成来减小Bundle体积
  1. Webpack 4 不会去分析导入和导出模块之间的依赖关系;Webpack5 里面会通过 optimization.innerGraph记录依赖关系,对于嵌套依赖模块有更好的效果。
  2. 可设置副作用 sideEffects 处理优化;
  3. 可以同时支持 ESM 和 commonJS 的 treeShaking。
  • 提高 Web 平台的兼容性
  • 清除之前为了实现 Webpack4 没有不兼容性变更导致的不合理 state
  • loader优化(内置静态资源构建)
  1. raw-loader 将文件导入为字符串
  2. url-loader 将文件作为 data url 内联到 bundle文件中
  3. file-loader 将文件发送到输出目录中
  4. asset/resource 替换 file-loader (发送单独文件)
  5. asset/inline 替换 url-loader (导出 url)
  6. asset/source 替换 raw-loader(识别资源文件)
  7. MinicssExtractPlugin.loader 替换 style-loader
  • 其他
  1. 内置使用 webpack serve 启动;
  2. webpack 可以实现 应用程序和应用程序之间的引用(微前端);
  3. 热更新配置项修改;
  4. Webpack 生成的代码不再仅仅是ES5,也会生成 ES6 的代码;
  5. Node.js 的最小支持版本从 6 升级到了 10