一看就懂之webpack基础配置

6次阅读

共计 14037 个字符,预计需要花费 36 分钟才能阅读完成。

一看就懂之 webpack 基础配置

一、webpack 简介

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


简单说,webpack 可以看做是一个模块打包机,主要作用就是: 分析你的项目结构,找到 JavaScript 模块以及一些浏览器不能直接运行的拓展语言(sass、less、typescript 等),然后将它们打包为合适的格式以供浏览器使用。


webpack 主要实现的功能:

代码转换(如: ES6 转换 ES5、sass 和 less 转换为 css 等)
文件优化(如: 将模块内容进行压缩)
代码分割(如: 多页面应用公共模块的抽离、路由懒加载)
模块合并(如: 按照不同的功能将多个模块合并为一个模块)
自动刷新(如: 启动本地服务,代码更新后进行自动刷新)
代码校验(如: 添加 eslint 进行代码规范检查)
自动发布(如: 应用打包完成后,自动发布)

二、webpack 安装

在 webpack 3 中,webpack 本身和它的 CLI 以前都是在同一个包中的,但在第 4 版之后,已经将两者分开来更好地管理它们,所以安装 webpack4 之后的版本,要同时安装 webpack 和 webpack-cli

注意,安装 webpack 的时候,必须进行本地安装才能生效,否则会报错,如:

但是全局安装了也有一个好处,那就是 可以在项目根目录下直接执行 webpack 即可完成项目的打包 ,如果没有进行全局安装,那么可以通过 npx 直接执行项目本地安装的模块,即 npx webpack 也可以完成项目的打包。

三、webpack 基础配置

webpack 是支持零配置的,即不需要配置文件即可完成打包,其 默认入口文件为项目根目录下的 src 目录下的 index.js 文件 ,其 默认出口为项目根目录下的 dist 目录的 main.js

如果没有给 webpack 添加配置文件,那么 webpack 的打包能力就会非常弱,webpack 执行的时候默认会加载项目根目录下的 webpack.config.js 文件,注意,该配置文件是一个 js 文件,而不是 json 文件,并且其是通过 node 去执行的,所以其完全支持 node 语法,即 node 中能用的,在配置文件中都可以用

webpack 配置文件必须要对外暴露一个对象,即通过 module.exports 进行对外暴露,其中的所有配置都必须写在这个对外暴露的对象中。

context
context 属性表示的是 webpack 的上下文目录,配置入口文件的时候,如果入口文件使用的是相对路径,那么就是相对于 context 所在的目录。

context 默认值为执行 webpack 命令时所在的当前工作目录,通常是在项目根目录下执行 webpack 命令,所以可以认为其值默认为项目根目录,所以如果入口文件路径写成相对路径,最好将 context 配置成 context: path.resolve(__dirname),以防止在非项目根目录下执行 webpack 命令时找不到入口文件路径而报错。

entry

entry 用于配置模块的入口文件,可以配置多个,webpack 将从入口文件开始搜索以及递归解析所有入口文件依赖的模块,其是 必填的 ,如果配置文件中没有 entry 则会报错。entry 的属性值可以是 表示路径的单个字符串 也可以是数组 ,数组中的元素为入口文件的路径, 还可以是对象 ,对象的属性名为入口文件的 chunk 名,即打包后输出文件的名字,属性值为入口文件的路径。注意,入口文件的路径 可以是绝对路径 也可以是相对路径 ,相对路径默认是以 配置文件中的 context属性值表示的路径

