Vue 2.0 + Axios + Vue Router 實現CNode社群

劉一筆發表於2019-03-03

這是我學習Vue之後的上手的第一個作品,還望大家多多指點。

預覽地址          原始碼地址            謝謝star ^_^

專案技術棧

  • Vue2.0:前端頁面展示

  • Vue-Router:頁面路由切換

  • Axios:一個基於 Promise 的 HTTP 庫,向後端發起請求

  • ES6:箭頭函式、Promise等等語法很好用

  • Webpack:vue init webpck

  • CSS3:CSS3過渡動畫及樣式

TODO

功能上:發帖,收藏,回覆,點贊,

實現上:使用Vuex共享各元件間的狀態

專案截圖在文章末尾。

以下為對CNode社群提供的部分API、實現的各個元件介紹以及專案編碼中遇到的坑而又是如何解決的的總結。

API介紹

Vue 2.0 + Axios + Vue Router 實現CNode社群

這裡由於API內容過多,是以縮略形式展示,需要完整的圖,請到專案根目錄static資料夾中獲取。

元件介紹

pagination 分頁元件

這個元件HTML和CSS部分的程式碼相對簡單,最重要的是理清各按鈕的排布邏輯。比如在點選頁碼5時,頁碼1 就應該去除且同時頁碼6顯示在頁碼5之後;還有比如在目前頁碼為1的情況下,需要禁止上一頁的執行。以下是元件template中的程式碼。

  <template>
    <div class="pagination">
      <div class="wrap">
        <span @click="changeBtn">首頁</span>
        <span @click="changeBtn">上一頁</span>
        <span v-if="judge">...</span>
        <span
          v-for="(btn,index) in pagebtns"
          :key="index"
          :class="[{curtPage:btn==curtPage},'pagebtn']"
          @click="changeBtn(btn)"
        >{{btn}}</span>
        <span @click="changeBtn">下一頁</span>
      </div>
    </div>
  </template>複製程式碼

第三個span是用來判斷當前頁碼是否大於4,如果大於4就應該用顯示...表明之前仍有頁碼。

新增了v-for指令的span是用來迴圈展示頁碼的。:class指令使用陣列語法動態繫結類名。如果該頁碼即為當前頁,那麼value值為true,類名curtPage得以新增到class上。需要注意的是,這裡的pagebtn是可直接使用的類名,所以是字串型別;如果是使用了元件data中的屬性,那就不應該使用字串型別。

所有span的click事件是用觸發頁碼變動事件的。這裡具體實現上,按鈕為文字的和數字的邏輯是不同的。對於文字按鈕,我這裡使用switch進行判斷。因為在點選非頁碼按鈕時是獲取不到頁面的,這時就需要對所點選按鈕的innerText做出判斷以響應,這裡為了方便使用jQuery獲取當前頁碼的上一個DOM節點模擬點選事件;或許要說明的是首頁按鈕的case,因為有可能頁面顯示的頁碼已經不是最開始的時候,所以要把頁碼初始化。也就是現在寫總結時才想起,完全可以在模板程式碼中直接傳遞確定的頁碼作為引數,這樣就省了很多事。

  //方法一
  switch (page.target.innerText) {
            case "上一頁":
              $("span.curtPage").prev().click(); break;
            case "下一頁":
              $("span.curtPage").next().click(); break;
            case "首頁":
              this.pagebtns = [1, 2, 3, 4, 5, "..."];
              this.changeBtn(1); break;
            default:
              break;
          }
  ​
  //方法2
  <span @click="changeBtn(1)">首頁</span>
  <span @click="changeBtn(curtPage-1)">上一頁</span>複製程式碼

實現樣式上效果後,更重要是點選了頁碼後屬實可以獲取到新一頁的內容。用Vue的話來說就是父子元件中的訊息傳遞,那在這裡就是子元件(pagination)把訊息(新的頁碼)傳遞到父元件(pagelist)。顯然是使用$emit()方法,在子元件中執行完改變頁碼的最後,使用$emit()向父元件傳送訊息,父元件中用自定義事件接受,程式碼如下。

  //子元件
  this.$emit("handle", this.curtPage);
  ​
  //父元件
  <pagination @handle="renderList"></pagination>
  ​
  renderList(value) {
        this.page = value;
        this.getData();
  }
  ​複製程式碼

postlist元件

此元件的目的是渲染從API請求到的主題資料。佈局也倒是簡單,頂部一個主題分類tab,下面則是渲染的主題列表,CSS程式碼此處不細講(到這個階段了,簡單的不應該成為問題)。

對介面的請求,那必然涉及到Axios。考慮到這個庫的使用相對簡單,就不展開介紹使用直接上專案程式碼好了。get方法的第一個引數是請求的介面地址,第二個引數是介面請求所需的引數。下面是使用了物件的形式傳遞引數,當然也可以模板字串拼接在地址中。請求成功後即將載入動畫取消,並且將回傳的資料賦給this.poststhis.posts是一個陣列,而每一條主題資料又是一個物件,在v-for指令中渲染的就是每一個物件。

  methods: {
      getData() {
        this.$http
          .get("https://cnodejs.org/api/v1/topics", {
            params: {
              page: this.page,
              limit: 20}})
          .then(response => {
            this.isLoading = false;
            this.posts = response.data.data;
          })
          .catch(err => {
            console.log(err);});
      },
      renderList(value) {
        this.page = value;this.getData();}
  },
      
  beforeMount() {
      this.isLoading = true; //載入成功之前顯示載入動畫
      this.getData(); //頁面載入前獲取資料
  }複製程式碼

