2016-05-14 139 views
12

我正在使用Webpack,我想將我的客戶代碼分成幾塊,並在用戶需要時加載它們。如何使用require.ensure將Webpack中的代碼拆分爲生產?

這是我的文件結構:

app.js  <-- Entry point as introduced to Webpack 
Module.js <-- To be loaded dynamically 

app.jsModule.js之間沒有直接的聯繫,而不是第二個文件是由第一次加載這樣的:

require.ensure([], (require) => { 
    let path = "Module"; 
    let module = require("./" + path).default; 
}); 

我以前"./" + path只是爲了讓確保Webpack不會對我變得聰明,並嘗試靜態解析路徑。無論如何,這段代碼在webpack-dev-server的開發模式下工作。我的意思是Module.js不會被下載,直到我觸發事件來運行上面的代碼。之後,它被完美加載並執行。

但是,當我包裝生產項目時,它停止工作。它提供了以下錯誤(在瀏覽器中,當我觸發下載事件),甚至沒有嘗試發送請求:

Uncaught Error: Cannot find module './Module'.

而且,當我撰寫的路徑動態(如上面的代碼),建設過程中散發出以下警告:

WARNING in ./src/app/app.js Critical dependencies: 74:34-47 the request of a dependency is an expression

爲生產配置Webpack的正確方法是什麼,因此它支持動態下載代碼拆分?


我測試了@wollnyst給出的解決方案,這裏是結果。當我實現它這樣的,它的工作原理:

require.ensure(["./Module"], (require) => { 
    let path = "Module"; 
    let module = require("./" + path).default; 
}); 

但是,這不是我想的那樣,我希望它是這樣的:

let path = "Module"; 
require.ensure(["./" + path], (require) => { 
    let module = require("./" + path).default; 
}); 

現在,它給出了一個運行時錯誤的瀏覽器:

Uncaught TypeError: webpack_require(...).ensure is not a function

還是沒有運氣!

+0

您不需要使模塊路徑動態化就可以使其動態加載。 'require.ensure'負責照顧。靜態路徑將在你的情況下工作得很好 – raiyan

+1

@RaiyanMohammed對不起,但我沒有關注!在上面的代碼中,我正在動態地創建路徑,因爲在現實生活中,我是這麼做的。在編寫代碼時,模塊的路徑是未知的。只有在運行時纔會在變量中指定路徑。 – Mehran

+1

這是問題:與node-require不同,webpack是靜態的。它在構建時運行一次。因此,webpack無法評估動態「需求」。你有什麼結構?也許定義一個自己的[context](https://webpack.github.io/docs/context.html)可能會有所幫助。 – wollnyst

回答

1

你需要傳遞你想要求在require.ensure第一個參數模塊:

require.ensure(['./Module'], function(require) { 
    const module = require('./Module'); 
}); 
+1

你的解決方案有點作品,有點不對!我在結果中包含了這個問題。謝謝。 – Mehran

3

把要動態需要在require.ensure數組第一個參數的路徑是錯誤的,不應該做的。此數組旨在用於要動態加載的模塊的依賴項,以及回調中的異步代碼安全運行所需的數組。

理解webpack如何處理代碼拆分的關鍵部分是您無法完全動態地執行語句,因爲webpack需要一些位置信息來解析您想要的模塊,因此需要提示dependency is an expression警告。即使你可以像預先假設./一樣聰明起見,即使你有很多模塊需要動態加載並且有點令人討厭,但最好還是用靜態模塊路徑字符串重複完整的ensure語句,以便你不會遇到任何問題。

另一種方法是創建一個專門用於此目的的文件夾,例如splits,並從這裏解析所有模塊。但是這意味着你需要在這個目錄中的每個模塊都處於相同的分割點,而不是真正除了特定用例之外的任何人想要的。


關於CONFIGS,你將需要使用namedModulesPluginsCommonsChunkPlugin之一。我個人做的是有一個main.js包含所有常見的來源,vendor.js,其中包含所有常見的node_modulesruntime.js(webpack要求)。然後,塊被分開,如果依賴於特定的供應商依賴關係,它將被添加到這個特定的塊而不是普通的vendor.js

plugins: [ 
    new webpack.NamedModulesPlugin(), 
    new webpack.optimize.CommonsChunkPlugin({ 
    name: 'vendor', 
    minChunks: module => module.context && module.context.indexOf('node_modules'), 
    }), 
    new webpack.optimize.CommonsChunkPlugin({ 
    name: 'runtime', 
    }), 
], 

您也可以確保有類似下面output.filename

output: { 
    filename: '[name]-[chunkhash].js', 
} 

否則你vendor.js的哈希值會改變你的每次改變的來源,即使你沒有修改過DEPS,如果你關心緩存,這很糟糕。

最近的webpack visualizer是一個很好的工具來檢查你的包和塊是什麼樣子,並驗證事情看起來不錯或檢測可能更好地分塊的依賴關係。所以它處理你的數據塊,您可能需要改一下你的配置,如果使用StatsWriterPlugin收集統計:

new StatsWriterPlugin({ 
    fields: null, 
    transform: (data, opts) => { 
    const stats = opts.compiler.getStats().toJson({ chunkModules: true }) 
    return JSON.stringify(stats, null, 2) 
    }, 
}), 

有一點需要注意得是,require.ensure是專用於的WebPack和正在取代import(),它現在也處理。由於這個問題是從2016年開始的,它可能與我們現在使用webpack 2的元素不同,所以我會擴展一下。

現在,dynamic import提案已進入ES,您可以在webpack中使用它。你需要一個Promise polyfill和類似babel syntax-dynamic-import插件(現在應該在stage-3 preset)或者dynamic-import-webpack(如果這對你還不適用)(基本上將import()轉換爲ensures)。

您仍然需要指定完整路徑,以確保您是否想要爲每個模塊獲取塊,但這是更好的語法,並且將來會更加面向。

您可以在新的webpack文檔頁面上查看關於code splitting的許多其他資源。

+1

此外,通過動態導入,可以在模塊路徑中使用模板字符串*,我們只需確保將一些路徑信息提供給webpack(就像您提到的那樣)。 –

+0

的確,我沒有降低,因爲它只是(酷)es6替代字符串連接。 –

+0

當然。我沒有看到任何提到字符串連接,所以我想我提到它。 –

相關問題