Vue 的生命周期函数 和 Vuex

创建一个 Vue 实例

每个 Vue 应用都是通过用 Vue 函数创建一个新的 Vue 实例开始的:

var vm = new Vue({// 选项
})

虽然没有完全遵循 MVVM 模型,但是 Vue 的设计也受到了它的启发。因此在文档中经常会使用 vm (ViewModel 的缩写) 这个变量名表示 Vue 实例。

当创建一个 Vue 实例时,你可以传入一个选项对象。这篇教程主要描述的就是如何使用这些选项来创建你想要的行为。作为参考,你也可以在 API 文档中浏览完整的选项列表。

一个 Vue 应用由一个通过 new Vue 创建的根 Vue 实例,以及可选的嵌套的、可复用的组件树组成。举个例子,一个 todo 应用的组件树可以是这样的:

根实例
└─ TodoList├─ TodoItem│  ├─ TodoButtonDelete│  └─ TodoButtonEdit└─ TodoListFooter├─ TodosButtonClear└─ TodoListStatistics

我们会在稍后的组件系统章节具体展开。不过现在,你只需要明白所有的 Vue 组件都是 Vue 实例,并且接受相同的选项对象 (一些根实例特有的选项除外)

数据与方法

一个 Vue 实例被创建时,它将 data 对象中的所有的 property 加入到 Vue 的响应式系统中。当这些 property 的值发生改变时,视图将会产生“响应”,即匹配更新为新的值。

// 我们的数据对象
var data = { a: 1 }// 该对象被加入到一个 Vue 实例中
var vm = new Vue({data: data
})// 获得这个实例上的 property
// 返回源数据中对应的字段
vm.a == data.a // => true// 设置 property 也会影响到原始数据
vm.a = 2
data.a // => 2// ……反之亦然
data.a = 3
vm.a // => 3// 注意只有这些被代理的属性是响应的。如果在实例创建之后添加新的属性到实例上,它不会触发视图更新。

当这些数据改变时,视图会进行重渲染。值得注意的是只有当实例被创建时就已经存在于 data 中的 property 才是响应式的。也就是说如果你添加一个新的 property,比如:

vm.b = 'hi'

那么对 b 的改动将不会触发任何视图的更新。如果你知道你会在晚些时候需要一个 property,但是一开始它为空或不存在,那么你仅需要设置一些初始值。比如:

data: {newTodoText: '',visitCount: 0,hideCompletedTodos: false,todos: [],error: null
}

简单的来讲就是,只有当实例被创建时就已经存在于 data 中的 property 才是响应式的。就是创建时有的数据才会有响应式效果,后面直接强加的property是没用的。如果我们一开始就知道后面需要那些相关数据,在创建Vue之前就要把数据加进去,设置一些初始值即可。

这里唯一的例外是使用 Object.freeze(),这会阻止修改现有的 property,也意味着响应系统无法再追踪变化。

var obj = {foo: 'bar'
}Object.freeze(obj)new Vue({el: '#app',data: obj
})
<div id="app"><p>{{ foo }}</p><!-- 这里的 `foo` 不会更新! --><button v-on:click="foo = 'baz'">Change it</button>
</div>

除了数据 property,Vue 实例还暴露了一些有用的实例 property 与方法。它们都有前缀 $,以便与用户定义的 property 区分开来。例如:

var data = { a: 1 }
var vm = new Vue({el: '#example',data: data
})vm.$data === data // => true
vm.$el === document.getElementById('example') // => true// $watch 是一个实例方法
vm.$watch('a', function (newValue, oldValue) {// 这个回调将在 `vm.a` 改变后调用
})

你可以在 API 参考中查阅到完整的实例 property 和方法的列表。

实例生命周期钩子

每个 Vue 实例在被创建时都要经过一系列的初始化过程——例如,需要设置数据监听、编译模板、将实例挂载到 DOM 并在数据变化时更新 DOM 等。同时在这个过程中也会运行一些叫做生命周期钩子的函数,这给了用户在不同阶段添加自己的代码的机会。

比如 created 钩子可以用来在一个实例被创建之后执行代码:

