Vuex相關(長期更新)

1、vue-cli使用

1.基本配置

(1)安裝node

官網下載node:https://nodejs.org/zh-cn/download/javascript

node -V #node版本
npm -V #npm版本

(2)安裝vue和vue-cli

npm install vue
npm install --global vue-cli

(3)建立基於webpack的項目

vue init webpack vue-study #初始化
cd vue-study #進入項目文件夾

項目建立好css

(4)運行項目

npm run dev

項目運行成功html

2.增長一些配置

(1)eslint報錯修復

"lint-fix": "eslint --fix --ext .js --ext .jsx --ext .vue src/",

格式錯誤能夠經過vue

npm run lint-fix

(2)添加中心狀態管理

安裝vuexjava

npm i vuex -D

新建store文件夾及其如下文件node

import Vue from 'vue'
import vuex from 'vuex'
import mutations from './mutations/mutations'
import actions from './actions/actions'
import getters from './getters/getters'
import state from './state/state'
Vue.use(vuex)

export default new vuex.Store({
  state,
  getters,
  actions,
  mutations
})

store/store.jsjquery

import store from './store/store'

new Vue({
  el: '#app',
  router,
  store,
  components: { App },
  template: '<App/>'
})

引入storewebpack

A.state和gettersios

state/state.jsweb

getters.js

mutations.js,actions.js都是export default{}形式

export default {
  name: 'App',
  mounted: function () {
    this.useStore()
  },
  methods: {
    useStore () {
      console.log(this.$store.state.count) // -> 0
    }
  },
  computed: {
    fullName () {
      return this.$store.getters.fullName
    }
  }
}

App.vue

npm run dev

運行

打印出store裏的state,獲得getters裏面的值

B.mutations,actions

export default {
  updateCountAsync (store, data) {
    setTimeout(() => {
      store.commit('updateCount', {
        num: data.num
      })
    }, data.time)
  }
}

actions.js

export default {
  updateCount (state, {num, num2}) {
    state.count = num
  }
}

mutations

dispatch用來觸發actions,和commit用來觸發mutations同樣

{{this.$store.state.count}}

  mounted: function () {
    this.useStore()
    this.useMutations() // 執行後當即變成20
    this.$store.dispatch('updateCountAsync', {
      num: 5,
      time: 2000
    }) // 執行後2秒變成5
  },
  methods: {
    useStore () {
      console.log(this.$store.state.count) // -> 0
    },
    useMutations () {
      this.$store.commit('updateCount', {
        num: 20,
        num2: 2
      })
    }
  },

App.vue

npm run dev

 

2秒以後從20變成5

C.讓store使用更便捷

<script>
import {
  mapState,
  mapGetters
} from 'vuex'
export default {
  name: 'App',
  mounted: function () {
  },
  methods: {
  },
  computed: {
    ...mapState(['count']),
    ...mapGetters(['fullName'])
  }
}

使用map語法更簡潔得到state和getters裏的值。

 

(3)跨域代理

A.axios和proxyTable解決get跨域問題

npm i axios -D #安裝axios

import axios from 'axios'
Vue.prototype.$axios = axios

src/main.js全局添加axios

拿到一個本地數據

jsonview後的視圖

proxyTable: {
      '/api': {
        target: 'http://localhost:8000',
        changeOrigin: true,
        pathRewrite: {
          '^/api': '/'
        }
      }
    },

config/index.js

API_HOST: '/api/'

condfig/dev.env.js增長API_HOST

API_HOST: '"http:/xxx.xxx.xxx.xxx:8000"' // 生產環境的地址,上線後修改

config/prod.env.js增長線上地址接口

mounted: function () {
    this.getapi()
  },
  methods: {
    getapi () {
      this.$axios.get('/api/article/', {
        params: {format: 'json'}
      })
        .then((res) => {
          console.log(res)
        })
        .catch(function (error) {
          console.log(error)
        })
    }
  }

App.vue內調用,注意接口格式

npm run dev

啓動開發環境

查看控制檯輸出跨域接口信息

B.qs解決post發送兼容問題

import qs from 'qs'

Vue.prototype.$axios = axios
Vue.prototype.$qs = qs

main.js

