美文网首页
webpack高手秘籍(二)

webpack高手秘籍(二)

作者: vv_小虫虫 | 来源:发表于2020-07-09 18:08 被阅读0次

    chunkFilename

    string = '[id].js'

    刚已经介绍过filename字段,filename会作用于chunk文件跟入口文件,如果需要单独设置chunk文件的名称的话,就可以使用chunkFilename字段,用法跟filename一样,

    webpack.config.js:

    const path = require("path");
    module.exports = {
        mode: "production",
        context: path.resolve(__dirname, "./src"),
        // entry: ["babel-polyfill","./index.js"]
        entry: {
            app: "./index.js"
        },
        output: {
            path: path.join(process.cwd(), "lib"), //默认为path.join(process.cwd(), "dist")
            pathinfo: true,
            filename: "[name].[contenthash:16].[fullhash:16].[id].js",
            chunkFilename: "[id].js" //默认配置
        }
    };
    

    打包后lib目录底下会有四个文件:

    721.js
    721.js.LICENSE
    app.791277018b73acf2.1e4e0e1ed02ab7b6.143.js
    app.791277018b73acf2.1e4e0e1ed02ab7b6.143.js.LICENSE
    

    assetModuleFilename

    output.filename 一样,但是是关于 Asset Modules的内容, Asset Modules就是我们在webpack处理文件模块,处理完的结果会放入到output.path目录,然后名称配置就是assetModuleFilename选项,比如可以设置为以下配置:

    assetModuleFilename: 'images/[hash][ext]'
    

    每一个loader都会有自己单独的assetModuleFilename配置,如果没有就会用config的assetModuleFilename字段,默认为:

    assetModuleFilename: '[hash][ext]'
    

    library

    string object

    stringobject(从 webpack 3.1.0 开始;用于 libraryTarget: "umd"

    output.library 的值的作用,取决于output.libraryTarget 选项的值;完整的详细信息请查阅该章节。注意,output.libraryTarget 的默认选项是 var,所以如果使用以下配置选项:

    output: {
      library: "MyLibrary"
    }
    

    如果生成的输出文件,是在 HTML 页面中作为一个 script 标签引入,则变量 MyLibrary 将与入口文件的返回值绑定。

    看官网的解释有点抽象,我们结合demo来分析,首先我们修改一下我们的配置文件,

    webpack.config.js:

    const path = require("path");
    module.exports = {
        mode: "development",
        context: path.resolve(__dirname, "./src"),
        // entry: ["babel-polyfill","./index.js"]
        entry: {
            app: "./index.js"
        },
        output: {
            path: path.join(process.cwd(), "lib"), //默认为path.join(process.cwd(), "dist")
            pathinfo: true,
            filename: "[name].[contenthash:16].[fullhash:16].[id].js",
            chunkFilename: "[id].js",
            library: "demoSay",
        }
    };
    

    我们指定了library: "demoSay",然后我们执行编译看结果,

    lib/app.07ddaf5704298aa1.67ea617b36eb3365.app.js:

    var demoSay =
    /******/ (() => { // webpackBootstrap
    /******/    var __webpack_modules__ = ({
      ....
    
    

    代码有点多,我们直接运行test.html文件,

    test.html:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    <script src="./lib/app.07ddaf5704298aa1.67ea617b36eb3365.app.js"></script>
    </body>
    </html>
    

    然后我们在浏览器调试窗打印一下demoSay:

    demoSay
    {}
    

    可以看到,demoSay返回了一个空对象,是的! 我们打开我们的入口文件index.js看看,

    src/index.js:

    __webpack_public_path__ = "http://localhost:8080/webpack-demo/lib/";
    import("./demo-publicpath").then((demoPublicPath) => {
        demoPublicPath.say();
    });
    

    我们直接执行了代码,并没有任何导出的内容,所以我们拿到的demoSay是一个空对象。

    我们修改一下index.js入口文件,让它默认导出一个demoSay方法,

    webpack.config.js:

    const path = require("path");
    module.exports = {
        mode: "development",
        context: path.resolve(__dirname, "./src"),
        // entry: ["babel-polyfill","./index.js"]
        entry: {
            app: "./index.js"
        },
        output: {
            path: path.join(process.cwd(), "lib"), //默认为path.join(process.cwd(), "dist")
            pathinfo: true,
            filename: "[name].[contenthash:16].[fullhash:16].[id].js",
            chunkFilename: "[id].js",
            library: "demoSay",
            libraryExport: "default",
        }
    };
    

    src/index.js:

    __webpack_public_path__ = "http://localhost:8080/webpack-demo/lib/";
    export default function demoSay() {
        import("./demo-publicpath").then((demoPublicPath) => {
            demoPublicPath.say();
        });
    }
    

    打包编译后lib目录下面文件:

    app.054433abb6526c08.97d75e7a53c562f4.app.js
    demo-publicpath_js.js
    

    修改test.html:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    <script src="./lib/app.054433abb6526c08.97d75e7a53c562f4.app.js"></script>
    </body>
    </html>
    

    运行test.html文件,然后打印并运行demoSay变量:

    demoSay
    ƒ demoSay() {
        __webpack_require__.e(/*! import() */ "demo-publicpath_js").then(__webpack_require__.bind(__webpack_require__, /*! ./demo-publicpath */ "./demo-publicpath.js")).then((demoPublicPath) …
    demoSay();
    undefined
    

    可以看到,这一次demoSay直接变成了我们入口文件中输出的demoSay方法,然后直接调用后页面上出现了“hello webpack”。

    libraryExport

    string default to ""

    细心的小伙伴已经发现了,我们在上面的demo中还加入了一个叫libraryExport的字段,然后我们给了一个“default”,也就是告诉webpack,默认导出对应模块的default变量。

    比如我们入口文件,

    src/index.js:

    __webpack_public_path__ = "http://localhost:8080/webpack-demo/lib/";
    export default function demoSay() {
        import("./demo-publicpath").then((demoPublicPath) => {
            demoPublicPath.say();
        });
    }
    

    我们默认导出模块的default变量,在这里也就是我们的demoSay方法,所以我们在运行的时候可以直接调用demoSay方法。

    libraryTarget

    string 默认值:"var"

    webpack5.0中可以为:"var" | "module" | "assign" | "this" | "window" | "self" | "global" | "commonjs" | "commonjs2" | "commonjs-module" | "amd" | "amd-require" | "umd" | "umd2" | "jsonp" | "system"

    配置如何暴露 library。可以使用下面的选项中的任意一个。注意,此选项与分配给 output.library 的值一同使用。

    上面我们已经演示过var了,也就是在全局暴露一个变量,然后变量名为library的配置值,

    module

    这个是webpack5特有的一个属性,需要跟另外一个叫experiments属性的一起用,experiments就是还在实验中的一些特性(不建议开启experiments的更多用法可以参考官网)。

    我们修改一下配置文件,webpack.config.js:

    const path = require("path");
    module.exports = {
        mode: "development",
        context: path.resolve(__dirname, "./src"),
        // entry: ["babel-polyfill","./index.js"]
        entry: {
            app: "./index.js"
        },
        output: {
            path: path.join(process.cwd(), "lib"), //默认为path.join(process.cwd(), "dist")
            pathinfo: true,
            filename: "[name].[contenthash:16].[fullhash:16].[id].js",
            chunkFilename: "[id].js",
            // library: "demoSay",
            // libraryExport: "default",
            libraryTarget: "module",
    
        },
        experiments: {
            outputModule: true
        }
    };
    

    我们把libraryTarget设置为了“module”,然后把experiments.outputModule设置为了“true”,然后我们执行打包命令看一下lib目录:

    app.4d52f6f40fbe4016.28aa8b97dd0f050f.app.js:

    /******/ "use strict";
    /******/ var __webpack_modules__ = ({
    
    /***/ "./index.js":
    /*!******************!*\
      !*** ./index.js ***!
      \******************/
    /*! export default [provided] [used] [missing usage info prevents renaming] */
    /*! other exports [not provided] [no usag
    ...
    

    demo-publicpath_js.js:

    (window["webpackJsonp"] = window["webpackJsonp"] || []).push([["demo-publicpath_js"],{
    
    /***/ "./demo-publicpath.js":
    /*!****************************!*\
      !*** ./demo-publicpath.js ***!
      \****************************/
    /*! export say [provided] [no usage info] [missing usage info prevents renaming] */
    /*! other exports [not provided] [no usage info] */
    /*! runtime requirements: __webpack_exports__, __webpack_require__.d, __webpack_require__.r, __webpack_require__.* */
    /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
    
    "use strict";
    eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */   \"say\": () => /* binding */ say\n/* harmony export */ });\nfunction say() {\n    document.body.append(document.createTextNode(\"hello webpack\"))\n}\n\n//# sourceURL=webpack:///./demo-publicpath.js?");
    
    /***/ })
    
    }]); 
    

    那我们怎么用呢?

    我们修改一下我们的test.html文件:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    <script src="./lib/app.c332dfffe553ed52.a7f098bb4c793dee.app.js"></script>
    <script>
        var demoSay = __webpack_require__("./index.js").default;
        demoSay();
    </script>
    </body>
    </html>
    
     var demoSay = __webpack_require__("./index.js").default;
    

    就可以拿到demoSay方法了,然后调用demoSay方法就会在页面上看到“hello webpack”了。

    assign

    直接在全局环境中暴露一个未申明的变量“demoSay”,js中可以当全局变量访问,所以是可以直接执行的:

    demoSay()
    
    this

    直接在this上面绑定一个变量“demoSay”,浏览器中this代表window的意思,所以我们也是可以在全局环境中访问demoSay方法。

    this["demoSay"] =xxx
    

    windowselfglobal都是一样的操作,在浏览器环境中都代表window,我们也是可以在全局环境中访问demoSay方法。

    jsonp

    通过jsonp的形式来导出当前模块,我们修改一下配置文件,把libraryTarget改成“jsonp”,

    webpack.config.js:

    const path = require("path");
    module.exports = {
        mode: "development",
        context: path.resolve(__dirname, "./src"),
        // entry: ["babel-polyfill","./index.js"]
        entry: {
            app: "./index.js"
        },
        output: {
            path: path.join(process.cwd(), "lib"), //默认为path.join(process.cwd(), "dist")
            pathinfo: true,
            filename: "[name].[contenthash:16].[fullhash:16].[id].js",
            chunkFilename: "[id].js",
            library: "demoSay",
            libraryExport: "default",
            libraryTarget: "jsonp",
    
        },
        experiments: {
            // outputModule: true
        }
    };
    

    然后我们打包后在html中引入打包完毕的js文件,最后window对象上面注册一个demoSay方法用来作为jsonp的回调函数,

    test.html:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    <script>
        function demoSay(module) {
            module();
        }
    </script>
    <script src="./lib/app.f366a0d04be496a3.312b7fd4a12f5d79.app.js"></script>
    </body>
    </html>
    

    ok~ 运行是可以正常显示的,我就不截图了,小伙伴自己一定要去跑跑代码哦。

    其它配置

    output的其它一些配置大家可以自己去参考官网跑一下demo(不过我们基本项目中也用不到),这里就不分析了。

    Module

    这些选项决定了如何处理项目中的不同类型的模块

    rules

    array

    创建模块时,匹配请求的规则数组。这些规则能够修改模块的创建方式。这些规则能够对模块(module)应用 loader,或者修改解析器(parser)。

    noParse

    regExp | [RegExp]

    RegExp | [RegExp] | function(从 webpack 3.0.0 开始)

    防止 webpack 解析那些任何与给定正则表达式相匹配的文件。忽略的文件中不应该含有 import, require, define 的调用,或任何其他导入机制。忽略大型的 library 可以提高构建性能。

    noParse: /jquery|lodash/
    
    // 从 webpack 3.0.0 开始
    noParse: function(content) {
      return /jquery|lodash/.test(content);
    }
    

    怎么理解呢?比如我们demo中前面讲entry的时候,我们导入了一个叫“babel-polyfill”的babel垫片,其实我们当成module导入的时候,webpack会去分析babel-polyfill的模块依赖,是会影响编译速度的,所以我们改一下配置文件,让它默认不去解析babel-polyfill,

    webpack.config.js:

    const path = require("path");
    module.exports = {
        mode: "development",
        context: path.resolve(__dirname, "./src"),
        // entry: ["babel-polyfill","./index.js"]
        entry: {
            app: ["babel-polyfill","./index.js"]
        },
        output: {
            path: path.join(process.cwd(), "lib"), //默认为path.join(process.cwd(), "dist")
            pathinfo: true,
            filename: "[name].[contenthash:16].[fullhash:16].[id].js",
            chunkFilename: "[id].js",
            library: "demoSay",
            libraryExport: "default",
            libraryTarget: "jsonp",
    
        },
        experiments: {
            // outputModule: true
        },
        module: {
            noParse: /babel-polyfill/
        }
    };
    

    然后我们编译运行webpack看结果,

    Lib/app.c2906c554fe563aa.9660e03dcc700539.app.js:

    demoSay(/******/ (() => { // webpackBootstrap
    /******/    var __webpack_modules__ = ({
    
    /***/ "../node_modules/babel-polyfill/lib/index.js":
    /*!***************************************************!*\
      !*** ../node_modules/babel-polyfill/lib/index.js ***!
      \***************************************************/
    /*! exports [maybe provided (runtime-defined)] [no usage info] */
    /*! runtime requirements: module, __webpack_exports__, top-level-this-exports */
    /***/ (function(module, exports) {
    
    eval("\"use strict\";\n\nrequire(\"core-js/shim\");\n\nrequire(\"regenerator-runtime/runtime\");\n\nrequire(\"core-js/fn/regexp/escape\");\n\nif (global._babelPolyfill) {\n  throw new Error(\"only one instance of babel-polyfill is allowed\");\n}\nglobal._babelPolyfill = true;\n\nvar DEFINE_PROPERTY = \"defineProperty\";\nfunction define(O, key, value) {\n  O[key] || Object[DEFINE_PROPERTY](O, key, {\n    writable: true,\n    configurable: true,\n    value: value\n  });\n}\n\ndefine(String.prototype, \"padLeft\", \"\".padStart);\ndefine(String.prototype, \"padRight\", \"\".padEnd);\n\n\"pop,reverse,shift,keys,values,entries,indexOf,every,some,forEach,map,filter,find,findIndex,includes,join,slice,concat,push,splice,unshift,sort,lastIndexOf,reduce,reduceRight,copyWithin,fill\".split(\",\").forEach(function (key) {\n  [][key] && define(Array, key, Function.call.bind([][key]));\n});\n\n//# sourceURL=webpack://demoSay/../node_modules/babel-polyfill/lib/index.js?");
    ..
    

    可以看到,webpack设置了noParse后就不会再去分析babel-polifill的模块依赖,直接把babel-polyfill/lib/index.js中的源文件给导入进来了,

    node_modules/babel-polyfill/lib/index.js:

    "use strict";
    
    require("core-js/shim");
    
    require("regenerator-runtime/runtime");
    
    require("core-js/fn/regexp/escape");
    
    if (global._babelPolyfill) {
      throw new Error("only one instance of babel-polyfill is allowed");
    }
    global._babelPolyfill = true;
    
    var DEFINE_PROPERTY = "defineProperty";
    function define(O, key, value) {
      O[key] || Object[DEFINE_PROPERTY](O, key, {
        writable: true,
        configurable: true,
        value: value
      });
    }
    
    define(String.prototype, "padLeft", "".padStart);
    define(String.prototype, "padRight", "".padEnd);
    
    "pop,reverse,shift,keys,values,entries,indexOf,every,some,forEach,map,filter,find,findIndex,includes,join,slice,concat,push,splice,unshift,sort,lastIndexOf,reduce,reduceRight,copyWithin,fill".split(",").forEach(function (key) {
      [][key] && define(Array, key, Function.call.bind([][key]));
    });
    

    这样在浏览器直接运行肯定是不行的,运行test.html:

    Navigated to http://localhost:8091/webpack-demo/test.html?_ijt=953ti63cuqmunn4c0eih81htnn
    index.js:3 Uncaught ReferenceError: require is not defined
        at eval (index.js:3)
        at Object.../node_modules/babel-polyfill/lib/index.js (app.c2906c554fe563aa.9660e03dcc700539.app.js:12)
        at __webpack_require__ (app.c2906c554fe563aa.9660e03dcc700539.app.js:49)
        at app.c2906c554fe563aa.9660e03dcc700539.app.js:244
        at app.c2906c554fe563aa.9660e03dcc700539.app.js:246
    eval @ index.js:3
    ../node_modules/babel-polyfill/lib/index.js @ app.c2906c554fe563aa.9660e03dcc700539.app.js:12
    __webpack_require__ @ app.c2906c554fe563aa.9660e03dcc700539.app.js:49
    (anonymous) @ app.c2906c554fe563aa.9660e03dcc700539.app.js:244
    (anonymous) @ app.c2906c554fe563aa.9660e03dcc700539.app.js:246
    

    可以看到,直接报错了,因为我们导入的是babel-polyfill未打包完成的入口,我们改成打包好的依赖,

    webpack.config.js:

    const path = require("path");
    module.exports = {
        mode: "development",
        context: path.resolve(__dirname, "./src"),
        // entry: ["babel-polyfill","./index.js"]
        entry: {
            app: ["babel-polyfill/dist/polyfill.min.js","./index.js"]
        },
        output: {
            path: path.join(process.cwd(), "lib"), //默认为path.join(process.cwd(), "dist")
            pathinfo: true,
            filename: "[name].[contenthash:16].[fullhash:16].[id].js",
            chunkFilename: "[id].js",
            library: "demoSay",
            libraryExport: "default",
            libraryTarget: "jsonp",
    
        },
        experiments: {
            // outputModule: true
        },
        module: {
            noParse: /babel-polyfill/
        }
    };
    

    再次打包运行看结果,

    lib/app.9e96cc5a4f61983c.e21638e08b6f3396.app.js:

    demoSay(/******/ (() => { // webpackBootstrap
    /******/    var __webpack_modules__ = ({
    
    /***/ "../node_modules/babel-polyfill/dist/polyfill.min.js":
    /*!***********************************************************!*\
      !*** ../node_modules/babel-polyfill/dist/polyfill.min.js ***!
      \***********************************************************/
    /*! exports [maybe provided (runtime-defined)] [no usage info] */
    /*! runtime requirements: module, __webpack_exports__, top-level-this-exports */
    /***/ (function(module, exports) {
    
    eval("!function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var c=\"function\"==typeof require&&require;if(!u&&c)return c(o,!0);if(i)return i(o,!0);var a=new Error(\"Cannot find module '\"+o+\"'\");throw a.code=\"MODULE_NOT_FOUND\",a}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(n){var r=t[o][1][n];return s(r||n)},f,f.exports,e,t,n,r)}return n[o].exports}for(var i=\"function\"==typeof require&&require,o=0;o<r.length;o++)s(r[o]);return s}({1:[function(t,n,r){(function(n){\"use strict\";function define(t,n,e){t[n]||Object[r](t,n,{writable:!0,configurable:!0,value:e})}if(t(327),t(328),t(2),n._babelPolyfill)throw new Error(\"only one instance of babel-polyfill is allowed\");n._babelPolyfill=!0;var r=\"defineProperty\";define(String.prototype,\"padLeft\",\"\".padStart),define(String.prototype,\"padRight\",\"\".padEnd),\"pop,reverse,shift,keys,values,entries,indexOf,every,some,forEach,map,filter,find,findIndex,includes,join,slice,concat,push,splice,unshift,sort,lastIndexOf,reduce,reduceRight,copyWithin,fill\".split(\",\").forEach(function(t){[][t]&&define(Array,t,Function.call.bind([][t]))})}).call(this,\"undefined\"!=typeof global?global:\"undefined\"!=typeof self?self:\"undefined\"!=typeof window?window:{})},{2:2,327:327,328:328}],2:[function(t,n,r){t(130),n.exports=t(23).RegExp.escape},{130:130,23:23}],3:[function(t,n,r){n.exports=function(t){if(\"function\"!=typeof t)throw TypeError(t+\" is not a function!\");return t}},{}],4:[function(t,n,r){var e=t(18);n.exports=function(t,n){if(\"number\"!=typeof t&&\"Number\"!=e(t))throw TypeError(n);return+t}},{18:18}],5:[function(t,n,r){var e=t(128)(\"unscopables\"),i=Array.prototype;void 0==i[e]&&t(42)(i,e,{}),n.exports=function(t){i[e][t]=!0}},{128:128,42:42}],6:[function(t,n,r){n.exports=function(t,n,r,e){if(!(t instanceof n)||void 0!==e&&e in t)throw TypeError(r+\": incorrect invocation!\");return t}},{}],7:[function(t,n,r){var e=t(51);n.exports=function(t){if(!e(t))throw TypeError(t+\" is not an object!\");return t}},{51:51}],8:[function(t,n,r){\"use strict\";var e=t(119),i=t(114),o=t(118);n.exports=[].copyWithin||function copyWithin(t,n){var r=e(this),u=o(r.length),c=i(t,u),a=i(n,u),f=arguments.length>2?arguments[2]:void 0,s=Math.min((void 0===f?u:i(f,u))-a,u-c),l=1;for(a<c&&c<a+s&&(l=-1,a+=s-1,c+=s-1);s-- >0;)
         ...
    

    可以看到,直接把babel-polyfill打包好的源码给导入进来了,这次我们就可以在浏览器中正常运行test.html文件了。

    Rule

    每个规则可以分为三部分 - 条件(condition),结果(result)和嵌套规则(nested rule)。

    Rule 条件

    条件有两种输入值:

    1. resource:请求文件的绝对路径。它已经根据 resolve 规则解析。
    2. issuer: 被请求资源(requested the resource)的模块文件的绝对路径。是导入时的位置。

    例如:app.js 导入 './style.css',resource 是 /path/to/style.css. issuer 是 /path/to/app.js

    在规则中,属性 test, include, excluderesource 对 resource 匹配,并且属性 issuer 对 issuer 匹配。

    当使用多个条件时,所有条件都匹配。

    小心!resource 是文件的解析路径,这意味着符号链接的资源是真正的路径,而不是符号链接位置。在使用工具来符号链接包的时候(如 npm link)比较好记,像 /node_modules/ 等常见条件可能会不小心错过符号链接的文件。注意,可以通过 resolve.symlinks 关闭符号链接解析(以便将资源解析为符号链接路径)。

    Rule 结果

    规则结果只在规则条件匹配时使用。

    规则有两种输入值:

    1. 应用的 loader:应用在 resource 上的 loader 数组。
    2. Parser 选项:用于为模块创建解析器的选项对象。

    这些属性会影响 loader:loader, options, use

    也兼容这些属性:query, loaders

    enforce 属性会影响 loader 种类。不论是普通的,前置的,后置的 loader。

    parser 属性会影响 parser 选项。

    嵌套的 Rule

    可以使用属性 rulesoneOf 指定嵌套规则。

    这些规则用于在规则条件(rule condition)匹配时进行取值。


    以上内容都是官网直接copy过来的,理解起来比较抽象,我们直接用demo来分析一下,我们从0开始搭建一个简单的vue项目:webpack+vue+sass

    首先我们在src目录底下创建一个demo-vue.vue文件,

    src/demo-vue.vue:

    <template>
    <div class="demo-vue">hello fox</div>
    </template>
    <script>
        export default {
            name: 'demo-vue',
        };
    </script>
    <style lang='scss' scoped>
    .demo-vue{
        color: red;
    }
    </style>
    
    

    然后修改一下我们的入口文件index.js,

    src/index.js:

    __webpack_public_path__ = "http://localhost:8091/webpack-demo/lib/";
    import demoVue from "./demo-vue.vue";
    import Vue from "vue";
    new Vue({
        el: "#app",
        render:(h)=>h(demoVue)
    });
    

    代码很简单,稍微有点vue基础的应该是没啥问题的,看不懂的也没关系,我们继续往下分析rule参数。

    enforce

    string 可能的值有:"pre" | "post"``

    所有 loader 通过 前置, 行内, 普通, 后置 排序,并按此顺序使用。

    普通的loader是从下往上使用。

    比如我们demo中需要处理scss样式语法:

    <style lang='scss' scoped>
    .demo-vue{
        color: red;
    }
    </style>
    

    所以我们需要去配置一下scss解析器,在配置之前我们需要安装一些依赖,

    处理vue文件:

    vue 、vue-loader、vue-loader-plugin、vue-template-compiler `

    npm install -S vue && npm install -D vue-loader && npm install -D vue-loader-plugin && npm 
    install -D vue-template-compiler
    

    处理scss文件:

    sass、sass-loader、postcss-loader、autoprefixer、css-loader、style-loader

    大家一样安装一遍,别漏了,我就不演示了。

    webpack在处理scss的时候顺序应该是这样的:

    1. vue-loader 负责解析vue模版中的style标签中的scss内容。
    2. sass-loader 负责解析scss为css。
    3. postcss-loader 对css做优化处理(这里用到了自动添加前缀的插件autoprefixer)。
    4. css-loader负责把处理完毕的css代码变成一个module。
    5. style-loader获取css-loader处理过后的module,然后把动态创建style标签加载module内容。

    所以我们修改一些我们的webpack配置文件,

    webpack.config.js:

    const path = require("path");
    module.exports = {
        mode: "development",
        context: path.resolve(__dirname, "./src"),
        // entry: ["babel-polyfill","./index.js"]
        entry: {
            app: ["./index.js"]
        },
        output: {
            path: path.join(process.cwd(), "lib"), //默认为path.join(process.cwd(), "dist")
            pathinfo: true,
            filename: "[name].[contenthash:16].[fullhash:16].[id].js",
            chunkFilename: "[id].js",
            // library: "demoSay",
            // libraryExport: "default",
            // libraryTarget: "jsonp",
    
        },
        experiments: {
            // outputModule: true
        },
        module: {
            noParse: /babel-polyfill/,
            rules: [
                {
                    test: /.vue$/,
                    use: 'vue-loader',
                },
                {
                    test: /\.(sass|scss)$/,
                    use: [
                        "style-loader",
                        "css-loader",
                        {
                            loader: "postcss-loader",
                            options: {
                                ident: "postcss",
                                config: {
                                    path: path.resolve(__dirname,"./postcss.config.js")
                                }
                            }
                        },
                        "sass-loader"
                    ]
                }
            ]
        },
        plugins: [
            new (require("vue-loader-plugin"))()
        ]
    };
    

    因为postcss需要指定一个配置文件,所以我们在根目录指定一个postcss.config.js文件作为postcss-loader的参数,

    postcss.config.js:

    module.exports={
        plugins:[
            require('autoprefixer')(),
        ]
    };
    

    autoprefixer又需要根据你浏览器的定义做前缀添加,所以我们在根目录创建一个.browserslistrc文件给autoprefixer插件,

    .browserslistrc:

    > 0.25%, not dead
    

    我们在讲babel的时候也提到了.browserslistrc文件,所以这里我就不分析了。

    可以看到上面的配置文件,我们配置了一个.vue文件的loader “vue-loader”,然后配置了很多个loader来处理.scss文件.

    我们运行一下webpack:

    ➜  webpack-demo git:(master) ✗ npx webpack
    Hash: 01d4acbf09e4ec864dc8
    Version: webpack 5.0.0-beta.7
    Time: 2076ms
    Built at: 2020-07-09 14:04:46
    1 asset
    Entrypoint app = app.cb21e253f621ffc6.01d4acbf09e4ec86.app.js
    ./index.js 186 bytes [built]
    ./demo-vue.vue 1.19 KiB [built]
    ../node_modules/vue/dist/vue.runtime.esm.js 222 KiB [built]
    ./demo-vue.vue?vue&type=template&id=47a7e22a&scoped=true& 212 bytes [built]
    ./demo-vue.vue?vue&type=script&lang=js& 258 bytes [built]
    ./demo-vue.vue?vue&type=style&index=0&id=47a7e22a&lang=scss&scoped=true& 824 bytes [built]
    ../node_modules/vue-loader/lib/runtime/componentNormalizer.js 2.71 KiB [built]
    ../node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!../node_modules/vue-loader/lib??vue-loader-options!./demo-vue.vue?vue&type=template&id=47a7e22a&scoped=true& 264 bytes [built]
    ../node_modules/vue-loader/lib??vue-loader-options!./demo-vue.vue?vue&type=script&lang=js& 52 bytes [built]
    ../node_modules/style-loader/dist/cjs.js!../node_modules/css-loader/dist/cjs.js!../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../node_modules/postcss-loader/src??ruleSet[0].rules[0].use[2]!../node_modules/sass-loader/dist/cjs.js!../node_modules/vue-loader/lib??vue-loader-options!./demo-vue.vue?vue&type=style&index=0&id=47a7e22a&lang=scss&scoped=true& 810 bytes [built]
    ../node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js 6.64 KiB [built]
    ../node_modules/css-loader/dist/cjs.js!../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../node_modules/postcss-loader/src??ruleSet[0].rules[0].use[2]!../node_modules/sass-loader/dist/cjs.js!../node_modules/vue-loader/lib??vue-loader-options!./demo-vue.vue?vue&type=style&index=0&id=47a7e22a&lang=scss&scoped=true& 278 bytes [built]
    ../node_modules/css-loader/dist/runtime/api.js 2.46 KiB [built]
        + 5 hidden modules
    ➜  webpack-demo git:(master) ✗ 
    
    

    再看一下生成的文件lib/*:

    lib/app.cb21e253f621ffc6.01d4acbf09e4ec86.app.js:

    /******/ (() => { // webpackBootstrap
    /******/    var __webpack_modules__ = ({
    
    /***/ "../node_modules/css-loader/dist/cjs.js!../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../node_modules/postcss-loader/src/index.js??ruleSet[0].rules[0].use[2]!../node_modules/sass-loader/dist/cjs.js!../node_modules/vue-loader/lib/index.js??vue-loader-options!./demo-vue.vue?vue&type=style&index=0&id=47a7e22a&lang=scss&scoped=true&":
    /*!*******************************************************************************************************************************************************************************************************************************************************************************************************************************************!*\
      !*** ../node_modules/css-loader/dist/cjs.js!../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../node_modules/postcss-loader/src??ruleSet[0].rules[0].use[2]!../node_modules/sass-loader/dist/cjs.js!../node_modules/vue-loader/lib??vue-loader-options!./demo-vue.vue?vue&type=style&index=0&id=47a7e22a&lang=scss&scoped=true& ***!
      \*******************************************************************************************************************************************************************************************************************************************************************************************************************************************/
    /*! exports [maybe provided (runtime-defined)] [no usage info] */
    /*! runtime requirements: __webpack_require__, __webpack_exports__, module */
    /***/ ((module, exports, __webpack_require__) => {
      ...
    

    代码有点多,而且很难看懂,我们还是在我们的test.html中引用试试,

    test.html:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    <div id="app"></div>
    <script src="./lib/app.cb21e253f621ffc6.01d4acbf09e4ec86.app.js"></script>
    </body>
    </html>
    

    然后运行浏览器:

    在这里插入图片描述

    可以看到,页面上已经显示了我们的“hello fox”并且scss样式也是起作用了,style-loader会在我们的html文件中插入以下代码:

    <style>.demo-vue[data-v-47a7e22a] {
      color: red;
    }</style>
    

    ok! 代码也讲完了,还是没讲到enforce的用法,demo的webpack配置中我们处理scss文件的时候用的rule配置是这样的:

    {
                    test: /\.(sass|scss)$/,
                    use: [
                        "style-loader",
                        "css-loader",
                        {
                            loader: "postcss-loader",
                            options: {
                                ident: "postcss",
                                config: {
                                    path: path.resolve(__dirname,"./postcss.config.js")
                                }
                            }
                        },
                        "sass-loader"
                    ]
                }
    

    webpack默认顺序是(从下往上):

    vue-loader-->sass-loader-->postcss-loader-->css-loader-->style-loader

    那如果我们需要改变它的默认顺序我们该怎么做呢?

    我们给sass-loader加一个enforce选项“pre”,让sass-loader第一个执行,

    webpack.config.js:

    {
                    test: /\.(sass|scss)$/,
                    enforce: "pre",
                    use: "sass-loader",
                },
    

    运行后会发现报错:

    ERROR in ./demo-vue.vue?vue&type=style&index=0&id=47a7e22a&lang=scss&scoped=true& (../node_modules/css-loader/dist/cjs.js!../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../node_modules/postcss-loader/src??ruleSet[0].rules[0].use!../node_modules/vue-loader/lib??vue-loader-options!../node_modules/sass-loader/dist/cjs.js!./demo-vue.vue?vue&type=style&index=0&id=47a7e22a&lang=scss&scoped=true&)
    Module build failed (from ../node_modules/sass-loader/dist/cjs.js):
    SassError: expected "{".
       ╷
    13 │ </style>
    
    

    我们可以利用enforce修改一下loader的默认加载顺序。

    exclude

    string、array、function

    哪些模块不需要被加载,如果你提供了 Rule.exclude 选项,就不能再提供 Rule.resource

    比如我们demo中sass-loader只加载.sass或.scss文件,我们就可以这样配置:

     {
                    exclude: (info)=>{
                        return !/\.(sass|scss)$/.test(info);
                    },
                    use: "sass-loader",
                },
    
    include

    string、array、function

    跟exclude相反的操作,比如上面的配置我们可以用include这样来:

      {
                    include: (info)=>{
                        return /\.(sass|scss)$/.test(info);
                    },
                    use: "sass-loader",
                },
    
    Issuer

    规定了哪个文件发起的request才需要被loader,比如我们这里的scss是由demo-vue.vue文件发起的,所以可以用Issuer来设置只有当demo-vue.vue文件发起的.scss模块才解析:

     {
                    issuer: (info)=>{
                        return /\.(sass|scss)$/.test(info)&&!/demo-vue\.vue/.test(info);
                    },
                    use: "sass-loader",
                },
    
    loader

    Rule.loaderRule.use: [ { loader } ] 的简写。详细请查看 Rule.useUseEntry.loader

    oneOf

    当规则匹配时,只使用第一个匹配规则。

    {
      test: /.png/,
      oneOf: [
        {
          resourceQuery: /inline/, // pub1.png?inline
          use: 'url-loader'
        },
        {
          resourceQuery: /external/, // pub1.png?external
          use: 'file-loader'
        }
      ]
    }
    

    我们试一下,比如我们需要加载一张图片,demo根目录下的,pub1.png图片,首先我们安装一下url-loader跟file-loader:

    • file-loader (会在output.path目录生成一个文件,返回这个文件的引用地址给调用处)
    • url-loader(文件size小于limit的话就直接把文件打成base64格式字符串,大于则用file-loader)
    npm install -D url-loader && npm install -D file-loader
    

    然后我们修改一下我们的demo-vue.vue文件,添加一个图片,

    demo-vue.vue:

    <template>
    <div class="demo-vue">
        hello fox
        <img :src="pubImg">
    </div>
    </template>
    
    <script>
        export default {
            name: 'demo-vue',
            data(){
                return {
                    pubImg: require("../pub1.png?external").default
                }
            }
        };
    </script>
    <style lang='scss' scoped>
    .demo-vue{
        color: red;
    }
    </style>
    
    

    可以看到,我们用require加载了一张图片,然后给图片加了一个external后缀,然后我们配置一下webpack的配置文件,

    webpack.config.js:

    const path = require("path");
    module.exports = {
        mode: "development",
        context: path.resolve(__dirname, "./src"),
        // entry: ["babel-polyfill","./index.js"]
        entry: {
            app: ["./index.js"]
        },
        output: {
            path: path.join(process.cwd(), "lib"), //默认为path.join(process.cwd(), "dist")
            pathinfo: true,
            filename: "[name].[contenthash:16].[fullhash:16].[id].js",
            chunkFilename: "[id].js",
            // library: "demoSay",
            // libraryExport: "default",
            // libraryTarget: "jsonp",
    
        },
        experiments: {
            // outputModule: true
        },
        module: {
            noParse: /babel-polyfill/,
            rules: [
                {
                    test: /.vue$/,
                    use: 'vue-loader',
                },
                {
                    test: /\.(sass|scss)$/,
                    use: "style-loader",
                },
                {
                    test: /\.(sass|scss)$/,
                    use: "css-loader",
                },
                {
                    test: /\.(sass|scss)$/,
                    use: {
                        loader: "postcss-loader",
                        options: {
                            config: {
                                path: path.resolve(__dirname,"./postcss.config.js")
                            }
                        }
                    },
                },
                {
                    issuer: (info)=>{
                        return /\.(sass|scss)$/.test(info)&&!/demo-vue\.vue/.test(info);
                    },
                    use: "sass-loader",
                },
                {
                    test: /\.png$/,
                    oneOf: [
                        {
                            resourceQuery: /inline/,
                            loader: "url-loader",
                            options: {
                                limit: 1024*1024*10
                            }
                        },
                        {
                            resourceQuery: /external/,
                            loader: "file-loader",
                        }
                    ]
                }
            ]
        },
        plugins: [
            new (require("vue-loader-plugin"))()
        ]
    };
    

    可以看到我们配置了两个loader,当图片指定了“inline”后缀就用url-loader,指定了“external”就使用file-loader。

    上面demo我们指定的是“external”,也就是说会以原文件的形式输出到output.path目录,

    我们运行webpack打包:

    63fe41824cb8236c0896a71b7df7f461.png
    app.f18f2f6646d5afa5.3be9121297511f01.app.js
    

    可以看到,生成了两个文件,我们运行一下test.html文件,

    test.html:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    <div id="app"></div>
    <script src="./lib/app.f18f2f6646d5afa5.3be9121297511f01.app.js"></script>
    </body>
    </html>
    
    在这里插入图片描述

    Ok! 我们已经看到效果了,inline我就不测试了,小伙伴自己试试哦!

    resource

    条件会匹配 resource。既可以提供 Rule.resource 选项,也可以使用快捷选项 Rule.testRule.excludeRule.include。在 Rule 条件 中查看详细。

    resourceQuery

    条件会匹配 resourceQuery,请求资源的参数,上面在使用file-loader跟url-loader的时候使用过,就不演示了,比如:

    require("../pub1.png?external").default
    

    external就是resourceQuery一部分,告诉webpack带有这个参数的时候才会用当前loader。

    rules

    规则数组,当规则匹配时使用。

    test

    如果你提供了一个 Rule.test 选项,就不能再提供 Rule.resource。详细请查看 Rule.resourceCondition.test,跟include一样,利用正则表达式test判断,返回true才会用当前loader。

    use

    应用于模块的 UseEntries 列表。每个入口(entry)指定使用一个 loader。

    传递字符串(如:use: [ "style-loader" ])是 loader 属性的简写方式(如:use: [ { loader: "style-loader "} ])。

    比如我们demo现在的配置文件为,

    webpack.config.js:

    const path = require("path");
    module.exports = {
        mode: "development",
        context: path.resolve(__dirname, "./src"),
        // entry: ["babel-polyfill","./index.js"]
        entry: {
            app: ["./index.js"]
        },
        output: {
            path: path.join(process.cwd(), "lib"), //默认为path.join(process.cwd(), "dist")
            pathinfo: true,
            filename: "[name].[contenthash:16].[fullhash:16].[id].js",
            chunkFilename: "[id].js",
            // library: "demoSay",
            // libraryExport: "default",
            // libraryTarget: "jsonp",
    
        },
        experiments: {
            // outputModule: true
        },
        module: {
            noParse: /babel-polyfill/,
            rules: [
                {
                    test: /.vue$/,
                    use: 'vue-loader',
                },
                {
                    test: /\.(sass|scss)$/,
                    use: "style-loader",
                },
                {
                    test: /\.(sass|scss)$/,
                    use: "css-loader",
                },
                {
                    test: /\.(sass|scss)$/,
                    use: {
                        loader: "postcss-loader",
                        options: {
                            config: {
                                path: path.resolve(__dirname,"./postcss.config.js")
                            }
                        }
                    },
                },
                {
                    issuer: (info)=>{
                        return /\.(sass|scss)$/.test(info)&&!/demo-vue\.vue/.test(info);
                    },
                    use: "sass-loader",
                },
                {
                    test: /\.png$/,
                    oneOf: [
                        {
                            resourceQuery: /inline/,
                            loader: "url-loader",
                            options: {
                                limit: 1024*1024*10
                            }
                        },
                        {
                            resourceQuery: /external/,
                            loader: "file-loader",
                        }
                    ]
                }
            ]
        },
        plugins: [
            new (require("vue-loader-plugin"))()
        ]
    };
    

    我们对.scss文件的匹配用了很多loader,但是这些loader争对的都是.scss文件,所以我们可以利用use合并这些loader,

    webpack.config.js:

    const path = require("path");
    module.exports = {
        mode: "development",
        context: path.resolve(__dirname, "./src"),
        // entry: ["babel-polyfill","./index.js"]
        entry: {
            app: ["./index.js"]
        },
        output: {
            path: path.join(process.cwd(), "lib"), //默认为path.join(process.cwd(), "dist")
            pathinfo: true,
            filename: "[name].[contenthash:16].[fullhash:16].[id].js",
            chunkFilename: "[id].js",
            // library: "demoSay",
            // libraryExport: "default",
            // libraryTarget: "jsonp",
    
        },
        experiments: {
            // outputModule: true
        },
        module: {
            noParse: /babel-polyfill/,
            rules: [
                {
                    test: /.vue$/,
                    use: 'vue-loader',
                },
                {
                    test: /\.(sass|scss)$/,
                    use: [
                        "style-loader",
                        "css-loader",
                        {
                            loader: "postcss-loader",
                            options: {
                                config: {
                                    path: path.resolve(__dirname,"./postcss.config.js")
                                }
                            }
                        },
                        "sass-loader"
                    ],
                },
                {
                    test: /\.png$/,
                    oneOf: [
                        {
                            resourceQuery: /inline/,
                            loader: "url-loader",
                            options: {
                                limit: 1024*1024*10
                            }
                        },
                        {
                            resourceQuery: /external/,
                            loader: "file-loader",
                        }
                    ]
                }
            ]
        },
        plugins: [
            new (require("vue-loader-plugin"))()
        ]
    };
    

    ok! 效果是一样的,我就不演示了。

    条件

    条件可以是这些之一:

    • 字符串:匹配输入必须以提供的字符串开始。是的。目录绝对路径或文件绝对路径。
    • 正则表达式:test 输入值。
    • 函数:调用输入的函数,必须返回一个真值(truthy value)以匹配。
    • 条件数组:至少一个匹配条件。
    • 对象:匹配所有属性。每个属性都有一个定义行为。

    { test: Condition }:匹配特定条件。一般是提供一个正则表达式或正则表达式的数组,但这不是强制的。

    { include: Condition }:匹配特定条件。一般是提供一个字符串或者字符串数组,但这不是强制的。

    { exclude: Condition }:排除特定条件。一般是提供一个字符串或字符串数组,但这不是强制的。

    { and: [Condition] }:必须匹配数组中的所有条件

    { or: [Condition] }:匹配数组中任何一个条件

    { not: [Condition] }:必须排除这个条件

    示例:

    {
      test: /\.css$/,
      include: [
        path.resolve(__dirname, "app/styles"),
        path.resolve(__dirname, "vendor/styles")
      ]
    }
    

    Resolve

    object

    配置模块如何解析。例如,当在 ES2015 中调用 import "lodash"resolve 选项能够对 webpack 查找 "lodash" 的方式去做修改。


    以上是官方的介绍,其实webpack在解析代码的时候回去加载模块(也就是读取文件),那么我们在node中读取文件路径可以用path.resolve,webpack的resolve也差不多就是这个意思了,webpack默认使用enhanced-resolve第三方库去解析文件路径,源码对应:

    lib/Compiler.js

    ...
    /** @type {ResolverFactory} */
    this.resolverFactory = new ResolverFactory();
    ...
    

    lib/ResolverFactory.js:

    ...
    const Factory = require("enhanced-resolve").ResolverFactory;
    ...
    

    enhanced-resolve:

    - plugin system
    - provide a custom filesystem
    - sync and async node.js filesystems included
    
    

    ok,概念我们就介绍到这里了,我们下面结合demo用一下resolve的配置。

    alias

    object

    设置一个路径的别名信息。

    创建 importrequire 的别名,来确保模块引入变得更简单。例如我们demo中,一些位于 src/ 文件夹下的常用模块:

    alias: {
        DemoVue: path.resolve(__dirname,"./src/demo-vue.vue")
    }
    

    之前我们在index.js中引用demo-vue.vue文件的时候是这样的:

    src/index.js

    __webpack_public_path__ = "http://localhost:8091/webpack-demo/lib/";
    import demoVue from "./demo-vue.vue";
    import Vue from "vue";
    new Vue({
        el: "#app",
        render:(h)=>h(demoVue)
    });
    

    现在换成别名的话我们可以这样导入了,

    src/index.js:

    __webpack_public_path__ = "http://localhost:8091/webpack-demo/lib/";
    import demoVue from "DemoVue";
    import Vue from "vue";
    new Vue({
        el: "#app",
        render:(h)=>h(demoVue)
    });
    

    也可以在给定对象的键后的末尾添加 $,以表示精准匹配:

    alias: {
      DemoVue$: path.resolve(__dirname,"./src/demo-vue.vue")
    }
    

    这将产生以下结果:

    import DemoVue from 'DemoVue'; // 精确匹配,所以 src/demo-vue.vue 被解析和导入
    import DemoVue from 'DemoVue/file.js'; // 非精确匹配,触发普通解析
    
    //如果不加$符号的话 以下解析将会报错 因为webpack会直接拼接path “/src/demo-vue.vue/file.js”报错
    import DemoVue from 'DemoVue/file.js';
    

    Extensions

    [string] 默认为 ['.wasm', '.mjs', '.js', '.json']

    能够使用户在引入模块时不带文件后缀,比如我们demo中引入.vue文件,我们也让它可以省略后缀,我们首先修改一些配置文件,

    webpack.config.js:

    const path = require("path");
    module.exports = {
        mode: "development",
        context: path.resolve(__dirname, "./src"),
        // entry: ["babel-polyfill","./index.js"]
        entry: {
            app: ["./index.js"]
        },
        output: {
            path: path.join(process.cwd(), "lib"), //默认为path.join(process.cwd(), "dist")
            pathinfo: true,
            filename: "[name].[contenthash:16].[fullhash:16].[id].js",
            chunkFilename: "[id].js",
            // library: "demoSay",
            // libraryExport: "default",
            // libraryTarget: "jsonp",
    
        },
        experiments: {
            // outputModule: true
        },
        module: {
            noParse: /babel-polyfill/,
            rules: [
                {
                    test: /.vue$/,
                    use: 'vue-loader',
                },
                {
                    test: /\.(sass|scss)$/,
                    use: [
                        "style-loader",
                        "css-loader",
                        {
                            loader: "postcss-loader",
                            options: {
                                config: {
                                    path: path.resolve(__dirname,"./postcss.config.js")
                                }
                            }
                        },
                        "sass-loader"
                    ],
                },
                {
                    test: /\.png$/,
                    oneOf: [
                        {
                            resourceQuery: /inline/,
                            loader: "url-loader",
                            options: {
                                limit: 1024*1024*10
                            }
                        },
                        {
                            resourceQuery: /external/,
                            loader: "file-loader",
                        }
                    ]
                }
            ]
        },
        resolve: {
            alias: {
                DemoVue: path.resolve(__dirname,"./src/demo-vue.vue")
            },
            extensions: ['.wasm', '.js', '.json','.vue']
        },
        plugins: [
            new (require("vue-loader-plugin"))()
        ],
    };
    

    然后我们导入.vue文件的时候就可以不用写后缀了:

    import demoVue from "./demo-vue";
    

    默认后缀的源码为:

    lib/WebpackOptionsDefaulter.js

    ...
    this.set("resolve.extensions", "make", options =>
                [options.experiments.mjs && ".mjs", ".js", ".json", ".wasm"].filter(
                    Boolean
                )
            );
    ...
    

    enforceExtension

    boolean 默认 false

    如果为true将不允许无扩展名(extension-less)文件,这个选项其实就是extendsion选项的开关。

    enforceModuleExtension

    boolean

    对模块是否需要使用的扩展(例如 loader)。默认:

    enforceModuleExtension: false
    

    Removed in webpack 5

    mainFields

    array

    当从 npm 包中导入模块时(例如,import * as D3 from "d3"),此选项将决定在 package.json 中使用哪个字段导入模块。根据 webpack 配置中指定的 target 不同,默认值也会有所不同。

    target 属性设置为 webworker, web 或者没有指定,默认值为:

    mainFields: ["browser", "module", "main"]
    

    对于其他任意的 target(包括 node),默认值为:

    mainFields: ["module", "main"]
    

    比如我们demo中的第三方依赖vue,我们打开vue的pakcage.json文件看看,

    node_modules/vue/package.json:

    {
      ...
      "main": "dist/vue.runtime.common.js",
      "module": "dist/vue.runtime.esm.js",
      ...
    }
    

    可以看到,vue指定了两个入口,所以demo中按照规则会优先加载“module”。

    modules

    array = ['node_modules']

    告诉 webpack 解析模块时应该搜索的目录。

    绝对路径和相对路径都能使用,但是要知道它们之间有一点差异。

    通过查看当前目录以及祖先路径(即 ./node_modules, ../node_modules 等等),相对路径将类似于 Node 查找 'node_modules' 的方式进行查找。

    使用绝对路径,将只在给定目录中搜索。

    resolve.modules defaults to:

    modules: ["node_modules"]
    

    如果你想要添加一个目录到模块搜索目录,此目录优先于 node_modules/ 搜索:

    modules: [path.resolve(__dirname, "src"), "node_modules"]
    

    比如我们需要在index.js中去加载src/demo-public.js文件,当我们没有指定路径加载的时候,

    src/index.js:

    import "demo-publicpath";
    

    打包编译会报错:

    ERROR in ./index.js 4:0-25
    Module not found: Error: Can't resolve 'demo-publicpath' in '/Users/ocj1/doc/h5/study/webpack/webpack-demo/src'
    
    ➜  webpack-demo git:(master) ✗ npx webpack
    
    

    然后我们把src也添加进modules,

    webpack.config.js:

    const path = require("path");
    module.exports = {
        mode: "development",
        context: path.resolve(__dirname, "./src"),
        // entry: ["babel-polyfill","./index.js"]
        entry: {
            app: ["./index.js"]
        },
        output: {
            path: path.join(process.cwd(), "lib"), //默认为path.join(process.cwd(), "dist")
            pathinfo: true,
            filename: "[name].[contenthash:16].[fullhash:16].[id].js",
            chunkFilename: "[id].js",
            // library: "demoSay",
            // libraryExport: "default",
            // libraryTarget: "jsonp",
    
        },
        experiments: {
            // outputModule: true
        },
        module: {
            noParse: /babel-polyfill/,
            rules: [
                {
                    test: /.vue$/,
                    use: 'vue-loader',
                },
                {
                    test: /\.(sass|scss)$/,
                    use: [
                        "style-loader",
                        "css-loader",
                        {
                            loader: "postcss-loader",
                            options: {
                                config: {
                                    path: path.resolve(__dirname,"./postcss.config.js")
                                }
                            }
                        },
                        "sass-loader"
                    ],
                },
                {
                    test: /\.png$/,
                    oneOf: [
                        {
                            resourceQuery: /inline/,
                            loader: "url-loader",
                            options: {
                                limit: 1024*1024*10
                            }
                        },
                        {
                            resourceQuery: /external/,
                            loader: "file-loader",
                        }
                    ]
                }
            ]
        },
        resolve: {
            alias: {
                DemoVue: path.resolve(__dirname,"./src/demo-vue.vue")
            },
            extensions: ['.wasm', '.mjs', '.js', '.json','.vue'],
            modules: [path.resolve(__dirname, "src"), "node_modules"]
        },
        plugins: [
            new (require("vue-loader-plugin"))()
        ],
    };
    

    再次打包编译是ok的。

    unsafeCache

    regex array boolean

    启用,会主动缓存模块,但并不安全。传递 true 将缓存一切。默认:

    unsafeCache: true
    

    正则表达式,或正则表达式数组,可以用于匹配文件路径或只缓存某些模块。例如,只缓存 utilities 模块:

    unsafeCache: /src\/utilities/
    

    这个选项其实是加快webpack打包速度的选项,比如我们上面说的./src/demo-publicpath.js文件,我们认为它是不会改变的,所以我们就把它加入到缓存中,

     resolve: {
            alias: {
                DemoVue: path.resolve(__dirname,"./src/demo-vue.vue")
            },
            extensions: ['.wasm', '.mjs', '.js', '.json','.vue'],
            modules: [path.resolve(__dirname, "src"), "node_modules"],
            unsafeCache: /demo-publicpath/,
        },
    

    这样webpack就不会每次都去做文件读取了,如果缓存中有就直接读缓存了。

    好啦,其实webpack的resolve也是依赖的另外一个第三方库,同样是在webpack组织下,估计是被收编了,resolve的更多参数和用法可以参考另外一个库:

    https://github.com/webpack/enhanced-resolve

    ResolveLoader

    object

    webpack加载loader的时候也是通过resolve去加载的,但是webpack单独把loader的配置抽离出来了,这组选项与上面的 resolve 对象的属性集合相同,但仅用于解析 webpack 的 loader 包。默认:

    {
      modules: [ 'node_modules' ],
      extensions: [ '.js', '.json' ],
      mainFields: [ 'loader', 'main' ]
    }
    

    注意,这里你可以使用别名,并且其他特性类似于 resolve 对象。例如,{ txt: 'raw-loader' } 会使用 raw-loader 去 shim(填充) txt!templates/demo.txt

    plugins

    array

    webpack 插件列表, 比如我们上面用了一个vue-loader-plugin插件:

    plugins: [
            new (require("vue-loader-plugin"))()
        ],
    

    webpack社区有很多插件,大家可以先浏览一下:

    https://webpack.js.org/plugins/

    后面我们会在项目实战的时候介绍一些常用插件的用法,最后还会去自定义一个插件。

    👌~ webpack的基本配置内容我们结合demo跟webpack的官网差不多就已经结束了,后面我们会针对开发环境还有包优化做解析(因为一篇文章实在是太长了~ 😂),下期见!!

    demo源码地址:https://github.com/913453448/webpack-demo.git

    相关文章

      网友评论

          本文标题:webpack高手秘籍(二)

          本文链接:https://www.haomeiwen.com/subject/bqwscktx.html