likes
comments
collection
share

Webpack入坑指北

作者站长头像
站长
· 阅读数 4

初始化项目

首先新建一个项目然后初始化。

npm init -y
pnpm add webpack webpack-cli -D

创建一个 src/main.js 文件,随便写点啥 在根目录创建 build/webpack.common.js 文件

const path = require("path");  
  
module.exports = {  
    entry: path.resolve(__dirname, "../src/main.js"),  
    output: {  
        filename: "[name].bundle.js",  
        path: path.resolve(__dirname, "../dist"),  
        clean: true,  
    },  
}  

在 package.json 加上打包命令

"scripts": {  
    "build": "webpack --config build/webpack.common.js"  
}  

通过执行 pnpm run build 进行打包,执行后会生成一个 dist 目录,里面就是我们打包的产物。

打包时控制台会报 The 'mode' option has not been set 的警告,意思就是我们需要设置一个 mode 它可以是 development 开发模式、production生产模式、none无模式。

创建 HTML

打包好的产物我们希望能通过html在浏览器中打开看效果,需要通过 html-webpack-plugin 插件实现

pnpm add -D html-webpack-plugin  

在配置文件中写入

const htmlWebpackPlugin = require("html-webpack-plugin")  
module.exports = {  
    //...  
    plugins: [  
        new htmlWebpackPlugin({  
            // 设置模板,需要在根目录创建一个 index.html 文件  
            template: path.resolve(__dirname, "../index.html")  
        })  
    ]  
}  

plugins 就是我们声明插件来对 webpack 的功能进行扩展的地方。这时候再打包,发现产物目录下有一个 html 文件,并且通过 script 引入了我们打包的 js 文件。

打包时显示进度

pnpm add progress-bar-webpack-plugin -D  
pnpm add chalk@4.1.2 -D  

这里的 chalk 最新版本(5.3.0)使用 esm 引入,我们使用 4.1.2 版本

使用

const chalk = require("chalk");  
const ProgressBarPlugin = require("progress-bar-webpack-plugin");  
module.exports = {  
    // ...  
    plugins: [  
        // ...  
        new ProgressBarPlugin({  
            format: ` :msg [:bar] ${chalk.green.bold(":percent")} (:elapsed s)`,  
        }),  
    ]  
}  

再进行打包就能看到进度条了,另外可以通过 chalk 来修改进度条的颜色等。

处理样式

我们定义一个 main.css 文件,在 main.js 引入,进行打包时会发现控制台报错,并提示我们需要用 loader 来处理这个类型的文件。 处理 css 类型文件需要用到以下两个 loader。

pnpm add -D style-loader css-loader  

在配置文件中声明使用这两个 loader 来处理 css 类型文件。

module: {  
    rules: [  
        {  
            test: /\.css$/,  
            use: ["style-loader", "css-loader"]  
        }  
    ]  
}  

module 是我们用来定义某种资源通过指定的 loader 来处理,如上代码我们通过 "style-loader""css-loader" 来处理后缀名为 css 的文件,"css-loader" 做的是解析 css 文件,翻译成 JavaScript 代码,使得 webpack 能够如同处理 JS 代码一样解析 CSS,"style-loader" 做的是将解析出来的文件通过 style 标签插入到页面中。

另外 loader 的书写顺序也是有要求的,一般情况下,会按照由右到左,由上到下的顺序执行。所以上面代码才是先解析再插入到页面。

通过打包的产物发现我们的 css 已经被打包进 js 文件中,下面我们把 css 代码抽离出来。安装MiniCssExtractPlugin插件

pnpm add -D mini-css-extract-plugin  

然后进行配置

const MiniCssExtractPlugin = require("mini-css-extract-plugin")  
module.exports = {  
    // ...  
    module: {  
        rules: [  
            {  
                test: /\.css$/,  
                use: [MiniCssExtractPlugin.loader, "css-loader"]  
            }  
        ]  
    },  
    plugins: [  
        // ...  
        new HtmlWebpackPlugin({  
            template: path.resolve(__dirname, "../index.html"),  
        }),  
        new MiniCssExtractPlugin()  
    ]  
}  

这时候再打包可以看到产物中多一个 css 文件,这样我们就把 css 文件抽离出来了 Webpack入坑指北 再看产物中的 index.html,多了一行 link 标签,所以这个插件的作用就是把 css 文件抽离并通过 link 标签的形式插入到页面中,这也是为什么在上面的配置中我们去掉了 style-loader。

<link href="main.css" rel="stylesheet">  

使用 MiniCssExtractPlugin 插件的好处在于,避免了 js 和 css 只能同步加载,对性能有影响,并且其中任意一个更新都会导致缓存失效。另外该插件必须和上面写到的 HTMLWebpackPlugin 插件一起使用(不然连 html 文件都没有怎么插入到页面)。

预处理器

我们以 less 为例

pnpm add -D less less-loader  

然后新建一个 less 文件,随便写点样式,在打包入口文件导入,接着进行对 less 的配置

