vue 模仿今日頭條demo

vue 頭條 demo

寫在前面

總結一下寫 demo 過程當中 遇到的一些問題,方便本身的學習總結!若有錯誤,還請指正!
  1. 一直想學習使用 vue ,並準備之後在實際項目使用,以前跟着慕課網 黃軼 老師 敲了一下 餓了麼商品購買頁的demo
    ele效果預覽
  2. 該 demo 借鑑自 hcy1996-github 這個項目,但內部內容,佈局風格,徹底不一樣,只爲共同窗習,共同交流
  3. 數據接口 直接打開 今日頭條 網頁版 ,在 network 分析了下,直接 copy 過來的
  4. 還有不少功能沒有實現,後期在完善吧!
  5. 項目地址:github-項目地址
  6. 預覽效果: demo預覽效果
  7. 建議在 chrome 瀏覽器查看(不知道爲何在手機上數據請求,一個勁卡死,不知道是否是由於今日頭條接口的緣由)

演示

演示gif

靜態資源
  1. 頭部添加 rem 佈局
  2. 引入 reset.css
  3. 使用 阿里媽媽圖標庫,index.html 引入

 使用 css 預處理器 sass

  1. 安裝 node-sass sass-loader npm install node-sass sass-loader --save-dev
  2. 使用 <style rel="stylesheet/scss" lang="scss">
  3. 參考文章: http://www.jianshu.com/p/67f52071657d

app.vue

  1. 底部導航欄欄 ==> 剛開始時用的 vuxtabbar tabbar-item 組件,發現有需求實現不了,剛開始還改了源碼,最後實在受不了了
    就用 vue-router roter-link 本身寫了
    底部導航欄 四個按鈕分別對應 四個組件css

    • 因爲本身對 vue-router 理解還欠火候,因此遇到了一個問題
    • routes 數組裏面的內容對應的就是 組件 ,
    • path 選項對應的是路由路徑,初始時沒有路由嵌套 即爲 \index
    • components 選項對應該路由對應的組件,因爲組件已經所有經過 import 引入了,因此不須要寫路徑了
    • 底部通用 tab 導航欄
    • 本想單獨抽出一個 bottom.vue 組件呢,但在左右切換的滑動樣式中,表現並很差,由於但願底部導航欄不滑動這才符合人的預期需求
    • 因此最終仍是選擇不抽離這個組件,直接寫在了 app.vue 裏面了
  2. 通用樣式庫 common scss 目錄 base.scss mixin.scss 經過 一個 index.scss 導入
  3. 引入 axios ,因爲 axios 不支持jsonp,因此還得引入 jsonphtml

    • npm install axios jsonp --save
    • 在 common/js/ajax.js 下使用這兩個庫
    • import axios from 'axios'
    • import jsonp from 'jsonp'
    • 將 ajax 請求,封裝在 一個通用的 js 文件裏,方便統一處理 ajax
    • 即先後端協做時 定義的一些返回值表明的意義,均可以在此方法裏統一處理
  4. 遇到個問題不知道怎麼解決vue

    • 我想有 loading.vue 組件,就是能夠在通用的 ajax.js 文件裏引用,
    • 問題是,我發現 當加載 ajax.js 文件時 ,loading.vue 組件 import 不進來的,因此沒法使用
    • 但我又想要 當用統一 ajax 處理的時候,統一執行 loading
    • 最終我想了一個下下策,把 loading組件的內容直接寫在了index.html 文件裏,這樣就能夠加載到了,就能夠在 ajax處理的時候集中使用 loading.vue 了
    • 不只 loading 組件,還有 通用彈框組件,(就是想在一個通用的 js 文件裏,每次只要在使用的地方 import 這個 js 文件,就能使用這些 通用組件,而沒必要每次都要 import 這些組件)
    • 如今的解決辦法太渣,看之後能不能想到什麼好的辦法
    • 若是各位大大,有什麼好的方法,但願能告知 小弟 一聲

