上面的三种方式 只适合在小范围的数据之间传递。如果要大范围进行数据的传递或者说频繁的进行数据传递,使用这三种方式就会力不从心
如果不适用vuex 实现数据状态的管理的话,一个数据要想传递给另外一个不相干的组件的时候,就必须一层一层的进行传递,或者往上或者往下。形成链条的形式。给他的父组件,父组件再传递给他的父组件。我只是想传递给其中的一个组件,但是这样的方式,就会造成,链条上的很多不相干的组件都会被影响到
vuex就相当于在所有的组件旁边,定义了一个数据共享对象。这个对象就叫store
,store
里面就存储着我们班要共享的数据。这样的话,组件a只需要将数据共享到store
中,其他想要使用a组件中的数据的话,就可以从store
对象中直接拿到数据,不需要进行传递
一般情况下,只有组件与组件之间共享的 数据,才有必要存储到vuex中
但是在实际开发中,如果想把数据都存储到vuex中,也是可以的
npm install vuex --save
import Vuex from 'vuex'
Vuex.use(Vuex)
const store = new Vuex.Store({
//state 中存放的就是全局共享的数据
state:{ count:0 }
})
new Vue({
el:'#app',
render:h => h(app),// 渲染app根组件
router,
// 将创建的共享数据对象,挂载到Vue 实例中
// 所有的组件 就可以直接从store中获取全局的数据了
store
})
store.js 就是用来初始化vuex的。在state中定义我们需要在全局共享的数据
打开main.js文件,导入 import store from './store'
导入store
并且在main.js
文件中new 一个vue实例,将store
挂载到vue实例中去
new Vue({
store,
render:h => h(App)
}).$mount('#app')
这样的话,每一个组件都可以放到到在全局挂载的store中的数据了
新建两个组件,在App.vue文件中导入
import Addition from "./components/Addition.vue";
import Subtraction from "./components/Subtraction.vue";
新起一个名字,一般都设置成-
形式的名字
components:{
"my-addition":Addition;
"my-subtraction":Subtraction
}
在项目根目录新建文件 .prettierrc
来格式化我们的代码
{
"semi":false,
"singleQuote":true
}
格式化文件 使用Prettier-Code formatter
来格式化
state提供唯一的公共数据源,所有共享的数据都要统一放到Store的State中进行存储
this.$store.state.全局数据名称// 如:this.$store.state.count
在组件中 template结构中 this 是可以省略的
{{$store.state.count}}
// 1.先从vuex中按需导入mapState函数
import { mapState } from 'vuex'
// 2.然后通过刚才导入的mapState函数,将当前组件需要的全局数据,映射为当前组件的computed计算属性
computed:{ ...mapState(['全局数据名称']) }
//比如说我们在某个组件中需要用到全局的 count数据了。可以在当前组件中华先定义一个computed属性,在计算属性中,调用mapState函数,需要用到哪些全局的数据了,就在mapState函数中做一下声明
//...展开运算符,将全局的数据映射为当前组件的一个计算属性。可以认为使用了...展开运算符,当前的count属性就是我当前组件的一个计算属性。
//然后再html结构中。直接把 count数据用 {{count}} 就可以了
现在的要求是,在加法或者减法组件中分别有一个按钮,点击,就将当前的count(vuex中全数据count加一或者减一)。最平常的思路,就是在加法组件或者减法组件中直接访问到全局对象store中的count中。通过this.$srore.state.count让这个全局属性加一 或者减一就可以。当然这种方式是可以实现我们现在的需求的。但其实这种方法是完全不正确的。
因为我们在一个组件中直接在修改了全局中的数据,在vuex中,是不允许一个组件直接去修改全局的数据的。vuex推荐我们使用 Mutation 。Mutation用于变更Store中的数据
原因是:如果我们的项目很大,代码越写越多。如果全局中的数据发生了变化,如果我们想知道到底是谁把全局中的数据改变了。这时候就会比较麻烦,就会一个一个组件的去翻,到底在那个组件中发生了改变。这种方式不利于后期的维护。使用Mutation 中的函数来修改,后期发生改变了,直接来store.js中文件中的mutation来查找什么时候发生改变就会很方便了
1.只能通过mutation变更Store数据,不可以直接操作Store中的数据
2.通过这种方式虽然看起来麻烦,但是可以集中监控所有数据的变化
在state平级定义
mutations:{
//里面可以定义很多的额方法函数,比如实现自增加一,可以定义 add(){}函数
add(state){
//上面的state就是当前的state对象。既然可以拿到state对象,自然就可以拿到state对象中的任何属性
state.count++
}
}
如何在组件中调用mutation 中的函数??
this.$store.commit('add')// 通过commit函数可以调用mutation中的任何函数
如何在调用Mutation的时候传递参数??
mutations:{
addN(state,step){//第一个永远都是state,第二个就是外界传递过来的值
state.count += step
}
}
如何在组件中调用 mutation中的函数并传递参数呢?
this.$store.commit('addN',3)// commit的作用就是调用某个mutation 函数
从vuex中按需导入mapMutation函数
import { mapMutations } from 'vuex'
通过刚才导入的mapMutations函数,将需要的mutations函数,映射为当前组件的methods方法
在组件中写::
methods:{
...mapMutations(['add','addN'])//想要调用vuex全局中的那个函数,就在组件这里进行声明。加上...展开云算符,就可以像,调用当前组件的方法一样调用全局函数了
}
具体实例代码:
store.js文件
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
count: 4
},
mutations: {
add(state, step) {
// 第一个形参永远都是state也就是$state对象
// 第二个形参是调用add时传递的参数
// 只要调用add,就让值加一
state.count += 1
},
addN(state, step) {
state.count += step
},
sub(state) {
state.count -= 1
},
subN(state, step) {
state.count -= step
}
},
actions: {
}
})
substraction.vue文件
减法组件-----count: {{count}}
如下:
//按需导入函数名
import { mapState,mapMutations } from 'vuex'
export default {
data() {
return {}
},
//映射为当前组件的methods方法
methods:{
//获得mapMutations映射的sub函数
...mapMutations(['sub']),
//当点击按钮时触发Sub函数
Sub(){
//调用sub函数完成对数据的操作
this.sub(10);
}
},
computed:{
...mapState(['count'])
}
}
mutation中不能写异步的代码vue的调试器不能正常使用了。异步的代码在Action中写如果通过异步操作变更数据,必须通过Action,而不能使用Mution,但是在action中还是要通过触发Mutation的方式间接变更数据
action中只能通过commit函数去触发mutation中的函数
dispath
就是用来触发action中定义的函数 (commit的作用 就是调用某个mutation函数)
在store.js中定义
// 定义 Action
const store = new Vuex.Store({
// ...省略其他代码
mutations: {
add(state) {
state.count++
}
},
actions: {
addAsync(context) {
setTimeout(() => {
context.commit('add')
}, 1000)
}
}
})
// 触发 Action
methods: {
handle() {
// 触发 actions 的第一种方式 dispatch就是用来触发某个函数
this.$store.dispatch('addAsync')
}
}
我们知道,只有mutations中定义的函数,才有权利修改state中的数据
1.commit
的作用 用来调用(触发)某个mutation
函数
2.dispatch
函数,用来触发actions
中的函数
操作步骤如下:
打开store.js文件,修改Action,如下:
actions: {
addAsync(context,step){
setTimeout(()=>{
context.commit('add',step);
},2000)
}
}
然后在Addition.vue中给按钮添加事件代码如下:
methods:{
AddAsync(){
this.$store.dispatch('addAsync',5)
}
}
触发actions异步任务时携带参数
异步任务的方法函数的第一个参数永远都是context
// 定义 Action
const store = new Vuex.Store({
// ...省略其他代码
mutations: {
addN(state, step) {
state.count += step
}
},
actions: {
// 所有的额异步任务方法都需要在actions中定义,才能在组件中使用
addNAsync(context, step) {
setTimeout(() => {
context.commit('addN', step)
}, 1000)
}
})
// 触发 Action
methods: {
btnHandler4(){
this.$store.dispatch('addNAsync',3)
}
}
// 1. 从 vuex 中按需导入 mapActions 函数
import { mapActions } from 'vuex'
通过刚才导入的 mapActions 函数,将需要的 actions 函数,映射为当前组件的 methods 方法:
// 2. 将指定的 actions 函数,映射为当前组件的 methods 函数
methods: {
...mapActions(['addASync', 'addNASync'])
}
如下:substraction.vue组件代码
减法组件-----count: {{count}}
上面的使用异步方法二,还需要每次使用的时候通过点击事件绑定函数的方式来调用。也可以不需要绑定函数,就可以直接使用映射过来的subAsync函数,方法是直接复制映射过来的函数名,给点击事件直接绑定映射过来的函数名(不需要自己再定义一个函数)
删除掉 和
btnHandler3
函数
直接调用:
原因是:mapActions函数就是把全局的某个函数映射为自己的某个函数,既然subAsync是自己的额函数了,就相当于在组件中在methods中定义了函数subAsync,所以就可以直接把subAsync作为函数直接调用
Getter用于对Store
中的数据进行加工处理形成新的数据
① Getter 可以对 Store 中已有的数据加工处理之后形成新的数据,类似 Vue 的计算属性。
② Store 中数据发生变化,Getter 的数据也会跟着变化。
打开store.js文件,添加getters,如下:
export default new Vuex.Store({
.......
getters:{
//添加了一个showNum的属性 接收的第一个参数就是state对象
showNum : state =>{
return '最新的count值为:'+state.count;
}
}
})
然后打开Addition.vue中,添加插值表达式使用getters
{{$store.getters.showNum}}
或者也可以在Addition.vue中,导入mapGetters,并将之映射为计算属性
import { mapGetters } from 'vuex'
computed:{
...mapGetters(['showNum'])
}
方式一:
方式二:
{{showNum}}
首先使用vue ui初始化一个使用vuex的案例
项目中的配置有(4个 vuex,link/formatter/使用配置文件/router);创建好之后安装依赖 axios;安装ant-design-vue依赖(都安装到运行依赖)
创建好之后,自动生成一个store.js (使用vuex,store被挂载到vue实例中,这样就可以在项目的全局使用store) main.js文件
初始化项目:赋值写好的代码,打开控制台看有没有出错,然后再催可视化面板中看有没有警告信息,有的话,去掉警告。(新建格式化工具eslink配置文件 .prettierrc文件,去除分号,可以使用单引号),然后在.eslinktrc配置文件中去除那些不必要的空格警告(函数名与小括号之间需要有空格等警告,在rules数组中,赋值警告信息到这里来,给他的值设置为0 就不会再报错了)、
将store.js组件导入到main.js中,并将其挂载到vue全局中
将list数组中的静态内容放到list.json文件中
然后打开public文件夹,创建一个list.json文件,文件代码如下:
[
{
"id": 0,
"info": "Racing car sprays burning fuel into crowd.",
"done": false
},
{
"id": 1,
"info": "Japanese princess to wed commoner.",
"done": false
},
{
"id": 2,
"info": "Australian walks 100km after outback crash.",
"done": false
},
{
"id": 3,
"info": "Man charged over missing wedding girl.",
"done": false
},
{
"id": 4,
"info": "Los Angeles battles huge wildfires.",
"done": false
}
]
再接着,打开main.js,添加store.js的引入,如下:
import Vue from 'vue'
import App from './App.vue'
import store from './store.js'
// 1. 导入 ant-design-vue 组件库
import Antd from 'ant-design-vue'
// 2. 导入组件库的样式表
import 'ant-design-vue/dist/antd.css'
Vue.config.productionTip = false
// 3. 安装组件库
Vue.use(Antd)
new Vue({
store,
render: h => h(App)
}).$mount('#app')
(因为想要通过异步请求的方式模拟发送真正ajax调取后台数据的过程,需要导入axios发起异步请求)再接着打开store.js,添加axios请求json文件获取数据的代码,如下:
import Vue from 'vue'
import Vuex from 'vuex'
import axios from 'axios'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
//所有任务列表
list: [],
//文本输入框中的值
inputValue: 'AAA'
},
mutations: {
initList(state, list) {
state.list = list
},
setInputValue(state,value){
state.inputValue = value
}
},
actions: {
getList(context) {
// // 将data从对象中解构赋值出来
axios.get('/list.json').then(({ data }) => {
console.log(data);
context.commit('initList', data)
})
}
}
})
最后,打开App.vue文件,将store中的数据获取并展示:
实现页面的文本框跟着文本框中的值变化(文本框的change事件)用的属性绑定 …mapState([‘inputValue’])
// 监听文本框内容的变化
handleInputChange(e){
// 拿到文本框最新的值 文本框的change事件
console.log(e.target.value)
//调用mutation函数
this.$store.commit('setInputValue',e.target.value)
}
store.js文件中
mutations: {
// 只能在这里操作state中的数据
initList(state, list) {
state.list = list
},
// 为store中的inputValue赋值
setInputValue(state, val) {
state.inputValue = val
}
},
设置好要传递的数据对象参数,然后将对象添加到list列表中去
store.js中
// 添加事项操作
addItem(state) {
// 根据json数据(后台数据库 接口要求的数据类型添加对象参数到list.json列表中)
const obj = {
id: state.nextId,
info: state.inputValue.trim(),
// 默认为没有完成的数据
done: false
}
// 将数据对象追加到list数组中
state.list.push(obj)
// 追加完之后就及时的让nextId 加一
state.nextId++
// 将input框中的值清空
state.inputValue = ''
}
app.vue文件中
// 为添加事项按钮绑定时间函数
addItemToList(){
// 判断文本框的内容是否为空
if(this.inputValue.trim().length <= 0){
return this.$message.warning('文本框中的内容不能为空!')
}
// 用户输入的数据不为空
this.$store.commit('addItem')
}
为每个删除按钮绑定点击事件,删除的时候,拿到当前数据列表的id,根据id号删除相应json文件中的数据。删除数组中的意向
store.js文件中
// 根据id删除事项
removeItem(state, id) {
// 根据id查找到对象想的索引
const i = state.list.findIndex(x => x.id === id)
// 根据索引,删除对应的元素
if (i !== -1) {
// 数组索引存在 就删除这条数据
state.list.splice(i, 1)
}
}
app.vue文件中
// 删除该条数据 在当前组件不能之家删除 操作 state中的数据 需要使用到mutation
removeItemById(id){
// console.log(id)
this.$store.commit('removeItem',id)
}
复选框的选中状态是由 :checked 属性决定的额
{{item.info}}
点击复选框,拿到复选框的id,根据id将状态赋值到list对应的item项
监听复选框状态改变的事件(change)
app.vue中
//复选框的状态发生改变 的事件 e就是事件对象
cbStatusChanged(e,id){
// console.log(e)
// 拿到当前最新的文本框选中状态
// console.log(e.target.checked)
// 获取到当前点击的文本框的id
// console.log(id)
// 定义参数对象值
const param ={
id:id,
status:e.target.checked
}
this.$store.commit('changeStatus',param)
}
store.js
// 复选框状态发生改变函数 修改列表项的选中状态
changeStatus(state, param) {
// 查找当前的id
const i = state.list.findIndex(x => x.id === param.id)
if (i !== -1) {
state.list[i].done = param.status
}
}
store.js文件中
//
getters: {
// 统计未完成的任务的条数
unDoneLength(state) {
return state.list.filter(x => x.done === false).length
}
}
app.vue文件中,先按需导入mapGetters函数然后再computed属性中映射一下,最后再在template结构中使用(差值表达式)
import { mapState,mapGetters} from 'vuex'
***********************************************
computed:{
// 全局中的state对象中的list数组 就变成当前组件中的一个list计算属性了
// 接下来就可以将list数据渲染到页面上了
...mapState(['list','inputValue']),
...mapGetters(['unDoneLength'])
},
为清除已完成按钮绑定事件
store.js文件中
// 清除已完成事项
cleanDone(state) {
// 将属性done是true的过滤出来,重新计算一下条数
// 将未完成的数据拿出来重新赋值给当前事项的数组,就相当于删除了属性值是true的事项
state.list = state.list.filter(x => x.done === false)
// 过滤出来的是一个新的数组,里面全部都是属性done是false的值
}
app.vue中 定义点击事件的函数
// 清除已完成函数
clean(){
// 还是不能再当前组件中直接操作store中的数据,需要使用mutation还操作数据
this.$store.commit('cleanDone')
}
列表底部高亮按钮的切换
app.vue文件
// 修改页面上展示的列表数据
changeList(key){
console.log(key)
this.$store.commit('changeViewKey',key)
}
全部
未完成
已完成
store.js文件中
先把store.js文件中的数据映射到app.vue组件中
computed:{
// 全局中的state对象中的list数组 就变成当前组件中的一个list计算属性了
// 接下来就可以将list数据渲染到页面上了
...mapState(['list','inputValue','viewKey']),
...mapGetters(['unDoneLength'])
},
// 修改视图的关键字
changeViewKey(state, key) {
state.viewKey = key
}
使用getters对数据进行包装 实现按需切换。在getters节点中新增infolist节点
store.js文件
// 过滤数据,对数据进行包装,但会相应的数据
infolist(state) {
if (state.viewKey === 'all') {
return state.list
}
if (state.viewKey === 'undone') {
return state.list.filter(x => !x.done)
}
if (state.viewKey === 'done') {
return state.list.filter(x => x.done)
}
//防止报错
return state.list
}
computed中按需导入函数
...mapGetters(['unDoneLength','infolist'])
将list的数据源绑定为 infolist
//-----------------------------------------------------------------------------------------------------------------------------------------------------
首先,打开App.vue文件,给“添加事项”按钮绑定点击事件,编写处理函数
//绑定事件
添加事项
//编写事件处理函数
methods:{
......
addItemToList(){
//向列表中新增事项
if(this.inputValue.trim().length <= 0){
return this.$message.warning('文本框内容不能为空')
}
this.$store.commit('addItem')
}
}
然后打开store.js编写addItem
export default new Vuex.Store({
state: {
//所有任务列表
list: [],
//文本输入框中的值
inputValue: 'AAA',
//下一个id
nextId:5
},
mutations: {
........
//添加列表项
addItem(state){
const obj = {
id :state.nextId,
info: state.inputValue.trim(),
done:false
}
//将创建好的事项添加到数组list中
state.list.push(obj)
//将nextId值自增
state.nextId++
state.inputValue = ''
}
}
......
})
首先,打开App.vue文件,给“删除”按钮绑定点击事件,编写处理函数
//绑定事件
删除
//编写事件处理函数
methods:{
......
removeItemById(id){
//根据id删除事项
this.$store.commit('removeItem',id)
}
}
然后打开store.js编写addItem
export default new Vuex.Store({
......
mutations: {
........
removeItem(state,id){
//根据id删除事项数据
const index = state.list.findIndex( x => x.id === id )
// console.log(index);
if(index != -1) state.list.splice(index,1);
}
}
......
})
首先,打开App.vue文件,给“复选”按钮绑定点击事件,编写处理函数
//绑定事件
{{item.info}}
//编写事件处理函数
methods:{
......
cbStateChanged(id,e){
//复选框状态改变时触发
const param = {
id:id,
status:e.target.checked
}
//根据id更改事项状态
this.$store.commit('changeStatus',param)
}
}
然后打开store.js编写addItem
export default new Vuex.Store({
......
mutations: {
........
changeStatus(state,param){
//根据id改变对应事项的状态
const index = state.list.findIndex( x => x.id === param.id )
if(index != -1) state.list[index].done = param.status
}
}
......
})
打开store.js,添加getters完成剩余项统计
getters:{
unDoneLength(state){
const temp = state.list.filter( x => x.done === false )
console.log(temp)
return temp.length
}
}
打开App.vue,使用getters展示剩余项
//使用映射好的计算属性展示剩余项
{{unDoneLength}}条剩余
//导入getters
import { mapState,mapGetters } from 'vuex'
//映射
computed:{
...mapState(['list','inputValue']),
...mapGetters(['unDoneLength'])
}
首先,打开App.vue文件,给“清除已完成”按钮绑定点击事件,编写处理函数
清除已完成
//编写事件处理函数
methods:{
......
clean(){
//清除已经完成的事项
this.$store.commit('cleanDone')
}
}
然后打开store.js编写addItem
export default new Vuex.Store({
......
mutations: {
........
cleanDone(state){
state.list = state.list.filter( x => x.done === false )
}
}
......
})
G.点击选项卡切换事项
打开App.vue,给“全部”,“未完成”,“已完成”三个选项卡绑定点击事件,编写处理函数
并将列表数据来源更改为一个getters。
......
全部
未完成
已完成
......
//编写事件处理函数以及映射计算属性
methods:{
......
changeList( key ){
//点击“全部”,“已完成”,“未完成”时触发
this.$store.commit('changeKey',key)
}
},
computed:{
...mapState(['list','inputValue','viewKey']),
...mapGetters(['unDoneLength','infoList'])
}
打开store.js,添加getters,mutations,state
export default new Vuex.Store({
state: {
......
//保存默认的选项卡值
viewKey:'all'
},
mutations: {
......
changeKey(state,key){
//当用户点击“全部”,“已完成”,“未完成”选项卡时触发
state.viewKey = key
}
},
......
getters:{
.......
infoList(state){
if(state.viewKey === 'all'){
return state.list
}
if(state.viewKey === 'undone'){
return state.list.filter( x => x.done === false )
}
if(state.viewKey === 'done'){
return state.list.filter( x => x.done === true )
}
}
}
})