module.exports = {entry: "./foo.js" // 属性值为一个表示路径的字符串}

其输出结果文件取决于配置文件的 output 配置,如果 output.filename 没有配置,则默认输出为 main.js, 如果 output.filename 值为指定的名称,则输出结果为 output.filename 的属性值

module.exports = {entry: [ "./foo.js", "./bar.js"] // 属性值为一个数组
}

其输出结果文件也是取决于配置文件的 output 配置,只不过,其会将 foo.js 和 bar.js 一起打包输出为一个文件,如果 output.filename 没有配置,则默认输出为 main.js, 如果 output.filename 值为指定的名称,则输出结果为 output.filename 的属性值

module.exports = {
    entry: { // 属性值为一个对象
        a: "./src/bar.js",
        b: "./src/foo.js",
        c: "./src/index.js"
    }
}

其输出结果不再取决于 output.filename 的配置,因为 entry 已经指定好了模块输出的 chunk 名,即会分别输出 a.js、b.js 和 c.js 三个文件,并且此时 output.filename 属性值不能配置为一个固定名称的输出文件,因为 入口文件有多个,必然输出文件也会有多个

chunk 和 module 的区别,二者都是表示模块,但是 module 可以看做是具有独立功能的小模块,即 小块 ,也就是打包之前,程序员编写的一个一个的文件,每个文件称为一个 module;而 chunk 则可以看做是由多个小 module 打包成的大模块,即 大块

output
output 配置的是如何输出最终想要的代码,output 是一个 object。

path: 用于配置打包后输出文件的本地存放目录,必须是绝对路径 ,当然 也可以不配置 ,因为如果没有配置 path,那么 会自动在执行 webpack 命令时所在的目录下自动创建 dist 目录 并将打包结果输出到 dist 目录下,与 context 的配置路径无关

module.exports = {
    output: {path: path.resolve(__dirname, "./dist") // 必须是绝对路径
    }
}

filename: 用于配置输出文件的名称,如果只有一个输出文件,那么可以配置成静态不变的文件名,如:

module.exports = {
    output: {filename: "bundle.js"}
}

但是,如果有多个 chunk 要输出时,即入口文件配置了多个时,那么 filename 就不能配置成静态不变的了,就必须 借助模板和变量 了,常见的两个变量,如:
[name]: 可以自动获取到入口文件配置的 chunk 名称;
[hash]: 可以自动生成 hash 值,hash 值的长度是可以指定的,默认为 20 位;

module.exports = {
    output: {filename: "[name][hash:8].js" // 以入口文件设置的 chunk 作为输出名,并且指定 hash 值为 8 位
    }
}

library 和 libraryTarget: 用于指定 将模块的输出结果挂载到哪个地方 或者以什么样的方式导出库 (模块输出结果)。二者通常要搭配一起使用。
libraryTarget 通常用于指定以何种方式导出库,library 通常用于指定接收库的名称。
我们将入口的一个或多个 js 文件打包输出后的结果也是一个 js 文件,在没有配置 library 和 libraryTarget 的时候,这个输出的 js 文件中包含了一个匿名自执行函数, 即 输出文件的执行结果没有被任何东西接收,我们引入输出文件执行后不会得到任何结果。 如:

(function(){console.log("foo");
    return "foo"; // 虽然有返回值,但是匿名自执行函数执行完毕后拿不到任何结果
})();
// 我们以 var 变量 的方式来接收函数的返回值
var foo = (function(){ // 匿名自执行函数执行完毕后就会将函数返回值保存到 foo 变量上
    console.log("foo");
    return "foo";
})();
console.log(`foo is ${foo}`);

打包后的输出文件的输出结果 (导出结果),就是入口文件的输出结果(导出结果),即入口文件通过 export、exports、module.exports 等输出(导出) 的结果

var: 将 libraryTarget 设置为 var, 同时指定一个自定义的变量名来接收模块的输出,这个自定义的变量名就是 library 的属性值

module.exports = {
    output: {
        filename: "bundle.js",
        path: path.resolve(__dirname, "./dist/"),
        libraryTarget: "var",
        library: "test"
    }
}

模块的输出结果将会赋值给 test 变量,其输出文件 bundle.js 内容大致如下:

var test = (function(modules){return result; // 返回值 result 将会被赋值给 test 变量})();

commonjs: 将 libraryTarget 设置为 commonjs, 即通过 commonjs 规范导出,同时指定一个自定义的变量名来接收模块的输出,这个自定义的变量名就是 library 的属性值, 只不过这个自定义的变量名是 exports 的属性名,如:

module.exports = {
    output: {
        filename: "bundle.js",
        path: path.resolve(__dirname, "./dist/"),
        libraryTarget: "commonjs",
        library: "test"
    }
}

模块的输出结果将会赋值给 exports[“test”]上,其输出文件 bundle.js 内容大致如下:

exports["test"] = (function(modules){return result; // 返回值 result 将会被赋值给 exports["test"]
})();

commonjs2: 将 libraryTarget 设置为 commonjs2,即通过 commonjs2 规范导出,此时 library 的配置将无意义,因为 commonjs2 的输出是固定的 module.exports,所以不需要指定 library 了,如:

module.exports = {
    output: {
        filename: "bundle.js",
        path: path.resolve(__dirname, "./dist/"),
        libraryTarget: "commonjs2"
    }
}

模块的输出结果将会被赋值到 module.exports 上,其输出文件 bundle.js 内容大致如下:

module.exports = (function(modules){return result; // 返回值 result 将会被赋值给 module.exports})();

commonjs 和 commonjs2 的区别在于,commonjs 只能使用 exports 进行导出,而 commonjs2 在 commonjs 的基础上增加了 module.exports 进行导出;

this: 将 libraryTarget 设置为 this, 那么此时 library 配置的变量名将作为 this 的属性名来接收模块的导出结果,如:

module.exports = {
    output: {
        filename: "bundle.js",
        path: path.resolve(__dirname, "./dist/"),
        libraryTarget: "this",
        library: "test"
    }
}

模块的输出结果将会被赋值到 this[“test”] 上,其输出文件 bundle.js 内容大致如下:

this["test"] = (function(modules){return result; // 返回值 result 将会被赋值给 this["test"]
})();

同理 libraryTarget 的属性值还可以是windowglobal,这里就不一一列举了。

publicPath
publicPath 用于配置打包资源发布到线上时服务器的 url 地址,打包完成后,html 文件中如果引入了 js、image、css 等资源,那么都会在前面加上 publicPath 所表示的路径

module.exports = {
    output: {
        filename: "bundle.js",
        path: path.resolve(__dirname, "./dist/"),
        publicPath: "http://www.lihb.com/"
    }
}

// index.html

<!DOCTYPE html>
<html lang="en">
<body>
    <script type="text/javascript" src="http://www.lihb.com/index.js"></script></body>
</html>

四、webpack 打包输出后的内容分析

webpack 打包输出后的结果默认是一个匿名自执行函数,匿名自执行函数传递的参数为一个对象,对象的属性名为入口文件的路径名 属性值为一个函数 ,函数体内部通过会执行 eval(),eval() 方法的参数为入口文件的内容字符串 ,而这个匿名自执行函数,内部有一个自定义的__webpack_require__方法,该方法需要传入 入口文件的路径名 作为参数,匿名自执行函数执行完成后会 返回__webpack_require__的结果 ,而__webpack_require__() 方法内部执行的时候,会首先 创建一个 module 对象module 对象里面有 exports 属性,属性值为一个空的对象,用于接收入口文件的模块输出,如:

(function(modules) {function __webpack_require__(moduleId) { // 传入入口文件的路径
        var module = installedModules[moduleId] = { // 创建一个 module 对象
             i: moduleId,
             l: false,
             exports: {} // exports 对象用于保存入口文件的导出结果};
        modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); // 执行入口文件
        return module.exports; // 返回模块输出结果
    }
    return __webpack_require__(__webpack_require__.s = "./src/bar.js"); // 返回入口文件
})({"./src/bar.js": (function(module, exports) {eval("module.exports = \"bar\";");
     })
  });

所以不管入口文件是以 ES6 模块的方式输出还是以 commonjs 模块的方式输出,最终入口文件的模块输出结果都会被绑定到__webpack_require__方法中定义的 module 对象的 exports 属性上 ,只不过,如果是以 commonjs 的方式输出,那么 入口文件的输出结果将会直接替换掉__webpack_require__方法中定义的 module 对象的 exports 属性 ;如果是以 ES6 模块的方式输出,则是 在__webpack_require__方法中定义的 module 对象的 exports 属性值中添加一个 default 属性或者具体变量名来保存入口文件的输出

五、webpack 本地服务器配置

为了更方便调试,我们需要用到 webpack 的本地 http 服务器功能,要想使用 webpack 提供的 Web 服务器功能,我们需要 安装 webpack-dev-server 模块,webpack-dev-server 会启动一个 web 服务器用于实现网页请求,也可以监听文件的变化自动刷新网页。

webpack-dev-server 模块安装完成后,我们可以在项目根目录下运行npx webpack-dev-server, 其和 webpack 命令一样,如果没有配置文件,则使用内置默认配置进行打包输出,如果有则使用配置文件中的配置,只不过其不会将打包结果输出到指定的目录中,因为 webpack-dev-server 会忽略配置文件中的 output.path 配置,其会将打包输出结果保存到内存中。webpack-dev-server 启动后会默认将启动 devServer 时所在的目录作为根目录,即执行 npx webpack-dev-server 命令时所在的目录

webpack 提供了一个 devServer 属性用于配置启动的服务器的一些参数,当然webpack 本身是无法识别 devServer 属性配置的,只有通过 webpack-dev-server 去启动 webpack 时,devServer 的配置才会生效

module.exports = {
    devServer: {
        port: 3000, // 让 devServer 监听 3000 端口
        contentBase: "./dist", // 将当前项目的 dist 目录作为 devServer 的根目录
        progress: true, // 显示打包进度条
        compress: true // 是否启用 Gzip 压缩,默认为 false
    }
}

webpackDevServer 启动后,默认会自动监听打包源文件的变化 ,如果修改了打包源文件,那么会自动重新打包到内存,并且会自动刷新浏览器,但是 自动刷新浏览器功能必须将 target 设置成 web,否则自动刷新功能将会失效,比如 target 为 node 就无法起作用。

六、webpack 插件配置

在不使用插件的时候,webpack 默认只能打包输出 js 文件,如果我们想要输出其他格式的文件到输出目录中,那么我们必须使用插件 。webpack 提供了一个plugins 属性用于配置使用到的插件,其 属性值为一个数组 ,数组中的元素为插件对象,通常插件都是一个类,我们 需要通过插件类来创建一个插件对象
html-webpack-plugin
该插件可以指定一个 html 文件,webpack 会 将该 html 文件打包输出到输出目录中 ,同时会将打包输出后的文件自动插入到该 html 文件中,即 让该 html 文件自动引入打包后的 js 文件

module.exports = {
    plugins: [
        new HtmlWebpackPlugin({
            template: "./src/index.html", // 要打包输出哪个文件,可以使用相对路径
            filename: "index.html", // 打包输出后该 html 文件的名称
            minify: {
                removeAttributeQuotes: true, // 去除 html 文件中的引号
                collapseWhitespace: true // 合并空格,即将 html 进行单行显示
            },
            hash: true // 向 html 文件中引入打包后的 js 时候带上 hash 值
        })
    ]
}

html 插件中配置了 hash 为 true, 是在引入打包后的 js 的时候带上 hash 值,如:

<script type="text/javascript" src="main.js?c7086a400fa368e84ad6"></script></body>

七、webpack 模块配置

webpack默认将所有格式的文件都当做模块进行处理 ,但是 wepback 默认只能处理 js 模块 。如果在 js 中通过 require 引入了其他格式的模块(文件),那么 webpack 就 必须通过安装合适的模块加载器,才能正确解析对应的模块内容 ,webpack 提供了一个module 属性,用于 进行模块解析器的配置 ,其 属性值为一个对象 ,对象中有一个rules 属性,其 属性值为一个数组 ,数组中的元素为一个 对象 ,该对象主要完成两件事, 匹配 对应格式的文件,并且 使用 对应模块加载器进行加载,匹配使用的是 test 属性,属性值为一个 正则表达式 ,【使用】使用的是 use 属性,属性值 可以是字符串也可以是数组 ,如果只有 一个 模块加载器的时候,可以使用 字符串 的形式,如果有 多个 模块加载器的时候,那么就需要使用 数组 的形式,当然,如果模块加载器 需要传递参数配置 ,那么可以将模块加载器写成 对象 的形式,通过 loader 属性指定模块加载器名称,通过 options 属性传递参数配置。

① 处理 css 样式,需要使用到 css-loader 和 style-loader。
首先需要安装 css-loader 和 style-loader。
css-loader 必须同时和 style-loader一起使用 才能正确加载 css 文件,一个负责 加载 ,一个负责 插入 css-loader 负责加载 css, 即 在 js 文件中能够通过 require 的方式引入 css,即加载和解析 css,同时支持 在 css 文件中使用 @ import 的方式引入其他 css 文件style-loader 负责将加载并解析好的 css 文件插入到 html 文件中去,从名字可以看出其是在 html 文件中生成 style 标签来引入 css 文件,loader 的执行顺序是从右向左,所以必须先加载然后再插入

比如,打包入口文件 index.js 中通过 require 的方式引入了一个 index.js 文件,即 require(“./index.css”), 那么 webpack 需要进行如下配置:

module.exports = {
    module: {
        rules: [
            {
                test: /\.css$/, // 匹配以.css 结尾的文件
                use: [ // 并交给 css-loader 和 style-loader 进行处理
                    {
                        loader: "style-loader", // 以对象的形式配置 loader
                        options: { // 通过 options 给 loader 传递参数
                            insertAt: 'top' // 默认为 bottom, 将加载的 css 文件插入到 head 标签的最上面,即优先级最低,会被覆盖
                        }
                    },
                    "css-loader" // 直接以字符串的形式配置 loader
                ]
            }
        ]
    }
}

打包输出后,会 将 index.css 中的内容放到 <style> 标签中 ,并且 将这个 <style> 标签自动插入到 index.html 文件的 <head> 标签的最底部,如果配置了 insertAt: “top”, 那么就会插入到 <head> 标签的最上面。

同理,我们也可以出来 sass,即 scss 文件,要处理 scss 文件,我们需要安装 sass-loader,而 sass-loader 需要配合 node-sass 一起使用,安装好 sass-loader 之后我们只需要再添加一个 rule,改成匹配.scss 结尾的模块,处理 sass 和处理 css 都需要用到 style-loader 和 css-loader,只不过处理 sass 还需要 sass-loader,即需要用 sass-loader 将 scss 文件转换为 css 文件,在 css-loader 后加一个 sass-loader 即可。

八、webpack 样式的高级处理

抽离样式
我们通过 css-loader 和 style-loader 处理 css 样式后是直接通过 <style> 标签将解析后样式插入到了 html 中,如果需要 将 css 样式抽离成一个单独的 css 文件, 并且自动 link 进 html 文件中 ,那么就需要mini-css-extract-plugin 这个插件。

首先安装 mini-css-extract-plugin 插件,然后创建插件对象,并进行相应的配置,主要就是 filename,即 抽离出的 css 文件叫什么名字

module.exports = {
    plugins: [
        new MiniCssExtractPlugin({filename: "css/index.css", // 抽离出的 css 文件叫什么名字, 前面可加路径})
    ]
}

filename 属性值中可以添加上一个路径,实现 css 资源的分类输出,上面 index.css 文件就会输出到输出目录下的 css 目录下

插件安装配置好之后还不行,因为还要对 loader 进行相应的配置,之前 css 文件是通过了 style-loader 处理的,而 style-loader 会将样式通过 <style> 标签的方式插入到 html 文件中,所以必须先移除 style-loader, 然后使用 mini-css-extract-plugin 这个插件提供的 loader

module.exports = {
    module: {
        rules: [
            {
                test: /\.css$/, // 匹配以.css 结尾的文件
                use: [ // 并交给 css-loader 和 MiniCssExtractPlugin 的 loader 进行处理
                    MiniCssExtractPlugin.loader, // 将 css 进行抽离
                    "css-loader" // 直接以字符串的形式配置 loader
                ]
            },
        ]
    }
}

压缩 css
要想压缩 css,那么需要用到 optimize-css-assets-webpack-plugin 插件,需要注意的是 该插件并不是配置到 plugins 属性中,而是配置到 optimization 属性中

module.exports = {
    optimization: {minimizer: [new OptimizeCSSAssetsPlugin({})], // 压缩 css
    }
}

我们可以通过修改 mode 为 production 来压缩我们的 js,但是当使用了 optimize-css-assets-webpack-plugin 插件后,那么 js 的压缩就会失效,我们需要使用 uglifyjs-webpack-plugin 插件进行压缩,如:

module.exports = {
    optimization: {
        minimizer: [new OptimizeCSSAssetsPlugin({}), // 压缩 css
            new UglifyjsWebpackPlugin({ // 压缩 js
                cache: true,
                parallel: true,
                sourceMap: true
            })
        ], 
    }
}

使用 uglifyjs-webpack-plugin 插件后,mode 的切换仍然是生效的了。

九、webpack JS 的高级处理

① 将 ES6 以上的高级语法特性转换 ES5
Babel 是一个 JavaScript 的编译器,能将 ES6 的代码转换为 ES5 的代码,我们需要安装 babel-loader 来处理我们 js 文件,而其需要配合 @babel/core 模块一起使用,还要告诉 babel 当前要转换的 JS 代码中使用了哪些新特性,即预设,我们使用包含当前所有最新 ES 语法特性的预设即可,@babel/preset-env

module.exports = {
    module: {
        rules: [
            {
                test: /\.js$/,
                use: [
                    {
                        loader: "babel-loader", // 使用 babel-loader 进行处理 js 文件
                        options: {presets: ["@babel/preset-env"] // 用了最新的 ES6 语法预设
                        }
                    }
                ]
            }
        ]
    }
}

② 减少代码冗余
babel 在转换 ES6 语法的时候, 会使用一些由 ES5 编写的帮助函数来实现新语法的转换 。比如转换 class 语法,就需要使用到 classCallCheck() 函数,如果多个文件中都使用到了 Class 语法,那么每个文件都会被注入 classCallCheck()辅助函数,代码就会变得非常冗余,通过引入 @babel/babel-plugin-transform-runtime 插件就可以在输出文件中通过 require 的方式引入一个公共的 classCallCheck()辅助函数, 然后所有文件一起使用即可减少代码的冗余。@babel/babel-plugin-transform-runtime 插件 需要配合 @babel/runtime 一起使用 ,因为 babel-plugin-transform-runtime 插件 引入的帮助函数,都存放在 @babel/runtime 中

module.exports = {
    module: {
        rules: [
            {
                test: /\.js$/,
                use: [
                    {
                        loader: "babel-loader", // 使用 babel-loader 进行处理 js 文件
                        options: {presets: ["@babel/preset-env"] // 用了最新的 ES6 语法预设
                            plugins: ["@babel/plugin-transform-runtime"] // 减少代码冗余插件
                        }
                    }
                ]
            }
        ]
    }
}

③ 转换新的 ES6API
babel 默认只能转换 ES6 的新语法,如果想要转换一下 ES6 的新功能 (Promise)、新接口(数组的 includes 方法) 等,那么需要使用到 @babel/polyfill, 其工作原理是 在全局对象或者内置对象中添加一下属性或者方法,其使用方式为: 一种是直接在 js 文件中 require, 如: require(“@babel/polyfill”); 另一种是将 ”@babel/polyfill” 作为入口文件一起打包成一个 chunk,如:

module.exports = {entry: ['@babel/polyfill', "./src/index.js"] 
}

比如,使用了 Promise,那么打包输出的文件中,可以看到 全局对象 global 被添加了一个 Promise 属性

十、webpack 第三方模块的处理

① 如果我们的项目中使用到了第三方模块,比如 jquery,我们直接在我们的 js 模块中引入 jquery,那么这个 jquery 只能在当前模块中使用,而 无法暴露到全局变量 window 中,如果想要实现,自动将 jquery 暴露给全局变量,那么需要引入expose-loader,其可以作为内联 loader 直接在 js 中使用,也可以作为普通 loader 在配置文件中使用。
// index.js

import $ from "expose-loader?$!jquery";
console.log($);
console.log(window.$); // 可以获取到 jquery

或者在配置文件中引入

module.exports = {
    module: {
        rules: [
            {test: require.resolve("jquery"), // 如果 require 的时候引入了 jquery
                use: "expose-loader?$" // 那么将 $ 变量暴露到全局
            }
        ]
    }
}

expose-loader 是将一个模块的输出暴露给全局变量 ( 不限于第三方模块,任何模块都可以 ),但是 具体是暴露给 window 和 global 取决于配置文件中 target 的配置,如果是 node 则暴露给 global,如果是 web 则暴露给 window

② 我们也可以不直接在我们的 js 文件中引入第三方模块,就可以使用他们,可以通过 webpack 提供的 providePlugin 内置插件来实现,将第三方模块注入到每个模块中,当然也可以是其他任何一个模块,如:

new webpack.ProvidePlugin({
            $: "jquery",
            foo: path.resolve(__dirname,"./src/foo.js") // 自定义模块必须是模块的绝对路径否则无法解析
        })

我们就可以在任何一个模块中使用 $ 和 foo 变量了,因为他们被注入到了所有模块中,但是全局变量中是无法访问到的

③ 以上两种方式第三方模块都会被打包输出到我们的最终输出文件中,我们通常是不希望将第三方模块打包到我们的输出文件中的,因为会我们可以通过 cdn 的方式直接引入第三方模块,这个时候我们就需要告诉 webpack 哪些模块不需要打包输出,并且用运行环境中的全局变量进行替换,需要用到 webpack 提供的 externals 配置,如:

module.exports = {
    externals: {"jquery": "jQuery" // 排除 jquery 模块打包,并用浏览器中的 jQuery 替换掉 jquery}
}

此时虽然 js 模块中引入了 jquery,但是也不会被打包到输出文件中,并且 html 模板文件中直接通过 cdn 引入了 jQuery,所以全局变量中也可以直接访问到 jQuery

十一、webpack 图片的处理

webpack 是将所有文件都当做模块进行处理的,所以图片也被认为是一个模块,需要通过 require 或者 import 进行引入加载才能被正确打包输出。
如果想要加载并解析图片模块,那么必须使用合适的 loader,解析图片可以使用 file-loaer。如:

module.exports = {
    module: {
        rules: [
            {test: /\.(png|jpg|gif)$/, 
                use: "file-loader" // 用 file-loader 解析图片模块
            }
        ]
    }
}

图片会被单独打包出来放在输出目录中,但是输出的图片名称会发生变化,以 hash 值作为图片文件名 ,如果在 css 文件中使用图片,不需要通过 require 的方式,可以直接通过 url(“ 图片路径 ”) 的方式,因为 css-loader 对 url()进行了转换,会转换为 require 的方式

如果想在 html 文件中直接使用 <img/> 标签引入图片,那么 需要使用到 html-withimg-loader 来处理我们的 html 文件,图片同样会被打包单独输出到输出目录中。

module.exports = {
    module: {
        rules: [
            {
                test: /\.html$/, // 处理 html 文件
                use: {
                    loader: "html-withimg-loader",
                    options: {min: false // 不去除 html 中的换行符}
                }
            }
        ]
    }
}

如果想将图片打包成 base64 编码格式,那么 需要使用到 url-loader 来处理我们的图片url-loader 其实包含了 file-loader 的功能,因为其可以设置一个 limit,限制图片的大小,只有图片的大小在 limit 范围内才会被打包为 base64 编码格式,超过 limit 则还是单独打包图片到输出目录中

module.exports = {
    module: {
        rules: [
            {test: /\.(png|jpg|gif)$/, 
                use: {
                    loader: "url-loader", // 用 url-loader 解析图片模块
                    options: {
                        limit: 200 * 1024, // 限制图片大小为 200kb 内才打包为 base64 编码格式
                        outputPath: "/img/", // 将图片打包输出到输出目录的 img 目录下
                        publicPath: "http://www.lihb.com/" // 仅仅给输出的图片资源添加资源服务器存放地址
                    }
                }
            }
        ]
    }
}

url-loader 可以配置一个 outputPath 将图片输出到指定目录下面,实现资源的分类输出。还可以配置一个 publicPath,在引入图片资源的时候添加上图片资源所在的服务器地址。

正文完
 0