index.vue 文件

  1. 頂部 x-header 組件 ,感受使用不夠靈活
    不知道如何 自定義左右圖標
    由於 vux 的 icon 是引自 icon圖標css庫
  2. 右側icon 是我引用 阿里媽媽圖標庫裏面的圖標的
  3. 標籤導航一欄 原本是用 vux 的scroller 組件寫的,但看到官方文檔上寫 ,此組件已不在維護,且不建議開發者繼續使用
    本身按着 demo 用了一下發現不知道 如何 refresh,就放棄了
  4. 本身 用 better-scroll 處理滾動 tab 標籤欄橫線滾動沒碰到問題
  • 新聞內容列表滾動也是用的 better-scroll 碰到了一個問題,
    下拉刷新的logo 老是沒法藏在 列表後面,不管怎麼設置 z-index
  • 最後才發現問題,本身還真的是蠢,列表容器沒寫背景顏色,天然永遠都能看到loading圖標
在糾結的過程當中也發現了幾個問題:
  • 移動端百分比佈局時:html,body必定要設置寬高百分比,否則會遇到不少坑
  • better-scroll 滾動容器必定要有明確的寬高,建議最好用絕對定位,背景顏色,overflow:hidden
  • 最大的一個坑,列表老是有一部分滾動不上來,碰到這個問題,首先想到:node

    • 列表高度是否大於容器高度
    • refresh 時機不對最終,發現確實時是 refresh 時機不對,
    • 但接下來糾結的時刻到了,不管放在哪都不對,最終寫了 setTimeout(fn,3000),能夠正常工做,但這確定不是解決辦法
    • 最後想到:由於列表中的圖片容器高度,是靠圖片撐開的,但圖片加載的比較慢因此 better-scroll 計算不到準確的高度,
    • 解決辦法:圖片容器高度事先寫死,完美的解決了
  1. 圖片懶加載 vue-lazyload 插件,超好用webpack

    • 懶加載引用的圖片地址:loading 圖片
    • 若是 在js 引用靜態圖片,由於webpack 不會解析 js 文件裏的圖片,
      因此要用 import 引用 import logo from './assets/loading.gif'
    • 或是把圖片放在頂層的 static 目錄裏

