美文网首页
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高手秘籍(二)

    chunkFilename string = '[id].js' 刚已经介绍过filename字段,filenam...

  • webpack高手秘籍(一)

    前言 前面写了一篇文章webpack源码解析一梳理了一遍webpack的编译过程,今天我们结合demo来过一遍we...

  • webpack高手秘籍(六)

    前言 前面我们写了几篇文章用来介绍webpack源码,跟着官网结合demo把整个webpack配置撸了一遍: we...

  • webpack高手秘籍(五)

    前言 前面我们写了几篇文章用来介绍webpack源码,跟着官网结合demo把整个webpack配置撸了一遍: we...

  • webpack高手秘籍(四)

    前言 我们继续前面的内容,把webpack剩下的配置项撸一遍,推荐大家先看一下前面的文章: webpack源码解析...

  • webpack高手秘籍(三)

    前言 我们接着前面的文章webpack源码解析二继续往下探索webpack的配置,demo的github地址:ht...

  • 共读会-对于个人的经验如何看待

    #Books/《跃迁:成为高手的技术(附赠高手秘籍和时间管理秘籍)》 #卡片/共读会 018 【价值】 第二次总结...

  • 《跃迁》书摘14

    《跃迁:成为高手的技术(附赠高手秘籍和时间管理秘籍)》 古典 >> 跃迁时刻 ...

  • LitePal使用笔记

    github地址 参考文章 Android数据库高手秘籍(一)——SQLite命令 Android数据库高手秘籍(...

  • 《跃迁》书摘

    《跃迁:成为高手的技术(附赠高手秘籍和时间管理秘籍)》 古典 跃迁:成为高手的技术 >> 不要以战术上的勤奋掩饰...

网友评论

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

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