vue項目實戰《電影快訊》--- 項目總結

Vue實戰之喵喵電影

day1 - 創建組件

頁面組件

  • 電影頁面組件
  • 影院頁面組件
  • 我的頁面組件

公共組件

  • 正在熱映組件
  • 即將上映組件
  • 城市組件
  • 搜索組件
  • 影院列表組件
  • 登錄組件

重點知識總結

  1. 創建新的組件、路由、js等文件時,應該首先創建一個對應名字的文件夾,然後在文件夾裏面創建的一個名爲index的對應文件,這樣既能夠避免將所有的代碼寫在一起,而引起的代碼複雜的問題,又能避免引入文件的路徑複雜(只用找到對應名字的文件夾即可,系統會默認尋找當前目錄下名爲index的文件)
  2. 當一個頁面在註冊公共的組件時,我們可以使用component: () => import('[組件路徑]')這種方式按需導入,可以優化一定的性能。
  3. 在導入外部文件時,由於當前項目的資源過多且目錄結構複雜而引起的相對路徑較爲複雜的問題,我們可以通過@來解決,@表示的是當前項目src目錄,因此我們可以通過@來使用絕對路徑,引入外部文件。
  4. 我們應該爲一些路由增添重定向,例如首頁重定向
{
    path: '/*'
    redirect: '/movie'
}

day2 - 請求數據,渲染頁面

重點知識總結

  1. 關於跨域問題的解決方法:利用axios使用反向代理來進行跨域,具體做法如下:
    • 在項目根目錄建立vue.config.js文件【此文件爲vue默認的配置文件】
    • 在文件中寫入如下代碼:
    • module.exports = {
          devServer: { //反向代理
              proxy: {
                  '/api': { //代理的接口
                      target: 'http://39.97.33.178', //代理的目標地址
                      changeOrigin: true //是否切換源
                  }
              }
          }
      }
      
      詳細的官方配置文檔 devserver-prox
    • 將axios註冊到全局Vue中,在其他頁面使用axios請求數據:
    • //全局註冊axios
      import axios from 'axios'
      Vue.property.axios = axios //註冊到Vue的原型上
      
      //使用axios請求數據
      this.axios.get('/api/...').then(...) //請求的地址,從代理的接口開始
      
  2. 在進行數據綁定的時候,有些數據的形式並不是我們期望的。這時,我們需要使用過濾器來生成需要的數據【全局/局部過濾器的選擇方式:此過濾器是否被項目中的多個地方共用,是則選用全局過濾器,否則選用局部過濾器】,需要注意的是:對於一些數據不同,而最終呈現的樣式不同的情況,我們也可以使用過濾器。
  3. 當需要根據輸入的內容進行查詢時,我們應該使用watch來監聽屬性【此屬性是輸入框雙向綁定的數據】。
    • 爲什麼使用watch而不是使用computed
      • vue的官方文檔中寫到:當需要在數據變化時執行異步或開銷較大的操作時,watch是最有用的。
      • 使用 watch 選項允許我們執行異步操作 (訪問一個 API),限制我們執行該操作的頻率,並在我們得到最終結果前,設置中間狀態。這些都是計算屬性無法做到的。
  4. 在輸入框中一直輸入內容時,立即查詢是非常消耗性能【函數防抖現象】,常見的解決方式有兩種:
    • 使用定時器完成
    • /**
      * @desc 函數防抖
      * @param func 函數
      * @param wait 延遲執行毫秒數
      * @param immediate true 表立即執行,false 表非立即執行
      */
      function debounce(func,wait,immediate) {
          let timeout;
      
          return function () {
              let context = this;
              let args = arguments;
      
              if (timeout) clearTimeout(timeout);
              if (immediate) {
                  var callNow = !timeout;
                  timeout = setTimeout(() => {
                      timeout = null;
                  }, wait)
                  if (callNow) func.apply(context, args)
              }
              else {
                  timeout = setTimeout(function(){
                      func.apply(context, args)
                  }, wait);
              }
          }
      }
      
    • 使用axios自帶的方法(連續發送多次請求,取消上一次請求)
    •  methods: {
          getMsg () {
              let CancelToken = axios.CancelToken
              let self = this
              axios.get('http://jsonplaceholder.typicode.com/comments', {
              cancelToken: new CancelToken(function executor(c) {
                  self.cancel = c
                  console.log(c)
                  // 這個參數 c 就是CancelToken構造函數裏面自帶的取消請求的函數,這裏把該函數當參數用
              })
              }).then(res => {
                  this.items = res.data
              }).catch(err => {
                  console.log(err)
              })
      
      
              //手速夠快就不用寫這個定時器了,點擊取消獲取就可以看到效果了
              setTimeout(function () {
              //只要我們去調用了這個cancel()方法,沒有完成請求的接口便會停止請求
              self.cancel()
              }, 100)
          },
          //cancelGetMsg 方法跟上面的setTimeout函數是一樣的效果,因爲手速不夠快,哦不,是因爲網速太快,導致我來不及點取消獲取按鈕,數據就獲取成功了
          cancelGetMsg () {
              // 在這裏去判斷你的id 1 2 3,你默認是展示的tab1,點擊的時候不管你上一個請求有沒有執行完都去調用這個cancel(),
              this.cancel()
          }
      }
      

