VueCli 自定义创建项目
1.安装脚手架 (已安装)
npm i @vue/cli -g
2.创建项目
vue create hm-exp-mobile
| Vue CLI v5.0.8 |
| ? Please pick a preset: |
| Default ([Vue 3] babel, eslint) |
| Default ([Vue 2] babel, eslint) |
| > Manually select features 选自定义 |

- 选择eslint的风格 (eslint 代码规范的检验工具,检验代码是否符合规范)
- 比如:const age = 18; => 报错!多加了分号!后面有工具,一保存,全部格式化成最规范的样子

npm run serve
Vuex
Vuex 是一个 Vue 的 状态管理工具,状态就是数据。 大白话:Vuex 是一个插件,可以帮我们管理 Vue 通用的数据 (多组件共享的数据)。例如:购物车数据 个人信息数
基本使用

1.安装 vuex
安装vuex与vue-router类似,vuex是一个独立存在的插件,如果脚手架初始化没有选 vuex,就需要额外安装。
yarn add vuex@3 或者 npm i vuex@3
2.新建 store/index.js
专门存放 vuex
为了维护项目目录的整洁,在src目录下新建一个store目录其下放置一个index.js文件。 (和 `router/index.js` 类似)

.创建仓库 store/index.js
| |
| import Vue from 'vue' |
| |
| import Vuex from 'vuex' |
| |
| Vue.use(Vuex) |
| |
| |
| const store = new Vuex.Store() |
| |
| |
| export default store |
4 在 main.js 中导入挂载到 Vue 实例上
| import Vue from 'vue' |
| import App from './App.vue' |
| import store from './store' |
| |
| Vue.config.productionTip = false |
| |
| new Vue({ |
| render: h => h(App), |
| store |
| }).$mount('#app') |
此刻起, 就成功创建了一个 空仓库!!
5.测试打印Vuex
App.vue
| created(){ |
| console.log(this.$store) |
| } |
state 状态
如何给仓库存储数据, 如果取出使用仓库的数据
提供数据(存入数据)
State提供唯一的公共数据源,所有共享的数据都要统一放到Store中的State中存储。 打开项目中的store.js
文件,在state对象中可以添加我们要共享的数据。
| |
| const store = new Vuex.Store({ |
| |
| |
| |
| |
| state: { |
| count: 101 |
| } |
| }) |
访问数据
| 获取 store: |
| 1.Vue模板中获取 this.$store |
| 2.js文件中获取 import 导入 store |
| |
| |
| 模板中: {{ $store.state.xxx }} |
| 组件逻辑中: this.$store.state.xxx |
| JS模块中: store.state.xxx |
如果数据量变大, 那么使用这种方法明显就比较累坠了。所以我们可以通过使用辅助函数来帮助我们把store中的数据映射到 组件的计算属性中, 它属于一种方便的用法

通过数组的方式得到对象
第一步:导入mapState (mapState是vuex中的一个函数)
import { mapState } from 'vuex'
第二步:采用数组形式引入state属性
mapState(['count'])
上面代码的最终得到的是 类似于
| count () { |
| return this.$store.state.count |
| } |
第三步:利用展开运算符将导出的状态映射给计算属性
| computed: { |
| ...mapState(['count']) |
| } |
| <div> state的数据:{{ count }}</div> |
注意:
通过这样方式如果修改属性会报错, 因为vuex
默认开启了严选模式 也就是说通过**vuex
** 得到的数据是单项流模式, 组件是不能直接修改仓库中的数据。
state数据的修改只能通过mutations,并且mutations必须是同步的

核心概念mutations
定义mutations
| const store = new Vuex.Store({ |
| state: { |
| count: 0 |
| }, |
| // 定义mutations |
| mutations: { |
| |
| } |
| }) |
mutations是一个对象, 对象中存放的是修改state的方法
| mutations: { |
| // 方法里参数 第一个参数是当前store的state属性 |
| // payload 载荷 运输参数 调用mutaiions的时候 可以传递参数 传递载荷 |
| addCount (state) { |
| state.count += 1 |
| } |
| }, |
组件中提交mutations
通过点击事件实现修改方法的触发, 然后在通过下面语句实现调用mustations中的addCount方法
| <button @click="add()">值 + 5</button> |
| methods: { |
| add(){ |
| this.$store.commit('addCount') |
| } |
| } |
带参数的mutations函数
**提交 mutation 是可以传递参数的 **this.$store.commit('xxx', 参数)
在定义mutations中的方法的时候可以直接通过下面的类似语句进行修改。
| mutations: { |
| |
| addCount (state, count) { |
| state.count = count |
| } |
| }, |
注意: 提交的参数只能是一个, 如果有多个参数要传, 可以传递一个对象
通过input标签修改state数据

