探索webpack熱更新對程式碼打包結果的影響(一)

Jade05發表於2018-01-29

更多文章請點選Jade

簡介:本專案以express搭建服務端,本地開發配合webpack-dev-middlerware、wepack-hot-middleware、webpack.HotModuleReplacementPlugin實現熱載入重新整理。生產打包在dist目錄下,開發打包熱載入替換目錄為/examples。

問題發現

問題描述

現象:原生程式碼通過生產配置檔案(webpack-build.config.js)打包生成的js包能夠被頁面引用到並且入口正常,通過開發配置檔案(webpack-develop.config.js)打包生成的js包雖然也能夠被頁面引用到但是入口不正常。

具體的例子: 入口檔案 - examples/test/index.js

const Test = {
  text: 'hello world!'
}

module.exports = Test
複製程式碼

html - example/test/index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>test</title>
</head>
<body>
    Test
    <!-- <script src="../../dist/components/test.js"></script> -->
    <script src="/examples/test.js"></script>
    <script>
      console.log(test)
      console.log(test.text)
    </script>
</body>
</html>
複製程式碼

webpack-develop.config.js:

entry: {
    test: [path.resolve(__dirname, 'examples/test/index.js'), 'webpack-hot-middleware/client?path=/examples/__what&reload=true'],
  },
  output: {
    filename: '[name].js',
    publicPath: '/examples/',
    library: '[name]',
    libraryTarget: 'umd',
  },
複製程式碼

本地起服務,在瀏覽器中開啟/examples/test/index.html,按照預期結果,控制檯應該輸出{text: "hello world!"} hello world!。但是控制檯卻輸出瞭如下結果:

image

第一反應是:js檔案沒有載入到。但是通過瀏覽器皮膚檢視後可以看到test.js已經被正確載入到,而且通過http://localhost:3000/examples/test.js可以看到,examples/test/index.js也已經打包進來:

02

問題分析

懵,就是取不到,明明就在那裡-.-。更神奇的是,如果我不用webpack-develop.config.js打包,而是用webpack-build.config.js直接打包到物理磁碟(注:因本地開發是基於webpack-dev-middleware和hot-middleware,開發打包的結果並不存在於物理磁碟,只存在於記憶體中),然後更換examples/test/index.html的引用入路徑為 ‘../../dist/components/test.js’,即生產打包後的物理檔案路徑,就一切正常,不會出現載入但是無法引用的問題。

webpack-develop.config.js和webpack-build.config.js有啥區別?區別還是有的,但是其他入口的頁面(注:本專案可理解為是多入口頁面)也是基於這兩個配置檔案的,本地開發和生產打包後皆正常。仔細排查,會發現可以通過以下兩個角度解決問題:

  1. webpack-develop.config.js和webpack-build.config.js是有區別的,build配置裡頭使用ExtractTextPlugin、UglifyJSPlugin,而develop沒有使用但有熱載入外掛;
  2. 其它入口的頁面都是正常的,唯獨examples/test/index.js這種情況有問題,比較後會發現引入的使用方式不同,其他入口檔案都是引用了模組檔案後自執行程式碼片段輸出dom,而examples/test/index.js作為打包入口最後通過module.exports = Test,輸出Test,然後example/test/index.html通過 script 標籤引入後直接去取全域性變數裡頭的Test引用,取引用失敗。而事實上我們知道,通過umd打包後,如果頁面通過script標籤直接引入,入口檔案module.exports輸出物件實際就是掛在window上的。(圖片裡頭的this在瀏覽器全域性環境中就是window)
    03

從第1個入口處進行排查,除去熱載入功能後,就一切正常了。結合以上兩點,我們有理由猜測導致上述問題的原因很可能是熱載入過程中導致入口檔案module.exports輸出的物件無法被正確掛載到window上。而至於真實情況是就是沒有掛載還是掛載後被沖掉(複寫)了,則需要分析原始碼才能知道。

問題解決

去掉熱更新,本地開發一切正常。

後記:直接去掉實在太殘暴,而且熱更新作為提高開發效率的利器就因為這一問題就直接去掉實在可惜,所以為了不影響其他入口的熱更新,本地開發模式可以只對指定入口禁用熱更新,webpack-develop.config.js 在指定entry去掉 'webpack-hot-middleware/client?path=/examples/__what&reload=true'即可。

深入探討

我們需要從webpack熱更新的原理說起

相關文章