day3 封裝組件,關聯頁面之間的數據

使用better-scroll組件來優化列表滑動效果

  • 適用的結構模型圖如下:[外層容器的高度(better-scroll控制的區域)要比內層容器小一點,才能觸發滑動效果]
    [外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-0CIuWvcy-1573185427211)(./images/1.png)]
  • 由better-scroll包裹的的區域必須只有一個根節點,如果不止一個根節點,則需要在外層增加一個div作爲唯一的根節點。
  • better-scroll初始化的時間,是在頁面的數據全部都渲染完畢之後。當我們需要在mounted生命週期中初始化這個插件時,我們需要將初始化的代碼寫在this.$nextTick方法的回調函數中【因爲它可以保證,在頁面完全將數據渲染完畢之後,纔會觸發它的回調函數】
this.$nextTick( () => {
    BScroll({
        ...
    });
})

注意:

  • 由於項目中的包含多個相同的列表,因此會在多個地方用到better-scroll這個插件,因此,我們考慮將其封裝成爲一個公共的組件。在這個組件中,我們將滑動區域的結構製作成爲一個插槽slot,引用時,直接將需要滑動的結構用自定義的標籤名包裹即可。
  • 擴展:還可以用它實現下拉刷新/上拉加載的功能,例如監聽scroll和touchEnd即可實現下拉刷新【詳細請查看官方文檔】,而針對不同的業務,即處理方式不同,我們可以在封裝的組件中,定義統一的接口,採用ref/父子通信的方式則可以暴露給父組件。需要注意的是,當子組件調用父組件定義的方法時,記得使用call改變this指向當前實例。

使用本地存儲,狀態管理

  • 對於項目中一些長時間不會改變的數據,例如:城市信息。我們在第一次請求回數據之後,就通過鍵值對的方式存儲到localStorage中,下一次直接從中讀取即可。
  • 對於項目中的一些數據,在被多個不相關的頁面共享時,我們可以採用狀態管理(vuex)的方式來管理這些數據。
  • 使用狀態管理時,也推薦採用模塊化開發的方式,即在store文件夾下單獨創建一個文件夾(city),在文件夾下新建一個index.js。store文件夾下的index.js添加內容如下:
import Vue from 'vue'
import Vuex from 'vuex'
import city from './city'

Vue.use(Vuex)

export default new Vuex.Store({
  state: {
  },
  mutations: {
  },
  actions: {
  },
  modules: {
    city //store模塊
  }
})

在city/index.js寫入一下結構:

const state = {}
const mutations = {}
const actions = {}
export default {
    namespaced: true,
    state,
    mutations,
    actions
}

在其他組件中訪問數據方式:
this.$store.state.[模塊名字city].[state中的屬性名]
this.$store.commit(city/[mutations中的方法名], [參數])
注意:mutations中方法名稱推薦全大寫。

本地存儲+狀態管理

  • 當需要記錄用戶上一次訪問頁面操作的狀態時,我們需要用到本地存儲,例如,項目中需要記錄用戶上一次選擇的城市,我們就可以將用戶切換城市之後,把本地存儲中的數據,用新數據覆蓋即可。
  • 使用了狀態管理的情況下,狀態管理的初始數據需要從本地存儲中獲取。

