Vue入門與進(jìn)階

1. Vue概述

1.1 Vue介紹

Vue 是一套用于構(gòu)建用戶界面的漸進(jìn)式框架。

1.2 Vue核心思想

  1. 雙向數(shù)據(jù)綁定
    Vue雙向數(shù)據(jù)綁定利用了Object對象的set()get()方法,原理如下:
  <input type="text" id="userName" />
  <span id="uName"></span>
  <script>
    const obj = {}
    Object.defineProperty(obj, 'text', {
      get: function(val) {
        console.log('get init');
      },
      set: function(val) {
        console.log('set:' + val);
        ipt.value = val;
        span.innerText = val;
      }
    })
    const ipt = document.getElementById('userName');
    const span = document.getElementById('uName');
    ipt.addEventListener('keyup', function(e) {
      obj.text = e.target.value;
    })
  </script>

1.3 Vue與React對比

1.3.1 不同點

一、Vue

  • 簡單的語法和項目構(gòu)建。

二、React

  • 適用于大型項目以及更好的可測試性。
  • 更大的生態(tài)圈帶來的更多的支持工具。

1.3.2 相同點

  • 虛擬DOM
  • 輕量級
  • 響應(yīng)式
  • 服務(wù)端渲染
  • 易于集成路由工具、打包工具和狀態(tài)管理工具
  • 優(yōu)秀的支持和社區(qū)

2. Vue基礎(chǔ)語法

2.1 Vue環(huán)境搭建

2.1.1 環(huán)境構(gòu)建方式

  1. 官方拷貝
    <script src="https://cdn.jsdelivr.net/npm/vue"></script>
  2. npm 安裝
  3. vue-cli工具構(gòu)建

2.1.1 vue-cli工具構(gòu)建SPA應(yīng)用

  1. npm i -g vue-cli
  2. vue init webpack-simple demo
    初始化一個簡單的webpack項目
  3. vue init webpack demo
    初始化一個完整的webpack項目

2.2 Vue基礎(chǔ)語法

  1. 模板語法
  • Mustache語法:{{msg}}
  • Html賦值:v-html = ""
  • 綁定屬性: v-bind:id = ""
  • 使用表達(dá)式:{{ok ? 'YES' : 'NO'}}
  • 文本賦值:v-text = ""
  • 指令:v-if = ""
  • 過濾器:{{message | capitalize}}v-bind:id = "rawId | formtaId"
    注釋: vue組件中的data推薦使用方法的方式data(){return {}}返回數(shù)據(jù),這樣不同組件實例之間就不會共用數(shù)據(jù)。
  1. ClassStyle綁定
  • 對象語法
<div v-bind:class="{active: isActive, 'text-danger': hasError}">
  • 數(shù)組語法
<div v-bind:class="[activeClass, errorClass]">
data: {
  activeClass: 'active',
  errorClass: 'text-danger'
}
  • style綁定
<div v-bind:style="{color: activeColor, fontSize: fontSize + 'px'}">
  1. 條件渲染
  • v-if
  • v-else
  • v-else-if
  • v-show
  • v-cloak
  1. 事件處理器
  • v-on:click="greet"@click="greet"
  • v-on:click.stopv-on:click.stop.preventv-on:click.selfv-on:click.once
  • v-on:keyup.enter/tab/delete/esc/space/up/down/left/right
  1. Vue組件
  • 全局組件和局部組件
    單頁面應(yīng)用一般使用的都是局部組件。
  • 父子組件通訊-數(shù)據(jù)傳遞
//父組件
<template>
  <div class="hello">
    <Counter v-bind:num="num" v-on:incre="increment" v-on:decre="decrement"/>
    <span>{{`parent: ${num}`}}</span>
  </div>
</template>
<script>
import Counter from './Counter'
export default {
  data () {
    return {
      num: 10
    }
  },
  components: {
    Counter
  },
  methods: {
    increment() {
      this.num++
    },
    decrement() {
      this.num--
    }
  }
}
</script>

