1. 程式人生 > >Vue開發小白筆記(一)

Vue開發小白筆記(一)

這是一篇寫給自己的筆記,如果不是和我一樣從負開始學習Vue的同學,還是不要看了。。。

才畢業,之前在學校一直在學習移動端開發的知識(現在也快忘了。。)。

如今工作,需要學一些移動端網頁的製作。從零學習前端,一個月的時間,每天下班回來學一點。先對這段時間的學習做個回顧。(主要是怕自己忘。。)

以前和大神合作過Asp.Net的網頁,基本划水,自己做過html的簡單頁面,僅僅限於瞭解了html的尖括號語法。。

硬著頭皮接觸Vue發現知識空缺太大,不談H5的語法,js和css這兩塊註定是需要投入大力氣去了解的。

真的是從零開始,甚至會是從負開始的學習筆記了。。。

目錄

一、介面:

五、結尾:

言歸正傳:

我差不多是看完條件渲染以後就開始跟著教程做一個小網頁了。

對於最開始的部分,推薦下載https://vuejs.org/js/vue.js  有了這個js檔案後,放在與html檔案同一目錄,就可以直接script引入

<script src="./vue.js"></script>

然後就可以來一個Hello World!了。

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Hello World</title>
    <script src="./vue.js"></script>
</head>
<body>
    <div id="app">
      <p>{{ message }}</p>
    </div>

    <script>
        var vm = new Vue({
          el: '#app',
          data: {
            message: 'Hello World!'
          }
        })
    </script>
</body>
</html>

Vue中一個很有意思的地方,可以建立很多物件,為不同的物件賦值,需要使用的時候可以直接呼叫物件,最最重要的一點,Vue是我們的資料和檢視繫結,一旦資料變化,頁面上會實時改變的。響應式系統(當然這侷限於寫在data中的元素,看過Vue官網教程一定明白(資料是否是響應式的?))

這種繫結的關係像是MVVM模型,Vue很大程度上受其啟發。因此官網教程上建立Vue物件的時候,起名都是vm。

每個 Vue 應用都是通過用 Vue 函式建立一個新的Vue 例項開始的。

在上面的程式碼中,vm是我們新建立的物件,el屬性是繫結需要被渲染的元素,例如這裡的el選擇了app這個元素,在上面的div中,就寫明瞭這個div的id是app這就建立起了頁面元素與Vue例項之間的關係。而data則是這個Vue例項中存放的資料。

之後在繫結好的div中使用Vue的插值表示式({{在這裡選擇需要被輸出的屬性名}})就可以在頁面上輸出Hello World了。

建議在這種環境下學習官網的基礎教學,學會基本的Vue語法。(才上手不推薦使用Vue-cli)。例如

  • 生命週期鉤子(在合適的時候呼叫的一系列方法)
  • Vue的模板語法
  • Vue中的指令v-xxx(常用的bind與on的用法與簡寫)

按照教程的順序來吧,基礎的方法沒有什麼捷徑,都打一遍留個印象,之後做個網頁,遇到需要實現的功能能想起來之前練過的某個方法就行了。

為什麼說一開始不推薦Vue-cli,我一開始找到很多教程都是Vue環境的搭建,環境搭好直接告訴我使用Vue-cli搭個框架,過程倒也不難,都有提示,但是搭完直接教程結束。對比一下路徑大概就明白差別了:

使用script引入Vue.js學習基本語法,路徑如下:

其實只有一個Vue.js檔案和一個html檔案就夠了,我只是把知識點分開寫了。

然而。。使用Vue-cli搭建專案框架如下:

第一次看到真的一臉懵逼,最初還是推薦直接引入Vue.js檔案來學習。

我的目錄比上文多兩個資料夾,

dist是專案寫完後使用npm run build命令將整個專案打包後生成的檔案路徑。

static 是存放靜態檔案的路徑,例如圖片,json資料等

之後總結一下這個月做的小網頁涉及到的知識點。做的網頁是根據網上的教程,對著去哪兒網的介面仿照了一個粗劣版。。

一、介面:

主介面:

城市選擇頁面:

推薦欄目詳情頁面:

二、製作步驟:

2.1.首頁:

2.1.1.首頁Header:

最初第一步完成的是首頁的製作,首先在主頁面index.html中加入

<meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum=1.0,user-scalable=no">

目的是針對移動端不同分辨路的裝置做適配,並且限制了使用者縮放。Vue-cli框架搭好後預設有這個屬性,只需要加入後面兩句就好。

由於Vue是元件化開發,因此如果你想,頁面上的每一個元素都可以單獨拿出來做成一個元件,在任何你需要的時候引入即可。

當然也沒必要那麼麻煩,按照區域分成不同的模組即可,從上到下,一開始需要做首頁的header部分,如下圖:

中間的搜尋欄是假的,左側的箭頭也是假的。具有功能的只有右側的城市選擇按鈕。

這裡涉及到的知識點有兩個:

一個是頁面上新增圖示的新方法,專案中採用了阿里的圖示庫iconfont,具體使用方法放在後面寫。

第二個是頁面的跳轉,vue中使用router-link做跳轉:

<router-link to="/foo">Go to Foo</router-link>

但是如果這樣使用的話,這個控制元件會被渲染成一個a標籤,功能正常但是顯示效果會發生變化。

這時就可以為這個router-link加入tag屬性

<router-link tag="li" to="/foo">
  <a>/foo</a>
</router-link>

這個程式碼中將這個router-link宣告為一個li標籤(當然也可以改成別的標籤),就不會發生變色了。

或者直接使用css修改顏色#fff變白。

我的每一個元件都是分開寫的,如果程式碼需要維護,修改起來也方便。(自己的程式碼練習,幾乎不用維護。。。)

目錄就像這樣:

寫好了以後只需要在Home.vue這個元件中呼叫相應的元件就可以了,呼叫方法如下:

1.新增引用

import HomeHeader from './components/Header'

2.宣告變數,由於我需要定義一個叫做HomeHeader的元件,同時我也引用了一個叫做HomeHeader的元件,名字相同可以簡寫。寫一個就好了。

export default {
  name: 'Home',
  components: {
    HomeHeader
  }
}

3.在template中插入定義好的元件,完成使用:(由於引入的元件不止一個,因此外側需要包裹一個div,Vue中要求template模板中只能包含一個元件,所以把引入的所有元件放在一個div中即可)

<template>
  <div>
    <home-header></home-header>
  </div>
</template>

2.1.2.首頁輪播圖:

請忽略這副優質的輪播圖。。。

這裡使用了一個非常好用的第三方外掛,叫做vue-awesome-swiper

直接npm install vue-awosome-swiper --save安裝即可,加上--save的目的在於

不論在開發環境中還是打包生成線上版本的程式碼,都需要使用這個庫。

因此使用--save,將其存入專案中package.json檔案中的dependencies裡面。

之後需要在main.js中引入這個包以及相應的css檔案,這樣引入一次,全域性都可以使用了。

import VueAwesomeSwiper from 'vue-awesome-swiper'
import 'swiper/dist/css/swiper.css'

Vue.use(VueAwesomeSwiper)

使用的方法如下:

<swiper :options="swiperOption" v-if="showSwiper">
    <swiper-slide v-for="item of list" :key="item.id">
        <img class="swiper-img" :src="item.imgUrl" />
    </swiper-slide>
    <div class="swiper-pagination" slot="pagination"></div>
</swiper>

這裡用到了v-for列表渲染的知識,以及v-if條件渲染的知識。很簡單,看一下vue.js官網教程就明白了。

開始的時候home元件給swiper元件傳入了一個輪播圖陣列,存放圖片url,這裡檢測圖片是否存在使用了一個計算屬性

showSwiper,在其中判斷了傳入的list長度,如果為0,則不顯示這個輪播圖控制元件

之後迴圈list,取出每一張圖片建立swiper-slide 就可以實現輪播圖效果了。

swiperOption用來控制這個輪播器的很多屬性,專案裡我用到了兩個:

data () {
  return {
    swiperOption: {
      // 通過此屬性加入圖片輪播次序點
      pagination: '.swiper-pagination',
      // 支援迴圈輪播
      loop: true
    }
  }
}

輪播圖下面的藍色圓點就是pagination實現的效果,預設圓點,當然不止圓點這一種顯示效果,還有很多種。例如另一處輪播圖我用到了

pagination: '.swiper-pagination',
paginationType: 'fraction'

這個的顯示效果是1 / 2   、  2 / 2 這樣的看你的需要,可以改變不同的效果。

2.1.3.專案推薦控制元件:

這兩個元件一樣,都是單純的css設計的知識,這次用到的關於css佈局的一些知識點,我之後寫在下面。

2.2.城市選擇頁面:

當前這個頁面第一次進入有可能會出現點選沒有反應的情況,重新整理一次就好了,還沒有查清原因。

2.2.1.新增路由設定:

在根目錄下建立router資料夾,其中建立index.js檔案設定路徑,目的是之後可以跳轉。

import Vue from 'vue'
import Router from 'vue-router'
import Home from '@/pages/home/Home'
import City from '@/pages/city/City'
import Detail from '@/pages/detail/Detail'

Vue.use(Router)

export default new Router({
  routes: [
    {
      path: '/',
      name: 'Home',
      component: Home
    }, {
      path: '/city',
      name: 'City',
      component: City
    }, {
      // 動態路由
      path: '/detail/:id',
      name: 'Detail',
      component: Detail
    }
  ],
  // 路由切換時新進入的頁面回到頂部
  scrollBehavior (to, from, savedPosition) {
    return { x: 0, y: 0 }
  }
})

最後的scrollBehavior方法是我專案最後才加上的,因為會出現有時主頁拉的位置比較低,再跳轉到其它頁面就沒有預設在頁面頂部,加入這個方法,每次跳轉的時候,都會預設顯示頁面頂部的內容。相當於給了一個x,y的預設座標,都是0.

之後需要在首頁根目錄下的main.js中新增這個index.js的引用。

import router from './router'

2.2.2.Header部分:

這次的左箭頭和搜尋欄都是真的了,有實際功能,可以搜尋、也可以返回,返回依舊是router-link不再贅述。

輸入漢字和拼音都可以找到對應城市,因為預設了一個關於城市的json檔案,城市選擇預設列表也是從json檔案中匯入的,對應城市名拼音拼寫等資訊。

只需要偵聽輸入的資料,每次檢測是否在預設資料中有重複的專案顯示即可。

程式碼如下,需要注意加入了一個小方法,設定了一個timer,如果輸入過快,可以有個小延時,防止快速調用搜索方法。資料節流。

watch: {
    keyword () {
      // 資料節流
      if (this.timer) {
        clearTimeout(this.timer)
      }
      // 如果沒有輸入,則將匹配列表清空
      if (!this.keyword) {
        this.list = []
        return
      }
      this.timer = setTimeout(() => {
        const result = []
        for (let i in this.cities) {
          this.cities[i].forEach((value) => {
            // 在每一個數據中,包含城市名:name 還有城市拼音spell
            // 在其中搜索與使用者輸入keyword的對應項新增進result中
            if (value.spell.indexOf(this.keyword) > -1 || value.name.indexOf(this.keyword) > -1) {
              result.push(value)
            }
          })
        }
        this.list = result
      }, 100)
    }
  }

2.2.3.城市列表:

當前城市沒有定位。只是根據使用者上次選擇的城市改變的。

