Vue全家桶開發(fā)的CNode社區(qū)單頁web應用

為什么要寫這篇文章呢?其目的標題寫的也很明了,就是為了記錄一下我的學習過程。在以后回顧此項目時,也可以更方便地發(fā)現此項目中的不足和精華。在此,感謝VNode社區(qū)提供的API。

源碼在此Vue-CNode
預覽地址使勁點我
你也可以掃描下面的二維碼預覽線上項目:

一、需求分析

要做一個項目之前,我覺得首先要把功能做一個總結,根據需求來寫項目,從而做到有的放矢。

所以我根據API寫了項目的需求,可見下圖:

CNode功能需求分析
CNode功能需求分析

二、技術棧

本項目使用的技術棧就是標準的Vue全家桶,即:

Vue2.0: 構建項目,屬于底層框架。
Vue-Router: 通過hash值的變化,從而改變頁面結構的路由。
Vuex: Vue官方提供的狀態(tài)管理模式。
Axios, Vue-Axios: http請求模塊。
ES6: 應用于生產環(huán)境,普及度較高的新Javascript語法。
Sass: CSS預編譯器。
Webpack: 用于打包項目。

三、項目初始化

利用Vue-cli提供的初始化工具,運行以下代碼:

# install vue-cli
$ npm install --global vue-cli
# create a new project using the "webpack" template
$ vue init webpack my-project
# install dependencies and go!
$ cd my-project
$ npm install
$ npm run dev

此時打開http://localhost:8080/就可以訪問初始化后的頁面了。

四、項目編寫

注意:詳細內容可以去源碼自行查看。

完成初始化之后呢,我們就可以開始編寫項目了。
代碼分為四塊,分別是:components(組件)、vue-router(路由)、vuex(狀態(tài)管理模式)common(放置公共樣式,字體和通用的功能代碼)。
在項目編寫之前,受限于要安裝依賴,代碼如下:

# 安裝vuex,vue-router,axios,vue-axios
$ npm install vuex vue-router axios vue-axios --save
// 安裝sass依賴
# npm install node-sass sass-loader --save-dev

1.common公用文件

包括樣式(style),字體(fonts)還有工具函數(utils, 包括時間格式化還有cookie存取功能)。

2.Components組件

現在暫時一共有14個組件,包括:

AboutMe
Article
ArticleCard
BackBar
BottomBar
Content
Loading
Login
MessageCard
MyCollect
navBar
Notification
Publish
UserDetail

具體內容可以參見后面的項目目錄。

3. Vue-Router 路由配置

通過路由,分為一下七個頁面:
① 主頁
② 文章詳情頁
③ 用戶詳情頁
④ 用戶登錄頁
⑤ 發(fā)布文章頁
⑥ 用戶收藏頁
⑦ 我的通知頁

5. Vuex:狀態(tài)管理模式

狀態(tài)管理分為六個模塊:content(主頁)、article(文章頁)、navbar(導航欄)、user(用戶詳情狀態(tài))、login(用戶登錄狀態(tài))和notification(通知)。

五、項目目錄