postapi () {
      let data = this.$qs.stringify({'schools': 1, 'id': 1})
      /* 接口請求 */
      this.$axios.post('/api/userSchoolFav/', data)
        .then((res) => {
          console.log('POST數據Fav返回值:', res)
        })
        .catch(function (error) {
          console.log(error)
        })
    }

或者qs非全局註冊

import Qs from 'qs'
export default {
   methods:{
    postapi () {
      let data = Qs.stringify({'schools': 1, 'id': 1})
      /* 接口請求 */
      this.$axios.post('/api/userSchoolFav/', data)
        .then((res) => {
          console.log('POST數據Fav返回值:', res)
        })
        .catch(function (error) {
          console.log(error)
        })
   }
}

當前頁面引入並使用

C.解決跨域攜帶token信息(JWT的方式不是cookie,因此這裏暫時用不上)

axios.defaults.withCredentials = true // 容許跨域攜帶cookie信息

以jwt的token爲例:

參考:https://my.oschina.net/u/3018050/blog/2054854

(4)jquery配置

安裝jquery

npm install jquery -D

const webpack = require('webpack')

plugins: [
    new webpack.ProvidePlugin({
      $: "jquery",
      jQuery: "jquery",
      jquery: "jquery",
      "window.jQuery": "jquery"
    })
  ],

添加build/webpack.base.conf.js相關plugin

import 'jquery'

main.js

jquery: true// 添加

能夠得到到,jquery可使用

(7)styl使用

安裝styl-loader

npm install stylus stylus-loader -D

<style scoped lang="stylus">

語言上lang='stylus'便可

<style lang="stylus">
@import "assets/base.styl"

外部引用

(5)外部ui庫

以element UI爲例

安裝element ui

npm i element-ui -S #安裝element-ui
npm i sass-loader node-sass -D #安裝sass-loader,node-sass

/* 改變主題色變量 */
$--color-primary: teal;

/* 改變 icon 字體路徑變量,必需 */
$--font-path: '../../node_modules/element-ui/lib/theme-chalk/fonts';
@import "../../node_modules/element-ui/packages/theme-chalk/src/index";

新建src/assets/element-variables.scss文件

import Element from 'element-ui'
import './assets/element-variables.scss'
Vue.use(Element)

使用element ui

主要按鈕的主題色定義好

 

3.優化一些設置

(1)使用路由守衛和request/response 攔截器

在main.js裏添加每一個跳轉前的路由鉤子。這裏是經過JWTtoken是否存在,以及跳轉頁面是否爲登陸頁、註冊頁、或者我的中心(例如我的信息頁面)等,肯定對localstorage裏存儲的token是否進行刪除,以及是否須要跳轉回登陸頁面

對request攔截,判斷localstorage裏的token是否存在,來添加authorization認證信息,確保登陸成功的狀態下,每次發送信息攜帶JWTtoken,使得一些須要用戶權限的頁面可以順利經過

對response攔截,判斷返回信息中是否有用戶未登陸401,或者登陸認證錯誤403的狀況,若是有,則返回登陸頁面

這裏對路由鉤子和http攔截判斷打印數字,以便在控制檯更好看到操做時,走的邏輯路徑

// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'

import App from './App'
import router from './router'
import store from './store/store'
import axios from 'axios'
import qs from 'qs'
// import 'jquery'
import Element from 'element-ui'
import './assets/element-variables.scss'

import IndexHeader from '@/layout/Header'
import IndexFooter from '@/layout/Footer'
import Banner from '@/components/Banner'
import Pages from '@/components/Pages'

Vue.use(Element)

Vue.component('IndexHeader', IndexHeader)
Vue.component('IndexFooter', IndexFooter)
Vue.component('Banner', Banner)
Vue.component('Pages', Pages)

router.beforeEach(({name}, from, next) => {
  // 獲取 JWT Token
  if (localStorage.getItem('JWT_TOKEN')) {
    // JWT_TOKEN存在
    console.log(1)
    if (name === 'login' || name === 'register') {
      // 登陸按鈕點擊,清楚JWT Token
      store.commit('DELSTORELOG')
      console.log(2)
      next()
    } else {
      // 其餘頁面返回
      console.log(3)
      next()
    }
  } else {
    // JWT_TOKEN不存在
    console.log(4)
    if (name === 'Information') {
      // 查看我的信息頁面
      console.log(5)
      next({name: 'login'})
    } else {
      console.log(6)
      next()
    }
  }
})

// http request 攔截器
axios.interceptors.request.use(
  config => {
    if (localStorage.JWT_TOKEN) { // 判斷是否存在token,若是存在的話,則每一個http header都加上token
      console.log(7)
      config.headers.Authorization = `JWT ${localStorage.JWT_TOKEN}`
      console.log('存在', localStorage.JWT_TOKEN)
    } else {
      console.log('不存在')
    }
    return config
  },
  err => {
    console.log(8)
    return Promise.reject(err)
  })

// http response 攔截器
axios.interceptors.response.use(
  response => {
    console.log(9)
    return response
  },
  error => {
    console.log(10)
    if (error.response) {
      console.log(11)
      console.log('axios:' + error.response.status)
      switch (error.response.status) {
        case 401:
          // 返回 401 清除token信息並跳轉到登陸頁面
          store.commit('DELSTORELOG')
          router.replace({
            path: 'login',
            query: {redirect: router.currentRoute.fullPath}
          })
          break
        case 403:
          // 返回 403 清除token信息並跳轉到登陸頁面
          store.commit('DELSTORELOG')
          router.replace({
            path: 'login',
            query: {redirect: router.currentRoute.fullPath}
          })
          break
      }
    }
    return Promise.reject(error.response.data) // 返回接口返回的錯誤信息
  })
Vue.prototype.$axios = axios
Vue.prototype.$qs = qs
axios.defaults.withCredentials = true // 容許跨域攜帶cookie信息(例如session等),使用localstorage設置爲false
Vue.config.productionTip = false

/* eslint-disable no-new */
new Vue({
  el: '#app',
  router,
  store,
  components: { App },
  template: '<App/>'
})

main.js

(2)mutations-types.js的設置

A.設置前

SETUSERINFO (state, info) {
    state.userInfo = info
  }

this['SETSTORELOG'](res.data.token)

B.設置後

import {
  SETUSERINFO
} from '../mutation-types.js'
export default {
  [SETUSERINFO] (state, info) {
    state.userInfo = info
  }
}

新建store/mutation-types.js

this.SETSTORELOG(res.data.token)

對比

1)增長mutation-types.js後,須要在mutations.js裏引入,而且將全部方法名用[]括起來。

