vue入门–基础命令+axios+案例练习(一)

Vue入门-基础命令+axios+案例实践Vue入门-Vue常用属性、生命周期、计算属性、过滤器、组件、虚拟DOM、数组的响应式方法、页面闪现、ES6简单语法增强Vue入门- js高阶函数(箭头函数)、v-model数据绑定、组件化、父子组件通信及访问vue介绍——槽(命名、匿名、作用域槽)+ES6模块化导入导出+webpack的使用(基础使用+配置使用+如何一步步演化成cli脚手架)+webpack插件使用(搭建本地服务器,配置文件分离)vue-cli脚手架2版和3版+安装,目录解析,only和compiler的区别, version 3+ 怎么改配置,箭头函数和this的基本使用指向vue-router,路由参数,懒加载,嵌套路由,导航守卫,keep-alive Promise的基本使用,三点ates、chain call和shorthand、所有方法Vuex的作用、用途、核心概念(State、Mutations、Getters、Actions、Modules)、文件提取、axios网络请求的基本使用、配置使用(全局axios和本地axios实例)、模块封装,axios拦截器Vuex作用

官方解释:

Vuex 是为 Vue.js 应用程序开发的状态管理模式。它使用集中式存储来管理应用程序所有组件的状态,并具有确保状态以可预测方式变化的规则。

Vuex还集成到Vue官方调试工具中,提供零配置穿越、状态快照导入导出等高级调试功能。

那么究竟什么是状态管理和集中存储管理?

其实我们可以简单的把它想象成多个组件共享的变量都存储在一个对象中,然后把这个对象放到最顶层的Vue实例中,让其他组件可以使用。而且 Vuex 也是响应式状态管理。

那我们先来了解一下,如果你想在Vue中放置一个全局对象,你能做到吗?当然可以,但是这个对象不是响应式对象。

如果已经导入了Vue相关的依赖

const obj = {
   
    name: "wlh",
    age: 21
}
Vue.prototype.obj = obj	// 在Vue中放置一个全局对象
Vue.component('aaa', {
   	// 由于其他组件都是继承自Vue的,都能拿到这个对象,但是这个对象不是响应式的
    this.obj.name
    this.obj.age
})

那么 Vuex 的存在就是为了解决 this 不是全局反应变量的问题。

管理哪些状态

最常见的是用户登录后的一些全局变量如用户名、头像、token等,以及商品的集合、购物车中的商品等,也是反应灵敏。

一些变量,比如普通的父子组件通信类,在Vuex中不需要管理,直接通过父子通信传递数据。

看图:

其中State其实是指我们的状态(可变数据,比如vue组件中定义的数据),然后会放到View上,也就是要显示的视图上。然后通过一个事件,我们就可以控制State(数据的状态),不断循环。

单页状态管理

代码说明:

<template>
  <div id="app">
    <h2>{
  {counter}}</h2>
    <button @click="counter++">+</button>
    <button @click="counter--">-</button>
  </div>
</template>
<script> export default {
      name: 'App', data() {
      return {
      counter: 0 } } } </script>

在上面的代码块中,data()中定义的变量就是我们的State状态,然后在页面上渲染counter变量。当我单击 + 和 – 按钮时,会触发事件并更改 data() 中定义的变量。 ,即改变State状态,形成循环。

Vuex 使用多页面状态管理

如果您在 App.vue 组件中使用另一个组件,您希望两个组件共享一个变量。当然,这种简单的场景可以通过使用父子组件之间的 props 值传递来实现。这只是为了说明如何进行全局状态管理。

安装 vuex

npm install vuex --save

vuex是一个插件,我们需要使用Vue.use来使用插件。

使用和访问

在目录下新建一个store文件夹(按约定命名),并创建一个index.js文件

import Vue from 'vue'
import Vuex from 'vuex'
// 1.安装插件
Vue.use(Vuex)
// 2.创建对象
const store = new Vuex.Store({
   
    // 保存状态
    state:{
   
        counter: 1000
    }
})
// 3.导出store对象
export default store

在 main.js 中,导入 store 并用于 vue 实例

import Vue from 'vue'
import App from './App'
import store from './store'
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
   
  el: '#app',
  store,
  render: h => h(App)
})

vuex的state对象中放置的变量可以在其他组件中使用$store.state.xxx调用

例如上面我们在store的状态中存储了一个计数器为1000的变量,然后我们在一个组件中使用它们:

两个都可以