模板中的程式碼稍微值得一提的也就動態繫結類名和router的使用了。詳見下方程式碼。

      <span
          :class="[{putgood:(post.good==true),puttop:(post.top==true),
          'putnormal':(post.good!=true&& post.top!=true)}]"
          >
              {{post|formatTopicType}}
      </span>
  ​
      <router-link :to="{name:'post_content',params:{id:post.id,name:post.author.loginname}}">
          <span class="title">{{post.title}}</span>
      </router-link>複製程式碼

這裡的動態繫結類名是針對主題分類使用的,putgoodputtop都是在post.goodpost.top為真時才會新增到類中,而putnormal則是在主題類別不為置頂和精華時新增到類中。router的使用,上面程式碼中路由的功能為在點選主題標題後跳轉到主題的詳情頁中,動態繫結的to屬性中各key含義為:name 路由名稱,params 路由路徑中需要的引數(也即API中需要的引數)。

  //路由配置檔案
  {
      name: 'post_content',
      path: '/topic/:id&name=:name',
      components: {
        main: Article,
        side: Sidebar
        //router-view中的name
        //點選首頁的文章標題後就會用article元件替換掉之前在main
        //中的postlist 元件
      }
    }複製程式碼

現在兩部分程式碼連起來看,就可以清楚地看出路由的動向。程式碼中另外還提到的Sidebar是下文會提到的另一個元件,寫在意味當走向這個路由時,頁面不止有name為main的Article元件還有name為Sidebar的元件。

Sidebar元件

這個元件是側邊欄元件,用於在主題詳情頁中渲染主題作者的相關資訊。

元件模板分為三個部分,作者概要資訊、作者最近主題、作者參與主題。在這裡再次用到router在點選使用者頭像時跳轉到使用者詳情頁,具體和上面上面類似,這裡就不贅述。

  getSideInfo() {
        this.$http
          .get(`https://cnodejs.org/api/v1/user/${this.$route.params.name}`)
          .then(result => {
            if (result.data.success) {
              this.userinfo = result.data.data;
            }
          })
          .catch(err => {
            console.log(err);
          });
      }複製程式碼

在這裡,我們繼續來看到對使用者詳情API的請求,和之前提到的有些不同的是,這裡用到了this.$route.param,這個所指向的就是先前提到的路由配置檔案中我們設定的路由引數。通過這個我們可以輕易獲取到路由中的引數以在元件中使用。

Article元件

這個元件是主題詳情元件,用於展示主題內容回覆等。沒有很多相較之前元件的新內容。說下用到的過濾器,由於API返回的建立時間是包含年月日等的具體時間,但希望頁面中顯示的是”幾小時錢“這樣,所以需要有過濾器對返回建立時間進行處理,這個過濾器邏輯比較簡單就不貼程式碼了。另外,考慮到這種是通用的過濾器,建議放在全域性中,即main.js檔案。

在這個頁面中,同時存在的還有sidebar,當點選sidebar元件中主題時,由於Vue元件例項複用的原因,生命週期的鉤子不會再被呼叫,這也意味著儘管點選後位址列URL發生變化,但並不會觸發getArticleData methods,此時就需要用到偵聽器watch,以監聽$route物件的變化,當路由變化時,則再次請求發起請求。

    watch: {
      $route(to, from) {
        this.getArticleData();
      }
    }複製程式碼

再有就是,除了簡單的豎線鍵值之外,我們還可以在模板中編寫JavaScript表示式,這一個feature在需要的時候會很有用的。比如這個元件中每條回覆是有點贊數的,但我們並不希望在點贊數為0的時候也顯示,這個時候就可以用到三目運算子進行判斷({{comment.ups.length>0?comment.ups.length:''}})。此外,我們希望顯示回覆的樓層,但返回的回覆為從0開始的陣列型別,那此時我們在模板這樣寫就好了({{index+1}}樓)。

headerbar元件

即網站頂部導航欄,這部分別的沒多少程式碼,主要編碼就是在CSS了。社群用的是float,我用了下flex佈局實現。

最後還有個Userinfo元件,但考慮在實現和sidebar元件無差,這裡就不具體分享了。

印象很深的坑

路由配置中的components少寫了s,當時beforeMount裡的程式碼都沒執行,控制檯也沒報錯,完全懵逼。只好請教了眼尖的掃地僧。(所有的程式設計初學者都滿級近視

專案截圖

以下為部分專案截圖,由於在技術實現上的重複性以及介面的問題,我並沒有實現社群中有的全部頁面。

Vue 2.0 + Axios + Vue Router 實現CNode社群

Vue 2.0 + Axios + Vue Router 實現CNode社群

謝謝看完,多多指教^_^


相關文章