06. Components
November 11, 2023About 8 min
Basic Usuage
Basic Example
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="../js/vue.js"></script>
</head>
<body>
<div id="app">
<!-- 使用组件 -->
<my-cpn></my-cpn>
<my-cpn></my-cpn>
</div>
<script>
// 1. 创建组件构造器对象
const cpnC = Vue.extend({
template: `
<div>
<h2> 我是标题</h2>
<p> 我是内容,哈哈哈哈哈</p>
<p> 我是内容,呵呵呵呵</p>
<p> 我是内容,嘿嘿嘿嘿嘿</p>
</div>
`
})
// 2. 注册组件(全局注册)
Vue.component('my-cpn', cpnC)
// 3. 创建vue实例
const app = new Vue({
el:"#app",
data:{
message: 'Hello world'
},
})
</script>
</body>
</html>
Global and Local Register
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="../js/vue.js"></script>
</head>
<body>
<div id="app">
{{message}}
<local_cpn></local_cpn>
</div>
<hr>
<div id="app2">
{{message}}
<my-cpn></my-cpn>
</div>
<script>
// 1. 创建组件构造器对象
const cpnC = Vue.extend({
template: `
<div>
<h2> 我是标题</h2>
<p> 我是内容,哈哈哈哈哈</p>
<p> 我是内容,呵呵呵呵</p>
<p> 我是内容,嘿嘿嘿嘿嘿</p>
</div>
`
})
// 2. 注册组件(全局组件)
Vue.component('my-cpn', cpnC)
// 3. 创建vue实例(使用局部组件)
const app = new Vue({
el:"#app",
data:{
message: '局部组件'
},
// 局部组件
components:{
local_cpn: cpnC
}
})
// 3. 创建vue实例(使用全局组件)
const app2 = new Vue({
el:"#app2",
data:{
message: '全局组件'
},
})
</script>
</body>
</html>
Parent and Children
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="../js/vue.js"></script>
</head>
<body>
<div id="app">
<cpn2></cpn2>
</div>
<script>
// 1. 创建组件构造器对象
//子组件
const cpnC1 = Vue.extend({
template: `
<div>
<h2> 我是子组件</h2>
</div>
`
})
//父组件
const cpnC2 = Vue.extend({
template: `
<div>
<h2> 我是父组件</h2>
<cpn1></cpn1>
</div>
`,
components: {
cpn1: cpnC1
}
})
// 3. 使用组件
const app = new Vue({
el:"#app",
data:{
message: 'Hello world'
},
// 局部组件
components:{
cpn2: cpnC2,
}
})
</script>
</body>
</html>
Syntactic sugar
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="../js/vue.js"></script>
</head>
<body>
<div id="app">
<cpn1></cpn1>
<cpn2></cpn2>
</div>
<script>
// 1. 创建组件构造器对象(普通方式)
const cpnC1 = Vue.extend({
template: `
<div>
<h2> 我是标题1</h2>
<p> 我是内容,哈哈哈哈哈</p>
</div>
`
})
// 2. 组件的全局注册方法
// 全局注册:传统方式
//Vue.component('cpn1', cpnC1)
// 全局注册:语法糖方式
Vue.component('cpn1', {
template: `
<div>
<h2>全局组件</h2>
</div>
`
})
// 3. vue实例创建
const app = new Vue({
el:"#app",
data:{
message: 'Hello world'
},
// 局部组件(语法糖和传统)
components:{
cpn2: {
template: `
<div>
<h2>局部组件</h2>
</div>
`
}
}
})
</script>
</body>
</html>
Template Detach
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="../js/vue.js"></script>
</head>
<body>
<div id="app">
<cpn1></cpn1>
<cpn2></cpn2>
</div>
<!-- 第一种分离方式 -->
<script type="text/x-template" id="cpn1">
<div>
<h2>我是分离标题1</h2>
<p> 我是内容,哈哈哈哈哈</p>
</div>
</script>
<!-- 第二种分离方式 -->
<template id="cpn2">
<div>
<h2>我是第二种分离标题</h2>
<p> 我是内容,哈哈哈哈哈</p>
</div>
</template>
<script>
// 1. 创建组件构造器对象
//子组件
const cpnC1 = Vue.extend({
template: `
<div>
<h2> 我是标题1</h2>
<p> 我是内容,哈哈哈哈哈</p>
</div>
`
})
//全局组件
// 1.传统方式
//Vue.component('cpn1', cpnC1)
// 2.语法糖方式
Vue.component('cpn1', {
template: `#cpn1`
})
// 3. 使用组件
const app = new Vue({
el:"#app",
data:{
message: 'Hello world'
},
// 局部组件(语法糖和传统)
components:{
cpn2: { template: `#cpn2` }
}
})
</script>
</body>
</html>
Data & Methods
data
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="../js/vue.js"></script>
</head>
<body>
<div id="app">
<cpn1></cpn1>
</div>
<script type="text/x-template" id="cpn1">
<div>
<h2>{{title}}</h2>
<p> 我是内容,哈哈哈哈哈</p>
</div>
</script>
<script>
// 3.语法糖方式注册全局组件
Vue.component('cpn1', {
template: `#cpn1`,
//组件数据必须为函数
data(){
return {
title: "我的动态属性"
}
}
})
// 3. vue实例
const app = new Vue({
el:"#app",
data:{
message: 'Hello world'
}
})
</script>
</body>
</html>
Methods
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="../js/vue.js"></script>
</head>
<body>
<div id="app">
<cpn1></cpn1>
</div>
<!-- 第二种分离方式 -->
<template id="cpn">
<div>
<h2>计数器:{{counter}}</h2>
<button @click="decrement">-</button>
<button @click="increment">+</button>
</div>
</template>
<script>
Vue.component('cpn1', {
template: `#cpn`,
data(){
return {
counter: 0
}
},
methods: {
decrement(){
this.counter --;
},
increment(){
this.counter++;
}
}
})
// 3. 使用组件
const app = new Vue({
el:"#app",
data:{
message: 'Hello world'
},
})
</script>
</body>
</html>
Communication
Parent to Children
父组件向子组件传递信息时,需要在子组件中定义中提前定义属性,父组件通过绑定子组件属性的方式向父组件中传递数据。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="../js/vue.js"></script>
</head>
<body>
<div id="app">
<!-- cMessage驼峰形式绑定时必须转换为下面形式 -->
<cpn v-bind:cmovies="movies" :c-message="message"></cpn>
</div>
<!-- 第二种分离方式 -->
<template id="cpn">
<div>
<ul>
<li v-for="item in cmovies"> {{item}}</li>
</ul>
<h2>计数器:{{counter}}</h2>
<button @click="decrement">-</button>
<button @click="increment">+</button>
<p>{{cMessage}}</p>
</div>
</template>
<script>
const cp1n = {
template: "#cpn",
data(){
return{
counter : 0
}
},
props: {
cMessage: {
type: String,
default: 'default',
required: true
},
cmovies: {
type: Array, //类型限定
default: ['aaa', 'xxxx'], //默认值
required: true //是否强制需要
}
},
methods: {
decrement(){
this.counter --;
console.log(this.cmovies)
console.log(this.cmessage)
},
increment(){
this.counter++;
}
}
}
//父传子:props
const app = new Vue({
el:"#app",
data:{
message: 'Hello world',
movies: ['aaaa', 'bbbb', 'cccc']
},
components:{
cpn: cp1n
}
})
</script>
</body>
</html>
Children to Parent
子组件向父组件传递消息通过事件传递,在父组件中定义好需要的事件,并向子组件中绑定该事件,子组件通过提交绑定的事件,将对应数据传递到父组件。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="../js/vue.js"></script>
</head>
<body>
<div id="app">
<cpn v-bind:cmovies="movies" :cmessage="message" @itemclick="itemclick"></cpn>
</div>
<!-- 子组件模板 -->
<template id="cpn">
<div>
<button v-for="item in categories" @click="clickbtn(item.name)">{{item.name}}</button>
</div>
</template>
<script>
const cp1n = {
template: "#cpn",
data(){
return{
counter : 0,
categories: [
{id: 'aaa', name: '热门推荐'},
{id: 'bbb', name: '手机数码'},
{id: 'ccc', name: '课程'}
],
}
},
props: {
cmovies: {
type: Array, //类型限定
default: ['aaa', 'xxxx'], //默认值
required: true //是否强制需要
}
},
methods: {
clickbtn(name){
this.$emit('itemclick', name)
}
}
}
//子传父:event
const app = new Vue({
el:"#app",
data:{
message: 'Hello world',
movies: ['aaaa', 'bbbb', 'cccc']
},
components:{
cpn: cp1n
},
methods: {
itemclick(name){
console.log('父组件:' + name)
}
}
})
</script>
</body>
</html>
Component Access
通过索引和名称均可对子组件实现访问,若通过名称访问需要对子组件定义名称。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="../js/vue.js"></script>
</head>
<body>
<div id="app">
<!-- 通过ref给子组件定义名称-->
<cpn ref="aaa" v-bind:cmovies="movies" :cmessage="message" @itemclick="itemclick"></cpn>
<button @click="accesschild"> 访问子组件</button>
</div>
<!-- 子组件模板 -->
<template id="cpn">
<div>
<button v-for="item in categories" @click="clickbtn(item.name)">{{item.name}}</button>
</div>
</template>
<script>
const cp1n = {
template: "#cpn",
data(){
return{
counter : 0,
categories: [
{id: 'aaa', name: '热门推荐'},
{id: 'bbb', name: '手机数码'},
{id: 'ccc', name: '课程'}
],
name: 'name1'
}
},
props: {
cmovies: {
type: Array, //类型限定
default: ['aaa', 'xxxx'], //默认值
required: true //是否强制需要
}
},
methods: {
clickbtn(name){
this.$emit('itemclick', name)
console.log(this.$parent.message)
}
}
}
//子传父:event
const app = new Vue({
el:"#app",
data:{
message: 'Hello world',
movies: ['aaaa', 'bbbb', 'cccc']
},
components:{
cpn: cp1n
},
methods: {
itemclick(name){
console.log('父组件:' + name)
},
//通过索引和名称均可访问子组件
accesschild(){
console.log(this.$children[0].name)
console.log(this.$refs.aaa.name)
}
}
})
</script>
</body>
</html>
Slot
Basic Usuage
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="../js/vue.js"></script>
</head>
<body>
<div id="app">
<cpn ref="aaa">
<!-- 将插槽中的内容替换掉 -->
<button>插槽按钮</button>
<button>插槽按钮2</button>
</cpn>
<cpn ref="aaa1"></cpn>
</div>
<!-- 子组件模板 -->
<template id="cpn">
<div>
<h2> 我是组件</h2>
<p> 我是组件哈哈哈</p>
<!-- 定义插槽 -->
<slot></slot>
<slot><p> 嘿嘿嘿</p></slot>
</div>
</template>
<script>
const cp1n = {
template: "#cpn",
data(){
return{
counter : 0,
categories: [
{id: 'aaa', name: '热门推荐'},
{id: 'bbb', name: '手机数码'},
{id: 'ccc', name: '课程'}
],
name: 'name1'
}
},
props: {
cmovies: {
type: Array, //类型限定
default: ['aaa', 'xxxx'], //默认值
}
},
methods: {
clickbtn(name){
//console.log(name)
this.$emit('itemclick', name)
console.log(this.$parent.message)
}
}
}
//子传父:event
const app = new Vue({
el:"#app",
data:{
message: 'Hello world',
movies: ['aaaa', 'bbbb', 'cccc']
},
components:{
cpn: cp1n
},
methods: {
itemclick(name){
console.log('父组件:' + name)
},
accesschild(){
console.log(this.$children[0].name)
console.log(this.$refs.aaa.name)
}
}
})
</script>
</body>
</html>
Named Slot
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="../js/vue.js"></script>
</head>
<body>
<div id="app">
<cpn ref="aaa">
<!-- 替换掉指定的插槽位置 -->
<button slot="l">插槽按钮</button>
<button slot="r">插槽按钮2</button>
</cpn>
<cpn ref="aaa"></cpn>
</div>
<!-- 子组件模板 -->
<template id="cpn">
<div>
<!-- 定义具名插槽 -->
<slot name="l"><span>左边</span></slot>
<slot name="m"><span>中间</span></slot>
<slot name="r"><span>右边</span></slot>
</div>
</template>
<script>
const cp1n = {
template: "#cpn",
data(){
return{
counter : 0,
categories: [
{id: 'aaa', name: '热门推荐'},
{id: 'bbb', name: '手机数码'},
{id: 'ccc', name: '课程'}
],
name: 'name1'
}
},
props: {
cmovies: {
type: Array, //类型限定
default: ['aaa', 'xxxx'], //默认值
}
},
methods: {
clickbtn(name){
//console.log(name)
this.$emit('itemclick', name)
console.log(this.$parent.message)
}
}
}
//子传父:event
const app = new Vue({
el:"#app",
data:{
message: 'Hello world',
movies: ['aaaa', 'bbbb', 'cccc']
},
components:{
cpn: cp1n
},
methods: {
itemclick(name){
console.log('父组件:' + name)
},
accesschild(){
console.log(this.$children[0].name)
console.log(this.$refs.aaa.name)
}
}
})
</script>
</body>
</html>
Data Access
<!DOCTYPE html>
<!-- 父组件替换插槽内容是,有时有需要访问子组件数据的需求。-->
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="../js/vue.js"></script>
</head>
<body>
<div id="app">
<cpn ref="aaa">
<!-- 获取子组件的属性 -->
<template slot-scope="slot">
<span >{{slot.abc}}--</span>
</template>
<!-- 获取子组件的属性 vue2.5以后可用-->
<div slot-scope="slot">
<span>{{slot.abc}}**</span>
<br>
<span v-for="item in slot.abc">###{{item}}###<br></span>
</div>
</cpn>
</div>
<!-- 子组件模板 -->
<template id="cpn">
<div>
<slot :abc="plang">
<ul>
<li v-for="item in plang">{{item}}</li>
</ul>
</slot>
</div>
</template>
<script>
const cp1n = {
template: "#cpn",
data(){
return{
counter : 0,
categories: [
{id: 'aaa', name: '热门推荐'},
{id: 'bbb', name: '手机数码'},
{id: 'ccc', name: '课程'}
],
name: 'name1',
plang: ['c++', 'js', 'java', 'c', 'python']
}
},
props: {
cmovies: function(){
return {
type: Array, //类型限定
default: ['aaa', 'xxxx'], //默认值
}
}
},
methods: {
clickbtn(name){
//console.log(name)
this.$emit('itemclick', name)
console.log(this.$parent.message)
}
}
}
//子传父:event
const app = new Vue({
el:"#app",
data:{
message: 'Hello world',
movies: ['aaaa', 'bbbb', 'cccc']
},
components:{
cpn: cp1n
}
})
</script>
</body>
</html>
Single File Component
<template>
<div class="deom">
<h2> school name: {{schoolName}}</h2>
<button @click="showName">click</button>
</div>
</template>
<script>
export default {
name: 'School',
data(){
return {
schoolName: "Unisa"
}
},
methods: {
showName(){
alert(this.schoolName)
}
}
}
</script>
<style>
.demo{
background-color: orange;
}
</style>