vue按需加載組件-webpack require.ensure(轉)

vue按需加載組件-webpack require.ensure

版權聲明:本文爲博主原創文章,未經博主容許不得轉載。 https://blog.csdn.net/qq_27626333/article/details/76228578

使用 vue-cli構建的項目,在 默認狀況下 ,執行 npm run build 會將全部的js代碼打包爲一個總體,javascript

打包位置是 dist/static/js/app.[contenthash].js 
相似下面的路由代碼css

router/index.js 路由相關信息,該路由文件引入了多個 .vue組件html

import Hello from '@/components/Hello' import Province from '@/components/Province' import Segment from '@/components/Segment' import User from '@/components/User' import Loading from '@/components/Loading'
  • 1
  • 2
  • 3
  • 4
  • 5

  執行 npm run build 會打包爲一個總體 app.[contenthash].js ,這個文件是很是大,可能幾兆或者幾十兆,加載會很慢vue

這裏寫圖片描述

  因此咱們須要分模塊打包,把咱們想要組合在一塊兒的組件打包到一個 chunk塊中去,分模塊打包須要下面這樣使用 webpack的 require.ensure,而且在最後加入一個 chunk名,相同 chunk名字的模塊將會打包到一塊兒。java

webpack中利用require.ensure()實現按需加載

一、require.ensure()python

  webpack 在編譯時,會靜態地解析代碼中的 require.ensure(),同時將模塊添加到一個分開的 chunk 當中。這個新的 chunk 會被 webpack 經過 jsonp 來按需加載。webpack

語法以下:git

require.ensure(dependencies: String[], callback: function(require), chunkName: String)
  • 1
  • 依賴 dependencies

  這是一個字符串數組,經過這個參數,在全部的回調函數的代碼被執行前,咱們能夠將全部須要用到的模塊進行聲明。es6

  • 回調 callback

  當全部的依賴都加載完成後,webpack會執行這個回調函數。require 對象的一個實現會做爲一個參數傳遞給這個回調函數。所以,咱們能夠進一步 require() 依賴和其它模塊提供下一步的執行。github

  • chunk名稱 chunkName

  chunkName 是提供給這個特定的 require.ensure() 的 chunk 的名稱。經過提供 require.ensure() 不一樣執行點相同的名稱,咱們能夠保證全部的依賴都會一塊兒放進相同的 文件束(bundle)。

讓咱們來看如下的項目

\\ file structure
    | js --| | |-- entry.js | |-- a.js | |-- b.js webpack.config.js | dist
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
\\ entry.js

require('a'); require.ensure([], function(require){ require('b'); });
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
\\ a.js
console.log('***** I AM a *****');
  • 1
  • 2
\\ b.js
console.log('***** I AM b *****');
  • 1
  • 2
\\ webpack.config.js
var path = require('path'); module.exports = function(env) { return { entry: './js/entry.js', output: { filename: 'bundle.js', path: path.resolve(__dirname, 'dist') } } }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

  經過執行這個項目的 webpack 構建,咱們發現 webpack 建立了2個新的文件束, bundle.js 和 0.bundle.js。

entry.js 和 a.js 被打包進 bundle.js.

b.js 被打包進 0.bundle.js.

二、require.ensure() 的坑點

(1)、空數組做爲參數

require.ensure([], function(require){ require('./a.js'); });
  • 1
  • 2
  • 3

以上代碼保證了拆分點被建立,並且 a.js 被 webpack 分開打包。

(2)、依賴做爲參數

require.ensure(['./a.js'], function(require) { require('./b.js'); });
  • 1
  • 2
  • 3

  上面代碼, a.js 和 b.js 都被打包到一塊兒,並且從主文件束中拆分出來。但只有 b.js 的內容被執行。a.js 的內容僅僅是可被使用,但並無被輸出。

  想去執行 a.js,咱們須要異步地引用它,如 require(‘./a.js’),讓它的 JavaScritp 被執行。

(3)、單獨打包成本身寫的名字配置 
  須要配置chunkFilename,和publicPath。publicPath是按需加載單獨打包出來的chunk是以publicPath會基準來存放的,chunkFilename:[name].js這樣纔會最終生成正確的路徑和名字