new Vue({data: {a: 1},created: function () {// `this` 指向 vm 实例console.log('a is: ' + this.a)}
})
// => "a is: 1"

也有一些其它的钩子,在实例生命周期的不同阶段被调用,如 mountedupdateddestroyed。生命周期钩子的 this 上下文指向调用它的 Vue 实例。

不要在选项 property 或回调上使用箭头函数,比如 created: () => console.log(this.a)vm.$watch('a', newValue => this.myMethod())。因为箭头函数并没有 thisthis 会作为变量一直向上级词法作用域查找,直至找到为止,经常导致 Uncaught TypeError: Cannot read property of undefinedUncaught TypeError: this.myMethod is not a function 之类的错误。

生命周期图示

下图展示了实例的生命周期。你不需要立马弄明白所有的东西,不过随着你的不断学习和使用,它的参考价值会越来越高。

Vue 实例生命周期

vue 生命周期原理图 - AcWing

在此步骤页面其实已经可以展示出来了

image.png

vue的生命周期及使用场景

1. 创建前:beforeCreate(){}

在 new 一个 vue 实例后,只有一些默认的生命周期钩子和默认事件,其他东西还都没有创建,此时还访问不到data 中的属性以及 methods 中的属性和方法,不能再这个阶段使用 data 中的数据和 methods 中的方法,我们可以在当前生命周期创建一个 loading 事件,在页面加载完成之后将loading 移除。

2. 创建完成:created(){}

created [kriˈeɪtɪd]

datamethods 都以将被初始化好了,如果要调用 methods 中的方法,或者操作 data 中的内容,最早可以在这个阶段中操作

当前生命周期执行的时候会遍历data中所有的属性,给每一个属性都添加一个 getter、setter 方法,将data中的属性变成一个 响应式属性 ;当前生命周期执行的时候会遍历 data&&methods 身上所有的属性和方法,将这些属性和方法代理到vue的实例身上,在当前生命周期中我们可以进行 前后端数据的交互 (注:在vue项目中,我们在进行前后端交互时一般不用ajax,因为我们只是想单纯的使用ajax,但是却要引入整个jQuery,在一定程度上降低了性能,再者vue本身设计就是尽量减少DOM元素的操作,但jQuery却是注重DOM元素的操作,这就有点不合理,所以,一般用axios)。

拓展:axios与ajax的区别及优缺点

区别:axios是通过Promise实现对ajax技术的一种封装,就像jquery对ajax的封装一样,简单来说就是ajax技术实现了局部数据的刷新,axios实现了对ajax的封装,axios有的ajax都有,ajax有的axios不一定有,总结一句话就是 axios是ajax,ajax不止axios
ajax:
1、本身是针对MVC编程,不符合前端MVVM的浪潮;
2、基于原生XHR开发,XHR本身的架构不清晰,已经有了fetch的替代方案,jquery整个项目太大,单纯使用ajax却要引入整个jquery非常不合理(采取个性化打包方案又不能享受cdn服务);
3、ajax不支持浏览器的back按钮;
4、安全问题ajax暴露了与服务器交互的细节;
5、对搜索引擎的支持比较弱;
6、破坏程序的异常机制;
7、不容易调试。
axios:
1、从node.js创建http请求;
2、支持Promise API;
3、客户端防止CSRF(网站恶意利用);
4、自动转化json数据;
5、提供了一些并发请求的接口。

小测试

到这里我们还不能操作 dom,在此我们可以做个小测试,给 p 绑定 ref

image.png

我们运行会发现,他会报错,告诉你 style 是underfunded

image.png

3. 挂载前:beforeMount(){}

mount [maʊnt]

执行这个钩子的时候你在内存中已经编译好了模板,模板与数据进行结合,但是还没有挂载到页面上,因此我们可以在当前生命周期中进行数据的修改。

注意:如果有el则直接用el的挂载点,如果没有则用mount挂载点

4. 挂载完成:mounted(){}

mounted ['maʊntɪd]

执行到这个钩子的时候,表示Vue实例已经初始化完成了。此时组件脱离了创建阶段,进入到了运行阶段。如果我们想要通过插件操作页面上的DOM节点,最早可以在这个阶段中执行

当前生命周期数据和模板进行相结合,并且已经挂载到页面上了,因此我们可以在当前生命周期中获取到真实的DOM元素,还可以在当前生命周期中做方法的实例化,给元素添加一个ref属性,且值必唯一,通过:this.$refs.属性获取DOM元素。

那之前那个小测试来验证

image.png

image.png

当我们把 this.$refs.p1.style.color = 'red' 放到 mounted 里时,从结果可看出,他是可以操作 dom 节点的

5. 更新前:beforeUpdate(){}

当执行这个钩子的时,页面中的显示的数据还是旧的,data中的数据是更新后的,页面还没有和最新的数据保持同步

当数据发生改变的时候当前生命周期就会执行,因此我们可以通过当前生命周期来检测数据的变化,当前生命周期执行的时候会将更新的数据与模板进行相结合,但是并没有挂载到页面上,因此我们可以在当前生命周期中做更新数据的最后修改。

6. 更新完成:updated(){}

页面显示的数据和data中的数据已经保持同步,都是最新的

数据与模板进行相结合,并且将更新后的数据挂载到了页面上。因此我们可以在当前生命周期中获取到最新的DOM结构(注:在当前生命周期中如果做实例化操作的时候须做条件判断)。

这又是一个小测试

image.png

我们可以看到他的先后顺序

image.png

image.png

7. 销毁前:beforeDestroy(){}

当前生命周期中我们需要做事件的解绑,监听的移除,定时器的清除等操作。

这个时间上所有的 data 和 methods,指令,过滤器。。。。处于可用状态。还没有真正被销毁

8. 销毁完成:destroyed(){}

destroy [dɪ’strɔɪ]

当前生命周期中我们需要做事件的解绑,监听的移除,定时器的清除等操作。(注:被keep-alive包裹的组件,不会被销毁,而是被缓存起来)。

这个时候上所有的data 和 methods ,指令 ,过滤器。。。。。。都是出于不可用状态。组件已经被销毁了

第一次页面加载会触发beforeCreate、created、beforeMount、mounted, mounted说明dom渲染完毕

Vue生命周期在真实场景下的业务应用

created:进行ajax请求异步数据的获取、初始化数据

mounted:挂载元素dom节点的获取

nextTick:针对单一事件更新数据后立即操作dom

updated:任何数据的更新,如果要做统一的业务逻辑处理

watch:监听数据变化,并做相应的处理

template

  • template的作用是模板占位符,可帮助我们包裹元素,但在循环过程当中,template不会被渲染到页面上
  • template中可以调用简单的可以直接返回的一元运算符,并且能直接返回数据的,也可以调用js的全局方法比如,Date.now(),但是不能调用自己在全局定义的变量
  • 如果有template,则会把template里面的内容当作渲染的模板,如果没有template,则会渲染挂载点为 app的模板

Vuex

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。Vuex 也集成到 Vue 的官方调试工具 devtools extension (opens new window),提供了诸如零配置的 time-travel 调试、状态快照导入导出等高级调试功能。

了解Vuex

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状 态,并以相应的规则保证状态以一种可预测的方式发生变化

使用Vuex管理数据的好处:
  • 能够在 vuex 中集中管理共享的数据,便于开发和后期进行维护
  • 能够高效的实现组件之间的数据共享,提高开发效率
  • 存储在 vuex 中的数据是响应式的,当数据发生改变时,页面中的数据也会同步更新
什么是“状态管理模式”?

让我们从一个简单的 Vue 计数应用开始:

new Vue({// statedata () {return {count: 0}},// viewtemplate: `<div>{{ count }}</div>`,// actionsmethods: {increment () {this.count++}}
})

这个状态自管理应用包含以下几个部分:

  • state,驱动应用的数据源;
  • view,以声明方式将 state 映射到视图;
  • actions,响应在 view 上的用户输入导致的状态变化。

以下是一个表示“单向数据流”理念的简单示意:

img

但是,当我们的应用遇到多个组件共享状态时,单向数据流的简洁性很容易被破坏:

  • 多个视图依赖于同一状态。
  • 来自不同视图的行为需要变更同一状态。

对于问题一,传参的方法对于多层嵌套的组件将会非常繁琐,并且对于兄弟组件间的状态传递无能为力。对于问题二,我们经常会采用父子组件直接引用或者通过事件来变更和同步状态的多份拷贝。以上的这些模式非常脆弱,通常会导致无法维护的代码。

因此,我们为什么不把组件的共享状态抽取出来,以一个全局单例模式管理呢?在这种模式下,我们的组件树构成了一个巨大的“视图”,不管在树的哪个位置,任何组件都能获取状态或者触发行为!

通过定义和隔离状态管理中的各种概念并通过强制规则维持视图和状态间的独立性,我们的代码将会变得更结构化且易维护。

这就是 Vuex 背后的基本思想,借鉴了 Flux (opens new window)、Redux (opens new window)和 The Elm Architecture (opens new window)。与其他模式不同的是,Vuex 是专门为 Vue.js 设计的状态管理库,以利用 Vue.js 的细粒度数据响应机制来进行高效的状态更新。

如果你想交互式地学习 Vuex,可以看这个 Scrimba 上的 Vuex 课程 (opens new window),它将录屏和代码试验场混合在了一起,你可以随时暂停并尝试。

vuex

什么情况下我应该使用 Vuex?

Vuex 可以帮助我们管理共享状态,并附带了更多的概念和框架。这需要对短期和长期效益进行权衡。

如果您不打算开发 大型单页应用 ,使用 Vuex 可能是繁琐冗余的。确实是如此——如果您的应用够简单,您最好不要使用 Vuex。一个简单的 store 模式 (opens new window)就足够您所需了。但是,如果您需要构建一个中大型单页应用,您很可能会考虑如何更好地在组件外部管理状态,Vuex 将会成为自然而然的选择。引用 Redux 的作者 Dan Abramov 的话说就是:

Flux 架构就像眼镜:您自会知道什么时候需要它。

简单组件传值

传统组件传值方式及缺点:

传统通过输入输出的方式传值

props:输入
$emit:输出

输入输出机制的缺点:页面复杂时,经常需要层层传递数据,因为非父子组件间的交互,只能寻找最近的祖先组件来做中转

同时,输入输出机制还有一个局限性:当不同页面的组件需要进行数据交互时,它就无能为力了。平常开发,这种输入输出的方案也基本满足了
但如果有需要跨页面的数据交互或者说,有需要数据做持久化处理的场景时;以及如果页面组件层次复杂,存在 props 和 $emit 层层传递时。这种方案其实是不好的。

计数器案例

下列代码演示一个计数器案例,此案例演示的时兄弟组件传值的问题

初始化项目

直接使用脚手架创建项目即可,安装的时候可以选择使用 vuex,也可以以后再安装

为了更加明了,我们先创建一个不带 vuex的项目 vuex-demo

以传统方式操作公共数据

增加页面(Addtion.vue):

<template><div><p>当前count变量的值是:{{ count }}</p><button @click="add">+1</button></div>
</template>
<script>
export default {props: {count: Number},methods: {add () {this.$emit('addCount')}}
}
</script>

减少页面(Subtraction.vue):

<template><div><p>当前count变量的值是:{{ count }}</p><button @click="sub">-1</button></div>
</template>
<script>
export default {props: {count: Number},methods: {sub () {this.$emit('subCount')}}
}
</script>

app.vue(公共文件)

<template><div id="app"><my-add :count="count" @addCount="handleChangeAdd"></my-add><my-sub :count="count" @subCount="handleChangeSub"></my-sub></div>
</template><script>
import Addtion from './components/Addtion.vue'
import Subtraction from './components/Subtraction.vue'export default {data () {return {count: 10}},name: 'App',components: {'my-add': Addtion,'my-sub': Subtraction},methods: {handleChangeAdd () {this.count++},handleChangeSub () {this.count--}}
}
</script>
使用 vuex 改造

创建 store 文件夹,在其中创建index.js 用来作为 vuex的仓库

首先安装 vuex
npm install vuex --save
对其进行配置

image.png

在使用vuex的方法时,我们可以不用再main.js对其进行操作,我们把它放在 store 文件夹下index.js里

使用vuex里面的方法进行改造

增加页面(Addtion.vue):

<template><div><p>当前count变量的值是:{{ $store.state.count }}</p><button @click="add">+1</button></div>
</template>
<script>
export default {methods: {add () {this.$store.commit('addCount')}}
}
</script>

减少页面(Subtraction.vue):

<template><div><p>当前count变量的值是:{{ $store.state.count }}</p><button @click="sub">-1</button></div>
</template>
<script>
export default {methods: {sub () {this.$store.commit('subCount')}}
}
</script>

app.vue(公共文件)

<template><div id="app"><my-add :count="count"></my-add><my-sub :count="count"></my-sub></div>
</template><script>
import Addtion from './components/Addtion.vue'
import Subtraction from './components/Subtraction.vue'export default {data () {return {count: 10}},name: 'App',components: {'my-add': Addtion,'my-sub': Subtraction}
}
</script>

store文件夹下的 index.js文件

import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)const store = new Vuex.Store({state: {count: 10},mutations: {addCount (state) {state.count++},subCount (state) {state.count--}}
})
export default store

任务单2.gif

安装

直接下载 / CDN 引用

https://unpkg.com/vuex(opens new window)

Unpkg.com (opens new window)提供了基于 NPM 的 CDN 链接。以上的链接会一直指向 NPM 上发布的最新版本。您也可以通过 https://unpkg.com/vuex@2.0.0 这样的方式指定特定的版本。

在 Vue 之后引入 vuex 会进行自动安装:

<script src="/path/to/vue.js"></script>
<script src="/path/to/vuex.js"></script>
NPM
npm install vuex --save
Yarn
yarn add vuex

在一个模块化的打包系统中,您必须显式地通过 Vue.use() 来安装 Vuex:

import Vue from 'vue'
import Vuex from 'vuex'Vue.use(Vuex)

当使用全局 script 标签引用 Vuex 时,不需要以上安装过程。

Promise

Vuex 依赖 Promise (opens new window)。如果你支持的浏览器并没有实现 Promise (比如 IE),那么你可以使用一个 polyfill 的库,例如 es6-promise (opens new window)。

你可以通过 CDN 将其引入:

<script src="https://cdn.jsdelivr.net/npm/es6-promise@4/dist/es6-promise.auto.js"></script>

然后 window.Promise 会自动可用。

如果你喜欢使用诸如 npm 或 Yarn 等包管理器,可以按照下列方式执行安装:

npm install es6-promise --save # npm
yarn add es6-promise # Yarn

或者更进一步,将下列代码添加到你使用 Vuex 之前的一个地方:

import 'es6-promise/auto'
自己构建

如果需要使用 dev 分支下的最新版本,您可以直接从 GitHub 上克隆代码并自己构建。

git clone https://github.com/vuejs/vuex.git node_modules/vuex
cd node_modules/vuex
npm install
npm run build

开始使用 Vuex,把状态拿到应用外部管理,Vuex 管这个管理状态的玩意叫 Store,一个完全独立的应用,他只负责状态管理。尝试把 Vuex 应用和 Vue 应用划清界限,

一个 Vuex 应用,做状态管理,可以理解是 Model 层一个 Vue 应用,仅负责数据展示,纯纯的 View 层

Vuex 应用

所谓状态管理,无非就是定义状态,修改状态

开始

在 Scrimba 上尝试这节课

每一个 Vuex 应用的核心就是 store(仓库)。“store”基本上就是一个容器,它包含着你的应用中大部分的状态 (state)。Vuex 和单纯的全局对象有以下两点不同:

  1. Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。
  2. 你不能直接改变 store 中的状态。改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation。这样使得我们可以方便地跟踪每一个状态的变化,从而让我们能够实现一些工具帮助我们更好地了解我们的应用。
Store
Store与全局对象的区别
  1. vuex的状态存储是响应式的。当vue组件从store中读取状态时,若store中的状态发生变化,那么相应的组件也会得到更新。
  2. 不能直接改变store中的状态。改变store中状态的唯一途径就是显式地提交mutation
Store中state的获取和修改
  • 可以通过直接调用store.state来获取。
  • 只能通过store.commit来提交mutation的方式修改store中的state值。
State
定义 state

在 Vuex 里定义状态,我们需要 new 一个 Store 出来,每一个 Vuex 应用的核心就是 store(仓库)。

// store.jsimport Vue from "vue";import Vuex from "vuex";
Vue.use(Vuex);
const store = new Vuex.Store({  state: {    count: 0  }});
export default store;

以上代码创建了一个 store,store.state 里定义了状态。与在 Vue 里定义 data 没有任何区别,

下面修改状态。

直接修改

如果你想快点用上 Vuex,你可以在组件里直接修改 store 里的 state,(直接修改的意思就是,用点操作修改)

state.count = 2;

虽然这样可以正常工作,但在严格模式下会报错,更改 store 中的状态的唯一方法应该是提交 mutation

严格模式

开启严格模式,仅需在创建 store 的时候传入 strict: true

const store = new Vuex.Store({  strict: true});

在严格模式下,无论何时发生了状态变更且不是由 mutation 函数引起的,将会抛出错误。

单一状态树

vuex使用单一状态树,即用一个对象就包含了全部的应用层级状态。至此它便作为一个“唯一数据源”而存在。这也就意味着每个应用将仅仅包含一个store实例。

在vue组件中获取vuex状态

最简单的做法就是在计算属性中返回某个状态:

return store.state.count

但是这种做法的缺点就是:在模块化的构建系统中,在每个需要使用state的组件中需要频繁地导入。为此,Vuex 通过 store 选项,提供了一种机制将状态从根组件“注入”到每一个子组件中(需调用 Vue.use(Vuex)):

const app = new Vue({el: '#app',// 把 store 对象提供给 “store” 选项,这可以把 store 的实例注入所有的子组件store,components: { Counter },template: `<div class="app"><counter></counter></div>`
})

通过在根实例中注册 store 选项,该 store 实例会注入到根组件下的所有子组件中,且子组件能通过 this.$store 访问到。

mapState辅助函数

当一个组件需要获取多个状态的时候,将这些状态都声明为计算属性会有些重复和冗余。为了解决这个问题,我们可以使用mapState辅助函数帮助我们生成计算属性

/ 在单独构建的版本中辅助函数为 Vuex.mapState
import { mapState } from 'vuex'export default {// ...computed: mapState({// 箭头函数可使代码更简练count: state => state.count,// 传字符串参数 'count' 等同于 `state => state.count`countAlias: 'count',// 为了能够使用 `this` 获取局部状态,必须使用常规函数countPlusLocalState (state) {return state.count + this.localCount}})
}
对象展开运算符

mapState函数返回的是一个对象。要使它能与其他的局部计算属性混合使用,需要用到对象展开运算符(...):

computed: {localComputed () { /* ... */ },// 使用对象展开运算符将此对象混入到外部对象中...mapState({// ...})
}
Getter

Vuex 允许我们在 store 中定义“getter”(可以认为是 store 的计算属性)。就像计算属性一样,getter 的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算。
Getter 接受 state 作为其第一个参数:

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)}}
})