module: {  
    rules: [  
        {  
            test: /\.less$/,  
            use: [MiniCssExtractPlugin.loader, "css-loader", "less-loader"]  
        }  
    ]  
}  

无非在处理 css 文件的基础上,多了一个 less-loader ,这个 loader 做的就是把 less 翻译成 css。

postcss

安装 postcsspostcss-loader

pnpm add -D postcss postcss-loader  

自动添加前缀

postcss 有强大的插件生态来处理各种问题,常用的如 autoprefixer,他能根据配置的浏览器兼容性,检测出需要添加前缀的 css 属性,构建的时候进行添加。

pnpm add -D autoprefixer  

对 postcss 配置

{  
    test: /\.less$/,  
    use: [  
        MiniCssExtractPlugin.loader,  
        "css-loader",  
        {
            loader: "postcss-loader",  
            options: {
                postcssOptions: {
                    // 添加 autoprefixer 插件  
                    plugins: [require("autoprefixer")],  
                },
            },
        },
        "less-loader"
    ]  
}  

在 less 文件中写入

::placeholder {
    color: gray;
}

打包后可以看到产物已经增加了前缀 Webpack入坑指北

关于系统支持的浏览器可以通过 package.json 的 browserslist 或者 .browserslistrc 文件进行配置 另外postcss-preset-env也可以做同样的事情,甚至更强大,他还能将现代 CSS 转换成大多数浏览器都能理解的东西。

处理 js

webpack 默认是支持处理 js 文件的,但在一些情况下,js 代码会存在兼容性的问题,在低版本的浏览器无法运行的情况,所以我们需要对这部分代码进行处理。这里用到的是 babel 对代码进行转译。

pnpm add -D @babel/core @babel/preset-env babel-loader  

@babel/core:babel核心库 @babel/preset-env 根据目标环境转译 JavaScript 的预设 babel-loader webpack 中的一个 loader,将 babel 集成到 webpack 构建过程中

配置

{
    test: /\.js$/,
    use: [
        {
            loader: 'babel-loader',
            options: {
                presets: ['@babel/preset-env']
            }
        }
    ]
}

在入口文件我们声明一个箭头函数并执行,然后分别看使用 babel-loader 进行语法降级和 不使用的效果,可以看到用 babel 后,箭头函数被转译成如下的形式。

var fun = function () {/*...*/}  

处理 ts

使用 ts-loader

pnpm add -D typescript ts-loader  

配置

{  
    test: /\.ts$/,  
    use: ["ts-loader"]  
},

还需要创建 ts 的配置文件 tsconfig.json

{
    "compilerOptions": {
        "noImplicitAny": true,
        "moduleResolution": "node"
    }
}

这时候就可以执行构建了

使用 babel

pnpm add -D @babel/preset-typescript  

配置

{  
    test: /\.ts$/,  
        use: [  
            {  
                loader: 'babel-loader',  
                options: {  
                presets: ['@babel/preset-typescript'],  
            },  
        },  
    ],  
},  

两者区别

使用 @babel/preset-typescript 只是进行了代码转换,而 ts-loader 还会对代码进行类型检查。例如我们将 number 赋值给一个 string 类型。ts-loader 在构建时会报错。 Webpack入坑指北

处理资源

file-loader

安装

pnpm add -D file-loader  

file-loader 将资源重命名,在代码中插入 url 地址。

url-loader

安装

pnpm add -D url-loader  

配置

{
    test: /\.(png|jpg)$/,
    use: [
        {
            loader: "url-loader",
            options: {
                limit: 1024
            }
        }
    ]
},

url-loader 会对比资源大小和配置的限制大小,如果没超出限制就转为 Base64 编码,超出的话就同 file-loader 一致。

raw-loader

不转译,直接将文件复制到产物。

Webpack5 通过 module.rules.type 指定资源类型。

asset/resource 对标 file-loader asset 对标 url-loadermodule.rules.parser.dataUrlCondition 用于限定文件大小阈值 asset/source 对标 raw-loader / asset/inline

查看更多

区分开发环境和生产环境

我们上面写到的 webpack.common.js 可以作为一个基础的配置,在开发环境和生产环境中配置也是有差异的,所以我们还需要分别去配置。 创建 build/webpack.dev.jsbuild/webpack.prod.js 因为开发环境和生产环境都基于基础配置,我们需要通过 webpack.merge 将他们连接起来。

pnpm add -D webpack-merge  

webpack-dev-server

一般开发环境我们希望项目构建后能直接在浏览器运行,这一点通过 webpack-dev-server 来实现

pnpm add -D webpack-dev-server  

配置

const { merge } = require("webpack-merge");  
const common = require("./webpack.common.js");  
const path = require("path");  
  
module.exports = merge(common, {
    mode: "development",
    devServer: {
        hot: true,
        open: true,
        port: 3000,
    },
});  

重写启动命令

{
    "scripts": {
        "dev": "webpack serve --config build/webpack.dev.js",
    }
}

执行 pnpm run dev 将会在 3000 端口启动。