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