可以使用store.getters来调用getter。
你也可以通过让 getter 返回一个函数,来实现给 getter 传参。在你对 store 里的数组进行查询时非常有用。

getters: {// ...getTodoById: (state) => (id) => {return state.todos.find(todo => todo.id === id)}
}
store.getters.getTodoById(2) // -> { id: 2, text: '...', done: false }
mapGetters 辅助函数

mapGetters 辅助函数仅仅是将 store 中的 getter 映射到局部计算属性,与mapState用法类似。

Mutation

更改 Vuex 的 store 中的状态的唯一方法是提交 mutation。

Vuex.Store的构造器选项中,有一个 mutation 选项,这个选项就像是事件注册:key 是一个字符串表示 mutation 的类型(type),value 是一个回调函数(handler),

这个回调函数就是我们实际进行状态更改的地方,它有两个入参,第一个参数是 state,就是 store 里的 state;第二个参数是 Payload,这是提交 mutation 时候额外传入的参数。

要唤醒一个 mutation handler,你需要以相应的 type 调用 store.commit 方法:

store.commit('increment')

补充 :commit 参数是对象

当 commit 的参数是一个对象的时候,对象里必须要有 type 字段,除了 type 字段,你还可以添加额外任意字段为载荷(payload)。

// 对象风格的提交方式 
store.commit({  type: "increment",amount: 10
});

