Vue 是很好用,可是以往的都是單頁面應用,這就致使了一些傳統的項目移植困難,一些用了 JQ 的插件的等等寫法都要改變。也還用專門找到相對於的 Vue 的插件才行,此次的 Cli 3.0 能夠在原來項目的基礎上直接移植,很是方便。css
在本文中,會講到以下內容:html
Vue 多頁面的優點與劣勢
Cli 3.0 的基本配置
Cli 3.0 多頁面的打包上線
Cli 3.0 的目錄解析
如何提高構建效率
受衆人羣:常常用 Vue 單頁面開發的人員,對多頁面有興趣,且實際工做中有需求。老項目想先後端分離,考慮效率又不想用單頁面重寫的開發人員。前端
@[toc]vue
Vue.js 3.0 支持單頁面也支持多頁面,不過對用久了 2.0 人的來講,開始還會有一點不習慣。建立方式、目錄結構、運行命令都有差別。node
本文將圍繞實際多頁面開發案例,剖析多頁面從構建到上線一條龍的過程。自定義配置有蠻多種,這裏只是只說其中一種。供你們參考使用。webpack
本篇文章的目錄結構核心以下: ios
單頁面應用開發(SPA)git
多頁面應用開發(MPA)web
以上一對比,多頁面仍是有蠻多優點的,特別是在老項目想先後端分離的時候,尤其突出。雖然近幾年 Vue 等框架興起,可是之前用 JQ、JS、其餘插件寫的項目也很多。大多都是沒有分離的,先後端分工不明確,甚至致使前端只是個寫頁面的,丟給跟後臺去套數據。通俗點來講就是個頁面仔、效果仔了。npm
多頁面(MPA)完美地解決了這個問題,能夠快速地在以前的狀況下使用,並存。
新建項目,執行:
$ vue create demo
這裏選默認第一個就好。
接下來用哪一個方式都行,我是習慣用 npm。
等待下載完成,初始化的目錄 (爲了你們清楚地對比多頁面改造後的,我把初始化跟改造後都列出來來供你們比對分析):
├── node_modules npm install 生成 └── public 打包所需靜態資源 ├── index.html 模板文件 └── favicon.ico 瀏覽器圖標 └── src └── assets 項目靜態資源 ├── logo.png ├── components 業務組件 ├── App.vue ├── main.js ├── .gitignore Git提交時忽略配置 ├── babel.config.js babel配置 ├── package.json ├── package-lock.json ├── README.md └── vue.config.js ( 須要自行建立 )
跟 2.0 相比,目錄簡化許多,webpack 配置也集成到 node_modules 去了。
留有一個配置入口,就是 vue.config.js 文件,這須要自行建立,
如沒有就會用默認的 http://localhost:8080/。
也就是 2.0 中 config/Index.js 的配置移到這裏去了,包括代理都在這寫。
這裏是已經改形成多頁面的目錄:
如上圖,目錄大致跟初始化的差很少,惟一的就是配置的在 src 中頁面,一個目錄生產出來就是一個單獨的 HTML。
詳細對應描述:
├── dist 打包後目錄 ├── node_modules npm install 生成 └── public 打包所需靜態資源 └── favicon.ico 瀏覽器圖標 └── src └── assets 項目靜態資源 ├── logo.png ├── components 業務組件 ├── pages 頁面文件 └── index 單個頁面目錄 └── index.html 單個頁面目錄 └── index.js 單個頁面入口 js (至關於 2. 0 的 main.js) └── index.vue 此頁面頁面組件 ├── util 配置放置目錄 └── axiosTool.js 請求封裝 └── cssCopy.js 多頁面 css 配置文件 └── getPages.js 多頁面 模板 配置文件 └── htmlReplace.js 多頁面 html 生成規則配置文件 └── jsCopy.js 多頁面 js 配置文件 ├── .gitignore Git提交時忽略配置 ├── babel.config.js babel配置 ├── package.json ├── package-lock.json ├── postcss.config.js ├── README.md └── vue.config.js
3.0 的目錄結構簡潔,多頁面的目錄能夠自行修改。改造後,本來的 public 的 index.html 就不須要了,每一個頁面都有一個 index.html。
配置分爲四個模塊,CSS 拷貝、JS 拷貝、HTML 規則以及獲取頁面。
在 util 中新建 cssCopy.js、jsCopy.js、htmlReplace.js、getPages.js 文件。引入 Node 的 fs 跟 glob 模塊:
### cssCopy.js: var fs = require( 'fs' ); const glob = require('glob'); /** * css文件拷貝 * @param src * @param dst */ var callbackFile = function( src, dst ){ fs.readFile(src,'utf8',function(error,data){ if(error){ console.log(error); return false; } fs.writeFile(dst,data.toString(),'utf8',function(error){ if(error){ console.log(error); return false; } // console.log('CSS寫入成功'); fs.unlink(src,function () {// css刪除成功 }) }) }) }; // 複製目錄 glob.sync( './dist/css/*.css').forEach((filepath,name) => { let fileNameList = filepath.split('.'); let fileName = fileNameList[1].split('/')[3];// 多頁面頁面目錄 let copyName = filepath.split('/')[3]; let changeDirectory = `./dist/${fileName}/css`;// 多頁面JS文件地存放址 fs.exists( changeDirectory, function( exists ){ if( exists ){// 已存在 // console.log(`${fileName}下CSS文件已經存在`) callbackFile(filepath,`${changeDirectory}/${copyName}`) } else{// 不存在 fs.mkdir( changeDirectory, function(){ callbackFile(filepath,`${changeDirectory}/${copyName}`) // console.log(`${fileName}下CSS文件建立成功`) }); } }); });
### jsCopy.js: var fs = require( 'fs' ); const glob = require('glob'); /** * JS文件拷貝 * @param src * @param dst */ let remoevePath = null var callbackFile = function( src, dst ){ fs.readFile(src,'utf8',function(error,data){ if(error){ console.log(error); return false; } fs.writeFile(dst,data.toString(),'utf8',function(error){ if(error){ console.log(error); return false; } if(dst.includes('.map')){ // let srcName = src.split('/')[4]; // fs.unlink(`./dist/js/${srcName}.map`,function () {// 刪除map // }) // fs.unlink(`./dist/js/${srcName}`,function () {// 刪除js // }) }else{//JS寫入成功 callbackFile(dst,`${dst}.map`) } }) }) }; // 複製目錄 glob.sync( './dist/js/*.js').forEach((filepath,name) => { let fileNameList = filepath.split('.'); let fileName = fileNameList[1].split('/')[3];// 多頁面頁面目錄 let copyName = filepath.split('/')[3]; let changeDirectory = `./dist/${fileName}/js`;// 多頁面JS文件地存放址 if(!fileName.includes('chunk-vendors')){ fs.exists( changeDirectory, function( exists ){ if( exists ){// 已存在 // console.log(`${fileName}下JS文件已經存在`) callbackFile(filepath,`${changeDirectory}/${copyName}`) } else{// 不存在 fs.mkdir( changeDirectory, function(){ callbackFile(filepath,`${changeDirectory}/${copyName}`) // console.log(`${fileName}下JS文件建立成功`) }); } }); } });
### htmlReplace.js var fs = require( 'fs' ); const glob = require('glob'); /** * html文件替換 * @param src * @param dst */ var callbackFile = function( src,dst, name, filepath ){ fs.readFile(src,'utf8',function(error,data){ if(error){ console.log(error); return false; } let regCss = new RegExp("\/css\/"+name+"",'g'); let regJs = new RegExp("\/js\/"+name+"",'g'); let htmlContent = data.toString().replace(regCss,`\.\/css\/${name}`).replace(regJs,`\.\/js\/${name}`); fs.writeFile(dst,htmlContent,'utf8',function(error){ if(error){ console.log(error); return false; } // console.log('html從新寫入成功'); if(src.indexOf('/index.html') == -1){ fs.unlink(src,function () { // console.log('html刪除成功') }) } fs.unlink(filepath,function () {// css刪除成功 }) fs.unlink(filepath+'.map',function () {// css刪除成功 }) }) }) }; // 複製目錄 glob.sync( './dist/js/*.js').forEach((filepath,name) => { let fileNameList = filepath.split('.'); let fileName = fileNameList[1].split('/')[3];// 多頁面頁面目錄 let thisDirectory = `./dist/${fileName}/${fileName}.html`;// 多頁面JS文件地存放址 let changeDirectory = `./dist/${fileName}/index.html`;// 多頁面JS文件地存放址 if(!fileName.includes('chunk-vendors')){ callbackFile(thisDirectory,changeDirectory,fileName,filepath) } });
### getPages.js const glob = require('glob') let pages = {} module.exports.pages = function (){ glob.sync( './src/pages/*/*.js').forEach(filepath => { let fileList = filepath.split('/'); let fileName = fileList[fileList.length-2]; pages[fileName] = { entry: `src/pages/${fileName}/${fileName}.js`, // 模板來源 template: `src/pages/${fileName}/${fileName}.html`, // 在 dist/index.html 的輸出 filename: process.env.NODE_ENV === 'development'?`${fileName}.html`:`${fileName}/${fileName}.html`, // 提取出來的通用 chunk 和 vendor chunk。 chunks: ['chunk-vendors', 'chunk-common', fileName] } }) return pages };
最後在 vue.config.js 中引入:
let pageMethod = require('./util/getPages.js'); pages = pageMethod.pages(); module.exports = { pages }
以上的 jsCopy、cssCopy、htmlReplace 是在打包的時候執行的,在 package.json 中加入。
到這裏,多頁面的配置修改就完了。
改造的原理就是,利用 Node 的文件系統把生成的文件,進行移動複製、組合,按照一個頁面一個目錄,一個頁面三個文件,以達到能組件化開發,打包後多個 HTML 文件。
$ npm run serve
1. 檢查
下面是 src 目錄文件:
一個目錄一個 HTML 頁面,目錄中 index.html 是入口文件,至關於單頁面中的 index.html。
index.js 就至關於單頁面的 man.js。index.vue 就至關於單頁面中的內容組件了。
這裏引入:
也就是一個頁面一個 Vue 實例,這目錄中的三個文件名字最好一致,打包後就是一個頁面。
index.html 中能夠把老項目中的 JS、CSS 所有在這引入。內容部分的就直接複製到 index.vue 中,有公用的部分,頭部底部什麼的就組件放在 components 中在 index.vue 中調用就好了。
2. 注意
除了 Vue 路由沒法使用以外,其餘都是可使用的。包括 Vuex,用法跟單頁面的同樣。只是每一個入口 JS 文件中要註冊一次罷了。
接下來就是頁面跳轉問題,跳轉直接用 a 標籤。
目錄下記得用絕對路徑。多頁面構建推薦用絕對路徑。因打包後目錄緣由,開發環境跟生產環境中的路由有差別。也就是開發環境須要加上 .html 後綴,生產環境則不須要。也就是兩種寫法。
<li><a href="/index.html/?orderNo=2"> index頁面,dev 的時候可用的寫法</a></li>
<li><a href="/index/?orderNo=2"> index頁面,production 可用的寫法</a></li>
就是在 dev 時候就等於這樣:
這也就產生了一個問題,本身的 dev 調試的時候是不須要後綴的,要上線的時候得把全部的 a 標籤跳轉連接的後綴加上,這就太麻煩了。
方法有蠻多,我這用的是判斷 production 來替換。
/** * 打包後路由修正 * @returns {string} */ export function urlRouter (){ let urlRouters = ".html"; if (process.env.NODE_ENV === "production") { // 爲線上環境修改配置... urlRouters="" } return urlRouters }
這樣就所有統一寫法了。反正就線上不須要後綴,在 dev 時候要後綴,你們自行想辦法解決這問題。不必定非用我這方法。
<a :href="`/index${configTool.urlRouter()}/?id=${id}`" >
先來看一下打包以後的 dist 目錄:
這兩個目錄是頁面目錄
裏面就是該頁面的資源文件。
生成環境訪問是這樣:
由於每一個目錄下都有該頁面的資源,一個目錄下都有 index.html。 須要在 dist 中加一個總入口中轉文件 index.html。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <script> window.location.href="/index/" </script> </body> </html>
到此,上線發佈就完成了。把 dist 目錄的文件丟到服務器就能夠了,推薦用的是放在根目錄,否則會找不到資源。官網也推薦多頁面應用的狀況下避免用相對路徑。
官網文檔中警告:
提升的方式有不少種,這裏推薦使用 webpack-parallel-uglify-plugin 插件。
默認狀況下 webpack 使用 UglifyJS 插件進行代碼壓縮,但因爲其採用單線程壓縮,速度很慢。
咱們能夠改用 webpack-parallel-uglify-plugin 插件,它能夠並行運行 UglifyJS 插件,從而更加充分、合理的使用 CPU 資源,從而大大減小構建時間。
1. 執行以下命令安裝 webpack-parallel-uglify-plugin:
npm i webpack-parallel-uglify-plugin
2. 打開 vue.config.js 文件,並做以下修改:
const ParallelUglifyPlugin = require('webpack-parallel-uglify-plugin'); //.... // webpack 提供的 UglifyJS 插件刪不刪都行,隨便,能夠並存 //new UglifyJsPlugin({ // uglifyOptions: { // compress: { // warnings: false // } // }, // sourceMap: config.build.productionSourceMap, // parallel: true //}), // 增長 webpack-parallel-uglify-plugin來替換 new ParallelUglifyPlugin({ cacheDir: '.cache/', uglifyJS:{ output: { comments: false }, compress: { warnings: false } } }),
3. 保存後再次構建項目,能夠感受到速度有所加快。
多頁面開發讓先後端分離更加變得更加方便,對已有項目進行分離,不須要作太多的修改;讓該項目再也不依靠後端去套,後期維護也方便。
對於前端來講,角色更加劇要,再也不會再出現,前端寫好頁面丟給後端,後端開發再嵌入項目中去,致使效果不同,後續有擴展加進去又致使樣式衝突;對於後端來講,也不須要作前端的事情。兩者分清,各司其職。
最後奉上本文測試的 Demo 哦,但願能幫到你們。有什麼疑問可評論喔!
http://download.lllomh.com/cliect/#/product/J417131589328672,獲取完整demo