# NPM
npm install vuex --save
# Yarn
yarn add vuex
# CDN
<script src="vuex.js"></script>
模块化打包系统中使用方式:
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
单一状态树,一个包含了全部应用层状态的数据源。
在一个应用中,state 只存在一个,这与多个模块并不冲突。
实际使用时,不同模块可以作为子树存放在 state 的不同属性中。
上文中提到 state 是一个单一状态树,如果业务中有多个模块涉及到全局状态,就可以通过 module 进行区分。
因为所有模块的全局状态都放在一个 state 内会使得代码变得臃肿,维护也会变得很困难。
访问/获取/计算全局状态属性的方法,它可以对原始全局状态属性进行计算后再返回。
与直接获取原始全局状态属性再计算不同之处在于,其计算结果会被缓存,只有原始全局状态属性变动后才会重新触发计算。
在 Vuex 想要变更全局状态属性,只可以通过提交 mutations 完成。
mutations 方法并不能直接调用,创建 mutations 更像是事件注册,实际调用使用 store.commit 或者注入到组件中去(推荐注入 Actions )。
actions 类似 mutations,但又有所不同。
actions 内最终提交 mutations 来完成对全局状态属性的修改,并且可以执行异步操作。
从使用角度来说,actions 内可以包含与服务端的交互,交互完成后提交 mutations 来完成最终的变更。
介绍完基本概念后,我们通过一个实例来完成 Vuex 全局状态管理的演示。
一般在 main.js 中注册全局状态对象 store。
import Vue from 'vue'
// 建议为 store 单独建立一个目录
import store from './store'
// 路由
import router from './router'
// 注册使用
new Vue({
el: '#app',
router,
store,
render: h => h(App)
})
store 目录的 index.js 对全部模块进行统一管理。
import Vue from 'vue'
import Vuex from 'vuex'
// 引入统一 getters
import getters from './getters'
// 在 store/modules 目录下存放各个模块内容
import user from './modules/user'
import setting from './modules/setting'
// 统一管理
Vue.use(Vuex)
const store = new Vuex.Store({
modules: { // 加入模块
user,
setting
},
getters // 加入 getters
})
export default store
getters 主要为了暴露 state 属性的值或者对内容进行计算,方便快速访问。
const getters = {
// 直接返回
name: state => state.user.name,
token: state => state.user.token,
// 包装计算返回
settingHead:(state, getters) => { return state.setting.setArray.filter(item => item == 1) }
}
export default getters
一个全局状态属性通过 getters 包装后访问形式有所差异,比如上文的 settingHead。
// 没有包装计算,需要在组件中通过 computed 进行计算(每个组件都要写一遍)
computed: {
settingHead() {
return this.$store.state.setting.setArray.filter(item => item == 1)
}
}
// getters 包装后,可以通过 computed 获取,也可以直接注入
computed: {
settingHead() {
return this.$store.state.getters.settingHead
}
}
// 注入组件中,同时注入多个属性
import { mapGetters } from 'vuex'
/* ... */
computed: {
...mapGetters([ 'name', 'settingHead' ])
}
以下以 user 模块演示。
// 与服务端交互 API
import { login, getUserInfo } from '/src/api/user'
// 从浏览器 localStorage 存取 token
import { getToken, setToken, removeToken } from '/src/utils/auth'
// 模块的 state(名称、权限集、登录TOKEN)
const getDefaultState = () => {
return { name: '', authKeys: [], token: getToken() }
}
const state = getDefaultState()
// mutations 操作
const mutations = {
SET_USER: (state, user) => {
state.name = user.name
state.authKeys = user.authKeys
},
SET_TOKEN: (state, token) => {
state.token = token
},
RESET_USER: (state) => {
Object.assign(state, getDefaultState())
}
}
// actions 方法
const actions = {
// 业务登录
login({ commit }, loginInfo) {
return new Promise((resolve, reject) => {
// 调用API登录方法
login(loginInfo).then(response => {
const { data } = response
if (!data) {
reject('登录失败.')
}
// 登录成功,修改 state 的 TOKEN
commit('SET_TOKEN', data)
// 修改浏览器的 localStorage
setToken(data)
resolve()
}).catch(error => {
reject(error)
})
})
},
// 获取用户信息
getInfo({ commit }) {
return new Promise((resolve, reject) => {
// TOKEN 存放在请求头中,无需提交
getUserInfo().then(response => {
const { data } = response
if (!data) {
reject('登录请求头不正确,请重新登录.')
}
const { authKeys, name } = data
// 检查权限存在性
if (!authKeys || authKeys.length <= 0) {
reject('该账号没有任何访问权限.')
}
// 获取成功,写入 state
commit('SET_USER', data)
resolve(data)
}).catch(error => {
reject(error)
})
})
},
// 登出
logout({ commit, state }) {
return new Promise((resolve, reject) => {
// 前后端分离项目可以不调用 API
// 移除 localStorage
removeToken()
// 重置 state
commit('RESET_USER')
resolve()
})
}
}
// 导出模块
export default {
namespaced: true,
state,
mutations,
actions
}
在具体业务组件代码中调用登录代码,实际调用的是 store->user->actions->login。
// 登录页面登录方法
methods: {
login() {
this.$refs.loginForm.validate(valid => {
if (valid) {
this.loading = true
// this.$store.dispatch('模块/actions方法')
this.$store.dispatch('user/login', this.loginForm).then(() => {
this.$router.push({ path: this.redirect || '/' })
this.loading = false
}).catch(() => {
this.loading = false
this.$message.error('账户密码错误或被锁定')
})
} else {
this.$message.warning('请输入账号密码后提交')
return false
}
})
}
}
而例如获取用户信息的方法,在页面路由加载前就会触发。
import store from './store'
import router from './router'
router.beforeEach(async(to, from, next) => {
// 当前是登录页无需继续执行后续操作
if(store.getters.token) {
// 是否有权限
if(store.getters.authKeys && store.getters.authKeys.length > 0) {
next()
} else {
try {
// 获取用户信息(store.dispatch('user/getInfo'))
const { authKeys } = await store.dispatch('user/getInfo')
// 根据权限集更新动态路由
const accessRoutes = await store.dispatch('router/accessRoutes', authKeys)
router.addRoutes(accessRoutes)
next({ ...to, replace: true })
} catch (error) {
// 获取异常,触发登出重置数据
await store.dispatch('user/logout')
Message.error(error || '产生异常')
next({ path: '/login' })
NProgress.done()
}
}
}
} else {
// 位于登录页
if(to.path.indexOf('login') !== -1) {
next()
} else {
// 未登录
next({ path: '/login' })
}
}
})
当前还没有观点发布,欢迎您留下足迹!
在前端页面中文字超出隐藏并添加省略号是比较常见的样式处理,实际CSS样式中单行省略和多行省略的样式有所差异,在某些特殊场景下多行超出的样式会不生效,本文给出相关解决方案
使用背景色渐变可以使得页面质感得到大幅度提升,CSS样式中通过gradients属性指定两个或多个颜色之间平滑过渡,由于颜色的渐变是由浏览器生成,因此不会出现不同分辨率下失真的情况。
对于很多二栏布局的网站而言,正文部分过长时,侧边栏区域下拉过程中出现大片空白,因此我们可以实现当屏幕下滑达到指定元素位置(如即将越过)后对元素固顶来解决侧边栏区域的空旷感
严谨的页面开发需要着重关注前台校验相关的内容,确保请求参数的合法以保证服务器安全,界面参数众多需要建立一个公共方法,公共校验器的核心方法是Jquery.bind()
Vue3 中所提供组合 API、ref、reactive、provide、inject几大特性,可以支持完成全局状态管理能力,Vuex 是为 Vue 单独提供的状态管理插件,相对比较笨重,如果项目简单可以尝试替代
绝大多数网站都会有个顶部导航,对于手机端而言为了便于访问导航常常会做固顶操作,通过CSS样式配合Jquery的scroll()方法或原生JS监听滑动事件方法,可以轻松实现下滑隐藏,上滑显示的效果