type 做第一参数

把 type 和 payload 分开也是个不错的选择,可以把 type 单独拿出来当第一个参数,以commit(type,[payload])的形式提交,官方称此为以载荷形式提交

store.commit("increment");
store.commit("increment", 10);
store.commit("increment", { count: 2 });

在大多数情况下,payload 应该是一个对象,这样可以包含多个字段并且记录的 mutation 会更易读。

到这里我们已经知道如何定义 mutation 和提交 mutation 了,commit 接口很简单,但在哪里使用呢?有两个地方用

Vue 应用的组件里,在组件里调用store.commit。还有 store 的 action 里。

提交载荷(Payload)

你可以向store.commit传入额外的参数,即 mutation 的 载荷(payload),在大多数情况下,载荷应该是一个对象,这样可以包含多个字段并且记录的 mutation 会更易读。

对象风格的提交方式

提交 mutation 的另一种方式是直接使用包含 type 属性的对象:

store.commit({type: 'increment',amount: 10
})
Mutation 需遵守 Vue 的响应规则

Vuex 的 store 中的状态是响应式的,那么当我们变更状态时,监视状态的 Vue 组件也会自动更新。这也意味着 Vuex 中的 mutation 也需要与使用 Vue 一样遵守一些注意事项:

  1. 最好提前在你的 store 中初始化好所有所需属性。
  2. 当需要在对象上添加新属性时,你应该
    • 使用Vue.set(obj, 'newProp', 123), 或者
    • 以新对象替换老对象。例如,利用 stage-3 的对象展开运算符我们可以这样写:
 state.obj = { ...state.obj, newProp: 123 }
