vue多頁面項目開發(fā)實(shí)戰(zhàn)指南
單頁應(yīng)用和多頁應(yīng)用
單頁應(yīng)用
SPA(single page application): 單頁面應(yīng)用,即一個web項目就只有一個頁面(即一個HTML文件)。
就是把整個項目的所有頁面的所有內(nèi)容分成了很多的小塊(就是組件),可以重復(fù)利用的,可以任意調(diào)整的組件,每個組件就是一個獨(dú)立的部分(包括html,css和javascript代碼)。再做一個html(基本上啥也沒有),這個html就是一個頁面容器,需要放哪個組件時,直接引入就行。跳轉(zhuǎn)時,直接跳轉(zhuǎn)組件就行。當(dāng)需要加載某個組件時,js會動態(tài)創(chuàng)建這些組件里的HTML,CSS。
這類項目通常都需要router來進(jìn)行頁面跳轉(zhuǎn).
一開始只需要加載一次js、css的相關(guān)資源。所有內(nèi)容都包含在主頁面,對每一個功能模塊組件化。單頁應(yīng)用跳轉(zhuǎn),就是切換相關(guān)組件,僅僅刷新局部資源。
打包后的頁面dist 目錄結(jié)構(gòu)
dist ├── static │ ├── css │ ├── js │ ├── img │ ├── dll │ └── ... └── index.html └── ... └── ...
多頁應(yīng)用
MPA(multipage application): 多頁面應(yīng)用,即一個web項目就有多個頁面(即多個HTML文件)。
指有多個獨(dú)立頁面的應(yīng)用(多個html頁面),每個頁面必須重復(fù)加載js、css等相關(guān)資源。多頁應(yīng)用跳轉(zhuǎn),需要整頁資源刷新。
項目是由多個完整的頁面組成。多頁面跳轉(zhuǎn)刷新所有資源,每個公共資源(js、css等)需選擇性重新加載。
打包后的頁面dist 目錄結(jié)構(gòu)
dist ├── page(這里名字打包出哪個文件夾自己配置) │ ├── css │ ├── js │ ├── img │ ├── index.html │ └── user.html │ └── setting.html │ └── ....html │ └── ....html . .... . .... . ....
優(yōu)缺點(diǎn)
單頁應(yīng)用的優(yōu)缺點(diǎn)
優(yōu)點(diǎn)
有良好的交互體驗(yàn)。能提升頁面切換體驗(yàn),用戶在訪問應(yīng)用頁面是不會頻繁的去切換瀏覽頁面,從而避免了頁面的重新加載。
單頁面是一次性把web應(yīng)用的所有代碼(HTML,JavaScript和CSS)全部請求過來,有時候考慮到首屏加載太慢會按需加載。這樣一來,以后用戶的每一個動作都不會重新加載頁面(即不用再問服務(wù)器要頁面的HTML慢,css和js代碼),取而代之的是利用 JavaScript 動態(tài)的變換HTML的內(nèi)容(這不需要和服務(wù)器交互,除非數(shù)據(jù)是動態(tài),那么只需要問服務(wù)器要數(shù)據(jù)即可)。缺點(diǎn)
SEO難度較高。
首屏加載(初次加載)耗時多。為實(shí)現(xiàn)單頁Web應(yīng)用功能及顯示效果,需要在加載頁面的時候?qū)avaScript、CSS統(tǒng)一加載,部分頁面可以在需要的時候加載。所以必須對JavaScript及CSS代碼進(jìn)行合并壓縮處理;多頁應(yīng)用的優(yōu)缺點(diǎn)
優(yōu)點(diǎn)
有利于seo。
首屏加載加載快。缺點(diǎn)
頁面切換慢。資源共用(html、css,js)不共享,不共用,每個頁面都需要加載。
頁面重復(fù)代碼多。
配置多頁應(yīng)用
1. 修改vue.config.js
在 multi-page 模式下構(gòu)建應(yīng)用。每個“page”應(yīng)該有一個對應(yīng)的 JavaScript 入口文件。其值應(yīng)該是一個對象,對象的 key 是入口的名字,value 是:
- 一個指定了 entry, template, filename, title 和 chunks 的對象 (除了 entry 之外都是可選的);
- 或一個指定其 entry 的字符串。
module.exports = { pages: { index: { // page 的入口 entry: 'src/index/main.js', // 模板來源 template: 'public/index.html', // 在 dist/index.html 的輸出 filename: 'index.html', // 當(dāng)使用 title 選項時, // template 中的 title 標(biāo)簽需要是 <title><%= htmlWebpackPlugin.options.title %></title> title: 'Index Page', // 在這個頁面中包含的塊,默認(rèn)情況下會包含 // 提取出來的通用 chunk 和 vendor chunk。 chunks: ['chunk-vendors', 'chunk-common', 'index'] }, // 當(dāng)使用只有入口的字符串格式時, // 模板會被推導(dǎo)為 `public/subpage.html` // 并且如果找不到的話,就回退到 `public/index.html`。 // 輸出文件名會被推導(dǎo)為 `subpage.html`。 subpage: 'src/subpage/main.js' } }
以上是官網(wǎng)的例子,這里我們改寫一下,統(tǒng)一配置多頁
前提條件在src 下新建一個pages文件夾
pages新建如下三個文件
pages └── index-skeleton.html └── indexApp.html └── app.js
const glob = require('glob') const fs = require('fs'); let titleObj = {}; // 統(tǒng)一配置多頁 // 這里是遍歷src下面的pages 下面每個文件夾(例如index)下以xxxApp.vue 命名的vue頁面 glob.sync('./src/pages/**/*App.vue').forEach((path) => { // 遍歷path console.log(path,'path') //./src/pages/index/indexApp.vue path // 找到文件名 const fileName = path.split('/')[path.split('/').length - 1]; console.log(fileName,'fileName') // indexApp.vue fileName // 去掉App 后綴 const chunk = path.substring(12, path.indexOf('/' + fileName)); console.log(chunk,'chunk') // index chunk // 這里是給每個頁面設(shè)置標(biāo)題,需要在indexApp.vue設(shè)置一個變量pageTitle let fileContent = fs .readFileSync(path, { encoding: 'utf-8' }) .toString() .replace(/\r\n/g, ''); fileContent = fileContent.substr(fileContent.indexOf('pageTitle:')); fileContent = fileContent.substr(0, fileContent.indexOf(',')); fileContent = fileContent.indexOf('"') > 0 ? fileContent.substr(fileContent.indexOf('"') + 1) : fileContent.substr(fileContent.indexOf("'") + 1); fileContent = fileContent.indexOf('"') > 0 ? fileContent.substr(0, fileContent.indexOf('"')) : fileContent.substr(0, fileContent.indexOf("'")); titleObj[chunk] = fileContent ? fileContent : '標(biāo)題'; }); // 這里是遍歷src下面的pages 下面每個文件夾(例如index)下以app.js glob.sync('./src/pages/**/app.js').forEach((path) => { //打包js const chunk = path.split('./src/pages/')[1].split('/app.js')[0]; const tmp = chunk.split('/'); // 模版html,如果都是一樣的,可以直接使用public下index.html,如果要設(shè)置標(biāo)題的話,需要每個頁面都有一個html模版,如果不需要,就可以使用同一個,看個人習(xí)慣 let templateUrl = 'src/pages/' + chunk + '/' + tmp[tmp.length - 1] + '-skeleton.html'; pages[chunk] = { entry: path,//入口文件 template: templateUrl,//模版html title: titleObj[chunk] ? titleObj[chunk] : '標(biāo)題',//標(biāo)題 filename: chunk.replace(/\//g, '-') + '.html',//打包出來的html名字 chunks: ['chunk-vendors', 'chunk-common', chunk],//依賴包 }; }); module.exports = { // 選項... publicPath: process.env.NODE_ENV === 'production' ? '/dist/' : '/', pages, }
2. 修改title
其實(shí)是用插件替換的
很簡單,就是把html模版中的title使用模版語法就行
例如index-skeleton.html 這里每個頁面html都是一樣的,復(fù)制即可
<title><%= htmlWebpackPlugin.options.title %></title>
3. 合并第三方庫
如果不設(shè)置分包,所有node_modules 里面的第三方資源庫,例如Echarts,Axios,ali-oss,等等都會被打進(jìn)chunk-vendors,至于為什么會打進(jìn)去,我們看下vue.config.js默認(rèn)的分包規(guī)則
官網(wǎng)默認(rèn)的配置
module.exports = { //... //... optimization: { splitChunks: { chunks: 'async', // 代碼分割時對異步代碼生效,all:所有代碼有效,inital:同步代碼有效 minSize: 30000, // 代碼分割最小的模塊大小,引入的模塊大于 30000B 才做代碼分割 maxSize: 0, // 代碼分割最大的模塊大小,大于這個值要進(jìn)行代碼分割,一般使用默認(rèn)值 minChunks: 1, // 引入的次數(shù)大于等于1時才進(jìn)行代碼分割 maxAsyncRequests: 6, // 最大的異步請求數(shù)量,也就是同時加載的模塊最大模塊數(shù)量 maxInitialRequests: 4, // 入口文件做代碼分割最多分成 4 個 js 文件 automaticNameDelimiter: '~', // 文件生成時的連接符 automaticNameMaxLength: 30, // 自動生成的文件名的最大長度 cacheGroups: { vendors: { test: /[\\/]node_modules[\\/]/, // 位于node_modules中的模塊做代碼分割 priority: -10 // 根據(jù)優(yōu)先級決定打包到哪個組里,例如一個 node_modules 中的模塊進(jìn)行代碼 }, // 分割,,既滿足 vendors,又滿足 default,那么根據(jù)優(yōu)先級會打包到 vendors 組中。 default: { // 沒有 test 表明所有的模塊都能進(jìn)入 default 組,但是注意它的優(yōu)先級較低。 priority: -20, // 根據(jù)優(yōu)先級決定打包到哪個組里,打包到優(yōu)先級高的組里。 reuseExistingChunk: true // //如果一個模塊已經(jīng)被打包過了,那么再打包時就忽略這個上模塊 } } } } };
我們重點(diǎn)看下 minChunks 這個配置,默認(rèn)大于1次就會進(jìn)行分包操作,以前是一個單頁面,所以分包沒有問題,只會在index.html引入,現(xiàn)在是多頁面,每個頁面都會引入這個chunk-vendors,有些包其實(shí)只有兩三個頁面用到,因此,最好是不分包,或者達(dá)到一定次數(shù)才有分包意義
我們這里設(shè)置8次,才分包,幾乎沒有分包,根據(jù)各位需求可以自己設(shè)置
optimization: { minimize: false, splitChunks: { cacheGroups: {// 緩存分組 common: {// 公共的模塊 name: 'chunk-common',//命名要和上面chunks定義的一致 chunks: 'initial', minSize: 1,// 大小限制 priority: 0, minChunks: 8,// 最少復(fù)用過幾次 }, // 打包第三方庫的文件 vendor: { name: 'chunk-vendors',//命名要和上面chunks定義的一致 test: /[\\/]node_modules[\\/]/, chunks: 'initial', priority: 10,// 權(quán)限更高,優(yōu)先抽離,重要!??! minChunks: 8, }, }, }, },
4. 打包第三方scss
有一些自己寫的公共scss,比如common.scss ,不想在頁面引入,因?yàn)槊總€頁面都要引入,其實(shí)也有很簡單處理的方法,這和以前沒什么變化,話不多說,直接上代碼
let scssVariables = require('./src/scss/variables.scss.js'); css: { loaderOptions: { scss: { prependData: Object.keys(scssVariables) .map((k) => `${k.replace('_', '-')}: ${scssVariables[k]};`) .join('\n') + '\n', }, }, },
5. 其它常見設(shè)置
就是一些常見設(shè)置,看個人設(shè)置 這里重點(diǎn)推薦一下filenameHashing,多頁面應(yīng)用不帶hash的設(shè)置,因?yàn)闆]有使用路由,也就用不到了.
module.exports = { publicPath: './', //輸出目錄 outputDir: 'fund', assetsDir: '', // 配置別名 chainWebpack: (config) => { config.resolve.alias.set('@', resolve('src')); config.resolve.alias.set('@@', resolve('src/components')); config.resolve.alias.set('@assets', resolve('src/assets')); config.resolve.alias.set('scss', resolve('src/scss')); }, // 關(guān)閉eslint校驗(yàn) lintOnSave: false, // 不生成map文件 productionSourceMap: false, //文件名稱不帶hash值 filenameHashing: false, devServer: { publicPath: '/fund/', proxy: {// 本地調(diào)試轉(zhuǎn)發(fā) '/api': { target: 'http://127.0.0.1:8080', changeOrigin: true, pathRewrite: { '^/api': '', }, }, }, }, };
總結(jié)
到此這篇關(guān)于vue多頁面項目開發(fā)的文章就介紹到這了,更多相關(guān)vue多頁面項目開發(fā)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
vue子傳父關(guān)于.sync與$emit的實(shí)現(xiàn)
這篇文章主要介紹了vue子傳父關(guān)于.sync與$emit的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-11-11Vue基于iview實(shí)現(xiàn)登錄密碼的顯示與隱藏功能
這篇文章主要介紹了Vue基于iview實(shí)現(xiàn)登錄密碼的顯示與隱藏功能,本文通過截圖實(shí)例代碼說明給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-03-03vue中實(shí)現(xiàn)methods一個方法調(diào)用另外一個方法
下面小編就為大家分享一篇vue中實(shí)現(xiàn)methods一個方法調(diào)用另外一個方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-02-02