当子组件需要和父级组件进行通信,可以派发并监听自定义事件。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>购物车</title>
<script src="vue.js"></script>
<style>
.active{
background-color: #ddd;
}
</style>
</head>
<body>
<!-- 宿主文件 -->
<!-- {{ mustache }}是一个动态的值 左右就用双括号{{}}-->
<!-- 插值文本 -->
<!-- v-once执行一次性插值 之后值变了动态值不变 -->
<div id= "app">
<h2 :title="title">
{{ title }}
</h2>
<!-- 新增课程-->
<!-- 在这个组件内部用户会交互 会会车会新增 这事候我们要通过某个方式告诉老爹-->
<!-- 要新增东西啦 并且要把新增的数据传递出来-->
<!-- 传递数据就涉及到自定义数据派发的监听过程 -->
<!--用 @add-course = "addCourse" 来监听组件事件发生过程 -->
<course-add @add-course = "addCourse"></course-add>
<!-- 列表渲染 -->
<!-- 被选中点击一个动态的类 没有被选中类被移除掉-->
<!-- transparent 没有颜色透明色 -->
<!-- 列表组件 -->
<course-list :courses = "courses"></course-list>
<!-- <div
v-for="c in courses"
:key="c"
:style = "{backgroundColor: selectedCourse === c ? '#' : 'transparent'}"
@click = "selectedCourse = c">
{{ c }}
</div> -->
<script>
// 课程新增的组件
Vue.component('course-add',{
data(){
return{
course:"",
}
},
template:`
<div>
<p>
<input type = "text" v-model = "course" v-on:keydown.enter = "addCourse">
<button @click = "addCourse">新增</button>
</p>
</div>
`,
methods: {
addCourse() {
//派发事件通知父组件新增课程$emit(事件,参数)
// 由于html中不区分大小写,建议派发事件的时候用羊肉串命名不要用驼峰
this.$emit('add-course',this.course)
this.course = ""
}
},
})
// 课程列表组件
// 组件命名建议取成羊肉串命名法,如果使用驼峰的方式将来在使用的时候很容易产生混淆
// 因为最终在列表组件那里使用的时候一般情况下是不区分大小写的
// 所以即使用了驼峰命名在下面也无法使用因为无法识别
// 而且组件名在内部进行注册的时候会对命名进行转换,这样会造成对使用过程造成混淆
// 推荐组件在命名的时候就进行羊肉串命名法
Vue.component('course-list',{
data(){
return{
selectedCourse:'',
}
},
// props是一个属性,接受老爹传过来的一些属性
// 定义方式有两种:一种是对象,一种是数组
// 如果属性是这样定义['courses'] ,那么就是希望老爹传过来的属性名就叫courses
// 将来在列表组件里就可以通过this.courses来访问传进来的参数了
// ['courses']这种方式对参数的校验没有好处,一般情况下用得更多的是对象形式
// 会用key的方式去命名,然后用type对其类型进行约束
props:{
courses:{
type:Array,
// 默认值为空数组,这样子对courses就会有提前的约束
default:[]
}
},
template:`
<!-- 条件渲染 -->
<!-- v-if 和 v-for最好不要放在一个标签上他们有优先级的不同会争抢 -->
<p v-if = "courses.length == 0 ">没有任何课程信息</p>
<div class="course-list" v-else>
<div
v-for="c in courses"
:key="c"
:class = "{active: selectedCourse === c}"
@click = "selectedCourse = c">
{{ c }}
</div>
</div>
`
})
// 模拟异步数据调用
function getCourses(){
return new Promise(resolve =>{
setTimeout(() => {
resolve(['定风波','web高级'])
})
})
}
// 1.创建vue实例
// 保存vue实例
const app = new Vue({
// 交代宿主是谁
el:"#app",
data: {
title: '课程:',
// courses:[],
courses:["黄帝内经","仲景心法","针灸","伤寒","金匮"],
totalCount:0,
},
methods: {
addCourse(course) {
// 1.添加course到数组
this.courses.push(course)
}
},
// 异步用async await
// 创建钩子
async created(){
// created执行时刻,组件实例已创建,由于未挂载dom不存在
const courses = await getCourses()
this.courses = courses
},
// mounted获取业务数据
// mounted时间点更靠后,除了创建之后还做了挂载,获取业务数据
// 挂载的实际操作是将前面所有的渲染函 数执行之后将虚拟DOM转换为真实DOM
// 也就是在mounted中可以访问到DOM元素了
// 其实vue实例被创建,挂载,渲染函数执行虚拟DOM转换为真实DOM都是很快的
// 异步数据获取选择mounted和created本质上没有什么差别
// 不用以为看到created界面会比较早 没有这个区别
// 因为我们访问数据所花销的时间远远大于初始化挂载的时间,没有什么实质上的区别
// mounted(){},
// computed是值变化了返回值
computed: {
total() {
// 计算属性是有缓存性的,如果值没有发生变化,则页面不会重新渲染
return this.courses.length+'门'
}
},
// watch 做某个值得监控,如果发生变化了则会执行这个函数做我想做的事情
// watch是值变化了执行这个函数
// 监控courses这个值的变化
// watch如果不加特别声明表示值变化之后才会执行,它一开始是不会执行的
// 默认情况下watch初始化时不执行想要watch立即执行 immediate为true。立即执行一次
// watch: {
// courses(newValue, oldValue) {
// this.totalCount = newValue.length +"门"
// }
// },
// 为了一开始就能执行要选带选项的watch vwatcher-options
watch: {
data: {
immediate: true,//立即执行 一次
// deep是当监控的数据是很复杂的数据的时候才使用如果是很浅就不需要用到deep
// deep: true,
handler(newValue, oldValue) {
}
}
},
})
</script>
</body>
</html>
vue组件化之后原本html里面至少二三十行的内容变成两行,这就是组件化带来的最明显的好处