熱門城市都是預設資料,v-for迴圈上去的。

這個頁面主要需要提到的是加入了better-scroll外掛,添加了一個滾動效果,可以實現果凍式的回彈效果。

使用方法:

第一步:還是npm install better-scroll --save

第二步:在需要的頁面引用better-scroll

import Bscroll from 'better-scroll'

第三步:在掛載的時候建立一個物件,並且指定一個需要滑動的頁面物件

mounted () {
  this.scroll = new Bscroll(this.$refs.wrapper)
}
// 頁面上元素新增屬性ref
<div class="list" ref="wrapper"></div>

2.2.4.快速搜尋條:

說起來有點複雜,直接看原始碼吧。註釋寫了很多。

最值得一提的,現在很多瀏覽器有手勢設定,因此如果在搜尋條上滑動,有可能會觸發手勢,因此在@touchstart屬性設定的時候,加上.prevent,就可以解決拖動搜尋條,頁面也跟著一同滑動的問題了。

.prevent 叫做事件修飾符,可以阻止touchstart的預設行為 有效防止頁面發生移動

<template>
    <ul class="list">
        <li
            class="item"
            v-for="item of letters"
            :key="item"
            :ref="item"
            @touchstart.prevent="handleTouchStart"
            @touchmove="handleTouchMove"
            @touchend="handleTouchEnd"
            @click="handleLetterClick"
        >
            {{item}}
        </li>
    </ul>
</template>

<script>
export default {
  // .prevent 叫做事件修飾符,可以阻止touchstart的預設行為 有效防止頁面發生移動
  name: 'CityAlphabet',
  props: {
    cities: Object
  },
  computed: {
    letters () {
      const letters = []
      for (let i in this.cities) {
        letters.push(i)
      }
      return letters
    }
  },
  data () {
    return {
      touchStatus: false,
      startY: 0,
      timer: null
    }
  },
  // 當頁面被更新同時頁面完成渲染時會執行此生命週期鉤子
  // 一開始渲染頁面時,用的資料cities是空值
  // 當ajax資料傳入,頁面重新渲染,此時獲取A標籤高度才是正確值
  updated () {
    // 獲取到A元素的高度
    this.startY = this.$refs['A'][0].offsetTop
  },
  methods: {
    handleLetterClick (e) {
    //   console.log(e.target.innerText)
    //   通過上一行可以看到每次點選獲取到了被點選的字母,因此這時需要把被點選的資料傳給list元件
    //   兄弟元件傳值需要通過bus匯流排傳值
      this.$emit('change', e.target.innerText)
    },
    // 滑動事件,完成
    handleTouchStart () {
      this.touchStatus = true
    },
    handleTouchMove (e) {
      if (this.touchStatus) {
        // 判斷timer是否存在,存在就清空
        if (this.timer) {
          clearTimeout(this.timer)
        }
        // 不存在就設定一個時間間隔,16毫秒
        // 否則頁面滾動太快,增多handleTouchMove執行頻率
        // 影響效能,因此需要函式節流
        this.timer = setTimeout(() => {
          // 獲取觸控位置的高度
          // 測量頁面上方header區域以及serach區域共長79畫素,因此把獲取到的觸控高度減去79就是距白邊的高度了
          const touchY = e.touches[0].clientY - 79
          // 用距頂部的距離減去A的高度,除以每個元素的高度20,再取整Math.floor 就可以知道目前手指的觸控位置了
          const index = Math.floor((touchY - this.startY) / 20)
          if (index >= 0 && index <= this.letters.length) {
            this.$emit('change', this.letters[index])
          }
        }, 16)
      }
    },
    handleTouchEnd () {
      this.touchStatus = false
    }
  }
}
</script>

<style lang="stylus" scoped>
    @import '~styles/varibles.styl'
    .list
        // 這三行控制此元素在垂直方向做居中
        display: flex
        flex-direction: column
        justify-content: center
        position: absolute
        top: 1.58rem
        right: 0
        bottom: 0
        width: .4rem
        .item
            line-height: .4rem
            text-align: center
            color: $bgColor
</style>

2.3.專案詳情頁面:

2.3.1.遞迴呼叫元件:

這個頁面沒有什麼特別的地方,新知識點只有遞迴呼叫元件

<div>
  <div
    class="item"
    v-for="(item, index) of list"
    :key="index"
  >
     <div class="item-title border-bottom">
        <span class="item-title-icon"></span>
           {{item.title}}
     </div>
     <!-- vue遞迴元件 -->
        <div v-if="item.children" class="item-children">
           <detail-list :list="item.children"></detail-list>
        </div>
  </div>
</div>

判斷item節點是否含有children節點,如果有則會按照item-children這個樣式再次呼叫detail-list這個元件,傳入的資料就是item.children。然而,當前這個Vue元件就是detail-list,這樣就達到了遞迴呼叫的效果。

這裡預設的json資料是這樣的,放上來便於理解層級關係

"categoryList": [{
        "title": "成人票",
        "children": [{
          "title": "成人三館聯票",
          "children": [{
            "title": "成人三館聯票 - 某一連鎖店銷售"
          }]
        },{
          "title": "成人五館聯票"
        }]
      }, {
        "title": "學生票"
      }, {
        "title": "兒童票"
      }, {
        "title": "特惠票"
      }]

顯示出來的效果就和上面那張截圖一樣。

三、額外知識點:

3.1.使用ajax動態獲取資料:

由於頁面一般不會直接將資料完全寫在html中,直接寫死的資料之後想修改就很麻煩,因此這此開發中使用ajax獲取頁面資料,之後如果有需要修改的,更新json資料就行,無需再變更頁面。

使用方法:

第一步:安裝Vue當前推薦的第三方模組axios,npm install axios --save

第二步:引入axios

import axios from 'axios'

