Vue基础06组件化实践
1 组件注册及数据传递,课程列表组件
注意:
- 组件模板单根元素
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>购物车</title>
</head>
<body>
<div id="app">
<h2>{{title}}</h2>
<input v-model="course" v-on:keydown.enter="addCourse">
<button @click="addCourse">新增</button>
<course-list :courses="courses"></course-list>
<div>课程总数:{{this.courses.length}}门</div>
</div>
<script src="../js/vue.js"></script>
<script>
// 课程列表组件
Vue.component('course-list', {
data() {
return {
selectedCourse: ''
}
},
props: {
courses: {
type: Array,
default: []
}
},
template: `
<div>
<div v-for="item in courses" :key="item"
@click="selectedCourse = item"
:style="{backgroundColor: (selectedCourse == item ? '#ddd' : 'transparent')}">
{{ item }}
</div>
</div>
`
})
// 模拟异步数据调用
function getCourses() {
return new Promise(resolve => {
setTimeout(() => {
resolve(['Java课程', 'Web课程', '爬虫课程' ])
}, 1000)
})
}
const app = new Vue({
el: '#app',
data() {
return {
title: '购物车',
courses: [],
course: ''
}
},
async created() {
const courses = await getCourses()
this.courses = courses
},
methods: {
addCourse() {
if (this.course == '' || this.courses.indexOf(this.course) > -1) {
return
}
this.courses.push(this.course)
this.course = ''
}
}
})
</script>
</body>
</html>
2 自定义事件及监听,新增课程组件
说明:
- course变量由子组件维护(有状态组件)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>购物车</title>
</head>
<body>
<div id="app">
<h2>{{title}}</h2>
<course-add @add-course="addCourse"></course-add>
<course-list :courses="courses"></course-list>
<div>课程总数:{{this.courses.length}}门</div>
</div>
<script src="../js/vue.js"></script>
<script>
// 课程新增组件
Vue.component('course-add', {
data() {
return {
course: ''
}
},
template: `
<div>
<input v-model="course" v-on:keydown.enter="addCourse">
<button @click="addCourse">新增</button>
</div>
`,
methods: {
addCourse() {
this.$emit('add-course', this.course)
this.course = ''
}
},
})
// 课程列表组件
Vue.component('course-list', {
data() {
return {
selectedCourse: ''
}
},
props: {
courses: {
type: Array,
default: []
}
},
template: `
<div>
<div v-for="item in courses" :key="item"
@click="selectedCourse = item"
:style="{backgroundColor: (selectedCourse == item ? '#ddd' : 'transparent')}">
{{ item }}
</div>
</div>
`
})
// 模拟异步数据调用
function getCourses() {
return new Promise(resolve => {
setTimeout(() => {
resolve(['Java课程', 'Web课程', '爬虫课程' ])
}, 1000)
})
}
const app = new Vue({
el: '#app',
data() {
return {
title: '购物车',
courses: []
}
},
async created() {
const courses = await getCourses()
this.courses = courses
},
methods: {
addCourse(course) {
if (course == '' || this.courses.indexOf(course) > -1) {
return
}
this.courses.push(course)
}
}
})
</script>
</body>
</html>
3 自定义组件实现双绑,新增课程组件无状态化
说明:
- 让组件无状态化
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>购物车</title>
</head>
<body>
<div id="app">
<h2>{{title}}</h2>
<course-add v-model="course" @add-course="addCourse"></course-add>
<!-- <course-add :value="course" @input="course=$event" @add-course="addCourse"></course-add> -->
<course-list :courses="courses"></course-list>
<div>课程总数:{{this.courses.length}}门</div>
</div>
<script src="../js/vue.js"></script>
<script>
// 课程新增组件
Vue.component('course-add', {
props: ['value'],
template: `
<div>
<input :value="value"
@input="onInput"
v-on:keydown.enter="addCourse">
<button @click="addCourse">新增</button>
</div>
`,
methods: {
addCourse() {
this.$emit('add-course')
},
onInput(e) {
this.$emit('input', e.target.value)
}
},
})
// 课程列表组件
Vue.component('course-list', {
data() {
return {
selectedCourse: ''
}
},
props: {
courses: {
type: Array,
default: []
}
},
template: `
<div>
<div v-for="item in courses" :key="item"
@click="selectedCourse = item"
:style="{backgroundColor: (selectedCourse == item ? '#ddd' : 'transparent')}">
{{ item }}
</div>
</div>
`
})
// 模拟异步数据调用
function getCourses() {
return new Promise(resolve => {
setTimeout(() => {
resolve(['Java课程', 'Web课程', '爬虫课程' ])
}, 1000)
})
}
const app = new Vue({
el: '#app',
data() {
return {
title: '购物车',
courses: [],
course: ''
}
},
async created() {
const courses = await getCourses()
this.courses = courses
},
methods: {
addCourse() {
if (this.course == '' || this.courses.indexOf(this.course) > -1) {
return
}
this.courses.push(this.course)
this.course = ''
}
}
})
</script>
</body>
</html>
4 插槽,弹窗组件
通过使用vue的元素可以给组件传递内容
分类:默认插槽、具名插槽、作用域插槽
关闭事件,.sync父组件可以少写一个事件,默认一个@update:show事件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>购物车</title>
<style>
.message-box {
padding: 10px 20px;
background: #4fc08d;
border: 1px solid #42b983;
}
.message-box-close {
float: right;
}
</style>
</head>
<body>
<div id="app">
<message :show.sync="show">
<template v-slot:title>
<strong>恭喜</strong>
</template>
<!-- 默认插槽内容 -->
<template>新增成功</template>
</message>
<h2>{{title}}</h2>
<course-add v-model="course" @add-course="addCourse"></course-add>
<course-list :courses="courses"></course-list>
<div>课程总数:{{this.courses.length}}门</div>
</div>
<script src="../js/vue.js"></script>
<script>
// 弹窗组件
Vue.component('message', {
props: ['show'],
template: `
<div class="message-box" v-if="show">
<!-- 具名插槽 -->
<slot name="title"></slot>
<!-- 通过slot插槽获取传入内容 -->
<slot></slot>
<span class="message-box-close" @click="$emit('update:show', false)">X</span>
</div>
`
})
// 课程新增组件
Vue.component('course-add', {
props: ['value'],
template: `
<div>
<input :value="value"
@input="onInput"
v-on:keydown.enter="addCourse">
<button @click="addCourse">新增</button>
</div>
`,
methods: {
addCourse() {
this.$emit('add-course')
},
onInput(e) {
this.$emit('input', e.target.value)
}
},
})
// 课程列表组件
Vue.component('course-list', {
data() {
return {
selectedCourse: ''
}
},
props: {
courses: {
type: Array,
default: []
}
},
template: `
<div>
<div v-for="item in courses" :key="item"
@click="selectedCourse = item"
:style="{backgroundColor: (selectedCourse == item ? '#ddd' : 'transparent')}">
{{ item }}
</div>
</div>
`
})
// 模拟异步数据调用
function getCourses() {
return new Promise(resolve => {
setTimeout(() => {
resolve(['Java课程', 'Web课程', '爬虫课程' ])
}, 1000)
})
}
const app = new Vue({
el: '#app',
data() {
return {
title: '购物车',
courses: [],
course: '',
show: false
}
},
async created() {
const courses = await getCourses()
this.courses = courses
},
methods: {
addCourse() {
if (this.course == '' || this.courses.indexOf(this.course) > -1) {
return
}
this.courses.push(this.course)
this.course = ''
this.show = true
setTimeout(() => {this.show = false}, 3000)
}
}
})
</script>
</body>
</html>
5 组件化探讨
组件化是Vue的精髓,Vue应用就是由一个个组件构成的。Vue的组件化涉及到的内容,可以从下面几点进行阐述:
- 定义:组件是可利用的Vue实例,准确讲它们是VueComponent的实例,继承自Vue
- 优点:组件化可以增加代码的利用性、可维护性和可测试性
- 使用场景:什么时候使用组件?以下分类可作为参考
- 通用组件:实现最基本的功能,具有能用性、可复用性,例如按钮组件、输入框组件、布局组件等
- 业务组件:完成具体业务,具有一定的复用性,例如登录组件、轮播组件
- 页面组件:组织应用各部分独立内容,需要时在不同页面组件间切换,例如列表页、详情组件
- 如何使用组件
- 定义(全局、局部):Vue.component(), components选项,sfc(single file component)
- 分类:有状态组件(有data的组件),functional(无状态组件也称为函数式组件), abstract(抽象组件,缓存、动画等)
- 通信:props, on(), provide/inject, parent/attrs/$listeners
- 内容分发:
<slot>, <template>, v-slot - 使用及优化: is, keep-alive, 异步组件
- 组件的本质
- 组件配置--->VueComponent实例--->render()--->Virtual DOM--->DOM
- 所以组件的本质是产生虚拟DOM