【VUE2】第五期——VueCli创建项目、Vuex多组件共享数据、json-server——模拟服务端api
黑马程序员视频地址:091-vuex的基本认知_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV1HV4y1a7n4?vd_source=0a2d366696f87e241adc64419bf12cab&spm_id_from=333.788.videopod.episodes&p=91
目录
1 VueCli 自定义创建项目
2 Eslint代码规范
2.1 规则表
2.2 手动修改对照文档
2.3 通过插件修改
3 Vuex
3.1 创建仓库
3.1.1 安装Vuex包
3.1.2 创建仓库
3.1.3 将仓库挂载到vue实例上
3.2 仓库中的共享数据与方法
3.2.1 state状态(数据)
3.2.1.1 提供数据
3.2.1.2 访问数据
3.2.1.2.1 $store
3.2.1.2.2 mapState
3.2.1.2 修改数据与严格模式
3.2.2 mutations(同步方法)
3.2.2.1 提供方法
3.2.2.2 使用方法
3.2.2.2.1 $store
3.2.2.2.2 mapMutations
3.2.3 actions(异步方法)
3.2.3.1 提供方法
3.2.3.2 使用方法
3.2.3.2.1 $store
3.2.3.2.2 mapActions
3.2.4 getters(计算数据)
3.2.4.1 提供计算数据
3.2.4.2 使用计算数据
3.2.4.2.1 $store
3.2.4.2.2 mapGetters
3.2.5 总结
3.3 module模块化
3.3.1 提供数据/方法
3.3.2 访问数据/方法
3.3.2.1 打印对比是否开启命名空间对$store中属性的影响
3.3.2.2 未开启命名空间
3.3.2.3 开启命名空间
4 json-server——模拟服务端api
4.1 使用步骤
4.2 使用语法
4.2.1 获取数据
4.2.2 修改数据
1 VueCli 自定义创建项目
1.安装脚手架 (已安装)
npm i @vue/cli -g
2.创建项目
vue create hm-exp-mobile
以上两步与之前普通创建vue2项目一样
不同操作:
Vue CLI v5.0.8
? Please pick a preset:
Default ([Vue 3] babel, eslint)
Default ([Vue 2] babel, eslint)
> Manually select features 选自定义
3.配置自定义项目
- 手动选择功能
- 选择vue的版本
3.x
> 2.x
- 是否使用history模式
- 选择css预处理
-
选择eslint的风格 (eslint 代码规范的检验工具,检验代码是否符合规范)
-
比如:const age = 18; => 报错!多加了分号!后面有工具,一保存,全部格式化成最规范的样子
- 选择校验的时机 (直接回车)
- 选择配置文件的生成方式 (直接回车)
- 是否保存预设,下次直接使用? => 不保存,输入 N
- 等待安装,项目初始化完成
- 启动项目
npm run serve
2 Eslint代码规范
2.1 规则表
ESLint:是一个代码检查工具,用来检查你的代码是否符合指定的规则(你和你的团队可以自行约定一套规则)。在创建项目时,我们使用的是 JavaScript Standard Style 代码风格的规则
2.2 手动修改对照文档
打开 ESLint 规则表,使用页面搜索(Ctrl + F)这个代码,查找对该规则的一个释义。
2.3 通过插件修改
特点:
eslint会自动高亮错误显示
通过配置,eslint会自动帮助我们修复错误
配置:
eslint的配置文件必须在根目录下,这个插件才能才能生效
加入以下代码:
// 当保存的时候,eslint自动帮我们修复错误
"editor.codeActionsOnSave": {"source.fixAll": true
},
// 保存代码,不自动格式化
"editor.formatOnSave": false
如果有以下语句记得删除,即自动格式化:
"editor.formatOnSave": true
3 Vuex
3.1 创建仓库
3.1.1 安装Vuex包
安装vuex与vue-router类似,vuex是一个独立存在的插件,如果脚手架初始化没有选 vuex,就需要额外安装
yarn add vuex@3 或者 npm i vuex@3
注意:报错可能是因为脚手架安装的eslint版本太高了,如果使用npm安装,可以在后面添:
npm i vuex@3 --legacy-peer-deps
进行强制安装
安装好后,为了维护项目目录的整洁,可以在src目录下新建一个store目录其下放置一个index.js文件 (和 router/index.js 类似)
3.1.2 创建仓库
//store/index.js
// 导入 vue
import Vue from 'vue'
// 导入 vuex
import Vuex from 'vuex'
// vuex也是vue的插件, 需要use一下, 进行插件的安装初始化
Vue.use(Vuex)// 创建仓库 store
const store = new Vuex.Store()// 导出仓库
export default store
3.1.3 将仓库挂载到vue实例上
//main.js
import Vue from 'vue'
import App from './App.vue'
import store from './store'Vue.config.productionTip = falsenew Vue({render: h => h(App),store
}).$mount('#app')
此刻起, 就成功创建了一个空仓库!!
3.2 仓库中的共享数据与方法
3.2.1 state状态(数据)
3.2.1.1 提供数据
//store/index.js
// 创建仓库 store
const store = new Vuex.Store({// state 状态, 即数据, 类似于vue组件中的data,// 区别:// 1.data 是组件自己的数据, // 2.state 中的数据整个vue项目的组件都能访问到state: {count: 101}
})
3.2.1.2 访问数据
3.2.1.2.1 $store
1.Vue模板中获取 this.$store
模板中: {{ $store.state.xxx }}
组件逻辑中: this.$store.state.xxx
2.js文件中获取 import 导入 store
JS模块中: store.state.xxx
这样写太复杂,可以使用下面的映射方法
3.2.1.2.2 mapState
第一步:导入mapState (mapState是vuex中的一个函数)(在需要该方法的vue文件中导入)
import { mapState } from 'vuex'
第二步:采用数组形式引入state属性
mapState(['count'])
上面这个语句最终得到类似于下面这个结果
count () {return this.$store.state.count
}
第三步:利用展开运算符将导出的状态映射给计算属性
computed: {...mapState(['count'])}
接下来就相当于conputed计算属性中有了count这个属性,可以直接用了
要区分的是,虽然写在computed中,但state并没有直接修改数据的权利,要使用getters
3.2.1.2 修改数据与严格模式
state中的数据实操上可以直接修改,如下:
<template><div class="box"><h2>Son1 子组件</h2>从vuex中获取的值: <label>{{$store.state.count}}</label><br><button @click="set">值 + 1</button></div>
</template><script>
export default {name: 'Son1Com',methods: {set () {this.$store.state.count++}}
}
</script>
但并不建议这样做,违背了vuex的单向数据流初衷,我们建议只使用mutations修改数据
通过 配置strict: true 可以开启严格模式,开启严格模式后,直接修改state中的值会报错
//store/index.js
import Vue from 'vue'
import Vuex from 'vuex'Vue.use(Vuex)const store = new Vuex.Store({strict: true,state: {count: 200}
})export default store
3.2.2 mutations(同步方法)
3.2.2.1 提供方法
import Vue from 'vue'
import Vuex from 'vuex'Vue.use(Vuex)const store = new Vuex.Store({strict: true,state: {count: 200},mutations: {// 方法里参数:第一个参数是当前store的state属性// 第二个参数:传递进来的数据,只能传一个,多参数可用对象addCount (state, n) {state.count += n}}
})export default store
3.2.2.2 使用方法
3.2.2.2.1 $store
语法:
this.$store.commit('方法名', 数据)
示例:
<template><div class="box"><h2>Son1 子组件</h2>从vuex中获取的值: <label>{{$store.state.count}}</label><br><button @click="set(1)">值 + 1</button></div>
</template><script>
export default {name: 'Son1Com',methods: {set (num) {this.$store.commit('addCount', num)}}
}
</script>
3.2.2.2.2 mapMutations
与mapState使用步骤相同,此处不再赘述,
注意:
1. mutations为方法而不是state那样的计算属性,因此要放在methods中进行展开
2. 传参直接加括号传即可,因为...mapMutations(['addCount'])等效于
addCount (n) {this.$store.commit('addCount', n)
}
示例:
<template><div class="box"><h2>Son2 子组件</h2>从vuex中获取的值:<label>{{count}}</label><br /><button @click="addCount(-1)">值 - 1</button></div>
</template><script>
import { mapState, mapMutations } from 'vuex'export default {name: 'Son2Com',//computed: {//...mapState(['count'])//},methods: {...mapMutations(['addCount'])}
}
</script>
注意:Vuex中mutations中要求不能写异步代码,如果有异步的ajax请求,应该放置在actions中
3.2.3 actions(异步方法)
3.2.3.1 提供方法
示例:1s后修改数据
import Vue from 'vue'
import Vuex from 'vuex'Vue.use(Vuex)const store = new Vuex.Store({//...mutations: {addCount (state, n) {state.count += n}},actions: {setAsyncCount (context, num) { //context在模块中理解,此处就当作是store仓库setTimeout(() => {context.commit('addCount', num) //actions不能直接修改state中的数据,需要调用mutations进行修改}, 1000)}}
})export default store
3.2.3.2 使用方法
3.2.3.2.1 $store
<template><div class="box"><h2>Son1 子组件</h2>从vuex中获取的值: <label>{{$store.state.count}}</label><br><button @click="set(1)">值 + 1</button></div>
</template><script>
export default {name: 'Son1Com',methods: {set (num) {this.$store.dispatch('setAsyncCount', num)}}
}
</script>
3.2.3.2.2 mapActions
与mapMutations使用方法类似,不再赘述
<template><div class="box"><h2>Son2 子组件</h2>从vuex中获取的值:<label>{{count}}</label><br /><button @click="setAsyncCount(-1)">值 - 1</button></div>
</template><script>
import { mapState, mapMutations, mapActions } from 'vuex'export default {name: 'Son2Com',computed: {...mapState(['count'])},methods: {...mapMutations(['addCount']),...mapActions(['setAsyncCount'])}
}
</script>
等价:
methods: {setAsyncCount (n) {this.$store.dispatch('setAsyncCount', n)},
3.2.4 getters(计算数据)
3.2.4.1 提供计算数据
const store = new Vuex.Store({//...getters: {// getters函数的第一个参数是 state// 必须要有返回值filterList (state) {return state.list.filter(item => item > 5)}// 简写:// filterList: state => state.list.filter(item => item > 5)}
})
3.2.4.2 使用计算数据
3.2.4.2.1 $store
与state一样
<div>{{ $store.getters.filterList }}</div>
3.2.4.2.2 mapGetters
computed: {...mapGetters(['filterList'])
}
<div>{{ filterList }}</div>
3.2.5 总结
3.3 module模块化
3.3.1 提供数据/方法
写法与前文中一样,只不过把实例化对象内部的state、mutations、actions、getters对象抽离出放在单独个js文件中,再导出,最后再在实例化对象中的module对象上挂载即可
示例:定义两个模块user、setting
user:
//modules/user.js
const state = {userInfo: {name: 'zs',age: 18}
}const getters = {// 分模块后,state指代子模块的stateUpperCaseName (state) {return state.userInfo.name.toUpperCase()}
}//默认模块中的 mutation 和 actions 会被挂载到全局,需要开启命名空间,才会挂载到子模块
const mutations = {setUser (state, newUserInfo) {state.userInfo = newUserInfo}
}const actions = {setUserSecond (context, newUserInfo) {// 将异步在action中进行封装setTimeout(() => {// 调用mutation context上下文,默认提交的就是自己模块的action和mutationcontext.commit('setUser', newUserInfo)}, 1000)}
}export default {namespaced: true, //开启命名空间state,mutations,actions,getters
}
setting:
// modules/setting.jsconst mutations = {setTheme (state, newTheme) {state.theme = newTheme}
}const actions = {}const getters = {}export default {
//官方推荐在模块化中,state以方法的形式进行书写,原因与data一样state () {return {theme: 'dark',desc: '描述真呀真不错'}},mutations,actions,getters
}
在store/index.js文件中的modules配置项中,注册这两个模块
import user from './modules/user'
import setting from './modules/setting'const store = new Vuex.Store({modules:{user,setting}
})
3.3.2 访问数据/方法
3.3.2.1 打印对比是否开启命名空间对$store中属性的影响
红色框:cart组件内的数据与方法
蓝色框: 全局的数据与方法
未开启命名空间:
开启命名空间:
3.3.2.2 未开启命名空间
未开启命名空间时,模块中的数据/方法存在时的变量名称与位置和全局数据/方法一样,
因此获取数据/方法的步骤语法也完全一样,此处就不再过多赘述,直接列出:
xxx代表数据/方法变量名,yyy代表模块名,n代表参数,组件逻辑指的是vue文件script内,模板指的是vue文件template内部标签中
mapxxx | 组件逻辑中直接使用 | 模板中直接使用 | JS文件中 | |
---|---|---|---|---|
state | mapState(['yyy']) | this.$store.state.yyy.xxx | $store.state.yyy.xxx | store.state.yyy.xxx |
getters | mapGetters(['xxx']) | this.$store.getters.xxx | $store.getters.xxx | store.getters.xxx |
mutations | mapMutations(['xxx']) | this.$store.commit('xxx', n) | $store.commit('xxx', n) | store.commit('xxx', n) |
actions | mapActions(['xxx']) | this.$store.dispatch('xxx', n) | $store.dispatch('xxx', n) | store.dispatch('xxx', n) |
注意:不需要死记硬背,观察3.3.4.1中的位置与名称即可
1.mapXXX(['aaa', 'bbb'])可以同时映射多个,开启命名空间时也同理
2.无论是否开启命名空间,只要写在模块中,state中的数据就会被套一层模块名,
因此要用mapState(['yyy'])
3.mapxxx使用前要引入,如 import { mapxxx } from 'vuex'
4.state、getters的mapxxx要在computed中进行展开,而mutations与actions的则要在methods中
5.展开之后相当于直接给computed/methods添加了同名的数据/方法,使用方法与事项与不使用vuex时的一样
6.在js中使用前要引入,如 import store from '@/store/index.js'
7.mutations与actions中的参数只能传一个,要传多个需要用数组或对象
3.3.2.3 开启命名空间
开启命名空间时,除了state保持不变,其他三类的数据/方法名都会被加上 模块名/ ,但本质使用方法仍不变
因此直接把原本的xxx替换为yyy/xxx即可,除此之外mapxxx还增加了一种写法,见下表
mapxxx | 组件逻辑中直接使用 | 模板中直接使用 | |
---|---|---|---|
state | mapState(['yyy']) mapState('yyy', ['xxx']) | this.$store.state.yyy.xxx | $store.state.yyy.xxx |
getters | mapGetters(['yyy/xxx']) mapGetters('yyy', ['xxx']) | this.$store.getters.['yyy/xxx'] | $store.getters.['yyy/xxx'] |
mutations | mapMutations(['yyy/xxx']) mapMutations('yyy', ['xxx']) | this.$store.commit('yyy/xxx', n) | $store.commit('yyy/xxx', n) |
actions | mapActions(['yyy/xxx']) mapActions('yyy', ['xxx']) | this.$store.dispatch('yyy/xxx', n) | $store.dispatch('yyy/xxx', n) |
注意:
1.由于篇幅原因,我删除了js这一列,但其也一样,将xxx改为yyy/xxx即可
2.记得加[''],因为yyy/xxx是一个整体作为变量名
4 json-server——模拟服务端api
4.1 使用步骤
第一步:安装全局工具 json-server (全局工具仅需要安装一次)
yarn global add json-server 或 npm i json-server -g
第二步:新建一个.json文件
推荐在vue等项目根目录中新建一个db文件夹,再在其中创建一个index.json文件
.json文件书写示例:
{"cart": [{"id": 100001,"name": "低帮城市休闲户外鞋天然牛皮COOLMAX纤维","price": 128,"count": 1,"thumb": "https://yanxuan-item.nosdn.127.net/3a56a913e687dc2279473e325ea770a9.jpg"},{"id": 100002,"name": "网易味央黑猪猪肘330g*1袋","price": 39,"count": 10,"thumb": "https://yanxuan-item.nosdn.127.net/d0a56474a8443cf6abd5afc539aa2476.jpg"},{"id": 100003,"name": "KENROLL男女简洁多彩一片式室外拖","price": 128,"count": 2,"thumb": "https://yanxuan-item.nosdn.127.net/eb1556fcc59e2fd98d9b0bc201dd4409.jpg"},{"id": 100004,"name": "云音乐定制IN系列intar民谣木吉他","price": 589,"count": 1,"thumb": "https://yanxuan-item.nosdn.127.net/4d825431a3587edb63cb165166f8fc76.jpg"}],"friends": [{ "id": 1, "name": "zs", "age": 18 },{ "id": 2, "name": "ls", "age": 19 },{ "id": 3, "name": "ww", "age": 20 }]
}
第三步:进入 db 目录,执行命令,启动后端接口服务
使用--watch 参数 可以实时监听 json 文件的修改
json-server --watch index.json
4.2 使用语法
4.2.1 获取数据
axios.get('http://localhost:3000/cart')
4.2.2 修改数据
axios.patch(`http://localhost:3000/cart/${obj.id}`, {count: obj.newCount})
要修改哪个值就在对象中传哪个值
更多使用方法见 json-server - npm