1. vuex
state是存储数据Vue Components是视图,修改state时只能通过Actions的commit调用Mutations修改或直接通过Mutations修改
1.1 简单使用:
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
}
})
store.commit('increment')
console.log(store.state.count) // -> 1
1.2 state 单一状态树
computed: mapState({
// 箭头函数可使代码更简练
count: state => state.count,
// 传字符串参数 'count' 等同于 `state => state.count`
countAlias: 'count',
// 为了能够使用 `this` 获取局部状态,必须使用常规函数
countPlusLocalState (state) {
return state.count + this.localCount
}
})
1.3 getter
有时候我们需要从 store 中的 state 中派生出一些状态,例如对列表进行过滤并计数,这时我们需要getter
const store = new Vuex.Store({
state: {
todos: [
{ id: 1, text: '...', done: true },
{ id: 2, text: '...', done: false }
]
},
getters: {
doneTodos: state => {
return state.todos.filter(todo => todo.done)
}
}
})
// 访问
store.getters.doneTodos // -> [{ id: 1, text: '...', done: true }]
// mapGetters辅助函数
import { mapGetters } from 'vuex'
export default {
// ...
computed: {
// 使用对象展开运算符将 getter 混入 computed 对象中
...mapGetters([
'doneTodosCount',
'anotherGetter',
// ...
])
}
}
1.4 mutation
更改 Vuex 的 store 中的状态的唯一方法是提交 mutation。Vuex 中的 mutation 非常类似于事件:每个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler)。这个回调函数就是我们实际进行状态更改的地方,并且它会接受 state 作为第一个参数,mutation必须是同步的。
const store = new Vuex.Store({
state: {
count: 1
},
mutations: {
increment (state, n) {
state.count += n
}
}
})
// 调用
store.commit('increment', 10)
// mapMutations
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')`
})
}
}
1.5 action
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
},
actions: {
increment (context) {
context.commit('increment')
}
}
})
// 解构方式
actions: {
increment ({ commit }) {
commit('increment')
}
}
// 触发
store.dispatch('increment')
// 异步调用与多重分发
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)
)
}
}
// mapActions
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
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
actions: {
async actionA ({ commit }) {
commit('gotData', await getData())
},
async actionB ({ dispatch, commit }) {
await dispatch('actionA') // 等待 actionA 完成
commit('gotOtherData', await getOtherData())
}
}
1.6 模块
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 的状态
具体使用参见官网:https://vuex.vuejs.org/zh/guide/modules.html
下面来看一个完整的例子:
首先项目目录如下:
index.js是启动文件
import Vue from 'vue'
import Vuex from 'vuex'
// * as是别名的意思
import * as actions from './actions'
import * as getters from './getters'
import state from './state'
import mutations from './mutations'
import createLogger from 'vuex/dist/logger' // 通过mutation修改state时会在控制台打出logger
Vue.use(Vuex)
const debug = process.env.NODE_ENV !== 'production' // 检测state的修改是否来源于mutation
export default new Vuex.Store({
actions,
getters,
state,
mutations,
strict: debug,
plugins: debug ? [createLogger()] : []
})
state.js是存储数据的文件
import {playMode} from 'common/js/config'
import { loadSearch, loadPlay, loadFavorite } from 'common/js/cache'
const state = {
singer: {}, // 歌手列表
mode: playMode.sequence, // 播放顺序
searchHistory: loadSearch(), // 搜索历史
playHistory: loadPlay(), // 播放历史
favoriteList: loadFavorite() // 收藏内容
}
export default state
// 状态可以是函数返回值
mutation-types.js是对mutation.js的方法名字的定义
// 存储mutation相关的字符常量
export const SET_SINGER = 'SET_SINGER'
......
mutation.js是对state进行修改
import * as types from './mutation-types'
const mutations = {
[types.SET_SINGER](state, singer) {
state.singer = singer
},
......
}
export default mutations
getters.js是state是映射,在这里可以进行一些计算操作
// state映射,并计算state数据
export const singer = state => state.singer
export const currentSong = (state) => {
return state.playlist[state.currentIndex] || {}
}
......
actions.js,对于需要同时修改好多个state的操作,可用actions.js封装
export const selectPlay = function({commit, state}, {list, index}) { // 点击歌曲列表播放
commit(types.SET_SEQUENCE_LIST, list)
if (state.mode === playMode.random) {
let randomList = shuffle(list)
commit(types.SET_PLAYLIST, randomList)
index = findIndex(randomList, list[index])
} else {
commit(types.SET_PLAYLIST, list)
}
commit(types.SET_CURRENT_INDEX, index)
commit(types.SET_FULL_SCREEN, true)
commit(types.SET_PLAYING_STATE, true)
}
......
修改数据方法:
// 在methods中对state映射方法:
...mapMutations({
setSinger: 'SET_SINGER'
})
// 然后在需要修改的地方调用方法名:
this.setSinger(singer)
取数据方法:
// 在computed计算属性中映射方法
...mapState({
currentCity: 'city'
})
this.singer即可取得
2. mixin
如果多个页面有相同的函数,可使用mixin.js
export const playlistMixin = {
computed: {
...mapGetters([
'playlist'
])
},
mounted() {
this.handlePlaylist(this.playlist)
},
activated() {
this.handlePlaylist(this.playlist)
},
watch: {
playlist(newVal) {
this.handlePlaylist(newVal)
}
},
methods: {
handlePlaylist() {
throw new Error('component must implement handlePlaylist method')
}
}
}
// 然后在页面内引入
import {playlistMixin} from 'common/js/mixin'
// 并在export default{}中注册
mixins: [playlistMixin]
3. <audio>在vue中的几种状态
@play // 加载完成
@error // 错误状态
@timeupdate // 播放时间位置
@ended // 播放结束
更多可查看: http://www.w3school.com.cn/jsref/dom_obj_audio.asp
4. 设置每个页面title
import Vue from 'vue'
import HelloWorld from '@/components/HelloWorld'
import Index from '../../static/Index'
import Router from 'vue-router'
Vue.use(Router)
const router = new Router({
routes: [
{path: '/', name: 'Index',meta:{title:'登陆'}, component: Index}
]
})
router.beforeEach((to, from, next) => {//beforeEach是router的钩子函数,在进入路由前执行
if (to.meta.title) {//判断是否有标题
document.title = to.meta.title
}
next()//执行进入路由,如果不写就不会进入目标页
})
export default router;
5. 路由守卫
https://router.vuejs.org/zh/guide/advanced/navigation-guards.html
6. directive
参数:
自定义指令,参见:https://cn.vuejs.org/v2/guide/custom-directive.html
7. jsx
参见:https://cn.vuejs.org/v2/guide/render-function.html
8. axios请求拦截
import axios from 'axios'
import router from './router';
// 请求拦截
axios.interceptors.request.use(
config => {
if(localStorage.wxToken) {
config.headers.Authorization = localStorage.wxToken
}
return config
},
error => {
return Promise.reject(error)
}
)
// 响应拦截
axios.interceptors.response.use(
response => {
return response
},
error => {
const { status } = error.response
if(status === 401) {
alert('token过期,请重新登录')
localStorage.removeItem('wxToken')
router.push('/login')
}
// alert(error.response.data.)
alert(error.response.data)
return Promise.reject(error)
}
)
export default axios
9. axios的封装
import {config} from '../config'
import axios from 'axios';
axios.defaults.withCredentials = true
const tips = {
1: '抱歉,出现了一个错误',
400: '请求错误',
401: '未登录',
404: '请求不存在',
405: '禁止访问',
451: '授权过期'
}
// # 解构
class HTTP{
request({url,data={},method='GET'}){
return new Promise((resolve, reject)=>{
this._request(url,resolve,reject,data, method)
})
}
_request(url,resolve, reject, data={}, method='GET'){
let $HTTP;
if(method === "GET") {
$HTTP = axios.get(config.api_url + url, {
params: data
})
} else if(method === "POST") {
$HTTP = axios.post(config.api_url + url, data)
}
$HTTP.then((res) => {
if(res.status==200 && !res.data.error) {
resolve(res.data)
}else{
reject()
const code = res.data.error.code
const msg = res.data.error.message
this._show_error(code, msg)
}
}).catch((err) => {
reject()
this._show_error(1)
})
}
_show_error(code, msg){
console.log(msg)
}
}
export {HTTP}
10. keep-alive
组件缓存