前言
vue-cli是Vue.js官方推出的腳手架,它功能豐富、擴充套件性強,為Vue應用開發帶來了極大的便捷,它提供了多種開發正規化,詮釋了開箱即用。vue-cli@3版本經歷了alpha、beta、rc版本近7個月的迭代開發,在最近幾天正式版終於釋出,本文主要講解如何使用vue-cli建立一個多入口工程,若要近一步瞭解vue-cli,請訪問官方文件。
什麼是多頁應用
單頁應用(SPA)往往只含有包含一個主入口檔案與index.html,頁面間切換通過區域性重新整理資源來完成。而在多頁應用中,我們會為每個HTML文件檔案都指定好一個JS入口,這樣一來當頁面跳轉時使用者會獲得一個新的HTML文件,整個頁面會重新載入。
單頁應用、多頁應用的優劣勢在此就不進行分析了,總而言之,多頁架構模式暫時是無法取代的,如果嘗試把幾十個不關聯的頁面做成一個,那麼開發成本會非常大的,Not every app has to be an SPA。
初始化專案
首先我們安裝好vue-cli腳手架,並初始化一個預設工程. 修改專案目錄:
.
├── assets
│ └── logo.png
├── components
│ ├── About.vue
│ ├── HelloWorld.vue
│ └── Home.vue
├── pages
│ ├── page1
│ │ ├── page1.html
│ │ ├── page1.js
│ │ └── page1.vue
│ └── page2
│ ├── page2.html
│ ├── page2.js
│ └── page2.vue
└── style
├── common.css
└── common.less
複製程式碼
vue.config.js是一個可選檔案,使用者需要自行建立,它會被@vue/cli-service
讀取。當正確新增配置後,重啟一下專案,測試一下專案在改變目錄結構後能否正常執行。試想一下,若照著這個思路進行配置多入口,那麼首先需要刪除或修改掉原有webpack配置項,然後還需新增多入口的一些外掛,雖然通過腳手架對外提供的API可以實現,可是這種修改方式還不是直接修改原生構建配置更快,那麼還有其他解決方法嗎?
vue.config.js 配置
let path = require('path')
let glob = require('glob')
//配置pages多頁面獲取當前資料夾下的html和js
function getEntry(globPath) {
let entries = {},
basename, tmp, pathname, appname;
glob.sync(globPath).forEach(function(entry) {
basename = path.basename(entry, path.extname(entry));
// console.log(entry)
tmp = entry.split('/').splice(-3);
console.log(tmp)
pathname = basename; // 正確輸出js和html的路徑
// console.log(pathname)
entries[pathname] = {
entry: 'src/' + tmp[0] + '/' + tmp[1] + '/' + tmp[1] + '.js',
template: 'src/' + tmp[0] + '/' + tmp[1] + '/' + tmp[2],
title: tmp[2],
filename: tmp[2]
};
});
return entries;
}
let pages = getEntry('./src/pages/**?/*.html');
console.log(pages)
//配置end
module.exports = {
lintOnSave: false, //禁用eslint
baseUrl:process.env.NODE_ENV === "production"?'https://www.mycdn.com/':'/',
productionSourceMap: false,
pages,
devServer: {
index: 'page1.html', //預設啟動serve 開啟page1頁面
open: process.platform === 'darwin',
host: '',
port: 8088,
https: false,
hotOnly: false,
proxy: {
'/xrf/': {
target: 'http://reg.tool.hexun.com/',
changeOrigin: true,
pathRewrite: {
'^/xrf': ''
}
},
'/wa/': {
target: 'http://api.match.hexun.com/',
changeOrigin: true,
pathRewrite: {
'^/wa': ''
}
}
}, // 設定代理
before: app => {}
},
chainWebpack: config => {
config.module
.rule('images')
.use('url-loader')
.loader('url-loader')
.tap(options => {
// 修改它的選項...
options.limit = 100
return options
})
Object.keys(pages).forEach(entryName => {
config.plugins.delete(`prefetch-${entryName}`);
});
if(process.env.NODE_ENV === "production") {
config.plugin("extract-css").tap(() => [{
path: path.join(__dirname, "./dist"),
filename: "css/[name].[contenthash:8].css"
}]);
}
},
configureWebpack: config => {
if(process.env.NODE_ENV === "production") {
config.output = {
path: path.join(__dirname, "./dist"),
filename: "js/[name].[contenthash:8].js"
};
}
}
}
複製程式碼
dist打包目錄
├── css
│ ├── page1.9951d5a1.css
│ └── page2.009d0d6f.css
├── img
│ └── logo.82b9c7a5.png
├── js
│ ├── chunk-vendors.f061f10e.js
│ ├── page1.5a5322e0.js
│ └── page2.db57562b.js
├── page1.html
└── page2.html
複製程式碼
原始碼部分
@vue/cli-service通過判斷是否傳入pages引數來生成對應Webpack配置檔案,讓我們先來看看沒有傳入時的處理函式:
if (!multiPageConfig) {
// default, single page setup.
htmlOptions.template = fs.existsSync(htmlPath)
? htmlPath
: defaultHtmlPath
webpackConfig
.plugin('html')
.use(HTMLPlugin, [htmlOptions])
if (!isLegacyBundle) {
// inject preload/prefetch to HTML
...
}
}
複製程式碼
由原始碼可知,pages引數可用於生成三個外掛:preload-plugin、prefetch-plugin、html-plugin,若不傳html檔案則會使用一個只空的預設html檔案,而在多入口模式下,程式碼的邏輯也很簡單,在此就不貼原始碼了,它會執行以下步驟:
- 清除原有entry
- 對pages欄位的每個key做迴圈,解析每個入口物件的引數entry(必填)、title、template、filename、chunks
- 通過entry欄位生成webpack的entry入口
- 通過其餘引數生成對應的html-webpack-plugin,若不為傳統模式,也會生成對應入口的preload外掛與prefetch外掛
區域性優化
移除prefetch
由於本人並不喜歡為將來做打算,因此並不希望預載入一些可能會用到的asyncChunk,因為會浪費掉一些頻寬,而且在多頁面中並不見得預載入其他入口的檔案是一件好事情,於是我們通過chainWebpack進行刪除:
modules.exports = {
// ...
chainWebpack: config => {
Object.keys(pages).forEach(entryName => {
config.plugins.delete(`prefetch-${entryName}`);
});
}
}
複製程式碼
關閉SourceMap
關閉之後不僅能加快生產環境的打包速度,也能避免原始碼暴露在瀏覽器端:
modules.exports = {
// ...
productionSourceMap: false,
}
複製程式碼
打包分類(強迫症患者福音)
首先回顧一下dist中的部分資料夾:
dist打包目錄
├── css
│ ├── page1.9951d5a1.css
│ └── page2.009d0d6f.css
├── img
│ └── logo.82b9c7a5.png
├── js
│ ├── chunk-vendors.f061f10e.js
│ ├── page1.5a5322e0.js
│ └── page2.db57562b.js
├── page1.html
└── page2.html
複製程式碼