.
├── build                               // webpack設置
│   ├── build.js
│   ├── check-versions.js
│   ├── dev-client.js
│   ├── dev-server.js
│   ├── utils.js
│   ├── vue-loader.conf.js
│   ├── webpack.base.conf.js
│   ├── webpack.dev.conf.js
│   └── webpack.prod.conf.js
├── config                              // 項目開發(fā)和打包設置
│   ├── dev.env.js
│   ├── index.js
│   └── prod.env.js
├── docs                                // 靜態(tài)資源地址
│   ├── index.html
│   └── static
│       ├── css
│       │   └── app.d99bca81a0eef77c7e0d8c70f520707c.css
│       ├── fonts
│       │   ├── iconfont.8553d3c.ttf
│       │   └── iconfont.b29ac85.eot
│       ├── img
│       │   └── iconfont.d4553f2.svg
│       └── js
│           ├── app.cb09e437ae0bec6205b9.js
│           ├── manifest.aa9548ef140031379c30.js
│           └── vendor.f3d0844a66c0c2cabe0b.js
├── src                                 // 項目文件位置
│   ├── App.vue                         // 組件總入口
│   ├── common                          // 通用文件
│   │   ├── fonts                       // 字體
│   │   │   ├── iconfont.eot
│   │   │   ├── iconfont.svg
│   │   │   ├── iconfont.ttf
│   │   │   └── iconfont.woff
│   │   ├── style                       // 樣式
│   │   │   ├── animation.scss          // 動畫
│   │   │   ├── base.scss               // 基本樣式
│   │   │   └── icon.scss               // iconfont的字體圖標樣式
│   │   └── utils                       // 工具函數
│   │       ├── cookie.js               // cookie存取和刪除
│   │       └── timeFormat.js           // 格式化時間函數
│   ├── components                      // 所有組件
│   │   ├── AboutMe                     // 關于
│   │   │   └── AboutMe.vue
│   │   ├── Article                     // 文章詳情頁
│   │   │   └── Article.vue
│   │   ├── ArticleCard                 // 文章列表的單個文章卡片
│   │   │   └── ArticleCard.vue
│   │   ├── BackBar                     // 頂部的返回欄(返回主頁和后退)
│   │   │   └── BackBar.vue
│   │   ├── BottomBar                   // 底部的回復欄(還包含收藏和編輯文件)
│   │   │   └── BottomBar.vue
│   │   ├── Content                     // 主頁
│   │   │   └── Content.vue
│   │   ├── Loading                     // 正在加載組件
│   │   │   ├── Loading.vue
│   │   │   └── loading.svg
│   │   ├── Login                       // 登錄
│   │   │   └── Login.vue
│   │   ├── MessageCard                 // 單個通知的詳情卡片
│   │   │   └── MessageCard.vue
│   │   ├── MyCollect                   // 我的收藏頁
│   │   │   └── MyCollect.vue
│   │   ├── Notification                // 通知頁
│   │   │   └── Notification.vue
│   │   ├── Publish                     // 發(fā)布文章和發(fā)布更新頁
│   │   │   └── Publish.vue
│   │   ├── UserDetail                  // 用戶詳情頁
│   │   │   └── UserDetail.vue
│   │   └── navBar                      // 主頁的頂部導航欄
│   │       ├── cnodejs_light.svg
│   │       └── navBar.vue
│   ├── main.js                         // 項目的總入口
│   ├── pic                             // 和代碼無關,README.md中的圖片
│   │   ├── CNode?\212\237?\203??\234\200?\202?\210\206?\236\220.png
│   │   └── QR-Code.png
│   ├── router                          // 路由設置
│   │   └── index.js
│   └── store                           // 狀態(tài)管理
│       ├── modules
│       │   ├── article                 // 文章詳情頁
│       │   │   ├── article-mutation-types.js
│       │   │   └── article.js
│       │   ├── content                 // 主頁
│       │   │   ├── content-mutation-types.js
│       │   │   └── content.js
│       │   ├── login                   // 登錄頁
│       │   │   ├── login-mutation-types.js
│       │   │   └── login.js
│       │   ├── navbar                  // 主頁導航欄
│       │   │   ├── navbar-mutation-types.js
│       │   │   └── navbar.js
│       │   ├── notification            // 通知頁
│       │   │   ├── notification-mutation-types.js
│       │   │   └── notification.js
│       │   └── user                    // 用戶詳情頁
│       │       ├── user-mutation-types.js
│       │       └── user.js
│       └── store.js                    // 狀態(tài)管理總入口
├── README.md
├── index.html
└── package.json

六、過程中遇到的問題

本項目算是本人第一個完整的手機和pc都兼容,有關于文章展示的項目。整個項目做下來,遇到的Bug很多,自然收獲也是很多。總結下來如下:

1.很長的單詞會超出邊界,導致可視區(qū)域變寬。

