vue基础 vuex、vue-router、axios(三)
Vue Router#
啥是Vue Router?#
Vue Router 是 Vue.js 官方的路由管理器。它和 Vue.js 的核心深度集成,让构建单页面应用变得易如反掌,在我们项目中也是必会技能之一。下面我想通过以下几个方面来分享。
一、集成Vue Router#
- 1.如果安装了vue脚手架cli3 在create项目的时候会有安装Vue router的选项,选中敲空格就会默认安装Vue router
- 2.如果在create项目的时候没有自动安装,那么要手动进行安装.
npm i vue-router --save
- 3.安装好vue-router后最好在src文件下创建一个router.js来专门管理整个项目的路由结构
二、使用Vue Router#
1.简单构建一个通过浏览器输入访问地址的路由#
-
1.
src
目录下创建一个router.js
文件然后在文件中构建路由数组并导出,一定要注意构建路由数组是routes
而不是routers
哦!!import Vue from 'vue' import Router from 'vue-router' Vue.use(Router) // 构建路由数组 const routes = [ ]; const router = new VueRouter({ mode: "hash", base: process.env.BASE_URL, // 配置单页应用的基路径 routes, }); export default router;
vue router 中 mode和base#
一、mode 有两种情况history和hash,例子如下:
mode:history
localhost:8080/user/list
mode:hash
localhost:8080/#/user/list
二、base
默认值: “/”,应用的基路径。例如,如果整个单页应用服务在 /app/ 下,然后 base 就应该设为 “/app/”
-
2.在
main.js
文件中全局引用router.js
并挂载到项目中// 引入router.js import router from 'router.js' Vue.config.productionTip = false new Vue({ router, render: h => h(App), }).$mount('#app')
目标:通过路由localhost:8080/#/home
跳转到Home组件界面
-
3.构建一个Home.vue的组件, 在router.js中构建
routes
的配置import Vue from 'vue' import Router from 'vue-router' // 引入views import Home from './views/Home.vue' Vue.use(Router); export default new Router({ routes: [ { path: '/home', name: 'home', component: Home }, { path: "/", name: "Index", component: Layout, redirect: "/home", //跳转 children: [ //嵌套路由 { path: "/active", name: "Active", component: () => import("../views/active-bgc.vue"), //路由懒加载 meta: { title: "背景提升", }, }, } ] })
-
4.给
Home组件
配置出口<router-view />
,这一步非常关键!!没有出口,是不会显示出来的.
在App.vue
中
<template>
<div id="app">
<router-view />
</div>
</template>
至此一个简单的路由配置就完美的配置完了,浏览器输入localhost:8080/#/home
就可以访问啦!如果有多个组件需要配置路由,都可以在router.js
中配置.
2.router-link#
说明:router-link
其实就是封装的a
标签
<router-link to="/home">Home</router-link>
3.vue-router配置子路由#
说明:访问二级或三级页面的时候需要配置子路由.
App.vue
<template>
<div id="app">
<p>导航 :
<router-link to="/">首页</router-link> |
<router-link to="/home">Home页面</router-link> |
<router-link to="/home/about">-About界面</router-link> |
<router-link to="/home/me">-Me页面</router-link>
</p>
<router-view></router-view>
</div>
</template>
router.js中配置home的children路由\
{path: '/home',
component: Home,
children: [
{path: 'about',component: About},
{path: 'me',component: Me}]
}
注意点:在Home.vue
中一定要添加出口<router-view></router-view>
否则不会生效
vue-router如何参数传递#
1.用$route
的params
来动态传参
- 通过
$route.name
的形式来接受参数name
这个参数 - 定义路由
path
键值对的形式传参
routes: [{
path: '/home/:name/:age/:height',
name: 'home',
component: Home
}]
在浏览器中输入localhost:8080/#/home/name=james/age=26/height=180
- 在
App.vue
中通过this.$route.params
来动态取值
2.$route
的query
来动态传参
$route.params
仅能取到类似localhost:8080/#/home/name=james/age=26/height=180
的地址传过来的参数,如果是localhost:8080/#/home/name=james/age=26/height=180?sex=男
带 ?
,只能通过$route.query
的方式来取值
性别{{this.$route.query.sex}}
上面两种方式是为了区分路由动态传递的参数的性质来分区取值,我们还可以直接封装方法,取值的时候不用这么麻烦,可以通过props
传值 在router.js中
let fun = ({parms,query}=>{
return {
age:param.age,
sex:param.sex,
height:query.height,
}
})
// 通过props传值
{path:'/home/:sex?height',name:'mine',component:Mine,props:fun}
// 定义接受的参数
props:['name','sex','height']
//直接取值
{{name}}
{{sex}}
{{height}}
三、导航守卫#
1.什么是路由守卫?#
通过路由守卫可以刷新或进入的路由界面进行权限验证,相当于Vue全局的中间件
2.全局守卫#
任何一个路由进入都可以先拦截,然后根据添加跳转不同的路由。
关键Code: router.js中
const router = new VueRouter({.....})
router.beforeEach((to,from,next)=>{
if (to.path !== 'login') { //验证是否登录
if(window.isLogin) {
next();
} else { //没有登录
next('/login?redirect='+to.path);
}
} else { //不需要验证
next();
}
next();
})
每个守卫方法接收三个参数:
-
to: Route
: 即将要进入的目标 路由对象 -
from: Route
: 当前导航正要离开的路由 -
next: Function
: 一定要调用该方法来 resolve 这个钩子。执行效果依赖next
方法的调用参数。next('/')
或者next({ path: '/' })
: 跳转到一个不同的地址。
这里有一个在用户未能验证身份时重定向到
/login
的示例:// BAD router.beforeEach((to, from, next) => { if (to.name !== 'Login' && !isAuthenticated) next({ name: 'Login' }) // 如果用户未能验证身份,则 `next` 会被调用两次 next() })
// GOOD router.beforeEach((to, from, next) => { if (to.name !== 'Login' && !isAuthenticated) next({ name: 'Login' }) else next() })
router.beforeEach()一般用来做一些进入页面的限制。比如没有登录,就不能进入某些页面,只有登录了之后才有权限查看某些页面。。。说白了就是路由拦截。
1、我们可以在路由中做以下配置,表示当前路由下的页面需要登录权限
meta:{requireAuth:true//true为这个页面需要登录权限 }
routes: [
{
name: 'home',
path: '/home',
component: resolve => void(require(['../components/Home.vue'], resolve)),
meta: {
title: '',
requireAuth: true, // 添加该字段,表示进入这个路由是需要登录的
},
},
]
2、在main.js中写上路由拦截
router.beforeEach((to, from, next) => {
if (to.matched.some(record => record.meta.requireAuth)) { // 判断该路由是否需要登录权限
if (sessionStorage.userName!='') { // 判断缓存里面是否有 userName //在登录的时候设置它的值
next();
} else {
next({
path: '/login',
query: {
redirect: to.fullPath
} // 将跳转的路由path作为参数,登录成功后跳转到该路由
})
}
} else {
next();
}
});
解析:一个路由匹配到的所有路由记录会暴露为 $route
对象 (还有在导航守卫中的路由对象) 的 $route.matched
数组。因此,我们需要遍历 $route.matched
来检查路由记录中的 meta
字段。
1、meta 字段就是路由元信息字段,requiresAuth 是自己起的字段名称,用来标记这个路由信息是否需要检测,true 表示要检测
2、if (to.matched.some(record => record.meta.requiresAuth) ),如果对这类写法不熟悉,可以去看看es6的箭头函数,这句话就是返回遍历的某个路由对象,我们定义为为record,检测这个对象是否拥有meta这个对象,如果有meta这个对象,检测它的meta对象是不是有requiresAuth这个属性,且为true,如果满足上述条件,就确定了是这个/foo/bar路由。
3.局部守卫#
只控制某单个组件的路由在routes
数组里做控制,和全局一样需要beforeEach
你可以在路由配置上直接定义 beforeEnter
守卫:
const router = new VueRouter({
routes: [
{
path: '/foo',
component: Foo,
beforeEnter: (to, from, next) => {
// ...
}
}
]
})
这些守卫与全局前置守卫的方法参数是一样的。
4.生命周期#
beforeRouteEnter
进入之前调用beforeRouteUpdate
路由参数变了beforeRouterLeave
路由离开之前
最后,你可以在路由组件内直接定义以下路由导航守卫:
const Foo = {
template: `...`,
beforeRouteEnter(to, from, next) {
// 在渲染该组件的对应路由被 confirm 前调用
// 不!能!获取组件实例 `this`
// 因为当守卫执行前,组件实例还没被创建
},
beforeRouteUpdate(to, from, next) {
// 在当前路由改变,但是该组件被复用时调用
// 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
// 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
// 可以访问组件实例 `this`
},
beforeRouteLeave(to, from, next) {
// 导航离开该组件的对应路由时调用
// 可以访问组件实例 `this`
}
}
beforeRouteEnter
守卫 不能 访问 this
,因为守卫在导航确认前被调用,因此即将登场的新组件还没被创建。
不过,你可以通过传一个回调给 next
来访问组件实例。在导航被确认的时候执行回调,并且把组件实例作为回调方法的参数。
beforeRouteEnter (to, from, next) {
next(vm => {
// 通过 `vm` 访问组件实例
})
}
注意 beforeRouteEnter
是支持给 next
传递回调的唯一守卫。对于 beforeRouteUpdate
和 beforeRouteLeave
来说,this
已经可用了,所以不支持传递回调,因为没有必要了。
beforeRouteUpdate (to, from, next) {
// just use `this`
this.name = to.params.name
next()
}
这个离开守卫通常用来禁止用户在还未保存修改前突然离开。该导航可以通过 next(false)
来取消。
beforeRouteLeave (to, from, next) {
const answer = window.confirm('Do you really want to leave? you have unsaved changes!')
if (answer) {
next()
} else {
next(false)
}
}
扩展:#
includes(), some(), es6扩展运算符#
利用数组进行判断的代码更优雅
includes函数
使用前
if (fruit === 'apple' || fruit === 'orange' || fruit === 'grape') {
//...
}
使用后
if (['apple', 'orange' ,'grape'].includes(fruit)) {
//...
}
还有类似的some()函数,通过回调方法进行判断,返回的是布尔值
只要数组中任意1个元素满足条件即返回true, 全无返回false
例如
const arry = [1, 1, 2, 2]
const bl = this.arry.some(e => {
return (e - 1) === 1
})
console.log(bl) // true
ES6中数组方法( every 和 some )#
every 一假即假 some 一真即真
判断对象数组中每个人是否成年,大于17成年,否则未成年
var arr = [
{name:'jerry',sex:'man',age:14},
{name:'jack',sex:'woman',age:19},
{name:'bill',sex:'man',age:18}
]
var every = arr.every(function(obj){ //every 一假即假
return obj.age > 17
})
var some = arr.some(function(obj){ // some 一真即真
return obj.age >17
})
console.log(every,some) //false true
四、路由懒加载#
用的时候在加载,提高性能
1.懒加载的使用方法:#
改变组件的引入方式:
由之前的
import About from './views/About'
转变为
const About = ()=> import('./views/About')
这样就轻松的实现了路由的懒加载啦!
相关API#
- this.$router.push(path): 相当于点击路由链接(可以返回到当前路由界面)
- this.$router.replace(path): 用新路由替换当前路由(不可以返回到当前路由界面)
- this.$router.back(): 请求(返回)上一个记录路由
- this.$router.go(-1): 请求(返回)上一个记录路由
- this.$router.go(1): 请求下一个记录路由
this.$router.push({})实现路由跳转
想要导航到不同的 URL,则使用 router.push
方法。这个方法会向 history 栈添加一个新的记录,所以,当用户点击浏览器后退按钮时,则回到之前的 URL。
当你点击 <router-link>
时,这个方法会在内部调用,所以说,点击 <router-link :to="...">
等同于调用 router.push(...)
。
声明式 | 编程式 |
---|---|
<router-link :to="..."> |
router.push(...) |
该方法的参数可以是一个字符串路径,或者一个描述地址的对象。例如:
// 字符串
router.push('home')
// 对象
router.push({ path: 'home' })
// 命名的路由
router.push({ name: 'user', params: { userId: '123' }})
// 带查询参数,变成 /register?plan=private
router.push({ path: 'register', query: { plan: 'private' }})
注意:如果提供了 path
,params
会被忽略,上述例子中的 query
并不属于这种情况。取而代之的是下面例子的做法,你需要提供路由的 name
或手写完整的带有参数的 path
:
const userId = '123'
router.push({ name: 'user', params: { userId }}) // -> /user/123
router.push({ path: `/user/${userId}` }) // -> /user/123
// 这里的 params 不生效
router.push({ path: '/user', params: { userId }}) // -> /user
同样的规则也适用于 router-link
组件的 to
属性。
注意: 如果目的地和当前路由相同,只有参数发生了改变 (比如从一个用户资料到另一个 /users/1
-> /users/2
),你需要使用 beforeRouteUpdate
来响应这个变化 (比如抓取用户信息)。
router.replace跟
router.push` 很像,唯一的不同就是,它不会向 history 添加新记录,而是跟它的方法名一样 —— 替换掉当前的 history 记录。
$router 和 $route的区别#
- $router
router是VueRouter的一个对象,通过Vue.use(VueRouter)和VueRouter构造函数得到一个router的实例对象,这个对象中是一个全局的对象,他包含了所有的路由包含了许多关键的对象和属性
- $route
route是一个跳转的路由对象,每一个路由都会有一个route对象,是一个局部的对象可以获取对应的name,path,params,query等
Vuex#
一、啥是Vuex?#
Vuex是用来管理Vue的所有组件状态,说白了就是组件中通信的一种高级方式。
Vuex官方文档已经讲解的非常详细,建议通读一遍,本文是自己的学习总结实践和归纳,有些概念和语句借鉴官方。
二、为什么使用Vuex?#
在之前的文章组件通信中我们讲了组件中的常用通信方式有props
,$emit/v-on
,$parent / $children & $refs
这三种,他们的局限性在于只能在父子组件中通信,不能在兄弟组件中通信,另外两种是Bus中央总线
、发布订阅模式
可以在任意组件之间进行通信,但是如果在大型项目中难免会有些许瓶颈,组件之间的耦合度较高,那么这时Vuex
就恰到好处的出现了,他能够合理的管理各组件中的状态(通信,方法捕获实现,数据传递),将状态从组建中剥离开来,定义和隔离状态管理中的各种概念并通过强制规则维持视图和状态间的独立性,这样我们的代码将会变得更结构化且易维护。
三、Vuex核心#
Vuex
提供一个单例Store
它就相当于一个容器一样,包含着你应用中的state
公共数据,我们通过单例Store
来统一的按照既定规则来动态的管理这些state
,这就是Vuex
的核心。
1.State
#
state就是一个纯对象,上面有一些状态挂载,而且一个应用应该只有一个
state
在store.js
中
export default new Vuex.Store({
state : {//初始状态
name:'极客James',
height:180,
age:27
}
})
1.1mapState
:
使用 mapState 辅助函数帮助我们生成计算属性,在需要通信的组件中通过
import {mapState} from 'vuex'
引入辅助函数,然后要在计算属性computed
钩子里面进行数据的映射。
mapState
辅助函数使用的三种方式:#
- 第一种:计算属性通过箭头函数取返回值
computed:mapState({
name:state=>state.name,
height:state=>state.name,
age:state=>state.age
})
- 第二种方法:利用ES6的...延展特性 (推荐做法)
computed:{
...mapState([
'name',
'height',
'age'
])
}
- 第三种方法:利用ES6的...延展特性通过对象取值
computed:{
...mapState({
name:state=>state.name,
height:state=>state.height,
age:state=>state.age
})
}
在组件中取数据
<template>
<div class="app">
<div>我是store里面的数据姓名:{{name}}</div>
<div>我是store里面的数据年龄:{{age}}</div>
<div>我是store里面的数据身高:{{height}}</div>
</div>
</template>
2.Getter
#
可以认为是
store
的计算属性,就像计算属性一样,getter
的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算。
2.1mapGetter
mapGetter
是getter的一个辅助函数,使用方法和mapState
类似,推荐使用延展...来取值.
3.Mutation
#
Vuex 中的 mutation 非常类似于事件:每个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler)。这个回调函数就是我们实际进行状态更改的地方,并且它会接受 state 作为第一个参数,同步改变state的唯一方法。
mutations:{
increment(state,payload){
if(state.age % 2 == 0){
state.age += payload.number;
}
},
reduce(state,payload){
state.age--;
}
}
在组件中通过事件方法来进行同步改变store
中的state
,需要通过commit
提交来响应mutation
中的事件方法。
methods: {
add () {
Store.commit('increment', { number: 10 });
},
reduce () {
Store.commit('reduce');
}
}
另外一种写法:对象风格的提交方式
methods:{
add(){
Store.commit({
type:"increment",
number:2
})
},
reduce(){
Store.commit({
type:'reduce',
})
}
}
推荐使用常量替代 Mutation 事件类型
还记得在组件通信中讲到的发布订阅
方法进行组件间通信,使用常量来定义事件类型吗?在Vuex
中也是非常推荐使用此办法的,将mutation
中的事件常量整合到一个文件夹mutation-type.js
中,通过export
方式导出.
在mutation-type.js
export const ADD = 'ADD'
export const REDUCE = 'REDUCE'
在store.js
中引入import {ADD,REDUCE} from './mutation-type.js'
import {ADD,REDUCE} from './mutation-type.js'
mutation:{
[ADD](state,payload) {
state.age += payload.number;
},
[REDUCE](state,payload) {
state.age --;
}
}
同样的在需要的组件中引入import {ADD,REDUCE} from './mutation-type.js'
import {ADD,REDUCE} from './mutation-type.js'
methods:{
add(){
Store.commit({
type:ADD,
number:2
})
}
}
3.1 mapMutation
mapMutation
是mutation
的辅助函数,和state
类似都可以通过...扩展运算来进行直接取值。
mapMutations用于在组件中映射mutation内的方法,以便在该组件中直接使用mutation里的方法 (说白了,就是一语法糖)
import {mapMutation} from 'vuex'
methods:{
//将mutation里的方法映射到该组件内
...mapMutation({
add:'add' //将 `this.add()` 映射为 `this.$store.commit('add')`
}),
...mapMutation([
'reduce', //将 `this.reduce` 映射为 `this.$store.commit('reduce')`
])
}
4.Action
#
- Action 提交的是
mutation
,而不是直接变更状态。 - Action 可以包含任意异步操作。
注册一个简单的Action
,并且从外界传值。
state:{
personalinfo:{
name:"Liu",
height:180,
age:27
}
},
mutations:{
add(state,payload){
stat.age += payload.number;
},
reduce(state){
state.age--
}
},
actions:{
add(context){
context.commit('add');
},
reduce(context){
context.commit('reduce')
}
}
Action 函数接受一个与 store 实例具有相同方法和属性的 context 对象,因此你可以调用 context.commit 提交一个 mutation,或者通过 context.state 和 context.getters 来获取 state 和 getters。
Action通过store.dispatch
方法触发,在Vue组件中:
methods:{
add(){
this.$store.dispath({
type:'add',
number:10
});
},
reduce(){
this.$store.dispatch('reduce');
}
}
另外的写法:参数解构
add({commit}){
commit('add');
}
4.1 mapAction
mapAction
是action
的辅助函数,便于在Vue组件中调用action里面的事件. 在Vue组件中,可以通过解构mapAction
的方式将映射
methods: {
...mapActions([
'add',
"reduce"
])
}
注意此时:用mapActiono
映射方法后需要传参那么需要写在事件触发的地方上
<button @click="add({number:10})">增加年龄</button>
4.2 组合 Action
组合Action其实就是异步处理多个commit
事件,可以相互调动action
,借助Promise
保证了数据状态的稳定性和有序性。
官方实例:
在store.js
中
actions:{
actionA({commit}){
return new Promise((resolve,reject)=>{
setTimeout(()=>{
commit('someMutation')
resolve()
},1000)
})
}
}
在vue组件中:
store.dispath('actionA').then(()=>{
})
在另外一个 action 中可以异步提交事件:
actions: {
actionB ({ dispatch, commit }) {
return dispatch('actionA').then(() => {
commit('someOtherMutation')
})
}
}
推荐阅读:
5.Module
#
当应用变得非常复杂时,store 对象就有可能变得相当臃肿,为了解决这一问题,我们可以将store
中的五种状态剥离成单个的模型,然后通过一个入口引入.一般在项目开发中在src
目录下创建一个store
的目录然后创建以下文件
└── store
├── index.js # 我们组装模块并导出 store 的地方
├── state.js # 根级别的 state
├── actions.js # 根级别的 action
├── getter.js # 根级别的 getter
├── mutations.js # 根级别的 mutation
├── mutations-type.js # mutation事件常数名
在index.js
中引入所有文件,统一挂载在Vuex实例上
import Vue from 'vue'
import Vuex from 'vuex'
import state from './state'
import mutations from './mutations'
import actions from './actions'
import getters from './getters'
Vue.use(Vuex);
export default new Vuex.Store({
state,
mutations,
actions,
getters
})
在main.js
中引入index.js
并挂载到实例Vue上
import Vue from 'vue'
import App from './App.vue'
import './assets/index.css'
import store from './store/index'
Vue.config.ProductionTip = false;
new Vue({
store,
render:h=>h(App)
}).$mount('#app');
常用项目结构:
├── index.html
├── main.js
├── api
│ └── ... # 抽取出API请求
├── components
│ ├── App.vue
│ └── ...
└── store
├── index.js # 我们组装模块并导出 store 的地方
├── state.js # 根级别的 state
├── actions.js # 根级别的 action
├── getter.js # 根级别的 getter
├── mutations.js # 根级别的 mutation
├── mutations-type.js # mutation事件常数名
└── modules
├── a.js # a模块
└── b.js # b模块
└── ...
ES6解构赋值拓展:#
1.函数中参数的解构赋值
【传了实参】传了一个对象,参数直接使用对象解构的形式
var obj = {
name: "zs",
age: 11,
email: "nodejs@163.com"
}
//实参是obj--对应形参是{ name, age, email }
//这里实际上就是{name:name,age:age,email:email}的间写
function func({ name, age, email }) {
console.log(name, age, email); //zs 11 nodejs@163.com
}
func(obj);
2.数组的解构赋值
基本
let [a, b, c] = [1, 2, 3];
// a = 1
// b = 2
// c = 3
可嵌套
let [a, [[b], c]] = [1, [[2], 3]];
// a = 1
// b = 2
// c = 3
可忽略
let [a, , b] = [1, 2, 3];
// a = 1
// b = 3
3.对象的解构赋值
基本
let { foo, bar } = { foo: 'aaa', bar: 'bbb' };
// foo = 'aaa'
// bar = 'bbb'
let { baz : foo } = { baz : 'ddd' };
// foo = 'ddd'
可嵌套可忽略
let obj = {p: ['hello', {y: 'world'}] };
let {p: [x, { y }] } = obj;
// x = 'hello'
// y = 'world'
let obj = {p: ['hello', {y: 'world'}] };
let {p: [x, { }] } = obj;
// x = 'hello'
action拓展:#
背景:mutation中是存放处理数据的方法的集合,我们使用的时候需要commit。但是commit是同步函数,而且只能是同步执行。那我们想异步操作怎么办?
作用:在actions中提交mutation,并且可以包含任何的异步操作。actions可以理解为通过将mutations里面处里数据的方法变成可异步的处理数据的方法,简单的说就是异步操作数据(但是还是通过mutation来操作,因为只有它能操作)
action
,可以操作任意的异步操作,类似于mutations
,但是是替代mutations
来进行异步操作的。首先mutations
中必须是同步方法,如果使用了异步,虽然页面上的内容改变了,但实际上Vuex.Store
没有监听到数据的更新,如下例子一
1、首先在state中有个对象
// 状态,存放一些公用的属性
state:{
personalinfo:{
name:"Liu",
age:27
}
},
2、在mutations
中写一个异步操作来修改name
值,比如用setTimeout
,因为setTimeout
是异步操作
mutations:{
changeinfosix(state){
setTimeout(function(){
state.personalinfo.name = "Rick"
},1000)
},
}
3、最后在路由组件中去调用mutations
中的这个异步操作
<template>
<div>
<h2>Vuex页面</h2>
<p>{{$store.state.personalinfo.name}}</p>
<button @click="changeinfosix">修改信息</button>
</div>
</template>
<script>
export default {
name: "Vuexsix",
methods:{
changeinfosix(){
this.$store.commit("changeinfosix")
}
}
}
</script>
发现页面上的内容更改了,但是在Devtools
中的却没有变化
解决方案:
1、在Vuex
的配置文件的actions
中,写一个方法,比如叫sixupdinfo
,并且里面有一个参数context
,这个context
可以理解为store
actions:{
sixupdinfo(context){
}
},
如果想要进行异步操作,直接在该方法中写入如下代码,也就是提交了刚刚在mutations
中的changeinfosix
方法
actions:{
sixupdinfo(context){
setTimeout(function(){
context.commit("changeinfosix")
},1000)
}
},
2、但是这个时候就需要把mutations
中的异步方法修改掉
mutations:{
changeinfosix(state){
state.personalinfo.name = "Rick"
// setTimeout(function(){
// state.personalinfo.name = "Rick"
// },1000)
},
}
3、最后,在路由组件中将调用的方法进行修改,可以看到不再是$store.commit
,而是$store.dispatch
,这个dispatch
就是该篇文章最上方的图中流程的dispatch
,也就是说想要执行actions
中的异步,必须先进行dispatch
<template>
<div>
<h2>Vuex页面</h2>
<p>{{$store.state.personalinfo.name}}</p>
<button @click="changeinfosix">修改信息</button>
</div>
</template>
<script>
export default {
name: "Vuexsix",
methods:{
changeinfosix(){
// this.$store.commit("changeinfosix")
this.$store.dispatch("sixupdinfo")
}
}
}
</script>
发现页面上的内容更改了,并且在Devtools中能看到对应的值也同时发生了改变
补充:
actions
中除了可以传context
参数,也可以传payload
参数
例二:在看完以上代码后,如果有一个需求,
需求一:不想在mutatios中用state.personalinfo.name = "Rick",这种死数据的形式修改信息,而是动态的去修改
需求二:希望在actions中传递多个参数给mutations中的方法使用
需求三:就是在actions中修改完信息后,想让路由组件知道已经修改完毕,并且继续让路由组件接着做其他的事情,再然后可以接收多个参数
以上的需求,就可以如下这么做:
1、首先,依然拿上面的例子的代码进行举例,在你的Vuex配置文件的state中有一个对象
// 状态,存放一些公用的属性
state:{
personalinfo:{
name:"Liu",
age:27
}
},
2、在mutations
中有修改信息的方法,但是会看到changeinfosix
方法中多了个payload
参数,这个参数位置是固定的,并且是用来接收参数的,至于payload.address
是什么,会在第4步看到这俩字段的来源
mutations:{
changeinfosix(state,payload){
console.log(payload)
state.personalinfo.name = payload.address
},
}
3、接下来,在actions
中通过异步操作去调用mutatios
的方法进行修改信息,再次看到payload.address
以及payload.telephone
,会在第4步看到这俩字段的来源
actions:{
sixupdinfo(context,payload){
setTimeout(function(){
context.commit("changeinfosix",{
address:payload.address,
telephone:payload.telephone
})
console.log(payload.address)
console.log(payload.telephone)
payload.success()
},1000)
}
},
4、最后,在路由组件的methods
中看到,this.$store.dispatch
调用了actions
中的sixupdinfo
方法,并且通过对象的形式传值,最后还有一个success
的回调,也就是说,信息修改完了,可以进行下一步了
<script>
export default {
name: "Vuexsix",
methods:{
changeinfosix(){
this.$store.dispatch("sixupdinfo",{
address:"上海",
telephone:"123456789",
success:() => {
console.log("已经完成,可以进行下一步")
}
})
}
}
}
</script>
最终可以看到,不仅动态修改了信息,而且传递的参数也获取到了。
@getter拓展:#
定义后state后,可以使用this.$store.state.changebleNum
在任何一个组件里面获取showfooter和changebleNum定义的值了,但这不是理想的获取方式;vuex官方API提供了一个getters,和vue计算属性computed一样,来实时监听state值的变化(最新状态),并把它也仍进Vuex.Store里面,具体看下面代码:
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
const state={ //要设置的全局访问的state对象
showFooter: true,
changableNum:0
//要设置的初始属性值
};
const getters = { //实时监听state值的变化(最新状态)
isShow(state) { //方法名随意,主要是来承载变化的showFooter的值
return state.showFooter
},
getChangedNum(){ //方法名随意,主要是用来承载变化的changableNum的值
return state.changebleNum
}
};
const store = new Vuex.Store({
state,
getters
});
export default store;
光有定义的state的初始值,不改变它不是我们想要的需求,接下来要说的就是mutations了,mutattions也是一个对象,这个对象里面可以放改变state的初始值的方法,具体的用法就是给里面的方法传入参数state或额外的参数,然后利用vue的双向数据驱动进行值的改变,同样的定义好之后也把这个mutations扔进Vuex.Store里面,如下:
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
const state={ //要设置的全局访问的state对象
showFooter: true,
changableNum:0
//要设置的初始属性值
};
const getters = { //实时监听state值的变化(最新状态)
isShow(state) { //承载变化的showFooter的值
return state.showFooter
},
getChangedNum(){ //承载变化的changebleNum的值
return state.changableNum
}
};
const mutations = {
show(state) { //自定义改变state初始值的方法,这里面的参数除了state之外还可以再传额外的参数(变量或对象);
state.showFooter = true;
},
hide(state) { //同上
state.showFooter = false;
},
newNum(state,sum){ //同上,这里面的参数除了state之外还传了需要增加的值sum
state.changableNum+=sum;
}
};
const store = new Vuex.Store({
state,
getters,
mutations
});
export default store;
这时候你完全可以用 this.$store.commit('show') 或 this.$store.commit('hide') 以及 this.$store.commit('newNum',6) 在别的组件里面进行改变showfooter和changebleNum的值了,但这不是理想的改变值的方式;因为在 Vuex 中,mutations里面的方法 都是同步事务,意思就是说:比如这里的一个this.$store.commit('newNum',sum)方法,两个组件里用执行得到的值,每次都是一样的,这样肯定不是理想的需求
好在vuex官方API还提供了一个actions,这个actions也是个对象变量,最大的作用就是里面的Action方法 可以包含任意异步操作,这里面的方法是用来异步触发mutations里面的方法,actions里面自定义的函数接收一个context参数和要变化的形参,context与store实例具有相同的方法和属性,所以它可以执行context.commit(' '),然后也不要忘了把它也扔进Vuex.Store里面:
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
const state={ //要设置的全局访问的state对象
showFooter: true,
changableNum:0
//要设置的初始属性值
};
const getters = { //实时监听state值的变化(最新状态)
isShow(state) { //承载变化的showFooter的值
return state.showFooter
},
getChangedNum(){ //承载变化的changebleNum的值
return state.changableNum
}
};
const mutations = {
show(state) { //自定义改变state初始值的方法,这里面的参数除了state之外还可以再传额外的参数(变量或对象);
state.showFooter = true;
},
hide(state) { //同上
state.showFooter = false;
},
newNum(state,sum){ //同上,这里面的参数除了state之外还传了需要增加的值sum
state.changableNum+=sum;
}
};
const actions = {
hideFooter(context) { //自定义触发mutations里函数的方法,context与store 实例具有相同方法和属性
context.commit('hide');
},
showFooter(context) { //同上注释
context.commit('show');
},
getNewNum(context,num){ //同上注释,num为要变化的形参
context.commit('newNum',num)
}
};
const store = new Vuex.Store({
state,
getters,
mutations,
actions
});
export default store;
而在外部组件里进行全局执行actions里面方法的时候,你只需要用执行
this.$store.dispatch('hideFooter')
或this.$store.dispatch('showFooter')
以及this.$store.dispatch('getNewNum',6) //6要变化的实参
这样就可以全局改变改变showfooter或changebleNum的值了
modules 模块化 以及 组件中引入 mapGetters、mapActions 和 mapStates的使用
因为在大多数的项目中,我们对于全局状态的管理并不仅仅一种情况的需求,有时有多方面的需求,比如写一个商城项目,你所用到的全局state可能是关于购物车这一块儿的也有可能是关于商品价格这一块儿的;像这样的情况我们就要考虑使用vuex中的 modules 模块化了,具体怎么使用modules呢?咱们继续一步一步的走:
首先,在store文件夹下面新建一个modules文件夹,然后在modules文件里面建立需要管理状态的js文件,既然要把不同部分的状态分开管理,那就要把它们给分成独立的状态文件了
└── store
├── modules
├── collection.js
├── footerStatus.js
而对应的store文件夹下面的index.js 里面的内容就直接改写成:
import Vue from 'vue';
import Vuex from 'vuex';
import footerStatus from './modules/footerStatus'
import collection from './modules/collection'
Vue.use(Vuex);
export default new Vuex.Store({
modules:{
footerStatus,
collection
}
});
相应的js,其中的 namespaced:true 表示当你需要在别的文件里面使用( mapGetters、mapActions 接下来会说 )时,里面的方法需要注明来自哪一个模块的方法:
//collection.js
const state={
collects:[], //初始化一个colects数组
};
const getters={
renderCollects(state){ //承载变化的collects
return state.collects;
}
};
const mutations={
pushCollects(state,items){ //如何变化collects,插入items
state.collects.push(items)
}
};
const actions={
invokePushItems(context,item){ //触发mutations里面的pushCollects ,传入数据形参item 对应到items
context.commit('pushCollects',item);
}
};
export default {
namespaced:true,//用于在全局引用此文件里的方法时标识这一个的文件名
state,
getters,
mutations,
actions
}
namespaced: true使用
使用模块中的mutations、getters、actions时候,要加上模块名,例如使用commint执行mutations时
格式:模块名/模块中的mutations
this.$store.commit(“userInfo/setUserInfo”,userInfo)
获取属性时同样加上模块名
格式:store.state.模块名.模块属性
$store.state.userInfo.userName
//footerStatus.js
const state={ //要设置的全局访问的state对象
showFooter: true,
changableNum:0
//要设置的初始属性值
};
const getters = { //实时监听state值的变化(最新状态)
isShow(state) { //承载变化的showFooter的值
return state.showFooter
},
getChangedNum(){ //承载变化的changebleNum的值
return state.changableNum
}
};
const mutations = {
show(state) { //自定义改变state初始值的方法,这里面的参数除了state之外还可以再传额外的参数(变量或对象);
state.showFooter = true;
},
hide(state) { //同上
state.showFooter = false;
},
newNum(state,sum){ //同上,这里面的参数除了state之外还传了需要增加的值sum
state.changableNum+=sum;
}
};
const actions = {
hideFooter(context) { //自定义触发mutations里函数的方法,context与store 实例具有相同方法和属性
context.commit('hide');
},
showFooter(context) { //同上注释
context.commit('show');
},
getNewNum(context,num){ //同上注释,num为要变化的形参
context.commit('newNum',num)
}
};
export default {
namespaced: true, //用于在全局引用此文里的方法时标识这一个的文件名
state,
getters,
mutations,
actions
}
这样一改就有了关于两个模块的state管理文件了 footerStatus.js和collection.js,现在你要运行当前的代码话,项目会报错!因为我们把上面的代码模块化分开了,引用的地方还没有改。接下来咱们一起来看看 mapState,mapGetters,mapActions的使用,首先 在需要用的 组件里面先导入 import {mapState,mapGetters,mapActions} from 'vuex';咱们先修正一下隐藏或显示页面底部的tabs选项卡(就是上面举的临时例子)的组件代码
<template>
<div id="app">
<router-view/>
<FooterBar v-if="isShow" />
</div>
</template>
<script>
import {mapState,mapGetters,mapActions} from 'vuex'; //先要引入
import FooterBar from '@/components/common/FooterBar'
import config from './config/index'
export default {
name: 'App',
components:{
FooterBar:FooterBar
},
data(){
return {
}
},
computed:{
...mapState({ //这里的...是超引用,ES6的语法,意思是state里有多少属性值我可以在这里放多少属性值
isShow:state=>state.footerStatus.showFooter //注意这些与上面的区别就是state.footerStatus,
//里面定义的showFooter是指footerStatus.js里state的showFooter
}),
//你也可以用下面的mapGetters来获取isShow的值,貌似下面的更简洁
/*...mapGetters('footerStatus',{ //footerStatus指的是modules文件夹下的footerStatus.js模块
isShow:'isShow' //第一个isShow是我自定义的只要对应template里v-if="isShow"就行,
//第二个isShow是对应的footerStatus.js里的getters里的isShow
})*/
},
watch:{
$route(to,from){
if(to.name=='book'||to.name=='my'){
this.$store.dispatch('footerStatus/showFooter') //这里改为'footerStatus/showFooter',
//意思是指footerStatus.js里actions里的showFooter方法
}else{
this.$store.dispatch('footerStatus/hideFooter') //同上注释
}
}
}
}
</script>
现在项目代码应该就不会报错了,好,最后咱们再来看一下mapActions的用法,实际上上面的this.$store.dispatch('footerStatus/showFooter')已经算是一种执行相应模块的action里的方法了,但有时会牵扯的事件的触发及传值,那就会有下面的mapActions用法了,还记得上面的另一个模块collection.js吗?来看一下里面的actions中的方法结构:
const state={
collects:[], //初始化一个colects数组
};
const getters={
renderCollects(state){ //承载变化的collects
return state.collects;
}
};
const mutations={
pushCollects(state,items){ //如何变化collects,插入items
state.collects.push(items)
}
};
const actions={
invokePushItems(context,item){ //触发mutations里面的pushCollects ,传入数据形参item 对应到items
context.commit('pushCollects',item);
}
};
需要传值来实时变动state.collects里的数据,那肯定要在执行它的地方进行传值了,所以下面用到它的地方我们用了个@click来执行这个invokePushItems方法了,并且传入相应的对象数据item,如下:
<template>
<div >
<section class="joinState">
<div class="joinStateHead">
<span class="h3">全国改性料通讯录</span>
<span class="joinStatus" @click="invokePushItems(item)">加入收藏列</span>
</div>
</section>
</div>
</template>
<script>
import { mapActions } from 'vuex'
export default {
components:{
conditionFilter
},
name: 'bookDetail',
data () {
return {
msg: '',
item:{
id:'01',
productName: '苹果',
price:'1.6元/斤'
}
}
},
mounted() {
this.$store.dispatch('footerStatus/hideFooter')
},
methods:{
...mapActions('collection',[ //collection是指modules文件夹下的collection.js
'invokePushItems' //collection.js文件中的actions里的方法,在上面的@click中执行并传入实参
])
}
}
</script>
这样一来,在这个组件里面操作的 collecttion.js 中的state的数据,在其他的任何的一个组件里面都会得到相应的更新变化了,获取状态的页面代码如下:
<template>
</div>
<div>
<ul>
<li v-for="(val,index) in arrList" :key="index">
<h5>{{val.productName}}</h5>
<p>{{val.price}}</p>
</li>
</ul>
</div>
</template>
<script>
import {mapState,mapGetters,mapActions} from 'vuex';
export default {
name: 'book',
data() {
return {
}
},
computed:{
// ...mapState({ //用mapState来获取collection.js里面的state的属性值
// arrList:state=>state.collection.collects
// }),
...mapGetters('collection',{ //用mapGetters来获取collection.js里面的getters
arrList:'renderCollects'
})
}
}
</script>
promise 扩展:#
本质上 Promise 是一个函数返回的对象。
在使用 Promise 时,会有以下约定:
- 在本轮 事件循环 运行完成之前,回调函数是不会被调用的。
- 即使异步操作已经完成(成功或失败),在这之后通过 then() 添加的回调函数也会被调用。
- 通过多次调用 then() 可以添加多个回调函数,它们会按照插入顺序进行执行。
1.Promise 很棒的一点就是链式调用(chaining)。
连续执行两个或者多个异步操作是一个常见的需求,在上一个操作执行成功之后,开始下一个的操作,并带着上一步操作所返回的结果。我们可以通过创造一个 Promise 链来实现这种需求。
注意:一定要有返回值,否则,callback 将无法获取上一个 Promise 的结果。(如果使用箭头函数,() => x
比 () => { return x; }
更简洁一些,但后一种保留 return
的写法才支持使用多个语句。)。
2.Catch 的后续链式操作
有可能会在一个回调失败之后继续使用链式操作,即,使用一个 catch
,这对于在链式操作中抛出一个失败之后,再次进行新的操作会很有用。请阅读下面的例子:
new Promise((resolve, reject) => {
console.log('初始化');
resolve();
})
.then(() => {
throw new Error('有哪里不对了');
console.log('执行「这个」”');
})
.catch(() => {
console.log('执行「那个」');
})
.then(() => {
console.log('执行「这个」,无论前面发生了什么');
});
输出结果如下:
初始化
执行“那个”
执行“这个”,无论前面发生了什么
axios#
什么是 axios?#
Axios 是一个基于 promise 的 HTTP 库,可以用在浏览器和 node.js 中。
创建实例#
可以使用自定义配置新建一个 axios 实例
axios.create([config])
const instance = axios.create({
baseURL: 'https://some-domain.com/api/',
timeout: 1000,
});
请求配置#
这些是创建请求时可以用的配置选项。只有 url
是必需的。如果没有指定 method
,请求将默认使用 get
方法。
{
// `url` 是用于请求的服务器 URL
url: '/user',
// `baseURL` 将自动加在 `url` 前面,除非 `url` 是一个绝对 URL。
// 它可以通过设置一个 `baseURL` 便于为 axios 实例的方法传递相对 URL
baseURL: 'https://some-domain.com/api/',
// `timeout` 指定请求超时的毫秒数(0 表示无超时时间)
// 如果请求话费了超过 `timeout` 的时间,请求将被中断
timeout: 1000,
拦截器#
在请求或响应被 then
或 catch
处理前拦截它们。
// 添加请求拦截器
axios.interceptors.request.use(function (config) {
// 在发送请求之前做些什么
return config;
}, function (error) {
// 对请求错误做些什么
return Promise.reject(error);
});
// 添加响应拦截器
axios.interceptors.response.use(function (response) {
// 对响应数据做点什么
return response;
}, function (error) {
// 对响应错误做点什么
return Promise.reject(error);
});
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?