| <input :value="count" @input="handleInput" type="text"> |
| |
| export default { |
| methods: { |
| handleInput (e) { |
| |
| const num = +e.target.value |
| |
| this.$store.commit('changeCount', num) |
| } |
| } |
| } |
然后在store/index.js
文件中
| mutations: { |
| changeCount (state, newCount) { |
| state.count = newCount |
| } |
| }, |
辅助函数 - mapMutations
mapMutations和mapState很像,它把位于mutations中的方法提取了出来,我们可以将它导入
在所要使用的组件中
| import { mapMutations } from 'vuex' |
| methods: { |
| ...mapMutations(['addCount']) |
| } |
上面代码的含义是将mutations的方法导入了methods中,等价于
| methods: { |
| |
| addCount () { |
| this.$store.commit('addCount') |
| } |
| } |
此时,就可以直接通过this.addCount调用了
<button @click="addCount">值+1</button>
注意: Vuex中mutations中要求不能写异步代码,如果有异步的ajax请求,应该放置在actions中
核心概念 - actions
state是存放数据的,mutations是同步更新数据 (便于监测数据的变化, 更新视图等, 方便于调试工具查看变化),actions则负责进行异步操作
说明:mutations必须是同步的
需求: 一秒钟之后, 要给一个数 去修改state

- 在组件中通过点击事件修改
| <button @click="setAfter()" >1s 后修改为 666</button> |
| |
| |
| |
| |
| methods: { |
| setAfter(){ |
| const val = 666 |
| this.$store.dispatch('change', val) |
| } |
| } |
- 通过
this.$store.dispatch('方法名', 参数)
调用store/index.js
中的方法
| |
| const store = new Vuex.Store({ |
| state: { |
| ount: 100 |
| }, |
| mutations: { |
| changeCount(state, count){ |
| state.ount = count |
| } |
| }, |
| actions: { |
| |
| change(context, count) { |
| setTimeout(() => { |
| |
| context.commit('changeCount', count) |
| },2000) |
| } |
| } |
| }) |

辅助函数 - mapActions
mapActions 是把位于 actions中的方法提取了出来,映射到组件methods中, 不需要在写方法来调用了
| import { mapActions } from 'vuex' |
| |
| methods: { |
| ...mapActions(['changeCountAction']) |
| } |
| |
| |
| |
| |
| |
| |
| |
| <!-- 参数可以直接进行传递, 不需要考虑methods中, 但是还是最多只能传一个, 多了就封装成为对象, 然后通过对象的形式传过去. --> |
| <button @click="changeCountAction(200)">+异步</button> |
然后在store/index.js
中进行修改
| |
| const store = new Vuex.Store({ |
| state: { |
| ount: 100 |
| }, |
| mutations: { |
| addCount(state, count) { |
| state.ount += count |
| }, |
| changeCount(state, count){ |
| state.ount = count |
| } |
| }, |
| actions: { |
| |
| change(context, count) { |
| setTimeout(() => { |
| |
| context.commit('changeCount', count) |
| },2000) |
| }, |
| |
| addFive(context, count) { |
| setTimeout(() => { |
| |
| context.commit('addCount', count) |
| },2000) |
| } |
| } |
| }) |
核心概念 - getters
除了state之外,有时我们还需要从state中筛选出符合条件的一些数据,这些数据是依赖state的,此时会用到getters
例如, 组件中定义了list数组, 我们需要筛选出list中 大于 X的数据. 就可以通过getters实现
| state: { |
| list: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] |
| }, |
| |
| getters: { |
| |
| |
| filterList: state => state.list.filter(item => item > 5) |
| } |
使用getters
2.1原始方式-$store
在组件中, 通过$store
对象来获取他的getters属性, 然后再获取其中的方法
<div>{{ $store.getters.filterList }}</div>
2.2辅助函数 - mapGetters
| computed: { |
| ...mapGetters(['filterList']) |
| } |
| <div>{{ filterList }}</div> |
四种核心方法使用总结

模块module
拆封模块的原因:
由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。 这句话的意思是,如果把所有的状态都放在state中,当项目变得越来越大的时候,Vuex会变得越来越难以维护

在store中配置module模块。 然后在每个模块中设置state、mutations、actions、getters

挂载模块
- 在
model/模块名.js
定义模块的内容, 设置4个属性, 然后导出
| |
| const state= { |
| theme: 'light' , |
| desc: '测试demo' |
| } |
| const mutations = { } |
| const actions = { } |
| const getters = { } |
| |
| |
| export default { |
| state, |
| mutations, |
| actions, |
| getters |
| } |
- 在
index.js
中进行导入模块和 注册这两个模块
| |
| import user from './modules/user' |
| import settings from './modules/settings' |
| |
| |
| const store = new Vuex.Store({ |
| |
| modules: { |
| user, |
| settings |
| }, |
| }) |

访问模块中的数据
具体细节可以参考之前的核心概念的使用方法 xxx 表示我们需要得到的属性
获取state内容
- 直接通过模块名访问
$store.state.模块名.xxx
- 通过 mapState 映射:
- 默认根级别的映射
mapState([ 'xxx' ])
- 子模块的映射 :
mapState('模块名', ['xxx'])
- 需要开启命名空间 namespaced:true 
获取getters中的内容

获取mutations中的内容

获取actions中的内容

