大部分学习内容及代码在gitee仓库
生命周期 | 描述 |
---|---|
beforeCreate | 组件实例被创建之初 |
created | 组件实例已经完全创建 |
beforeMount | 组件挂载之前 |
mounted | 组件挂载到实例上去之后 |
beforeUpdate | 组件数据发生变化,更新之前 |
updated | 组件数据更新之后 |
beforeDestroy | 组件实例销毁之前 |
destroyed | 主键实例销毁之后 |
安装vue-router
npm install vue-router --save
新建router文件夹,router文件夹下新建index.js
文件
index.js
//配置路由相关的信息
import VueRouter from "vue-router";
import Vue from "vue";//1.通过Vue.use(插件),安装插件
Vue.use(VueRouter);//2.创建VueRouter对象const routes = []const router = new VueRouter({//配置路由和组件之间的应用关系routes
})//3.将router对象传入到Vue实例
export default router
main.js
中引入vue-router的实例,以及Vue实例中挂载路由实例
main.js
import Vue from 'vue'
import App from './App'
import router from "./router/index";Vue.config.productionTip = false/* eslint-disable no-new */
new Vue({el: '#app',router,render: h => h(App)
})
基本步骤完成
组件中的内容会动态显示在
的位置,其作用就是充当占位
创建路由组件
Home.vue
欢迎进入home界面
About.vue
欢迎进入about界面
配置路由映射
index.js
//配置路由相关的信息
import VueRouter from "vue-router";
import Vue from "vue";
import HelloWorld from "../components/HelloWorld";
import Home from "../components/Home";
import About from "../components/About";//1.通过Vue.use(插件),安装插件
Vue.use(VueRouter);//2.创建VueRouter对象
//配置路由映射
const routes = [{path: "/home",component: Home},{path: "/about",component: About}
]const router = new VueRouter({//配置路由和组件之间的应用关系routes
})//3.将router对象传入到Vue实例
export default router
使用路由显示
点击首页或者点击关于,其组件中的信息就会显示在
的位置
App.vue
首页 关于
显示测试
点击首页
点击关于
- 配置不需要点击路径,一进入页面就显示配置的路由页信息
- 修改url为history模式(比如默认url显示为:
/#/aaa
,history模式下url显示为/aaa
)
路由的默认值修改
会在进入页面时就显示
/home
页的信息主要修改:
{//配置路由默认信息path: '/',redirect: '/home' },
//配置路由相关的信息
import VueRouter from "vue-router";
import Vue from "vue";
import HelloWorld from "../components/HelloWorld";
import Home from "../components/Home";
import About from "../components/About";//1.通过Vue.use(插件),安装插件
Vue.use(VueRouter);//2.创建VueRouter对象//配置路由映射
const routes = [{//配置路由默认信息path: '/',redirect: '/home'},{path: "/home",component: Home},{path: "/about",component: About}
]const router = new VueRouter({//配置路由和组件之间的应用关系routes
})//3.将router对象传入到Vue实例
export default router
history的修改
主要修改:
const router = new VueRouter({ //配置路由和组件之间的应用关系 routes, //history模式修改 mode: 'history' })
//配置路由相关的信息
import VueRouter from "vue-router";
import Vue from "vue";
import HelloWorld from "../components/HelloWorld";
import Home from "../components/Home";
import About from "../components/About";//1.通过Vue.use(插件),安装插件
Vue.use(VueRouter);//2.创建VueRouter对象//配置路由映射
const routes = [{path: '/',redirect: '/home'},{path: "/home",component: Home},{path: "/about",component: About}
]const router = new VueRouter({//配置路由和组件之间的应用关系routes,配置路由相关的信息
import VueRouter from "vue-router";
import Vue from "vue";
import HelloWorld from "../components/HelloWorld";
import Home from "../components/Home";
import About from "../components/About";//1.通过Vue.use(插件),安装插件
Vue.use(VueRouter);//2.创建VueRouter对象//配置路由映射
const routes = [{path: '/',redirect: '/home'},{path: "/home",component: Home},{path: "/about",component: About}
]const router = new VueRouter({//配置路由和组件之间的应用关系routes,//history模式修改mode: 'history'
})//3.将router对象传入到Vue实例
export default routermode: 'history'
})//3.将router对象传入到Vue实例
export default router
修改完成后,url路径显示
tag
默认是使用a标签,我们可以使用tag去修改
首页
关于
replace
用户点击了该路由标签后无法点击返回
首页
关于
active-class
当用户点击
router-link
的标签的时候,其标签中会出现如下class
于是,我们可以通过设置这个class的名字的值去动态的改变我们选中的
router-link
标签的样式
建议使用方式三,前两种只是用来比较
方式一
直接在该vue的文件中创建style的,class的名字是router-link-exact-active router-link-active
方式二
我们可以修改单个标签上的class的名字为我们指定的
首页 关于
这样后,我们在style中class的名字就可以是active
但是这样做有一个弊端,我们每条router-link
都需要手动去进行设置
方式三
在路由的index.js
文件中去进行修改
const router = new VueRouter({//配置路由和组件之间的应用关系routes,//history模式修改mode: 'history',//修改router-link-exact-active router-link-active的名字为activelinkActiveClass: 'active'
})
这样就可以保证每条router-link
的router-link-exact-active router-link-active
都修改为了active
最终效果
点击哪个router-link哪个就变红
点击首页
点击关于
不使用
router-link
去进行路由跳转,使用@click
去绑定方法,在javaScript的方法中进行路由跳转
新建User.vue
界面
使用计算属性获取url中传来的值info
我是用户界面
我是用户的相关信息
{{userInfo}}
index.js中注册组件路由
注册的路由路径中绑定一个
:info
,那么在点击路径的时候可以传值近进info,进入组件后也可以获取info绑定的值
//配置路由相关的信息
import VueRouter from "vue-router";
import Vue from "vue";
import HelloWorld from "../components/HelloWorld";
import Home from "../components/Home";
import About from "../components/About";
import User from "../components/User";//1.通过Vue.use(插件),安装插件
Vue.use(VueRouter);//2.创建VueRouter对象//配置路由映射
const routes = [{path: '/',redirect: '/home'},{path: "/home",component: Home},{path: "/about",component: About},{path: "/user/:info",component: User}
]const router = new VueRouter({//配置路由和组件之间的应用关系routes,//history模式修改mode: 'history',//修改router-link-exact-active router-link-active的名字为activelinkActiveClass: 'active'
})//3.将router对象传入到Vue实例
export default router
App.vue中写入链接
router-link中去绑定要写入组件路由路径要传入的值,那么访问路径就是
"'/user/'+userInfo.name"
首页 关于
用户
测试访问
访问路径
http://localhost:8080/user/lzj
访问显示
懒加载可以增强用户的体验,在访问页面时只加载需要使用到的js文件。
路由懒加载的主要作用就是将路由对应的组件打包成一个个的js代码块.一个懒加载变成一个js文件
//配置路由相关的信息
import VueRouter from "vue-router";
import Vue from "vue";
import HelloWorld from "../components/HelloWorld";
// import Home from "../components/Home";
// import About from "../components/About";
// import User from "../components/User";//1.通过Vue.use(插件),安装插件
Vue.use(VueRouter);//2.创建VueRouter对象//配置路由映射
const routes = [{path: '/',redirect: '/home'},{path: "/home",component: ()=> import('../components/Home')},{path: "/about",component: ()=> import('../components/About')},{path: "/user/:info",component: ()=> import('../components/User')}
]const router = new VueRouter({//配置路由和组件之间的应用关系routes,//history模式修改mode: 'history',//修改router-link-exact-active router-link-active的名字为activelinkActiveClass: 'active'
})//3.将router对象传入到Vue实例
export default router
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wrulkZm6-1670513227461)(https://lzj-love-study.oss-cn-hangzhou.aliyuncs.com/blog/image-20221017193042856.png)]
children
新建HomeNews.vue和HomeMessage.vue
HomeNews.vue
- 新闻1
- 新闻2
- 新闻3
- 新闻4
HomeMessage.vue
- 消息1
- 消息2
- 消息3
- 消息4
编写index.js
- 使用children来配置嵌套路由
- 可以设置默认路由
//配置路由相关的信息
import VueRouter from "vue-router";
import Vue from "vue";
import HelloWorld from "../components/HelloWorld";
// import Home from "../components/Home";
// import About from "../components/About";
// import User from "../components/User";//1.通过Vue.use(插件),安装插件
Vue.use(VueRouter);//2.创建VueRouter对象//配置路由映射
const routes = [{path: '/',redirect: '/home'},{path: "/home",component: ()=> import('../components/Home'),children: [{path: "/",redirect: "news"},{path: "news",component: ()=> import('../components/HomeNews')},{path: "message",component: ()=> import('../components/HomeMessage')}]},{path: "/about",component: ()=> import('../components/About')},{path: "/user/:info",component: ()=> import('../components/User')}
]const router = new VueRouter({//配置路由和组件之间的应用关系routes,//history模式修改mode: 'history',//修改router-link-exact-active router-link-active的名字为activelinkActiveClass: 'active'
})//3.将router对象传入到Vue实例
export default router
Home.vue
欢迎进入home界面
新闻 消息
测试效果
传递参数主要有两种方式
params
和query
param的类型
配置路由格式:
/router/:id
传递的方式:在path后面跟上对应的值,示例:
//userInfo为自己定义的对象
用户 传递后形成的路径:
/router/123
获取值的方式:
$route.params.id
query的类型
配置路由格式:
router
,也就是普通配置传递的方式:对象中使用query的key作为传递方式,示例:
//{path: '/profile',query: {name: 'lzj',age: 18}}可以放在data中
档案 传递后形成的路径:
/router?id=123
获取值的方式:
$route.query.id
通过button使用方法的方式
路由跳转的方式采用普通button的方式
在methods中定义方法进行跳转传值
新建User.vue
我是用户界面
我是用户的相关信息
{{userInfo}}
index.js中注册组件路由
{path: "/user/:info",component: ()=> import('../components/User')}
app.vue中编写router-link
用户
测试效果
新建Profile.vue
我是profile组件
{{$route.query.name}}
{{$route.query.age}}
index.js中注册组件路由
const Profile = ()=>import('../components/Profile');{path: "/profile",component: Profile}
app.vue中编写router-link
档案
测试效果
**作用:**在进入路由前调用的钩子函数
重要代码示例:
const router = new VueRouter({ //配置路由和组件之间的应用关系 routes, //history模式修改 mode: 'history', //修改router-link-exact-active router-link-active的名字为active linkActiveClass: 'active' }) //前置守卫 router.beforeEach((to, from, next)=>{ //从from跳转到to,修改标题为meta中的数据 document.title = to.matched[0].meta.title; console.log(to); //next必须调用 next(); })
参数解析:
- to:即将要进入的目标的路由对象
- from:当前导航即将要离开的路由对象
- next:调用该方法后,才能进入下一个钩子
修改标题案例代码:
在路由配置中添加meta的数据
- meta:元数据
//配置路由相关的信息
import VueRouter from "vue-router";
import Vue from "vue";
import HelloWorld from "../components/HelloWorld";
// import Home from "../components/Home";
// import About from "../components/About";
// import User from "../components/User";//1.通过Vue.use(插件),安装插件
Vue.use(VueRouter);//2.创建VueRouter对象const Profile = ()=>import('../components/Profile');//配置路由映射
const routes = [{path: '/',redirect: '/home'},{path: "/home",component: ()=> import('../components/Home'),meta: {title : "首页"},children: [{path: "/",redirect: "news"},{path: "news",component: ()=> import('../components/HomeNews')},{path: "message",component: ()=> import('../components/HomeMessage')}]},{path: "/about",component: ()=> import('../components/About'),meta: {title: "关于"}},{path: "/user/:info",component: ()=> import('../components/User')},{path: "/profile",component: Profile,meta: {title: "档案"}}
]const router = new VueRouter({//配置路由和组件之间的应用关系routes,//history模式修改mode: 'history',//修改router-link-exact-active router-link-active的名字为activelinkActiveClass: 'active'
})//路由导航router.beforeEach((to, from, next)=>{//从from跳转到to,修改标题为meta中的数据document.title = to.matched[0].meta.title;console.log(to);//next必须调用next();
})//3.将router对象传入到Vue实例
export default router
点击首页,最终效果
**作用:**在进入路由后调用的钩子函数
重要代码示例:
const router = new VueRouter({ //配置路由和组件之间的应用关系 routes, //history模式修改 mode: 'history', //修改router-link-exact-active router-link-active的名字为active linkActiveClass: 'active' }) //后置守卫 router.afterEach((to, from)=>{ console.log("after"); })
参数解析:
- to:进入的目标的路由对象
- from:上一个离开的路由对象
**作用:**只有在进入指定路由才会触发
https://router.vuejs.org/zh/guide/advanced/navigation-guards.html#%E8%B7%AF%E7%94%B1%E7%8B%AC%E4%BA%AB%E7%9A%84%E5%AE%88%E5%8D%AB
**作用:**keep-alive是Vue内置的一个组件,可以使被包含的组件保留状态,或避免重新渲染
使用方式示例:
属性介绍:
- include:字符串或正则表达式,只有匹配的组件会被缓存
- exclude:字符串或正则表达式,任何匹配的组件都不会被缓存
**方法函数介绍:**只有该组件被
keep-alive
标签包裹,才会生效
- activated(): 该组件进入活跃状态时执行函数
- deactivated(): 该组件从活跃状态改变时执行函数
在写项目的过程中,我们常常需要使用到文件的导入引用,而有时候路径又过于复杂和多,所以我们可以在配置文件中写入别名,来方便我们的开发
配置位置:
webpack.base.conf.js
使用方式:
import引入方式:
import TabBar from "@/components/tabbar/TabBar";
html标签引入方式示例(其路径前必须加
~
):
![]()
进入build文件夹中的webpack.base.conf.js
这里有默认的@
别名来指代项目是src目录路径
在alias
中写入我们自己定义的路径别名
定义路径src/assets
的别名为assets
'assets': resolve('src/assets')
在vue文件中使用路径别名
注意,在html标签中使用时,其前面必须加
~
import的方式
使用默认别名@
在javascript中引入src/components/tabbar/TabBar
import TabBar from "@/components/tabbar/TabBar";
在html的标签中使用
使用自定义的assets
在html的标签中引入src/assets/img/123.png
使用默认别名@
在在html的标签中引入src/assets/img/123.png
Promise是异步编程的一种解决方案
基本代码示例:
//参数 -> 函数 //在执行传入的回调函数时,会传入两个参数:resolve, reject 本身他们又是函数 new Promise((resolve, reject)=>{//这个可以替换为网络请求setTimeout((data)=>{//成功的时候调用resolveresolve(data)//失败的时候调用reject,就不会进入then中了,会进入catch中},1000)}).then((data)=>{//处理回调console.log(data) }).catch((err)=>{console.log(err) })
这里简单演示了下Promise如何去使用,只包含了
resolve
的使用
Title
这里包含了
resolve
和reject
的另外一种处理方式使用,不需要使用catch
Title
异步操作中会产生三种状态,分别是:
- pending:等待状态,比如正在进行网络请求,或者定时器没有到时间
- fulfill:满足状态,当我们主动回调了resolve时,就处于该状态,并且会回调
.then()
- reject:拒绝状态,当我们主动回调了reject时,就处于该状态,并且会回调
.catch()
满足状态的调用和拒绝状态的调用都有简写语法
方式1:
return new Promise((resolve, reject)=>{resolve(data);
}).then(data => {return new Promise((resolve, reject)=>{resolve(data + "1");}).then(data => {})
})
方式二
return new Promise((resolve, reject)=>{resolve(data);
}).then(data => {return Promise.resolve(data +"1")
}).then(data => {return Promise.resolve(data +"2")
})
方式三
return new Promise((resolve, reject)=>{resolve(data);
}).then(data => {return data +"1"
}).then(data => {return data +"2"
})
方式一
return new Promise((resolve, reject)=>{resolve(data);
}).then(data => {return new Promise((resolve, reject)=>{reject(err);}).then(data => {})
}).catch(err =>{console.log(err)
})
方式二
return new Promise((resolve, reject)=>{resolve(data);
}).then(data => {return Promise.reject(err)
}).catch(err =>{console.log(err)
})
方式三
return new Promise((resolve, reject)=>{resolve(data);
}).then(data => {throw err
}).catch(err =>{console.log(err)
})
Title
如果有两个网络请求,我们需要在他们两个请求都完成后再进行相关操作,那么我们可以使用Promise的all方法。
简单示例:
Promise.all([]).then(result =>{})
简单示例:
Title
官方文档链接
Vuex是一个专为Vue.js应用程序开发的状态管理模式,它是响应式的。
简单理解就是:将多个组件共享的变量全部存储在一个对象里面,每个组件都可以去使用和改变它
Vuex遵循State单一状态树
Vuex状态管理图例:
在谷歌商店中搜索即可
配置完成后可以在
.vue
文件中使用$store
来使用其数据
下载Vuex
npm install vuex@3.6.1 --save
配置Vuex
src目录下新建store目录
store目录中新建index.js
index.js
import Vue from "vue";
import Vuex from "vuex";//1.安装插件
Vue.use(Vuex);//2.创建对象
const store = new Vuex.Store({//状态state:{},mutations:{},actions:{},getters:{},modules:{}
})//导出Store
export default store
main.js
中引入store
- import引入store
- Vue中注册store
import Vue from 'vue'
import App from './App'
import router from './router'
import store from "./store";Vue.config.productionTip = false/* eslint-disable no-new */
new Vue({el: '#app',router,store,render: h => h(App)
})
编写测试案例(计数案例)
编写store目录下的index.js
import Vue from "vue";
import Vuex from "vuex";//1.安装插件
Vue.use(Vuex);//2.创建对象
const store = new Vuex.Store({//状态state:{counter: 0},mutations:{//方法increment(state){state.counter++;},decrement(state){state.counter--;}},actions:{},getters:{},modules:{}
})//导出Store
export default store
编写App.vue
使用
this.$store.commit()
来获取我们定义的mutations
{{message}}
{{$store.state.counter}}
新建HelloVuex.vue组件
{{$store.state.counter}}
结束
点击App.vue中的按钮,HelloVuex.vue中的$store.state.counter
也会进行改变
类似于vue中的计算属性
获取年龄大于20岁的学生
- 在Getters中去获取,使用filter过滤
- 也可以让用户自定义输入年龄参数,那么我们需要使用到函数
index.js
import Vue from "vue";
import Vuex from "vuex";//1.安装插件
Vue.use(Vuex);//2.创建对象
const store = new Vuex.Store({//状态state:{counter: 0,students:[{name: "lzj1",age: 15},{name: "lzj2",age: 18},{name: "lzj3",age: 23},{name: "lzj4",age: 24},]},mutations:{//方法increment(state){state.counter++;},decrement(state){state.counter--;}},actions:{},getters:{more20stu: state => {return state.students.filter(s => s.age >=20);},more20stu2: state => {return age =>{return state.students.filter(s => s.age >= age);}}},modules:{}
})//导出Store
export default store
App.vue
{{message}}
{{$store.state.counter}}
自带年龄
{{$store.getters.more20stu}}
自定义输入年龄
{{$store.getters.more20stu2(15)}}
Vuex的store状态的唯一更新方式:提交Mutation
更改 Vuex 的 store 中的状态的唯一方法是提交 mutation。Vuex 中的 mutation 非常类似于事件:每个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler)。这个回调函数就是我们实际进行状态更改的地方,并且它会接受 state 作为第一个参数:
const store = new Vuex.Store({state: {count: 1},mutations: {increment (state) {// 变更状态state.count++}}
})
你不能直接调用一个 mutation handler。这个选项更像是事件注册:“当触发一个类型为 increment
的 mutation 时,调用此函数。”要唤醒一个 mutation handler,你需要以相应的 type 调用 store.commit 方法:
store.commit('increment')
你可以向 store.commit
传入额外的参数,即 mutation 的 载荷(payload):
// ...
mutations: {increment (state, n) {state.count += n}
}
store.commit('increment', 10)
在大多数情况下,载荷应该是一个对象,这样可以包含多个字段并且记录的 mutation 会更易读:
// ...
mutations: {increment (state, payload) {state.count += payload.amount}
}
store.commit('increment', {amount: 10
})
提交 mutation 的另一种方式是直接使用包含 type
属性的对象:
store.commit({type: 'increment',amount: 10
})
当使用对象风格的提交方式,整个对象都作为载荷传给 mutation 函数,因此 handler 保持不变:
mutations: {increment (state, payload) {state.count += payload.amount}
}
既然 Vuex 的 store 中的状态是响应式的,那么当我们变更状态时,监视状态的 Vue 组件也会自动更新。这也意味着 Vuex 中的 mutation 也需要与使用 Vue 一样遵守一些注意事项:
使用 Vue.set(obj, 'newProp', 123)
, 或者
以新对象替换老对象。例如,利用对象展开运算符 (opens new window)我们可以这样写:
state.obj = { ...state.obj, newProp: 123 }
使用常量替代 mutation 事件类型在各种 Flux 实现中是很常见的模式。这样可以使 linter 之类的工具发挥作用,同时把这些常量放在单独的文件中可以让你的代码合作者对整个 app 包含的 mutation 一目了然:
mutation-types.js
// mutation-types.js
export const SOME_MUTATION = 'SOME_MUTATION'
store.js
// store.js
import Vuex from 'vuex'
import { SOME_MUTATION } from './mutation-types'const store = new Vuex.Store({state: { ... },mutations: {// 我们可以使用 ES2015 风格的计算属性命名功能来使用一个常量作为函数名[SOME_MUTATION] (state) {// mutate state}}
})
用不用常量取决于你——在需要多人协作的大型项目中,这会很有帮助。但如果你不喜欢,你完全可以不这样做。
一条重要的原则就是要记住 mutation 必须是同步函数。为什么?请参考下面的例子:
如果需要使用异步函数,则可以使用
Action
mutations: {someMutation (state) {api.callAsyncMethod(() => {state.count++})}
}
现在想象,我们正在 debug 一个 app 并且观察 devtool 中的 mutation 日志。每一条 mutation 被记录,devtools 都需要捕捉到前一状态和后一状态的快照。然而,在上面的例子中 mutation 中的异步函数中的回调让这不可能完成:因为当 mutation 触发的时候,回调函数还没有被调用,devtools 不知道什么时候回调函数实际上被调用——实质上任何在回调函数中进行的状态的改变都是不可追踪的。
你可以在组件中使用 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 类似于 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.state
和 context.getters
来获取 state 和 getters。当我们在之后介绍到 Modules 时,你就知道 context 对象为什么不是 store 实例本身了。
实践中,我们会经常用到 ES2015 的 参数解构 (opens new window)来简化代码(特别是我们需要调用 commit
很多次的时候):
actions: {increment ({ commit }) {commit('increment')}
}
Action 通过 store.dispatch
方法触发:
store.dispatch('increment')
乍一眼看上去感觉多此一举,我们直接分发 mutation 岂不更方便?实际上并非如此,还记得 mutation 必须同步执行这个限制么?Action 就不受约束!我们可以在 action 内部执行异步操作:
actions: {incrementAsync ({ commit }) {setTimeout(() => {commit('increment')}, 1000)}
}
Actions 支持同样的载荷方式和对象方式进行分发:
// 以载荷形式分发
store.dispatch('incrementAsync', {amount: 10
})// 以对象形式分发
store.dispatch({type: 'incrementAsync',amount: 10
})
来看一个更加实际的购物车示例,涉及到调用异步 API 和分发多重 mutation:
actions: {checkout ({ commit, state }, products) {// 把当前购物车的物品备份起来const savedCartItems = [...state.cart.added]// 发出结账请求,然后乐观地清空购物车commit(types.CHECKOUT_REQUEST)// 购物 API 接受一个成功回调和一个失败回调shop.buyProducts(products,// 成功操作() => commit(types.CHECKOUT_SUCCESS),// 失败操作() => commit(types.CHECKOUT_FAILURE, savedCartItems))}
}
注意我们正在进行一系列的异步操作,并且通过提交 mutation 来记录 action 产生的副作用(即状态变更)。
你在组件中使用 this.$store.dispatch('xxx')
分发 action,或者使用 mapActions
辅助函数将组件的 methods 映射为 store.dispatch
调用(需要先在根节点注入 store
):
import { mapActions } from 'vuex'export default {// ...methods: {...mapActions(['increment', // 将 `this.increment()` 映射为 `this.$store.dispatch('increment')`// `mapActions` 也支持载荷:'incrementBy' // 将 `this.incrementBy(amount)` 映射为 `this.$store.dispatch('incrementBy', amount)`]),...mapActions({add: 'increment' // 将 `this.add()` 映射为 `this.$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)})}
}
现在你可以:
store.dispatch('actionA').then(() => {// ...
})
在另外一个 action 中也可以:
actions: {// ...actionB ({ dispatch, commit }) {return dispatch('actionA').then(() => {commit('someOtherMutation')})}
}
最后,如果我们利用 async / await (opens new window),我们可以如下组合 action:
// 假设 getData() 和 getOtherData() 返回的是 Promiseactions: {async actionA ({ commit }) {commit('gotData', await getData())},async actionB ({ dispatch, commit }) {await dispatch('actionA') // 等待 actionA 完成commit('gotOtherData', await getOtherData())}
}
一个
store.dispatch
在不同模块中可以触发多个 action 函数。在这种情况下,只有当所有触发函数完成后,返回的 Promise 才会执行。
由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,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 的状态
对于模块内部的 mutation 和 getter,接收的第一个参数是模块的局部状态对象。
const moduleA = {state: () => ({count: 0}),mutations: {increment (state) {// 这里的 `state` 对象是模块的局部状态state.count++}},getters: {doubleCount (state) {return state.count * 2}}
}
同样,对于模块内部的 action,局部状态通过 context.state
暴露出来,根节点状态则为 context.rootState
:
const moduleA = {// ...actions: {incrementIfOddOnRootSum ({ state, commit, rootState }) {if ((state.count + rootState.count) % 2 === 1) {commit('increment')}}}
}
对于模块内部的 getter,根节点状态会作为第三个参数暴露出来:
const moduleA = {// ...getters: {sumWithRootCount (state, getters, rootState) {return state.count + rootState.count}}
}
默认情况下,模块内部的 action、mutation 和 getter 是注册在全局命名空间的——这样使得多个模块能够对同一 mutation 或 action 作出响应。
如果希望你的模块具有更高的封装度和复用性,你可以通过添加 namespaced: true
的方式使其成为带命名空间的模块。当模块被注册后,它的所有 getter、action 及 mutation 都会自动根据模块注册的路径调整命名。例如:
const store = new Vuex.Store({modules: {account: {namespaced: true,// 模块内容(module assets)state: () => ({ ... }), // 模块内的状态已经是嵌套的了,使用 `namespaced` 属性不会对其产生影响getters: {isAdmin () { ... } // -> getters['account/isAdmin']},actions: {login () { ... } // -> dispatch('account/login')},mutations: {login () { ... } // -> commit('account/login')},// 嵌套模块modules: {// 继承父模块的命名空间myPage: {state: () => ({ ... }),getters: {profile () { ... } // -> getters['account/profile']}},// 进一步嵌套命名空间posts: {namespaced: true,state: () => ({ ... }),getters: {popular () { ... } // -> getters['account/posts/popular']}}}}}
})
启用了命名空间的 getter 和 action 会收到局部化的 getter
,dispatch
和 commit
。换言之,你在使用模块内容(module assets)时不需要在同一模块内额外添加空间名前缀。更改 namespaced
属性后不需要修改模块内的代码。
如果你希望使用全局 state 和 getter,rootState
和 rootGetters
会作为第三和第四参数传入 getter,也会通过 context
对象的属性传入 action。
若需要在全局命名空间内分发 action 或提交 mutation,将 { root: true }
作为第三参数传给 dispatch
或 commit
即可。
modules: {foo: {namespaced: true,getters: {// 在这个模块的 getter 中,`getters` 被局部化了// 你可以使用 getter 的第四个参数来调用 `rootGetters`someGetter (state, getters, rootState, rootGetters) {getters.someOtherGetter // -> 'foo/someOtherGetter'rootGetters.someOtherGetter // -> 'someOtherGetter'},someOtherGetter: state => { ... }},actions: {// 在这个模块中, dispatch 和 commit 也被局部化了// 他们可以接受 `root` 属性以访问根 dispatch 或 commitsomeAction ({ dispatch, commit, getters, rootGetters }) {getters.someGetter // -> 'foo/someGetter'rootGetters.someGetter // -> 'someGetter'dispatch('someOtherAction') // -> 'foo/someOtherAction'dispatch('someOtherAction', null, { root: true }) // -> 'someOtherAction'commit('someMutation') // -> 'foo/someMutation'commit('someMutation', null, { root: true }) // -> 'someMutation'},someOtherAction (ctx, payload) { ... }}}
}
若需要在带命名空间的模块注册全局 action,你可添加 root: true
,并将这个 action 的定义放在函数 handler
中。例如:
{actions: {someOtherAction ({dispatch}) {dispatch('someAction')}},modules: {foo: {namespaced: true,actions: {someAction: {root: true,handler (namespacedContext, payload) { ... } // -> 'someAction'}}}}
}
当使用 mapState
, mapGetters
, mapActions
和 mapMutations
这些函数来绑定带命名空间的模块时,写起来可能比较繁琐:
computed: {...mapState({a: state => state.some.nested.module.a,b: state => state.some.nested.module.b})
},
methods: {...mapActions(['some/nested/module/foo', // -> this['some/nested/module/foo']()'some/nested/module/bar' // -> this['some/nested/module/bar']()])
}
对于这种情况,你可以将模块的空间名称字符串作为第一个参数传递给上述函数,这样所有绑定都会自动将该模块作为上下文。于是上面的例子可以简化为:
computed: {...mapState('some/nested/module', {a: state => state.a,b: state => state.b})
},
methods: {...mapActions('some/nested/module', ['foo', // -> this.foo()'bar' // -> this.bar()])
}
而且,你可以通过使用 createNamespacedHelpers
创建基于某个命名空间辅助函数。它返回一个对象,对象里有新的绑定在给定命名空间值上的组件绑定辅助函数:
import { createNamespacedHelpers } from 'vuex'const { mapState, mapActions } = createNamespacedHelpers('some/nested/module')export default {computed: {// 在 `some/nested/module` 中查找...mapState({a: state => state.a,b: state => state.b})},methods: {// 在 `some/nested/module` 中查找...mapActions(['foo','bar'])}
}
如果你开发的插件(Plugin)提供了模块并允许用户将其添加到 Vuex store,可能需要考虑模块的空间名称问题。对于这种情况,你可以通过插件的参数对象来允许用户指定空间名称:
// 通过插件的参数对象得到空间名称
// 然后返回 Vuex 插件函数
export function createPlugin (options = {}) {return function (store) {// 把空间名字添加到插件模块的类型(type)中去const namespace = options.namespace || ''store.dispatch(namespace + 'pluginAction')}
}
在 store 创建之后,你可以使用 store.registerModule
方法注册模块:
import Vuex from 'vuex'const store = new Vuex.Store({ /* 选项 */ })// 注册模块 `myModule`
store.registerModule('myModule', {// ...
})
// 注册嵌套模块 `nested/myModule`
store.registerModule(['nested', 'myModule'], {// ...
})
之后就可以通过 store.state.myModule
和 store.state.nested.myModule
访问模块的状态。
模块动态注册功能使得其他 Vue 插件可以通过在 store 中附加新模块的方式来使用 Vuex 管理状态。例如,vuex-router-sync
(opens new window)插件就是通过动态注册模块将 vue-router 和 vuex 结合在一起,实现应用的路由状态管理。
你也可以使用 store.unregisterModule(moduleName)
来动态卸载模块。注意,你不能使用此方法卸载静态模块(即创建 store 时声明的模块)。
注意,你可以通过 store.hasModule(moduleName)
方法检查该模块是否已经被注册到 store。
在注册一个新 module 时,你很有可能想保留过去的 state,例如从一个服务端渲染的应用保留 state。你可以通过 preserveState
选项将其归档:store.registerModule('a', module, { preserveState: true })
。
当你设置 preserveState: true
时,该模块会被注册,action、mutation 和 getter 会被添加到 store 中,但是 state 不会。这里假设 store 的 state 已经包含了这个 module 的 state 并且你不希望将其覆写。
有时我们可能需要创建一个模块的多个实例,例如:
runInNewContext
选项是 false
或 'once'
时,为了在服务端渲染中避免有状态的单例 (opens new window))如果我们使用一个纯对象来声明模块的状态,那么这个状态对象会通过引用被共享,导致状态对象被修改时 store 或模块间数据互相污染的问题。
实际上这和 Vue 组件内的 data
是同样的问题。因此解决办法也是相同的——使用一个函数来声明模块状态(仅 2.3.0+ 支持):
const MyReusableModule = {state: () => ({foo: 'bar'}),// mutation, action 和 getter 等等...
}
Vuex 并不限制你的代码结构。但是,它规定了一些需要遵守的规则:
只要你遵守以上规则,如何组织代码随你便。如果你的 store 文件太大,只需将 action、mutation 和 getter 分割到单独的文件。
对于大型应用,我们会希望把 Vuex 相关代码分割到模块中。下面是项目结构示例:
├── index.html
├── main.js
├── api
│ └── ... # 抽取出API请求
├── components
│ ├── App.vue
│ └── ...
└── store├── index.js # 我们组装模块并导出 store 的地方├── actions.js # 根级别的 action├── mutations.js # 根级别的 mutation└── modules├── cart.js # 购物车模块└── products.js # 产品模块
官方文档:https://axios-http.com/zh/docs/intro
Axios 是一个基于 promise 网络请求库,作用于
node.js
和浏览器中。 它是 isomorphic 的(即同一套代码可以运行在浏览器和node.js中)。在服务端它使用原生 node.jshttp
模块, 而在客户端 (浏览端) 则使用 XMLHttpRequests。
运行下载命令
npm install axios --save
简单使用axios
引入axios和使用axios
import Vue from 'vue'
import App from './App'
import router from './router'
import axios from "axios";Vue.config.productionTip = false/* eslint-disable no-new */
new Vue({el: '#app',router,render: h => h(App)
})axios({// method: "get",url: "https://mockapi.eolink.com/4kerZSi97ab1195a22fd3d0e63f397de74e237788a9429a/info"
}).then(res =>{console.log(res);
})axios({method: "post",url: "https://mockapi.eolink.com/4kerZSi97ab1195a22fd3d0e63f397de74e237788a9429a/list"
}).then(res =>{console.log(res);
})
可以使用自定义配置新建一个实例。
axios.create([config])
const instance = axios.create({baseURL: 'https://some-domain.com/api/',timeout: 1000,headers: {'X-Custom-Header': 'foobar'}
});
以下是可用的实例方法。指定的配置将与实例的配置合并。
这些是创建请求时可以用的配置选项。只有 url
是必需的。如果没有指定 method
,请求将默认使用 GET
方法。
{// `url` 是用于请求的服务器 URLurl: '/user',// `method` 是创建请求时使用的方法method: 'get', // 默认值// `baseURL` 将自动加在 `url` 前面,除非 `url` 是一个绝对 URL。// 它可以通过设置一个 `baseURL` 便于为 axios 实例的方法传递相对 URLbaseURL: 'https://some-domain.com/api/',// `transformRequest` 允许在向服务器发送前,修改请求数据// 它只能用于 'PUT', 'POST' 和 'PATCH' 这几个请求方法// 数组中最后一个函数必须返回一个字符串, 一个Buffer实例,ArrayBuffer,FormData,或 Stream// 你可以修改请求头。transformRequest: [function (data, headers) {// 对发送的 data 进行任意转换处理return data;}],// `transformResponse` 在传递给 then/catch 前,允许修改响应数据transformResponse: [function (data) {// 对接收的 data 进行任意转换处理return data;}],// 自定义请求头headers: {'X-Requested-With': 'XMLHttpRequest'},// `params` 是与请求一起发送的 URL 参数// 必须是一个简单对象或 URLSearchParams 对象params: {ID: 12345},// `paramsSerializer`是可选方法,主要用于序列化`params`// (e.g. https://www.npmjs.com/package/qs, http://api.jquery.com/jquery.param/)paramsSerializer: function (params) {return Qs.stringify(params, {arrayFormat: 'brackets'})},// `data` 是作为请求体被发送的数据// 仅适用 'PUT', 'POST', 'DELETE 和 'PATCH' 请求方法// 在没有设置 `transformRequest` 时,则必须是以下类型之一:// - string, plain object, ArrayBuffer, ArrayBufferView, URLSearchParams// - 浏览器专属: FormData, File, Blob// - Node 专属: Stream, Bufferdata: {firstName: 'Fred'},// 发送请求体数据的可选语法// 请求方式 post// 只有 value 会被发送,key 则不会data: 'Country=Brasil&City=Belo Horizonte',// `timeout` 指定请求超时的毫秒数。// 如果请求时间超过 `timeout` 的值,则请求会被中断timeout: 1000, // 默认值是 `0` (永不超时)// `withCredentials` 表示跨域请求时是否需要使用凭证withCredentials: false, // default// `adapter` 允许自定义处理请求,这使测试更加容易。// 返回一个 promise 并提供一个有效的响应 (参见 lib/adapters/README.md)。adapter: function (config) {/* ... */},// `auth` HTTP Basic Authauth: {username: 'janedoe',password: 's00pers3cret'},// `responseType` 表示浏览器将要响应的数据类型// 选项包括: 'arraybuffer', 'document', 'json', 'text', 'stream'// 浏览器专属:'blob'responseType: 'json', // 默认值// `responseEncoding` 表示用于解码响应的编码 (Node.js 专属)// 注意:忽略 `responseType` 的值为 'stream',或者是客户端请求// Note: Ignored for `responseType` of 'stream' or client-side requestsresponseEncoding: 'utf8', // 默认值// `xsrfCookieName` 是 xsrf token 的值,被用作 cookie 的名称xsrfCookieName: 'XSRF-TOKEN', // 默认值// `xsrfHeaderName` 是带有 xsrf token 值的http 请求头名称xsrfHeaderName: 'X-XSRF-TOKEN', // 默认值// `onUploadProgress` 允许为上传处理进度事件// 浏览器专属onUploadProgress: function (progressEvent) {// 处理原生进度事件},// `onDownloadProgress` 允许为下载处理进度事件// 浏览器专属onDownloadProgress: function (progressEvent) {// 处理原生进度事件},// `maxContentLength` 定义了node.js中允许的HTTP响应内容的最大字节数maxContentLength: 2000,// `maxBodyLength`(仅Node)定义允许的http请求内容的最大字节数maxBodyLength: 2000,// `validateStatus` 定义了对于给定的 HTTP状态码是 resolve 还是 reject promise。// 如果 `validateStatus` 返回 `true` (或者设置为 `null` 或 `undefined`),// 则promise 将会 resolved,否则是 rejected。validateStatus: function (status) {return status >= 200 && status < 300; // 默认值},// `maxRedirects` 定义了在node.js中要遵循的最大重定向数。// 如果设置为0,则不会进行重定向maxRedirects: 5, // 默认值// `socketPath` 定义了在node.js中使用的UNIX套接字。// e.g. '/var/run/docker.sock' 发送请求到 docker 守护进程。// 只能指定 `socketPath` 或 `proxy` 。// 若都指定,这使用 `socketPath` 。socketPath: null, // default// `httpAgent` and `httpsAgent` define a custom agent to be used when performing http// and https requests, respectively, in node.js. This allows options to be added like// `keepAlive` that are not enabled by default.httpAgent: new http.Agent({ keepAlive: true }),httpsAgent: new https.Agent({ keepAlive: true }),// `proxy` 定义了代理服务器的主机名,端口和协议。// 您可以使用常规的`http_proxy` 和 `https_proxy` 环境变量。// 使用 `false` 可以禁用代理功能,同时环境变量也会被忽略。// `auth`表示应使用HTTP Basic auth连接到代理,并且提供凭据。// 这将设置一个 `Proxy-Authorization` 请求头,它会覆盖 `headers` 中已存在的自定义 `Proxy-Authorization` 请求头。// 如果代理服务器使用 HTTPS,则必须设置 protocol 为`https`proxy: {protocol: 'https',host: '127.0.0.1',port: 9000,auth: {username: 'mikeymike',password: 'rapunz3l'}},// see https://axios-http.com/zh/docs/cancellationcancelToken: new CancelToken(function (cancel) {}),// `decompress` indicates whether or not the response body should be decompressed // automatically. If set to `true` will also remove the 'content-encoding' header // from the responses objects of all decompressed responses// - Node only (XHR cannot turn off decompression)decompress: true // 默认值}
一个请求的响应包含以下信息。
{// `data` 由服务器提供的响应data: {},// `status` 来自服务器响应的 HTTP 状态码status: 200,// `statusText` 来自服务器响应的 HTTP 状态信息statusText: 'OK',// `headers` 是服务器响应头// 所有的 header 名称都是小写,而且可以使用方括号语法访问// 例如: `response.headers['content-type']`headers: {},// `config` 是 `axios` 请求的配置信息config: {},// `request` 是生成此响应的请求// 在node.js中它是最后一个ClientRequest实例 (in redirects),// 在浏览器中则是 XMLHttpRequest 实例request: {}
}
当使用 then
时,您将接收如下响应:
axios.get('/user/12345').then(function (response) {console.log(response.data);console.log(response.status);console.log(response.statusText);console.log(response.headers);console.log(response.config);});
当使用 catch
,或者传递一个rejection callback作为 then
的第二个参数时,响应可以通过 error
对象被使用,正如在错误处理部分解释的那样。
您可以指定默认配置,它将作用于每个请求。
axios.defaults.baseURL = 'https://api.example.com';
axios.defaults.headers.common['Authorization'] = AUTH_TOKEN;
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
// 创建实例时配置默认值
const instance = axios.create({baseURL: 'https://api.example.com'
});// 创建实例后修改默认值
instance.defaults.headers.common['Authorization'] = AUTH_TOKEN;
配置将会按优先级进行合并。它的顺序是:在lib/defaults.js中找到的库默认值,然后是实例的 defaults
属性,最后是请求的 config
参数。后面的优先级要高于前面的。下面有一个例子。
// 使用库提供的默认配置创建实例
// 此时超时配置的默认值是 `0`
const instance = axios.create();// 重写库的超时默认值
// 现在,所有使用此实例的请求都将等待2.5秒,然后才会超时
instance.defaults.timeout = 2500;// 重写此请求的超时时间,因为该请求需要很长时间
instance.get('/longRequest', {timeout: 5000
});
在请求或响应被 then 或 catch 处理前拦截它们。
// 添加请求拦截器
axios.interceptors.request.use(function (config) {// 在发送请求之前做些什么return config;}, function (error) {// 对请求错误做些什么return Promise.reject(error);});// 添加响应拦截器
axios.interceptors.response.use(function (response) {// 2xx 范围内的状态码都会触发该函数。// 对响应数据做点什么return response;}, function (error) {// 超出 2xx 范围的状态码都会触发该函数。// 对响应错误做点什么return Promise.reject(error);});
如果你稍后需要移除拦截器,可以这样:
const myInterceptor = axios.interceptors.request.use(function () {/*...*/});
axios.interceptors.request.eject(myInterceptor);
可以给自定义的 axios 实例添加拦截器。
const instance = axios.create();
instance.interceptors.request.use(function () {/*...*/});
axios.get('/user/12345').catch(function (error) {if (error.response) {// 请求成功发出且服务器也响应了状态码,但状态代码超出了 2xx 的范围console.log(error.response.data);console.log(error.response.status);console.log(error.response.headers);} else if (error.request) {// 请求已经成功发起,但没有收到响应// `error.request` 在浏览器中是 XMLHttpRequest 的实例,// 而在node.js中是 http.ClientRequest 的实例console.log(error.request);} else {// 发送请求时出了点问题console.log('Error', error.message);}console.log(error.config);});
使用 validateStatus
配置选项,可以自定义抛出错误的 HTTP code。
axios.get('/user/12345', {validateStatus: function (status) {return status < 500; // 处理状态码小于500的情况}
})
使用 toJSON
可以获取更多关于HTTP错误的信息。
axios.get('/user/12345').catch(function (error) {console.log(error.toJSON());});
从 v0.22.0
开始,Axios 支持以 fetch API 方式—— AbortController
取消请求:
const controller = new AbortController();axios.get('/foo/bar', {signal: controller.signal
}).then(function(response) {//...
});
// 取消请求
controller.abort()
deprecated
您还可以使用 cancel token 取消一个请求。
Axios 的 cancel token API 是基于被撤销 cancelable promises proposal。
此 API 从
v0.22.0
开始已被弃用,不应在新项目中使用。
可以使用 CancelToken.source
工厂方法创建一个 cancel token ,如下所示:
const CancelToken = axios.CancelToken;
const source = CancelToken.source();axios.get('/user/12345', {cancelToken: source.token
}).catch(function (thrown) {if (axios.isCancel(thrown)) {console.log('Request canceled', thrown.message);} else {// 处理错误}
});axios.post('/user/12345', {name: 'new name'
}, {cancelToken: source.token
})// 取消请求(message 参数是可选的)
source.cancel('Operation canceled by the user.');
也可以通过传递一个 executor 函数到 CancelToken
的构造函数来创建一个 cancel token:
const CancelToken = axios.CancelToken;
let cancel;axios.get('/user/12345', {cancelToken: new CancelToken(function executor(c) {// executor 函数接收一个 cancel 函数作为参数cancel = c;})
});// 取消请求
cancel();
注意: 可以使用同一个 cancel token 或 signal 取消多个请求。
在过渡期间,您可以使用这两种取消 API,即使是针对同一个请求:
const controller = new AbortController();const CancelToken = axios.CancelToken;
const source = CancelToken.source();axios.get('/user/12345', {cancelToken: source.token,signal: controller.signal
}).catch(function (thrown) {if (axios.isCancel(thrown)) {console.log('Request canceled', thrown.message);} else {// 处理错误}
});axios.post('/user/12345', {name: 'new name'
}, {cancelToken: source.token
})// 取消请求 (message 参数是可选的)
source.cancel('Operation canceled by the user.');
// 或
controller.abort(); // 不支持 message 参数
创建network目录
network目录下创建request.js文件
request.js
import axios from "axios";//方式1,直接返回instance
export function request(config){//1.创建axios的实例const instance = axios.create({baseURL: "https://mockapi.eolink.com/4kerZSi97ab1195a22fd3d0e63f397de74e237788a9429a",timeout: 5000})//返回的本身就是Promise对象return instance(config)}//方式二:返回Promise对象
// export function request(config){
// return new Promise(((resolve, reject) => {
// //1.创建axios的实例
// const instance = axios.create({
// baseURL: "https://mockapi.eolink.com/4kerZSi97ab1195a22fd3d0e63f397de74e237788a9429a",
// timeout: 5000
// })
//
// instance(config)
// .then( res =>{
// resolve(res)
// }).catch( err =>{
// reject(err)
// })
//
//
// }))
//
// }//方式三:返回回调函数
// export function request(config){
// //1.创建axios的实例
// const instance = axios.create({
// baseURL: "https://mockapi.eolink.com/4kerZSi97ab1195a22fd3d0e63f397de74e237788a9429a",
// timeout: 5000
// })
// instance(config.baseConfig)
// .then( res =>{
// config.success(res);
// }).catch( err =>{
// config.failure(err);
// })
//
// }
其它文件中调用封装好的axios请求
//5.封装request模块
import {request} from "./network/request";//封装方式三的调用方法
// request({
// baseConfig: {
// url: "/info"
// },
// success: res =>{
// console.log(res);
// },
// failure: err=>{
// console.log(err);
// }
// })//封装方式-、二的调用方式
request({url: "/info"
}).then(res =>{console.log(res);
}).catch(err =>{console.log(err);
})
这个只是封装的模板,可以在基础上进行再封装
下载axios
npm install axios --save
创建./network/http.js
http.js
import axios from "axios";//设置全局超时时间10s
axios.defaults.timeout = 10000;//创建axios实例
const instance = axios.create({baseURL: "",
})//http request 拦截器
instance.interceptors.request.use(config => {// const token = getCookie('名称');注意使用的时候需要引入cookie方法,推荐js-cookieconfig.data = JSON.stringify(config.data);config.headers = {'Content-Type':'application/x-www-form-urlencoded'}// if(token){// config.params = {'token':token}// }return config;},error => {return Promise.reject(error);}
)//http response 拦截器
instance.interceptors.response.use(response => {// if(response.data.errCode ==2){// router.push({// path:"/login",// query:{redirect:router.currentRoute.fullPath}//从哪个页面跳转// })// }return response;},error => {return Promise.reject(error)}
)/*** 封装get方法* @param url* @param params* @returns {Promise}*/export function fetch(url,params={}){return new Promise((resolve,reject) => {instance.get(url,{params:params}).then(response => {resolve(response.data);}).catch(err => {reject(err)})})
}/*** 封装post请求* @param url* @param data* @returns {Promise}*/export function post(url,data = {}){return new Promise((resolve,reject) => {instance.post(url,data).then(response => {resolve(response.data);},err => {reject(err)})})
}/*** 封装patch请求* @param url* @param data* @returns {Promise}*/export function patch(url,data = {}){return new Promise((resolve,reject) => {instance.patch(url,data).then(response => {resolve(response.data);},err => {reject(err)})})
}/*** 封装put请求* @param url* @param data* @returns {Promise}*/export function put(url,data = {}){return new Promise((resolve,reject) => {instance.put(url,data).then(response => {resolve(response.data);},err => {reject(err)})})
}
在main.js
中引入
将方法放入Vue原型全局变量中
import {post,fetch,patch,put} from './network/http'//定义全局变量
Vue.prototype.$post=post;
Vue.prototype.$fetch=fetch;
Vue.prototype.$patch=patch;
Vue.prototype.$put=put;
在组件中进行使用
mounted() { this.$fetch("https://mockapi.eolink.com/4kerZSi97ab1195a22fd3d0e63f397de74e237788a9429a/info").then(res =>{console.log(res);})
}
结果
需求:
1.更加模块化
2.更方便多人开发,有效减少解决命名冲突
3.处理接口域名有多个情况
这里这里呢新建了一个api文件夹,里面有一个index.js和一个base.js,以及多个根据模块划分的接口js文件。index.js是一个api的出口,base.js管理接口域名,其他js则用来管理各个模块的接口。
新建./api/index.js
index.js是一个api接口的出口,这样就可以把api接口根据功能划分为多个模块,利于多人协作开发,比如一个人只负责一个模块的开发等,还能方便每个模块中接口的命名哦。
/** * api接口的统一出口*/
// 文章模块接口
import article from '@/api/article';
// 其他模块的接口……// 导出接口
export default { article,// ……
}
新建./api/base.js
通过base.js来管理我们的接口域名,不管有多少个都可以通过这里进行接口的定义。即使修改起来,也是很方便的。
最后就是接口模块的说明,例如上面的article.js
/*** 接口域名的管理*/
const base = { sq: 'https://xxxx111111.com/api/v1', bd: 'http://xxxxx22222.com/api'
}export default base
新建./api/acticle.js
/*** article模块接口列表*/import base from './base'; // 导入接口域名列表
import axios from '@/utils/http'; // 导入http中创建的axios实例
import qs from 'qs'; // 根据需求是否导入qs模块const article = { // 新闻列表 articleList () { return axios.get(`${base.sq}/topics`); }, // 新闻详情,演示 articleDetail (id, params) { return axios.get(`${base.sq}/topic/${id}`, { params: params }); },// post提交 login (params) { return axios.post(`${base.sq}/accesstoken`, qs.stringify(params)); }// 其他接口…………
}export default article
将api调用挂载到vue的原型上,在main.js
中
import Vue from 'vue'
import App from './App'
import router from './router' // 导入路由文件
import store from './store' // 导入vuex文件
import api from './api' // 导入api接口Vue.prototype.$api = api; // 将api挂载到vue的原型上复制代码
然后我们可以在页面中这样调用接口
methods: { onLoad(id) { this.$api.article.articleDetail(id, { api: 123 }).then(res=> {// 执行某些操作 }) }
}
上一篇:Linux内核缓存
下一篇:Java入门教程(26)——继承