1. Cookie
客户端浏览器存储数据都是通过cookie来实现的。cookie因其自身的特性,在一些方面有其独有的优势,比如可配置过期时间、可跨域共享(具有相同祖先域名时)、与服务器数据交互等,但在做数据存储方面,其缺点显而易见:
- 客户端发送请求时,cookie会作为头部将无用数据一起发送给服务器
- 请求被拦截后,cookie数据有泄漏和被篡改的安全风险
- cookie存储数据的大小被限制在4K。IE8、Firefox、opera每个域对cookie的数量也有限制,上限是50,Safari/WebKit没有限制。
以下是获取和设置cookie的方法:
// 根据name获取cookie
function getCookie(name) {
if (document.cookie.length > 0) {
let c_start = document.cookie.indexOf(name + "=");
if (c_start != -1) {
c_start = c_start + name.length + 1;
let c_end = document.cookie.indexOf(";", c_start);
if (c_end == -1) c_end = document.cookie.length;
return unescape(document.cookie.substring(c_start, c_end));
}
}
return ""
}
// 设置cookie,name/value为名和值,params为参数
function setCookie(name, value, params = {}) {
let { expires, ...args } = params;
if (expires) {
const date = new Date();
expires = new Date(date.setDate(date.getDate() + expires)).toGMTString();
}
let cookie = `${name}=${escape(value)};expires=${expires}`;
Object.keys(args).forEach(key => {
cookie += `;${key}=${args[key]}`;
});
document.cookie = cookie;
}
因此cookie不适合做大数据量的存储,相比之下,Web Storage更适合存储大量数据:
2. Web Storage
每个域名下可提供5M的存储容量(不同浏览器可能有差异,比如IE是10M)
以key/value键值对的方式存储字符串,方便数据存取操作
只存储在客户端本地,不会随请求发送给服务端
Web Storage分为两种,即sessionStorage和localStorage,
两种对象的使用方法和api基本相同:
const storage = sessionStorage || localStorage;
// 设置xxx的值为'yyy'
storage.setItem('xxx', 'yyy');
// 获取xxx的值
storage.getItem('xxx');
// 删除xxx及其值
storage.removeItem('xxx');
// 获取键值对的数量
storage.length;
// 清空所有值
storage.clear();
以下是sessionStorage和localStorage之间的区别:
sessionStorage 为每一个给定的源(given origin)维持一个独立的存储区域,该存储区域在页面会话期间可用(即只要浏览器处于打开状态,包括页面重新加载和恢复)。
localStorage 同样的功能,但是在浏览器关闭,然后重新打开后数据仍然存在。
3. VueX
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式 + 库。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
3.1 State
用一个对象就包含了全部的应用层级状态。至此它便作为一个“唯一数据源 (SSOT)”而存在。
State提供唯一公共数据源,所有共享数据统一放置Store的State中存储(通常项目中state[本质是一个对象]放在store文件夹之中)
//创建store数据源,
const store = new Vuex.Store({
state:{count:0}
})
//或者
const store = createStore({
state:{count:0}
})
组件访问State数据方式:
//第一种
this.$store.state.全局数据名称
//在template当中使用可以省略this
//-------------------------------
//第二种方式
<template>
<h1>
{{count}}
</h1>
</template>
<script>
import { mapState } from 'vuex'
export default {
data(){
return {}
},
computed:{
...mapState(['count'])
// 映射 this.count 为 store.state.count
//...对象展开运算符:将此对象混入到外部对象中。
//详细说明:https://blog.csdn.net/weixin_41642136/article/details/111666448
}
}
</script>
3.2 Getter
Getter用于对Store数据加工处理,相当于Vue的计算属性的功能。
const store = createStore({
state: {
todos: [
{ id: 1, text: '...', done: true },
{ id: 2, text: '...', done: false }
],
count:0
},
getters: {
showNum:state=>{
return '当前最新数据是['+state.count+']'
},
doneTodos (state) {
return state.todos.filter(todo => todo.done)
},
getTodoById: (state) => (id) => {
return state.todos.find(todo => todo.id === id)
},
}
})
访问getters:
//第一种
this.$store.getters.showNum
this.$store.getters.doneTodos
this.$store.getters.getTodoById(2)
//第二种
import { mapGetters } from 'vuex'
export default {
...
computed: {
// 使用对象展开运算符将 getter 混入 computed 对象中
...mapGetters([
'doneTodosCount',
'anotherGetter',
])
...mapGetters({
// 把 `this.doneCount` 映射为 `this.$store.getters.doneTodosCount`
doneCount: 'doneTodosCount'
})
}
}
3.3 Murtation
Murtation:一般用于变更Store当中的数据
store当中不可在js代码当中直接用this.$store.state.全局数据名称
去修改数据,
Vuex提供一种修改的 store 中的状态的唯一方法是提交 mutation。
每个 mutation 都有一个字符串的事件类型 (type)和一个回调函数 (handler)。
//定义mutations
const store = createStore({
state: {
count: 0
},
mutations: {
increment_1 (state) {
// 变更状态
state.count++
},
increment_2 (state, payload) {
state.count += payload.amount
}
}
})
访问mutations:
1.使用this.$store,commit()
methods:{
函数名(){
this.$store,commit('increment_1')
this.$store.commit('increment_2', {amount: 10})
this.$store.commit({type: 'increment',amount: 10})
}
}
2.使用mapMutations
//1.从VueX种按需导入mapMutations函数
import { mapMutations } from 'vuex'
使用 mapMutations 辅助函数将组件中的 methods 映射为 store.commit 调用
//2.将指定的mutations函数,映射未当前组件methods
methods:{
...mapMutations(['increment_1'],['increment_2'])
...mapMutations({
add: 'increment' // 将 `this.add()` 映射为 `this.$store.commit('increment')`
})
}
3.4 Action
Action用于处理异步任务,通过触发Mutation的方式间接变更数据。
const store = createStore({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
},
actions: {
increment (context) {
setTimeout((=>{
context.commit('increment')
},1000)
}
},
//实践中,我们会经常用到 ES2015的参数解构来简化代码(特别是我们需要调用 commit 很多次的时候):
actions: {
increment ({ commit }) {
commit('increment')
}
}
})
触发Action:
//第一种
methods:{
函数名(){
this.$store.dispatch('increment')
// 以载荷形式分发
this.$store.dispatch('incrementAsync', {
amount: 10
})
// 以对象形式分发
this.$store.dispatch({
type: 'incrementAsync',
amount: 10
})
}
}
//第二种
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')`
})
}
}
3.5 Module
Vuex 允许我们将 store分割成模块(module),每个模块拥有自己的state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割:
const moduleA = {
state: () => ({ ... }),
mutations: { ... },
actions: { ... },
getters: { ... }
}
const moduleB = {
state: () => ({ ... }),
mutations: { ... },
actions: { ... }
}
const store = createStore({
modules: {
a: moduleA,
b: moduleB
}
})
store.state.a // -> moduleA 的状态
store.state.b // -> moduleB 的状态
模块局部状态
const moduleA = {
state: () => ({
count: 0
}),
mutations: {
increment (state) {
// 这里的 `state` 对象是模块的局部状态
state.count++
}
},
getters: {
doubleCount (state) {
return state.count * 2
},
//对于模块内部的 getter,根节点状态会作为第三个参数暴露出来:
sumWithRootCount (state, getters, rootState) {
return state.count + rootState.count
}
},
actions: {
//对于模块内部的 action,局部状态通过 context.state 暴露出来,根节点状态则为 context.rootState:
incrementIfOddOnRootSum ({ state, commit, rootState }) {
if ((state.count + rootState.count) % 2 === 1) {
commit('increment')
}
}
}
}
命名空间
启用了命名空间的 getter 和 action 会收到局部化的 getter,dispatch 和 commit。换言之,你在使用模块内容(module assets)时不需要在同一模块内额外添加空间名前缀。更改 namespaced 属性后不需要修改模块内的代码。
如果希望你的模块具有更高的封装度和复用性,你可以通过添加 namespaced: true 的方式使其成为带命名空间的模块。当模块被注册后,它的所有 getter、action 及 mutation 都会自动根据模块注册的路径调整命名。例如:
const store = createStore({
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']
}
}
}
}
}
})
带命名空间模块访问全局内容:
modules: {
foo: {
namespaced: true,
getters: {
// 在这个模块的 getter 中,`getters` 被局部化了
// 你可以使用 getter 的第四个参数来调用 `rootGetters`
someGetter (state, getters, rootState, rootGetters) {
getters.someOtherGetter // -> 'foo/someOtherGetter'
rootGetters.someOtherGetter // -> 'someOtherGetter'
rootGetters['bar/someOtherGetter'] // -> 'bar/someOtherGetter'
},
someOtherGetter: state => { ... }
},
actions: {
// 在这个模块中, dispatch 和 commit 也被局部化了
// 他们可以接受 `root` 属性以访问根 dispatch 或 commit
someAction ({ dispatch, commit, getters, rootGetters }) {
getters.someGetter // -> 'foo/someGetter'
rootGetters.someGetter // -> 'someGetter'
rootGetters['bar/someGetter'] // -> 'bar/someGetter'
dispatch('someOtherAction') // -> 'foo/someOtherAction'
dispatch('someOtherAction', null, { root: true }) // -> 'someOtherAction'
commit('someMutation') // -> 'foo/someMutation'
commit('someMutation', null, { root: true }) // -> 'someMutation'
},
someOtherAction (ctx, payload) { ... }
}
}
}
3.6 一些注意事项
1.store当中不可在js代码当中直接用this.$store.state.全局数据名称
去修改state数据。
2.不要在mutations函数当中直接执行异步操作,例如:setTimeout(()=>{...},100)