Webpack(十五):webpack+vue+router 按需加載頁面

1. 為什么需要按需加載猜憎?

對于vue單頁應(yīng)用來講尉尾,我們常見的做法把頁面上所有的代碼都打包到一個bundle.js文件內(nèi)蚂子,但是隨著項目越來越大本缠,文件越來越多的情況下斥扛,那么bundle.js文件也會越來越大,文件大的時候會導致打開頁面用戶體驗相對來說會變慢丹锹。因此按需加載代碼是很有必要的稀颁,每次打開某一個頁面的時候,只按需加載那個頁面的代碼楣黍,這樣的話匾灶,項目中其他代碼就不會被加載,這樣的話租漂,bundle.js文件也不會把所有項目頁面文件代碼打包進去阶女,文件也不會很大颊糜。其他的頁面對應(yīng)的代碼第一次都是按需加載的,加載完成后秃踩,就會在瀏覽器緩存中了衬鱼。

2. 如何使用webpack+vue+router 實現(xiàn)vue頁面按需加載?

還是和之前一樣憔杨,在實現(xiàn)功能之前鸟赫,我們還是看下我們項目中的整個目錄架構(gòu)如下:

### 目錄結(jié)構(gòu)如下:
demo1                                       # 工程名
|   |--- dist                               # 打包后生成的目錄文件             
|   |--- node_modules                       # 所有的依賴包
|   |--- app
|   | |---index
|   | | |-- views                           # 存放所有vue頁面文件
|   | | | |-- home.vue
|   | | | |-- index.vue
|   | | | |-- xxx.vue
|   | | |-- components                      # 存放vue公用的組件
|   | | |-- app.js                          # vue入口配置文件
|   | | |-- router.js                       # 路由配置文件
|   |--- views
|   | |-- index.html                        # html文件
|   |--- webpack.config.js                  # webpack配置文件 
|   |--- .gitignore  
|   |--- README.md
|   |--- package.json
|   |--- .babelrc                           # babel轉(zhuǎn)碼文件

webpack中提供了 require.ensure()來實現(xiàn)頁面按需加載。以前我們引入路由的時候都是通過 import 這樣的方式引入的芍秆,但是使用import引入vue文件它是不會進行頁面按需加載的惯疙。首先可以看下我們路由頁面。如下:

app/index/router.js 之前代碼使用import引入如下:

import Vue from 'vue';
import VueRouter from 'vue-router';

// 引入組件 
import home from './views/home';

import xxx from './views/xxx';

// 告訴 vue 使用 vueRouter

Vue.use(VueRouter);

const routes = [
  {
    path: '/home',
    component: home
  },
  {
    path: '/xxx',
    component: xxx
  },
  {
    path: '*', // 其他沒有的頁面都重定向到 home頁面去
    redirect: '/home'
  }
]

var router = new VueRouter({
  routes: routes
});

export default router;

如上代碼妖啥,每一個vue單頁面都是使用import引用進來的霉颠,它不會進行頁面懶加載的,所以在打包的時候都會打包到bundle.js文件內(nèi)荆虱,因此文件會變得越來越大蒿偎。因此我們需要使用 require.ensure()實現(xiàn)按需加載。

2.1怀读、學習使用 require.ensure()

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

基本使用語法如下:require.ensure(dependencies: String[], callback: function(require), chunkName: String);

參數(shù)dependencies:該參數(shù)是一個字符串數(shù)組,通過這個參數(shù)啤誊,在所有的回調(diào)函數(shù)的代碼被執(zhí)行前岳瞭,我們可以將所有需要用到的模塊進行聲明。

參數(shù)callback: 當所有的依賴都加載完成后蚊锹,webpack會執(zhí)行這個回調(diào)函數(shù)瞳筏。require 對象的一個實現(xiàn)會作為一個參數(shù)傳遞給這個回調(diào)函數(shù)。因此牡昆,我們可以進一步 require() 依賴和其它模塊提供下一步的執(zhí)行姚炕。

參數(shù)chunkName: chunkName 是提供給這個特定的 require.ensure() 的 chunk 的名稱。

下面我們來看一個demo丢烘,在app/index 下新建一個文件夾為js柱宦。在js文件夾內(nèi)分別新建a.js和b.js,代碼分別如下:

a.js 代碼如下:

alert('aaaaaa');

b.js 代碼如下

alert('bbbbbbb');

然后入口文件app.js 代碼如下:

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

webpack.config.js 打包如下:

