一、浏览器本地存储webStorage
存储内容大小一般支持5MB左右(不同浏览器可能还不一样)
浏览器端通过 Window.sessionStorage 和 Window.localStorage 属性来实现本地存储机制。
1. 相关API
(1) xxxxxStorage.setItem('key', 'value')
该方法接受一个键和值作为参数,会把键值对添加到存储中,如果键名存在,则更新其对应的值。
(2) xxxxxStorage.getItem('person')
;该方法接受一个键名作为参数,返回键名对应的值。
(3) xxxxxStorage.removeItem('key')
;该方法接受一个键名作为参数,并把该键名从存储中删除。
(4)xxxxxStorage.clear()
该方法会清空存储中的所有数据。
2. 注意点:
(1). SessionStorage存储的内容会随着浏览器窗口关闭而消失。
(2). LocalStorage存储的内容,需要手动清除才会消失。
(3). ```xxxxxStorage.getItem(xxx)```如果xxx对应的value获取不到,那么getItem的返回值是null。
(4). ```JSON.parse(null)```的结果依然是null。
二、组件的自定义事件
组件的自定义事件
1. 一种组件间通信的方式,适用于:<strong style="color:red">子组件 ===> 父组件</strong>
2. 使用场景:A是父组件,B是子组件,B想给A传数据,那么就要在A中给B绑定自定义事件(<span style="color:red">事件的回调在A中</span>)。
3. 绑定自定义事件:
1. 第一种方式,在父组件中
:```<Demo @atguigu="test"/>``` 或 ```<Demo v-on:atguigu="test"/>```
2. 第二种方式,在父组件中:
<Demo ref="demo"/>
......
mounted(){
this.$refs.xxx.$on('atguigu',this.test)
}
```
3. 若想让自定义事件只能触发一次,可以使用```once```修饰符,或```$once```方法。
4. 触发自定义事件:```this.$emit('atguigu',数据)```
5. 解绑自定义事件```this.$off('atguigu')```
6. 组件上也可以绑定原生DOM事件,需要使用```native```修饰符。
7. 注意:通过```this.$refs.xxx.$on('atguigu',回调)```绑定自定义事件时,回调<span style="color:red">要么配置在methods中</span>,<span style="color:red">要么用箭头函数</span>,否则this指向会出问题!
父组件给子组件传递数据,用 props属性
子组件给父组件传递数据的方法如下:
1.props法
父组件先给子组件传递一个函数
<!-- 通过父组件给子组件传递函数类型的props实现:子给父传数据 -->
<School :getSchoolName="getSchoolName" />
getSchoolName(name) {
console.log('App收到了学校名:', name);
},
子组件用props接收到函数后进行调用传值
<button @click="sendSchoolName(name)">点击把name交给App</button>
props: ['getSchoolName'],
methods: {
sendSchoolName(name) {
this.getSchoolName(name);
}
},
2.自定义事件(v-on)
父组件给子组件绑定zzz事件,事件触发就执行getStudentName函数
<!-- 通过父组件给子组件绑定一个自定义事件实现:子给父传数据(第一种写法,使用v-on或@) -->
<Student v-on:zzz="getStudentName"></Student>
getStudentName(name, age) {
console.log('App收到了学生名:', name, age);
}
在子组件中用$emit去触发zzz事件,第一个参数是事件名称,后面可以传参数
<button @click="sendStudentName">点我把学生名给App</button>
sendStudentName() {
//触发Student组件实例身上的zzz事件,并传数据过去
this.$emit('zzz', this.name, this.age);
}
3.自定义事件(ref)
<Student ref="student" />
用ref把标签标记一下,再获取进行绑定自定义事件
methods: {
getSchoolName(name) {
console.log('App收到了学校名:', name);
},
getStudentName(name, age) {
console.log('App收到了学生名:', name, age);
}
},
//挂载完成时,手动把zzz事件绑定到student的vc上
mounted() {
// this.$refs.student.name???
setTimeout(() => {
//挂载完成后,隔3秒再给Student绑定事件
this.$refs.student.$on('zzz', this.getStudentName);
}, 3000);
这种方法可以实现异步
4.解绑自定义事件
//解绑一个自定义事件
this.$off('zzz');
//解绑多个自定义事件
this.$off(['zzz', 'zc']);
//解绑所有自定义事件
this.$off();
5.注意点
(1)组件标签上写的v-on
都会被当成自定义事件,即便是写@click也会当成自定义事件,想要用原生DOM事件的话,需要加native
,比如@click.native = "demo"
(2)
通过this.$refs.xxx.$on('atguigu',回调)
绑定自定义事件时,回调要么配置在methods中然后通过this.getStudentName
传过来。
this.$refs.student.$on('zzz', this.getStudentName);
mounted() {
this.$refs.student.$on('zzz', (name, ...arr) => {
console.log(this); //this指向App组件实例
this.studentName = name; //成功,因为this指向的是App组件实例对象
});
}
如果不写箭头函数,this会出现问题,即指向的是student组件
三、全局事件总线(GlobalEventBus)
一种组件间通信的方式,适用于任意组件间通信。是想实现这个功能,必须在所有组件外设置一个中转站,所有组件都能访问到它,并且他还有$on,$off、$emit等方法。显然,只能Vue.prototype
上添加一个属性,因为vc和vm都能访问到它。
1.安装全局事件总线
用vm安装(一般用这个)
new Vue({
......
beforeCreate() {
Vue.prototype.$bus = this //安装全局事件总线,$bus就是当前应用的vm
},
......
})
用vc安装(了解)
const Demo = Vue.extend({});
const d = new Demo();
Vue.prototype.$bus = d;
2.使用全局事件总线
接收数据:A组件想接收数据,则在A组件中给$bus绑定自定义事件,事件的<回调留在A组件自身。
methods(){
demo(data){......}
}
......
mounted() {
this.$bus.$on('xxxx',this.demo)
}
提供数据的组件B去触发这个自定义事件这样就实现了组件B把数据传给组件A
this.$bus.$emit('xxxx',数据)
3.解绑全局事件总线
在组件被销毁前解绑它所绑定的事件总线
beforeDestory(){
this.$bus.$off('xxxx')
}
当遇到孙传爷数据,之前都是传函数给儿子再给孙子比较麻烦,现在就可以用事件总线了。
四、消息订阅与发布(pubsub)
和全局事件总线一样适用于任意组件间通信,但它要安装第三方库,而全局事件总线是在Vue上操作,所以pubsub用的比较少。
1.安装
安装pubsub:npm i pubsub-js
引入:import pubsub from 'pubsub-js
2.使用
接收数据:A组件想接收数据,则在A组件中订阅消息,订阅的回调留在A组件自身。
methods(){
demo(msgName,data){......}
}
......
mounted() {
this.pubsubId = pubsub.subscribe('xxx',this.demo) //订阅消息
},
beforeDestroy() {
pubsub.unsubscribe(this.pubsubId); //销毁时取消订阅
},
提供数据:
methods: {
sendStudentName() {
pubsub.publish('xxx', this.name); //发布消息并传数据
}
},
五、$nextTick
1. 语法:this.$nextTick(回调函数)
2. 作用:在下一次 DOM 更新结束后执行其指定的回调。
3. 什么时候用:当改变数据后,要基于更新后的新DOM进行某些操作时,要在nextTick所指定的回调函数中执行。
4.写一个定时器(延迟为0)也和$nextTick有同样的效果
为什么没写$nextTick初始获取不了焦点,因为input框开始v-show是false是隐藏的,调用handleEdit时虽然先把isEdit改为了true,但模版还没解析(函数执行完才解析,不是改一个数据就解析一次,效率问题),所以input还是隐藏的,this.$refs.inputTitle.focus()找不到元素。
<input
type="text"
v-show="todo.isEdit"
:value="todo.title"
@blur="handleBlur(todo,$event)"
ref="inputTitle"
>
handleEdit(todo){
if(todo.hasOwnProperty('isEdit')){
todo.isEdit = true
}else{
// console.log('@')
this.$set(todo,'isEdit',true)
}
//无法获取焦点
// this.$refs.inputTitle.focus()
//能够获取焦点
this.$nextTick(function(){
this.$refs.inputTitle.focus()
})
},
六、Vue封装的过度与动画
1.动画效果
实现-实现come和go的样式切换,就是换clss的名字 用transtion交给vue管理,下面的类名也必须写成.v-enter-active,.v-leave-active,transtion可以设置name,但下面的类名的v也要改成name的值。appear就是让页面一点开就有v-enter-active的效果。最后渲染完会去掉transtion标签
<template>
<div>
<button @click="isShow=!isShow">显示与隐藏</button>
<!--实现come和go的样式切换,就是换clss的名字 用transtion交给vue管理 -->
<transition appear>
<h1 v-show="isShow" class="come">你好啊</h1>
</transition>
</div>
</template>
<script>
export default {
// 名字加引号
name:'Test1',
data(){
return{
isShow:true
}
}
}
</script>
<style scoped>
h1{
background-color: orange;
}
/* .come{
animation: myCss 1s linear;
} */
.v-enter-active{
animation: myCss 1s linear;
}
/* .go{
animation: myCss 1s reverse;
} */
.v-leave-active{
animation: myCss 1s reverse;
}
@keyframes myCss {
from{
transform: translateX(-100%);
}
to{
transform: translateX(0);
}
}
</style>
2.过渡效果
写法:
1. 准备好样式:
元素进入的样式:
1. v-enter:进入的起点
2. v-enter-active:进入过程中
3. v-enter-to:进入的终点
元素离开的样式:
1. v-leave:离开的起点
2. v-leave-active:离开过程中
3. v-leave-to:离开的终点
2. 使用```<transition>```包裹要过度的元素,并配置name属性:
<transition name="hello">
<h1 v-show="isShow">你好啊!</h1>
</transition>
3. 备注:若有多个元素需要过度,则需要使用:```<transition-group>```,且每个元素都要指定```key```值。
<transition-group name="hello" appear>
<h1 v-show="!isShow" key="1">你好啊!</h1>
<h1 v-show="isShow" key="2">尚硅谷!</h1>
</transition-group>
<template>
<div>
<button @click="isShow=!isShow">显示与隐藏</button>
<transition name="hello" appear>
<h1 v-show="isShow" class="come">你好啊</h1>
</transition>
</div>
</template>
<script>
export default {
// 名字加引号
name:'Test1',
data(){
return{
isShow:true
}
}
}
</script>
<style scoped>
h1{
background-color: orange;
}
/* 进入的起点*/
.hello-enter,.hello-leave-to{
transform: translateX(-100%);
}
/* 进入的终点*/
.hello-enter-to,.hello-leave{
transform: translateX(0);
}
.hello-enter-active,.hello-leave-active{
transition: 1s linear;
}
</style>
3.集成第三方动画库
/在js里引入
import 'animate.css'
//在template配置 完全不用写css
<transition-group
appear
name="animate__animated animate__bounce"
enter-active-class="animate__swing"
leave-active-class="animate__backOutUp"
>
<h1 v-show="!isShow" key="1">你好啊!</h1>
<h1 v-show="isShow" key="2">尚硅谷!</h1>
</transition-group>
七、脚手架配置代理
1.方法一
在脚手架文件目录下npm i axios
安装一下子axios,用axios发送请求
问题:当出现跨域问题(违反同源策略,同源策略就是协议、域名、端口号三个必须完全一样)怎么办?可以用代理服务器解决,就是在vue脚手架中再开启一台代理服务端,端口也是8080,浏览器发送8080请求去找资源,会去代理服务器找,而代理服务器经过配置(若配置5000端口)会转发给5000端口那台服务器找。代理服务器和5000这台服务器它们是两台服务器之间交换数据,不是浏览器和服务器所以不需要遵循同源策略,很容易就能得到数据。
//在vue.config.js中添加如下配置
devServer:{
proxy:"http://localhost:5000"
}
<template>
<body>
<button @click="getStudents">获取学生信息</button>
</body>
</template>
<script>
import axios from 'axios';
export default {
name: 'App',
methods:{
getStudents(){
axios.get('http://localhost:8080/students').then(
response=>{
console.log('请求成功了',response.data);
},
error=>{
console.log('请检查错误',error)
}
)
}
}
}
</script>
说明:
1. 优点:配置简单,请求资源时直接发给前端(8080)即可。
2. 缺点:不能配置多个代理,不能灵活的控制请求是否走代理。
3. 工作方式:若按照上述配置代理,当请求了前端不存在的资源时,那么该请求会转发给服务器 (优先匹配前端资源)如果要找的资源前端有同名的就不会转发到服务器去找了,如前端public有个students文件,就不会转发了。
2.方式二
在vue.config.js中添加如下配置
devServer: {
proxy: {
// zcj写在路径前面,加上这个才会进行代理转发,即使前端有同名资源
'/zcj': {
//指明ajax请求要转发给谁
target: 'http://localhost:5000',
//pathRewrite把请求转发给5000时去掉zcj路径,正则表达式
pathRewrite: { '^/zcj': '' },
ws: true, //用于支持websocket
//changeOrigin用于控制请求头中的host值
// true就是把请求源改成和目标服务器一致(5000),false就是8080
changeOrigin: true //写true比较合适,默认也是true(React里默认false)
},
'/cj': {
//指明ajax请求要转发给谁
target: 'http://localhost:5001',
//pathRewrite把请求转发给5000时去掉zcj路径
pathRewrite: { '^/cj': '' },
ws: true, //用于支持websocket
//changeOrigin用于控制请求头中的host值
// true就是把请求源改成和目标服务器一致(5000),false就是8080
changeOrigin: true //写true比较合适,默认也是true(React里默认false)
},
}
}
/*
changeOrigin设置为true时,服务器收到的请求头中的host为:localhost:5000
changeOrigin设置为false时,服务器收到的请求头中的host为:localhost:8080
changeOrigin默认值为true
*/
methods:{
getStudents(){
axios.get('http://localhost:8080/zcj/students').then(
response=>{
console.log('请求成功了',response.data);
},
error=>{
console.log('请检查错误',error)
}
)
},
getCars(){
axios.get('http://localhost:8080/cj/cars').then(
response=>{
console.log('请求成功了',response.data);
},
error=>{
console.log('请检查错误',error)
}
)
},
}
1. 优点:可以配置多个代理,且可以灵活的控制请求是否走代理。加了前缀,即使前端有同名资源代理服务器也会转发。
2. 缺点:配置略微繁琐,请求资源时必须加前缀。
3.pathRewrite,把路径重写去掉这个路径前缀,要不然最后的资源服务器找不到这个前缀路径下的资源
八、Github案例
1.步骤
(1)首先把现成的html和css进行拆分,注意bootstrap样式需要在public下新建css文件夹粘贴bootstrap.css,然后去index.html引入,直接import会有问题。
(2)在search输入框中绑定keyWord,获得用户输入的值。在按钮里绑定click事件的函数,函数去发axios请求给`https://api.github.com/search/users?q=${this.keyWord}`得到返回的数据
(3)再用全局事件总线把数据传给List,List把数据给data,再用v-for去渲染这个页面。
(4)进行完善,在初始页面添加欢迎词,页面加载时提示正在加载中,有错误显示错误信息,注意代码中扩展运算符的运用,
2.代码
App.vue
<template>
<div class="container">
<search></search>
<List></List>
</div>
</template>
<script>
import axios from 'axios';
import List from './components/List.vue';
import Search from './components/Search.vue';
export default {
name: 'App',
components:{
Search,
List
},
}
</script>
<style>
</style>
List.vue
<template>
<div class="row">
<div v-show="info.users.length" class="card" v-for="user in info.users" :key="user.login">
<a :href="user.html_url" target="_blank">
<img :src="user.avatar_url" style='width: 100px'/>
</a>
<p class="card-text">{{ user.login }}</p>
</div>
<h1 v-show="info.isFirst">欢迎您</h1>
<h1 v-show="info.isLoading">正在 加载......</h1>
<h1 v-show="info.eorMsg">{{ info.eorMsg }}</h1>
</div>
</template>
<script >
export default {
name:'List',
data(){
return {
info:{
isFirst:true,
isLoading:false,
eorMsg:'',
users:[]
}
}
},
mounted(){
this.$bus.$on('UpdateInfo',(infoObj)=>{
// 这里很厉害,infoObj里的属性替换合并info对象里的属性
this.info={...this.info,...infoObj}
})
}
}
</script >
<style scoped>
.album {
min-height: 50rem; /* Can be removed; just added for demo purposes */
padding-top: 3rem;
padding-bottom: 3rem;
background-color: #f7f7f7;
}
.card {
float: left;
width: 33.333%;
padding: .75rem;
margin-bottom: 2rem;
border: 1px solid #efefef;
text-align: center;
}
.card > img {
margin-bottom: .75rem;
border-radius: 100px;
}
.card-text {
font-size: 85%;
}
</style>
search.vue
<template>
<section class="jumbotron">
<h3 class="jumbotron-heading">Search Github Users</h3>
<div>
<input type="text" placeholder="enter the name you search" v-model="keyWord"/>
<button @click="getUsers">Search</button>
</div>
</section>
</template>
<script>
import axios from 'axios';
export default {
name:'Search',
data(){
return {
keyWord:''
}
},
methods:{
getUsers(){
this.$bus.$emit('UpdateInfo',{isFirst:false,isLoading:true,eorMsg:'',users:[]})
axios.get(`https://api.github.com/search/users?q=${this.keyWord}`).then(
response=>{
console.log('请求成功了')
// idFirst后面都是false可以不用传,因为在List中用了...运算符进行合并属性
this.$bus.$emit('UpdateInfo',{isLoading:false,eorMsg:'',users:response.data.items})
},
error=>{
console.log('出现了错误')
this.$bus.$emit('UpdateInfo',{isLoading:false,eorMsg:error.message,users:[]})
}
)
}
}
}
</script>
<style>
</style>
3.vue-resource
vue-resource是用来发送请求的一个插件。在vue1.0版本经常用,现在都用axios,了解一下就行。发送的方法就是把axios.get
换成this.$http.get,其它都一样。
安装vue-resource :npm i vue-resource
引入插件:import vueResource from 'vue-resource'
使用插件:Vue.use(vueResource)
this.$http.get(`https://api.github.com/search/users?q=${this.keyWord}`).then(
response=>{
console.log('请求成功了')
// idFirst后面都是false可以不用传,因为在List中用了...运算符进行合并属性
this.$bus.$emit('UpdateInfo',{isLoading:false,eorMsg:'',users:response.data.items})
},
error=>{
console.log('出现了错误')
this.$bus.$emit('UpdateInfo',{isLoading:false,eorMsg:error.message,users:[]})
}
)
九、插槽
1. 作用:让父组件可以向子组件指定位置插入html结构,也是一种组件间通信的方式,适用于 <strong style="color:red">父组件 ===> 子组件</strong> 。
2. 分类:默认插槽、具名插槽、作用域插槽
1.默认插槽
这三个用同一个组件,如何更换一下里面的内容?
在组件标签写要显示的内容,但是要插在组件模板的哪个位置要用插槽来指明,插槽起到一个定位的效果。相当于slot
标签在子组件挖个坑,然后在父组件的组件标签里面写东西往里边填。
子组件
<template>
<div class="category">
<h3>{{title}}分类</h3>
<slot>我是一些默认值,没传数据显示我</slot>
</div>
</template>
<script>
export default {
name:'Category',
props:['title']
}
</script>
父组件
<template>
<div class="container">
<Category title="美食">
<img src="https://s3.ax1x.com/2021/01/16/srJlq0.jpg" alt="">
</Category>
<Category title="游戏">
<ul>
<li v-for="(item,index) in games" :key='index'>{{ item }}</li>
</ul>
</Category>
<Category title="电影">
<video controls src="http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4"></video>
</Category>
</div>
</template>
<script>
import Category from './components/Category1.vue';
export default {
name: 'App',
components:{
Category
},
data(){
return{
foods:['小米','大米','西瓜','苹果'],
games:['植物大战僵尸','qq飞车','王者荣耀','恋与深空'],
films:['唐探1','唐探2','唐探3','唐探4']
}
}
}
</script>
2、具名插槽
给子组件的slot取个名字,在父组件指明slot='xxx'内容放在哪个插槽里。
子组件
<template>
<div class="category">
<h3>{{title}}分类</h3>
<slot name="center">我是一些默认值,没传数据显示我1</slot>
<slot name="footer">我是一些默认值,没传数据显示我1</slot>
</div>
</template>
父组件 当想把多个结构放在同一标签,可以用template标签和v-slot:footer配合使用
<template>
<div class="container">
<Category title="美食">
<img src="https://s3.ax1x.com/2021/01/16/srJlq0.jpg" alt="" slot="center">
<a href="http://www.baidu.com" slot="footer">点我跳到百度</a>
</Category>
<Category title="游戏">
<ul>
<li v-for="(item,index) in games" :key='index'>{{ item }}</li>
</ul>
<!-- 当想把多个结构放在同一标签,可以用template标签和v-slot:footer配合使用 -->
<template v-slot:footer>
<h4>试一下v-slot</h4>
<a href="http://www.baidu.com" >点我跳到百度</a>
</template>
</Category>
<Category title="电影">
<video controls src="http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4"></video>
</Category>
</div>
</template>
3.作用域插槽
现在三个categroy内容全不变,但是要让样式变而且数据在category组件里怎么办?
在category组件里往App组件传数据,App组件一定要写<template scope=’xxx‘>去接收,或者是slot-scope。
子组件
<template>
<div class="category">
<h3>{{title}}分类</h3>
<slot name="center" :youxis="games">我是一些默认值,没传数据显示我1</slot>
<!-- <slot name="footer" :youxis="games">我是一些默认值,没传数据显示我2</slot> -->
</div>
</template>
<script>
export default {
name:'Category',
props:['title'],
data(){
return{
games:['植物大战僵尸','qq飞车','王者荣耀','恋与深空'],
}
}
}
</script>
父组件
<template>
<div class="container">
<Category title="游戏">
<template slot="center" scope="zcj">
<ul>
<li v-for="(item,index) in zcj.youxis" :key='index'>{{ item }}</li>
</ul>
</template>
</Category>
<Category title="游戏">
<!-- template标签必须写 -->
<!-- 这个scope随便取名,会把插槽的数据自动付给它,它是一个对象,因为可能很多数据传过来 -->
<template slot="center" scope="zcj">
<ol>
<li v-for="(item,index) in zcj.youxis" :key='index'>{{ item }}</li>
</ol>
</template>
</Category>
<Category title="游戏">
<template slot="center" scope="zcj">
<h4 v-for="(item,index) in zcj.youxis" :key='index'>{{ item }}</h4>
</template>
</Category>
</div>
</template>
<script>
import Category from './components/Category1.vue';
export default {
name: 'App',
components:{
Category
},
}
</script>
十、Vuex
1.Vuex的基本概念及环境
在Vue中实现集中式状态(数据)管理的一个Vue插件,对vue应用中多个组件的共享状态进行集中式的管理(读/写),也是一种组件间通信的方式,且适用于任意组件间通信。 多个组件需要共享数据时使用。
2.环境搭建
(1)安装vuex,输入命令npm i vuex@3。vue2安装vuex3,vue3安装vuex4。在package.json
文件里看下vue的版本。
(2)创建文件:src/store/index.js
为什么不能在main文件下写Vue.use(Vuex),因为我们在main文件下要import ./store/index.js(vue会自动找index,所以可以省略index) 而在import的时候会执行index里的代码,而创建store必须先执行Vue.use(Vuex),所以把这行代码写在index中。
//引入Vue核心库
import Vue from 'vue'
//引入Vuex
import Vuex from 'vuex'
//应用Vuex插件
Vue.use(Vuex)
//准备actions对象——响应组件中用户的动作
const actions = {}
//准备mutations对象——修改state中的数据
const mutations = {}
//准备state对象——保存具体的数据
const state = {}
//创建并暴露store
export default new Vuex.Store({
actions,
mutations,
state
})
(3)在main中引入和配置store
js在执行时会先扫描import把import都提到最前面先执行了。如果在main.js里 import store from './store'
无论放到哪里都会比Vue.use(Vuex)
先执行,要想把 Vue.use(Vuex)
要放到实例化之前只有放进index.js。
import Vue from 'vue'
import App from './App.vue'
import store from './store' //引入store
Vue.config.productionTip = false
// import vueResource from 'vue-resource'
// Vue.use(vueResource)
new Vue({
el: '#app',
// render: h => h(App),
render: creatElement => creatElement(App),
store, //配置store
beforeCreate() {
Vue.prototype.$bus = this
},
})
2.Vuex的工作原理
vc里写函数dispatch给action(服务员),action里面写数据业务逻辑,commit给mutations(厨师)做最后的处理。
3.vuex的使用
以求和为例
(1)把要让公共访问的sum放到index.js的state里,在count.vue的模板里用{{ $store.state.sum }}去访问sum。
const state = {
sum: 0,
}
(2)组件的回调函数之间写 this.$store.dispatch('函数名',参数)给action进行业务处理,如果不需要业务处理,可以之间this.$store.commit('JIA',this.n)给mutation。
methods:{
increment(){
this.$store.commit('JIA',this.n)
},
incrementOdd(){
this.$store.dispatch('jiaOdd',this.n)
},
},
(3)在action里用jiaOdd来接收,jiaOdd能够收到俩个参数,第一个是简化版的store称之为context进行业务处理,第二个就是传过来的参数value,处理完再commit给mutation。
const actions = {
jiaOdd(context, value) {
console.log('jian的action执行了');
if (context.state.sum % 2) {
context.commit('JIA', value)
}
},
}
(4)在mutation中用JIA来接收,,一般都写成actions
对应函数的大写。也是接两个参数,第一个参数是state对象,第二个参数是传过来的数据。
// 用于操作数据
const mutations = {
JIA(state, value) {
// console.log(state, value)
state.sum += value
},
}
(5)mutation主要用来处理数据来实现响应式页面
框架:
//引入Vue核心库
import Vue from 'vue'
//引入Vuex
import Vuex from 'vuex'
//引用Vuex
Vue.use(Vuex)
const actions = {
//响应组件中加的动作
jia(context,value){
// console.log('actions中的jia被调用了',miniStore,value)
context.commit('JIA',value)
},
}
const mutations = {
//执行加
JIA(state,value){
// console.log('mutations中的JIA被调用了',state,value)
state.sum += value
}
}
//初始化数据
const state = {
sum:0
}
//创建并暴露store
export default new Vuex.Store({
actions,
mutations,
state,
})
注意点:
-
组件中读取vuex中的数据:
$store.state.sum
-
tions
里面也可以操作数据,但是如果不在mutations
里操作数据,而在actions
里操作数据,vuex开发者工具会失效的 -
一般来说都会把网络请求或其他业务逻辑写到
actions
里面 -
组件中修改vuex中的数据:
$store.dispatch('action中的方法名',数据)
或$store.commit('mutations中的方法名',数据)
备注:若没有网络请求或其他业务逻辑,组件中也可以越过actions,即不写
dispatch
,直接编写commit
4.getter的使用
(1)概念:当state中的数据需要经过加工后再使用时,可以使用getters加工。
(2)使用:
在index.js中写getters配置项,需要有返回值这和计算属性很像。
const getters = {
// 有参数state,这样才能拿到sum进行操作
bigSum(state) {
return state.sum * 10
}
}
// 创建并暴露store
export default new Vuex.Store({
actions,
mutations,
state,
// 把getters配置到里面
getters
})
(2)组件中读取数据:$store.getters.bigSum
<h1>放大后的和为:{{ $store.getters.bigSum }}</h1>
(3)其实state
就类似于data
,getters
就类似computed
5.mapState与mapGetters
<h1>当前求和为:{{ $store.state.sum }}</h1>
<h1>放大后的和为:{{ $store.getters.bigSum }}</h1>
<h1>我在{{$store.state.school}}学校,学习{{ $store.state.subject }}</h1>
每次用插值语法我定义的属性 ,都是写$store.state,很麻烦怎么办?可以用计算属性去解决。
computed:{
sum(){
return this.$store.state.sum
},
school(){
return this.$store.state.school
},
subject(){
return this.$store.state.subject
}
},
<h1>当前求和为:{{ sum }}</h1>
<h1>放大后的和为:{{ $store.getters.bigSum }}</h1>
<h1>我在{{school}}学校,学习{{ subject }}</h1>
可是写计算属性也很麻烦,怎么办?用mapState
先引入import {mapState,mapGetters,mapMutations,mapActions} from 'vuex'
数组写法和对象写法:如果方法名和函数名不一样用对象写法,一样用数组写法(相当于是省略)
// 数组写法
// ...mapState(['sum','school','subject']),
// 对象写法
...mapState({sum:'sum',school:'school',subject:'subject'})
...是扩展运算符,以下是例子:
let obj1 = {x:100, y:200};
let obj2 = {
a:1,
...obj1,
b:2
}
console.log(obj2); //{a: 1, x: 100, y: 200, b: 2}
6.mapActions与mapMutations
methods:{
increment(){
this.$store.commit('JIA',this.n)
},
decrement(){
this.$store.commit('JIAN',this.n)
},
incrementOdd(){
this.$store.dispatch('jiaOdd',this.n)
},
incrementWait(){
this.$store.dispatch('jiaWait',this.n)
}
},
同样调用dispatch和commit也很麻烦,所以创建了mapActions与mapMutations。
mapActions方法:用于帮助我们生成与actions
对话的方法,即:包含$store.dispatch(xxx)
的函数。mapMutation方法:用于帮助我们生成与mutations
对话的方法,即:包含$store.commit(xxx)
的函数。
也有对象写法和数组写法:
methods:{
// 对象写法
...mapMutations({increment:'JIA',decrement:'JIAN'}),
...mapActions({incrementOdd:'jiaOdd',incrementWait:'jiaWait'})
// 数组写法,就当作是函数名和即将处理的函数名相同的简写方式
// ...mapMutations({JIA:'JIA',JIAN:'JIAN'}),
// ...mapMutations(['JIA','JIAN'])
},
当时好像没有指定传入的参数this.n,实际底层创建的是这样,这个value其实就是event,而我们想要的是自己指定参数,所以可以在事件函数里面就传如下:
add(value) {
this.$store.commit('JIA', value);
},
<button @click="increment(n)">+</button>
<button @click="decrement(n)">-</button>
<button @click="incrementOdd(n)">当前求和为奇数再加</button>
<button @click="incrementWait(n)">等一等再加</button>
7.多组件共享数据
放在vuex的state里的值就相当于全局变量,每一个组件都可以用,可以在插值语法里用
$store.state.personList之间调用,也有在computed里写上...mapState({personList:'personList'}),
然后之间在插值语法里写personList调用。
8.vuex模块化+namespaced
目的:一个index的mutation、action里包括了人员相关操作、订单相关操作等很乱。所以最后给它进行分类,每一类有自己的mutation、action等,让代码更好维护,让多种数据分类更加明确。
使用:首先要写命名空间namespaced=true,要不然mapState等不会认识我们的模块名,不开启命名空间只能要获取值要一层一层调,如perAbout.personList
(1)开启命名空间后,组件中读取state数据:
//方式一:自己直接读取
this.$store.state.personAbout.list
//方式二:借助mapState读取:
...mapState('countAbout',['sum','school','subject']),
(2)开启命名空间后,组件中读取getters数据:
//方式一:自己直接读取
this.$store.getters['personAbout/firstPersonName']
//方式二:借助mapGetters读取:
...mapGetters('countAbout',['bigSum'])
(3)开启命名空间后,组件中调用dispatch:
//方式一:自己直接dispatch
this.$store.dispatch('personAbout/addPersonWang',person)
//方式二:借助mapActions:
...mapActions('countAbout',{incrementOdd:'jiaOdd',incrementWait:'jiaWait'})
(4)开启命名空间后,组件中调用commit:
//方式一:自己直接commit
this.$store.commit('personAbout/ADD_PERSON',person)
//方式二:借助mapMutations:
...mapMutations('countAbout',{increment:'JIA',decrement:'JIAN'}),
index.js
// 该文件用于创建Vuex中最核心的store
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const countOptions = {
namespaced: true,
actions: {
jiaOdd(context, value) {
console.log('jian的action执行了');
if (context.state.sum % 2) {
context.commit('JIA', value)
}
},
jiaWait(context, value) {
console.log('jiaWait的action执行了');
setTimeout(() => {
context.commit('JIA', value)
}, 500);
}
},
mutations: {
JIA(state, value) {
// console.log(state, value)
state.sum += value
},
JIAN(state, value) {
// console.log(state, value)
state.sum -= value
},
},
state: {
sum: 0,
school: 'hzu',
subject: '计算机',
},
getters: {
bigSum(state) {
return state.sum * 10
}
}
}
const personOptins = {
namespaced: true,
actions: {
},
mutations: {
ADD_PERSON(state, value) {
state.personList.unshift(value)
}
},
state: {
personList: [
{ id: '001', name: 'zhangsan' }
]
},
getters: {
}
}
// 创建并暴露store
export default new Vuex.Store({
modules: {
countAbout: countOptions,
personAbout: personOptins
}
})
Count.vue
<template>
<div>
<h4>当前求和为:{{ sum }}</h4>
<h4>放大后的和为:{{ bigSum }}</h4>
<h4>我在{{school}}学校,学习{{ subject }}</h4>
<h4>下方组件的总人数是:{{personList.length }}</h4>
<!-- option的value是字符 在前面加冒号 -->
<!-- 后者在v-model后加number 让收到的值转为数字型 -->
<select v-model.number="n">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
<button @click="increment(n)">+</button>
<button @click="decrement(n)">-</button>
<button @click="incrementOdd(n)">当前求和为奇数再加</button>
<button @click="incrementWait(n)">等一等再加</button>
</div>
</template>
<script>
import { mapState,mapGetters,mapActions,mapMutations } from 'vuex';
export default {
name:'Count',
data(){
return {
n:1
}
},
computed:{
// 手动写法
// sum(){
// return this.$store.state.sum
// },
// school(){
// return this.$store.state.school
// },
// subject(){
// return this.$store.state.subject
// }
// 数组写法
// ...mapState(['sum','school','subject']),
// 对象写法
...mapState('countAbout',{sum:'sum',school:'school',subject:'subject'}),
...mapState('personAbout',{personList:'personList'}),
...mapGetters('countAbout',['bigSum'])
},
methods:{
// 程序员手动写
// increment(){
// this.$store.commit('JIA',this.n)
// },
// decrement(){
// this.$store.commit('JIAN',this.n)
// },
// incrementOdd(){
// this.$store.dispatch('jiaOdd',this.n)
// },
// incrementWait(){
// this.$store.dispatch('jiaWait',this.n)
// }
// 对象写法
...mapMutations('countAbout',{increment:'JIA',decrement:'JIAN'}),
...mapActions('countAbout',{incrementOdd:'jiaOdd',incrementWait:'jiaWait'})
// 数组写法,就当作是函数名和即将处理的函数名相同的简写方式
// ...mapMutations({JIA:'JIA',JIAN:'JIAN'}),
// ...mapMutations(['JIA','JIAN'])
},
mounted(){
console.log('Count',this);
}
}
</script>
<style>
button{
margin-left: 5px;
}
</style>
Person.vue
<template>
<div>
<h5>人员列表</h5>
<input type="text" placeholder="请输入名字" v-model="name">
<button @click="add">添加</button>
<ul>
<li v-for="p in personList" :key="p.id">{{ p.name }}</li>
</ul>
<h5>count里面的sum值是多少:{{ sum }}</h5>
</div>
</template>
<script>
import { nanoid } from 'nanoid';
export default {
name:'Person',
data(){
return {
name:''
}
},
computed:{
personList(){
return this.$store.state.personAbout.personList
},
sum(){
return this.$store.state.countAbout.sum
}
},
methods:{
add(){
const personObj={id:nanoid(),name:this.name}
// console.log(personObj);
this.$store.commit('personAbout/ADD_PERSON',personObj)
}
}
}
</script>
<style>
</style>
App.vue
<template>
<div>
<Count></Count>
<Person></Person>
</div>
</template>
<script>
import Count from './components/Count.vue';
import Person from './components/Person.vue';
export default {
name: 'App',
components:{
Count,
Person
},
mounted() {
console.log(this);
},
}
</script>
<style>
</style>