第一章:Vue基础知识笔记(模板语法、数据绑定、事件处理、计算属性)(一)
第二章:Vue基础知识(计算属性、监视属性、computed和watch之间的区别、绑定样式)(二)
第三章:Vue基础知识(条件渲染、列表渲染、收集表单数据、过滤器)(三)
第四章:Vue基础知识(内置指令、自定义指令、Vue生命周期)(四)
第五章:Vue基础知识之组件机制(非单文件组件、单文件组件)(五)
第六章:Vue创建脚手架(六)
第七章:Vue使用脚手架(ref、props、mixin、插件、scoped)(七)
为了节省空间,样式的代码我都不截取
一种组件间通信的方式,适用于:子组件 ===> 父组件
使用场景:A是父组件,B是子组件,B想给A传数据,那么就要在A中给B绑定自定义事件(事件的回调在A中
)。
绑定自定义事件:
第一种方式,在父组件中:
第二种方式,在父组件中:
<template>
<Demo ref="demo"/>
</template>
<script>
...
methods:{
test(){...}
},
mounted(){
this.$refs.demo.$on('custom',this.test)
}
</script>
若想让自定义事件只能触发一次
,可以使用once修饰符
,或$once方法。
触发自定义事件: 在子组件中调用this.$emit('custom',数据)
解绑自定义事件: this.$off('custom')
组件上
也可以绑定原生DOM事件
,需要使用nativ
e修饰符。
注意: 通过this.$refs.xxx.$on('atguigu',回调)
绑定自定义事件时,回调
要么配置在methods中
,要么用箭头函数
,否则this指向会出问题!
案例(代码片段):
App.vue
<template>
<div class="app">
<h1>{{ msg }},学生姓名是:{{ studentName }}</h1>
<!-- 通过父组件给子组件绑定一个自定义事件实现:子给父传递数据(第一种写法,使用@或v-on) -->
<!-- <Student @custom="getStudentName" @demo="m1" /> -->
<!-- 通过父组件给子组件绑定一个自定义事件实现:子给父传递数据(第二种写法,使用ref) -->
<Student ref="student" @click.native="show" />
</div>
</template>
<script>
import School from "./components/School";
export default {
name: "App",
components: { School },
data() {
return {
msg: "你好啊!",
studentName: "",
};
},
methods: {
getStudentName(name, ...params) {
console.log("App收到了学生名:", name, params);
this.studentName = name;
},
m1() {
console.log("demo事件被触发了!");
},
show() {
alert(123);
},
},
mounted() {
this.$refs.student.$on("custom", this.getStudentName); //绑定自定义事件
// this.$refs.student.$once('custom',this.getStudentName) //绑定自定义事件(一次性)
},
};
</script>
Student.vue
<template>
<div class="student">
<h2>学生姓名:{{ name }}</h2>
<h2>学生性别:{{ sex }}</h2>
<h2>当前求和为:{{ number }}</h2>
<button @click="add">点我number++</button>
<button @click="sendStudentlName">把学生名给App</button>
<button @click="unbind">解绑atguigu事件</button>
<button @click="death">销毁当前Student组件的实例(vc)</button>
</div>
</template>
<script>
export default {
name: "Student",
data() {
return {
name: "张三",
sex: "男",
number: 0,
};
},
methods: {
add() {
this.number++;
},
sendStudentlName() {
//触发Student组件实例身上的atguigu事件
this.$emit("custom", this.name, 666, 888, 900);
// this.$emit('demo')
// this.$emit('click')
},
unbind() {
this.$off("custom"); //解绑一个自定义事件
// this.$off(['custom','demo']) //解绑多个自定义事件
// this.$off() //解绑所有的自定义事件
},
death() {
this.$destroy(); //销毁了当前Student组件的实例,销毁后所有Student实例的自定义事件全都不奏效。
},
},
};
</script>
任意组件间通信
。 new Vue({
......
beforeCreate() {
Vue.prototype.$bus = this //安装全局事件总线,$bus就是当前应用的vm
},
......
})
使用事件总线:
回调留在A组件自身
。methods(){
demo(data){......}
}
......
mounted() {
this.$bus.$on('xxxx',this.demo)
}
this.$bus.$emit('xxxx',数据)
将数据传递给AbeforeDestroy钩子
中,用this.$bus.$off('xxxx')去解绑当前组件所用到的事件
。案例(代码片段):
main.js
//引入Vue
import Vue from 'vue'
//引入App
import App from './App.vue'
//创建vm
new Vue({
el:'#app',
render: h => h(App),
beforeCreate() {
Vue.prototype.$bus = this //安装全局事件总线
},
})
App.vue
<template>
<div class="app">
<h1>{{ msg }}</h1>
<School />
<Student />
</div>
</template>
<script>
import Student from "./components/Student";
import School from "./components/School";
export default {
name: "App",
components: { School, Student },
data() {
return {
msg: "你好啊!",
};
},
mounted() {
this.$bus.$on("school", (data) => {
console.log("我是App组件,收到了School组件的数据", data);
});
},
};
</script>
School.vue
<template>
<div class="school">
<h2>学校名称:{{ name }}</h2>
<h2>学校地址:{{ address }}</h2>
<button @click="sendSchoolAddress">把学校地址给App组件</button>
</div>
</template>
<script>
export default {
name: "School",
data() {
return {
name: "你猜",
address: "天堂",
};
},
mounted() {
this.$bus.$on("hello", (data) => {
console.log("我是School组件,收到了Student的数据", data);
});
},
beforeDestroy() {
this.$bus.$off("hello");
},
methods: {
sendSchoolAddress() {
this.$bus.$emit("school", this.address);
},
},
};
</script>
Student.vue
<template>
<div class="student">
<h2>学生姓名:{{ name }}</h2>
<h2>学生年龄:{{ age }}</h2>
<button @click="sendStudentName">把学生名给School组件</button>
</div>
</template>
<script>
export default {
name: "Student",
data() {
return {
name: "张三",
age: 12,
};
},
mounted() {
// console.log('Student',this.x)
},
methods: {
sendStudentName() {
this.$bus.$emit("hello", this.name);
},
},
};
</script>
一种组件间通信的方式,适用于任意组件间通信。
使用步骤:
npm i pubsub-js
import pubsub from 'pubsub-js'
methods(){
demo(data){......}
}
......
mounted(){
this.pid = pubsub.subscribe('xxx',this.demo) //订阅消息}
pubsub.publish('xxx',数据)
PubSub.unsubscribe(pid)
去取消订阅
。School.vue
<template>
<div class="school">
<h2>学校名称:{{ name }}</h2>
<h2>学校地址:{{ address }}</h2>
</div>
</template>
<script>
import pubsub from "pubsub-js";
export default {
name: "School",
data() {
return {
name: "你猜",
address: "你猜",
};
},
mounted() {
this.pubId = pubsub.subscribe("hello", (msgName, data) => {
console.log(this);
console.log("有人发布了hello消息,hello消息的回调执行了", msgName, data);
});
},
beforeDestroy() {
// this.$bus.$off('hello')
pubsub.unsubscribe(this.pubId);
},
};
</script>
Student.vue
<template>
<div class="student">
<h2>学生姓名:{{ name }}</h2>
<h2>学生性别:{{ sex }}</h2>
<button @click="sendStudentName">把学生名给School组件</button>
</div>
</template>
<script>
import pubsub from "pubsub-js";
export default {
name: "Student",
data() {
return {
name: "张三",
sex: "男",
};
},
mounted() {
// console.log('Student',this.x)
},
methods: {
sendStudentName() {
pubsub.publish("hello", 666);
},
},
};
</script>
父组件可以向子组件指定位置插入html结构
,也是一种组件间通信的方式,适用于 父组件 ===> 子组件
。默认插槽、具名插槽、作用域插槽
插槽内容是在父组件中编译后, 再传递给子组件的。
父组件中:
<Category>
<div>html结构1</div>
</Category>
子组件中:
<template>
<div>
<!-- 定义插槽 -->
<slot>插槽默认内容...</slot>
</div>
</template>
案例(代码片段):
App.vue
<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="(g,index) in games" :key="index">{{g}}</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/Category'
export default {
name:'App',
components:{Category},
data() {
return {
foods:['火锅','烧烤','小龙虾','牛排'],
games:['红色警戒','穿越火线','劲舞团','超级玛丽'],
films:['《教父》','《拆弹专家》','《你好,李焕英》','《尚硅谷》']
}
},
}
</script>
Category.vue
<template>
<div class="category">
<h3>{{title}}分类</h3>
<!-- 定义一个插槽(挖个坑,等着组件的使用者进行填充) -->
<slot>我是一些默认值,当使用者没有传递具体结构时,我会出现</slot>
</div>
</template>
<script>
export default {
name:'Category',
props:['title']
}
</script>
当子组件的功能复杂时,子组件的插槽可能并非是一个。就可以用到具名插槽。
使用方法:
父组件中:
<Category>
<template slot="center">
<div>html结构1</div>
</template>
<template v-slot:footer>
<div>html结构2</div>
</template>
</Category>
子组件中:
<template>
<div>
<!-- 定义插槽 -->
<slot name="center">插槽默认内容...</slot>
<slot name="footer">插槽默认内容...</slot>
</div>
</template>
案例(代码片段):
App.vue
<template>
<div class="container">
<Category title="美食">
<img
slot="center"
src="https://s3.ax1x.com/2021/01/16/srJlq0.jpg"
alt=""
/>
<a slot="footer" href="http://www.atguigu.com">更多美食</a>
</Category>
<Category title="游戏">
<ul slot="center">
<li v-for="(g, index) in games" :key="index">{{ g }}</li>
</ul>
<div class="foot" slot="footer">
<a href="http://www.atguigu.com">单机游戏</a>
<a href="http://www.atguigu.com">网络游戏</a>
</div>
</Category>
<Category title="电影">
<video
slot="center"
controls
src="http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4"
></video>
<template v-slot:footer>
<div class="foot">
<a href="http://www.atguigu.com">经典</a>
<a href="http://www.atguigu.com">热门</a>
<a href="http://www.atguigu.com">推荐</a>
</div>
<h4>欢迎前来观影</h4>
</template>
</Category>
</div>
</template>
<script>
import Category from "./components/Category";
export default {
name: "App",
components: { Category },
data() {
return {
foods: ["火锅", "烧烤", "小龙虾", "牛排"],
games: ["红色警戒", "穿越火线", "劲舞团", "超级玛丽"],
films: ["《教父》", "《拆弹专家》", "《你好,李焕英》", "《尚硅谷》"],
};
},
};
</script>
Category.vue
<template>
<div class="category">
<h3>{{ title }}分类</h3>
<!-- 定义一个插槽(挖个坑,等着组件的使用者进行填充) -->
<slot name="center"
>我是一些默认值,当使用者没有传递具体结构时,我会出现1</slot
>
<slot name="footer"
>我是一些默认值,当使用者没有传递具体结构时,我会出现2</slot
>
</div>
</template>
<script>
export default {
name: "Category",
props: ["title"],
};
</script>
数据在组件的自身
,但根据数据生成的结构需要组件的使用者来决定
。(games数据在Category组件中,但使用数据所遍历出来的结构由App组件决定) 父组件中:
<Category>
<template scope="scopeData">
<!-- 生成的是ul列表 -->
<ul>
<li v-for="g in scopeData.games" :key="g">{{g}}</li>
</ul>
</template>
</Category>
<Category>
<template slot-scope="scopeData">
<!-- 生成的是h4标题 -->
<h4 v-for="g in scopeData.games" :key="g">{{g}}</h4>
</template>
</Category>
子组件中:
<template>
<div>
<slot :games="games"></slot>
</div>
</template>
<script>
export default {
name:'Category',
props:['title'],
//数据在子组件自身
data() {
return {
games:['红色警戒','穿越火线','劲舞团','超级玛丽']
}
},
}
案例(代码片段):
App.vue
<template>
<div class="container">
<Category title="游戏">
<template scope="atguigu">
<ul>
<li v-for="(g, index) in atguigu.games" :key="index">{{ g }}</li>
</ul>
</template>
</Category>
<Category title="游戏">
<template scope="{games}">
<ol>
<li style="color: red" v-for="(g, index) in games" :key="index">
{{ g }}
</li>
</ol>
</template>
</Category>
<Category title="游戏">
<template slot-scope="{ games }">
<h4 v-for="(g, index) in games" :key="index">{{ g }}</h4>
</template>
</Category>
</div>
</template>
<script>
import Category from "./components/Category";
export default {
name: "App",
components: { Category },
};
</script>
Category.vue
<template>
<div class="category">
<h3>{{ title }}分类</h3>
<slot :games="games" msg="hello">我是默认的一些内容</slot>
</div>
</template>
<script>
export default {
name: "Category",
props: ["title"],
data() {
return {
games: ["红色警戒", "穿越火线", "劲舞团", "超级玛丽"],
};
},
};
</script>
查看第六章中的内容