寫這篇文章就是爲了記錄一下個人學習過程。在之後回顧此項目時,也能夠更方便地發現此項目中的不足和精華。在此,感謝VNode社區提供的API。css
源碼在此:Vue-CNode
預覽地址:使勁點我
你也能夠掃描下面的二維碼預覽線上項目:html
要作一個項目以前,我以爲首先要把功能作一個總結,根據需求來寫項目,從而作到有的放矢。vue
因此我根據API寫了項目的需求,可見下圖:node
本項目使用的技術棧就是標準的Vue全家桶
,即:webpack
Vue2.0: 構建項目,屬於底層框架。 Vue-Router: 經過hash值的變化,從而改變頁面結構的路由。 Vuex: Vue官方提供的狀態管理模式。 Axios, Vue-Axios: http請求模塊。 ES6: 新的Javascript語法。 Sass: CSS預編譯器。 Webpack: 用於打包項目。
[x] 首頁列表ios
[x] 無限懶加載文章列表git
[x] 切換內容主題github
[x] 文章詳情web
[x] 在文章詳情頁時,能夠後退至主頁vue-router
[x] 回到頂部功能,並添加動畫效果
[x] 關於
[x] 用戶登陸
[x] 用戶退出
[x] 我的主頁
[x] 個人收藏
[x] 點擊用戶頭像,能夠進入該用戶的簡介頁面
[x] 登錄後,可在文章詳情頁點贊和評論
[x] 登錄後,在主頁顯示發佈主題按鈕,能夠發佈主題
[x] 消息通知,消息設置已讀功能
[x] 對本身的文章能夠進行編輯更新
[x] 操做成功或失敗後的消息提醒
[ ] 增長markdown的編輯器組件和預覽器組件
[ ] 評論排序功能
利用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(狀態管理模式)和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
包括樣式(style),字體(fonts)還有工具函數(utils, 包括時間格式化還有cookie存取功能)。
如今暫時一共有14個組件,包括:
AboutMe Article ArticleCard BackBar BottomBar Content Loading Login MessageCard MyCollect navBar Notification Publish UserDetail
具體內容能夠參見後面的項目目錄。
經過路由,分爲一下七個頁面:
① 主頁
② 文章詳情頁
③ 用戶詳情頁
④ 用戶登陸頁
⑤ 發佈文章頁
⑥ 用戶收藏頁
⑦ 個人通知頁
狀態管理分爲六個模塊:content
(主頁)、article
(文章頁)、navbar
(導航欄)、user
(用戶詳情狀態)、login
(用戶登陸狀態)和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 // 項目開發和打包設置 │ ├── dev.env.js │ ├── index.js │ └── prod.env.js ├── docs // 靜態資源地址 │ ├── 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 // 發佈文章和發佈更新頁 │ │ │ └── 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 // 狀態管理 │ ├── 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 // 狀態管理總入口 ├── README.md ├── index.html └── package.json
本項目算是本人第一個完整的手機和pc都兼容,有關於文章展現的項目。整個項目作下來,遇到的Bug不少,天然收穫也是不少。總結下來以下:
1.很長的單詞會超出邊界,致使可視區域變寬。
解決辦法:經過word-wrap: break-word;
實現打斷效果。
2.第二次進入文章時,會殘留(暫未解決)。
解決辦法:經過路由的鉤子函數beforeRouteEnter,來獲取數據,未成功獲取數據時,顯示Loading頁面,加載完成後,顯示文章詳情頁,從而解決這個問題。
3.回到首頁時,不能保留原來的狀態(暫未解決)。
解決辦法:
①此方法爲容易固定高度的解決辦法。(具體方法:用vuex和vue-router的鉤子函數來解決這個問題,即經過scroll事件動態保存此時的scrollTop直,當路由的beforeRouteEnter出發時,恢復其scrollTop的值。)
② 若是沒有固定高度,直接經過Vue自帶的keep-alive組件,保留組件狀態。
4.載入中的動畫效果如何作?
解決辦法:以前是經過CSS3繪製一個圖形,可是後來發現太醜了,就直接用了Iconfont上的svg圖,並添加了動畫效果。
5.如何實現主頁文章列表的懶加載?
解決辦法:判斷滑動的總高度 - 滑動距離頂部的距離 <= 屏幕的可用高度,也就是如下公式:
document.documentElement.offsetHeight - window.scrollY <= window.screen.height
這裏會出現一個bug,知足條件時,繼續滑動,會加載屢次。在此能夠加入一個狀態,表示此時正在加載(詳細參見源代碼),從而解決此bug。
6.回到頂部的動畫怎麼作?
解決辦法:能夠把如今的window.scrollY
分紅n
份,而後再設置一個定時器,每隔m
秒,向上滾動一份的高度,當window.scrollY >= 0
時,再終止定時器。(其中的m, n
爲任意數,根據狀況設定)
7.如何控制正在加載頁面的顯示?
解決辦法:由於加載數據是異步的,能夠在加載以前和加載以後,分別更改一個相似於isLoading
(名稱本身設定)的狀態,從而控制加載頁面的顯示。
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:當進入其餘路由時,仍然會觸發主頁的scroll事件。
解決辦法:以前生命週期鉤子用的是mounted
,所以進入其餘路由時,scroll事件仍然存在。因此如今改用beforeRouteEnter
和beforeRouteLeave
這兩個路由的生命週期鉤子,分別實現載入路由時的scroll事件掛載、離開路由時的scroll事件卸載。從而防止主頁內容的懶加載一直觸發。
11.發佈新文章或更新跳轉至文章詳情頁面後,再按後退,怎麼實現回到主頁?
解決辦法:如今初步是使用,路由跳轉的時候,先跳到主頁,再跳到文章詳情頁,再按後退時,就會回到主頁。
12.如何實現點擊評論右側的回覆按鈕,添加@信息,並focus輸入框?
解決辦法:經過vuex來實時記錄回覆相關的信息,並經過watch輸入框的value來判斷是否focus。
13.有一個很奇怪的bug:ios下,若是在文章詳情頁返回主頁時,此時的window.scrollY
會保持文章詳情頁時的window.scrollY
,若是此值知足異步加載更多數據的條件時,會致使異常加載數據。
解決辦法:不得已,只好在beforeRouteEnter
鉤子中,綁定滾動事件的函數加一個定時器,使其在100ms後綁定事件,因此此時的window.scrollY
就會變成以前的值。
14.如何實現全局的消息提醒?
解決辦法:我是經過一個和路由同級的組件Messages
,而且建立了一個狀態管理的模塊messages
,在其中經過state: messages
存放如今顯示的的通知數據,利用Messages
組件和vuex
的actions
控制其顯示。
本人新手一枚,還在苦逼的找工做中T_T。如過您在代碼中發現了bug,能夠經過評論和我交流,互相學習!有什麼好的想法,也能夠提出來,一塊兒討論。