webpack 高級(jí)配置與優(yōu)化桦他,讓你的項(xiàng)目飛起來(lái)

“ 關(guān)注 前端開(kāi)發(fā)社區(qū) ,回復(fù) '領(lǐng)取資源'谆棱,免費(fèi)領(lǐng)取Vue快压,小程序,Node Js垃瞧,前端開(kāi)發(fā)用的插件以及面試視頻等學(xué)習(xí)資料蔫劣,讓我們一起學(xué)習(xí),一起進(jìn)步

image

打包多頁(yè)面應(yīng)用

所謂打包多頁(yè)面个从,就是同時(shí)打包出多個(gè) html 頁(yè)面脉幢,打包多頁(yè)面也是使用 html-webpack-plugin,只不過(guò)嗦锐,在引入插件的時(shí)候是創(chuàng)建多個(gè)插件對(duì)象嫌松,因?yàn)橐粋€(gè)html-webpack-plugin 插件對(duì)象只能打包出一個(gè) html 頁(yè)面。如:

module.exports = {
    entry: {
        index: "./src/index.js", // 指定打包輸出的chunk名為index
        foo: "./src/foo.js" // 指定打包輸出的chunk名為foo
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: "./src/index.html", // 要打包輸出哪個(gè)文件奕污,可以使用相對(duì)路徑
            filename: "index.html", // 打包輸出后該html文件的名稱
            chunks: ["index"] // 數(shù)組元素為chunk名稱萎羔,即entry屬性值為對(duì)象的時(shí)候指定的名稱,index頁(yè)面只引入index.js
        }),
        new HtmlWebpackPlugin({
            template: "./src/index.html", // 要打包輸出哪個(gè)文件碳默,可以使用相對(duì)路徑
            filename: "foo.html", // 打包輸出后該html文件的名稱
            chunks: ["foo"] // 數(shù)組元素為chunk名稱贾陷,即entry屬性值為對(duì)象的時(shí)候指定的名稱,foo頁(yè)面只引入foo.js
        }),
    ]
}

打包多頁(yè)面時(shí)嘱根,關(guān)鍵在于 chunks 屬性的配置髓废,因?yàn)樵跊](méi)有配置 chunks 屬性的情況下,打包輸出的 index.htmlfoo.html 都會(huì)同時(shí)引入 index.jsfoo.js该抒。

所以必須配置 chunks 屬性慌洪,來(lái)指定打包輸出后的 html 文件中要引入的輸出模塊,數(shù)組的元素為 entry 屬性值為對(duì)象的時(shí)候指定的 chunk 名,如上配置蒋譬,才能實(shí)現(xiàn)割岛,index.html 只引入 index.js,foo.html 只引入 foo.js 文件

配置 source-map

source-map 就是源碼映射犯助,主要是為了方便代碼調(diào)試癣漆,因?yàn)槲覀兇虬暇€后的代碼會(huì)被壓縮等處理,導(dǎo)致所有代碼都被壓縮成了一行剂买,如果代碼中出現(xiàn)錯(cuò)誤惠爽,那么瀏覽器只會(huì)提示出錯(cuò)位置在第一行,這樣我們無(wú)法真正知道出錯(cuò)地方在源碼中的具體位置瞬哼。webpack 提供了一個(gè) devtool 屬性來(lái)配置源碼映射婚肆。

let foo = 1;
console.lg(`console對(duì)象的方法名log寫(xiě)成了lg`); // 源文件第二行出錯(cuò)

index.js:1 Uncaught TypeError: console.lg is not a function
    at Object.<anonymous> (index.js:1)
    at o (index.js:1)
    at Object.<anonymous> (index.js:1)
    at o (index.js:1)
    at index.js:1
    at index.js:1

源碼中出錯(cuò)的位置明明是第二行代碼,而瀏覽器中提示的錯(cuò)誤確實(shí)在第一行坐慰,所以如果代碼很復(fù)雜的情況下较性,我們就無(wú)法找到出錯(cuò)的具體位置

devtool 常見(jiàn)的有 6 種配置:

  • 1、source-map:

這種模式會(huì)產(chǎn)生一個(gè).map文件结胀,出錯(cuò)了會(huì)提示具體的行和列赞咙,文件里面保留了打包后的文件與原始文件之間的映射關(guān)系,打包輸出文件中會(huì)指向生成的.map文件糟港,告訴js引擎源碼在哪里攀操,由于源碼與.map文件分離,所以需要瀏覽器發(fā)送請(qǐng)求去獲取.map文件,常用于生產(chǎn)環(huán)境秸抚,如:

