目标: Vuex的高级使用与localStorage本地存储
- bug:我们在
src/store/index.js
中设置的city的默认值是“上海”,当我们在城市列表中选择某一个城市的时候,首页上确实会变成相应的城市,但是当我们再刷新首页的时候会发现又变成了“上海”。
正常情况下我们去访问一个网页,当再次打开的时候,它显示的应该是用户上次请求的内容。在H5中有一个新增API: localStorage,它可以帮助我们实现类似Cookie的功能,做到本地存储,这个API比Cookie更简单,所以在这里使用localStorage来实现城市保存的功能。
- localStorage的使用:在city这个数据发生变化的时候,除了改变公共数据state中的city,还要把这个city进行本地存储,而首页中city的默认值就是
localStorage.city
或者上海
:
export default new Vuex.Store ({
state: {
city: localStorage.city || '上海'
},
mutations: {
changeCity (state, city) {
state.city = city
localStorage.city = city
}
}
})
注意: 在使用localStorage的时候最好是在外层加一个try-catch
,因为如果用户关闭了浏览器的本地存储功能或者使用隐身模式,使用localStorage有可能会导致浏览器直接抛出异常,整个代码就无法运行了,为了避免这种问题,最好是在外层加一个try-catch
:
let defaultCity = '上海'
try {
if (localStorage.city) {
defaultCity = localStorage.city
}
} catch (error) {}
export default new Vuex.Store ({
state: {
city: defaultCity
},
mutations: {
changeCity (state, city) {
state.city = city
try {
localStorage.city = city
} catch (error) {}
}
}
})
- store目录下的index.js文件中的内容开始变得复杂起来了,在真正的项目开发中,会将整个文件做进一步的拆分。
在store中创建state.js文件,将与城市数据更新相关的代码拆分到state.js文件中,并将city export出去,这时需要在index.js中引入这个模块才能使用state.js接口对象中的city数据。
state.js:
let defaultCity = '上海'
try {
if (localStorage.city) {
defaultCity = localStorage.city
}
} catch (error) {}
export default {
city: defaultCity
}
创建store/mutations.js,将index.js中的mutations下的changeCity函数拆分到mutations.js文件中并export出去,则在index.js中使用这个模块同样要import进去。
mutations.js:
export default {
changeCity(state, city) {
state.city = city
try {
localStorage.city = city
} catch (error) {}
}
}
此时index.js中就变成了:
import Vue from 'vue'
import Vuex from 'vuex'
import state from './state'
import mutations from './mutations';
Vue.use(Vuex)
export default new Vuex.Store ({
state: state,
mutations: mutations
})
键和值是一样的,可以进一步修改成ES6的写法:
export default new Vuex.Store ({
state,
mutations
})
- 小bug: 当我们选择的城市名字是4个字或者5个字的时候,会发现首页的header部分会被撑开,页面样式放生了变化。
在home的Header.vue组件中进行样式的修改:
原本是:
.header-right
width: 1.24rem
float: right
text-align: center
color: #fff
上面的代码写死了它的宽度,修改:
.header-right
min-width: 1.04rem
padding 0 .1rem
float: right
text-align: center
color: #fff
- vuex的使用的优化
我们在使用state当中的city数据时,是这样写的:this.$store.state.city
,写起来很繁琐, vuex提供了一个简便的API。
在pages/home/components/Header.vue
中首先引入:import { mapState } from 'vuex'
, 然后设置一个计算属性:
export default {
name: 'HomeHeader',
computed: {
...mapState(['city'])
}
}
计算属性中的...
是一个展开运算符,mapState的意思是指把vuex里面的数据映射到当前这个组件的计算属性中,就是把city这个公用数据映射到名称为city的计算属性中,所以,在使用这个数据的时候就可以使用this.city
替换原来的this.$store.state.city
。
也可以这样用:
computed: {
...mapState({
currentCity: 'city'
})
}
表示的是: 想要把公用数据中的city映射到当前组件的计算属性当中,映射过来的名称叫作currentCity,所以这里就可以直接用this.currentCity
代替this.$store.state.city
。
同样的,当点击城市按钮的时候,city数据将会发生变化,就会去派发一个mutation,即this.$store.commit('changeCity', city)
。Vuex同样也提供了一个简便的方法:mapMutations。
首先,import { mapState, mapMutations } from 'vuex'
,然后在methods中应用mapMutations:
methods: {
handleCityClick (city) {
// this.$store.commit('changeCity', city)
this.changeCity(city)
this.$router.push('/')
},
...mapMutations(['changeCity'])
}
...mapMutations(['changeCity'])
表示的意思是: 有一个名字叫作changeCity的mutation,我把这个mutation映射到当前组件的一个名为changeCity的方法中,那如果要调用这个mutation就可以直接使用this.changeCity(city)
来代替this.$store.commit('changeCity', city)
了。
Search.vue中也要做出同样的修改,但是一定要记得先引入这个方法。
Vuex核心概念中的Getters它是一个方法,它的参数是state,可以根据state的值经过计算得到一些新的值,避免数据的冗余,它的功能有点类似于计算属性。
Module在目前还应用不上,具体的应用方法去官网自行学习。