第二章 常用扩展

59 阅读7分钟

清除输出目录

在打包结果的文件名是以hash命名的场景中,一旦源代码发生变化,hash改变,打包结果中的也将出现新的文件

清除输出目录就是用于在打包时将原本已经存在于输出目录中的文件删除,从而避免打包结果文件的累积

相关的plugin叫做clean-webpack-plugin

// webpack.config.js

var { CleanWebpackPlugin } = require("clean-webpack-plugin");

module.exports = {
    plugins: [
        new CleanWebpackPlugin()
    ]
}

自动生成页面

正常情况下,webpack的打包结果中是不会出现html文件的

如果打包结果是在浏览器环境下运行时,则需要使用到html文件,并且需要在html中引用打包结果中的js文件

html-webpack-plugin就可以完成这件事情,该插件可以向webpack的打包结果中输出一个html文件,并且还往html中加入引用了打包结果的js文件的script元素

// webpack.config.js

var HtmlWebpackPlugin = require("html-webpack-plugin");

module.exports = {
    plugins: [
        new HtmlWebpackPlugin()
    ]
}

在html-webpack-plugin的构造函数中可以传入一个对象,该对象是提供给html-webpack-plugin使用的配置对象,配置对象中可以包含以下属性:

  • filename

    生成的html文件的文件名,默认值为"index.html"

    可以包含目录

  • template

    指定该插件所参考的html模版,之后插件所生成的html文件的内容会与模版一致

    // webpack.config.js
    
    var HtmlWebpackPlugin = require("html-webpack-plugin");
    
    module.exports = {
        plugins: [
            new HtmlWebpackPlugin({
                template: "./public/index.html"
            })
        ]
    }
    

    如果不设置该配置项,则html-webpack-plugin会使用默认的html模版

    开发者提供的html模版中不需要引用js文件,因为html-webpack-plugin会在生成的html文件中自动引用js文件

  • chunks

    用于指定html中会引用哪些js文件,默认值为"all",表示html中会引用打包结果中所有的js文件

    new HtmlWebpackPlugin({
        chunks: "all"
    });
    

    也可以指定一个chunk的name字符串,表示页面中只会引用到某个chunk中对应的js文件

    new HtmlWebpackPlugin({
        chunks: "main"
    });
    

    也可以指定一个包含多个chunk的name字符串的数组,表示页面中会引用到这些chunk对应的js文件

    new HtmlWebpackPlugin({
        chunks: [ "a", "b", ... ]
    });
    

要想多创建几个html页面,只需要在plugins数组中多设置几个HtmlWebpackPlugin实例即可

// webpack.config.js

var HtmlWebpackPlugin = require("html-webpack-plugin");

module.exports = {
    plugins: [
        new HtmlWebpackPlugin({
            filename: "a.html"
        }),
        new HtmlWebpackPlugin({
            filename: "b.html"
        }),
        new HtmlWebpackPlugin({
            filename: "c.html"
        })
    ]
}

复制静态内容

copy-webpack-plugin用于直接将某个目录中或文件拷贝一份放到webpack的输出目录中

// webpack.config.js

var CopyWebpackPlugin = require("copy-webpack-plugin");

module.exports = {
    plugins: [
        new CopyWebpackPlugin(
            [
                {
                    from: "./public",	// 是以CWD为基准的相对路径,可以是目录也可以是文件
                    to: "./"			// 是以output.path为基准的相对路径
                }
            ]
        )
    ]
}

如果输出目录中已经包含了同名文件,则该插件就不会将参考目录中的同名文件拷贝过去

该插件可以和html-webpack-plugin进行配合使用,html-webpack-plugin注册的钩子会在copy-webpack-plugin注册的钩子之前被执行,因此html-webpack-plugin复制的html文件不会被copy-webpack-plugin覆盖

开发服务器

webpack为了方便开发者在开发阶段查看代码效果,便提供了一个库webpack-dev-server

webpack-dev-server既不是loader,也不是plugin,它是一个库

安装

npm i -D webpack-dev-server

使用

webpack-dev-server命令几乎支持所有的webpack命令参数,如--config、--env等等,你可以把它当作webpack命令使用

webpack-dev-server --mode=development

工作原理

当我们执行webpack-dev-server命令,并传递了一些参数时,webpack-dev-server在内部会进行下面的处理:

  1. 执行webpack命令,并将webpack-dev-server命令中的参数传递过去

  2. 开启watch

  3. webpack-dev-server向webpack中注册一些钩子函数

    该步骤的主要目的有以下两个:

    ① 将资源列表保存起来(保存在内存中)

    ② 阻止webpack输出文件

  4. 用express开启一个服务器,并监听某个端口(默认为8080),当有请求到来时,根据请求的路径将内存中的相应资源响应给浏览器

配置

webpack通过配置devServer来控制webpack-dev-server

