- 官方的解释是:Vuex是一个专为Vue.js应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
简单来说就是
。
- 集中管理所有的状态
当多个组件之间共享一个状态时,event bus 可能就变成这样。
回到我们的项目,需要共享状态的总共有 3 组件:
这三个组件都需要用到购物车列表 goodsList
- 首先安装:cnpm install vuex--save
安装好后,新建一个 store 文件同时新建 index.js 文件,引用并且使用 vuex
- import Vue from 'vue'
- import Vuex from 'vuex'
- Vue.use(Vuex);
其次,导出一个 vuex.Store 实例,可接受一个对象作为参数:
- {
- state, <!--状态-->
- getters, <!-- 状态的计算属性 -->
- mutations, <!-- 用于改变状态 -->
- actions, <!-- 可包含异步操作的mutation -->
- }
我们先只传入 state
- export const store = new Vuex.Store({
- state: {
- goodsList: []
- }
- })
接着,在 main.js 中引入并挂载到 Vue 实例上
- ...
- import {store} from './store/index.js'
- new Vue({
- el: '#app',
- router,
- store,
- render: h => h(App)
- })
在购物车 car.vue 组件通过一个计算属性获取 vuex 的状态 goodsList
- computed: {
- goodsList() {
- return this.$store.state.goodsList
- }
- }
这样我们就可以通过 v-for 将购物车列表循环出来了,不过现在是数组空的,我把添加的按钮放到电影详情里面去了。
- 我们在首页的电影列表套了一层router-link,并将电影的id作为参数,所以点击时就会入到详情页面。
- <router-link tag="li" v-for="(v,i) in array" :key="v.id" :to='{path:"/film-detail/"+v.id}'>
在详情页面 js 中,我们通过 this.$route.params.id 获取(参数 key 为 id 取自我们路由的配置)。
获取到 id 后,接下来就是在某个生命周期(通常是 mounted)发送请求获取该电影的 data,然后就是赋值操作让数据显示出来了。这里主要讲一下
,由于我们在 App.vue 使用了
- activated生命周期
,所以 film-detail 组件在第一次进入后就会
- keep-alive
,由于该组件
- 被缓存
,所以之后我们每次进来都会保持第一次进来获取的数据。
- 不会被销毁
因此,我们将发送请求的时间点由之前的
改变为
- mounted(已挂载)
,这样既能复用组件,又能保证每次进入时都获取最新的数据。
- activated(组件激活时)
- 回到vuex,点击详情里面的按钮时,要将该电影加入到购物车,也就是说要改变state的状态。
vuex 规定改变 store 中的状态的唯一方法是
,虽然你也可以直接改变,比如点击某个按钮时 this.$store.state.number++,不过最好时通过 mutation 触发。通常我们走路都是正着走的,假如你非要倒立着走,也没办法拦着你。
- 提交mutation
- 定义mutation
- mutations: {
- add: state = >state.number++
- }
- 使用mutation
- <!-- 在某个组件里面使用mutation -->
- this.$store.commit("add");
- 为了将电影加入购物车,在导出的实例的参数中添加mutations
- export const store = new Vuex.Store({
- state: {
- goodsList: []
- },
- mutations: {
- addGoods: (state, data) = >{
- state.goodsList.push(data);
- },
- }
- })
- 点击按钮时,首先判断该电影是否在购物车已存在,如果存在则不再加入。
- var idExist = this.$store.state.goodsList.find((item) = >{
- return item.id == id
- })
使用 es6 数组新增 find 函数来实现,该函数返回满足条件的第一个元素。如果不存在该 id,则将对应的数据存入。
- if (!idExist) {
- var data = {
- url: this.smallPic,
- title: this.title,
- price: Math.floor(Math.random() * 100),
- stock: "盒",
- number: 1,
- select: false,
- id: this.id
- }
- this.$store.commit("addGoods", data);
- this.addSuccess = true;
- } else {
- return alert("已加入購物車")
- }
- 为了给加入购物车成功一个反馈,写个简单的效果,让 + 1缓缓移动并且透明度慢慢消失
```html
+1
span{
animation:move 1.6s forwards;
}
@keyframes move{
from{
opacity: 1;
transform:translateY(0);
- }
- to {
- opacity: 0;
- transform: translateY( - 100 % );
- }
- }
```
- 当购物车数量大于0时,底部导航需要显示当前购物车的数量,也即是goodsList.length;
可以通过
来取得,不过最好的办法是通过 vuex 的
- this.$store.state.goodsList.length
来获取。因为假如你有多个页面需要用到这个 length 时,你可能就会在每个需要用到的地方复制这段代码过来 "this.$store.state.goodsList.length"。
- getters
- 在配置参数中加一个getters
- export const store = new Vuex.Store({
- state: {
- goodsList: []
- },
- getters: {
- goddsNumber: state = >{
- return state.goodsList.length
- }
- },
- mutations: {
- addGoods: (state, data) = >{
- state.goodsList.push(data);
- },
- }
- })
- 使用的方法跟获取state基本一致,只不过是由state.xx改为getters.xx
- computed: {
- number() {
- return this.$store.getters.number
- }
- }
- 我们希望当number > 0才显示,无非就是一个v - show = "number>0"
购物车涉及到的操作有:
, 看起来需要 4 个方法,实际上总结一下 2 个方法就够了:
- 数量加,数量减,是否选中,删除
需要改变进行操作即可。比如 number+1,或者 number-1,或者选中状态为 true 变为 false,由 false 变为 true。我们只需要将要改变和
- 哪个状态
和要设置的
- key
都作为参数,就可以实现 1 个方法进行多个操作。
- value
- 在mutation中再加2个方法:
```javascript
mutations:{
- deleteGoods(state,index){
- state.goodsList.splice(index,1);
- },
- updateGoods(state,data){
- <!--index为操作第几个元素,key为要改变的key,value为新的值 -->
- const {index,key,value}=data;
- state.goodsList[index][key]=value;
- }
}
```
- findPosition(id){
- return this.goodsList.findIndex(item=>{
- return item.id==id
- })
- },
点击删除时:
- del(id){
- var i=this.findPosition(id);
- this.$store.commit("deleteGoods",i);
- },
点击切换选中时:
- toggleSelect(id) {
- var i = this.findPosition(id);
- var select = this.goodsList[i].select;
- this.$store.commit("updateGoods", {
- index: i,
- key: "select",
- value: !select
- });
- }
点击加减号时,传入 + 1 或者 - 1:
- changeNumber(id, val) {
- var i = this.findPosition(id);
- var number = this.goodsList[i].number;
- this.$store.commit("updateGoods", {
- index: i,
- key: "number",
- value: number + val <= 0 ? 1 : number + val
- })
- }
- vuex提供了mapMutations 辅助函数将组件中的 methods 映射为 store.commit 调用,当有多个mutation需要使用时,使用mapMutations可以让代码更为简洁。
- import { mapMutations } from 'vuex'
- //在methos中使用展开运算符混入到原有方法中,比如:
- methods:{
- ...mapMutations(
- ["deleteGoods","updateGoods"](向methods混入2个方法)
- ),
- changeNumber(){
- ...(原有1个方法)
- }
- }
- 混入后,现在就有3个方法了,可以通过this.deleteGoods和this.updateGoods调用。
- ...mapMutations({
- coolDelete: 'deleteGoods',coolUpdate,
- 'updateGoods'
- })
- 这样一来,点击删除时:(更新的也同理)
- del(id) {
- var i = this.findPosition(id);
- this.coolDelete(i);
- }
- 除了mutaion有mapMutation外,state,getters和actions也都有辅助的map函数,可以使用Mutation,可以一次获取多个状态和方法。
完整文件如下:
- store.js
- import Vue from 'vue'import Vuex from 'vuex'Vue.use(Vuex);
- export const store = new Vuex.Store({
- state: {
- goodsList: localStorage["goodsList"] ? JSON.parse(localStorage["goodsList"]) : []
- },
- getters: {
- sum: state = >{
- var total = 0;
- state.goodsList.forEach((item) = >{
- if (item.select) {
- total += item.price * item.number
- }
- }) return total
- },
- goddsNumber: state = >{
- return state.goodsList.length
- }
- },
- mutations: {
- addGoods: (state, data) = >{
- state.goodsList.push(data);
- localStorage.setItem("goodsList", JSON.stringify(state.goodsList));
- },
- deleteGoods(state, index) {
- state.goodsList.splice(index, 1);
- localStorage.setItem("goodsList", JSON.stringify(state.goodsList));
- },
- updateGoods(state, data) {
- const {
- index,
- key,
- value
- } = data;
- state.goodsList[index][key] = value;
- localStorage.setItem("goodsList", JSON.stringify(state.goodsList));
- }
- }
- })
- car.vue
- <template>
- <div class="car-list-container">
- <ul>
- <li class="car-list" v-for="(v,i) in goodsList">
- <div class="car-list__img">
- <img :src="v.url">
- </div>
- <div class="car-list__detail">
- <p class="car-list__detail__title">{{v.title}}</p>
- <p class="car-list__detail__number">数量:<button class="number--decrease iconfont icon-jianhao" @click="changeNumber(v.id,-1)"></button><input type="text" readonly="" v-model="v.number"><button class="number--increase iconfont icon-iconfont7" @click="changeNumber(v.id,1)"></button></p>
- <p class="car-list__detail__type">规格:<span>{{v.stock}}</span></p>
- <p class="car-list__detail__price">单价:<span>¥{{v.price}}</span></p>
- <p class="car-list__detail__sum">小计:<span>¥{{v.price*v.number}}</span></p>
- </div>
- <div class="car-list__operate">
- <span class="iconfont icon-shanchu delete-goods" @click="del(v.id)"></span>
- <label >
- <input type="checkbox" name="goods" :checked="v.select==true" @change="toggleSelect(v.id)">
- <span></span>
- </label>
- </div>
- </li>
- </ul>
- <div class="car-foot-nav">
- <button class="sum-price">总额:¥{{sum}}</button>
- <router-link :to='{name:"index"}' class="continue-shopping" tag="button">继续购物</router-link>
- <button class="to-pay">去结算</button>
- </div>
- </div>
- </template>
- <script>
- import { mapMutations } from 'vuex'
- import { mapGetters } from 'vuex'
- export default {
- name: 'car',
- data () {
- return {
- }
- },
- methods:{
- ...mapMutations(
- ["deleteGoods","updateGoods"]
- ),
- findPosition(id){
- return this.goodsList.findIndex(item=>{
- return item.id==id
- })
- },
- changeNumber(id,val){
- var i=this.findPosition(id);
- var number=this.goodsList[i].number;
- this.updateGoods({
- index:i,
- key:"number",
- value:number+val<=0?1:number+val
- })
- },
- del(id){
- var i=this.findPosition(id);
- this.deleteGoods(i);
- },
- toggleSelect(id){
- var i=this.findPosition(id);
- var select=this.goodsList[i].select;
- this.updateGoods({
- index:i,
- key:"select",
- value:!select
- })
- }
- },
- computed:{
- ...mapGetters(
- [ "sum"]
- ),
- goodsList(){
- return this.$store.state.goodsList
- }
- },
- mounted(){
- this.$ajax.get("/api/car",function(res){
- console.log(res)
- })
- }
- };
- </script>
代码已上传到 github,点击查看
项目地址:https://github.com/linrunzheng/vueApp
来源: http://www.cnblogs.com/zhengrunlin/p/7446180.html