Mutation 必须是同步函数

一条重要的原则就是要记住 mutation 必须是同步函数。如果使用的是异步函数,那么任何在回调函数中进行的的状态的改变都是不可追踪的。

在组件中提交 Mutation

你可以在组件中使用this.$store.commit('xxx')提交 mutation,或者使用mapMutations辅助函数将组件中的 methods 映射为store.commit调用(需要在根节点注入store)。

import { mapMutations } from 'vuex'export default {// ...methods: {...mapMutations(['increment', // 将 `this.increment()` 映射为 `this.$store.commit('increment')`// `mapMutations` 也支持载荷:'incrementBy' // 将 `this.incrementBy(amount)` 映射为 `this.$store.commit('incrementBy', amount)`]),...mapMutations({add: 'increment' // 将 `this.add()` 映射为 `this.$store.commit('increment')`})}
}
Action

Action 类似于 mutation,不同在于:

  • Action 提交的是 mutation,而不是直接变更状态。
  • Action 可以包含任意异步操作。
const store = new Vuex.Store({state: {count: 0},mutations: {increment (state) {state.count++}},actions: {increment (context) {context.commit('increment')}}
})

Action 函数接受一个与 store 实例具有相同方法和属性的 context 对象,因此你可以调用context.commit提交一个 mutation,或者通过context.statecontext.getters来获取 state 和 getters。
实践中,我们会经常用到 ES2015 的 参数解构 来简化代码(特别是我们需要调用 commit 很多次的时候):

