vuex

​ 在vue中,我们想要实现父子组件中的传值,通过props和自定义事件可以可轻松的办到,如果是两个没有关联的组件,通过$bus事件公交也能实现兄弟子件的传值,但是在大型项目中,使用$bus容易导致代码繁琐,且不容易阅读.这个时候,vuex的出现可以很好的帮助我们解决我们这种问题

vuex是一个专为 Vue.js 应用程序开发的状态管理模式, 采用集中式存储管理应用的所有组件的状态,解决多组件数据通信。(简单来说就是管理数据的,相当于一个仓库,里面存放着各种需要共享的数据,所有组件都可以拿到里面的数据)

​ Vuex的核心概念包括state(状态)、getters(获取器)、mutations(突变)和actions(动作)。State代表应用程序的状态数据,Getters用于从state中派生出一些新的状态,Mutations是用于修改state的方法,而Actions则是用于提交mutations的方法,可以包含任意异步操作。

​ 使用Vuex的好处在于,它可以帮助开发者更好地组织和管理应用程序的状态,避免了状态分散在各个组件中导致的混乱和难以维护的问题。此外,Vuex还 提供了一些高级特性,如严格模式、插件等,以及开发工具的支持,帮助开发者更好地调试和监控状态的变化。

总之,Vuex是一个强大的状态管理工具,适用于中大型的Vue.js应用程序,能够提高应用程序的可维护性和可扩展性。

要点:

1.vue官方搭配,专属,有专门的调试工具

2.数据变化是可预测的(响应式)

3.集中式管理数据状态方案

1.state 统一定义管理公共数据

2.mutations: 使用它来修改数据

3.getters: 类似于vue中的计算属性

4.actions: 类似于methods,用于发起异步请求,比如axios

5.modules: 模块拆分

其中最重要的内容为statemutations

搭建vue环境

state

  1. vuex 管理的状态对象
  2. 它应该是唯一的
  3. 示例代码:

actions

  1. 值为一个对象,包含多个响应用户动作的回调函数
  2. 通过 commit( )来触发 mutation 中函数的调用, 间接更新 state
  3. 如何触发 actions 中的回调?

​ 在组件中使用: $store.dispatch(‘对应的action回调名’)触发

  1. 可以包含异步代码(定时器, ajax 等等)
  2. 示例代码

mutations

  1. 值是一个对象,包含多个直接更新 state 的方法
  2. 谁能调用 mutations 中的方法?如何调用?

​ 在 action 中使用:commit(‘对应的mutations 方法名) 触发

  1. mutations 中方法的特点:不能写异步代码、只能单纯的操作 state
  2. 示例代码:

getters

  1. 值为一个对象,包含多个用于返回数据的函数
  2. 如何使用?—— $store.getters.xxx,有点像专属于state的计算属性
  3. 示例代码:

四个map方法的使用

1.mapState方法:用于帮助我们映射state中的数据为计算属性

1
2
3
4
5
6
computed:{
/借助mapState.生成计算属性:sum、school、subject(对象写法)
...mapState({sum:'sum',school:'school',subject:'subject'}),
//借助mapState.生成计算属性:sum、school、subject(数组写法)
...mapState(['sum','school','subject']),
}

2.mapGetters方法:用于帮助我们映射getters中的数据为计算属性

1
2
3
4
5
6
computed:{
/借助mapGetters?生成计算属性:bigSum(对象写法)
...mapGetters({bigSum:'bigSum'}),
/借助mapGetters:生成计算属性:bigSum(数组写法)
...mapGetters(['bigSum'])
}

3.mapActions方法:用于帮助我们生成与actions对话的方法,即:包含$store.dispatch(xxx)的函数

1
2
3
4
5
6
methods:{
//靠mapActions生成:incrementodd、incrementWait(对象形式)
...mapActions({incrementodd:'jiaOdd',incrementWait:'jiaWait'))
//靠mapActions.生成:incrementodd、incrementWait(数组形式)
...mapActions(['jiaOdd','jiaWait'])
}

4.mapMutations方法:用于帮助我们生成与mutations对话的方法,即:包含$store.commit(xxx)的函数

1
2
3
4
5
6
methods:{
//靠mapMutations生成:increment、decrement(对象形式)
...mapMutations({increment:'JA',decrement:'JIAN'}),
//靠mapMutations生成:JIA、JIAN(对象形式)
...mapMutations(['JIA','JIAN']),
}

具体使用

store

在任意组件中,通过this.$store.state.属性名 来获取公共数据。

在模块中,则可以直接省略this而直接写成:{{$store.state.属性名}}