2)二者引入都徐亞用map from vuex的方式,而且在methods裏添加該方法

3)使用時,無mutation-types須要將方法名用[]括起來,而有mutation-types時,則直接使用,不須要[]

(3)封裝axios方法

A.封裝思路

import axios from 'axios'
export default {
  ajaxGet (api, cb) {
    axios.get(api).then(cb).catch(err => { console.log(err) })
  },
  ajaxPost (api, post, cb) {
    axios.post(api, post).then(cb).catch(err => { console.log(err) })
  }
}

新建src/axios/http.js

import http from './axios/http'
Vue.prototype.$http = http

在main.js中引入

<template>
  <div id="app">
    <IndexHeader></IndexHeader>
    <div @click="GetMethod">axios封裝get方法</div>
    <div @click="PostMethod">axios封裝post方法</div>
    <router-view/>
    <IndexFooter></IndexFooter>
  </div>
</template>

<script>
export default {
  name: 'App',
  methods: {
    GetMethod () {
      this.$http.ajaxGet('/api/article/', res => {
        console.log('ajaxGet', res)
      })
    },
    PostMethod () {
      this.$http.ajaxPost('/api/userSchoolFav/', {'schools': 1}, res => {
        console.log('ajaxPost', res)
      })
    }
  }
}
</script>

在.vue文件中使用

點擊後可在控制檯看到相關內容。

這種封裝方式更簡化了寫法,不須要then,catch這些關鍵字,專心寫裏面的處理邏輯

由此,咱們能夠將一些經常使用方法封裝起來

B.一些方法封裝示例

新建src/axios/methods.js來保存經常使用方法,方便在調用

import axios from 'axios'
export default {
  ajaxGetArticle (api) {
    axios.get(api).then((res) => {
      console.log('ajaxGetArticle', res)
    }).catch(err => { console.log(err) })
  },
  ajaxPostUserSchoolFav (api, post) {
    axios.post(api, post).then((res) => {
      console.log('ajaxPostUserSchoolFav', res)
    }).catch(err => { console.log(err) })
  }
}