第三步:從頁面的根元件中傳送ajax請求,比如Home頁面下,有輪播圖,有分類圖示,有推薦模組。這些元件都需要請求資料,如果每一個元件都發送請求,那請求的次數太多了,因此在Home.vue這個根元件中傳送一次ajax請求,將返回的資料分類傳遞給各個子元件就好。

methods: {
    getHomeInfo () {
      // 只有static資料夾下的資料,靜態資料,才可以直接被訪問,別的路徑會被跳轉到首頁,因此把模擬資料放到static目錄下
      // ret: true 伺服器正常響應請求
      // 因為使用了keep-alive因此每次回到首頁的時候,需要根據當前城市獲取到首頁的資料
      axios.get('/api/index.json?city=' + this.city).then(this.getHomeInfoSucc)
    },
    getHomeInfoSucc (res) {
      res = res.data
      // 如果伺服器返回資料,並且返回資料不為空
      if (res.ret && res.data) {
        const data = res.data
        this.swiperList = data.swiperList
        this.iconList = data.iconList
        this.recommendList = data.recommendList
        this.weekendList = data.weekendList
      }
    }
}

在頁面掛載的時候,呼叫getHomeInfo方法就好。

額外補充:沒有後端伺服器支援的時候,模擬資料的方法

在static靜態檔案目錄中放入需要的json檔案

這時就可以直接在axios.get方法中請求本地路徑了。

但是這樣在專案上線的時候需要改成伺服器上的地址,容易出問題。

因此寫法不變,新增一個轉發機制就行了,webpack-dev-server這個工具提供這個功能。

在config - index.js 中 在dev開發環境下,預設提供一個proxyTable配置項。

在其中配置:(改變專案配置項後需要重啟伺服器)