actions: {increment ({ commit }) {commit('increment')}
}

上面的例子向函数传入一个用{}包围的对象,在ES6语法中将会进行解构,能够直接在函数体中使用对象属性名。

同步和异步

在 Vuex 中,mutation 都是同步事务,在 mutation 中混合异步调用会导致你的程序很难调试。

例如,当你调用了两个包含异步回调的 mutation 来改变状态,你怎么知道什么时候回调和哪个先回调呢?这就是为什么我们要区分这两个概念。

Action 确实和 mutation 很类似,不同在于:

Action 提交的是 mutation,而不是直接变更状态。Action 可以包含任意异步操作。

注册 action:

注册 action 就跟定义 mutation 一样,除了 handler 的入参不同。

Action 函数的入参是一个与 store 实例具有相同方法和属性的 context 对象。因此可以

context.commit 提交一个 mutation,context.state获取 state。context.getters 获取 getters。

const store = new Vuex.Store({  state: {count: 0  },  mutations: {  increment(state) {  state.count++;   }  },  actions: {    increment(context) {context.commit("increment");    }  }
});
分发Action

Action 通过store.dispatch方法触发:

store.dispatch('increment')
组合Action

Action 通常是异步的,那么如何知道 action 什么时候结束呢?首先,你需要明白 store.dispatch 可以处理被触发的 action 的处理函数返回的 Promise,并且store.dispatch仍旧返回 Promise:

actions: {actionA ({ commit }) {return new Promise((resolve, reject) => {setTimeout(() => {commit('someMutation')resolve()}, 1000)})}
}
Module

由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。为了解决以上问题,Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割:

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 的状态
store.state.b // -> moduleB 的状态

项目结构

Vuex 并不限制你的代码结构。但是,它规定了一些需要遵守的规则:

  1. 应用层级的状态应该集中到单个 store 对象中。
  2. 提交 mutation 是更改状态的唯一方法,并且这个过程是同步的。
  3. 异步逻辑都应该封装到 action 里面。
    下面是项目结构示例:
├── index.html
├── main.js
├── api
│   └── ... # 抽取出API请求
├── components
│   ├── App.vue
│   └── ...
└── store├── index.js          # 我们组装模块并导出 store 的地方├── actions.js        # 根级别的 action├── mutations.js      # 根级别的 mutation└── modules├── cart.js       # 购物车模块└── products.js   # 产品模块

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/470940.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

使用etl工具kettle的日常踩坑梳理之二、从Hadoop中导出数据

想操作MySQL等关系型数据库的可以访问我上一篇文章&#xff0c;本章主要介绍操作Hadoop等大数据组件。 根据2024年11月份测试了kettle工具在9.3及以上版本已经没有内置连接大数据(如Hadoop)组件了。 建议安装9.2及以下的&#xff0c;我这里送上8.3.0版本的请用百度网盘下载链…

新版 idea 编写 idea 插件时,启动出现 ClassNotFound

IntelliJ IDEA 2024.1.6 (Ultimate Edition) Build #IU-241.19072.14, built on August 8, 2024 Licensed to Sophia Tout Subscription is active until June 29, 2025. For educational use only. Runtime version: 17.0.111-b1207.30 amd64 Kotlin: 241.19072.14-IJ 新版本…

Java面向对象编程进阶之包装类

Java面向对象编程进阶之包装类 一、为什么要使用包装类二、掌握基本数据类型与包装类之间的转换1、为什么需要转换&#xff1f;2、如何转换&#xff1f; 三、String与基本数据类型、包装类之间的转换1、案例2、特别注意 一、为什么要使用包装类 为了使得基本类型的数据变量具备…

【mysql】使用宝塔面板在云服务器上安装MySQL数据库并实现远程连接

前言 使用宝塔Linux面板安装MySQL数据库并实现远程连接 使用宝塔面板安装mysql 宝塔面板&#xff0c;华为云开放3306端口 一些命令 // 命令行连接数据库 mysql -uroot -p // MySQL 5 版本 GRANT ALL ON *.* TO root% IDENTIFIED BY 替换成你的root密码 WITH GRANT OPTION; // …

性能测试|JMeter接口与性能测试项目

前言 在软件开发和运维过程中&#xff0c;接口性能测试是一项至关重要的工作。JMeter作为一款开源的Java应用&#xff0c;被广泛用于进行各种性能测试&#xff0c;包括接口性能测试。本文将详细介绍如何使用JMeter进行接口性能测试的过程和步骤。 JMeter是Apache组织开发的基…

OpenGL ES 共享上下文实现多线程渲染

OpenGL ES 共享上下文时,可以共享哪些资源? 共享上下文实现多线程渲染 EGL 概念回顾 EGL 是 OpenGL ES 和本地窗口系统(Native Window System)之间的通信接口,它的主要作用: 与设备的原生窗口系统通信; 查询绘图表面的可用类型和配置; 创建绘图表面; 在OpenGL ES 和…

应用于新能源汽车NCV4275CDT50RKG车规级LDO线性电压调节器芯片

关于车规级芯片&#xff08;Automotive Grade Chip&#xff09;&#xff0c;车规级芯片是专门用于汽车行业的芯片&#xff0c;具有高可靠性、高稳定性和低功耗等特点&#xff0c;以满足汽车电子系统的严格要求。这些芯片通常用于车载电子控制单元&#xff08;ECU&#xff09;和…

MQTT协议解析 : 物联网领域的最佳选择

1. MQTT协议概述 1.1 MQTT协议是什么 MQTT : Message Queuing Telemetry Transport 模式 : 发布 / 订阅主题优点 : 代码量小、低带宽、实时可靠应用 : 物联网、小型设备、移动应用MQTT 常用端口 : 1883 MQTT是一个网络协议&#xff0c;和HTTP类似&#xff0c;因为轻量简单&…

【OH】openHarmony开发环境搭建(基于windows子系统WSL)