解決辦法:通過word-wrap: break-word;實現打斷效果。

2.第二次進入文章時,會殘留(暫未解決)。

解決辦法:通過路由的鉤子函數beforeRouteEnter,來獲取數據,未成功獲取數據時,顯示Loading頁面,加載完成后,顯示文章詳情頁,從而解決這個問題。

3.回到首頁時,不能保留原來的狀態(tài)(暫未解決)。

解決辦法:

①此方法為容易固定高度的解決辦法。(具體方法:用vuex和vue-router的鉤子函數來解決這個問題,即通過scroll事件動態(tài)保存此時的scrollTop直,當路由的beforeRouteEnter出發(fā)時,恢復其scrollTop的值。)

② 如果沒有固定高度,直接通過Vue自帶的keep-alive組件,保留組件狀態(tài)。

4.載入中的動畫效果如何做?

解決辦法:之前是通過CSS3繪制一個圖形,但是后來發(fā)現太丑了,就直接用了Iconfont上的svg圖,并添加了動畫效果。

5.如何實現主頁文章列表的懶加載?

解決辦法:判斷滑動的總高度 - 滑動距離頂部的距離 <= 屏幕的可用高度,也就是以下公式:

document.documentElement.offsetHeight - window.scrollY
<= window.screen.height

這里會出現一個bug,滿足條件時,繼續(xù)滑動,會加載多次。在此可以加入一個狀態(tài),表示此時正在加載(詳細參見源代碼),從而解決此bug。

6.回到頂部的動畫怎么做?

解決辦法:可以把現在的window.scrollY分成n份,然后再設置一個定時器,每隔m秒,向上滾動一份的高度,當window.scrollY >= 0時,再終止定時器。(其中的m, n為任意數,根據情況設定)

7.如何控制正在加載頁面的顯示?

解決辦法:因為加載數據是異步的,可以在加載之前和加載之后,分別更改一個類似于isLoading(名稱自己設定)的狀態(tài),從而控制加載頁面的顯示。

8.如何設置登錄功能?

解決辦法:因為官方只提供了access-token,所以可以將此值和一些用戶相關的數值,存入document.cookie中,存入的函數我單獨寫了一個cookie的工具函數,代碼如下:

/**
* Created by jerryshen on 2017/7/15.
* 用戶本地cookie的存取以及清空
* 函數的功能分別是:
* 設置單個,獲取所有,獲取單個,刪除所有,刪除單個
*/

export function setCookie (name, value, exdays = 30) {
 var time = new Date()
 time.setTime(time.getTime() + exdays * 24 * 3600 * 1000)
 var expires = 'expires=' + time.toGMTString()
 document.cookie = name + '=' + value + ';' + expires
}

export function getAllCookies () {
 if (document.cookie === '') {
   return {}
 }
 const cookies = document.cookie.split(';')
 const newCookies = {}
 for (let i = 0; i < cookies.length; i++) {
   let cookie = cookies[i].trim()
   const splitCookie = cookie.split('=')
   newCookies[splitCookie[0]] = splitCookie[1]
 }
 return newCookies
}

export function getCookie (name) {
 const cname = name + '='
 const cookies = document.cookie.split(';')
 for (let i = 0; i < cookies.length; i++) {
   let cookie = cookies[i].trim()
   if (cookie.indexOf(cname) === 0) {
     return {
       success: true,
       cookie: {
         name,
         value: cookie.split(cname)[1]
       }
     }
   } else {
     return {
       success: false,
       cookie: {
         name,
         value: undefined
       }
     }
   }
 }
}

export function deleteAllCookie () {
 document.cookie += ';expires=Thu, 01 Jan 1970 00:00:00 GMT'
}

export function deleteCookie (name) {
 document.cookie = `${name}=;expires=Thu, 01 Jan 1970 00:00:00 GMT`
}

9.如何將API中的時間轉換成 => ..年前,..月前,..天前等等,這種類型的格式呢?

解決辦法:我自己寫了一個格式化的工具函數,代碼如下:

export default function timeFormat (date) {
  // 獲取當前時間和所傳時間的Date對象
  const nowTime = new Date()
  const inDate = new Date(date)
  if (nowTime.getYear() - inDate.getYear() > 0) {
    // 年份差值 > 0,返回年
    return `${nowTime.getFullYear() - inDate.getFullYear()}年前`
  } else if (nowTime.getMonth() - inDate.getMonth() > 0) {
    // 月份差值 > 0,返回月
    return `${nowTime.getMonth() - inDate.getMonth()}個月前`
  } else if (nowTime.getDate() - inDate.getDate() > 0) {
    // 日期差值 > 0,返回日
    return `${nowTime.getDate() - inDate.getDate()}天前`
  } else if (nowTime.getHours() - inDate.getHours() > 0) {
    // 小時差值 > 0,返回時
    return `${nowTime.getHours() - inDate.getHours()}個小時前`
  } else if (nowTime.getMinutes() - inDate.getMinutes() > 0) {
    // 分鐘差值 > 0,返回分鐘
    return `${nowTime.getMinutes() - inDate.getMinutes()}分鐘前`
  } else {
    // 其他情況,也就是秒數差值 > 0,返回秒鐘
    return `${nowTime.getSeconds() - inDate.getSeconds()}秒前`
  }
}

10.BUG:當進入其他路由時,仍然會觸發(fā)主頁的scroll事件。

解決辦法:之前生命周期鉤子用的是mounted,因此進入其他路由時,scroll事件仍然存在。所以現在改用beforeRouteEnterbeforeRouteLeave這兩個路由的生命周期鉤子,分別實現載入路由時的scroll事件掛載、離開路由時的scroll事件卸載。從而防止主頁內容的懶加載一直觸發(fā)。

11.發(fā)布新文章或更新跳轉至文章詳情頁面后,再按后退,怎么實現回到主頁?

解決辦法:現在初步是使用,路由跳轉的時候,先跳到主頁,再跳到文章詳情頁,再按后退時,就會回到主頁。

12.如何實現點擊評論右側的回復按鈕,添加@信息,并focus輸入框?

解決辦法:通過vuex來實時記錄回復相關的信息,并通過watch輸入框的value來判斷是否focus。

13.有一個很奇怪的bug:ios下,如果在文章詳情頁返回主頁時,此時的window.scrollY會保持文章詳情頁時的window.scrollY,如果此值滿足異步加載更多數據的條件時,會導致異常加載數據。

解決辦法:不得已,只好在beforeRouteEnter鉤子中,綁定滾動事件的函數加一個定時器,使其在100ms后綁定事件,所以此時的window.scrollY就會變成之前的值。

七、后記

本人新手一枚,還在苦逼的找工作中T_T。如過您在代碼中發(fā)現了bug,可以通過評論和我交流,互相學習!有什么好的想法,也可以提出來,一起討論。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

  • 轉載 :OpenDiggawesome-github-vue 是由OpenDigg整理并維護的Vue相關開源項目庫...
    果汁密碼閱讀 23,406評論 8 124
  • 來源:github.com Vue.js開源項目速查表:https://www.ctolib.com/cheats...
    zhangtaiwei閱讀 11,926評論 1 159
  • 再見成都,不是別離的再見,而是再次相見的再見,成都,我學習生活了四年的城市,時隔半年,我又回到你的懷抱,你的懷抱是...
    無痛不青春閱讀 1,223評論 0 3
  • 誰不想一直保持一副20歲的面容?尤其是女性,臉蛋是女人最大的門面,不管走到哪里,漂亮的女人永遠是最受關注的。保持年...
    小蕾拉閱讀 645評論 0 0
  • 這張照片2015年正月十五拍攝于東莞。朋友問我怎樣拍的?喜歡攝影的人一定嘗試一下拍煙花這一課。現在簡單幾句分享一下...
    深圳男人閱讀 291評論 0 2

友情鏈接更多精彩內容