Vue官网中描述:组件是可复用的 Vue 实例,且带有一个名字,我们可以在一个通过 new Vue 创建的 Vue 根实例中,把这个组件作为自定义元素来使用。
因为组件是可复用的 Vue 实例,所以它们与 new Vue
接收相同的选项,例如 data
、computed
、watch
、methods
以及生命周期钩子等。仅有的例外是像 el
这样根实例特有的选项。
通常一个应用会以一棵嵌套的组件树的形式来组织:
通过组件化开发可以大大提升开发效率,代码结构也更加简单清晰。
为了能在模板中使用,这些组件必须先注册以便Vue能够识别,有两种组件注册方式:全局组件和局部组件
全局组件可以在多个vue实例中使用,类似于全局变量,使用Vue.component('cpn-name', {//options})
方式注册
<div id="app">
<my-cpn>my-cpn>
div>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js">script>
<script>
// 1.创建组件构造器对象
const cpnc = Vue.extend({
template:`
标题
内容1
内容2
`
})
// 2.注册组件(全局组件,可以在多个vue实例中使用)
Vue.component('my-cpn', cpnc)
script>
注册全局组件语法糖
// 注册全局组件语法糖
Vue.component('cpn1', {
template:`
标题
内容
`
})
局部组件类似于局部变量,只能在当前vue实例挂载的对象中使用。
<div id="app">
<cpnc>cpnc>
div>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js">script>
<script>
// 1.创建组件构造器对象
const cpnc = Vue.extend({
template:`
标题
内容1
内容2
`
})
const app = new Vue({
el:"#app",
components:{// 局部组件创建
cpnc, // 等价于cpnc:cpnc
}
})
script>
注册局部组件语法糖
const app = new Vue({
el:"#app",
components:{//局部组件创建
cpn2:{
template:`
标题
内容
`
}
}
})
script
标签使用script
标签定义组件的模板,标签类型为text/x-template
。
<script type="text/x-template" id="cpn1">
<div>
<h2>组件模板的分离写法</h2>
<p>script标签注意为text/x-template</p>
</div>
script>
使用template
标签,模板内容直接写在标签内
<template id="cpn2">
<div>
<h2>组件模板的分离写法h2>
<p>template标签p>
div>
template>
<script>
const app = new Vue({
el: "#app",
components: {
cpn1:{
template:'#cpn1'
},
cpn2: {
template: '#cpn2'
}
}
})
script>
在定义一个组件时,对于组件的data
并不能像这样直接提供一个对象
data: {
count: 0
}
组件的data
必须是一个函数
data() {
return {
count: 0
}
}
之所以以这种方式存放组件的数据,就是因为组件的特性
组件的思想是复用,我们在复用组件的时候当然是希望各个组件之间内部变量不互相干扰,如果确实需要共享数据或状态,可以使用全局组件或者用vuex状态管理工具。
可以通过一个例子更加直观的理解
组件实例间的作用域时相互独立的,这就意味着组件间的数据不能直接互相引用,但是在不同的场景下组件间的通信是必要的,因此针对不同场景有不同的通信方式
父组件通过props
方式向子组件传递数据
在父组件中用v-bind
绑定要传递的数据
//App.vue父组件
<template>
<div id="app">
<users :users="users">users>
div>
template>
<script>
import Users from "./components/Users"
export default {
name: 'App',
data(){
return{
users:["zhh","xz","fty"]
}
},
components:{
Users
}
}
在子组件中使用props
接收父组件传递来的数据
//Users子组件
<template>
<div class="hello">
<ul>
<li v-for="user in users">{{user}}li>
ul>
div>
template>
<script>
export default {
name: 'Users',
props:{
users:{
type:Array,
required:true
}
}
}
script>
props
属性数组写法
props: ['cmessage', 'cmovies']
对象写法
props: {
cmessage: {
type: String,
default: 'zzzzz',
required: true // 在使用组件必传值
}
}
props属性的类型限制,多个类型使用数组
cmovies:Array,// 限制为数组类型
cmessage:String,// 限制为String类型
cmessage:['String','Number']// 限制为String或Number类型
props属性的默认值
cmessage: {
type: String,
default: 'message',// 默认值
}
props属性的必传值
cmessage: {
type: String,
default: 'message',
required: true // 在使用组件必传值
}
类型是Object/Array,默认值必须是一个函数
// 类型是Object/Array,默认值必须是一个函数
cmovies: {
type: Array,
default () {
return [1, 2, 3, 4]
}
},
自定义验证函数
vaildator: function (value) {
// 传递的值必须匹配下列字符串中的一个
return ['aaa', 'bbb', 'ccc'].indexOf(value) !== -1
}
自定义类型
function Person(firstName, lastName) {
this.firstName = firstName
this.lastName = lastName
}
cmessage:Person//限定了cmeessage必须是Person类型
需要注意的是,v-bind
指令中不支持使用驼峰标识,需要使用-
代替
如:myCpn
要改成:my-cpn
子组件向父组件传递数据时,使用$emit
通过事件传递。
// 子组件
<template>
<header>
<h1 @click="changeTitle">{{title}}h1>//绑定一个点击事件
header>
template>
<script>
export default {
name: 'app-header',
data() {
return {
title:"Vue.js Demo"
}
},
methods:{
changeTitle() {
this.$emit("titleChanged","子组件向父组件传递数据");
// 自定义事件传递数据
}
}
}
script>
// 父组件
<template>
<div id="app">
<app-header @titleChanged="updateTitle" >app-header>
// 与子组件titleChanged自定义事件保持一致
// updateTitle($event)接受传递过来的文字
<h2>{{title}}h2>
div>
template>
<script>
import Header from "./components/Header"
export default {
name: 'App',
data(){
return{
title:"父组件数据"
}
},
methods:{
updateTitle(e){
this.title = e;
}
},
components:{
"app-header":Header,
}
}
script>
$paren
/ $children
与 ref
ref
: 如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例$parent
/ $children
:通过父链/子链,访问父/子实例使用这两种方法得到组件实例,就可以直接调用组件的方法或者访问数据
ref
方法首先使用ref
给子组件一个引用标记
<cpn ref="componentA">cpn>
在父组件中就可以使用this.$refs.componentA
就可以访问了这个子组件了,包括这个组件的数据和方法。
$paren
/ $children
<div id="app">
<cpn>cpn>
<cpn>cpn>
<cpn ref="componentA">cpn>
<button @click="btnClick" >按钮button>
div>
<template id="cpn">
<div>
我是子组件
div>
template>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js">script>
<script>
// 父传子:props
const cpn = {
template: "#cpn",
data() {
return {
name:"我是子组件的name"
}
},
methods: {
showMessage(){
console.log("showMessage");
}
},
};
const app = new Vue({
el: "#app",
data() {
return {
message:"hello"
}
},
methods: {
btnClick(){
// 1.children
console.log(this.$children[0].showMessage)
for (let cpn of this.$children) {
console.log(cpn.showMessage)
}
// 2.$ref
// console.log(this.$refs.componentA.name)
}
},
components: {
cpn
},
})
script>
$children
官方介绍:
当前实例的直接子组件。需要注意 $children
并不保证顺序,也不是响应式的。如果你发现自己正在尝试使用 $children
来进行数据绑定,考虑使用一个数组配合 v-for
来生成子组件,并且使用 Array
作为真正的来源。
$parent
官方介绍:
当前组件树的根 Vue 实例。如果当前实例没有父实例,此实例将会是其自己。