微信左右滾動效果(切換底部tab時)---如下是通用思路

  • 在 全局路由 beforeEach(function(to,from,next){***}) 鉤子裏 要作下面的事情
  • 假如待切換的組件爲 index1,index2
  • sessionStorage 裏面 建立 一個 __router__
  • __router__的值包括:count, transitionName , to.path ,from.path
  • count 初識值爲 0;
  • transitionName 初始值爲 ''
  • to.path 初始值爲 undefined
  • form.path 初始值爲 undefined
  • 首次進入時 to.index(index1) 爲空 執行 else
  • count++
  • 判斷 to.path !== '/' && history[to.path] = historyCount
  • history['transitionName'] = 'forward'; 爲前進狀態
  • 此時: index1:1,count:1
  • 二次 進入(路由已跳轉過一次) 此時 to.path 依舊 爲 undefined ,而 from.path 爲 to.path 的值
  • 繼續走 else 裏面,重複上面的步驟 此時 index2:2,count2,index1:1
  • 在繼續點第一個 tab 至關於回到第一個 tab
  • 此時:to.path == index1, from.path == index2
  • 假如 :!fromIndex || parseInt(toIndex) > parseInt(fromIndex
  • 或:toIndex === '0' && fromIndex === '0'
  • forward 前進狀態
  • 不然: 爲 reserve 後退狀態
  • 這樣就能判斷是前進狀態仍是後退狀態,就能夠用樣式控制滾動方向了
  • Do not bb ,show me codeios

    router.beforeEach(function (to, from, next) {
      let history = window.sessionStorage.__router__;
      if(!history){
        history = {};
      }else{
        history = JSON.parse(history);
      }
    
      let historyCount = history.count * 1;    //記錄走過的 tab 頁數量
      const toIndex = history[to.path];        // 要去的索引
      const fromIndex = history[from.path];    //要離開的索引
    
      if (toIndex) {
          if (!fromIndex || parseInt(toIndex) > parseInt(fromIndex) || (toIndex === '0' && fromIndex === '0')) {
          history['transitionName'] = 'forward';
          } else {
              history['transitionName'] = 'reverse';
          }
      } else {
          //第一次沒有記錄session-storage 的狀況
          ++historyCount;
          history['count'] = historyCount;
    
          to.path !== '/' && (history[to.path] = historyCount);
          history['transitionName'] = 'forward';
      }
    
      history = JSON.stringify(history);
    
      window.sessionStorage.__router__ = history;
    
      if (/\/http/.test(to.path)) {
          let url = to.path.split('http')[1];
          window.location.href = `http${url}`
      } else {
          next()
      }
      });

遇到問題(2017-08-10)

  • 當第一次進入頁面時 ,若是不是處在第一個 tab 時,history 裏面記錄的索引就會出現錯亂現象
  • 解決辦法:事先設置好 首頁出現的四個 tab 的索引,設置好 初始的 count 爲 4
  • 這樣就不會發生索引錯亂現象

切換時的一個小問題

  • 當左右華東切換時,要注意將各個 tab 頁頂層設置 ,position:absolute,這樣纔會排在同一排,不然會出現一上一下的現象

具體實現查看 github-項目地址 裏面的 main.js

app.vue

經過 watch 選項監測 $route 動態的改變transitionName 的值git

<transition :name="transitionName">
        <keep-alive>
            <router-view></router-view>
        </keep-alive>
    </transition>

  watch: {
      '$route' (to, from) {
          console.log(to,from);
          this.transitionName = JSON.parse(window.sessionStorage.__router__).transitionName;
      }
  },

  樣式:
    //微信切換樣式 ,左右滾動
  //前進動畫樣式
  .forward-enter-active,.forward-leave-active{
    transition: all 0.3s;
  }

  .forward-enter{
    transform: translateX(100%);
  }
  .forward-leave-to{
    transform: translateX(-100%);
  }

  // 後退動畫樣式
  .reverse-enter-active,.reverse-leave-active{
    transition: all 0.3s;
  }
  .reverse-enter{
    transform: translateX(-100%);
  }
  .reverse-leave-to{
    transform: translateX(100%);
  }

sessionStorage 和 localStorage 本地存儲問題

  1. sessionStorage 本地會話存儲,會話結束-瀏覽器關閉(不包括刷新頁面,恢復頁面),存儲結果清除
  2. localStorage 本地存儲,除非手動清除,不然永不清除
  3. 大小傳說 5M
  4. 方法1:getItem(key),setItem(key,value),clear()
  5. 方法2:利用 . 或 [] 語法,訪問或設置
  6. 事件:
  7. 若是你監聽storage變動事件你就會發現,當數據發生變化時本頁是監聽不到storage事件變動消息的。而同域的其餘打開的頁面反而監聽到了該消息。悲劇不?github

    • 解決辦法百度

storage只能存儲字符串 不能存儲其餘類型數據

  1. 存儲對象,讀取對象:
let history = window.sessionStorage.__router__;
  if(!history){
    history = {};
  }else{
    //讀取
    history = JSON.parse(history);
  }
  //存儲
  history = JSON.stringify(history);
  window.sessionStorage.__router__ = history;

activated 鉤子

  1. 在 <keep-alive></keep-alive> 組件中使用
  2. keep-alive 組件在第二次渲染時不會觸發 create mounted updated 鉤子
  3. 可是會觸發 activated 鉤子
  4. 使用場景: 列表頁==> 詳情頁的切換web

    • 第一次從列表頁進詳情頁時會加載數據 觸發 created,mounted,updated 鉤子
    • 第二次以上鉤子就不會被觸發了, 須要加上一個 activated 生命週期鉤子,在裏面加載請求數據
    • 路由跳轉時 須要用到 動態路由 即在 路徑後面加個 id
    • 我用query 進行傳遞參數,若是不主動傳遞參數,跳轉後的子頁面刷新時數據就丟失了(原計劃用 vuex 作收藏功能)
    • index.js {path: '/newsDetails/:key', name: 'newsDetails',component:newsDetails },
    • 導航寫法:
    <router-link class="news-item" v-for="(item,index) in newsData"
        :to ='{
            path: "/newsDetails" + item.source_url,
            query:{
                newsItem:JSON.stringify(item)
            }
        }'
        tag='li'
        :key='index'
    >
    </router-link>
    //路由外鏈
    <router-view></router-view>

或者

<router-link
    class="news-item" v-for="(item,index) in newsData"
    :to=" 'newsDetail' + item.source_url "
    tag='li'
   :key='index'
   >
</router-link>

列表頁 ==> 詳情頁

從列表頁到詳情頁不適合用嵌套路由
由於其是兩個單獨的頁面,並不會同時出如今一屏上

嵌套路由寫法

  • route.js
{path: '/index', name:'index', component: index,
    children:[
        {path: '/index/newsDetails/:id', name: 'newsDetails',component:newsDetails },
    ]
  }
  • index.vue