//# sourceMappingURL=index.js.map

  • 2速和、eval:

這種模式打包速度最快,不會(huì)生成.map文件剥汤,會(huì)使用eval將模塊包裹颠放,在末尾加入sourceURL,常用于開(kāi)發(fā)環(huán)境吭敢,如:

//# sourceURL=webpack:///./src/index.js
  • 3慈迈、eval-source-map:

每個(gè) module 會(huì)通過(guò) eval() 來(lái)執(zhí)行,并且生成一個(gè) DataUrl 形式的 SourceMap (即 base64 編碼形式內(nèi)嵌到 eval 語(yǔ)句末尾), 但是不會(huì)生成 .map 文件省有,可以減少網(wǎng)絡(luò)請(qǐng)求痒留,但是打包文件會(huì)非常大

//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly8vLi9zcmMvaW5kZXguanM/YjYzNSJdLCJuYW1lcyI6WyJmb28iLCJjb25zb2xlIiwibGciXSwibWFwcGluZ3MiOiJBQUFBLElBQUlBLEdBQUcsR0FBRyxDQUFWO0FBQ0FDLE9BQU8sQ0FBQ0MsRUFBUix1RSxDQUFxQyIsImZpbGUiOiIuL3NyYy9pbmRleC5qcy5qcyIsInNvdXJjZXNDb250ZW50IjpbImxldCBmb28gPSAxO1xuY29uc29sZS5sZyhgY29uc29sZeWvueixoeeahOaWueazleWQjWxvZ+WGmeaIkOS6hmxnYCk7IC8vIOa6kOaWh+S7tuesrOS6jOihjOWHuumUmVxuIl0sInNvdXJjZVJvb3QiOiIifQ==\n//# sourceURL=webpack-internal:///./src/index.js
  • 4蠢沿、cheap-source-map:

加上 cheap伸头,就只會(huì)提示到第幾行報(bào)錯(cuò),少了列信息提示舷蟀,同時(shí)不會(huì)對(duì)引入的庫(kù)做映射恤磷,可以提高打包性能面哼,但是會(huì)產(chǎn)生 .map 文件。

  • 5扫步、cheap-module-source-map:

和 cheap-source-map 相比魔策,加上了 module,就會(huì)對(duì)引入的庫(kù)做映射河胎,并且也會(huì)產(chǎn)生 .map 文件闯袒,用于生產(chǎn)環(huán)境。

  • 6游岳、cheap-module-eval-source-map:

常用于開(kāi)發(fā)環(huán)境政敢,使用 cheap 模式可以大幅提高 souremap 生成的效率,加上 module 同時(shí)會(huì)對(duì)引入的庫(kù)做映射胚迫,eval 提高打包構(gòu)建速度喷户,并且不會(huì)產(chǎn)生 .map 文件減少網(wǎng)絡(luò)請(qǐng)求。

凡是帶 eval 的模式都不能用于生產(chǎn)環(huán)境访锻,因?yàn)槠洳粫?huì)產(chǎn)生 .map 文件褪尝,會(huì)導(dǎo)致打包后的文件變得非常大。通常我們并不關(guān)心列信息期犬,所以都會(huì)使用 cheap 模式河哑,但是我們也還是需要對(duì)第三方庫(kù)做映射,以便精準(zhǔn)找到錯(cuò)誤的位置哭懈。

watch 和 watchOptions 配置

webpack 可以監(jiān)聽(tīng)文件變化灾馒,當(dāng)它們修改后會(huì)重新編譯茎用,如果需要開(kāi)啟該功能遣总,那么需要將watch 設(shè)置為 true,具體監(jiān)聽(tīng)配置通過(guò) watchOptions 進(jìn)行相應(yīng)的設(shè)置轨功。

module.exports = {
    watch: true,
    watchOptions: {
        poll: 1000, // 每隔一秒輪詢一次文件是否發(fā)生變化
        aggregateTimeout: 1000, // 當(dāng)?shù)谝粋€(gè)文件更改旭斥,會(huì)在重新構(gòu)建前增加延遲。這個(gè)選項(xiàng)允許 webpack 將這段時(shí)間內(nèi)進(jìn)行的任何其他更改都聚合到一次重新構(gòu)建里
        ignored: /node_modules/ // 排除一些文件的監(jiān)聽(tīng)
    }
}