//子組件
<template>
  <div>
    <button @click="increment">+</button>
    <button v-on:click="decrement">-</button>
    <p><span>{{num}}</span></p>
  </div>
</template>
<script>
export default {
  props: ['num'],
  methods: {
    increment() {
      this.$emit('incre');
    },
    decrement() {
      this.$emit('decre')
    }
  }
}
</script>
  • Slot
    使用slot可以減少父組件props傳值的數(shù)量以及子組件$emit觸發(fā)父組件方法的數(shù)量。
 <modal v-bind:mdShow="mdShowCard" v-on:close="closeModelCard">
   <p slot="message">
     <svg class="icon-status-ok">
       <use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#icon-cart"></use>
     </svg>
     <span>加入購物車成功</span>
   </p>
   <div slot="btnGroup">
     <a class="btn btn--m" @click="mdShowCard=false">繼續(xù)購物</a>
     <router-link class="btn btn--m btn--red" href="javascript:;" to="/cart">查看購物車</router-link>
   </div>
 </modal>

2.3 路由 vue-router

2.3.1路由基礎(chǔ)介紹

  1. 路由
    根據(jù)不同的url地址展示不同的內(nèi)容或頁面。
  2. 后端路由
    服務(wù)器根據(jù)url地址返回不同頁面。
  3. 前端路由
    不同路由對應(yīng)不同內(nèi)容或頁面的任務(wù)交給前端來做。
  4. 前端路由使用場景
    單頁面應(yīng)用。
  5. 前端路由的優(yōu)缺點
    優(yōu)點:路由跳轉(zhuǎn)用戶體驗好。
    缺點:不利于SEO;瀏覽器前進(jìn)后退重新發(fā)送請求,沒有利用緩存;無法記住之前頁面的滾動位置。
  6. vue-router介紹
    vue-router官網(wǎng)
    (1) 跳轉(zhuǎn)組件
    <router-link></router-link>
    注釋: router-link就是一個封裝好的a標(biāo)簽,可以添加樣式。
    (2) js跳轉(zhuǎn)
    this.$router.push({path: ''})
    (3) 展示組件
    <router-view></router-view>
    注釋: vue-router是對historyAPI的封裝。

2.3.2. 動態(tài)路由匹配

  1. 動態(tài)路由介紹
模式 匹配路徑 $route.params
/user/:username /user/even {username: 'even'}
/user/:username/post/:post_id /user/even/post/123 {username: 'even', post_id: 123}
  1. 動態(tài)路由的使用
//路由入口文件: scr/router/index.js
import Vue from 'vue'
import Router from 'vue-router'
import GoodList from '@/views/GoodList'
Vue.use(Router)
export default new Router({
  mode: 'history', //默認(rèn)值為hash
  routes: [
    {
      path: '/goods/:goodsId/user/:userName',
      name: 'GoodList',
      component: GoodList
    }
  ]
})
//src/views/GoodsList.vue文件
<template>
  <div>
    <div>這是商品列表頁面</div>
    <span>{{$route.params.goodsId}}</span>
    <span>{{$route.params.userName}}</span>
  </div>
</template>
<script>
export default {}
  • 使用vue-cli工具構(gòu)建的項目已經(jīng)嵌套vue-router
  • 路徑完全匹配模式(/goods/@@@/user/&&&)才能夠訪問到該路由視圖。
  • mode 默認(rèn)取值為hash,此時通過# + 路徑才能訪問到。如果取值為history,則不用加#

2.3.3. 嵌套路由

  1. 嵌套路由的使用
//路由入口文件: scr/router/index.js
import Vue from 'vue'
import Router from 'vue-router'
import GoodList from '@/views/GoodList'
import Title from '../views/Title'
import Image from '../views/Image'

Vue.use(Router)