<template>
  <div id="app">
    <IndexHeader></IndexHeader>
    <div @click="GetMethod">axios封裝get方法method</div>
    <div @click="PostMethod">axios封裝post方法method</div>
    <router-view/>
    <IndexFooter></IndexFooter>
  </div>
</template>

<script>
import http from './axios/methods.js'
export default {
  name: 'App',
  methods: {
    GetMethod () {
      http.ajaxGetArticle('/api/article/')
    },
    PostMethod () {
      http.ajaxPostUserSchoolFav('/api/userSchoolFav/', {'schools': 2})
    }
  }
}
</script>

.vue裏引用方法並使用

能夠看到頁面可以調用。

目的和使用:

這種方式在rest模式下,當數據格式和返回status一致時,提供很好的使用方法。

好比,咱們要調用不少列表數據時,寫了不少不一樣的.vue下模板,填充數據,就能夠用一條流水線的方式來處理。

把axios發送get請求,對得到列表統一置入當前this的data下,這樣的方式封裝好。

而後統一在method裏使用這個方法帶上url參數便可。

固然,填寫表單也是這個思路,只是多個post傳遞數據對象,表單還要對返回的錯誤提示進行提醒,也就是error裏操做顯示給用戶看。前面咱們都是把method方法和error打印進行統一處理,如今能夠針對不一樣的error代碼獨立出來再也不封裝便可靈活使用

舉例:

import axios from 'axios'
export default {
  ajaxgetList (api, that) {
    axios.get(api).then((res) => {
      console.log(res.data.results)
      if (res.status === 200) {
        that.list = res.data.results
        that.allCount = res.data.count
      } else {
        that.$message.error('獲取失敗')
      }
    }).catch(err => { console.log('cuole', err) })
  }
}

axios/methods.js,寫入方法,傳入that指代this

schoolList局部引入

schoolList一行代碼調用

同理,teacherList局部引入

同理,teacherList一行代碼調用

如此便可方便使用。

import axios from 'axios'
export function ajaxgetList2 (api, that) {
  axios.get(api).then((res) => {
    console.log(res.data.results)
    if (res.status === 200) {
      that.list = res.data.results
      that.allCount = res.data.count
    } else {
      that.$message.error('獲取失敗')
    }
  }).catch(err => { console.log(err) })
}

若是隻是一個方法單獨在一個文件裏,能夠導出爲命名方法

兩種引用方法均可

使用不須要http下了

 

 

(4)actions提交mutation(未練習)

import * as types from './mutation-types';
// 提交mutation
function makeAction (type) {
  return ({ commit }, ...args) => commit(type, ...args);
};
export const setShopList = makeAction(types.SET_SHOPLIST);

actions.js

import * as types from './mutation-types';
import cookie from '../static/js/cookie';
import {getShopCarts} from '../api/api'
// 相似於事件 每一個mutation都有字符類型的事件類型和回調函數
//全局引入vue
import Vue from 'vue';
import Axios from 'axios';
Vue.prototype.$http = Axios


export default {
    [types.SET_SHOPLIST] (state) { //設置購物車數據
        // token = cookie.getCookie('token')
        if(cookie.getCookie('token') != null){
          getShopCarts().then((response)=> {
            // 更新store數據
            state.goods_list.goods_list = response.data;
            console.log(response.data)
            var totalPrice = 0
            response.data.forEach(function(entry) {
              totalPrice += entry.goods.shop_price*entry.nums
            });
            state.goods_list.totalPrice = totalPrice;

          }).catch(function (error) {
            console.log(error);
          });
        }
    },


}

mutations.js

// 獲取購物車數據
export const SET_SHOPLIST = 'SET_SHOPLIST';

mutation-types.js

addShoppingCart () { //加入購物車
            addShopCart({
                goods: this.productId, // 商品id
                nums: this.buyNum, // 購買數量
            }).then((response)=> {
                this.$refs.model.setShow();
                // 更新store數據
                this.$store.dispatch('setShopList');

            }).catch(function (error) {
                console.log(error);
            });
        },

.vue中method方法使用

C.http攔截器抽出

import {getStore} from '../util/mUtils'
import axios from 'axios'
// 全局狀態控制引入
import store from '../store/store'
import router from '../router'