三個(gè)常見(jiàn)小插件的使用

  • 1古涧、clean-webpack-plugin: 其作用就是每次打包前先先將輸出目錄中的內(nèi)容進(jìn)行清空垂券,然后再將打包輸出的文件輸出到輸出目錄中。
const {CleanWebpackPlugin} = require("clean-webpack-plugin");
module.exports = {
    plugins: [
        new CleanWebpackPlugin() // 打包前清空輸出目錄
    ]
}

需要注意的是羡滑,require("clean-webpack-plugin)的結(jié)果是一個(gè)對(duì)象而不是類(lèi)菇爪,這個(gè)對(duì)象中的 CleanWebpackPlugin 屬性才是一個(gè)類(lèi),我們就是用這個(gè)類(lèi)去創(chuàng)建插件對(duì)象柒昏。

  • 2凳宙、copy-webpack-plugin: 其作用就是打包的時(shí)候帶上一些 readMe.md、history.md 等等一起輸出到輸出目錄中职祷。
module.exports = {
    plugins: [
        new CopyWebpackPlugin([
            {
                from: "./readMe.md", // 將項(xiàng)目根目錄下的readMe.md文件一起拷貝到輸出目錄中
                to: "" // 屬性值為空字符串則表示是輸出目錄
            }
        ])
    ]
}
  • 3氏涩、BannerPlugin: 其作用就是在打包輸出的 js 文件的頭部添加一些文字注釋届囚,比如版權(quán)說(shuō)明等等,BannerPlugin 是 webpack 內(nèi)置的插件是尖,如:
module.exports = {
    plugins: [
        new webpack.BannerPlugin("Copyright ? 2019") // 在js文件頭部添加版權(quán)說(shuō)明
    ]
}

webpack 跨域問(wèn)題

為什么 webpack 會(huì)存在跨域問(wèn)題意系?因?yàn)?webpack 打包的是前端代碼,其最終會(huì)被部署到前端服務(wù)器上饺汹,而前后端代碼通常部署在不同的服務(wù)器上蛔添,即使是部署在同一個(gè)服務(wù)器上,所使用的端口也是不一樣的首繁,當(dāng)前端代碼通過(guò) ajax 等手段向后端服務(wù)器獲取數(shù)據(jù)的時(shí)候作郭,由于前后端代碼不在同一個(gè)域中,故存在跨域問(wèn)題弦疮。

比如夹攒,我們通過(guò) webpack 的 devServer 來(lái)運(yùn)行部署我們的前端應(yīng)用代碼,devServer 啟動(dòng)在 8080 端口上胁塞,而前端應(yīng)用代碼中會(huì)通過(guò) ajax 請(qǐng)求后端數(shù)據(jù)咏尝,后端服務(wù)器啟動(dòng)在 3000 端口上。

// index.js

const xhr = new XMLHttpRequest();
// xhr.open("get", "http://localhost:3000/api/test"); // 由于跨域問(wèn)題無(wú)法直接訪問(wèn)到http://localhost:3000下的資源
xhr.open("get", "/api/test"); // 本來(lái)是要訪問(wèn)http://localhost:3000/api/test
xhr.onreadystatechange = () => {
    if (xhr.readyState === 4) {
        console.log(xhr.responseText);
    }
}
xhr.send();

由于前端代碼是運(yùn)行在瀏覽器中的啸罢,如果在前端代碼中直接通過(guò) ajax 向http://localhost:3000/api/test 發(fā)起請(qǐng)求獲取數(shù)據(jù)编检,那么由于瀏覽器同源策略的影響,會(huì)存在跨域的問(wèn)題扰才,所以必須訪問(wèn) /api/test允懂。但是這樣訪問(wèn)又會(huì)出現(xiàn) 404 問(wèn)題,因?yàn)槠鋵?shí)訪問(wèn)的是 http://localhost:8080/api/test衩匣,8080 服務(wù)器上是沒(méi)有該資源的蕾总,解決辦法就是通過(guò) devServer 配置一個(gè)代理服務(wù)器

module.exports = {
    devServer: {
        proxy: {
            "/api": "http://localhost:3000" // 路徑以/api開(kāi)頭則代理到localhost:3000上
        }
    }
}

訪問(wèn) http://localhost:8080/api/test
就會(huì)被代理到http://localhost:3000/api/test 上,proxy 還支持路徑的重寫(xiě)琅捏,如果 3000 端口服務(wù)器上并沒(méi)有 /api/test 路徑生百,只有 /test 路徑,那么就可以對(duì)路徑進(jìn)行重寫(xiě)柄延,將 /api 替換掉