export default new Router({
  mode: 'history', //默認(rèn)值為hash
  routes: [
    {
      path: '/goods',
      name: 'GoodList',
      component: GoodList,
      children: [
        {
          path: 'title',
          name: 'title',
          component: Title
        },
        {
          path: 'img',
          name: 'img',
          component: Image
        }
      ]
    }
  ]
})
//src/views/GoodsList.vue文件
<template>
  <div>
    <div>這是商品列表頁面</div>
    <router-link to="/goods/title">
      顯示標(biāo)題子路由
    </router-link>
    <router-link to="/goods/img">
      顯示圖片子路由
    </router-link>
    <div>
      <router-view></router-view>
    </div>
  </div>
</template>
<script>
export default {}
</script>
//src/views/Image.vue文件
<template>
  <div>圖片子路由</div>
</template>
<script>
export default {}
</script>
//src/views/Title.vue文件
<template>
  <div>標(biāo)題子路由</div>
</template>
<script>
export default {}
</script>
  • children處注冊路由時使用相對于父路由的路徑即可,在router-linkto屬性跳轉(zhuǎn)地址要使用完整路徑。
  • 從子路由的用法可知,路由的本質(zhì)并不是整個頁面的顯示/隱藏切換,而是頁面某個區(qū)域的顯示隱藏切換。

