Vue或React的多頁應(yīng)用腳手架
前言
一直以來都在研究多頁應(yīng)用如何能有一套像SPA一樣優(yōu)雅的開發(fā)模式
本套架構(gòu)在項(xiàng)目上使用感覺還不錯(cuò)(已跑在上百個(gè)頁面的項(xiàng)目上),所以決定開源出來給大家
閱讀完本文能實(shí)現(xiàn)在項(xiàng)目中使用ES6(7)+組件化(.vue | .jsx)開發(fā)多頁應(yīng)用
(其實(shí)我是想把它做為大家多頁應(yīng)用的腳手架)
目錄結(jié)構(gòu)介紹
TIPS:任何的項(xiàng)目的架構(gòu)都和目錄結(jié)構(gòu)有關(guān),所以這部分非常重要,請仔細(xì)耐心閱讀
我們先宏觀的看下結(jié)構(gòu)
- |--- public // 生產(chǎn)環(huán)境下所需的文件
 - |--- static
 - |--- css
 - |--- es6
 - |--- fonts
 - |--- images
 - |--- views
 - |--- src
 - |--- assets
 - |--- fonts
 - |--- images
 - |--- components
 - |--- js
 - |--- sass
 - |--- static
 - |--- css
 - |--- es6
 - |--- fonts
 - |--- images
 - |--- views
 
src里的assets,components,js,sass里的文件***都會生成到src/static下,這個(gè)作為我們dev中引用的資源文件。而public不用說,是線上訪問的文件。
我們展開介紹下具體的頁面應(yīng)該如何對應(yīng)它的資源。拿js和views為例
- |--- views
 - |--- home // 官網(wǎng)介紹 業(yè)務(wù)模塊
 - |--- index.html
 - ...
 - |--- shopping // 購物業(yè)務(wù)模塊
 - |--- buy.html
 - ...
 - |--- js
 - |--- lib
 - |--- vue.js
 - |--- react.js
 - |--- react.dom.js
 - ...
 - |--- home // 官網(wǎng)介紹業(yè)務(wù)模塊的js
 - |--- index.js
 - ...
 - |--- shopping // 購物業(yè)務(wù)模塊的js
 - |--- buy.js
 - ...
 - tools.js
 - common.js
 
在多頁應(yīng)用中,往往我們的頁面以業(yè)務(wù)模塊劃分,業(yè)務(wù)模塊由許多的頁面組成。
如home,shopping,可能就分別為官網(wǎng)介紹和購物的業(yè)務(wù)模塊。在這業(yè)務(wù)模塊下,分別有許多個(gè)頁面,那我們的js文件也需要命名一一對應(yīng)。
當(dāng)然,我們還有第三方的js庫是不需要編譯的,所以我們專門用一個(gè)lib文件夾來存放他們。(包括你自己編寫的指令或者filter等,不需要編譯的,也直接放在lib下引入即可)
另外,你還有許多自己寫的需要編譯的工具庫直接放在js目錄下即可(如,tools.js,common.js)
我們的sass也是同理
- |--- sass
 - |--- home
 - |--- index.scss
 - ...
 - |--- shopping
 - |--- buy.scss
 - ...
 
他們編譯在static下的文件將為
- |--- static
 - |--- css // scss 編譯后的
 - |--- home
 - |--- index.css
 - |--- shopping
 - |--- buy.css
 - |--- js // babel處理后的js
 - |--- home
 - |--- index.js
 - |--- shopping
 - |--- buy.js
 
頁面引用的路徑就為(home/index.html為例)
- ...
 - <link rel="stylesheet" href="../../static/css/home/index.css">
 - ...
 - <script src="../../static/es6/lib/vue(react).js"></script>
 - <script src="../../static/es6/lib/react.dom.js"></script>
 - <script src="../../static/es6/home/index.js"></script>
 - ...
 
js和sass搞定了后,我們的難點(diǎn)是編寫組件的過程中,如何知道應(yīng)該編譯哪個(gè)入口js文件呢?
所以我們需要對我們的組件名進(jìn)行一些約定,這也就是約定大于配置的前提。
- |--- components
 - |--- home // home 業(yè)務(wù)模塊
 - |--- home-header.vue(jsx)
 - |--- index-info.vue(jsx)
 - ...
 - |--- shopping // shopping 業(yè)務(wù)模塊
 - |--- buy-list.vue(jsx)
 - ...
 
我們components下的業(yè)務(wù)模塊名和之前的sass,js一樣。具體組件那就有所不同。
我們分為幾種類型的組件
一、當(dāng)前頁面使用的組件
二、當(dāng)前業(yè)務(wù)模塊下的公用組件
三、所有業(yè)務(wù)模塊的通用組件
當(dāng)前頁面組件的命名,我們約定為 [頁面]-[組件].vue(jsx)
如下
- |--- components
 - |--- home
 - |--- index-info.vue(jsx)
 
這個(gè) index-info 的組件就僅僅只有在home/index.html頁面下使用,當(dāng)你修改了這個(gè)組件后,會自動(dòng)編譯home/index.js路口js文件并刷新頁面。
當(dāng)前業(yè)務(wù)模塊下的公用組件,我們約定為 [業(yè)務(wù)模塊]-[組件].vue(jsx)
如下
- |--- components
 - |--- common
 - |--- loading.vue(jsx)
 