proxyTable: {
      // 專案上線之前再改程式碼,容易出現問題,因此一開始設定獲取資料的地址都是api路徑
      // 但是一開始模擬資料資源都在本地,因此需要先做一個跳轉,日後好改。
      '/api': {
        target: 'http://localhost:8080',
        pathRewrite: {
          // 一旦請求的地址是以api開頭的,會自動取static/ajax中的內容
          '^/api': '/static/ajax'
        }

注:今天往伺服器上部署的時候,發現把這裡的地址改為伺服器地址,配套路徑都沒有辦法獲取到資料,還沒有查清原因。

如圖:

只好在程式碼裡修改了獲取資料的方式。

3.2.防止swiper顯示效果出錯:

在詳情介面,點選圖片會在最上層顯示圖片輪播介面,再次點選會消失。

但是在顯隱改變的過程中,顯示效果會出現問題,不太清楚為什麼,但是重新重新整理控制元件就好了。因此需要加入這個屬性。(後兩行)

swiperOptions: {
        pagination: '.swiper-pagination',
        paginationType: 'fraction',
        // 防止顯隱改變時滾動效果出錯,swiper外掛監聽到自身或父級元素變化時會自我重新整理一次
        observeParents: true,
        observer: true
      }

3.3.iconfont數量圖示庫:

頁面中有很多小的圖示,如果能做到圖示匹配,會很大程度上的提升頁面的效果。

在iconfont這個網站上就能很方便的找到各式各樣的圖示,使用方法簡單,官網教程也說的非常清楚。

第一步:找到喜歡的圖示新增到專案中(iconfont中可以建立自己的專案)並下載,順便解壓一下。。

第二步:提取這四個檔案

放到src/assets/styles/iconfont下,(個人習慣)

再把iconfont.css放到src/assets/styles目錄

第三步:在iconfont.css中修改前面幾行的引用程式碼,涉及到那四個檔案,只需要改前面的路徑就好。

src: url('./iconfont/iconfont.eot?t=1537099246601'); /* IE9*/
src: url('./iconfont/iconfont.eot?t=1537099246601#iefix') format('embedded-opentype'), /* IE6-IE8 */
url('./iconfont/iconfont.ttf?t=1537099246601') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+*/
url('./iconfont/iconfont.svg?t=1537099246601#iconfont') format('svg'); /* iOS 4.1- */

第四步:在iconfont中 ”我的專案“中找到需要的圖示,滑鼠移動到圖示上,點選複製程式碼。

第五步:在main.js中引入iconfont

import 'styles/iconfont.css'

第六步:正常使用:直接貼上剛才複製的程式碼。class需要設定為iconfont,別的可以再加。

<div class="iconfont back-icon">&#xe624;</div>

圖示大小可以通過font-size這個css樣式來改變。

3.4.使用keep-alive優化效能:

簡單地說就是快取。。

這個功能是Vue內建的,防止每次路由切換到某頁面的時候,都重新獲取資料,如果是靜態頁面,沒必要每次都重新獲取資料。

因此使用keep-alive標籤,包裹不需要每次都重新請求的內容。

<!-- keep-alive是vue自帶的標籤 -->
    <!-- 不使用keep-alive每次進入新頁面都會重新渲染內容,執行鉤子函式 -->
    <!-- 加入之後,每次進入新頁面後,都會將資料存入記憶體,下次再進入此頁面的時候不用重新渲染,提升效能 -->
    <!-- 不用activated生命週期鉤子,避免keep-alive快取的辦法 -->
    <keep-alive>
      <!-- 顯示的是當前路由地址所對應的內容 -->
      <router-view/>
    </keep-alive>

額外補充如何修改快取中的資料?

方法一:

每當頁面載入都會執行mounted函式,但是如果使用了keep-alive,第二次訪問這個頁面就不會再執行這個函數了,因此一些修改資訊的方法不能解除安裝mounted函式中。

不過一旦使用了keep-alive,就會多一個生命週期函式,activated函式,每次進入頁面都會執行這個方法。

如果需要修改一些小內容,可以把修改方法寫在其中。

方法二:

在keep-alive標籤中直接加入不希望被快取的vue元件

例如:

<keep-alive exclude="Detail">
      <router-view/>
</keep-alive>

這裡的Detail對應的是我詳情頁面的根元件,其中的name屬性就是Detail.

3.5.解決低版本手機頁面白屏:

沒有遇到這種情況,但是開發過程的老師提到了,似乎比較常見,記錄在此:

npm install babel-polyfill --save

之後在main.js中引用import 'babel-polyfill'即可

3.6.修復1px邊框問題:

1px指的是css畫素,但是有的螢幕因為畫素比較高,在二倍屏或者多倍屏上對應的不是一個物理畫素的高度,而是兩個或者多個物理畫素的高度。

很多時候定義的1px邊框會因為螢幕倍數被放大,只需要在main.js中引入一個css檔案即可

檔案在我的專案中src/assets/styles/border.css

我把碼雲的地址發上來,有需要的同志可以下載。

傳到csdn上還要c幣,一個css檔案5個c幣我自己都覺得虧。。。

引入後只用在class中新增border-topbottom(上下邊框)或者border-bottom(下邊框)

這樣就有1px邊框了。

3.7.reset.css重置頁面樣式表:

在不同的手機瀏覽器上預設的樣式是不統一的,因此需要把這些樣式做一個統一。

其實只需要引入一個css檔案就可以做到了。

如同border.css,在我的專案中src/assets/styles/reset.css

3.8.fastclick:

在移動端專案中會有一個300毫秒點選延遲的問題,某些機型某些瀏覽器上,使用click事件時會存在這樣的問題。

這樣會影響使用者體驗,因此解決這個問題需要引入fastclick庫。

解決問題需要做這幾步:

第一步:npm install fastclick --save

第二步:main.js中

import fastClick from 'fastclick'

第三步:依舊是main.js attach是fastclick自帶的方法,將其繫結到body上就好了。

fastClick.attach(document.body)

3.9.頁面跳轉:

每一個Vue例項中都有這樣一個例項屬性,router

this.$router.push('/')

可以直接通過其push方法完成頁面跳轉,根據地址跳轉,/跳轉到主頁。

3.10.Vuex實現資料共享:

先說應用場景,在本次專案中,首頁右上角有城市選擇按鈕,預設顯示當前城市,如果點進去修改,回到主頁會變為新選擇的城市。

這時這兩個頁面之間就需要資料傳遞了,學完vue的基本語法應該已經學過兄弟元件或者父子元件之間傳值了。

不過專案中的主頁Home.vue和城市選擇頁面City.vue之間沒有一個公用的父級元件,無法做一個數據中轉。

不過這裡可以使用一個數據共享來實現這個功能,就是標題上說的Vuex。

Vue中推薦的資料框架就是Vuex。

可以這麼理解,如果專案中資料比較複雜需要共享的時候,可以把這些資料放到一個公用的資料儲存空間統一儲存。

一個元件改變了這個公用空間的資料,其他元件就可以感知到。

上圖右側虛線區域就可以理解為一個倉庫store,state中存放所有公用的資料,組建如果想要使用state中的資料,直接呼叫即可。

如果需要改變state的資料,元件無法直接修改,需要走一個流程。

這個流程就是元件先呼叫Actions做一些非同步處理或者批量的同步操作,之後Actions再呼叫Mutations,Mutations中存放的是一個個同步的對State的修改。

有時也可以略過Actions,讓元件直接呼叫Mutations去修改資料。

元件呼叫Actions時呼叫的是Dispatch方法,Actions呼叫Mutations的時候呼叫的是Commit這個方法。

Vuex實際上就是一個單向資料傳遞的流程。

具體使用Vuex的方法:

第一步:安裝Vuex

npm install Vuex --save

第二步:建立Vuex目錄

因為後期可能使用的資料比較複雜,因此有可能需要對Vuex的三部分進行拆分。就需要先建立目錄

在src目錄建立一個資料夾名為store

在store資料夾下建立index.js檔案,我把Actions,Mutations,State三部分做了拆分,當前這三部分涉及到的公用資料與方法只是針對城市選擇頁面與主頁之間的城市資料共享,因此程式碼量很少,但是如果資料量大的時候,拆分還是易於維護的。

// 本來應該直接寫在根目錄下的main.js,但由於store的目錄還是比較複雜的,因此另建一個檔案
import Vue from 'vue'
import Vuex from 'vuex'
import state from './state'
import mutations from './mutations'
import actions from './actions'

// Vuex是一個外掛,因此需要use來使用外掛
Vue.use(Vuex)

// 匯出的不是Vuex而是Vuex建立的倉庫
export default new Vuex.Store({
  state,
  actions,
  mutations
})

actions.js:

export default {
  // actions方法第一個引數ctx是上下文,第二個引數就是傳入的引數
  changeCity (ctx, city) {
    ctx.commit('changeCity', city)
  }
}

mutations.js:

export default {
  changeCity (state, city) {
    state.city = city
    try {
      localStorage.city = city
    } catch (error) { }
  }
}

state.js:

let defualtCity = '城市'
// 使用localStorage時推薦使用trycatch
// 原因見vuex筆記21行
try {
  if (localStorage.city) {
    defualtCity = localStorage.city
  }
} catch (error) {}

export default {
  // 預設從localStorage中取值,如果取不到再選擇‘城市’
  city: defualtCity
}

在main.js中引入store

import store from './store'

之後建立根Vue例項的時候把store傳入進去。

/* eslint-disable no-new */
new Vue({
  el: '#app',
  router,
  store,
  // ES6 相當於 components: { App: App }
  components: { App },
  template: '<App/>'
})
// 路由就是根據網址的不同,返回不同的頁面給使用者

第三步:元件中使用:

{{this.$store.state.city}}

就可以直接訪問到city這個資料了。

額外補充:

1.資料的修改方法:

this.$store.dispatch('changeCity', city)

意為呼叫一個名為changeCity的Actions,並將city這個資料傳入

這就需要提前在actions.js中寫好方法,在上面的程式碼裡已經寫好了。

Actions就會呼叫Mutations,因此這時mutations.js中對應的方法也應該寫好。

這樣就可以修改了。

當然元件也可以直接呼叫commit方法直接修改資料:

this.$store.commit('changeCity', city)

2.Vuex的簡寫方法:

第一步:引入

// 訪問Vuex store中的資料需要寫這麼多{{this.$store.state.city}}
// Vuex提供了一個高階的api 使用方法如下:
import { mapState } from 'vuex'

第二步:加入計算屬性:

// 意思是mapState是值,把vuex裡面的資料對映到此元件的計算屬性裡
  // 把city這個資料對映到名為city的計算屬性中
  // 這樣在使用的時候就不用寫之前那麼長的程式碼,直接this.city就可以直接使用了
  computed: {
    ...mapState(['city'])
  }

第三步:使用:

{{this.city}}

再額外補充。。。

1.傳入資料多樣

computed: {
    // 傳入的值可以是一個數組[],也可以是一個物件
    // 這樣寫的意思是,把state中city資料對映到此元件裡,對映過來的名字就是currentCity
    ...mapState({
      currentCity: 'city'
    })
  }

2.簡寫不僅能用於state,還可以用於actions以及mutations,需要在引入的時候加入

import { mapState, mapActions } from 'vuex'

使用方法:在methods中加入對映關係

    // 意為有一個mutation叫changeCity,把這個mutation對映到這個元件中一個名為changeCity的方法
    // 再直接呼叫mutation的時候可以直接這麼使用this.changeCity(city)
    // ...mapMutations(['changeCity']),
    ...mapActions(['changeCity'])

具體使用方法與state簡寫一樣:

this.changeCity(city)

3.Vuex中還有兩個概念:

getters
類似computed計算屬性

module
當遇到非常複雜的應用場景,資料很多
都放在mutations時,會非常難以管理
可以將公用資料分模組拆分
建立store的時候進行一個整合就可以了,易於維護

3.11.localStorage本地儲存:

舉一個實際的應用場景,每次使用者選擇玩城市後,下次進入這個頁面就希望能記住上一次選擇的城市。因此這時需要localStorage api。

這個api類似cookies但是比cookies更簡單。

在上一步的mutations中,每次改變資料的時候,在localStorage中也儲存一下使用者修改的地址

changeCity (state, city) {
    state.city = city
    try {
      localStorage.city = city
    } catch (error) { }
  }

每次取值的時候也先從localStorage中取,如果取不到再設定為預設值。

try {
  if (localStorage.city) {
    defualtCity = localStorage.city
  }
} catch (error) {}

使用localStorage時推薦使用trycatch
因為有的使用者瀏覽器 關閉了本地儲存功能或者使用隱身模式,則會直接丟擲異常,程式碼無法執行

3.12.動畫效果(簡單的):

這裡不知道怎麼總結了。。。我直接放我當時學習過程中的程式碼吧。放在頁面最後。

3.13.真機測試:

如果需要在開發過程中進行真機測試,手機和電腦連線在同一區域網內,在命令列中ipconfig查到電腦端ip地址,是無法直接在手機上訪問頁面的,需要在專案根目錄下的package.json檔案內找到"scripts" -> "npm run dev" 在webpack-dev-server後面加入--host 0.0.0.0即可,實際上這裡控制的是我們輸入npm run dev後系統自動幫我們執行的命令。加入這句之後,重啟伺服器即可在手機上直接測試我們的網站了。

四、css相關知識點:

4.1.stylus:

在style標籤上加入lang="stylus"即可,之後就可以使用stylus的簡便語法了。

不再需要花括號,用縮排表明層級關係

4.2.解決不同元件中的style樣式相互影響:

為了限制style樣式只在當前元件中生效,需要在style標籤中加入scoped關鍵字

4.3.rem:

在統一樣式(3.7)中加入的reset.css中對於html的font-size已經設定為50px

rem是相對於這個50px的設定的。

1rem = html font-size = 50px

舉個實際的例子,如果有一張圖片的height需要86px,但是目前由於螢幕解析度都比較高,圖片資源中常見二倍尺寸的圖片。

所以這裡設計時寫43px就夠了。這時換算成rem就是0.86rem 可以簡寫成.86rem

正好對應要求高度86px,除以100直接是對應的rem,這也是html中font-size被設定成50px的目的所在,方便使用。

4.4.自定義別名:

有時對於一些自定義的路徑,比較常用且路徑又比較複雜的時候,可以使用自定義別名對這個路徑進行定義。之後引用的時候就不用輸入一長串路徑,直接輸入別名即可。

自定義別名修改路徑: bulid/webpack.base.conf.js resolve -> alias

alias: {
      'vue$': 'vue/dist/vue.esm.js',
      '@': resolve('src'),
      'styles': resolve('src/assets/styles'),
      'common': resolve('src/common'),
    }

這時我如果再需要引入一個src/assets/styles下的檔案,就可以直接

// css中引入其它css需要加~
@import '~styles/varibles.styl'

4.5.超出文字限制長度後用...省略:

如圖:

使用方法:

第一步:

自定義檔案,名字隨意,我寫的是mixins.styl

ellipsis()
  overflow: hidden
  white-space: nowrap
  text-overflow: ellipsis

第二步:

在需要的元件styles中引入

@import '~styles/mixins.styl'

第三步:

在需要加入省略的class外側class中加入這一行

// 不加這一行,ellipsis省略的效果顯示不出來
min-width: 0

第四步:

在需要加入省略功能的class中加入:

ellipsis()

4.6.零碎的css知識點:

1.overflow:hidden的意思就是超出部分隱藏

2.css中引入其它css需要加~

3.padding: 0 .2rem  //兩個引數意為左右不給間距,上下給0.2rem的間距

4.佔位以及css穿透:

// 在wrapper的外層加入div設定class如下,可以為未載入的圖片輪播器佔位,防止因為圖片載入慢導致的頁面抖動問題
// 圖片寬高比=高度/寬度例項中是640*200的圖片,因此寬高比設定為31.25% 高度設定為0 通過padding-bottom設定高度。
// 不論網速如何,先加載出的內容都會在圖片下方。
// 圖片未加載出來時加入一個灰色背景
// 圖片輪播次序點不在此組建中設定css,在這裡修改顏色無效
// 需要通過 >>> 做穿透,就不受scoped限制
    .wrapper >>> .swiper-pagination-bullet-active
        background: #00bcd4
    .wrapper
        overflow: hidden
        width: 100%
        height: 0
        padding-bottom: 31.25%
        background: #eee
        .swiper-img
            width: 100%

5.垂直居中:

// 這三行控制此元素在垂直方向做居中
display: flex
flex-direction: column
justify-content: center

6.有時圖示未顯示,需要加入絕對定位限制:

// 圖示未顯示 加入絕對定位解決
        position: absolute
        top: 0
        left: 0

7.邊框顏色:

// 通過這兩個before和after元素就可以控制頁面上一畫素邊框的顏色
        .border-topbottom
            &:before
                border-color: #ccc
            &:after
                border-color: #ccc
        // 下邊框只有before屬性
        .border-bottom
            &:before
                border-color: #ccc

8.文字框留左右間隙:

// 加入這兩行,可以在輸入框輸入滿了以後左右預留間隙,整體更美觀
            padding: 0 .1rem
            box-sizing: border-box

9.z-index:

z-index 屬性設定元素的堆疊順序。

數值越大,排列位置越前(Z座標上方,檢視層級關係)

擁有更高堆疊順序的元素總是會處於堆疊順序較低的元素的前面。

元素z-index可以為負值

只針對有定位的元素 例如position: absolute

10.漸變色:

background-image: linear-gradient(top, rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.8))

五、結尾:

本次筆記可以說是很凌亂了。。第一次寫筆記,如果看到的各位大神有任何意見,歡迎直接提出,我會做相應修改的。

最後,這次的教學源自慕課網的Vue2.0去哪兒網開發課程,有需要視訊課程的同學可以去官網學習。

5.1.附簡單的動畫效果程式碼&筆記:

5.1.1.Vue中的css動畫原理:

<!DOCTYPE html>

<head>
    <meta charset="UTF-8">
    <title>Vue中的css動畫原理</title>
    <script src="./vue.js"></script>
    <style>
        /* 為什麼以.fade開頭 因為需要渲染的transition的name屬性為fade 如果沒有name屬性 這裡可以用v-enter等等 */
        .fade-enter,
        .fade-leave-to {
            opacity: 0;
        }
        .fade-enter-active,
        .fade-leave-active {
            transition: opacity 1s;
        }
    </style>
</head>

<body>
    <div id="root">
        <!-- 自動向標籤上新增class屬性以增加動畫效果 -->
        <!-- transition 過渡 -->
        <!-- 支援v-show v-if 動態元件 -->
        <!-- 動畫開始的時候 自動添加了v-enter v-enter-active -->
        <!-- 開始的第二秒加入 v-enter-to 同時去掉v-enter -->
        <!-- transition一直監控opacity的變化,預設是1 變成0以後開始動畫,淡出同理 -->
        <!-- 直到動畫結束自動去除這些自動新增的class -->
        <transition name="fade">
            <div v-if="show">
                hello World
            </div>
        </transition>

        <button @click="handleClick">切換</button>
    </div>

    <script>


        var vm = new Vue({
            el: "#root",
            methods: {
                handleClick: function () {
                    this.show = !this.show;
                }
            },
            data: {
                show: true,
            }
        })
    </script>
</body>

</html>

5.1.2.Vue中使用animate.css庫:

<!DOCTYPE html>

<head>
    <meta charset="UTF-8">
    <title>Vue中使用animate.css庫</title>
    <script src="./vue.js"></script>
    <link rel="stylesheet" type="text/css" href="./animate.css">
    <style>


        @keyframes bounce-in {
            0% {
                /* @keyframes與使用animate庫沒有區別都是封裝 */
                transform: scale(0)
            }

            50% {
                transform: scale(1.5)
            }

            100% {
                transform: scale(1)
            }
        }


        .active {
            /* 不加這一行,動畫會跑偏。不懂時去掉此行程式碼觀察 */
            /* 預設名稱是.fade-enter-active 下面同理  為了自定義名字修改 */
            transform-origin: left center;
            animation: bounce-in 1s;
        }

        .leave {
            transform-origin: left center;
            animation: bounce-in 1s reverse;
        }
    </style>
</head>

<body>
    <div id="root">
        <!-- 使用animated動畫庫三大要點 -->
        <!-- 1、必須使用自定義active-class的形式 -->
        <!-- 2、class類中必須包含animated這個類 -->
        <!-- 3、必須將動畫效果名稱寫在後面 -->
        <transition 
            name="fade"
            enter-active-class="animated swing"
            leave-active-class="animated shake"
            >
            <div v-if="show">
                hello World
            </div>
        </transition>

        <button @click="handleClick">切換</button>
    </div>

    <script>


        var vm = new Vue({
            el: "#root",
            methods: {
                handleClick: function () {
                    this.show = !this.show;
                }
            },
            data: {
                show: true,
            }
        })
    </script>
</body>

</html>

5.1.3.Vue中同時使用過渡和動畫:

<!DOCTYPE html>

<head>
    <meta charset="UTF-8">
    <title>Vue中同時使用過渡和動畫</title>
    <script src="./vue.js"></script>
    <link rel="stylesheet" type="text/css" href="./animate.css">
    <style>
        .fade-enter,
        .fade-leave-to {
            opacity: 0;
        }
        .fade-enter-active,
        .fade-leave-active {
            transition: opacity 3s;
        }
    </style>
</head>

<body>

    <!-- animate 動畫叫keyframe 動畫 是 css3的動畫 -->
    <!-- transition是過渡動畫 -->

    <div id="root">
       <!-- 需要在頁面第一次載入的時候呼叫animate動畫特效 需要加入appear屬性 -->

       <!-- 定義type宣告兩種動畫時常以transition時長為主 type="transition" -->
       
       <!-- 也可以自定義動畫時常 使用duration="毫秒數" -->
       <!-- duration控制class持續的時間 -->
       <!-- 或者更復雜的設定出場和入場動畫 -->
        <transition :duration="{enter: 5000, leave: 10000}"
                    name="fade" 
                    appear
                    enter-active-class="animated swing fade-enter-active" 
                    leave-active-class="animated shake fade-leave-active"
                    appear-active-class="animated shake"
                    >
            <div v-if="show">
                Hello World
            </div>
        </transition>

        <button @click="handleClick">切換</button>
    </div>

    <script>


        var vm = new Vue({
            el: "#root",
            methods: {
                handleClick: function () {
                    this.show = !this.show;
                }
            },
            data: {
                show: true,
            }
        })
    </script>
</body>

</html>

5.1.4.Vue中的Js動畫與Velocity.js的結合:

<!DOCTYPE html>

<head>
    <meta charset="UTF-8">
    <title>Vue中的Js動畫與Velocity.js的結合</title>
    <script src="./vue.js"></script>
    <link rel="stylesheet" type="text/css" href="./animate.css">
    <script src="./velocity.min.js"></script>
    <style>

    </style>
</head>

<body>

    <div id="root">

        <!-- 鉤子函式 -->
        <!-- before-enter 在動畫開始之前呼叫 -->
        <!-- enter 動畫呼叫結束後呼叫 -->
        <!-- @after-enter done之後呼叫 -->
        <!-- enter換成leave就是出場動畫 -->
        <transition 
            name="fade"
            @before-enter="handleBeforeEnter"   
            @enter="handleEnter" 
            @after-enter="handleAfterEnter"
        >
            <div v-if="show">
                Hello World
            </div>
        </transition>

        <button @click="handleClick">切換</button>
    </div>

    <script>


        var vm = new Vue({
            el: "#root",
            methods: {
                handleClick: function () {
                    this.show = !this.show;
                },
                // el 這個引數 是指被動畫效果包裹的div標籤
                handleBeforeEnter: function(el) {
                    // el.style.color = 'red'
                    el.style.opacity = 0;

                },
                // done是回撥函式,當動畫執行完後需要手動呼叫done函式
                // 相當於告訴後臺動畫執行完畢
                handleEnter: function(el, done) {
                    // // 延時兩秒
                    // setTimeout(() => {
                    //     el.style.color = 'green'
                        
                    // }, 2000);

                    // // 四秒後呼叫done
                    // setTimeout(() => {
                    //     done()
                    // }, 4000);

                    // complete配置表示動畫執行完畢 
                    Velocity(el, {opacity: 1}, {duration: 1000, complete: done})
                },
                handleAfterEnter: function(el) {
                    // el.style.color = 'black'
                    alert("done")
                }
            },
            data: {
                show: true,
            }
        })
    </script>
</body>

</html>

5.1.5.Vue中多個元素或元件的過度:

<!DOCTYPE html>

<head>
    <meta charset="UTF-8">
    <title>Vue中多個元素或元件的過度</title>
    <script src="./vue.js"></script>
    <link rel="stylesheet" type="text/css" href="./animate.css">
    <style>
        .fade-enter,
        .fade-leave-to {
            opacity: 0;
        }
        .fade-enter-active,
        .fade-leave-active {
            transition: opacity 1s;
        }
    </style>
</head>

<body>

    <div id="root">

        <!-- 不加key屬性 由於vue預設dom複用,不會出現出現與顯示的特效。因此建立key屬性 -->

        <!-- mode="in-out" 動畫元素先進來再隱藏 -->
        <!-- mode="out-in" 動畫元素先隱藏再顯示 -->
        <transition name="fade" mode="out-in">

            <!-- 動態元件 -->
            <component :is="type"></component>
            <!-- 不同元件 -->
            <!-- <child v-if="show"></child>
            <child-one v-else></child-one> -->

            <!-- 普通元素 -->
            <!-- <div v-if="show" key="hello">
                Hello World
            </div>
            <div v-else key="bye">
                Bye World
            </div> -->
            
        </transition>

        <button @click="handleClick">切換</button>
    </div>

    <script>

        Vue.component('child', {
            template: '<div>child</div>'
        })

        Vue.component('child-one', {
            template: '<div>child-one</div>'
        })
         
        var vm = new Vue({
            el: "#root",
            methods: {
                handleClick: function() {
                    // this.show = !this.show

                    this.type = this.type === "child" ? "child-one" : "child"
                }
            },
            data: {
                // show: true,
                type: "child"
            }
        })
    </script>
</body>

</html>

5.1.6.Vue中的列表過渡:

<!DOCTYPE html>

<head>
    <meta charset="UTF-8">
    <title>Vue中的列表過渡</title>
    <script src="./vue.js"></script>
    <link rel="stylesheet" type="text/css" href="./animate.css">
    <script src="./velocity.min.js"></script>
    <style>
        .v-enter, .v-