2.3.4. 編程式路由

  1. 編程式路由介紹
    使用js實現(xiàn)頁面的跳轉(zhuǎn)。
  • $router.push("name")
  • $router.push({path: "name"})
  • $router.push({path: "name?a=123})
  • $router.push({path: "name", query: {a: 123}})
  • $router.go(1/-1)
    注意: 對比區(qū)分query參數(shù)的傳遞與動態(tài)路由params參數(shù)的傳遞。 query傳遞的是?a=1;b=2字段,通過$route.query.key的方式取值。動態(tài)參數(shù)傳遞的是/a/b字段,通過$route.params.key的方式取值。
    總結(jié):①$route.query.key獲取的是當(dāng)前urlquery中的字段值,$route.params.key獲取的是當(dāng)前urlparams中的字段值。②使用 router-link組件跳轉(zhuǎn)和js跳轉(zhuǎn)都可以傳遞paramsquery
  1. 編程式路由的使用
//路由入口文件: scr/router/index.js
import Vue from 'vue'
import Router from 'vue-router'
import GoodList from '@/views/GoodList'
import Cart from '@/views/Cart'

Vue.use(Router)

export default new Router({
  mode: 'history', //默認(rèn)值為hash
  routes: [
    {
      path: '/goods',
      name: 'GoodList',
      component: GoodList
    },
    {
      path: '/cart',
      name: 'cart',
      component: Cart
    }
  ]
})
//src/views/GoodsList.vue文件
<template>
  <div>
    <div>這是商品列表頁面</div>
    <button @click="toCart">
      跳轉(zhuǎn)到購物車
    </button>
  </div>
</template>
<script>
export default {
  methods: {
    toCart() {
      this.$router.push({path: "/cart", query: {a: 1}})
    }
  }
}
</script>
//src/views/Cart.vue文件
<template>
  <div>
    <div>這是購物車頁面</div>
    <span>{{$route.query.a}}</span>
    <button @click="backToGoods">
      返回商品列表
    </button>
  </div>
</template>
<script>
export default {
  methods: {
    backToGoods() {
      this.$router.go(-1)
    }
  }
}
</script>

2.3.5. 命名路由和命名視圖

1.命名路由和命名視圖介紹
給路由定義不同的名字,根據(jù)名字進(jìn)行匹配。
給不同的router-view定義名字,通過名字進(jìn)行對應(yīng)組件的渲染。

  1. 命名路由的使用
//路由入口文件: scr/router/index.js
import Vue from 'vue'
import Router from 'vue-router'
import GoodList from '@/views/GoodList'
import Cart from '@/views/Cart'

Vue.use(Router)

export default new Router({
  mode: 'history', //默認(rèn)值為hash
  routes: [
    {
      path: '/goods',
      name: 'GoodList',
      component: GoodList
    },
    {
      path: '/cart/:cartId',
      name: 'cart',
      component: Cart
    }
  ]
})
//src/views/GoodsList.vue文件
<template>
  <div>
    <div>這是商品列表頁面</div>
    <router-link v-bind:to="{name: 'cart', params: {cartId:123}, query: {a:1}}">跳轉(zhuǎn)到購物車頁面</router-link>
  </div>
</template>
<script>
export default {}
</script>
//src/views/Cart.vue文件
<template>
  <div>
    <div>這是購物車頁面</div>
    <span>{{$route.params.cartId}}</span>
    <span>{{$route.query.a}}</span>
  </div>
</template>
<script>
export default {}
</script>
  1. 命名視圖
    Vue Router文檔-命名視圖

2.3.6 HTML5 History 模式

  1. vue-router默認(rèn)hash模式 —— 使用URLhash來模擬一個完整的 URL,于是當(dāng) URL 改變時,頁面不會重新加載。
    如果不想要很丑的 hash,我們可以用路由的 history 模式,這種模式充分利用 history.pushState API 來完成 URL 跳轉(zhuǎn)而無須重新加載頁面。
const router = new VueRouter({
  mode: 'history',
  routes: [...]
})
  1. 當(dāng)你使用 history 模式時,URL 就像正常的 url,例如 http://yoursite.com/user/id,也好看!
    不過這種模式要玩好,還需要后臺配置支持。因為我們的應(yīng)用是個單頁客戶端應(yīng)用,如果后臺沒有正確的配置,當(dāng)用戶在瀏覽器直接訪問 http://oursite.com/user/id 就會返回 404,這就不好看了。
    所以呢,你要在服務(wù)端增加一個覆蓋所有情況的候選資源:如果 URL 匹配不到任何靜態(tài)資源,則應(yīng)該返回同一個index.html頁面,這個頁面就是你 app 依賴的頁面。
    注釋:vue-router處理前端路由跳轉(zhuǎn),后端路由處理ajax/fetch請求以及用戶訪問url頁面。當(dāng)用戶直接訪問某一個路由頁面url(http://oursite.com/user/id)時,該請求會由服務(wù)器端進(jìn)行處理。
    注意:把服務(wù)器的url解析重定向到index.html的首頁里面即可。如果mode 取默認(rèn)值hash,通過# + 路徑地址才能訪問到,后臺是不識別錨點,不會出現(xiàn)404的狀態(tài)。
  2. 后端配置例子
    (1)原生 Node.js
const http = require('http')
const fs = require('fs')
const httpPort = 80

http.createServer((req, res) => {
  fs.readFile('index.htm', 'utf-8', (err, content) => {
    if (err) {
      console.log('We cannot open "index.htm" file.')
    }

    res.writeHead(200, {
      'Content-Type': 'text/html; charset=utf-8'
    })

    res.end(content)
  })
}).listen(httpPort, () => {
  console.log('Server listening on: http://localhost:%s', httpPort)
})

(2) 基于 Node.jsExpress
對于 Node.js/Express,請考慮使用 connect-history-api-fallback 中間件

  1. 給個警告,因為這么做以后,你的服務(wù)器就不再返回 404 錯誤頁面,因為對于所有路徑都會返回index.html 文件。為了避免這種情況,你應(yīng)該在 Vue 應(yīng)用里面覆蓋所有的路由情況,然后在給出一個 404 頁面。
const router = new VueRouter({
  mode: 'history',
  routes: [
    { path: '*', component: NotFoundComponent }
  ]
})

或者,如果你使用 Node.js 服務(wù)器,你可以用服務(wù)端路由匹配到來的 URL,并在沒有匹配到路由的時候返回 404,以實現(xiàn)回退。更多詳情請查閱 Vue 服務(wù)端渲染文檔

2.4 請求數(shù)據(jù)

2.4.1 vue-resource

  1. vue-resource 的請求API是按照REST風(fēng)格設(shè)計的,它提供了7種請求API
  • get(url, [options])
  • head(url, [options])
  • delete(url, [options])
  • jsonp(url, [options])
  • post(url, [body], [options])
  • put(url, [body], [options])
  • patch(url, [body], [options])
  1. 發(fā)送請求時的options選項對象包含以下屬性
參數(shù) 類型 描述
url string 請求的URL
method string 請求的HTTP方法,例如:'GET', 'POST'或其他HTTP方法
body Object, FormData string request body
params Object 請求的URL參數(shù)對象
headers Object request header
timeout number 單位為毫秒的請求超時時間 (0 表示無超時時間)
before function(request) 請求發(fā)送前的處理函數(shù),類似于jQuerybeforeSend函數(shù)
progress function(event) ProgressEvent回調(diào)處理函數(shù)
credientials boolean 表示跨域請求時是否需要使用憑證
emulateHTTP boolean 發(fā)送PUT, PATCH, DELETE請求時以HTTP POST的方式發(fā)送,并設(shè)置請求頭的X-HTTP-Method-Override
emulateJSON boolean request bodyapplication/x-www-form-urlencoded content type發(fā)送
  1. 全局?jǐn)r截器interceptors
Vue.http.interceptors.push((request, next) => {
        // ...
        // 請求發(fā)送前的處理邏輯
        // ...
    next((response) => {
        // ...
        // 請求發(fā)送后的處理邏輯
        // ...
        // 根據(jù)請求的狀態(tài),response參數(shù)會返回給successCallback或errorCallback
        return response
    })
})
  1. vue-resource使用示例
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>vue-resource</title>
  <!-- 最新版本的 Bootstrap 核心 CSS 文件 -->
  <link rel="stylesheet" >
  <script src="../../node_modules/vue/dist/vue.js"></script>
  <script src="../../node_modules/vue-resource/dist/vue-resource.js"></script>
</head>
<body>
<div id="app">
  <h2>vue-resource演示</h2>
  <a href="#" @click="sendGet">發(fā)送Get請求</a>
  <a href="#" @click="sendPost">發(fā)送Post請求</a>
  <a href="#" @click="sendJsonp">發(fā)送Jsonp請求</a>
  <a href="#" @click="sendHttp">全局函數(shù)</a>
  <p v-text="response"></p>
</div>

<script>

  new Vue({
    el:"#app",
    data:{
      response:''
    },
    http: {
      root: 'http://localhost:8050/imoocmall/'
    },
    mounted() {
      Vue.http.interceptors.push((request, next) => {
        console.log('request init.');
        next((response) => {
          console.log('response init.');
          return response
        })
      })
    },
    methods:{
      sendGet() {
        this.$http.get('package.json',{
          params:{
            userId: "101",
          },
          headers:{
            access_token:"abc"
          }
        }).then(res => {
          this.response = res.data;
        }).catch(err => {
          this.response = err;
        });
      },
      sendPost() {
        this.$http.post('package.json', {
          userId: '102'
        }, {
          headers: {
            access_token:"abcd"
          }
        }).then(res => {
          this.response = res.data;
        }).catch(err => {
          this.response = err;
        });
      },
      sendJsonp(){
        this.$http.jsonp("http://www.imooc.com/course/ajaxskillcourse?cid=796",{
          params:{
            userId:"1001"
          }
        }).then(res => {
          this.response = res.data;
        }).catch(err => {
          this.response = err;
        })
      },
      sendHttp() {
        this.$http({
          url:"package.json",
          method:"GET",
          params:{ userId:"103" },
          headers:{ token:"123" },
          timeout:50,
          before() {
            console.log("before init")
          }
        }).then(res => {
          this.response = res.data;
        });
      }
    }
  });
</script>
</body>
</html>

注釋: ①引入 vue-resource之后可以通過this.$http的方式使用。

2.4.2 Axios

  1. Axios簡介
    Axios 是一個基于 promiseHTTP 庫,可以用在瀏覽器和 node.js 中。
  2. 請求方法介紹
  • axios.request(config)
  • axios.get(url[, config])
  • axios.delete(url[, config])
  • axios.head(url[, config])
  • axios.post(url[, data[, config]])
  • axios.put(url[, data[, config]])
  • axios.patch(url[, data[, config]])
    注意: ·Axios· 請求方法中沒有jsonp請求。
  1. 執(zhí)行get請求
// 為給定 ID 的 user 創(chuàng)建請求
axios.get('/user?ID=12345')
  .then(function (response) {
    console.log(response);
  })
  .catch(function (error) {
    console.log(error);
  });

// 可選地,上面的請求可以這樣做
axios.get('/user', {
    params: {
      ID: 12345
    }
  })
  .then(function (response) {
    console.log(response);
  })
  .catch(function (error) {
    console.log(error);
  });
  1. 執(zhí)行 POST 請求
axios.post('/user', {
    firstName: 'Fred',
    lastName: 'Flintstone'
  })
  .then(function (response) {
    console.log(response);
  })
  .catch(function (error) {
    console.log(error);
  });
  1. 執(zhí)行多個并發(fā)請求
function getUserAccount() {
  return axios.get('/user/12345');
}

function getUserPermissions() {
  return axios.get('/user/12345/permissions');
}

axios.all([getUserAccount(), getUserPermissions()])
  .then(axios.spread(function (acct, perms) {
    // 兩個請求現(xiàn)在都執(zhí)行完成
  }));
  1. Axios使用示例
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>axios</title>
  <link rel="stylesheet" >
  <script src="../../node_modules/vue/dist/vue.js"></script>
  <script src="../../node_modules/axios/dist/axios.js"></script>
</head>
<body>
<div id="app">
  <h2>vaxios演示</h2>
  <a href="#" @click="sendGet">發(fā)送Get請求</a>
  <a href="#" @click="sendPost">發(fā)送Post請求</a>
  <a href="#" @click="sendHttp">全局函數(shù)</a>
  <p v-text="response"></p>
</div>

<script>
  new Vue({
    el:"#app",
    data:{
      response:''
    },
    mounted() {
      axios.interceptors.request.use((req) => {
        console.log('request init.');
        return req;
      });
      axios.interceptors.response.use((res) => {
        console.log('response init.');
        return res;
      });
    },
    methods:{
      sendGet() {
        axios.get('../../package.json',{
          params:{
            userId: "101",
          },
          headers:{
            token:"abc"
          }
        }).then(res => {
          this.response = res.data;
        }).catch(err => {
          this.response = err;
        });
      },
      sendPost() {
        axios.post('../../package.json', {
          userId: '102'
          }, {
            headers: {
              token:"abcd"
            }
          }).then(res => {
          this.response = res.data;
        }).catch(err => {
          this.response = err;
        });
      },
      sendHttp() {
        axios({
          url:'../../package.json',
          method:"POST",
          data:{ userId:"103" },
          headers:{ token:"123" }
        }).then(res => {
          this.response = res.data;
        });
      }
    }
  });
</script>
</body>
</html>

注釋:①axios的參數(shù)傳遞方式與vue-resource基本相同。② 注意區(qū)分get請求與post請求的參數(shù)傳遞方式。

2.5 Vuex基本用法

2.5.1 Vuex介紹

  1. Vuex是一個專門為Vue.js應(yīng)用程序開發(fā)的狀態(tài)管理模式。
  2. 當(dāng)我們構(gòu)建一個中大型的單頁應(yīng)用程序時,Vuex可以更好的幫助我們在組件外部統(tǒng)一管理狀態(tài)。

2.5.2 核心概念

State Getters Mutations Actions Modules

  1. State
    State唯一數(shù)據(jù)源,單一狀態(tài)樹。
// 創(chuàng)建一個 Counter 組件
const Counter = {
  template: `<div>{{ count }}</div>`,
  computed: {
    count () {
      return store.state.count
    }
  }
}
  1. Getters
    通過Getters可以派生出一些新的狀態(tài)。
const store = new Vuex.Store({
  state: {
    todos: [
      { id: 1, text: '...', done: true },
      { id: 2, text: '...', done: false }
    ]
  },
  getters: {
    doneTodos: state => {
      return state.todos.filter(todo => todo.done)
    }
  }
})
  1. Mutations
    更改Vuexstore中狀態(tài)的唯一方法是提交mutation
const store = new Vuex.Store({
  state: {
    count: 1
  },
  mutations: {
    increment (state) {
      // 變更狀態(tài)
      state.count++
    }
  }
})

觸發(fā)mutation handler

store.commit('increment')
  1. Actions
    Action提交的是mutation,而不是直接改變狀態(tài)。
    Action可以包含任意異步操作。
const store = new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    increment (state) {
      state.count++
    }
  },
  actions: {
    increment (context) {
      context.commit('increment')
    }
  }
})

