Vue两大核心: 1.数据驱动界面改变 2.组件化
自定义全局组件
-
1.什么是组件? 什么是组件化?
- 1.1在前端开发中组件就是把一个很大的界面拆分为多个小的界面, 每一个小的界面就是一个组件
- 1.2将大界面拆分成小界面就是组件化
-
2.组件化的好处
- 2.1可以简化Vue实例的代码
- 2.2可以提高复用性
-
3.Vue中如何创建组件?
- 3.1创建组件构造器
- 3.2注册已经创建好的组件
- Vue.component(参数1, 参数2);
- 第一个参数:指定注册的组件的名称
- 第二个参数:传入已经创建好的组件构造器
- Vue.component(参数1, 参数2);
- 3.3使用注册好的组件
- 示例:
<div id="app">
<!--3.3使用注册好的组件-->
<abc></abc>
</div>
<script>
// 3.1创建组件构造器
let Profile = Vue.extend({
template: `
<div>
<img src="images/fm.jpg"/>
<p>我是描述信息</p>
</div>
`
});
// 3.2注册已经创建好的组件
Vue.component("abc", Profile );
</script>
- 注意点:
- ==在创建组件指定组件的模板的时候,模板只能有一个根元素==
创建组件的其它方式
- 1.在注册组件的时候,除了传入一个组件构造器以外, 还可以直接传入一个对象(参数2的位置会自动执行Vue.extend())
- 第一种,对象单独定义,Vue.component(组件名称,对象名称)
- 第二种,把对象直接放入参数2的位置
- 示例:
// 第一种
<div id="app">
<abc></abc>
</div>
<script>
let obj = {
template: `
<div>
<img src="images/fm.jpg" alt="">
<p>我是描述信息</p>
</div>`
};
Vue.component('abc',obj);
</script>
// 第二种
Vue.component('abc', {template: `
<div>
<img src="images/fm.jpg" alt="">
<p>我是描述信息</p>
</div>`});
- 2.在编写组件模板的时候,除了可以在字符串模板中编写以外, 还可以像过去的art-template一样在script中编写(不建议)
- 示例:
<div id="app">
<abc></abc>
</div>
<script id="info" type="text/html">
<div>
<img src="images/fm.jpg" alt="">
<p>我是描述信息</p>
</div>
</script>
<script>
Vue.component('abc',{
template: '#info'
});
</script>
- 3.在编写组件模板的时候,除了可以在script中编写以外, vue还专门提供了一个自定义组件模板的标签template(简化,方便,==推荐==)
- 示例:
<div id="app">
<abc></abc>
</div>
<template id="info">
<div>
<img src="images/fm.jpg" alt="">
<p>我是描述信息</p>
</div>
</template>
<script>
Vue.component('abc',{
template: '#info'
});
</script>
自定义局部组件
-
1.自定义全局组件特点
- 在任何一个Vue实例控制的区域中都可以使用
-
2.自定义局部组件特点
- 只能在自定义的那个Vue实例控制的区域中可以使用
-
3.如何自定义一个局部组件
- 1.在vue实例中新增components: {}
- 2.在{}中通过key/vue形式注册组件
components:{ abc: {} }
- 示例:
<div id="app1">
<abc></abc>
</div>
<div id="app2">
<abc></abc>
</div>
<template id="info">
<div>
<img src="images/fm.jpg"/>
<p>我是描述信息</p>
</div>
</template>
<script>
let vue1 = new Vue({
el: '#app1' // vue1不输出
});
let vue2 = new Vue({
el: '#app2',
// 专门用于定义局部组件的
components: {
"abc": {
template: "#info" // vue2输出
}
}
});
</script>
自定义组件中的data和methods
-
1.自定义组件中的data和methods
- Vue实例控制的区域相当于一个大的组件,在大组件中我们可以使用data和methods
- 而我们自定义的组件也是一个组件,所以在自定义的组件中也能使用data和methods
-
2.自定义组件中data注意点
- methods使用方法一样
- data不一样,自定义组件中的data要求必须是一个函数
- 所以必须通过返回函数的方式来使用data
- 示例:
<div id="app">
<abc></abc>
</div>
<template id="info">
<div>
<img src="images/fm.jpg"/>
<button @click="abcFn">我是按钮</button>
<p>{{abcMsg}}</p>
</div>
</template>
<script>
Vue.component("abc", {
template: "#info",
methods: {
abcFn(){
alert("abcFn");
}
},
data: function () {
return {
abcMsg: "学院"
}
}
});
</script>
- 3.组件中的data为什么是一个函数?
- 因为自定义组件可以复用,为了保证复用时每个组件的数据都是独立的, 所以必须是一个函数
- 组件中的data如果不是通过函数返回的,那么多个组件就会公用一份数据,就会导致数据混乱;如果组件中的data是通过函数返回的,那么每创建一个新的组件,都会调用一次这个方法,将这个方法返回的数据和当前创建的组件绑定在一起,这样就有效的避免了数据混乱
- 因为自定义组件可以复用,为了保证复用时每个组件的数据都是独立的, 所以必须是一个函数
<div id="app">
<abc></abc>
<abc></abc>
<abc></abc>
</div>
<template id="info">
<div>
<button @click="add">累加</button>
<p>{{number}}</p>
</div>
</template>
<script>
// 自定义全局组件
Vue.component("abc", {
template: "#info",
data: function () {
return {
number: 0
}
},
methods: {
add(){
this.number++;
}
}
});
</script>
组件切换
- 组件切换
- 对于普通的元素我们可以通过v-if来实现切换,对于组件我们也可以通过v-if来实现切换,因为组件的本质就是一个自定义元素
<div id="app">
<button @click="toggle">切换</button>
<home v-if="isShow"></home>
<photo v-else></photo>
</div>
<template id="home">
<div>
<p>我是首页</p>
</div>
</template>
<template id="photo">
<div>
<img src="images/fm.jpg">
</div>
</template>
<script>
// 自定义全局组件
Vue.component("home", {
template: "#home",
});
Vue.component("photo", {
template: "#photo",
});
let vue = new Vue({
el: '#app',
data: {
isShow: true
},
methods: {
toggle(){
this.isShow = !this.isShow;
}
}
});
</script>
动态组件
-
1.什么是动态组件?
- 通过v-if/v-else-if/v-else确实能够切换组件,但是在Vue中切换组件还有一种更专业的方式
<component v-bind:is="需要显示组件名称"></component>
- component我们称之为动态组件,也就是你让我显示谁我就显示谁
- 通过v-if/v-else-if/v-else确实能够切换组件,但是在Vue中切换组件还有一种更专业的方式
-
2.为什么可以通过v-if切换还要有component
- 因为component可以配合keep-alive来保存被隐藏组件隐藏之前的状态,不写keep-alive的话默认是不保存之前状态的
- 示例:
<div id="app">
<button @click="toggle">按钮</button>
<keep-alive>
<compotent v-bind:is="name"></compotent>
</keep-alive>
</div>
<template id="home">
<div>
<p>微双</p>
<input type="checkbox">
</div>
</template>
<template id="hidden" >
<div>
<img src="images/fm.jpg" >
</div>
</template>
<script>
Vue.component("home", {
template: "#home"
});
Vue.component("hidden", {
template: "#hidden"
});
let vue = new Vue({
el: '#app',
data: {
isShow: true,
name: 'home'
},
methods:{
toggle(){
this.isShow = !this.isShow;
this.name = this.name === 'home' ? 'hidden' : 'home';
}
}
});
</script>
组件动画
- 1.如何给组件添加动画?
- 给组件添加动画和过去给元素添加动画一样,如果是单个组件就使用transition,如果是多个组件就使用transition-group
- 示例:
<style>
.v-enter{
opacity: 0;
margin-left: 500px;
}
.v-enter-to{
opacity: 1;
}
.v-enter-active{
transition: all 3s;
}
.v-leave{
opacity: 1;
}
.v-leave-to{
opacity: 0;
}
.v-leave-active{
transition: all 3s;
margin-left: 500px;
}
</style>
<div id="app">
<button @click="toggle">按钮</button>
<transition>
<compotent v-bind:is="name"></compotent>
</transition>
</div>
<template id="home">
<div>
<p>微双</p>
<input type="checkbox">
</div>
</template>
<template id="hidden" >
<div>
<img src="images/fm.jpg" >
</div>
</template>
<script>
Vue.component("home", {
template: "#home"
});
Vue.component("hidden", {
template: "#hidden"
});
let vue = new Vue({
el: '#app',
data: {
isShow: true,
name: 'home'
},
methods:{
toggle(){
this.isShow = !this.isShow;
this.name = this.name === 'home' ? 'hidden' : 'home';
}
}
});
</script>
- 2.过渡动画注意点
- 默认情况下进入动画和离开动画是同时执行的,如果想一个做完之后再做另一个,需要指定动画模式
- 示例中在transition中添加
mode="out-in"
- 示例中在transition中添加
- 默认情况下进入动画和离开动画是同时执行的,如果想一个做完之后再做另一个,需要指定动画模式
<transition mode="out-in>
<compotent v-bind:is="name"></compotent>
</transition>
父子组件
-
1.什么是父子组件?
- 在一个组件中又定义了其它组件就是父子组件
- 其实局部组件就是最简单的父子组件,因为我们说过可以把Vue实例看做是一个大组件。我们在Vue实例中定义了局部组件,就相当于在大组件里面定义了小组件,所以实局部组件就是最简单的父子组件
-
2.如何定义其它的父子组件
- 前面我们说过, 自定义组件中可以使用data, 可以使用methods.当然自定义组件中也可以使用components,所以我们也可以在自定义组件中再定义其它组件
- 示例:
<div id="app">
<father></father>
</div>
<template id="father">
<div>
<p>我是父组件</p>
<son></son>
</div>
</template>
<template id="son" >
<div>
<p>我是子组件</p>
</div>
</template>
<script>
let vue = new Vue({
el: '#app',
data: {
},
methods:{
components: {
'father': {
template : '#father',
components: {
'son' : {
template: '#son'
}
}
}
}
});
</script>
父子组件数据传递
-
1.父子组件数据传递?
- 在Vue中子组件是不能访问父组件的数据的(就是子组件不继承父组件的数据),如果子组件想要访问父组件的数据,必须通过父组件传递
-
2.如何传递数据
- 2.1在父组件中通过v-bind传递数据
- 传递格式
v-bind:自定义接收名称 = "要传递数据"
- 传递格式
- 2.2在子组件中通过props接收数据
- 接收格式
props: ["自定义接收名称"]
- 接收格式
- 示例:
- 2.1在父组件中通过v-bind传递数据
<div id="app">
<father></father>
</div>
<template id="father">
<div>
<p>{{name}}</p>
<p>{{age}}</p>
<!--这里将父组件的name通过parentname传递给了子组件-->
<son :def="name" :abc="age"></son>
</div>
</template>
<template id="son" >
<div>
<!--这里通过设置props使用了父组件传递过来的数据-->
<p>{{def}}</p>
<p>{{abc}}</p>
</div>
</template>
<script>
let vue = new Vue({
el: '#app',
components: {
'father': {
template : '#father',
data: function () {
return {
name: 'ws',
age: 33
}
},
components: {
'son' : {
template: '#son',
props: ['def','abc']
}
}
}
}
});
</script>
备注:上面示例的模板可以换成全局组件,根据需求
- ==注意点==
- 组件是可以使用自己的数据的
- 子组件要用传递过来自定义的名称,用父组件原名称是无效的
父子组件传递方法
- 和传递数据不同,如果传递的是方法,那么在子组件中不需要接收,需要进行如下操作:
- 1.需要在子组件中自定义一个方法
- 2.在子组件中直接使用自定义的方法
- 3.在子组件自定义的方法中通过
this.$emit("自定义接收的名称")
的方法来触发父组件传递过来的方法 - 示例:
<div id="app">
<father></father>
</div>
<template id="father">
<div>
<button @click="say">我是父按钮</button>
<son @parentsay="say"></son>
</div>
</template>
<template id="son">
<div>
<button @click="sonFn">我是子按钮</button>
</div>
</template>
<script>
// 父组件
Vue.component("father", {
template: "#father",
methods: {
say(){
alert("www.it666.com");
}
},
// 子组件
components: {
"son": {
template: "#son",
methods: {
sonFn(){
this.$emit("parentsay");
}
}
}
}
});
let vue = new Vue({
el: '#app'
});
</script>
子组件传递数据给父组件
- 如何将子组件数据传递给父组件
- 既然我们可以将父组件的方法传递给子组件,既然我们可以在子组件中调用父组件中的方法,那么我们就可以在调用方法的时候给方法传递参数
- 传递的参数, 就是我们需要传递的数据
- 格式
this.$emit(参数1, 参数2);
- 第一个参数: 需要调用的函数名称
- 第二个参数:给调用的函数传递的参数
- 示例:
<div id="app">
<father></father>
</div>
<template id="father">
<div>
<button @click="say">我是父按钮</button>
<!--这里通过parentsay将父组件的say方法传递给了子组件-->
<son @parentsay="say"></son>
</div>
</template>
<template id="son">
<div>
<button @click="sonFn">我是子按钮</button>
</div>
</template>
<script>
// 父组件
Vue.component("father", {
template: "#father",
methods: {
say(data){
// alert("www.it666.com");
console.log(data);
}
},
// 子组件
components: {
"son": {
template: "#son",
methods: {
sonFn(){
this.$emit("parentsay", "知播渔");
}
}
}
}
});
let vue = new Vue({
el: '#app'
});
</script>
组件中的命名注意点
- 1.组件中的命名注意点
- 1.1注册组件的时候使用了"驼峰命名", 那么在使用时需要转换成"短横线分隔命名"
- 例如: 注册时:
myFather
-> 使用时需改为:my-father
- 例如: 注册时:
- 1.2在传递参数的时候如果想使用"驼峰名称", 那么就必须写"短横线分隔命名"
- 例如: 传递时:
parent-name="name"
-> 接收时需改成:props: ["parentName"]
- 例如: 传递时:
- 1.3在传递方法的时候不能使用"驼峰命名", 只能用"短横线分隔命名"
@parent-say="say" -> this.$emit("parent-say");
- 1.1注册组件的时候使用了"驼峰命名", 那么在使用时需要转换成"短横线分隔命名"
==总结:命名别用驼峰命名法,正常小写命名就好,省事==
数据和方法的多级传递
- 数据和方法的多级传递
- 在Vue中如果儿子想使用爷爷的数据, 必须一层一层往下传递
- 在Vue中如果儿子想使用爷爷的方法, 必须一层一层往下传递
- 示例:
<div id="app">
<grandfather></grandfather>
</div>
<template id="grandfather">
<div>
<p>{{name}}</p>
<button @click="say">我是爷爷按钮</button>
<father :gfname="name" @gfsay="say"></father>
</div>
</template>
<template id="father">
<div>
<p>{{gfname}}</p>
<button @click="fatherFn">我是父按钮</button>
<son :fname="gfname" @fsay="fatherFn"></son>
</div>
</template>
<template id="son">
<div>
<p>{{fname}}</p>
<button @click="sonFn">我是子按钮</button>
</div>
</template>
<script>
// 爷爷组件
Vue.component("grandfather", {
template: "#grandfather",
data: function(){
return {
name: "lnj"
}
},
methods: {
say(){
console.log("我是爷爷的方法");
}
},
// 爸爸组件
components: {
"father": {
template: "#father",
props: ["gfname"],
methods:{
fatherFn(){
this.$emit("gfsay");
}
},
// 儿子组件
components: {
"son": {
template: "#son",
props: ["fname"],
methods: {
sonFn(){
this.$emit("fsay");
}
},
}
}
}
}
});
// 这里就是MVVM中的View Model
let vue = new Vue({
el: '#app'
});
</script>
插槽
- 什么是插槽?
- 默认情况下使用子组件时在子组件中编写的元素是不会被渲染的
- 如果子组件中有部分内容是使用时才确定的, 那么我们就可以使用插槽,也就是说想在使用子组件的时候,给子组件动态的添加内容, 那么就必须使用插槽
- 插槽就是在子组件中放一个"坑"
<slot></slot>
,以后由父组件来"填" - 示例:
<!--需求: 在使用子组件的时候给子组件动态的添加一些内容-->
<div id="app">
<father></father>
</div>
<template id="father">
<div>
<son>
<div>我是追加的内容1</div>
<div>我是追加的内容2</div>
<div>我是追加的内容3</div>
</son>
</div>
</template>
<template id="son">
<div>
<div>我是头部</div>
<!--这里的slot标签就是插槽, 插槽其实就是一个坑
只要有了这个坑, 那么以后使用者就可以根据自己的需要来填这个坑-->
<slot>我是默认数据</slot>
<div>我是底部</div>
</div>
</template>
<script>
// 父组件
Vue.component("father", {
template: "#father"
// 子组件
components: {
"son": {
template: "#son"
}
}
});
let vue = new Vue({
el: '#app'
});
</script>
- ==注意点==:
- 插槽可以指定默认数据,如果使用者没有填这个坑,那么就会显示默认数据。如果使用者填了这个坑,那么就会利用使用者填坑的内容替换整个插槽
- 插槽是可以指定名称的,默认情况下如果没有指定名称
- 有多少个匿名插槽,填充的数据就会被拷贝几份
匿名插槽
- 什么是匿名插槽
- 没有名字的插槽,会利用使用时指定的能容替换整个插槽(如上面的示例)
- ==注意点==:如果有多个匿名插槽,每一个匿名插槽都会被指定的内容替换。虽然写多个匿名插槽不会报错,但是==在企业开发中推荐只能有一个匿名插槽==
具名插槽
-
1.什么是具名插槽
- 默认情况下有多少个匿名插槽,我们填充的数据就会被拷贝多少份,这导致了所有插槽中填充的内容都是一样的,那么如果我们想给不同的插槽中填充不同的内容怎么办呢?
- 这个时候就可以使用具名插槽
-
2.具名插槽使用
- 通过插槽的name属性给插槽指定名称,在使用时可以通过
slot="name"
方式,指定当前内容用于替换哪一个插槽 - 示例:
- 通过插槽的name属性给插槽指定名称,在使用时可以通过
<div id="app">
<father></father>
</div>
<template id="father">
<div>
<son>
<!--这里通过slot属性告诉Vue,当前的内容是要填充到哪一个插槽中的-->
<div slot="one">我是追加的内容1</div>
<div slot="one">我是追加的内容11</div>
<div slot="two">我是追加的内容2</div>
<div slot="two">我是追加的内容22</div>
</son>
</div>
</template>
<template id="son">
<div>
<div>我是头部</div>
<slot name="one">我是默认内容</slot>
<slot name="two">我是默认内容</slot>
<div>我是底部</div>
</div>
</template>
<script>
// 父组件
Vue.component("father", {
template: "#father",
// 子组件
components: {
"son": {
template: "#son",
}
}
});
// 这里就是MVVM中的View Model
let vue = new Vue({
el: '#app'
});
</script>
- 注意点:
- ==如果没有指定要替换哪个插槽中的内容, 则不会被替换==
v-slot指令
- 什么是v-slot指令?
- v-slot指令是Vue2.6中用于替代slot属性的一个指令
- 在Vue2.6之前,我们通过slot属性告诉Vue当前内容填充到哪一个具名插槽
- 从Vue2.6开始,我们通过v-slot指令告诉Vue当前内容填充到哪一个具名插槽
- 示例:
//只需把上节父组件在填充子组件的代码:
<div slot="one">我是追加的内容1</div>
<div slot="one">我是追加的内容11</div>
<div slot="two">我是追加的内容2</div>
<div slot="two">我是追加的内容22</div>
// 换成:
<template v-slot:one>
<div>我是追加的内容1</div>
<div>我是追加的内容11</div>
</template>
<template v-slot:two>
<div>我是追加的内容2</div>
<div>我是追加的内容22</div>
</template>
- ==注意点==:
- v-slot指令只能用在template标签上
- 格式
v-slot:插槽名称
- 可以使用#号替代v-slot:变成
#插槽名称
作用域插槽(不推荐使用)
-
1.什么是作用域插槽
- 作用域插槽就是带数据的插槽,就是让父组件在填充子组件插槽内容时也能使用子组件的数据
-
2.如何使用作用域插槽
- 2.1在slot中通过
v-bind:数据名称="数据名称"
方式暴露数据 - 2.2在父组件中通过
<template slot-scope="作用域名称">
接收数据 - 2.3在父组件的
<template></template>
中通过作用域名称.数据名称
方式使用数据 - 示例:
- 2.1在slot中通过
<div id="app">
<father></father>
</div>
<template id="father">
<div>
<son>
<!--
slot-scope="abc"作用: 接收子组件插槽暴露的数据
-->
<template slot-scope="abc">
<!-- <div>我是填充的内容 {{abc.names}}</div>-->
<li v-for="(name, index) in abc.names">{{name}}</li>
</template>
</son>
</div>
</template>
<template id="son">
<div>
<div>我是头部 {{names}}</div>
<!--
v-bind:names="names"作用: 将当前子组件的names数据暴露给父组件
-->
<slot v-bind:names="names">我是默认内容 {{names}}</slot>
<div>我是底部</div>
</div>
</template>
<script>
// 父组件
Vue.component("father", {
template: "#father",
// 子组件
components: {
"son": {
template: "#son",
data:function () {
return {
names: ["zs", "ls", "ww", "zl"]
}
}
}
}
});
let vue = new Vue({
el: '#app'
});
</script>
- 作用域插槽的应用场景:
- 子组件提供数据, 父组件决定如何渲染
通过v-slot也可以接收作用域暴露的数据
- 也就是说我们除了可以通过v-slot指令告诉Vue内容要填充到哪一个具名插槽中,==还可以通过v-slot指令告诉Vue如何接收作用域插槽暴露的数据(推荐用v-slot,它取代了 slot 和 slot-scope)==
- 格式
- 第一种
v-slot:插槽名称="作用域名称"
- 第二种
v-slot:default='作用域名称'
- 第三种
#default='作用域名称'
- 第一种
- 示例(上个示例):
- 格式
// 三种写法,上个示例中父组件和子组件部分改为如下即可
<template id="father">
<div>
<son>
<!--这种可以<template v-slot:default="abc">-->
<!--这种也可以<template #default="abc">-->
<template #one="abc">
<li v-for="(name, index) in abc.names">{{name}}</li>
</template>
</son>
</div>
</template>
<template id="son">
<div>
<div>我是头部 {{names}}</div>
<slot name="one" v-bind:names="names">我是默认内容 {{names}}</slot>
<div>我是底部</div>
</div>
</template>
数据传递练习(理解,不是最优版,可以用Vue优化)
- 如何实现儿子中的数据和父亲中的数据同步
- 1.父亲给儿子传递一个方法
- 2.在儿子中修改数据
- 3.儿子中修改完数据,调用父亲传递过来的方法, 并且将修改之后的数据传递给父亲的方法
- 4.在父亲的方法中保存最新的数据
<div id="app">
<father></father>
</div>
<template id="father">
<div>
<son1 @parentchange="change"></son1>
<son2 :parentnum="count"></son2>
</div>
</template>
<template id="son1">
<div>
<!--需求: 在第一个子组件中添加两个按钮和一个输入框, 要求通过按钮控制输入框中的数据+1和-1-->
<button @click="plusFn">加按钮</button>
<button @click="decFn">减按钮</button>
<input type="text" :value="num">
</div>
</template>
<template id="son2">
<div>
<!--需求: 在第二个子组件中展示第一个子组件中的数据-->
<p>{{parentnum}}</p>
</div>
</template>
<script>
Vue.component('father',{
template:'#father',
data:function(){
return {
count: '0'
}
},
methods:{
change(newNum){
this.count = newNum;
}
},
components:{
'son1':{
template:'#son1',
data: function(){
return {
num: '0'
}
},
methods:{
plusFn(){
this.num++;
this.$emit('parentchange',this.num);
},
decFn(){
this.num--;
this.$emit('parentchange',this.num);
}
}
},
'son2':{
template:'#son2',
props:['parentnum']
}
}
});
let vue = new Vue({
el:'#app'
});
</script>
-
以上写法不是最优版的原因:
- 两个问题:
- 1.如果想要在子组件中使用祖先组件中的数据,那么就必须一层一层的传递(非常麻烦)
- 2.兄弟组件之间不能直接传递数据,如果兄弟组件之间想要传递数据,那么就必须借助父组件(非常麻烦),这种方式非常的复杂, 非常的不推荐
- 两个问题:
解决方案: 使用Vuex
共享数据Vuex
- 1.什么是Vuex?
- vuex是Vue配套的公共数据管理工具,它可以把一些共享的数据,保存到vuex中,方便整个程序中的任何组件直接获取或修改我们的公共数据
- 示例:
<div id="app">
<grandfather></grandfather>
</div>
<template id="grandfather">
<div>
<p>{{this.$store.state.msg}}</p>
<father></father>
</div>
</template>
<template id="father">
<div>
<!--4.在使用Vuex中保存的共享数据的时候, 必须通过如下的格式来使用-->
<p>{{this.$store.state.msg}}</p>
<son></son>
</div>
</template>
<template id="son">
<div>
<p>{{this.$store.state.msg}}</p>
</div>
</template>
<script>
// 2.创建Vuex对象
const store = new Vuex.Store({
// 这里的state就相当于组件中的data, 就是专门用于保存共享数据的
state: {
msg: "微双"
},
});
// 爷爷组件
Vue.component("grandfather", {
template: "#grandfather",
// 3.在祖先组件中添加store的key保存Vuex对象
// 只要祖先组件中保存了Vuex对象 , 那么祖先组件和所有的后代组件就可以使用Vuex中保存的共享数据了
store: store,
// 爸爸组件
components: {
"father": {
template: "#father",
// 儿子组件
components: {
"son": {
template: "#son",
}
}
}
}
});
let vue = new Vue({
el: '#app'
});
</script>
- 注意点:
- ==必须在引入Vue之后再引入Vuex==
- ==只有需要共享的才放到vuex上,不需要共享的数据依然放到组件自身的data上==
数据传递练习(上面的优化版)
- 注意点:
- 在Vuex中不推荐直接修改共享数据
- 如果多个组件都修改了共享的数据,那么后期数据发生了错误,我们如果需要去调试错误,就需要把每一个修改了共享数据的组件都检查一遍,这样非常低效,非常的不利于我们去维护
- 解决方法:在Vuex中添加
mutations
- 示例:
<div id="app">
<father></father>
</div>
<template id="father">
<div>
<son1></son1>
<son2></son2>
</div>
</template>
<template id="son1">
<div>
<!--需求: 在第一个子组件中添加两个按钮和一个输入框, 要求通过按钮控制输入框中的数据+1和-1-->
<button @click="add">增加</button>
<button @click="sub">减少</button>
<input type="text" :value="this.$store.state.count">
</div>
</template>
<template id="son2">
<div>
<button @click="add">增加</button>
<button @click="sub">减少</button>
<input type="text" :value="this.$store.state.count">
</div>
</template>
<script>
const store = new Vuex.Store({
// state: 用于保存共享数据
state: {
count: 0
},
// mutations: 用于保存修改共享数据的方法
mutations: {
// 注意点: 在执行mutations中定义的方法的时候, 系统会自动给这些方法传递一个state参数
// state中就保存了共享的数据
mAdd(state){
state.count = state.count + 1;
},
mSub(state){
state.count = state.count - 1;
}
}
});
// 爸爸组件
Vue.component("father", {
template: "#father",
store: store,
// 儿子组件
components: {
"son1": {
template: "#son1",
methods: {
add(){
// 注意点: 在Vuex中不推荐直接修改共享数据
// this.$store.state.count = this.$store.state.count + 1;
this.$store.commit("mAdd");
},
sub(){
// this.$store.state.count = this.$store.state.count - 1;
this.$store.commit("mSub");
}
}
},
"son2": {
template: "#son2",
methods: {
add(){
// this.$store.state.count = this.$store.state.count + 1;
this.$store.commit("mAdd");
},
sub(){
// this.$store.state.count = this.$store.state.count - 1;
this.$store.commit("mSub");
}
}
}
}
});
let vue = new Vue({
el: '#app'
});
</script>
Vuex-getters
- 什么是Vuex的getters?
- Vuex的getters属性就和组件的计算属性一样, 会将数据缓存起来,只有数据发生变化才会重新计算
- 示例:
<div id="app">
<father></father>
</div>
<template id="father">
<div>
{{this.$store.getters.formart}}
{{this.$store.getters.formart}}
{{this.$store.getters.formart}}
</div>
</template>
<script>
const store = new Vuex.Store({
// state: 用于保存共享数据
state: {
msg: "微双"
},
// mutations: 用于保存修改共享数据的方法
mutations: {
},
getters: {
formart(state){
console.log("getters方法被执行了");
return state.msg + "www.it666.com"
}
}
});
// 爸爸组件
Vue.component("father", {
template: "#father",
store: store
});
let vue = new Vue({
el: '#app'
});
</script>