写在前面
Vue快乐学习第三篇,本节主要学习 Vue 组件化和父子组件通信,通过学习,你应该了解组件化相关概念,学习和使用 Vue 全局组件和局部组件注册,以及父子组件通信等知识,干就完了
组件化是指解耦复杂系统时将多个功能模块拆分、重组的过程,有多种属性、状态反映其内部特性。
组件系统是 Vue 的另一个重要概念,因为它是一种抽象,允许我们使用小型、独立和通常可复用的组件构建大型应用。仔细想想,几乎任意类型的应用界面都可以抽象为一个组件树:
为了解耦:把复杂系统拆分成多个组件,分离组件边界和责任,便于独立升级和维护。
♦️ 创建组件构造器
♦️ 注册组件
♦️ 使用组件
调用 Vue.extend() 插件的是一个组件构造器,通常在创建组件构造器时,传入 template 代表自定义组件的模板,这个模板就是要显示的 HTML 代码,事实上,这种写法实际上基本不使用,但这是基础
调用 Vue.component() 是将刚创建的组件构造器注册成为一个组件,并且给它起一个组件标签名,所以传递了两个参数,1.注册组件的标签名2.组件构造器
<body>
<div id="app">
<hello>hello>
<hello>hello>
div>
<script src="./vue.js">script>
<script>
// 1.插件组件构造器
const hello = Vue.extend({
template: `
Hello World
`
})
// 2.注册组件
Vue.component('hello',hello)
const vm = new Vue({
el: '#app'
})
script>
body>
全局注册
<body>
<div id="app">
<hello>hello>
<hello>hello>
div>
<div id="app1">
<hello>hello>
div>
<script src="./vue.js">script>
<script>
// 1.插件组件构造器
const hello = Vue.extend({
template: `
Hello World
`
})
// 2.注册组件(全局注册,意味着可以在多个vue实例下使用)
Vue.component('hello',hello)
const vm = new Vue({
el: '#app'
})
const vm1 = new Vue({
el:'#app1'
})
script>
body>
局部注册
局部注册实在某个vue实例内部,通过配置项 components 配置,局部组件只能在注册它的 vue 实例下才可以使用
<body>
<div id="app">
<hello>hello>
<hello>hello>
div>
<div id="app1">
<hello>hello>
div>
<script src="./vue.js">script>
<script>
// 1.插件组件构造器
const hello = Vue.extend({
template: `
Hello World
`
})
// 2.注册组件(全局注册,意味着可以在多个vue实例下使用)
const vm = new Vue({
el: '#app',
// 局部注册,只能在当前 vue 实例下使用
components:{
hello:hello
}
})
const vm1 = new Vue({
el:'#app1'
})
script>
body>
组件和组件之间存在关系,最常见的就是父子组件的关系
<body>
<div id="app">
<c2>c2>
div>
<script src="./vue.js">script>
<script>
// 创建第一个组件
const c1 = Vue.extend({
template: `
我是p1
`
})
// 创建第二个组件
const c2 = Vue.extend({
template: `
我是p2
`,
components: {
c1: c1
}
})
new Vue({
el: '#app',
data: {
message: 'Hello World'
},
components: {
c2: c2
}
})
script>
body>
注意如果在vue实例下写标签 c1 是不正确的,因为我们既没有全局注册 c1 组件,也没有在 vue 实例中进行注册,而是在 c2 组件内部进行了注册,作用域在 c2 组件,所以可以在 c2 的模板中使用,这一点需要注意
Vue为简化书写组件的过程,提供了注册的语法糖
省去了 Vue.extend() 这个步骤,而是直接使用一个对象来代替
全局注册组件语法糖,直接使用 Vue.component ,第一个配置组件名,第二个是template模板,底层还是extend的形式
<body>
<div id="app">
<hello>hello>
div>
<script src="./vue.js">script>
<script>
// 全局注册,语法糖,底层还是 extend
Vue.component('hello',{
template:`
Hello World
`
})
new Vue({
el: '#app'
})
script>
body>
局部注册组件语法糖,使用 components 配置项,组件名后跟模板,省去了 Vue.extend()
<body>
<div id="app">
<hello>hello>
div>
<script src="./vue.js">script>
<script>
new Vue({
el: '#app',
components:{
hello:{
template:`
Hello World
`
}
}
})
script>
body>
<body>
<div id="app">
<hello>hello>
<world>world>
div>
<script type="text/x-template" id="hello">
<div>
<h2>Hello Vue</h2>
</div>
script>
<template id="world">
<div>
<h2>Hello Worldh2>
div>
template>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js">script>
<script>
Vue.component('hello',{
template:'#hello'
})
Vue.component('World',{
template:'#world'
})
new Vue({
el: '#app',
})
script>
body>
因为以函数形式返回,当多次调用组件,每个组件都会有自己的一份data,彼此互不影响
组件时一个单独模块的封装,这个模块有属于自居的 HTML 模板,也应该有自己的数据 data……
组件是不可以直接访问 Vue 实例的data的
Vue组件应该有自己保存数据的地方
组件对象也有一个 data 属性,也可以有 method 等属性
只是这个 data 属性必须是一个函数
而且这个函数返回一个对象,对象内部保存着数据
<body>
<div id="app">
<hello>hello>
div>
<template id="hello">
<div>
<h2>{{hello}}h2>
div>
template>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js">script>
<script>
Vue.component('hello', {
template: '#hello',
data() {
return {
hello: 'Hello World,Hello Vue.js'
}
}
})
new Vue({
el: '#app',
})
script>
body>
<body>
<div id="app">
<counter>counter>
<counter>counter>
<counter>counter>
div>
<template id="counter">
<div>
<button @click="sub">-button>
{{count}}
<button @click="add">+button>
div>
template>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js">script>
<script>
Vue.component('counter', {
template: '#counter',
data() {
return {
count: 0
}
},
methods: {
add() {
this.count++;
},
sub() {
this.count--;
}
},
})
new Vue({
el: '#app',
})
script>
body>
<body>
<div id="app">
<counter>counter>
<counter>counter>
<counter>counter>
div>
<template id="counter">
<div>
<button @click="sub">-button>
{{obj.count}}
<button @click="add">+button>
div>
template>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js">script>
<script>
const obj = {
count: 0
}
Vue.component('counter', {
template: '#counter',
data() {
return {
obj
}
},
methods: {
add() {
this.obj.count++;
},
sub() {
this.obj.count--;
}
},
})
new Vue({
el: '#app',
})
script>
body>
<body>
<div id="app">
<child :cmessage="message" :cmovies="movies">child>
div>
<template id="child">
<div>
<p>我是子组件p>
<p>{{cmessage}}p>
<ul>
<li v-for="item in cmovies">{{item}}li>
ul>
div>
template>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js">script>
<script>
const child = {
template: '#child',
// 数组写法
// props: ['cmessage']
// 对象写法,还可以指定类型,
props: {
cmessage: {
// 类型
type: String,
// 默认值
default: '哈哈',
// 必须传值,否则报错
required: true
},
cmovies: {
type: Array,
default() {
return [];
}
}
}
}
new Vue({
el: '#app',
data: {
message: '你好啊,子组件',
movies: ['天气之子', '你的名字', '千与千寻']
},
components: {
child
}
})
script>
body>
<body>
<div id="app">
<npm :c-npm="person">npm>
div>
<template id="npm">
<div>
<p>{{cNpm.width}}p>
<p>{{cNpm.height}}p>
div>
template>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js">script>
<script>
const npm = {
template: '#npm',
props: {
cNpm: {
type: Object
}
}
}
new Vue({
el: '#app',
components: {
npm
},
data: {
person: {
height: 1.8,
width: 1.5
}
}
})
script>
body>
当子组件需要向父组件传递时,就要用到自定义事件了,之前学习的 v-on 不仅可以用于监听 DOM 事件,也可以监听组件间的自定义事件
在子组件使用 $emit()来触发事件,在父组件中使用 v-on 来监听子组件事件
<body>
<div id="app">
<hello @cclick="aa">hello>
div>
<template id="zi">
<div>
<button v-for="item in shoppings" @click="btnClick(item.name)">{{item.name}}button>
div>
template>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js">script>
<script>
// 子组件
const hello = {
template: '#zi',
data() {
return {
shoppings: [
{ id: 'a', name: '热门推荐' },
{ id: 'b', name: '家用电器' },
{ id: 'c', name: '数码办公' }
]
}
},
methods: {
btnClick(name) {
// 自定义事件 cclick
this.$emit('cclick', name)
}
},
}
// 父组件
new Vue({
el: '#app',
components: {
hello
},
methods: {
aa(name) {
console.log(name);
}
}
})
script>
body>