注釋:mutations中的方法必須是同步的,actions中的方法可以有異步操作。

  1. Modules
    面對復(fù)雜的應(yīng)用程序,當(dāng)管理的狀態(tài)比較多時,我們需要將Vuexstore對象分割成模塊(modules)。
const moduleA = {
  state: { ... },
  mutations: { ... },
  actions: { ... },
  getters: { ... }
}

const moduleB = {
  state: { ... },
  mutations: { ... },
  actions: { ... }
}

const store = new Vuex.Store({
  modules: {
    a: moduleA,
    b: moduleB
  }
})
store.state.a // -> moduleA 的狀態(tài)
store.state.b // -> moduleB 的狀態(tài)
  1. 概念關(guān)系圖


2.5.3 項目結(jié)構(gòu)

├── index.html
├── main.js
├── api
│   └── ... # 抽取出API請求
├── components
│   ├── App.vue
│   └── ...
└── store
    ├── index.js          # 我們組裝模塊并導(dǎo)出 store 的地方
    ├── actions.js        # 根級別的 action
    ├── mutations.js      # 根級別的 mutation
    └── modules
        ├── cart.js       # 購物車模塊
        └── products.js   # 產(chǎn)品模塊

2.5.4 代碼示例

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Vuex</title>
  <script src="../../node_modules/vue/dist/vue.js"></script>
  <script src="../../node_modules/vuex/dist/vuex.min.js"></script>