keepalive的使用

  • 當用戶第二次進入某一個頁面時,我們不希望它重新刷新一遍,這時我們可以使用keepalive標籤將router-view標籤包裹。如果用戶第二次進入頁面,就會讀取緩存。
  • 還有一種場景,某些情況下,用戶再次進入頁面,希望再次刷新頁面,由於我們只使用了keppalive,導致再次進入頁面,不會再執行mounted等生命週期函數。這時我們需要使用由使用keepalive而產生的生命週期函數activated
    • activated 生命週期在keep-alive 組件激活時調用。
    • 例如:當用戶切換了城市之後,頁面跳轉到其他頁面,此時就應該加載新的數據信息,而請求數據的代碼就應該放在activated生命週期函數中。

自定義JS彈窗組件

  • 首先創建一個JS組件所在的文件夾,新建一個index.js文件,再此文件夾中像普通組件一樣,創建一個新的模塊(組件名稱文件夾/index.vue),寫好必要的代碼結構:
<template>
    <div>
    </div>
</template>
<script>
export default {
    name: ''
}
</script>
<style scoped>
</style>
  • 在index.js文件中寫入以下代碼:
import Vue from 'vue'
import MessageBox from './MessageBox' //引入剛剛創建的JS組件模塊
export let messageBox = (function(){
     let defaults = { } //JS組件中需要的一些data/methods及默認值
    return (options) => { //options:用戶傳入配置項
        //根據用戶額配置項,更新默認值
        for(let attr in options){
            defaults[attr] = options[attr];
        }
        let MessageBoxCom = Vue.extend(MessageBox);
        let vm = new MessageBoxCom({
            el: document.createElement('div'),
            data: {}, //將defaults中的數據項註冊到組件的data中,即可以在html結構中使用
            methods: {}//將defaults中的方法註冊到組件的methods中,可以在html結構中綁定相應的事件
        });
        document.body.appendChild(vm.$el); //將組件的html結構追加到頁面中
    }
})();
  • 在其它頁面使用此JS組件時,只需導入JS文件夾即可
import {messageBox} from '@/components/JS'
  • 初始化組件
 messageBox({
    title: '定位',
    content: nm,
    cancel: '取消',
    sure: '切換',
    handleSure: () => {
    }
});

day4 電影詳細頁面開發

子組件的路由設計

  • 將電影詳細頁面看做是電影頁面的子路由,由於電影頁面已經存在一個子路由,因此需要使用“命名視圖”,在一個組件裏面需要定義多個子路由,則需要爲router-view標籤添加name屬性,以此來區分子組件。例如,有如下子路由<router-view name='detail'></router-view>,則它的路由配置爲:
{
    path: 'detail/1/:movieId',
    components:{
        default: () => import('@/components/NowPlaying'), //默認展示的組件
        detail: () => import('@/views/Movie/detail') //detail爲router-view的name屬性的屬性值
    }
}
+ 如果沒有設置`default`【默認顯示的組件】,它會不顯示任何內容,即空白展示,因此我們最好時設置一下。
  • 動態路由及參數獲取
    • 由父組件跳轉到子組件頁面時,需要傳遞參數,就可以用動態路由,例如path: 'detail/1/:movieId'

    • 子組件頁面接收參數可以使用如下方式:

      1. $route
      const User = {
          template: '<div>User {{ $route.params.id }}</div>'
      }
      
      1. 取代與 $route 的耦合[在組件中使用 $route 會使之與其對應路由形成高度耦合,從而使組件只能在某些特定的 URL 上使用,限制了其靈活性。]
      const User = {
          template: '<div>User {{ $route.params.id }}</div>'
      }
      
      const User = {
          props: ['id'],
          template: '<div>User {{ id }}</div>'
      }
          const router = new VueRouter({
          routes: [
              { path: '/user/:id', component: User, props: true },
      
              // 對於包含命名視圖的路由,你必須分別爲每個命名視圖添加 `props` 選項:
              {
                  path: '/user/:id',
                  components: { default: User, sidebar: Sidebar },
                  props: { default: true, sidebar: false }
              }
          ]
      })
      + 如果 props 被設置爲 true,route.params 將會被設置爲組件屬性
      
      
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章