module.exports={ entry:'./js/entry.js', output:{ path:path.resolve(__dirname,"./dist"), filename:'js/a.bundle.js', publicPath:"./", chunkFilename:'js/[name].js' }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

因此router/index.js 修改成懶加載組件:

const Province = r => require.ensure([], () => r(require('@/components/Province.vue')), 'chunkname1') const Segment = r => require.ensure([], () => r(require('@/components/Segment.vue')), 'chunkname2') const Loading = r => require.ensure([], () => r(require('@/components/Loading.vue')), 'chunkname3') const User = r => require.ensure([], () => r(require('@/components/User.vue')), 'chunkname3')
  • 1
  • 2
  • 3
  • 4

  根據 chunkame的不一樣, 上面的四個組件, 將會被分紅3個塊打包,最終打包以後與組件相關的js文件會分爲3個 (除了app.js,manifest.js, vendor.js)

  分模塊打包以後在 dist目錄下是這樣的, 這樣就把一個大的 js文件分爲一個個小的js文件了,按需去下載,其餘的使用方法和import的效果同樣

這裏寫圖片描述

參考:http://blog.csdn.net/yangbingbinga/article/details/61417689 
   http://www.css88.com/doc/webpack2/guides/code-splitting-require/

 
 
 

在vue中使用import()來代替require.ensure()實現代碼打包分離

 

1、require.ensure() 方法來實現代碼打包分離

require.ensure() 是 webpack 特有的,已經被 import() 取代。

require.ensure(
  dependencies: String[],
  callback: function(require),
  errorCallback: function(error),
  chunkName: String
)

給定 dependencies 參數,將其對應的文件拆分到一個單獨的 bundle 中,此 bundle 會被異步加載。 
當使用 CommonJS 模塊語法時,這是動態加載依賴的惟一方法。 
意味着,能夠在模塊執行時才運行代碼,只有在知足某些條件時才加載依賴項。 
這個特性依賴於內置的 Promise。若是想在低版本瀏覽器使用 require.ensure, 
記得使用像 es6-promise 或者 promise-polyfill 這樣 polyfill 庫, 來預先填充(shim) Promise 環境。
var a = require('normal-dep');
if ( module.hot ) {
  require.ensure(['b'], function(require) {
    var c = require('c');
    // Do something special...
  });
}

按照上面指定的順序,webpack 支持如下參數:

  • dependencies:字符串構成的數組,聲明 callback 回調函數中所需的全部模塊。
  • callback:只要加載好所有依賴,webpack 就會執行此函數。require 函數的實現,做爲參數傳入此函數。當程序運行須要依賴時,可使用 require() 來加載依賴。函數體可使用此參數,來進一步執行 require() 模塊。
  • errorCallback:當 webpack 加載依賴失敗時,會執行此函數。
  • chunkName:由 require.ensure() 建立出的 chunk 的名字。經過將同一個 chunkName 傳遞給不一樣的 require.ensure() 調用,咱們能夠將它們的代碼合併到一個單獨的 chunk 中,從而只產生一個瀏覽器必須加載的 bundle。
雖然咱們將 require 的實現,做爲參數傳遞給回調函數,然而若是使用隨意的名字,
例如 require.ensure([], function(request) { request('someModule'); }) 
則沒法被 webpack 靜態解析器處理,因此仍是請使用 require,例如 require.ensure([], function(require) { require('someModule'); })。

2、在vue中使用import()來代替require.ensure()實現代碼打包分離 

有時候咱們想把某個路由下的全部組件都打包在同個異步塊 (chunk) 中。 
只須要使用 命名 chunk,一個特殊的註釋語法來提供 chunk name (須要 Webpack > 2.4)。
const Foo = () => import(/* webpackChunkName: "group-foo" */ './Foo.vue')
const Bar = () => import(/* webpackChunkName: "group-foo" */ './Bar.vue')
const Baz = () => import(/* webpackChunkName: "group-foo" */ './Baz.vue')

Webpack 會將任何一個異步模塊與相同的塊名稱組合到相同的異步塊中。 

例:require.ensure()實現
const notFound = r => require.ensure([], () => r(require('@views/common/404')), 'index')
const login = r => require.ensure([], () => r(require('@views/common/login')), 'index')
const register = r => require.ensure([], () => r(require('@views/common/register')), 'index')
const main = r => require.ensure([], () => r(require('@views/main')), 'index')
const myWorks = r => require.ensure([], () => r(require('@views/my/index')), 'my')
const myAccountSetting = r => require.ensure([], () => r(require('@views/my/account-setting')), 'my')
const makeIndex = r => require.ensure([], () => r(require('@views/make/index')), 'make')
例:import()實現
const notFound = () => import(/* webpackChunkName: "index" */ '@views/common/404')
const login = () => import(/* webpackChunkName: "index" */ '@views/common/login')
const register = () => import(/* webpackChunkName: "index" */ '@views/common/register')
const main = () => import(/* webpackChunkName: "index" */ '@views/main')
const myWorks = () => import(/* webpackChunkName: "my" */ '@views/my/index')
const myAccountSetting = () => import(/* webpackChunkName: "my" */ '@views/my/account-setting')
const makeIndex = () => import(/* webpackChunkName: "make" */ '@views/make/index')
 
分類:  Vue, Webpack
標籤:  Webpackvue
 
 
 

require.ensure的用法;異步加載-代碼分割;

96 
吳高亮 
2017.09.29 11:49* 字數 1558 閱讀 1830評論 0

文章轉載地址[轉載github地址[https://github.com/wugaoliang1116/webpa_ensure]];

webpack異步加載的原理

webpack ensure相信你們都聽過。有人稱它爲異步加載,也有人說作代碼切割,那這
個傢伙究竟是用來幹嗎的?其實說白了,它就是把js模塊給獨立導出一個.js文件的,而後使用這個
模塊的時候,webpack會構造script dom元素,由瀏覽器發起異步請求這個js文件。

場景分析:

好比應用的首頁裏面有個按鈕,點擊後能夠打開某個地圖。打開地圖的話就要利用百度地圖的js,因而
咱們不得不在首頁中把百度地圖的js一塊兒打包進去首頁,一個百度地圖的js文件是很是大的,假設爲
1m,因而就形成了咱們首頁打包的js很是大,用戶打開首頁的時間就比較長了。

有沒有什麼好的解決方法呢?

解決1

既然打包成同一個js很是大的話,那麼咱們徹底能夠把百度地圖js分類出去,利用瀏覽器的併發請求
js文件處理,這樣的話,會比加載一個js文件時間小得多。嗯,這也是個不錯的方案。爲baidumap.js
配置一個新的入口就好了,這樣就能打包成兩個js文件,都插入html便可(若是baidumap.js被多個
入口文件引用的話,也能夠不用將其設置爲入口文件,並且直接利用CommonsChunkPlugin,導出到一個
公共模塊便可)能夠參考我以前文章
webpack模塊打包

那還有沒有更好的解決方案呢?

解決2

固然仍是有的!咱們細想,百度地圖是用戶點擊了才彈出來的,也就是說,這個功能是可選的。那麼解決
方案就來了,能不能在用戶點擊的時候,我在去下載百度地圖的js.固然能夠。那如何實現用戶點擊的時候
再去下載百度地圖的js呢?因而,咱們能夠寫一個按鈕的監聽器

mapBtn.click(function() { //獲取 文檔head對象 var head = document.getElementsByTagName('head')[0]; //構建 <script> var script = document.createElement('script'); //設置src屬性 script.async = true; script.src = "http://map.baidu.com/.js" //加入到head對象中 head.appendChild(script); }) 

上面的幾行代碼對你們來講都不難。能夠在點擊的時候,才加載百度地圖,等百度地圖加載完成後,在
利用百度地圖的對象去執行咱們的操做。ok,講到這裏webpack.ensure的原理也就講了一大半了。
它就是 把一些js模塊給獨立出一個個js文件,而後須要用到的時候,在建立一個script對象,加入
到document.head對象中便可,瀏覽器會自動幫咱們發起請求,去請求這個js文件,在寫個回調,去
定義獲得這個js文件後,須要作什麼業務邏輯操做。

ok,那麼咱們就利用webpack的api去幫咱們完成這樣一件事情。點擊後才進行異步加載百度地圖js,上面
的click加載js時咱們本身寫的,webpack能夠輕鬆幫咱們搞定這樣的事情,而不用咱們手寫

mapBtn.click(function() { require.ensure([], function() { var baidumap = require('./baidumap.js') //baidumap.js放在咱們當前目錄下 }) }) 

搞定!固然仍是分析一下。require.ensure這個函數是一個代碼分離的分割線,表示 回調裏面的require
是咱們想要進行分割出去的,即require('./baidumap.js'),把baidumap.js分割出去,造成一個
webpack打包的單獨js文件。固然ensure裏面也是能夠寫一些同步的require的,好比

var sync = require('syncdemo.js') //下面ensure裏面也用到 mapBtn.click(function() { require.ensure([], function() { var baidumap = require('./baidumap.js') //baidumap.js放在咱們當前目錄下 var sync = require('syncdemo.js') //這個不會獨立出去,由於它已經加載到模塊緩存中了 }) }) 

也就是說,ensure會把沒有使用過的require資源進行獨立分紅成一個js文件. require.ensure的
第一個參數是什麼意思呢?[], 其實就是 當前這個 require.ensure所依賴的其餘 異步加載的模塊。你想啊?若是A 和 B都是異步加載的,B中須要A,那麼B下載以前,是否是先要下載A啊?,因此ensure的第一個參數[]
也是請求下載的模塊,若是想加載A require.ensure(['A.js'],function) 便可

說完了上面的原理。下面就實踐一下

entry.js 依賴三個 js。

  • Abtn-work.js 是封裝了 abtn按鈕點擊後,才執行的業務邏輯
  • Bbtn-work.js 是封裝了 bbtn按鈕點擊後,才執行的業務邏輯
  • util.js 是封裝了 entry.js須要利用的工具箱

針對上面的需求,優化方案

假設 Abtn-work.js Bbtn-work.js util.js都是很是大的文件
由於 Abtn-work.js Bbtn-work.js 都不是entry.js必須有的,便可能發生的操做,那麼咱們把
他們利用異步加載,當發生的時候再去加載就好了

util.js是entry.js當即立刻依賴的工具箱。可是它又很是的大,因此將其配置打包成一個公共模塊,
利用瀏覽器的併發加載,加快下載速度。ok,構思完成,開始實現

index.html

<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>index</title> </head> <body> <div id="aBtn">Abtn</div> <div id="bBtn">Bbtn</div> </body> </html> 

定義了兩個buttom

而後看看 entry.js

var util_sync = require('./util-sync.js') alert(util_sync.data) document.getElementById("aBtn").onclick = function() { require.ensure([], function() { var awork = require('./workA-async.js') alert(awork.data) //異步裏面再導入同步模塊--實際是使用同步中的模塊 var util1 = require('./util-sync.js') }) } document.getElementById("bBtn").onclick = function() { require.ensure([], function() { var bwork = require('./workB-async.js') alert(bwork.data) }) } 

能夠看到,workA-async.js, workB-async.js 都是點擊後才ensure進來的。何時加載完成呢?
就是 require.ensure() 第二個函數參數,即回調函數,它表示當下載js完成後,發生的由於邏輯

webpack打包後,造成

 
image.png

其實, 1.1.... 2.2...就是咱們ensure導出來的js文件

咱們看看代碼是如何加載的執行的,點擊打包插入js後的html

 
image.png

能夠看到,並無加載 ensure導出來的 1.1...js 2.2....js

點擊 abtn,

 
image.png

發現瀏覽器下載並加載了 1.1....js

點擊 bbtn

 
image.png


發現瀏覽器下載並加載了 2.2....js

ok 所有完成

todo: 異步公用模塊的代碼抽離

喜歡的話 歡迎star

:源碼地址:https://github.com/yongningfu/webpa_ensure

相關文章
相關標籤/搜索