前言 本文主要介绍基于windows子系统WSL搭建openHarmony开发环境。 WSL与Vmware虚拟机的区别&#xff0c;可以查看WSL与虚拟机的区别 更详细的安装配置过程可参考微软官网&#xff1a; ​安装 WSL 前提 以下基于windows 111专业版进行配置&#xff0c;windows 10应该也是可以…

豆瓣均分9:不容错过的9本大模型入门宝藏书籍,非常详细收藏我这一篇就够了

在这个大模型风起云涌的时代&#xff0c;技术的边界被不断拓宽&#xff0c;AI的力量正以前所未有的方式重塑我们的世界。如果你渴望站在技术的浪尖&#xff0c;深入了解增强现实&#xff08;AR&#xff09;、机器学习&#xff08;ML&#xff09;与强化学习&#xff08;RL&#…

OCR识别铁路电子客票

随着中国铁路客运领域进入全面数字化时代&#xff0c;国家税务总局、财政部和国铁集团于2024年10月18日联合发布公告&#xff0c;自2024年11月1日起&#xff0c;推广使用“电子发票&#xff08;铁路电子客票&#xff09;”。这一举措不仅为旅客出行提供了极大的便利&#xff0c…

【学习】Fine-tuning知识汇总

Fine-tuning 微调&#xff08;Fine-tuning&#xff09;是一种迁移学习的方法&#xff0c;用于在一个预训练模型的基础上&#xff0c;通过在特定任务的数据上进行有监督训练&#xff0c;来适应该任务的要求并提高模型性能。微调利用了预训练模型在大规模通用数据上学习到的语言…

qt QKeySequence详解

1、概述 QKeySequence 是 Qt 框架中的一个类&#xff0c;用于表示和处理键盘快捷键序列。它提供了一种方便的方式来解析、存储和比较键盘快捷键&#xff0c;这些快捷键通常用于触发应用程序中的特定操作或命令。QKeySequence 支持多种格式的快捷键表示&#xff0c;包括单个按键…

【MySQL】约束

4. 约束 4.1 概述 概念&#xff1a;约束是作用于表中字段上的规则&#xff0c;用于限制存储在表中的数据。 目的&#xff1a;保证数据库中数据的正确、有效性和完整性。 注意&#xff1a;约束是作用于表中字段上的&#xff0c;可以在创建表/修改表的时候添加约束。 4.2 约束…

css:盒子模型

目录 盒子模型 边框&#xff08;border&#xff09; 内边距&#xff08;padding&#xff09; 外边距&#xff08;margin&#xff09; 盒子模型&#xff0c;浮动&#xff0c;定位 把一只大象塞进冰箱里需要三步&#xff1a;打开冰箱门&#xff0c;把大象塞进去&#xff0c;…

【最新版】Stable Diffusion4.9(AI绘画)下载及安装教程(附软件安装包)!

NO.1 软件下载 软件名称&#xff1a;Stable Diffusion4.9&#xff08;SD&#xff09;软件语言&#xff1a;中文软件大小&#xff1a;9.6G系统要求&#xff1a;Windows10或更高&#xff0c;64位操作系统 NO.2 软件介绍 Stable Diffusion Stable Diffusion是一款前沿AI绘画工…

微服务(二)

目录 1.网关路由 1.1.认识网关 1.2.快速入门 1.2.1.引入依赖 1.2.2.启动类 1.2.3.配置路由 1.3.路由过滤 2.网关登录校验 2.1.鉴权思路分析 2.2.网关过滤器 2.3.自定义过滤器 2.3.1.自定义GatewayFilter 2.3.2.自定义GlobalFilter 2.4.登录校验 2.4.1.JWT工具 …

uniCloud云对象调用第三方接口,根据IP获取用户归属地的免费API接口,亲测可用

需求 在2022年5月初&#xff0c;网络上各大平台上&#xff0c;都开始展示用户IP属地&#xff0c;在某音、某手等小视频平台以及各主流网站应用中&#xff0c;都展示IP归属地&#xff0c;如下图所示&#xff1a; 解决办法 收费文档的肯定有很多&#xff0c;基本你百度搜“归…

基于标签相关性的多标签学习

✨✨ 欢迎大家来访Srlua的博文&#xff08;づ&#xffe3;3&#xffe3;&#xff09;づ╭❤&#xff5e;✨✨ &#x1f31f;&#x1f31f; 欢迎各位亲爱的读者&#xff0c;感谢你们抽出宝贵的时间来阅读我的文章。 我是Srlua小谢&#xff0c;在这里我会分享我的知识和经验。&am…

C++中的栈(Stack)和堆(Heap)

在C中&#xff0c;堆&#xff08;heap&#xff09;和栈&#xff08;stack&#xff09;是两种用于存储数据的内存区域。理解它们的原理和区别&#xff0c;对于优化代码性能和确保代码的安全性至关重要。以下是对C中堆栈的详细解析&#xff0c;包括它们的分配方式、优缺点、应用场…