文章目录
前言
组件(Component)是 Vue 最核心的功能,也是整个框架设计最出彩的地方,而组件实例的作用域是相互独立的,也就是说不同组件间的数据是无法直接互相引用的。那么,组件之间是如何进行通信传递数据的呢?这就需要我们先搞清楚组件之间的关系:
一、Vue组件关系
如上图所示,组件间的关系有:
- 父子关系:A与B、A与C、B与D、C与E
- 兄弟关系:B与C
- 隔代关系(可能隔多代):A与D、A与E
- 非直系亲属关系:D与E
以上关系可总结为三大类:父子组件通信、兄弟组件通信、跨级组件通信。
二、Vue组件通信方式
Vue组件的通信方式,总结起来有以下8种:
1. props / $emit
1.1 props 父传子
父组件向子组件传递数据或参数,是通过 props
来实现的。在父组件中在自定义的子组件标签上添加要传递的 props 的名称及数据(可通过v-bind动态绑定props的值),而子组件通过选项 props
来声明需要从父组件接收的数据,props
的值分为两种:一种是字符串数组,一种是对象(当prop需要验证时使用,推荐)。
<!--parent.vue-->
<template>
<div>
<!--props的值可以直接传递也可以动态绑定-->
<child message1="我是父组件直接传递的数据" :message2="message"></child>
</div>
</template>
<script>
import child from "./child"
export default {
name: 'parent',
components: {
child
},
data() {
return {
message: ['我是父组件动态绑定传递的数据1', '我是父组件动态绑定传递的数据2', '我是父组件动态绑定传递的数据3']
}
}
}
</script>
<!--child.vue-->
<template>
<div>
<p>{{message1}}</p>
<ul>
<li v-for="(item,index) in message2" :key=index>{{item}}</li>
</ul>
</div>
</template>
<script>
export default {
name: 'child',
//props: ['message1', 'message2'], //props的第一种:字符串数组
props: {//props的第二种:对象
message1: {
type: String,
default: ''
},
message2: {
type: Array,
default: () => []
}
},
data() {
return {
}
}
}
</script>
1.2 $emit 子传父
通过props传递数据是单向的,父组件数据变化时会传递给子组件,但子组件不能通过修改props传过来的数据来修改父组件的相应状态,即所谓的单向数据流。因为这种特性,子组件需要向父组件传递数据时,就要用到自定义事件。子组件用 $emit()
来出发事件,父组件用 $on()
来监听子组件的事件,也可以直接在子组件的自定义标签上使用v-on来监听子组件触发的自定义事件。
<!--parent.vue-->
<template>
<div>
<p>{{parentData}}</p>
<!--在子组件自定义标签上用v-on(此处用的语法糖@)监听子组件触发的自定义事件clickEvent-->
<child @clickEvent='parentClickEvent'></child>
</div>
</template>
<script>
import child from "./child"
export default {
name: 'parent',
components: {
child
},
data() {
return {
parentData: '父组件本来的数据'
}
},
methods: {
parentClickEvent(val) {
this.parentData = val; //val是子组件传递过来的数据
}
}
}
</script>
<!--child.vue-->
<template>
<button @click="childClickEvent">点击修改父组件数据</button>
</template>
<script>
export default {
name: 'child',
data() {
return {
}
},
methods: {
childClickEvent() {
//$emit方法第一个参数是自定义事件(clickEvent),后面的参数都是要传递的数据,可以不填或填写多个
this.$emit('clickEvent', '子组件向父组件传递的数据');
}
}
}
</script>
1.3 父子组件双向绑定
1.3.1 v-model
Vue 2.X 可以在自定义组件上使用 v-model
指令,实现父子组件之间的通信,该方式与上面介绍的方式类似,是一个语法糖。父组件通过 v-model
向子组件传递数据时,会自动传递一个 value
的 prop
属性,而子组件通过 this.$emit('input',val)
自动修改 v-model
绑定的值。
<!--parent.vue-->
<template>
<div>
<h2>我是父组件</h2>
<p>我是父组件的数据:{{pData}}</p>
<child v-model="pData"></child>
</div>
</template>
<script>
import child from './child'
export default {
name: 'parent',
components: {
child
},
data() {
return {
pData: '我是父组件的数据'
}
}
}
</script>
<!--child.vue-->
<template>
<div>
<h2>我是子组件</h2>
<p>我是子组件的数据: {{cData}}</p>
<p>我是父组件传递过来的数据: {{msg}}</p>
<button @click="handleClick">点击传递子组件数据给父组件</button>
</div>
</template>
<script>
export default {
name: 'child',
props: ['value'],//v-model 会自动传递一个字段为 value 的 props 属性
data() {
return {
msg: this.value,
cData: '我是子组件的数据'
}
},
methods: {
handleClick() {
this.$emit('input', this.cData);//通过emit特定的input事件可以改变父组件上v-model绑定的值
}
}
}
</script>
1.3.2 .sync
某些情况下,我们需要实现某个 prop
的双向绑定,而真正的双向绑定会带来维护上的问题,因为子组件可以修改父组件,且在父子组件中都没有明显的改动来源,所以官方推荐以 update:my-prop-name
的模式触发事件实现 “上行绑定“” 最终实现 “双向绑定“。而 .sync
是一个编译时的语法糖,它会被自动扩展为一个自动更新父组件属性的 v-on
监听器。如:<child :abc.sync=”msg”></child>
就会被扩展为: <child :abc=”data” @update:abc=”val => data= val”>
(@
是 v-on
的简写)。当子组件需要更新 abc
的值的时候,他需要显示的触发一个更新事件:this.$emit( “update:abc”, newValue )
。当使用一个对象一次性设置多个属性的时候,这个 .sync
修饰符也可以和 v-bind
一起使用。如:<child v-bind.sync = “{ a: data1, b: data2}”></child>
(不能写成 :.sync=...
,否则会报错),这样会为 a
和 b
同时添加用于更新的 v-on
监听器。
<!--parent.vue-->
<template>
<div>
<h2>我是父组件</h2>
<p>我是父组件的数据(单属性):{{pData}}</p>
<p>我是父组件的数据(多属性):{{myProps.a1}},{{myProps.a2}}</p>
<!--1.单个属性传递-->
<child :abc.sync="pData" :ifShow="true"></child>
<!-- <child :abc="pData" @update:abc="val=>pData= val"></child> 上面会自动扩展为该形式-->
<!--2.多个属性传递-->
<child v-bind.sync="myProps" :ifShow="false"></child> <!-- 不能写成字面量形式如 v-bind.sync="{ a1: '我是父组件的pData1', a2: '我是父组件的pData2'}"-->
</div>
</template>
<script>
import child from './child'
export default {
name: 'parent',
components: {
child
},
data() {
return {
pData: 'Hi!我是父组件!',
myProps: { a1: '我是父组件的pData1', a2: '我是父组件的pData2' }
}
}
}
</script>
<!--child.vue-->
<template>
<div>
<h2>我是子组件</h2>
<p v-if="ifShow">我是子组件接收到的父组件单个属性:{{abc}}</p>
<p v-else>我是子组件接收到的父组件的多个属性:{{a1}},{{a2}}</p>
<button @click="handleClick">点击传递子组件数据给父组件</button>
</div>
</template>
<script>
export default {
name: 'child',
props: ['ifShow', 'abc', 'a1', 'a2'],
data() {
return {
cData: 'Hi!我是子组件!'
}
},
methods: {
handleClick() {
this.$emit("update:abc", this.cData);
this.$emit("update:a1", this.cData);
this.$emit("update:a2", this.cData);
}
}
}
</script>
如果你只是单纯的在子组件中修改父组件的某个数据,建议使用.sync
。
注意:将 v-bind.sync 用在一个字面量的对象上,例如v-bind.sync=”{ title: doc.title
}”,是无法正常工作的,因为在解析一个像这样的复杂表达式的时候,有很多边缘情况需要考虑。
v-model
和 .sync
都可以实现 props
的双向绑定,但是 v-model
有局限性,只能传递 value
属性,而 .sync
可以传递其他的属性值。
2. $parent / $children
2.1 $parent / $children
子组件使用 $parent
可以直接访问父组件的实例(对象),而父组件通过 $children
可以访问所有的子组件实例(数组),并且可以递归向上或向下无限访问,直到根实例或最内层组件。虽然 Vue 允许这样操作,但在实际处理中,不建议这么做,因为这样会使父子组件紧耦合,而且会使父组件的状态因为可能被任意组件修改而难以理解。
<!--parent.vue-->
<template>
<div>
<child></child>
<button @click="parentClickEvent">点击修改子组件数据</button>
</div>
</template>
<script>
import child from "./child"
export default {
name: 'parent',
components: {
child
},
data() {
return {
parentData: '父组件的数据'
}
},
methods: {
parentClickEvent() {
this.$children[0].childData = '这是父组件修改的子组件数据';
}
}
}
</script>
<!--child.vue-->
<template>
<div>
<p>子组件数据:{{childData}}</p>
<p>子组件获取的父组件数据:{{parentData}}</p>
</div>
</template>
<script>
export default {
name: 'child',
data() {
return {
childData: '子组件的数据'
}
},
computed: {
parentData() {
return this.$parent.parentData;
}
}
}
</script>
2.2 $dispatch / $broadcast
这也是一组成对出现的方法,在 Vue2.0 中被废弃了,但是还是有很多开源软件自己封装了这种组件通信方式,如 Mint UI、Element UI 和 iView 等,可以解决父子组件、嵌套父子组件的通信。核心是向上寻找 $parent
和遍历 $children
,使用 $on
和 $emit
进行事件的监听和调用。通过 $dispatch
和 $broadcast
定向的向某个父或者子组件远程调用事件,这样就避免了通过传 props
或者使用 refs
调用组件实例方法的操作。
//main.js
import Vue from 'vue'
import parent from './parent'
//在Vue的原型上添加$dispatch方法,通过this.$dispatch调用
Vue.prototype.$dispatch = function (eventName, params) {
let parent = this.$parent;
while (parent) {
parent.$emit(eventName, params);
parent = parent.$parent;
}
};
//在Vue的原型上添加$broadcast方法,通过this.$broadcast调用
Vue.prototype.$broadcast = function (eventName, params) {
//获取当前组件下所有的子孙组件,递归调用
const boradcast = children => {
children.forEach(child => {
child.$emit(eventName, params);
if (child.$children) {
boradcast(child.$children);
}
});
}
boradcast(this.$children);
};
new Vue({
render: h => h(parent)
}).$mount("#app")
<!--parent.vue-->
<template>
<div>
<h2>我是parent.vue</h2>
<p>parent组件:{{pData}}</p>
<button @click="clickEvent">所有子孙组件(-100)</button>
<child></child>
</div>
</template>
<script>
import child from './child'
export default {
name: 'parent',
components: {
child
},
data() {
return {
pData: 100
}
},
methods: {
test(val) {
this.pData += val;
},
clickEvent() {
this.$broadcast('broadcastEvent', -100);//向所有子孙广播
}
},
mounted() {
this.$on('dispatchEvent', this.test);//用$on监听
}
}
</script>
<!--child.vue-->
<template>
<div>
<h2>我是child.vue</h2>
<p>child组件:{{cData}}</p>
<grandson></grandson>
</div>
</template>
<script>
import grandson from './grandson'
export default {
name: 'child',
components: {
grandson
},
data() {
return {
cData: 200
}
},
methods: {
test(val) {
this.cData += val;
}
},
mounted() {
this.$on('dispatchEvent', this.test);//用$on监听dispatchEvent
this.$on('broadcastEvent', this.test);//用$on监听broadcastEvent
},
}
</script>
<!--grandson.vue-->
<template>
<div>
<h2>我是grandson.vue</h2>
<p>grandson组件:{{gData}}</p>
<button @click="clickEvent">所有祖先组件(+100)</button>
</div>
</template>
<script>
export default {
name: 'grandson',
data() {
return {
gData: 100
}
},
methods: {
test(val) {
this.gData += val;
},
clickEvent() {
this.$dispatch('dispatchEvent', 100);//向所有的祖先派发
}
},
mounted() {
this.$on('broadcastEvent', this.test);//用$on监听broadcastEvent
}
}
</script>
3. $attrs / $listeners
对于隔代关系,如上图中父组件A和孙组件D之间想要传递数据,按照上面的方法,只能是在组件A,组件B,组件D这个链条中一级级使用 props
和$emit
向下和向上进行数据传递,如果中间有更多的层级,这种方式就更加复杂难以维护。为此,Vue2.4 版本提供了 $attrs
和 $listeners
来解决这种跨级通信的需求:
$attrs
:包含了父作用域中不作为 prop 被识别 (且获取) 的特性绑定 (class 和 style 除外)。当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 (class 和 style 除外),并且可以通过 v-bind="$attrs" 传入内部组件。通常配合inheritAttrs
选项一起使用。$listeners
:包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。它可以通过 v-on="$listeners" 传入内部组件。
<!--parent.vue-->
<template>
<div>
<child :a1="a1" :a2="a2" :a3="a3" :a4="a4" @click.native="clickEventNative" @click="clickEvent"
@pEvent1="parentEvent1" @pEvent2="parentEvent2">
</child>
</div>
</template>
<script>
import child from "./child"
export default {
name: 'parent',
components: {
child
},
data() {
return {
a1: "我是parent属性1的数据",
a2: "我是parent属性2的数据",
a3: "我是parent属性3的数据",
a4: "我是parent属性4的数据",
}
},
methods: {
clickEventNative() {
console.log('我是parent的native事件');
},
clickEvent() {
console.log('我是parent事件0');
},
parentEvent1() {
console.log('我是parent事件1');
},
parentEvent2() {
console.log('我是parent事件2');
}
}
}
</script>
<!--child.vue-->
<template>
<div>
<p>child中接收到的a1: {{a1}}</p>
<p>child中接收到的$attrs: {{$attrs}}</p>
<grandson v-bind="$attrs" v-on="$listeners"></grandson>
</div>
</template>
<script>
import grandson from './grandson'
export default {
name: 'child',
components: {
grandson
},
props: ['a1'],
mounted() {
console.log(this.$listeners);//{click: ƒ, pEvent1: ƒ, pEvent2: ƒ}
this.$emit('pEvent1');//parent方法调用方式1
},
}
</script>
<!--grandson.vue-->
<template>
<div>
<p>grandson中接收到的a2: {{a2}}</p>
<p>grandson中接收到的a3: {{a3}}</p>
<p>grandson中接收到的$attrs: {{$attrs}}</p>
</div>
</template>
<script>
import grandSon from './grandson'
export default {
name: 'child',
inheritAttrs: false, // 可以关闭自动挂载到组件根元素上的没有在props声明的属性
props: ['a2', 'a3'],
mounted() {
this.$listeners.pEvent2();//parent方法调用方式2
},
}
</script>
运行结果:
例子中,给 grandson.vue
加上 inheritAttrs:false
属性前后如图所示:
4. provide / inject
4.1 provide / inject 使用方法
这对选项需要一起使用,以允许一个祖先组件向其所有子孙后代注入一个依赖,不论组件层次有多深,并在起上下游关系成立的时间里始终生效。即,在父组件中通过 provider
来提供属性,然后在子组件中通过 inject
来注入变量。不论子组件有多深,只要调用了 inject
那么就可以注入在 provider
中提供的数据,只要在父组件的生命周期内,子组件都可以调用。
<!--parent.vue-->
<template>
<div>
<child></child>
</div>
</template>
<script>
import child from "./child"
export default {
name: 'parent',
components: {
child
},
provide: {//provide选项可以是一个对象或返回一个对象的函数
parentData: '我是父组件的数据'
},
}
</script>
<!--child.vue-->
<template>
<div>{{parentData}}</div>
</template>
<script>
export default {
name: 'child',
inject: ['parentData']//injec选项可以是一个字符串数组或一个对象
}
</script>
4.2 实现 provide / inject 数据响应式
provide 和 inject 绑定并不是可响应的,即修改了上例中 parent.vue
中的parentData,child.vue
中的 parentData 是不会改变的。要实现数据响应式,有两种方法:
- provide 祖先组件的实例,在后代组件中注入依赖。这样就可以在后代组件中直接修改祖先组件实例的属性。
- 使用
Vue. observable
优化响应式 provide(2.6新增API,推荐)
<!--parent.vue-->
<template>
<div>
<p>{{pData}}</p>
<child></child>
<button @click=changeData>改变parentData</button>
</div>
</template>
<script>
import Vue from 'vue'
import child from "./child"
export default {
name: 'parent',
components: {
child
},
data() {
return {
pData: '我是父组件数据'
}
},
//初始用法
// provide() {
// return {
// parentData: this.pData,//该方式绑定的数据不是响应式的,即祖先组件中parentData变化,后代组件中不会跟着变
// }
// },
// methods: {
// changeData() {
// this.pData = "我是改变以后的父组件数据";
// }
// }
//方法一
// provide() {
// return {
// parentData: this,//provide祖先组件的实例
// }
// },
// methods: {
// changeData() {
// this.pData = "我是改变以后的父组件数据1";
// }
// }
//方法二
provide() {
this.parentData = Vue.observable({
pData: this.pData
});
return {
parentData: this.parentData
}
},
methods: {
changeData() {
this.parentData.pData = "我是改变以后的父组件数据2";
}
}
}
</script>
<!--child.vue-->
<template>
<div>{{parentData.pData}}</div>
</template>
<script>
export default {
name: 'child',
inject: {
parentData: {
default: () => { }
}
}
}
</script>
另外, provide
和 inject
主要为高阶插件/组件库提供用例,不推荐直接用于应用程序代码,可视情况采用。
5. ref
ref
如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例,可以通过实例直接调用组件的方法或访问数据。
<!--parent.vue-->
<template>
<div>
<p>我是父组件获取的子组件的数据:{{pData}}</p>
<child ref='compChild'></child>
</div>
</template>
<script>
import child from './child'
export default {
name: 'parent',
components: {
child
},
data() {
return {
pData: ''
}
},
methods: {
parentEvent() {
let compChild = this.$refs.compChild;//通过this.$refs获取子组件实例
this.pData = compChild.childData;//获取子组件数据
compChild.childEvent();//调用子组件方法
}
},
mounted() {
this.parentEvent();
},
}
</script>
<!--child.vue-->
<template>
<div></div>
</template>
<script>
export default {
name: 'child',
data() {
return {
childData: '我是child的数据'
}
},
methods: {
childEvent() {
console.log('我是child的方法');
}
}
}
</script>
6. EventBus
中央事件总线(EventBus)可以巧妙而轻量地实现任何组件间的通信,包括父子、兄弟、跨级。如深入使用,可以扩展 bus 实例,给它添加 data、methods、computed 等选项,进行公用,业务中,一些需要共享的通用信息如用户登录信息,授权token等,只需在初始化时让 bus 获取一次,任何时间、组件就可以直接使用,在协同开发及单页应用(SPA)中特别实用。但是,当项目较大时,这种方式不太容易维护,可以选择后面要说的状态管理解决方案 Vuex。
//eventBus.js
import Vue from 'vue'
export const bus = new Vue();
<!--compA.vue-->
<template>
<div>
<comp-b></comp-b>
<comp-c></comp-c>
</div>
</template>
<script>
import compB from './compB'
import compC from './compC'
export default {
name: 'compA',
components: {
compB, compC
}
}
</script>
<!--compB.vue-->
<template>
<div>
<p>compB:{{dataB}}</p>
<button @click='handleEventB'>点击emit组件compB的数据</button>
</div>
</template>
<script>
import { bus } from './eventBus'
export default {
name: 'compB',
data() {
return {
dataB: '我是组件compB中的数据'
}
},
methods: {
handleEventB() {
bus.$emit('on-msg', this.dataB);//发送事件
}
}
}
</script>
<!--compC.vue-->
<template>
<div>
<p>{{dataC}}</p>
</div>
</template>
<script>
import { bus } from './eventBus'
export default {
name: 'compC',
data() {
return {
dataC: '我是组件compC中的数据'
}
},
methods: {
handleEventC() {
//接收事件
bus.$on('on-msg', val => {
this.dataC = val;
})
}
},
mounted() {
this.handleEventC();
},
beforeDestroy() {
bus.$off('on-msg', {})//移除事件监听
},
}
</script>
7. Vuex
Vuex
是一个专为 Vue 服务,用于管理页面数据状态、提供统一数据操作的生态系统。它集中于 MVC 模式中的 Model 层,规定所有的数据操作必须通过 action - mutation - state change
的流程来进行,再结合 Vue 的数据视图双向绑定特性来实现页面的展示更新。
Vuex 各模块的主要功能:
- Vue Components: Vue组件。HTML页面上,负责接收用户操作等交互行为,执行
dispatch
方法触发对应action
进行回应。 - dispatch: 操作行为触发方法,是唯一能执行
action
的方法。 - actions: 操作行为处理模块。负责处理
Vue Components
接收到的所有交互行为。包含同步/异步操作,支持多个同名方法,按照注册的顺序依次触发。向后台API请求的操作就在这个模块中进行,包括触发其他action
以及提交mutation
的操作。该模块提供了 Promise 的封装,以支持 action 的链式触发。 - commit: 状态改变提交操作方法。对
mutation
进行提交,是唯一能执行mutation 的方法。 - mutations: 状态改变操作方法。是 Vuex 修改
state
的唯一推荐方法,其他修改方式在严格模式下将会报错。该方法只能进行同步操作,且方法名只能全局唯一。操作之中会有一些hook
暴露出来,以进行state
的监控等。 - state: 页面状态管理容器对象。集中存储
Vue components
中 data 对象的零散数据,全局唯一,以进行统一的状态管理。页面显示所需的数据从该对象中进行读取,利用 Vue 的细粒度数据响应机制来进行高效的状态更新。 - getters:
state
对象读取方法。图中没有单独列出该模块,应该被包含在了render
中,Vue Components
通过该方法读取全局state对象。
下面看个实例:
//main.js 入口文件
import Vue from 'vue'
import compA from './compA'
import store from './store'; //使用store
Vue.config.productionTip = false;
new Vue({
store, //关联store
render: h => h(compA)
}).$mount("#app")
//store.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const state = {
dataB: '',
dataC: ''
}
const mutations = {
setDataB(state, data) {
// 将compA组件的数据存放于state
state.dataB = data
},
setDataC(state, data) {
// 将compB组件的数据存放于state
state.dataC = data
}
}
export default new Vuex.Store({
state,
mutations
})
<!--compA.vue-->
<template>
<div>
<comp-b></comp-b>
<comp-C></comp-c>
</div>
</template>
<script>
import compB from './compB'
import compC from './compC'
export default {
name: 'compA',
components: {
compB, compC
}
}
</script>
<!--compB.vue-->
<template>
<div>
<h2>我是compB组件</h2>
<p>compB组件获取到的数据:{{showDataB}}</p>
<button @click="handleEventB">点击将compB的数据传给compC</button>
</div>
</template>
<script>
export default {
name: 'compB',
data() {
return {
dataB: '我是compB的数据'
}
},
computed: {
showDataB() {
return this.$store.state.dataB//获取数据dataB
}
},
methods: {
handleEventB() {
this.$store.commit('setDataC', this.dataB);//修改数据dataC
}
}
}
</script>
<!--compC.vue-->
<template>
<div>
<h2>我是compC组件</h2>
<p>compB组件获取到的数据:{{showDataC}}</p>
<button @click="handleEventC">点击将compC的数据传给compB</button>
</div>
</template>
<script>
export default {
name: 'compC',
data() {
return {
dataC: '我是compC的数据'
}
},
computed: {
showDataC() {
return this.$store.state.dataC//获取数据dataC
}
},
methods: {
handleEventC() {
this.$store.commit('setDataB', this.dataC);//修改数据dataB
}
}
}
</script>
效果如下:
Vuex 存储的数据是响应式的,但并不会保存,刷新之后会回到初始状态,要解决这个问题,可以结合下面要说的 localStorage
来实现,当 Vuex 中数据变化时,将数据存储到 localStorage
中,刷新之后,如果 localStorage
中有数据,取出来替换 store
中的 state
。
8. localStorage / sessionStorage
HTML5 的本地存储 API 中的 localStorage
与 sessionStorage
在使用方法上是相同的,区别在于 sessionStorage
在关闭页面后即被清空,而 localStorage
则会一直保存。存储的内容是以 Json 的形式存储的,JSON.parse()
用于将一个 JSON 字符串转换为对象,JSON.stringify()
可以将对象转换为字符串。
- sessionSorage: 用于临时保存同一窗口(或标签页)的数据,在关闭窗口或标签页之后将会删除这些数据。
- localSorage: 用于长久保存整个网站的数据,保存的数据没有过期时间,直到手动去除。
保存数据到本地:
sessionStorage.setItem('key', JSON.stringify(value));
localStorage.setItem('key', JSON.stringify(value));
取得本地的数据:
let data1 = JSON.parse(sessionStorage.getItem('key'));
let data2 = JSON.parse(localStorage.getItem('key'));
清空全部数据:
sessionStorage.clear()
localStorage.clear()
删除单个数据:
localStorage.removeItem(key);
sessionStorage.removeItem(key);
得到某个索引的key:
localStorage.key(index);
sessionStorage.key(index);
三、总结
综上所述,Vue 组件通信的方式大概有八大类:
- props / $emit
- $children / $parent
- $attrs / $listeners
- provide / inject
- ref
- EventBus
- Vuex
- localStorage / sessionStorage
按组件间的关系对应合适的使用场景可大致归纳如下:
- 父子组件通信:
props / $emit
,$parent / $children
,$attrs / $listeners
,provide / inject
,ref
,EventBus
,Vuex
,localStorage/sessionStorage
- 兄弟组件通信:
EventBus
,Vuex
,localStorage/sessionStorage
- 跨级组件通信:
$attrs / $listeners
,provide / inject
,EventBus
,Vuex
,localStorage/sessionStorage
四、其他
本文所示的例子都已上传至 github,都采用快速原型开发,如果需要可参考以下步骤:
- 使用如下命令安装 vue-cli3
npm install @vue/cli -g
或者
yarn global add @vue/cli
- 使用如下命令安装一个额外的全局插件,这样就可以使用 vue serve 和 vue build 命令独立运行单个 * .vue 文件:
npm install -g @vue/cli-service-global
或者
yarn global add @vue/cli-service-global
- 新建 *.vue 文件
- 在 *.vue 文件所在目录下运行如下命令:
# App.vue
vue serve
# 指定入口文件
vue serve component.vue