module.exports = {
    devServer: {
        proxy: {
            "/api": {
                target: "http://localhost:3000",
                pathRewrite: {"/api": ""} // 將/api替換掉
            }
        }
    }
}

訪問(wèn) http://localhost:8080/api/test
就會(huì)被代理到 http://localhost:3000/test

如果前端只是想 mock 一些數(shù)據(jù)蚀浆,并不需要真正的去訪問(wèn)后臺(tái)服務(wù)器,那么我們可以通過(guò) devServer 提供的 before 鉤子函數(shù)獲取到內(nèi)置的服務(wù)器對(duì)象進(jìn)行處理請(qǐng)求搜吧,這個(gè)內(nèi)置的服務(wù)器對(duì)象就是 webpack 的 devServer 即 8080 端口的 server市俊,因?yàn)槭窃谕粋€(gè)服務(wù)器中請(qǐng)求數(shù)據(jù)所以也不會(huì)出現(xiàn)跨域問(wèn)題。

before(app) { // 此app即webpack的devServer
            app.get("/api/test", (req, res, next) => {
                res.json({name: "even"});
            })
        }

我們還可以不通過(guò) webpack 提供的 devServer 來(lái)啟動(dòng) webpack滤奈,而是使用自己服務(wù)器來(lái)啟動(dòng) webapck摆昧。

const express = require("express");
const app = express();
const webpack = require("webpack"); // 引入webpack
const config = require("./webpack.config.js"); // 引入配置文件
const compiler = webpack(config); // 創(chuàng)建webpack的編譯器
const middleWare = require("webpack-dev-middleware"); //引入webpack的中間件
app.use(middleWare(compiler)); // 將compiler編譯器交給中間件處理
app.get("/api/test", (req, res, next) => {
    res.json({name: "lhb"});
});
app.listen(3000);

通過(guò)自定義服務(wù)器啟動(dòng) webpack,這樣 webpack 中的前端代碼請(qǐng)求數(shù)據(jù)就和服務(wù)器的資源在同一個(gè)域中了僵刮。

resolve 配置

resolve 用于配置模塊的解析相關(guān)參數(shù)的据忘,其屬性值為一個(gè)對(duì)象鹦牛。

  • 1、modules: 告訴 webpack 解析模塊時(shí)應(yīng)該搜索的目錄勇吊,即 require 或 import 模塊的時(shí)候曼追,只寫(xiě)模塊名的時(shí)候,到哪里去找汉规,其屬性值為數(shù)組礼殊,因?yàn)榭膳渲枚鄠€(gè)模塊搜索路徑,其搜索路徑必須為絕對(duì)路徑针史,比如晶伦,src 目錄下面有一個(gè) foo.js 文件和 index.js 文件:
// index.js

const foo = require("./foo"); // 必須寫(xiě)全foo.js模塊的路徑
// const foo = require("foo"); // resolve.modules中配置了模塊解析路徑為.src目錄,則可用只寫(xiě)foo即可搜索到foo.js模塊
console.log(foo);

module.exports = {
    resolve: {
        modules: [path.resolve(__dirname, "./src/"), "node_modules"]
    },
}

由于 resolve.modules 中配置了 ./src 目錄作為模塊的搜索目錄啄枕,所以 index.js 中可以只寫(xiě)模塊名即可搜索到 foo.js 模塊

  • 2婚陪、alias: 用于給路徑或者文件取別名,當(dāng) import 或者 require 的模塊的路徑非常長(zhǎng)時(shí)频祝,我們可以給該模塊的路徑或者整個(gè)路徑名+文件名都設(shè)置成一個(gè)別名泌参,然后直接引入別名即可找到該模塊,比如常空,有一個(gè)模塊位置非常深
// const foo = require("./a/b/c/foo"); // foo.js在./src/a/b/c/foo.js
// const foo = require("foo"); // foo被映射成了./src/a/b/c/foo.js文件
const foo = require("bar/foo.js"); // bar被映射成了./src/a/b/c/路徑
console.log(foo);


module.exports = {
    resolve: {
        alias: {
            "foo": path.resolve(__dirname, "./src/a/b/c/foo.js"),
            "bar": path.resolve(__dirname, "./src/a/b/c/")
        }
    },
}