</head>
<body>
<div id="app">
  <h2>{{msg}}</h2>
  <button @click="add">同步增加</button>
  <button @click="asyncAdd">異步增加</button>
  <counter></counter>
</div>
<script>
  const counter = {
    template: `
      <div>
         <h3>{{count}}<h3>
         <h3>{{squareCount}}</h3>
      </div>
    `,
    computed: {
      ...Vuex.mapState(['count']),  //mapState語法糖
      /*count() {
        return this.$store.state.count;
      },*/
      squareCount() {
        return this.$store.getters.squareCount;
      }
    }
  };
  const store =  new Vuex.Store({
    state: {
      count: 0
    },
    getters: {
      squareCount(state) {
        return state.count * state.count;
      }
    },
    mutations: {
      increment(state, num) {
        state.count += num;
      }
    },
    actions: {
      asyncIncrement(context, num) {
        setTimeout(() => {
          context.commit('increment', num);
        }, 1000);
      }
    }
  });
  new Vue({
    el: '#app',
    store,
    data: {
      msg: 'Vuex的使用'
    },
    components: {
      counter
    },
    methods: {
      add() {
        this.$store.commit('increment', 1);
      },
      asyncAdd() {
        this.$store.dispatch('asyncIncrement', 2)
      }
    }
  })
