Vue豆瓣系列文章

項目地址 在線演示

http://www.jianshu.com/p/3e4d06b9e0fd?utm_source=tuicool&utm_medium=referral

不識廬山真面目,只緣身在此山中。

大概一個月前,開源了Vue重構豆瓣移動端的項目,效果還可以,收到了很多小夥伴的反饋,話說是要寫一些文章的,但遲遲沒有動筆,估計小夥伴們等的花都謝了,拖延症是病,需要治療…

接下來開始填坑,這一系列的文章會把在開發中遇到的問題總結下來。這篇文章是第一篇,是一些準備工作,包括從idea到具體項目實現…

目錄結構

萬事開頭難,當有idea從你腦海中閃過的時候,你第一個想到的應該是:腳手架工具,快速生成基本的項目結構,即刻上手。Vue官方就提供了方便易用的Vue-cli命令行工具,快速,高效,爽!

這個項目本身是基於Vue-cli的webpack模板,簡單的輸入一條命令vue init webpack my-project,便能生成一個使用webpack作爲打包工具,具備熱重載,代碼檢查,css預處理等一系列功能的初始項目。

這一系列文章不會講webpack如何使用?有可能會涉及一些簡單的配置項。當然,學習webpack能很好的理解和解決開發中遇到的問題,有需要可自行谷歌。
不過,也不必擔心,正如上面所說,即使你不瞭解webpack,你仍然可以用Vue-cli快速開發出高逼格Vue應用。

來看看我們的目錄結構:

.
├── build                      // Webpack打包相關配置
├── config                     // 基本環境配置
├── index.html                 // 通用HTML模板
├── package.json               // 相關依賴
├── README.md                  // README
├── src                        // 源碼目錄
│   ├── App.vue                // 入口頁面
│   ├── assets                 // 靜態資源目錄
│   ├── components             // 全局公用組件目錄
│   │   ├── Banner.vue         // 廣告橫幅組件
│   │   ├── Card.vue           // 卡片組件
│   │   ├── DownloadApp.vue    // 底部app下載組件
│   │   ├── Group.vue          // 小組組件
│   │   ├── HeaderBar.vue      // 頂部導航組件
│   │   ├── List.vue           // 列表組件
│   │   ├── Rating.vue         // 星級評分組件
│   │   ├── Scroller.vue       // 橫向滾動組件
│   │   ├── Marking.vue        // 標記組件
│   │   ├── SubNav.vue         // 二級導航組件
│   │   ├── Tags.vue           // 標籤組件
│   │   ├── Types.vue          // 項目類型組件
│   │   └── UserBar.vue        // 用戶欄組件
│   ├── main.js                // 應用初始化入口文件
│   ├── router                 // 路由目錄
│   │   └── index.js           // 路由配置
│   ├── store                  // Vuex全局狀態目錄
│   │   ├── index.js           // Store根文件
│   │   └── modules            // 模塊目錄
│   │       ├── activities.js  // 活動相關狀態
│   │       ├── book.js        // 書籍相關狀態
│   │       ├── group.js       // 小組相關狀態
│   │       ├── movie.js       // 電影相關狀態
│   │       ├── search.js      // 搜索相關狀態
│   │       ├── subject.js     // 主題相關狀態
│   │       └── user.js        // 用戶相關狀態
│   └── views                  // 視圖目錄
│       ├── BookView.vue       // 書籍視圖
│       ├── DetailView.vue     // 首頁活動詳情視圖
│       ├── GroupView.vue      // 小組視圖
│       ├── HomeView.vue       // 主頁視圖
│       ├── LoginView.vue      // 登錄視圖
│       ├── MovieView.vue      // 電影視圖
│       ├── PagesView.vue      // 綜合視圖
│       ├── RegisterView.vue   // 註冊視圖
│       ├── SearchView.vue     // 搜索視圖
│       ├── StatusView.vue     // 廣播視圖
│       ├── SubjectView.vue    // 主題視圖
│       └── TalionView.vue     // 覆蓋層視圖
└── static                     // 靜態文件目錄

能開始了嗎?。客官,莫急!

首先,我們要對寫的項目有一個整體的印象,哪些東西是公用的?可以抽離爲組件的?哪些是一次性組件?這些組件之間是否擁有某種關係?通過怎樣的邏輯把他們關聯起來?哪些頁面是可重用的?又有哪些頁面需要同時顯示在視圖裏?路由要怎麼劃分?項目是否足夠的大?要不要統一狀態管理……

從上面結構中可以看出,我們把一些可重用的或者是一次性組件寫在了src/components中。
把完全不同或者是關聯性不大的頁面獨立爲不同的視圖放在src/views裏,這些包含了一個個子組件的視圖同樣可視爲組件。

上面結構中對各個目錄和文件做了簡單的描述,可能有些不太精確,建議結合代碼來看。
關於視圖方面,主要想說一下PagesView視圖和TalionView視圖。TalionView在這裏是作爲彈出層,覆蓋在其他頁面之上,擁有較高的在z-index,其內容包括了搜索框和一些快速導航。PagesView視圖,又對各個頁面做了一次組合,包括了<header-bar>頭部導航組件,路由出口<router-view>,還有一個TalionView視圖。