這個(gè)home-header組件就屬于home業(yè)務(wù)模塊下的公用組件,當(dāng)你修改了這個(gè)組件后,會自動(dòng)編譯home業(yè)務(wù)模塊下所有的js文件并刷新頁面。
剩下的就是所有業(yè)務(wù)模塊下的通用組件,我們約定全放在components/common目錄下,不需要具體命名約定
- |--- components
 - |--- common
 - |--- loading.vue(jsx)
 
這個(gè)loading組件就屬于所有業(yè)務(wù)模塊下的公用組件,當(dāng)你修改了這個(gè)組件后,會自動(dòng)編譯所有業(yè)務(wù)模塊下的js文件并刷新頁面。
編譯組件的原理以及為什么約定命名的原因是:
我會根據(jù)組件更改變動(dòng),去讀取文件夾名,組件名,并編譯對應(yīng)名的路口js
至此,我們就把組件的問題也解決了
由于我采用的是主gulp輔webpack,webpack僅僅只編譯用,所以編譯基本達(dá)到秒編譯。比單純利用webpack做構(gòu)建快得多。如果單純采用webpack做構(gòu)建,需要去配置entry,配置HTMLPlugin。所以會慢得多,然而我這一套并不需要如此繁瑣。
圖片&&字體文件
這其實(shí)是一個(gè)大坑
我們的實(shí)現(xiàn)目標(biāo)是組件能相對路徑引入圖片或字體文件
- // 如 在html標(biāo)簽里這樣
 - <template>
 - <figure>
 - <img src="../../assets/images/home/logo.jpg" alt="頭像">
 - </figure>
 - </template>
 - // 在style里這樣
 - <style rel="stylesheet/scss" lang="sass">
 - @import "../../sass/home/index-info";
 - // 甚至可能在這@import面引入相對路徑,這都會算是在組件里引入相對路徑
 - #bg h3 {
 - background: url("../../assets/images/holmes.jpg");
 - color: #fff;
 - }
 - </style>
 
這個(gè)坑,真是不可描述,我個(gè)人嘗試了各種體位,才把這個(gè)坑配置好。
直接給大家看***實(shí)現(xiàn)是怎樣的。
dev 的路徑是這樣,頁面可以顯示圖片或字體。
build 后的路徑是這樣
這樣就達(dá)到了開發(fā)和發(fā)布后的資源統(tǒng)一,摸索這一步真是挺累的 T.T,有興趣的自己看源碼吧。
環(huán)境變量的配置
我們在webpack中經(jīng)常會遇見不同環(huán)境下不同配置的問題
首先可在package.json里配置一條script
- // package.json
 - "scripts": {
 - "build": "NODE_ENV=production gulp build",
 - "dev": "NODE_ENV=dev gulp reload"
 - },
 
假設(shè)我們需要為不同環(huán)境配置不同的api請求地址,就可以利用我們在package.json設(shè)置的NODE_ENV來識別當(dāng)前環(huán)境(這部分我在gulpfile中處理了,所以在文件里可直接識別NODE_ENV,如下)
- // src/js/ajaxurl.js
 - const server1 = 'https://production.server.com';
 - const server2 = 'https://dev.server.com';
 - let useServer = null;
 - if(NODE_ENV === 'production') {
 - useServer = server1;
 - } else if(NODE_ENV === 'dev') {
 - useServer = server2;
 - }
 - export default useServer;
 - // src/js/home/index.js
 - import url from '../ajaxurl';
 - console.log(url);
 
這樣就解決了我們不同環(huán)境下不同配置的問題,我默認(rèn)配置了dev和production,大家可以自行拓展。比如
假設(shè)你需要在 開發(fā)中 配置測試,你可以寫一條NODE_ENV=test gulp reload。
如果需要 預(yù)發(fā)布打包 測試,就可以另一條NODE_ENV=preproduction gulp build。
總之就是打包使用gulp build,開發(fā)使用gulp reload。
注意事項(xiàng)
開發(fā):執(zhí)行命令 npm run dev
發(fā)布:執(zhí)行命令 npm run build (BTW,別忘了去gulpfile.js里替換你的CDN鏈接,進(jìn)入gulp文件修改 const CDN = 'yourCDNLink'這里的變量即可)
命名一定要按約定來!
命名一定要按約定來!
命名一定要按約定來!
否則不知道要編譯誰!!!
gulp配置很簡單,大家可以看一下針對各自項(xiàng)目進(jìn)行修改,不懂得可以直接問我。
如果你們不完全的前后端分離,把這個(gè)src直接放在后臺目錄下也沒有問題。
寫vue和react都沒問題,我把示例demo都寫好了,下面是分別兩個(gè)的repo地址。
vue-multpage : https://github.com/MeCKodo/vue-multipage
react-multpage : https://github.com/MeCKodo/react-multipage
TODO
- [ ] 項(xiàng)目的Unit test
 - [ ] 項(xiàng)目Cli腳手架
 
后話
本來是想寫成vue-cli或者是create-react-app這種cli腳手架的,但是!本人真是太懶又沒有時(shí)間了! 各位看官可以先嘗試clone把玩把玩,如果有足夠多人喜歡,我就把他寫成cli,發(fā)布npm :)
我是用mac下開發(fā)完成的,用了半天多時(shí)間專門去給window寫了兼容,window還可能會有bug,不是我說!window就是辣雞!
***給大家看下我們的某項(xiàng)目結(jié)構(gòu)。
總覽
js部分
sass部分
組件和頁面





















 
 
 









 
 
 
 