</script>
</body>
</html>

3. Vue生態(tài)圈

3.1 模擬mock數(shù)據(jù)

vue開發(fā)過程中,有時需要使用本地json模擬后臺接口數(shù)據(jù),測試前端頁面展示情況。對于使用vue-cli工具構(gòu)建的項目,封裝了express框架,我們可以通過攔截請求的方式使用mock數(shù)據(jù)。

  1. 創(chuàng)建mock數(shù)據(jù)json文件
  2. webpack.dev.conf.js文件中攔截請求
//imoocmall/build/webpack.dev.conf.js文件
var goodsData = require('../mock/goods.json')
devServer: {
     before (app) {
       app.get('/goods', function (req, res) {
         res.json(goodsData);
       })
     },
    //...
}
//..

注釋: 這里的app.get('/goods', (req, res) => {})就是express框架定義后端路由接口的寫法。

  1. 使用mock數(shù)據(jù)
axios.get('/goods',)
  .then(res => { 
    //...
})

3.2 圖片懶加載

使用vue-lazyload插件可以實現(xiàn)圖片懶加載。

  1. 安裝
    npm i vue-lazyload -d
  2. 引入
    src/main.js
import VueLazyload from 'vue-lazyload'
Vue.use(VueLazyload, {
  error: 'dist/error.png',
  loading: 'dist/loading.gif',
})
  1. 使用