douban_TalionView.gif

爲什麼要這麼做呢?
綜合項目所有視圖來看,除了登錄註冊頁面外,其他頁面的佈局大致相同:頂部導航+中間內容視圖+覆蓋層。這在無形中通過視圖將此項目分成了兩大模塊:登錄註冊類無通用樣式模塊,和具備通用公共樣式的普通模塊。

當然這樣劃分的另一個基礎是對路由的配置。接下來,我們來看看路由,進行進一步解釋。

路由

先瞄一眼我們的路由配置:

routes: [
    {                           // [1]
      path: '/',
      redirect: '/pages/'
    },
    {                           // [2]
      path: '/pages',
      component: PagesView,
      children: [
        {
          path: '',
          redirect: '/pages/home'
        },
        {
          path: 'home',
          name: 'HomeView',
          component: HomeView
        },
        {
          path: 'movie',
          name: 'MovieView',
          component: MovieView
        },
        ......
        {
          path: 'detail/:id',
          name: 'DetailView',
          component: DetailView
        }
      ]
    },
    {                           // [3]
      path: '/pages/:classify/subject/:id',
      name: 'SubjectView',
      components: {
        default: PagesView,
        subject: SubjectView
      }
    },
    {                           // [4]
      path: '/search',
      name: 'SearchView',
      components: {
        default: PagesView,
        search: SearchView
      }
    },
    {                           // [5]
      path: '/login',
      name: 'LoginView',
      component: LoginView
    },
    {                           // [6]
      path: '/register',
      name: 'RegisterView',
      component: RegisterView
    },
    {                           // [7]
      path: '*',
      redirect: '/pages/'
    }
  ]

路由的匹配規則是自上而下的,第一條[1]規則對路由進行了重定向。接着,第二條[2],對應了組件PagesView,其中又包含了若干條子路由。這相當於上面描述的第一類模塊,擁有統一的視圖,子路由的路由出口爲本組件PagesView中定義的<router-view>。而這一個聚合的擁有子路由和子視圖的模塊對應的路由出口爲入口文件App.vue中定義的無名路由出口<router-view class="view"></router-view>

可以看到在App.vue中還有兩個具名的路由出口:name="subject"name="search"subject出口對應的路由爲[3],是電影,圖書等的詳情頁面,是不是覺得可以把它劃分到[2]中的子路由中?完全可以,但這裏沒有這麼做,爲了將視圖和路由對應起來,擁有一個好看直觀的路徑,所以就抽了出來,單獨配置了,是不是有點蛇精病…回正題,爲了同時(同級)展示多個視圖,而不是嵌套展示,我們配置了components,將PagesViewSubjectView同時展示在視圖中。search路由也做了同樣的處理。

[5][6]爲登錄註冊路由,只在視圖中展示了各自對應的單個組件,不再受約束。自由發揮…

最後一條路由[7],相當於404頁面,在以上所有路由都不匹配的情況下,重定向到404頁面,這裏又把它重定向到了主頁,是不是很機智…

狀態管理

數據驅動應用,當我們的應用越來越大時,對數據的操控將會越來越複雜。那麼,我們到底需要不需要集中式的狀態管理(Vuex)?

Flux 架構就像眼鏡:您自會知道什麼時候需要它。 –Dan Abramov

對於組件來說,如果,數據本身高度隸屬於本組件,那麼,直接封裝在組件裏將是一個好的選擇。當父子組件之間共享數據,我們也能通過props和自定義事件解決。即使組件越來越多,視圖越來越多,如果彼此之間並無太大聯繫,我們也沒有必要使用Vuex

稍微吐槽一下本項目:除了用戶模塊和搜索模塊的狀態之外,其他的狀態是沒有必要一定使用Vuex的。電影數據,圖書數據,小組數據等是一次性數據,什麼時候訪問就什麼時候呈現,頂多會通過路由傳遞一些必要的參數。即是說,其他視圖不依賴這些數據,它本身也不需要來自其他視圖的狀態,並且這些狀態並不影響全局狀態。這個時候你不必使用Vuex。但你這裏使用了,作何解釋呢?的確,Vuex很酷,我想試試。沒別的理由。我能清楚明瞭的查看每一個組件的狀態,掌控全局,這的確很酷。


douban_vuex_store.gif

吐槽完了!回正題。現在假設我們的項目足夠大了(接着更新說不定就大了哦-_-!)。組件,視圖多的數不過來,並且他們之間的關係錯綜複雜。整個應用也需要記錄和維護更多的複雜的全局狀態。現在我們有了Vuex,我們把共享狀態抽取出來,以一個全局單例模式管理。我們的應用構成一張巨大的蜘蛛網,而不論網的哪一個節點都能訪問狀態。描述爲星型拓撲結構更容易理解一些吧。

爲了防止單一的狀態集中到一個文件中而變得冗餘,難以維護,我們進一步的將狀態分割成了模塊,這些模塊和視圖一一對應,這樣代碼就變得更結構化,且非常容易維護了。