实现案例
获取请求, 然后存入vuex ,最后渲染

- 首先创建模块
modules/cart.js
, 然后构建框架
| import { mapActions ,mapGetters, mapState, mapMutations } from "vuex" |
| |
| export default { |
| namespaced: true, |
| state() { |
| return { |
| |
| list: [] |
| } |
| }, |
| mutations: { |
| }, |
| actions: { |
| }, |
| getters: { |
| |
| } |
| } |
通常都是通过一个对象的形式来进行构建数据的
- 注册模块到
index.js
| import Vue from 'vue |
| |
| import Vuex from 'vuex' |
| |
| |
| import cart from './modules/cart' |
| Vue.use(Vuex) |
| |
| export default new Vuex.Store({ |
| modules: { |
| cart |
| } |
| }) |
- 准备需要的
actions
和 mutations
代码( 因为我们获取数据是通过异步的方式, 所以在actions
里 )
| export default { |
| namespaced: true, |
| state() { |
| return { |
| |
| list: [] |
| } |
| }, |
| mutations: { |
| |
| updateList(state, newList){ |
| state.list = newList |
| } |
| }, |
| actions: { |
| |
| async getList(context){ |
| const res = await axios.get('http://localhost:3000/cart') |
| |
| context.commit('updateList', res.data) |
| } |
| }, |
| getters: {} |
| } |
仅仅这样在模块中写还无法将数据加载到组件中, 需要在App.vue
组件中调用才行
| <script> |
| |
| import { mapActions , mapGetters, mapState, mapMutations} from 'vuex' |
| import cart from './store/modules/cart' |
| export default { |
| name: 'App', |
| components: { |
| |
| CartHeader, |
| CartFooter, |
| CartItem |
| }, |
| |
| computed: { |
| ...mapState('cart', ['list']) |
| }, |
| |
| |
| created() { |
| |
| |
| this.getList() |
| }, |
| methods: { |
| ...mapActions('cart',['getList']) |
| } |
| } |
| </script> |
- 动态渲染
| <template> |
| <div class="app-container"> |
| |
| <cart-header></cart-header> |
| |
| |
| |
| |
| |
| <cart-item v-for="(item, index) in list" :key="item.id" :item="item"></cart-item> |
| |
| |
| <cart-footer></cart-footer> |
| </div> |
| </template> |
子组件通过props
数据, 然后进行渲染即可
数据更新
- 基于state 来使用getters从而实现 数据的更新
| import axios from "axios" |
| import { mapActions ,mapGetters, mapState, mapMutations } from "vuex" |
| |
| export default { |
| namespaced: true, |
| state() { |
| return { |
| |
| list: [] |
| } |
| }, |
| mutations: { |
| |
| updateList(state, newList){ |
| state.list = newList |
| }, |
| |
| add(state, id) { |
| const goods = state.list.find((item) => item.id == id) |
| goods.count += 1 |
| }, |
| |
| del(state, id) { |
| const goods = state.list.find((item) => item.id == id) |
| goods.count -= 1 |
| } |
| |
| }, |
| actions: { |
| |
| async getList(context){ |
| const res = await axios.get('http://localhost:3000/cart') |
| |
| context.commit('updateList', res.data) |
| }, |
| |
| async addItem(context, item) { |
| const newCount = item.count + 1 |
| const res = await axios.patch(`http://localhost:3000/cart/${item.id}`, { |
| count: newCount |
| }) |
| console.log(res.data) |
| context.commit('add', item.id) |
| |
| }, |
| |
| async delItem(context, item) { |
| const newCount = item.count - 1 |
| if(newCount < 1) return |
| const res = await axios.patch(`http://localhost:3000/cart/${item.id}`, { |
| count: newCount |
| }) |
| context.commit('del', item.id) |
| } |
| }, |
| getters: { |
| totalCount(state) { |
| return state.list.reduce((sum,item ) => sum + item.count, 0); |
| |
| }, |
| totalMoney(state) { |
| return state.list.reduce((sum,item ) => sum += item.price*item.count, 0); |
| } |
| } |
| } |
- 然后通过使用
getter
实现总数的计算
| <template> |
| |
| <button class="btn btn-light" @click="delItem(item)">-</button> |
| <span class="count">{{ item.count }}</span> |
| <button class="btn btn-light" @click="addItem(item)">+</button> |
| |
| |
| <div class="footer-container"> |
| |
| <div> |
| <span>共 {{totalCount}} 件商品,合计:</span> |
| <span class="price">¥{{ totalMoney }}</span> |
| </div> |
| |
| <button class="btn btn-success btn-settle">结算</button> |
| </div> |
| </template> |
| |
| <script> |
| import { mapActions , mapGetters, mapState, mapMutations} from 'vuex'; |
| import cart from '../store/modules/cart' |
| export default { |
| name: 'CartFooter', |
| props: { |
| list: { |
| type: Array, |
| required: true |
| } |
| }, |
| computed: { |
| ...mapGetters('cart', ['totalCount','totalMoney']) |
| } |
| |
| |
| } |
| </script> |