<router-link>
  :to = "'index/newsDetails' + item.source_url"
</router-link>

路由傳參

  • 不只僅傳遞一個動態路由id還能夠 經過 params 和 query進行傳遞,但都會顯示在 url上
  • 列表頁 ==> 將該項全部參數傳遞到詳情頁,能夠現將對象數據 序列化爲字符串,放在 query li
  • 在詳情頁時,取值時反序列化,繼而能夠在詳情頁裏面使用
  • 因爲數據是存在 url 裏 故能夠在刷新頁面仍能夠拿到數據

路由小結

  1. 路由中的三個基本概念: route routes router
  2. route 一條路由(單條路由的走向) routes 一組路由(靜止的一組路由的集合)
  3. router 是一種機制至關於一個管理者(當用戶點擊時 去 routes 去執行相應的路由)
  4. 普通路由
  5. 動態路由
  6. 嵌套路由
  7. 編程式導航
  8. 組件內的掛載到 根實例上的兩個對象 路由源信息對象: this.$route 和 路由實例對象 this.$router

父子組件通信

  • 父組件 => 子組件
  • 父組件傳遞: <to-top :flag="toTop" @scrolltoTop="scroll_to"></to-top>
  • 簡寫方式(直接傳值)數組形式 ==> : props:['flag']
  • 默認值寫法: 對象形式ajax

    props:{
            flag:{
                type:Bollean,
                dafault(){
                    return false;
                }
            }
        }
  • 子組件 ==> 父組件
  • v-on 綁定 子組件派發而來的事件
  • 父組件接收: <to-top :flag="toTop" @scrolltoTop="scroll_to"></to-top>

-

methods:{
    scroll_to(childmsg){
        //執行 。。。
        childmsg  爲子組件向父組件傳遞的參數
    }
}
  • 子組件

    methods:{
       xxx(){
           this.$emit('scrolltoTop','aaa向父組件傳遞的參數')
       }
    }
注意不能直接使用 $on 監聽子組件拋出的事件,而必須在模板裏使用 v-on 綁定

收藏頁 ==> vuex

  • 在詳情頁進行 收藏/取消 操做
  • 將該操做的數據存在 vuex 裏,而後存在 localStorage 裏,
  • store.js 裏建一個 newsItem字段,值爲數組,而後經過 mutations 操做,
  • 向數組裏添加或刪除元素
  • 在收藏組件裏進行渲染

原計劃收藏頁的新聞是能夠 收藏/取消收藏的

  • 收藏 存進vuex,取消收藏從vuex裏刪除
  • 但今日頭條的數據結構感受有點亂
  • 想着真實開發中,後臺確定會返回一個字段告訴該條新聞本人是否已經收藏過
  • 只作了收藏,暫無取消收藏功能,收藏以後存進 vuex ==> localstorage
  • bug ==> 同一條新聞可重複收藏
  • vuex 操做流程
  • store.js
import Vue from 'vue'
import Vuex from 'vuex'
import mutations from './mutations'
Vue.use(Vuex);
const store = new Vuex.Store({
    state:{         //數據管理中心
        count:0,
    },
    mutations,     //使用處進行 commit
    getters:{      //外界在此處得到 vuex 數據
        nowTime(state){
            return new Date() - 0 + '-' + state.count;
        }
    }
});
export default store;
  • mutations.js
//全局觸發事件
export default {
    increment (state){   // 只有經過此處的方法才能改變vuex 內的數據
        state.count++;
    },
    decrement (state){
        state.count--;
    },
}
  • 使用的時候 引入 import {mapState,mapMutations,mapGetters} from 'vuex'
  • 而後經過 this.$store 對象進行操做

vuex 待續。。。

處理資源

  1. js 引用圖片 必須用 import 導入 import logo.png from '相對路徑'
  2. 放在 src 目錄裏的文件都是交由 webpack 處理的
  3. 放在 static 目錄裏面的文件 webpack 不會處理,而是在 build 以後,直接拷貝 相應目錄裏
  4. 因此在 項目裏若是要引用 static 目錄裏的文件 必需要使用絕對路徑 /static/[filename]
  5. main.js 裏 引用 圖片懶加載 的加載中圖片時 路徑必須爲 './static/img/loading.gif'(我也不知道緣由)
相關文章
相關標籤/搜索