需要注意的就是沽一,alias 可以映射文件也可以映射路徑

  • 3、mainFields: 我們的 package.json 中可以有多個(gè)字段漓糙,用于決定優(yōu)先使用哪個(gè)字段來(lái)導(dǎo)入模塊铣缠,比如 bootstrap 模塊中含有 js 也含有 css,其 package.json 文件中 main 字段對(duì)應(yīng)的是"dist/js/bootstrap"昆禽,style 字段中對(duì)應(yīng)的是"dist/css/bootstrap.css",我們可以通過(guò)設(shè)置 mainFields 字段來(lái)改變默認(rèn)引入蝗蛙,如:
module.exports = {
    resolve: {
        mainFields: ["style", "main"]
    },
}
  • 4、extensions: 用于設(shè)置引入模塊的時(shí)候为狸,如果沒(méi)有寫(xiě)模塊后綴名歼郭,webpack 會(huì)自動(dòng)添加后綴去查找遗契,extensions 就是用于設(shè)置自動(dòng)添加后綴的順序辐棒,如:
module.exports = {
    resolve: {
        extensions: ["js", "vue"]
    },
}

如果項(xiàng)目中引入了 foo 模塊,require("./foo"),其會(huì)優(yōu)先找 ./foo.js,如果沒(méi)有找到 ./foo.js 則會(huì)去找 ./foo.vue 文件

設(shè)置環(huán)境變量

設(shè)置環(huán)境變量需要用到 webpack 提供的一個(gè)內(nèi)置插件 DefinePlugin 插件牍蜂,其作用是將一個(gè)字符串值設(shè)置為全局變量漾根,如:

module.exports = {
    plugins: [
        new webpack.DefinePlugin({
            DEV_MODE: JSON.stringify('development') // 將'development'設(shè)置為全局變量DEV_MODE
        }),
    ]
}

這樣配置之后任何一個(gè)模塊中都可以直接使用 DEV_MODE 變量了,并且其值為'development',與 ProvidePlugin 有點(diǎn)相似鲫竞,ProvidePlugin 是將一個(gè)模塊注入到所有模塊中辐怕,實(shí)現(xiàn)模塊不需要引入即可直接使用。

webpack 優(yōu)化

  • 1从绘、noParse: 該配置是作為 module 的一個(gè)屬性值寄疏,即不解析某些模塊是牢,所謂不解析,就是不去分析某個(gè)模塊中的依賴關(guān)系陕截,即不去管某個(gè)文件是否 import(依賴)了某個(gè)文件驳棱,對(duì)于一些獨(dú)立的庫(kù),比如 jquery农曲,其根本不存在依賴關(guān)系社搅,jquery 不會(huì)去引入其他的庫(kù)(要根據(jù)自己對(duì)某個(gè)模塊的了解去判斷是否要解析該模塊),所以我們可以讓 webpack 不去解析 jquery 的依賴關(guān)系乳规,提高打包速度形葬,如:
module.exports = {
    module: {
        noParse:/jquery/,//不去解析jquery中的依賴庫(kù)
    }
}

noParse 是 module 配置中的一個(gè)屬性,其屬性值為一個(gè)正則表達(dá)式暮的,填入不被解析的模塊名稱笙以。
為了更清楚的展示 noParse 的作用,假設(shè)我們?cè)谌肟谖募?index.js 中引入 bar.js 模塊冻辩,同時(shí)這個(gè) bar.js 模塊中也引入了 foo.js 模塊源织,foo.js 不再依賴其他模塊了,那么在不使用 noParse 的情況下微猖,webpack 打包的時(shí)候谈息,會(huì)先去分析 index.js 模塊,發(fā)現(xiàn)其引入了 bar.js 模塊凛剥,然后接著分析 bar.js 模塊侠仇,發(fā)現(xiàn)其引入了 foo.js 模塊,接著分析 foo.js 模塊犁珠。

Entrypoint index = index.js
[./src/bar.js] 55 bytes {index} [built]
[./src/foo.js] 21 bytes {index} [built]
[./src/index.js] 81 bytes {index} [built]

而此時(shí)如果使用了 noParse: /bar/逻炊,那么 webpack 打包的時(shí)候,會(huì)先去分析 index.js 模塊犁享,發(fā)現(xiàn)其引入了 bar.js 模塊余素,但是由于 noParse 的作用,將不再繼續(xù)解析 bar.js 模塊了炊昆,即不會(huì)去分析 bar.js 中引入的 foo.js 模塊了桨吊。

Entrypoint index = index.js
[./src/bar.js] 55 bytes {index} [built]
[./src/index.js] 81 bytes {index} [built]
  • 2、exclude: 在 loader 中使用 exclude 排除對(duì)某些目錄中的文件處理凤巨,即引入指定目錄下的文件時(shí)候视乐,不使用對(duì)應(yīng)的 loader 進(jìn)行處理,exclude 是 loader 配置中的一個(gè)屬性敢茁,屬性值為正則表達(dá)式佑淀,如:
module.exports = {
    module: {
        rules: [
            {
                test: /\.js$/,
                use: [
                    {
                        loader: "babel-loader",
                        options: {
                            presets: ["@babel/preset-env"],
                            plugins: ["@babel/plugin-transform-runtime"]
                        }
                    }
                ],
                exclude: /node_modules/
            }
        ]
    }
}
  • 3、使用 IgnorePlugin 來(lái)忽略某個(gè)模塊中某些目錄中的模塊引用彰檬,比如在引入某個(gè)模塊的時(shí)候伸刃,該模塊會(huì)引入大量的語(yǔ)言包谎砾,而我們不會(huì)用到那么多語(yǔ)言包,如果都打包進(jìn)項(xiàng)目中捧颅,那么就會(huì)影響打包速度和最終包的大小棺榔,然后再引入需要使用的語(yǔ)言包即可,如:

項(xiàng)目根目錄下有一個(gè) time 包隘道,其中有一個(gè) lang 包症歇,lang 包中包含了各種語(yǔ)言輸出對(duì)應(yīng)時(shí)間的 js 文件,time 包下的 index.js 會(huì)引入 lang 包下所有的 js 文件谭梗,那么當(dāng)我們引入 time 模塊的時(shí)候忘晤,就會(huì)將 lang 包下的所有 js 文件都打包進(jìn)去,添加如下配置:


const webpack = require("webpack");
module.exports = {
    plugins: [
        new webpack.IgnorePlugin(/lang/, /time/)
    ]
}

引入 time 模塊的時(shí)候激捏,如果 time 模塊中引入了其中的 lang 模塊中的內(nèi)容设塔,那么就忽略掉,即不引入 lang 模塊中的內(nèi)容远舅,需要注意的是闰蛔,這 /time/ 只是匹配文件夾和 time 模塊的具體目錄位置無(wú)關(guān),即只要是引入了目錄名為 time 中的內(nèi)容就會(huì)生效图柏。

  • 4序六、使用 HappyPack:由于在打包過(guò)程中有大量的文件需要交個(gè) loader 進(jìn)行處理,包括解析和轉(zhuǎn)換等操作蚤吹,而由于 js 是單線程的例诀,所以這些文件只能一個(gè)一個(gè)地處理,而 HappyPack 的工作原理就是充分發(fā)揮 CPU 的多核功能裁着,將任務(wù)分解給多個(gè)子進(jìn)程去并發(fā)執(zhí)行繁涂,子進(jìn)程處理完后再將結(jié)果發(fā)送給主進(jìn)程,happypack 主要起到一個(gè)任務(wù)劫持的作用二驰,在創(chuàng)建 HappyPack 實(shí)例的時(shí)候要傳入對(duì)應(yīng)文件的 loader扔罪,即 use 部分,loader 配置中將使用經(jīng)過(guò) HappyPack 包裝后的 loader 進(jìn)行處理桶雀,如:
const HappyPack = require("happypack"); // 安裝并引入happypack模塊
module.exports = {
    plugins: [
        new HappyPack({ // 這里對(duì)處理css文件的loader進(jìn)行包裝
            id: "css",// 之前的loader根據(jù)具體的id進(jìn)行引入
            use: ["style-loader","css-loader"],
            threads: 5 // 設(shè)置開(kāi)啟的進(jìn)程數(shù)
        })
    ],
    module: {
        rules: [
            {
                test: /\.css$/, // 匹配以.css結(jié)尾的文件
                use: ["happypack/loader?id=css"] //根據(jù)happypack實(shí)例中配置的id引入包裝后的laoder矿酵,這里的happyPack的h可以大寫(xiě)也可以小寫(xiě)
            }
        ]
    }
}

webpack 要打包的文件非常多的時(shí)候才需要使用 happypack 進(jìn)行優(yōu)化,因?yàn)殚_(kāi)啟多進(jìn)程也是需要耗時(shí)間的背犯,所以文件少的時(shí)候坏瘩,使用 happypack 返回更耗時(shí)

  • 5盅抚、抽離公共模塊: 對(duì)于多入口情況漠魏,如果某個(gè)或某些模塊,被兩個(gè)以上文件所依賴妄均,那么可以將這個(gè)模塊單獨(dú)抽離出來(lái)剂癌,不需要將這些公共的代碼都打包進(jìn)每個(gè)輸出文件中丈甸,這樣會(huì)造成代碼的重復(fù)和流量的浪費(fèi)钧舌,即如果有兩個(gè)入口文件 index.js 和 other.js,它們都依賴了 foo.js壤巷,那么如果不抽離公共模塊,那么 foo.js 中的代碼都會(huì)打包進(jìn)最終輸出的 index.js 和 other.js 中去瞧毙,即有兩份 foo.js 了胧华。抽離公共模塊也很簡(jiǎn)單,直接在 optimization 中配置即可宙彪,如:
module.exports = {
     splitChunks: { // 分割代碼塊矩动,即抽離公共模塊
         cacheGroups: { // 緩存組
             common: { // 組名為common可自定義
                    chunks: "initial",
                    minSize: 0, // 文件大小為0字節(jié)以上才抽離
                    minChunks: 2, // 被引用過(guò)兩次才抽離
                    name: "common/foo", // 定義抽離出的文件的名稱
             }
         }
     }
}

這樣就會(huì)將公共的 foo.js 模塊抽離到 common 目錄下 foo.js 中了,但是如果我們也有多個(gè)文件依賴了第三方模塊如 jquery释漆,如果按以上配置悲没,那么 jquery 也會(huì)被打包進(jìn) foo.js 中,會(huì)導(dǎo)致代碼混亂男图,所以我們希望將 jquery 單獨(dú)抽出來(lái)示姿,即與 foo.js 分開(kāi),我們可以復(fù)制一份以上配置逊笆,并通過(guò)設(shè)置抽離代碼權(quán)重的方式來(lái)實(shí)現(xiàn)栈戳,即優(yōu)先抽離出 jquery,如:

module.exports = {
     splitChunks: { // 分割代碼塊难裆,即抽離公共模塊
         cacheGroups: { // 緩存組
             common: { // 組名為common可自定義
                    chunks: "initial",
                    minSize: 0, // 文件大小為0字節(jié)以上才抽離
                    minChunks: 2, // 被引用過(guò)兩次才抽離
                    name: "common/foo", // 定義抽離出的文件的名稱
             },
             verdor: {
                    test: /node_modules/,
                    priority: 1, // 設(shè)置打包權(quán)重荧琼,即優(yōu)先抽離第三方模塊
                    chunks: "initial",
                    minSize: 0, // 文件大小為0字節(jié)以上才抽離
                    minChunks: 2, // 被引用過(guò)兩次才抽離
                    name: "common/jquery", // 定義抽離出的文件的名稱
                }
         }
     }
}

這樣就會(huì)在 common 目錄下同時(shí)抽離出foo.jsjquery.js 了,需要注意的是差牛,代碼的抽離必須是該模塊沒(méi)有被排除打包命锄,即該模塊會(huì)被打包進(jìn)輸出 bundle 中,如果第三方模塊已經(jīng)通過(guò)externals 排除打包偏化,則以上 vendor 配置無(wú)效脐恩。

  • 6、按需加載侦讨,即在需要使用的時(shí)候才打包輸出驶冒,webpack 提供了 import() 方法,傳入要?jiǎng)討B(tài)加載的模塊韵卤,來(lái)動(dòng)態(tài)加載指定的模塊骗污,當(dāng) webpack 遇到 import()語(yǔ)句的時(shí)候,不會(huì)立即去加載該模塊沈条,而是在用到該模塊的時(shí)候需忿,再去加載,也就是說(shuō)打包的時(shí)候會(huì)一起打包出來(lái),但是在瀏覽器中加載的時(shí)候并不會(huì)立即加載屋厘,而是等到用到的時(shí)候再去加載涕烧,比如,點(diǎn)擊按鈕后才會(huì)加載某個(gè)模塊汗洒,如:
const button = document.createElement("button");
button.innerText = "點(diǎn)我"
button.addEventListener("click", () => { // 點(diǎn)擊按鈕后加載foo.js
    import("./foo").then((res) => { // import()返回的是一個(gè)Promise對(duì)象
        console.log(res);
    });
});
document.body.appendChild(button);

從中可以看到议纯,import() 返回的是一個(gè) Promise 對(duì)象,其主要就是利用 JSONP 實(shí)現(xiàn)動(dòng)態(tài)加載溢谤,返回的 res 結(jié)果不同的 export 方式會(huì)有不同瞻凤,如果使用的 module.exports 輸出,那么返回的 res 就是 module.exports 輸出的結(jié)果世杀;如果使用的是 ES6 模塊輸出鲫构,即 export default 輸出,那么返回的 res 結(jié)果就是 res.default玫坛,如:


// ES6模塊輸出结笨,res結(jié)果為


{default: "foo", __esModule: true, Symbol(Symbol.toStringTag): "Module"}
  • 7、開(kāi)啟模塊熱更新: 模塊熱更新可以做到在不刷新網(wǎng)頁(yè)的情況下湿镀,更新修改的模塊炕吸,只編譯變化的模塊,而不用全部模塊重新打包勉痴,大大提高開(kāi)發(fā)效率赫模,在未開(kāi)啟熱更新的情況下,每次修改了模塊蒸矛,都會(huì)重新打包瀑罗。

要開(kāi)啟模塊熱更新,那么只需要在 devServer 配置中添加hot:true 即可雏掠。當(dāng)然僅僅開(kāi)啟模塊熱更新是不夠的斩祭,我們需要做一些類(lèi)似監(jiān)聽(tīng)的操作,當(dāng)監(jiān)聽(tīng)的模塊發(fā)生變化的時(shí)候乡话,重新加載該模塊并執(zhí)行摧玫,如:

module.exports = {
    devServer: {
        hot: true // 開(kāi)啟熱更新
    }
}

----------


import foo from "./foo";
console.log(foo);
if (module.hot) {
    module.hot.accept("./foo", () => { // 監(jiān)聽(tīng)到foo模塊發(fā)生變化的時(shí)候
        const foo =  require("./foo"); // 重新引入該模塊并執(zhí)行
        console.log(foo);
    });
}

如果不使用 module.hot.accept 監(jiān)聽(tīng),那么當(dāng)修改 foo 模塊的時(shí)候還是會(huì)刷新頁(yè)面的绑青。

https://segmentfault.com/a/119000002032087

作者:JS_Even_JS

往期
安利幾個(gè)JS開(kāi)發(fā)的小技巧

請(qǐng)各位帥哥美女多多支持帥編诬像,回復(fù)“加群”即可領(lǐng)取 前端干貨

image

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市闸婴,隨后出現(xiàn)的幾起案子坏挠,更是在濱河造成了極大的恐慌,老刑警劉巖邪乍,帶你破解...
    沈念sama閱讀 218,122評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件降狠,死亡現(xiàn)場(chǎng)離奇詭異对竣,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)喊熟,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,070評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)柏肪,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)姐刁,“玉大人芥牌,你說(shuō)我怎么就攤上這事∧羰梗” “怎么了壁拉?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,491評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)柏靶。 經(jīng)常有香客問(wèn)我弃理,道長(zhǎng),這世上最難降的妖魔是什么屎蜓? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,636評(píng)論 1 293
  • 正文 為了忘掉前任痘昌,我火速辦了婚禮,結(jié)果婚禮上炬转,老公的妹妹穿的比我還像新娘辆苔。我一直安慰自己,他們只是感情好扼劈,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,676評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布驻啤。 她就那樣靜靜地躺著,像睡著了一般荐吵。 火紅的嫁衣襯著肌膚如雪骑冗。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,541評(píng)論 1 305
  • 那天先煎,我揣著相機(jī)與錄音贼涩,去河邊找鬼。 笑死薯蝎,一個(gè)胖子當(dāng)著我的面吹牛磁携,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播良风,決...
    沈念sama閱讀 40,292評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼谊迄,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了烟央?” 一聲冷哼從身側(cè)響起统诺,我...
    開(kāi)封第一講書(shū)人閱讀 39,211評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎疑俭,沒(méi)想到半個(gè)月后粮呢,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,655評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,846評(píng)論 3 336
  • 正文 我和宋清朗相戀三年啄寡,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了豪硅。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,965評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡挺物,死狀恐怖懒浮,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情识藤,我是刑警寧澤砚著,帶...
    沈念sama閱讀 35,684評(píng)論 5 347
  • 正文 年R本政府宣布,位于F島的核電站痴昧,受9級(jí)特大地震影響稽穆,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜赶撰,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,295評(píng)論 3 329
  • 文/蒙蒙 一舌镶、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧豪娜,春花似錦餐胀、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,894評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至惕虑,卻和暖如春坟冲,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背溃蔫。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,012評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工健提, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人伟叛。 一個(gè)月前我還...
    沈念sama閱讀 48,126評(píng)論 3 370
  • 正文 我出身青樓私痹,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親统刮。 傳聞我的和親對(duì)象是個(gè)殘疾皇子紊遵,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,914評(píng)論 2 355