module.exports = {
  // 入口文件
  entry: {
    main: './app/index/app.js'
  },
  output: {
    filename: process.env.NODE_ENV === 'production' ? '[name].[contenthash].js' : 'bundle.js',
    // 將輸出的文件都放在dist目錄下
    path: path.resolve(__dirname, 'dist')
  }
}

通過執(zhí)行這個項目的 webpack 構(gòu)建播瞳,我們發(fā)現(xiàn) webpack 創(chuàng)建了2個新的文件束捷沸, bundle.js 和 0.bundle.js。如下圖所示:

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

其中 0.bundle.js 代碼如下:

(window.webpackJsonp=window.webpackJsonp||[]).push([[0],{"./app/index/js/b.js":function(b,n){alert("bbbbbbb")}}]);

bundle.js 代碼如下:

!function(u){function e(e){for(var n,t,r=e[0],o=e[1],a=0,i=[];a<r.length;a++)t=r[a],p[t]&&i.push(p[t][0]),p[t]=0;for(n in o)Object.prototype.hasOwnProperty.call(o,n)&&(u[n]=o[n]);for(l&&l(e);i.length;)i.shift()()}var t={},p={main:0};function c(e){if(t[e])return t[e].exports;var n=t[e]={i:e,l:!1,exports:{}};return u[e].call(n.exports,n,n.exports,c),n.l=!0,n.exports}c.e=function(a){var e=[],t=p[a];if(0!==t)if(t)e.push(t[2]);else{var n=new Promise(function(e,n){t=p[a]=[e,n]});e.push(t[2]=n);var r,o=document.getElementsByTagName("head")[0],i=document.createElement("script");i.charset="utf-8",i.timeout=120,c.nc&&i.setAttribute("nonce",c.nc),i.src=c.p+""+a+".bundle.js",r=function(e){i.onerror=i.onload=null,clearTimeout(u);var n=p[a];if(0!==n){if(n){var t=e&&("load"===e.type?"missing":e.type),r=e&&e.target&&e.target.src,o=new Error("Loading chunk "+a+" failed.\n("+t+": "+r+")");o.type=t,o.request=r,n[1](o)}p[a]=void 0}};var u=setTimeout(function(){r({type:"timeout",target:i})},12e4);i.onerror=i.onload=r,o.appendChild(i)}return Promise.all(e)},c.m=u,c.c=t,c.d=function(e,n,t){c.o(e,n)||Object.defineProperty(e,n,{enumerable:!0,get:t})},c.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},c.t=function(n,e){if(1&e&&(n=c(n)),8&e)return n;if(4&e&&"object"==typeof n&&n&&n.__esModule)return n;var t=Object.create(null);if(c.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:n}),2&e&&"string"!=typeof n)for(var r in n)c.d(t,r,function(e){return n[e]}.bind(null,r));return t},c.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return c.d(n,"a",n),n},c.o=function(e,n){return Object.prototype.hasOwnProperty.call(e,n)},c.p="",c.oe=function(e){throw e};var n=window.webpackJsonp=window.webpackJsonp||[],r=n.push.bind(n);n.push=e,n=n.slice();for(var o=0;o<n.length;o++)e(n[o]);var l=r;c(c.s="./app/index/app.js")}({"./app/index/app.js":function(e,n,t){t("./app/index/js/a.js"),t.e(0).then(function(e){t("./app/index/js/b.js")}.bind(null,t)).catch(t.oe)},"./app/index/js/a.js":function(e,n){alert("aaaaaa")}});

如上代碼分析可以看到狐史,通過 require.ensure([], function(require) { require('./js/b'); }) 動態(tài)加載的b函數(shù)代碼會單獨生成一個0.bundle.js文件痒给,如果頁面有多個 require.ensure 這個動態(tài)加載文件的話说墨,它就會生成多個xx.bundle.js文件了,因此對于vue路由頁面來講可以使用這種方法來動態(tài)加載vue頁面了苍柏。

如上webpack.config.js 文件尼斧,我們可以再加個配置項 chunkFilename,該配置項试吁,可以指定chunk的名字棺棵,如下配置:

module.exports = {
  // 入口文件
  entry: {
    main: './app/index/app.js'
  },
  output: {
    filename: process.env.NODE_ENV === 'production' ? '[name].[contenthash].js' : 'bundle.js',
    // 將輸出的文件都放在dist目錄下
    path: path.resolve(__dirname, 'dist'),
    chunkFilename: 'chunks/[name].chunk.js'
  }
}

