為何選擇多單頁
最近在核心業務上做了一項新的嘗試,拋棄臃腫的PHP後端渲染的多頁應用模式,全站改造單頁應用。單頁渲染在載入速度已經有了極大的提升,並且MVVM的模式使前端開發從繁瑣的DOM操作中解放出來。為什麼考慮多單頁模式,可綜合多頁和單頁的優勢,多頁分離減少模組耦合,特別是應用複雜程度高時,此後也可在每個單頁裡面嘗試不同的方案,利於擴充。本文是記錄多單頁實踐的思路,腳手架vue-cli
生成,詳細的程式碼可參考demo,歡迎探討。
webpck搭建多單頁
1.從入口檔案entry
說起
entry
物件主要用於配置應用js
入口檔案,由模組名稱為屬性名(編譯生成的js chunks
的名稱),以該js
檔案路徑為屬性值。以vue-cli
搭建的單頁應用為例,以下是初始配置。單頁應用通常只有一個入口js
檔案(不包括其他公共js
模組)。
多單頁應用則有多個js
入口檔案,entry
也要配置多個入口,如下:
webpack.base.conf.js
entry: {
'index': './src/pages/main.js',// index.html 的入口檔案
'asset': './src/pages/asset/index.js',// 頁面 asset.html 的入口檔案
'login': './src/pages/login/index.js',// 頁面 login.html 的入口檔案
},
複製程式碼
當頁面應用比較多的時候,可以引入glob
模組,封裝遍歷函式。
//引入模組
var glob = require('glob');
//遍歷函式
function getEntry(globPath) {
var entries = {},
basename, tmp, pathname;
//下面的路徑根據自己的目錄結構修改,列印出來檢查是否跟手寫的一致
glob.sync(globPath).forEach(function (entry) {
basename = path.basename(entry, path.extname(entry));
console.log("basename",basename);
tmp = entry.split('/').splice(-3);
pathname = tmp.splice(0, 1) + '/' + tmp.splice(0, 1) + '/' + basename; // 正確輸出js和html的路徑
entries[pathname] = entry;
});
return entries;
}
//遍歷pages路徑下所有的main.js檔案,所有的入口檔案命名一致
var entries=getEntry('./src/pages/**/main.js');
// 傳值
entry: entries,
複製程式碼
關於entry
更詳細的內容可參考官網。
2. 多頁應用還需修改HtmlWebpackPlugin
配置頁面渲染模版
webpack.dev.conf.js
new HtmlWebpackPlugin(
{
//訪問
filename: 'pages/login/login.html',
template: './src/pages/login/login.html',
inject: true,
//對應entry的chunks名稱
chunks:['login']
}),
new HtmlWebpackPlugin({
filename: 'pages/asset/asset.html',
template: './src/pages/asset/asset.html',
inject: true,
chunks:['asset']
}),
複製程式碼
寫成遍歷: webpack.dev.conf.js
var pages = getEntry('./src/pages/**/*.html');
for (var pathname in pages) {
// 配置生成的html檔案,定義路徑等
var conf = {
filename: pathname + '.html',
template: pages[pathname], // 模板路徑
inject: true // js插入html尾部
};
if (pathname in baseWebpackConfig.entry) {
//這裡配置當前單頁需要載入的chunks
conf.chunks = ['manifest', 'vendor',pathname];
conf.hash = true;
}
devWebpackConfig.plugins.push(new HtmlWebpackPlugin(conf));
}
//遍歷函式
function getEntry(globPath) {
var entries = {}, basename, tmp, pathname;
glob.sync(globPath).forEach(function (entry) {
basename = path.basename(entry, path.extname(entry));
tmp = entry.split('/').splice(-3);
pathname = tmp.splice(0, 1) + '/' + tmp.splice(0, 1) + '/' + basename; // 正確輸出js和html的路徑
entries[pathname] = entry;
});
return entries;
}
複製程式碼
webpack構建多單頁一些踩過的坑
- webpack打包後樣式丟失 檢查了好久發現是這2個外掛衝突的問題,postCss loader 與 OptimizeCSSPlugin。 解決:去掉下列程式碼。
new OptimizeCSSPlugin({
cssProcessorOptions: config.build.productionSourceMap
? { safe: true, map: { inline: false } }
: { safe: true }
}),
複製程式碼
- cannot find the element #app
單頁頁面載入了其他頁面的入口檔案,找不到對應的模版id導致。解決:在webpack配置,在前面
HtmlWebpackPlugin
配置只注入指定的chunks
,這樣就不會把其他頁面的js也載入進來,導致找不到對應的html模版,也優化了載入。
if (pathname in baseWebpackConfig.entry) {
//這裡配置當前單頁需要載入的chunks
conf.chunks = ['manifest', 'vendor',pathname];
conf.hash = true;
}
複製程式碼
- 多單頁命名問題 多單頁面vue應用根元素的id不能相同, 否則會出現頁面互相覆蓋的問題。
new Vue({
//根元素id
el: '#app',
components: { App },
template: '<App/>'
})
複製程式碼
- css 到底是
import
還是require
兩種都是模組引入的方式,import使es6規範,require是commonJS規範,需要注意的是import會有提升效果,可能出現import覆蓋require樣式。
require('/style.css');
import '/style.css';
複製程式碼
-
多單頁之間vuex資料不能共享 單頁應用之間vuex資料是不能共享的,因為其本質還是多頁應用。頁面與頁面之間的資料傳遞還是要通過URL傳引數或者通過快取讀取。這在一定程度上還是增加了應用複雜度,所以在業務粒度的拆分上不能太細,儘量保證在單個業務流程是在一個單頁內,資料可以共享。
-
考慮後續部署的問題 單頁應用的部署很簡單,只有一個html檔案,採取多單頁的方案,必須要提前跟運維溝通後續部署的問題,確保能實現平穩過渡。