<img  v-lazy="'/static/'+good.productImage" alt="">

3.3 滾動加載

使用vue-infinite-scroll插件可以實現(xiàn)滾動加載。

  1. 安裝
    npm install vue-infinite-scroll --save
  2. 引入
    src/main.js
import infiniteScroll from 'vue-infinite-scroll'
Vue.use(infiniteScroll)
  1. 使用
<div v-infinite-scroll="loadMore" infinite-scroll-disabled="busy" infinite-scroll-distance="30">
   <img src="../assets/loading-spinning-bubbles.svg" >
</div>

var count = 0;
new Vue({
  el: '#app',
  data: {
    data: [],
    busy: false
  },
  methods: {
    loadMore: function() {
      this.busy = true;
      setTimeout(() => {
        for (var i = 0, j = 10; i < j; i++) {
          this.data.push({ name: count++ });
        }
        this.busy = false;
      }, 1000);
    }
  }
});

注釋:infinite-scroll-disabled表示滾動加載是否禁用。

3.4 請求代理

  1. 開發(fā)過程中,前端服務(wù)與后端接口一般存在著跨域問題。vue-cli提供了proxyTable代理功能解決跨域問題。
    注釋:① 開發(fā)環(huán)境前端服務(wù)端口8080(/config/index.js中的port: 8080)與后端服務(wù)端口(/server/bin/www中的var port = normalizePort(process.env.PORT || '3000');)不同,存在跨域,所有需要使用請求代理。② 一般僅在開發(fā)環(huán)境中配置。
    注意:①跨域問題只是web前端瀏覽器的行為,在web前端請求不符合同源策略接口數(shù)據(jù)時出現(xiàn)。②后端node連接mongodb數(shù)據(jù)庫即使協(xié)議域名端口不同(不符合同源策略),也不存在跨域問題。
  2. 修改/config/index.js 文件中的dev.proxyTable配置
proxyTable: {
      '/goods': {
        target: 'http://localhost:3000' 
      }
    }
}

此時,當(dāng)我們請求 http://localhost:8888/goods 的時候,就等于請求了http://localhost:3000/goods
注意: '/goods'規(guī)則不僅能夠匹配'/goods'規(guī)則的路徑,還能夠匹配'/goods/a''/goods/a/b'等規(guī)則的路徑。

proxyTable: {
      '/api': {
        target: 'http://localhost:3000' ,
        pathRewrite: {
          '^/api':  ''
        }
      }
    }
}

此時,當(dāng)我們請求 http://localhost:8888/api 的時候,就等于請求了http://localhost:3000

參考資料

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,237評論 6 537
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,957評論 3 423
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,248評論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,356評論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 72,081評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,485評論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,534評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,720評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,263評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 41,025評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,204評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,787評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,461評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,874評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,105評論 1 289
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,945評論 3 395
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 48,205評論 2 375