<template>
  <div>
    <h2>{
  {$store.state.counter}}</h2> 
    <h2>{
  {counter}}</h2>
  </div>
</template>
<script> export default {
      name: 'HelloVuex', data() {
      return {
      // 这种方式有一个坏处:因为data()是每次vue组件实例化完后返回的,这个counter如果不进行维护,那么只会在实例化组件的时候返回一次,后面如果 store.state.counter变化了,这里是不能及时响应的。 counter: this.$store.state.counter } } } </script>
<style> </style>

npm run dev 运行项目。如果没有运行并报错,多半是版本问题:

如果你的vue版本是2.X,可以通过升级vuex到3.X.X来解决。可以通过命令安装,也可以直接在package.json中修改版本,然后npm install。

npm install --save vuex@3.6.2

如果你的vue版本是3.X,升级vuex到4.X.X就可以解决问题

npm install --save vuex@4.0.0

我们在vuex的state中定义的state在页面上正常显示:

修改

访问是没问题的,如果想在vuex中修改state中的变量怎么办?

也许我们都理想地这样使用它:

App.vue

<template>
  <div id="app">
    <h2>---------------App.vue</h2>
    <h2>{
  {$store.state.counter}}</h2>
    <button @click="$store.state.counter++">+</button>
    <button @click="$store.state.counter--">-</button>
    <h2>---------------HelloVuex</h2>
    <hello-vuex></hello-vuex>
  </div>
</template>
<script> import HelloVuex from './components/HelloVuex' export default {
      name: 'App', components: {
      HelloVuex } } </script>

HelloVuex.vue

<template>
  <div>
    <h2>{
  {$store.state.counter}}</h2>
    <h2>{
  {counter}}</h2>
  </div>
</template>
<script> export default {
      name: 'HelloVuex', data() {
      return {
      counter: this.$store.state.counter } } } </script>

在HelloVuex中,第二个计数器称为data(),然后data()函数中的计数器在实例化的时候只返回一次,以后不再维护,所以一直是store.state。计数器的初始值。

根据vuex中定义的规范,我们不能随意改变vuex中的状态,比如:$store.state.counter++,不符合vuex中的规范。我们需要在 vue 组件中触发事件后提交 Mutations 进行修改。稍后,我们将使用浏览器插件 DevTools。为了让我们使用这个插件可以更好的跟踪谁修改了状态的状态,方便出错和快速定位。修改状态时,必须在Mutations中执行。

我们首先在 Google Chrome 上安装 DevTools。

大部分人的谷歌浏览器应该是打不开谷歌商店的,你可以在线下载。下载极简插件,下载后解压,然后直接将解压后的文件拖入谷歌扩展中。

完成后重启浏览器,再次打开vue程序,选择vue插件的页面。

然后开始定义store中实际对state进行操作的部分,也就是Mutations中的定义:

在定义的方法中

mutations,参数会默认传递一个状态变量,即store中的状态对象。

const store = new Vuex.Store({
   
    state:{
   
        counter: 1000
    },
    mutations:{
   
        increment(state){
   	// state是固定的,要写
            state.counter++;
        },
        decrement(state){
   
            state.counter--;
        }
    }
})

当我们要在组件中修改vuex中的状态时,需要调用上面mutations中定义的规定方法来执行。

<button @click="add()">+</button>
<button @click="minus()">-</button>
<script> export default {
      name: 'App', methods: {
      add(){
      this.$store.commit('increment') }, minus(){
      this.$store.commit('decrement') } } } </script>

调用$store.commit方法,在mutations中传入对应的方法名,直接调用方法操作store中的状态。

Vuex 核心概念

此图说明了vue组件在vuex中访问和修改状态的完整过程。 Actions可以走也可以不走,但一定要记住:修改vuex的状态时,一定要经过Mutations

状态单状态树

单一真理来源:单一真理来源。只创建了一个vuex实例对象,任何对vuex中状态的访问都需要经过this定义的唯一一个vuex实例对象(单一数据源)。方便我们管理和维护。

吸气剂

类似于Vue中的计算属性,计算缓存(获取计算属性后会生成缓存数据)并及时更新数据,但在不同的对象中。

有一个场景:状态中有一个属性定义了一个计数器,但是我在使用的时候想要这个计数器的平方值,所以可以定义一个getter

在定义vuex的index.js文件中

getters:{
   
    powerCounter(state){
   
        return state.counter * state.counter
    }
}

那么怎么称呼呢?

$store.getters.powerCounter 可以直接调用,这也是一个属性

<h2>---------------App.vue counter的平方</h2>
<h2>{
  {$store.getters.powerCounter}}</h2>

getter 中的参数传递:

如果状态中有一个数组vuex存放全局变量,我们要过滤掉数字>20的数据,同样得到数字。我们该怎么做?

const store = new Vuex.Store({
   
    // 保存状态
    state:{
   
        arrs:[1,2,30,40,23,25]
    },
    getters:{
   
        getArr(state){
   
            return state.arrs.filter(x => x > 20)
        },
        // 此方法中 state虽然没有用到,但是一定不能省略,否则就直接报错了
        // 方法中,state和getters可以命名为其他名称,但是实际上第一个参数就是 指的state,第二个参数就是指的 getters引用,与名称无关
        getArrLength(state, getters){
   
            return getters.getArr.length
        }
    }
})

如果我不确定要过滤多少数据怎么办?需要从外界传入一个参数

那么,我们可以:定义一个getter,这个getter不返回属性,而是返回一个函数,可以被外界调用并传入参数

getActiveArrLen(state){
   
    return age => {
   
        return state.arrs.filter(x => x > age)
    }
}

怎么打电话?

过滤掉 > 30 个数据

<h2>{
  {$store.getters.getActiveArrLen(30)}}</h2>

变异带有参数。

当需要在Vuex中改变store状态时,唯一的修改方式是:提交Mutation

变异主要由两部分组成:

调用时传入参数,可以称为mutations的payload

例如

mutatsions:{
   
	// increment就是事件的类型
    // 整个方法体就是一个 回调函数。,而且第一个参数就是 Vuex的 state
    increment(state){
   
        state.counter++
    }
}

由于第一个参数默认是state,如果还有其他参数怎么传呢?

第一个是默认状态,后面是我们需要接收的参数。

const store = new Vuex.Store({
   
    // 保存状态
    state:{
   
        counter: 1000
    },
    mutations:{
   
        // 这里记得接收参数 count
        incrementCount(state, count){
   
            state.counter += count
        }
    }
})

调用时传入参数:

<button @click="addCounter(10)">+10</button>
<script> export default {
      name: 'App', methods: {
      addCounter(count){
      // 调用 commit到mutations中,记得传入参数 this.$store.commit('incrementCount', count) } } } </script>

Payload(负载)也支持传入对象类型的参数:

addCounter(count){
   
	const stu = {
   id:1, name: "wlh"}
    this.$store.commit('incrementCount', stu)
}

变异提交样式

Vuex除了通过代码中的commit方法提交(普通方法)外,还提供了一个对象方法,其中包含要提交的type属性。

addCounter(count){
   
    // 普通提交
    this.$store.commit('incrementCount', count)
    
    // 特殊提交
    this.$store.commit({
   
        type: 'incrementCount',
        count
    })
}

以上两种方式都可以提交,那么这两种提交方式有什么区别呢?打印日志并查看

incrementCount(state, count){
   
    console.log(count)
}

也就是说,通过提交mutations的特殊方式接收到的是一个payload对象,写成payload的时候更容易理解

incrementCount(state, payload){
   
    console.log(payload)
}

突变中的类型常量

在实际开发中,我们在mutations中定义的方法名,以及我们在vue组件中使用commit时的commit的名字,很可能会不小心出现问题,所以我们可以统一管理这些名字。

在store目录下创建一个mutations-types.js文件,里面定义了我们使用的名字:

export const UPDATESTU = 'updateStu'
...

在vue组件中使用:

import {
   UPDATESTU} from './store/mutations-types'	// 导入
export default {
   
  name: 'App',
  methods: {
   
    update(){
   
      this.$store.commit(UPDATESTU)	// 提交时直接使用即可
    }
  }
}

然后在 vuex 突变中也使用相同的常量:

import {
   UPDATESTU} from './mutations-types'
const store = new Vuex.Store({
   
	...
    mutations:{
   
        // 直接定义即可
        [UPDATESTU](state){
   
            
        }
    }
    
})

变异同步函数

通常Vuex要求我们Mutation中的方法必须是同步方法,原因

数据的响应性原则

Vuex 的 state 中的 state 是响应式的。当状态中的数据发生变化时,Vue组件会自动更新,这就需要我们遵守一些Vuex对应的规则:

状态中定义的变量会被添加到响应式系统中,响应式系统会监听属性的变化。当属性发生变化时,会通知界面中所有使用该属性的地方刷新界面。

如果给状态中的对象添加新属性时没有响应,那么我们需要使用上面两种方法来自动刷新。

这里使用的vue2在动态添加属性时无法响应,需要是Vue.set或者其他方法。不过这种情况在高版本的vue中是不存在的,需要注意

const store = new Vuex.Store({
   
    // 保存状态
    state:{
   
        info:{
   
            name:'wlh',
            age: 21
        }
    },
    mutations:{
   
        updateStu(state){
   
            // 动态新增属性,不能做到响应式
            //state.info['address'] = 'sdfjkldsfs'
            // 使用Vue.set,把这个属性增加到 响应式系统中
            Vue.set(state.info, 'address', '北京市')
           
        }
    }
    
})

Vue.delete删除属性,可以响应式

Vue.delete(state.info, 'age')

动作

前面我们提到了mutation中不应该进行异步操作,但是在某些情况下,我们确实想在Vuex中进行一些异步操作,比如网络请求,必须是异步操作。如何处理?

Action 类似于 Mutation,但用于执行异步操作而不是 Mutation。

让我们回忆一下上图:

因为我们要异步操作状态的状态,那么我们需要添加一个Actions链接。首先vue组件dispatch到Actions,Actions去commit到Mutations,然后Mutations修改状态

定义动作:

const store = new Vuex.Store({
   
    mutations:{
   
        updateStu(state){
   
			// 处理
            state.xxx = 'xxx'
        }
    },
    // 定义actions
    actions:{
   
        
        // 需要用到一个 context上下文,这个context就是我们的 store对象
        aUpdateInfo(context){
   
            setTimeout(() => {
   
                
                // 函数回调中, commit到 mutations的方法中,由 mutations中的方法对state进行状态更新
                context.commit('updateStu')
            }, 1000);
        }
    }
    
})

由于定义好的actions已经提交了mutations,那么在vue组件中调用时,就不需要提交mutations了,直接dispatch到actions中的方法。

<button @click="update">修改</button>
<script> export default {
      name: 'App', methods: {
      update(){
      // 调用转发到 指定的actions中 this.$store.dispatch('aUpdateInfo') } } } </script>

与突变类似,动作也可以接收一些参数。传入参数时,支持普通传入和对象传入。

actions:{
   
    aUpdateInfo(context, payload){
   
        setTimeout(() => {
   
            console.log(payload)
            context.commit(UPDATESTU)
        }, 1000);
    }
}

那么当actions中的异步方法执行时,如何给回调呢?

那么我们需要使用之前学过的Promise来包装异步操作,让代码更具可读性。

在actions中访问时,直接返回一个Promise对象,后续回调处理交给调用者处理。

actions:{
   
    aUpdateInfo(context, payload){
   
        return new Promise((resolve, reject) => {
   
            setTimeout(() => {
   
                console.log(payload)
                context.commit(UPDATESTU)
				resolve('1234')
            }, 1000);
        })
    }
}

即当我们执行dispatch时,actions会返回一个Promise对象,然后交给调用者处理回调

methods: {
   
    update(){
   
        this.$store.dispatch('aUpdateInfo', 'payload')
            .then(res => {
   
            console.log('方法回调完毕')
            console.log('参数是' + res)
        })
    }
}

模块

modules 指的是 vuex 对象中的模块。当应用程序变得非常复杂时,商店的状态可能会变得非常臃肿。为了解决这个问题,Vuex 允许我们将 store 划分为模块,每个模块都有自己的 state、mutations、actions 和 getter。等等(简单来说就是类似于树状结构,可以有无限个子集)

定义模块

// 创建对象
const moduleA = {
   
    state: {
   
        name: 'hhh'
    },
    mutations:{
   
        updateName(state, payload){
   
            state.name = payload
        }
    }
}
const store = new Vuex.Store({
   
    modules:{
   
        a: moduleA
    }
})

访问状态

定义了一个名为a的模块,那么如何在vue组件中使用呢?

<h2>{
  {$store.state.a.name}}</h2>

其实我们定义的模块有一个子模块,访问的时候需要从根级父模块的状态中找到。

访问突变

那么如何访问子模块中的突变?

<button @click="updateName">修改名字</button>
<script> export default {
      name: 'App', methods: {
      updateName(){
      this.$store.commit('updateName', 'ls') } } } </script>

如您所见,您可以直接提交。它会先去根级模块找,找不到再去子模块找。所以:不要重复所有模块中定义的突变名称

访问 getter

定义一个getter

const moduleA = {
   
    state: {
   
        name: 'hhh'
    },
    getters:{
   
        fullname(state){
   
            return state.name + '123'
        }
    }
}

访问和以前一样,都是全局的

<h2>{
  {$store.getters.fullname}}</h2>

所以难免会遇到,子模块想要访问根级别的一些状态怎么办?如果根级状态有计数器属性

getters:{
   
    fullname(state, getters, rootState){
   
        return state.name + '123' + rootState.counter
    }
}

前面提到的getter中的参数,第一个是状态,第二个是getter,然后第三个是rootState(随便写的名字),rootState就是根级模块中的状态。注意:mutations 和 getter 是全局的,无论你在其中定义哪个模块

访问操作

动作有点类似于突变和吸气剂

const moduleA = {
   
    state: {
   
        name: 'hhh'
    },
    mutations:{
   
        updateName(state, payload){
   
            state.name = payload
        }
    },
    actions:{
   
        aUpdateName(context){
   
            console.log(context)
            setTimeout(() => {
   
                // 这里的上下文就不是 store对象了,而是当前的一个子模块对象
                context.commit('updateName', 'zs');
            }, 1000);
        }
    }
}

在子模块的actions中,commit执行时,只会提交到自己模块中的mutations

你仍然可以直接访问actions,所以我再次提醒你,mutations、getter和actions中的方法名不要重复。

updateName(){
   
    this.$store.dispatch('aUpdateName')
}

我们来看看打印出来的上下文信息:

是具有getter、rootGetters、rootState、state等属性的对象类型

那么熟悉对象解构的兄弟可能会有一个想法。既然接收到的上下文是对象类型vuex存放全局变量,那我可以直接把他解构成几个变量吗?当然。

// 将context 解构为几个 参数,注意:是按照名称分配的,跟顺序无关
aUpdateName({
    state, commit, rootState}){
   
    
    setTimeout(() => {
   
        commit('updateName', 'zs');
    }, 1000);
}

需要注意的是,有些属性在根级模块中是不可用的,比如:rootState,根级模块本身就是root,它没有前一个父级

店铺目录结构

以上所有代码都写在一个 index.js 文件中。在实际项目中,我们将导入和导出模块来提取它们。让我们的项目结构更加清晰。

提取状态

根目录下的状态可以这样提取:

const state = {
   
    name: 'wlh'
}
const store = new Vuex.Store({
   
    state
})

提取突变

根目录中的变异可以提取为文件,然后导入到文件中。

import {
   UPDATESTU} from './mutations-types'
export default {
   
    [UPDATESTU](state){
   
        state.info.name = "ls"
    }
}

使用导入

import mutations from './mutations'
const store = new Vuex.Store({
   
    mutations
})

action 和 getter 也是如此。

提取模块

在 store 目录下新建一个文件夹 modules。例如,如果有购物车模块,则创建一个购物车.js

export default {
   
    state: {
   
        name: 'hhh'
    },
    mutations,
    getters:{
   
        fullname(state, getters, rootState){
   
            return state.name + '123' + rootState.counter
        }
    },
    actions:{
   
        aUpdateName(context){
   
            console.log(context)
            setTimeout(() => {
   
                context.commit('updateName', 'zs');
            }, 1000);
        }
    }
}

在index.js中直接导入使用

import Cart from './modules/cart'
const store = new Vuex.Store({
   
    modules:{
   
        Cart	
    }
})

Vue入门-基础命令+axios+案例实践Vue入门-Vue常用属性、生命周期、计算属性、过滤器、组件、虚拟DOM、数组的响应式方法、页面闪现、ES6简单语法增强Vue入门- js高阶函数(箭头函数)、v-model数据绑定、组件化、父子组件通信及访问vue介绍——槽(命名、匿名、作用域槽)+ES6模块化导入导出+webpack的使用(基础使用+配置使用+如何一步步演化成cli脚手架)+webpack插件使用(搭建本地服务器,配置文件分离)vue-cli脚手架2版和3版+安装,目录解析,only和compiler的区别, version 3+ 怎么改配置,箭头函数和this的基本使用指向vue-router,路由参数,懒加载,嵌套路由,导航守卫,keep-alive Promise的基本使用,三点ates、chain call和shorthand、所有方法Vuex的作用、用途、核心概念(State、Mutations、Getters、Actions、Modules)、文件提取、axios网络请求的基本使用、配置使用(全局axios和本地axios实例)、模块封装,axios拦截器

© 版权声明
THE END
喜欢就支持一下吧
点赞258赞赏 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片

    暂无评论内容