// http request 攔截器
axios.interceptors.request.use(
  config => {
    if (localStorage.JWT_TOKEN) { // 判斷是否存在token,若是存在的話,則每一個http header都加上token
      // 1.不設置有效期
      // config.headers.Authorization = `JWT ${localStorage.JWT_TOKEN}`
      // 2.設置有效期
      let JWT_TOKEN = getStore('JWT_TOKEN')
      config.headers.Authorization = `JWT ${JWT_TOKEN}`
    } else {
    }
    return config
  },
  err => {
    return Promise.reject(err)
  })

// http response 攔截器
axios.interceptors.response.use(
  response => {
    return response
  },
  error => {
    if (error.response) {
      console.log('axios:' + error.response.status)
      switch (error.response.status) {
        case 401:
          // 返回 401 清除token信息並跳轉到登陸頁面
          store.commit('DELTOKEN')
          router.replace({
            path: 'login',
            query: {redirect: router.currentRoute.fullPath}
          })
          break
        case 500:
          console.log('服務器錯誤')
        // case 403:
        //   // 返回 403 清除token信息並跳轉到登陸頁面
        //   store.commit('DELTOKEN')
        //   router.replace({
        //     path: 'login',
        //     query: {redirect: router.currentRoute.fullPath}
        //   })
        //   break
      }
    }
    return Promise.reject(error.response.data) // 返回接口返回的錯誤信息
  })

新建src/axios/index.js,把main.js中關於http攔截器部分所有剪切過來

在main.js中引入

(5)設置default/404圖片

對圖片src是否存在,404等狀況設置默認圖

<!-- 404Img -->
<template>
  <div style="width:100%;height:500px;border:2px solid red">
    <img :src="avatar?avatar:defaultNoImage" :onerror="defaultImg" alt="" >
  </div>
</template>

<script>
export default {
  name: 'Demo404Img',
  data () {
    return {
      defaultImg: 'this.src="' + require('../assets/images/banner1.jpg') + '"',
      defaultNoImage: 'this.src="' + require('../assets/images/banner2.jpg') + '"',
      avatar: '../static/images/avatar-1.jpg'
    }
  }
}
</script>

 

 

 

報錯解決

1.[Vue warn]: You are using the runtime-only build of Vue where the template compiler is not available. Either pre-compile the templates into render functions, or use the compiler-included build.

resolve: {
    alias: {
      'vue$': 'vue/dist/vue.js'
    }
  }

2.npm install時,報錯:正在生成解決方案配置「Release|x64」。

npm config set sass_binary_site https://npm.taobao.org/mirrors/node-sass/

設置全局鏡像源,以後再涉及到 node-sass 的安裝時就會從淘寶鏡像下載。

3.服務器http://localhost:8080要求用戶輸入用戶名和密碼

還未解決

 

 

 

參看文檔

1.vuex官方中文文檔:https://vuex.vuejs.org/zh-cn/

2.詳解 Vue & Vuex 實踐:https://zhuanlan.zhihu.com/p/25042521

3.運用JS設置cookie、讀取cookie、刪除cookie:https://www.cnblogs.com/limeiky/p/6927305.html

4.Django框架基於session的登陸/註銷實現:https://www.cnblogs.com/cllovewxq/p/7878248.html

5.先後端分離之JWT(JSON Web Token)的使用:http://www.javashuo.com/article/p-pylxbsfz-mt.html

6.Vue 中使用 jQuery:https://blog.csdn.net/anxin_wang/article/details/78788773

7.在vue項目中使用stylus:https://blog.csdn.net/shooke/article/details/75907388

8.Vue的elementUI實現自定義主題:https://blog.csdn.net/wangcuiling_123/article/details/78513245

9.【Vue】axios請求的方法封裝和運用:https://blog.csdn.net/lgysjfs/article/details/80407130

10.node-sass 安裝失敗的解決辦法:https://lzw.me/a/node-sass-install-helper.html#%E8%A7%A3%E5%86%B3%E6%96%B9%E6%B3%95%E4%B8%80%EF%BC%9A%E4%BD%BF%E7%94%A8%E6%B7%98%E5%AE%9D%E9%95%9C%E5%83%8F%E6%BA%90

11.vue 顯示圖片404的解決辦法:https://blog.csdn.net/qq_15576765/article/details/83823700

相關文章
相關標籤/搜索