│   ├── store                  // Vuex全局狀態目錄
│   │   ├── index.js           // Store根文件
│   │   └── modules            // 模塊目錄
│   │       ├── activities.js  // 活動相關狀態
│   │       ├── book.js        // 書籍相關狀態
│   │       ├── group.js       // 小組相關狀態
│   │       ├── movie.js       // 電影相關狀態
│   │       ├── search.js      // 搜索相關狀態
│   │       ├── subject.js     // 主題相關狀態
│   │       └── user.js        // 用戶相關狀態

不要爲了用vuex而用vuex

如果應用確實不大,只是有一些多層嵌套組件,兄弟組件之間傳遞狀態的需求,使用事件總線global event bus就ok啦!如果需要構建一箇中大型應用,爲了更好地管理狀態,上Vuex。至於,不要爲了用vuex而用vuex,只要你覺得用着爽,拿來用又何妨!

HTTP請求

Ajax庫的選擇是比較自由的。起初,項目使用的是vue-resource,其提供的jsonp方法用着比較爽,由於項目的很多數據來自豆瓣的api,直接上jsonp簡單方便,跨域什麼的不考慮。你很機智嘛!vue-resource不支持服務端渲染,官方也不再推薦vue-resource。但這個不影響它的使用,如果你喜歡,接着用也沒有問題。這裏我將vue-resource的版本released出來:The last version of vue-resource

花了一點時間,遷移到了Superagent,準備做一些有趣的事情。和axios基本相同,很明顯axios更火,axios的文章也更多一點,有的選未必不好。Superagent是在寫express時接觸的,前後端通用,作者殺馬特TJ,用了一段時間,還不錯。現在拿來和vue一塊用,更親切。
Superagent的api簡單易用,能滿足大多數應用需求,輕量級,支持ssr,可擴展性強。Superagent和axios的核心都不支持jsonp,這裏採用了Superagent的插件superagent-jsonp

我們可以這麼使用:

import request from 'superagent'
import jsonp from 'superagent-jsonp'

request
    .get('https://api.douban.com/v2/event/list?loc=108288&start=' +
       state.skip + '&count=3')
    .use(jsonp)
    .end((err, res) => {
        if (!err) {
          commit({
            type: 'loadMore',
            res: res.body.events
          })
        }
    })

有關於Superagent的更多問題可以閱讀官方文檔或參考本項目源碼。

表單驗證

登錄註冊部分只簡單的在前後端做了基本檢驗,更嚴格的表單驗證,沒有做。社區裏表單驗證的插件也很多,vee-validate比較好用,就不多說了,如果有需要可以仔細閱讀文檔。這裏先挖個坑,我想再造個輪子,試一試…
我暫時總結了一些關於表單驗證的關鍵詞,算是佔個坑吧!

冗餘,遠程,時機,分離,集中式管理,重用,可配置,順序,自定義,字段消毒,隨機補充規則,優雅

我去!要求很多嘛…

API

數據來源,分兩個部分,一個是豆瓣的api:Douban Api V2,另一個是mock的一些數據:Douban Backend。採用express手寫完成。跨域問題,上面已經介紹,在不能操控的豆瓣數據上,使用的是jsonp。部署在heroku上的mock數據服務,完全可控,採用cors,藉助cors模塊完成。當然,方法很多,你的地盤,你做主。

發佈到服務器

在構建成功之後,我們將dist/中的文件發佈到我們已經準備好的服務器上。
在發佈中會遇到兩個常見的問題:

第一:npm run build後,項目部署到服務器,訪問頁面空白?
由於項目是用vue-cli構建,我們並沒有對項目做任何配置,儘管默認配置下它工作的很好。看控制檯,報錯,一連串的404,文件找不到。js沒加載上,vue沒有初始化,頁面自然空白。
解決方法:把congif/目錄下index.jsbuildassetsPublicPath改爲 ./,然後重新npm run build。

第二:assetsPublicPath配置正確,頁面依舊空白?
查看路由配置信息:mode: 'history'啓用了History模式。使用 history 模式時,URL 就像正常的 url,不過這種模式要玩好,還需要後臺配置支持,vue-router文檔給出了不同服務器的簡單配置:HTML5 History 模式

發佈到github pages上:
github pages用來展示項目再好不過了。不過,我們沒辦法做服務器配置,HTML5 History 模式要捨棄了,頁面的滾動行爲的體驗差點也沒關係,關於這兩點有後續文章。
npm run build後通過下面Git命令發佈:

git add -f dist
git commit -am 'Update live demo'
git subtree push --prefix dist origin gh-pages

最後

這是第一篇,比較雜亂一些,寫了這麼長,希望對你有所幫助。如果你在查看這個項目,或閱讀本文章時有什麼問題可以直接聯繫我或到項目地址提交英文issue,歡迎各位小夥伴前來斧正,不勝感激……項目,系列文章將會持續更新,拖延症也在治療中…

本文首發於簡書 
作者:jeneser
Github:https://github.com/jeneser
版權聲明:自由轉載-非商用-非衍生-保持署名(創意共享3.0許可證

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章