在组件中修改state使用mutations

通过this.$store.commit('mutation事件名',mapper参数)可以修改state里面的数据

注意点

  1. 要修改vuex中的数据,就要使用mutations去修改
  2. 在methods里面$store.state.xxx = xxx这种方式虽然可以直接修改数据,但是vue不推荐,并且在严格模式下通过这种方式修改数据,会直接报错
1
2
3
4
5
6
7
8
9
10
11
12
13
14
export default new Vuex.Store({
state: {
num: 100,
userinfo: {
name:'柚子'
},

},
mutations: {
setname(state, newval) {
state.num = newval
},
}

1
2
3
4
5
6
7
8
methods: {
btn() {
this.$store.state.num = 200 //不推荐这种写法,vuex所有修改数据都要写在mutations里
},
btnmutation() {
this.$store.commit('setbooks',200) // 点击按钮 然后通过commit触发mutation事件
},

vuex中用getters的派生状态

作用:

在state中的数据的基础上,进一步对数据进行加工得到新数据。(与组件中computed一样)

在vuex中配置getters:

1
2
3
4
5
6
7
8
9
new Vuex.store({
// 省略其他...
getters: {
// state 就是上边定义的公共数据state
getter的名字1: function(state) {
return 要返回的值
}
}
})

在组件中用this.$store.getters.xxx来获取getters派生后的的值

注意: getter定义的时候,第一个参数是state,不能传第二个参数,派生的值通过return返回

小结

vuex 中维护公共数据主要有两个要点

1.定义数据

2.提供获取/修改数据的方法

vuex异步请求

前面我们已经讲过了修改数据和定义数据,那当我们想要从服务器发起请求拿数据的时候,就不可避免的要发起异步请求来获取数据, 这个时候actions就可以很好的帮助我们,它可以包含任意异步请求的操作.

定义格式:

1
2
3
4
5
6
7
8
9
10
11
12
13
new Vuex.store({
// 省略其他...
actions: {
// context对象会自动传入,它与store实例具有相同的方法和属性
action的名字: function(context, 载荷) {
// 1. 发异步请求, 请求数据

// 2. commit调用mutation来修改/保存数据

// context.commit('mutation名', 载荷)
}
}
})

我们可以使用Action来修改state,这一点是类似于 mutation的,不同在于:

  • action中可以通过调用 mutation来修改state,而不是直接变更状态。
  • action 可以包含任意异步(例如ajax请求)操作。

调用格式

​ 在组件中通过this.$store.dispatch('actions的名字', 参数)来调用action

1
2
3
4
5
6
7
8
9
10
11
12
13
// 发ajax请求,从后端获取数据,再来去修改state中的数据
actions: {
getBooks (context, params) {
console.log('getbooks的查询参数是', params)
axios({
url: 'https://www.fastmock.site/mock/37d3b9f13a48d528a9339fbed1b81bd5/book/api/books',
method: 'GET'
}).then(res => {
console.log(res)
context.commit('setBooks', res.data.data)
})
}
},

小结

将ajax请求放在actions中有两个好处:

  1. 代码得到了进一步封装。将发ajax和保存数据到vuex绑定在一起。
  2. 逻辑更通顺。如果数据需要保存在Vuex的state中,那从接口处获取数据的操作就定义在Vuex的actions中。

modules

问题导入

随着项目越来越大,需要放在vuex中的数据越来越多,整个store/index.js中代码会越来越长,怎么办呢?

由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。

Vuex用modules来拆分复杂业务

为了解决以上问题,Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割:

modules的作用

拆分模板,把复杂的场景按模块来拆开

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
export default new Vuex.Store({
// state: 用来保存所有的公共数据
state: {},
getters: {},
mutations: {},
actions: {},
modules: {
模块名1: {
// namespaced为true,则在使用mutations时,就必须要加上模块名
namespaced: true,
state: {},
getters: {},
mutations: {},
actions: {},
modules: {}
},
模块名2: {
// namespaced不写,默认为false,则在使用mutations时,不需要加模块名
state: {},
getters: {},
mutations: {},
actions: {},
modules: {}
}
}
})

也可以进一步对文件进行拆分

1
2
3
4
5
lua复制代码|--store /
|------- index.js # 引入模块
|------- modules
|-------------- / mod1.js # 模块1
|-------------- / mod2.js # 模块2

访问数据和修改数据的调整

  • 访问模块中的数据,要加上模块名
1
2
bash复制代码获取数据项:  {{$store.state.模块名.数据项名}}
获取getters: {{$store.getters['模块名/getters名']}}

访问模块中的mutations/actions:

  • 如果namespaced为true,则需要额外去补充模块名
  • 如果namespaced为false,则不需要额外补充模块名
1
2
bash复制代码$store.commit('mutations名')        // namespaced为false
$store.commit('模块名/mutations名') // namespaced为true

小结

使用了modules之后,在访问数据时就要额外添加modules的名字了。

结论: 在使用modules时,建议都给加上 namespaced!

Vuex的mapState

有的时候,当访问某个数据项嵌套太深了,用$store访问数据太麻烦,并且不方便修改和阅读,我们能不能优化一下代码的格式? 这个时候 map辅助函数可以很好的帮我们做这件事

  • 当然,不用map函数我们也可以完全使用vuex,这只是一个拔尖的过程,可了解或不阅读,对使用vuex也无关紧要

用mapState把公共数据(vuex.store) 映射 到本组件内部的计算属性

mapState映射的使用步骤

1
2
3
4
5
6
7
8
9
10
11
// 1. 导入辅助函数mapState,它是在vuex中定义的一个工具函数。
// es6 按需导入 import { mapState } from 'vuex'
import { mapState } from 'vuex'

computed: {
// 说明1: ...对象 是把对象展开,合并到computed
// 说明2: mapState是一个函数
// ['数据项1', '数据项2']
...mapState(['xxx']),
...mapState({'新名字': 'xxx'})
}

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 步骤
// 1. 导入辅助函数mapState,它是在vuex中定义的一个工具函数。
// es6 按需导入 import { mapState } from 'vuex'
import { mapState } from 'vuex'

// 2. 在computed中使用 ...mapState(['books'])
// const res = mapState(['books'])
// res的结果是一个对象: { books: function() {}}
// console.log('mapState', res)

export default {
computed: {
c1 () {
return 'c1'
},
// books: function() {}
// ..res: 把res这个对象合并到computed对象中
// ...res
...mapState(['books'])
}
}
</script>

小结

  1. mapState是辅助函数,将vuex中的数据投射到组件内部;

  2. computed:{ …mapState() } 这里的…是对象的展开运算符,整体来看是对象的合并。

Vuex的mapState对数据重命名

vuex中的数据与本组件内的数据名相同,我们可以使用...mapState({'新名字': 'xxx'})对数据重命名

Vuex-map函数用法汇总

如何使用全局state

  • 直接使用: this.$store.state.xxx;

  • map辅助函数:

    1
    2
    3
    4
    computed: { 
    ...mapState(['xxx']),
    ...mapState({'新名字': 'xxx'})
    }

如何使用modules中的state

  • 直接使用: this.$store.state.模块名.xxx;

  • map辅助函数:

    1
    2
    3
    4
    computed: { 
    ...mapState('模块名', ['xxx']),
    ...mapState('模块名', {'新名字': 'xxx'})
    }

如何使用全局getters

  • 直接使用:this.$store.getters.xxx

  • map辅助函数:

    1
    2
    3
    4
    computed: { 
    ...mapGetters(['xxx']),
    ...mapGetters({'新名字': 'xxx'})
    }

如何使用modules中的getters

  • 直接使用: this.$store.getters.模块名.xxx

  • map辅助函数:

    1
    2
    3
    4
    computed: { 
    ...mapGetters('模块名', ['xxx']),
    ...mapGetters('模块名',{'新名字': 'xxx'})
    }

如何使用全局mutations

  • 直接使用:this.$store.commit('mutation名', 参数)

  • map辅助函数:

    1
    2
    3
    4
    methods: { 
    ...mapMutations(['mutation名']),
    ...mapMutations({'新名字': 'mutation名'})
    }

如何使用modules中的mutations(namespaced:true)

  • 直接使用: this.$store.commit('模块名/mutation名', 参数)

  • map辅助函数:

    1
    2
    3
    4
    matlab复制代码methods: { 
    ...mapMutations('模块名', ['xxx']),
    ...mapMutations('模块名',{'新名字': 'xxx'})
    }

如何使用全局actions

  • 直接使用:this.$store.dispatch('action名', 参数)

  • map辅助函数:

    1
    2
    3
    4
    css复制代码methods: { 
    ...mapActions(['actions名']),
    ...mapActions({'新名字': 'actions名'})
    }

如何使用modules中的actions(namespaced:true)

  • 直接使用: this.$store.dispatch('模块名/action名', 参数)

  • map辅助函数:

    1
    2
    3
    4
    matlab复制代码methods: { 
    ...mapActions('模块名', ['xxx']),
    ...mapActions('模块名',{'新名字': 'xxx'})
    }

核心api小结

Vuex开发者工具(devtools)

使用:

可以回滚事件,可以观察数据,可以测试使用

同源策略:

同源策略是浏览器中的一个重要的安全策略,同源策略的作用就是为了限制不同源之间的交互,从而能够有效避免XSS、CSFR等浏览器层面的攻击。
同源指的是两个请求接口URL的协议(protocol)、域名(host)和端口(port)一致。

配置代理:

解决跨域问题:

1.cors

2.jsonp(只能解决get请求的跨域问题,解决不了post请求和其他请求的跨域问题)

3.代理服务器

  • nginx : 反向代理服务器

  • vue-cli :开启代理服务器(四行代码)(主要内容)

配置代理服务器

扩展: ajax是前端技术,node.js无法使用XMLHttpRequest

public文件夹里的都是8080端口有的

缺点:

1.不能设置多个代理,不灵活

2.本地有的先访问本地,就没法通过代理得到目标端口的文件

/api 请求前缀(可以自定义)

例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
/* 对axios进行二次包装
1. 配置通用的基础路径和超时
2. 显示请求进度条
3. 成功返回的数据不再是response, 而直接是响应体数据response.data
4. 统一处理请求错误, 具体请求也可以选择处理或不处理*/
import axios from 'axios'
import NProgress from 'nprogress'
import 'nprogress/nprogress.css'

// 配置不显示右上角的旋转进度条, 只显示水平进度条
NProgress.configure({ showSpinner: false })

const service = axios.create({
baseURL: "/api",// 基础路径
timeout: 5000// 连接请求超时时间
})

service.interceptors.request.use((config) => {
// 显示请求中的水平进度条
NProgress.start()

// 必须返回配置对象
return config
})

service.interceptors.response.use((response) => {
// 隐藏进度条
NProgress.done()
// 返回响应体数据
return response.data
}, (error) => {
// 隐藏进度条
NProgress.done()

// 统一处理一下错误
alert(`请求出错:${error.message || '未知错误'}`)

// 后面可以选择不处理或处理
return Promise.reject(error)
})

export default service

用请求前缀通过代理服务器会导致目标服务器也找带有前缀的目标文件,可能会造成错误,但也可以简化很多路径

扩展:

1
2
3
4
5
(pathRewrite):{'^/atguigu':' '}  //对路径进行重写

ws:true// 用于支持websocket

changeOrigin:true //表示不告诉服务器真实来源(说谎来源为目的服务器),false表示告诉服务器真实来源 用于控制请求头中的host值

10种组件通信的方式

组件通信方式1:props

使用场景:[父子通信]

传递数据类型:

1:函数 ———–实质就是子组件想给父亲传递数据

2:不是函数———–实质就是父亲给子组件传递数据

1
<TodoList :todos="123"  updateChecked="hander">

特殊情况:路由传递的props

1:布尔值类型,把路由中params参数映射为组件props数据

2:对象,静态数据,很少用

3:函数,可以把路由中params|query参数映射为组件props数据

组件通信方式2: 自定义事件 $emit $on[简写@]

事件:原生DOM事件—-【click|mouseenter……..】

事件:自定义事件—–[子给父传递数据]

组件通信方式3:$bus 全局事件总线—-【万能】

组件实例的原型的原型指向的Vue.prototype

组件通信方式4:pubsub-js【发布订阅消息】

在vue中根本不用【React】 —-万能

组件通信方式5: Vuex[仓库] —–数据非持久化—-万能的

核心概念:
state
mutations
actions
getters
modules

组件通信方式6:插槽slot—–父子通信【结构】

默认插槽

具名插槽

作用域插槽:子组件的数据来源于父组件,但是子组件的自己的结构有父亲决定。

组件通信方式7: v-model实现组件通信

v-model:可以收集表单数据【text、radio、checkbox、range】等等
切记:v-model收集checkbox需要用数组收集

v-model:实现原理 :value @input 还可以实现父子数据同步。

1
<CustomInput v-model="msg"></CustomInput>

组件通信方式8:属性修饰符.sync

可以实现父子数据同步。

以后在elementUI组件中出现,实现父子数据同步。

组件通信方式9:$attrs与$listeners

$attrs:组件实例的属性,可以获取到父亲传递的props数据(前提子组件没有通过props接受)
$listeners:组件实例的属性,可以获取到父亲传递自定义事件(对象形式呈现)

组件通信方式10:$children与$parent

可以实现父子组件通信

ref:可以在父组件内部获取子组件—实现父子通信

$children:可以在父组件内部获取全部的子组件【返回数组】

$parent:可以在子组件内部获取唯一的父组件【返回组件实例】