devServer配置是一个对象,对象中又有很多具体的配置:

  • port

    设置访问地址所监听的端口号

  • open

    设为true后,在运行webpack-dev-server命令时,webpack-dev-server就能够自动打开默认浏览器,并访问对应的url地址

  • index

    设置默认访问的资源列表中的html文件的文件路径和文件名

    相对路径以输出目录为基准

  • openPage

    和index的作用一致

  • proxy

    设置代理服务器

    该配置是一个对象,具体如下:

    proxy: {
        "/api": {
            target: "https://www.mysite.com",
            changeOrigin: true
        }
    }
    

    当webpack-dev-server接收到来自浏览器的源与页面源相同且path以/api开头的请求时,webpack-dev-server就会向target所指示的服务器发送一个请求,在这个请求的url中,path部分和浏览器请求webpack-dev-server的url的path保持一致,但源会被替换为https://www.mysite.com

    当target服务器给webpack-dev-server响应了数据后,webpack-dev-server就会将该数据再次响应给浏览器


    changeOrigin选项用于控制是否改变请求的host和origin头部,默认为false

    如果changeOrigin设置为false,则webpack-dev-server向target服务器发送的请求中,请求头会与浏览器发送给webpack-dev-server的请求的请求头保持一致

    将changeOrigin设置为true后,开发服务器向target服务器发送的请求中的host和origin请求头就会被更改为https://www.mysite.com(剩下的请求头就保持不变)

    更多配置具体参见:www.webpackjs.com/configurati…

module.exports = {
    devServer: {
        port: 8080,
        open: true,
        proxy: {
            "/api": {
            	target: "https://duyiedu.com",
            	changeOrigin: true
            }
        }
    }
}

普通文件处理

在webpack构建的工程中导入图片文件时,通常希望得到的是图片在输出目录中的路径或将图片内容转换为base64格式后的结果

输出目录即打包结果的根目录

file-loader

file-loader可以将文件的原始内容复制一份到输出目录中,并返回该复制的文件在输出目录中的路径

file-loader是通过ES Module的方式导出路径的,路径是作为导出的对象的default属性存在,在使用require导入时要注意

使用方式:

// webpack.config.js

module.exports = {
    module: {
        rules: [
            {
                test: /\.(jpg|png|gif)$/,		// 匹配文件规则
                use: {
                    loader: ["file-loader"],
                    options: {
                        name: "imgs/[name]-[hash:5].[ext]"
                    }
                }
            }
        ]
    }
}

options.name用于设置复制出来的文件在输出目录中的文件存放路径和文件名称

name属性中的[name]中会被替换为被转换文件的文件名称

[hash]中会被替换为一个hash字符串(该hash是根据被转换文件的内容生成的,是loader生成的hash,而不是webpack生成的hash)

[ext]会被替换为被转换文件的后缀名

url-loader

url-loader可以将文件内容转换为base64格式

url-loader是通过ES Module的方式导出base64字符串的,base64字符串是作为导出的对象的default属性存在

url-loader内部包含有file-loader,因此如果安装了url-loader就不必再安装file-loader

若一个文件经过url-loader处理后,就不会被file-loader处理;若url-loader不对文件进行处理,file-loader就会处理

url-loader的options中除了可以设置url-loaders自己所需要的参数,也可以设置file-loader的所需的参数,当url-loader不处理文件时,就会将参数传递给file-loader,让它对文件进行处理

// webpack.config.js

module.exports = {
    module: {
        rules: [
            {
                test: /\.(jpg|png|gif)$/,	// 匹配文件规则
                use: {
                    loader: ["url-loader"],
                    options: {
                        limit: 1024,		// 大小超过1024B的文件转交给file-loader处理
                        name: "imgs/[name]-[hash:5].[ext]"	// file-loader的配置参数
                    }
                }
            }
        ]
    }
}

webpack内置插件

webpack的内置插件是webpack自带的,无需下载安装的插件

内置插件一般是按照下面的形式使用的

// webpack.config.js

var webpack = require("webpack");

module.exports = {}
    plugins: [
        new webpack.插件名(具体配置)
    ]
}

DefinePlugin

该插件用于定义一些常量,这些常量可以在任何模块中使用,编译时webpack会替换这些常量

需要注意的是,DefinePlugin函数传入的对象,它的属性的值必须是字符串

// webpack.config.js

var webpack = require("webpack");

module.exports = {}
    plugins: [
        new webpack.DefinePlugin({
            PI: "Math.PI",
            YEAR: "2024",
            DOMAIN: "'duyi.com'"
        })
    ]
}
// index.js

console.log(PI);			// 会被替换为console.log(Math.PI)
console.log(YEAR);			// 会被替换为console.log(2024)
console.log(DOMAIN);		// 会被替换为console.log('duyi.com')

ProvidePlugin

该插件用于自动导入模块,之后在模块中就无需手动导入模块

// webpack.config.js

var webpack = require("webpack");

module.exports = {
    plugins: [
        new webpack.ProvidePlugin({
            $: "jquery",
            _: "lodash"
        })
    ]
}
// index.js

console.log($);				// 直接使用
console.log(_);				// 直接使用

html-loader

html-loader需要配合html-webpack-plugin一起使用

html-loader能够识别出html-webpack-plugin生成的html页面中的资源路径,如果发现某个资源的路径满足某个loader的匹配规则,则就将路径所对应的资源交付给该loader进行处理

root
   	|—— public
           |—— index.html
           |—— logo.svg
    	|—— src
        	|—— index.js
<!-- index.html -->

<img src="./logo.svg">
// webpack.config.js

var HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
    plugins: [
        new HtmlWebpackPlugin({
            filename: "target.html"
            template: "./public/index.html"
        })
    ],
    module: {
        rules: [
            {
                test: /\.html$/,
                use: "html-loader"
            },
            {
                test: /\.svg$/,
                use: "url-loader"
            }
        ]
    }
}

在打包时,html-loader会将html-webpack-plugin生成出的html页面中的svg资源交给url-loader进行处理,然后将处理结果,也就是base64编码字符串覆盖原来的路径

<!-- target.html -->

<img src="data:image/svg+xml;base64,...">