app/index/app.js 代碼如下,加了第三個參數(shù), 名字為b:

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

然后會在 dist/chunks 下生成 b.chunk.js, 如下圖所示:

2.2. 在vue-router中使用require.ensure() 方法動態(tài)加載不同的vue頁面熄捍。

因此我們現(xiàn)在可以在 app/index/router.js 里面如下對vue頁面文件進行按需加載了烛恤,如下代碼:

import Vue from 'vue';
import VueRouter from 'vue-router';

// 告訴 vue 使用 vueRouter
Vue.use(VueRouter);

const routes = [
  {
    path: '/home',
    name: 'home',
    component: resolve => require.ensure([], () => resolve(require('./views/home')), 'home')
  },
  {
    path: '/xxx',
    name: 'xxx',
    // component: resolve => require(['./views/xxx'], resolve)
    component: resolve => require.ensure([], () => resolve(require('./views/xxx')), 'xxx')
  },
  {
    path: '*', // 其他沒有的頁面都重定向到 home頁面去
    redirect: '/home'
  }
]

var router = new VueRouter({
  base: '/app/index', // 配置單頁應(yīng)用的基路徑
  routes: routes
});

export default router;

webpack.config.js 代碼配置如下:

module.exports = {
  // 入口文件
  entry: {
    main: './app/index/app.js'
  },
  output: {
    filename: process.env.NODE_ENV === 'production' ? '[name].[contenthash].js' : 'bundle.js',
    // 將輸出的文件都放在dist目錄下
    path: path.resolve(__dirname, 'dist'),
    chunkFilename: 'chunks/[name].chunk.js'
  }
}

在dist/chunks 目錄下會生成如下文件,如下圖所示

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末余耽,一起剝皮案震驚了整個濱河市缚柏,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌碟贾,老刑警劉巖币喧,帶你破解...
    沈念sama閱讀 217,734評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異袱耽,居然都是意外死亡杀餐,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,931評論 3 394
  • 文/潘曉璐 我一進店門朱巨,熙熙樓的掌柜王于貴愁眉苦臉地迎上來史翘,“玉大人,你說我怎么就攤上這事冀续∏矸恚” “怎么了?”我有些...
    開封第一講書人閱讀 164,133評論 0 354
  • 文/不壞的土叔 我叫張陵沥阳,是天一觀的道長跨琳。 經(jīng)常有香客問我自点,道長桐罕,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,532評論 1 293
  • 正文 為了忘掉前任桂敛,我火速辦了婚禮功炮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘术唬。我一直安慰自己薪伏,他們只是感情好,可當我...
    茶點故事閱讀 67,585評論 6 392
  • 文/花漫 我一把揭開白布粗仓。 她就那樣靜靜地躺著嫁怀,像睡著了一般设捐。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上塘淑,一...
    開封第一講書人閱讀 51,462評論 1 302
  • 那天萝招,我揣著相機與錄音,去河邊找鬼存捺。 笑死槐沼,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的捌治。 我是一名探鬼主播岗钩,決...
    沈念sama閱讀 40,262評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼肖油!你這毒婦竟也來了兼吓?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,153評論 0 276
  • 序言:老撾萬榮一對情侶失蹤构韵,失蹤者是張志新(化名)和其女友劉穎周蹭,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體疲恢,經(jīng)...
    沈念sama閱讀 45,587評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡凶朗,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,792評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了显拳。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片棚愤。...
    茶點故事閱讀 39,919評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖杂数,靈堂內(nèi)的尸體忽然破棺而出宛畦,到底是詐尸還是另有隱情,我是刑警寧澤揍移,帶...
    沈念sama閱讀 35,635評論 5 345
  • 正文 年R本政府宣布次和,位于F島的核電站,受9級特大地震影響那伐,放射性物質(zhì)發(fā)生泄漏踏施。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,237評論 3 329
  • 文/蒙蒙 一罕邀、第九天 我趴在偏房一處隱蔽的房頂上張望畅形。 院中可真熱鬧,春花似錦诉探、人聲如沸日熬。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,855評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽竖席。三九已至耘纱,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間毕荐,已是汗流浹背揣炕。 一陣腳步聲響...
    開封第一講書人閱讀 32,983評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留东跪,地道東北人畸陡。 一個月前我還...
    沈念sama閱讀 48,048評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像虽填,于是被迫代替她去往敵國和親丁恭。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,864評論 2 354