Vue学习-组件化开发
认识组件
提供了一种抽象、让我们可以开发出一个个独立可复用的组件来构造我们的应用
任何的应用都会被抽象成一颗组件树
组件的使用三个步骤:
- 创建组件构造器
- 注册组件
- 使用组件
基本使用过程
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<my-cpn></my-cpn>
</div>
<script src="../js/vue.js"></script>
<script>
// 1. 创建组件构造器对象
const cpnC = Vue.extend({
template: `
<div>
<h2>我是标题</h2>
<p>我是内容,哈哈哈</p>
<p>我是内容,呵呵呵</p>
</div>
`,
});
// 2. 注册组件
Vue.component('my-cpn', cpnC);
const app = new Vue({
el: '#app',
data: {
},
methods: {
}
})
</script>
</body>
</html>
全局组件和局部组件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<cpn></cpn>
</div>
<div id="app2">
<cpn></cpn>
</div>
<script src="../js/vue.js"></script>
<script>
// 1. 创建组件构造器
const cpnC = Vue.extend({
template: `
<div>
<h2>我是标题</h2>
<p>我是内容,阿哈哈哈</p>
</div>
`
});
// 2. 注册组件(全局组件)
// 全局组件可以在多个vue实例下面使用
// Vue.component('cpn', cpnC);
const app = new Vue({
el: '#app',
data: {
},
methods: {
},
// 注册局部组件
components: {
cpn: cpnC
}
});
const app2 = new Vue({
el: '#app2',
data: {}
})
</script>
</body>
</html>
父组件和子组件的区分
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<cpn2></cpn2>
<hr>
<!-- 不会生效 -->
<cpn1></cpn1>
</div>
<script src="../js/vue.js"></script>
<script>
const cpnC1 = Vue.extend({
template: `
<div>
<h2>title</h2>
<p>content: hahahaha</p>
</div>
`
});
const cpnC2 = Vue.extend({
template:
`
<div>
<h2>title</h2>
<p>content: hehehehe</p>
<cpn1></cpn1>
</div>
`,
components: {
cpn1: cpnC1
}
})
const app = new Vue({
el: '#app',
data: {
},
components: {
cpn2: cpnC2
}
})
</script>
</body>
</html>
注册组件的语法糖写法
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<cpn1></cpn1>
<cpn2></cpn2>
</div>
<script src="../js/vue.js"></script>
<script>
// 1. 全局组件注册的语法糖
// 1. 创建组件构造器
// const cpn1 = Vue.extend()
// 2. 注册组件
Vue.component('cpn1', {
template:
`
<div>
<h2>title</h2>
<p>content: hahahaha</p>
</div>
`
})
const app = new Vue({
el: '#app',
data: {
},
methods: {
},
components: {
cpn2: {
template:
`
<div>
<h2>title</h2>
<p>content: hehehe</p>
</div>
`
}
}
})
</script>
</body>
</html>
组件模板的抽离写法
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<cpn1></cpn1>
<cpn2></cpn2>
</div>
<!-- 方法1 -->
<script type="text/x-template" id="cpn1">
<div>
<h2>title</h2>
<p>content: hahahaha</p>
</div>
</script>
<!-- 方法2 -->
<template id="cpn2">
<div>
<h2>title</h2>
<p>content: hehehehe</p>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
Vue.component('cpn1', {
template: '#cpn1'
})
const app = new Vue({
el: '#app',
data: {
},
methods: {
},
components: {
cpn2: {
template: '#cpn2'
}
}
})
</script>
</body>
</html>
为什么组件data必须是函数
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<cpn></cpn>
<cpn></cpn>
<cpn></cpn>
<cpn></cpn>
</div>
<template id="cpn">
<div>
<h2>当前计数: {{ counter }}</h2>
<button @click="increment">+</button>
<button @click="decrement">-</button>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
Vue.component('cpn', {
template: '#cpn',
data() {
// 每次都返回一个新的对象,多个组件之间的数组不会相互影响
return {
counter: 0
}
},
methods: {
increment() {
this.counter++;
},
decrement() {
this.counter--;
}
},
})
const app = new Vue({
el: '#app',
data: {
}
})
</script>
</body>
</html>
父子组件通信
父传子-props
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<cpn v-bind:cmovies="movies" :cmessage="message"></cpn>
</div>
<template id="cpn">
<div>
<p>{{ cmovies }}</p>
<h2>{{ cmessage }}</h2>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
const cpn = {
template: '#cpn',
// props方式1:数组
// props: [
// 'cmovies',
// 'cmessage'
// ],
// props方式2: 对象
props: {
// 1. 类型限制
// cmovies: Array,
// cmessage: String
// 2. 提供一些默认值
cmessage: {
type: String,
default: 'default messgae'
},
cmovies: {
type: Array,
default: [],
// 必须传值
required: true
}
// 支持多种数据类型以及自定义验证
}
}
const app = new Vue({
el: '#app',
data: {
movies: ['aaa', 'bbb', 'ccc'],
message: 'hello'
},
components: {
cpn
}
})
</script>
</body>
</html>
父传子-驼峰标识
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<!-- 这里不能使用驼峰 -->
<cpn :c-info="info"></cpn>
</div>
<template id="cpn">
<div>
{{ cInfo }}
</div>
</template>
<script src="../js/vue.js"></script>
<script>
const cpn = {
template: '#cpn',
props: {
cInfo: {
type: Object,
default() {
return {}
}
}
}
}
const app = new Vue({
el: '#app',
data: {
info: {
name: 'xx',
age: 18,
height: 1.88
}
},
components: {
cpn
}
})
</script>
</body>
</html>
子传父-自定义事件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<cpn v-on:item-click="cpnClick"></cpn>
</div>
<template id="cpn">
<div>
<button v-for="item in categories" :key="item.id" @click="handleClick(item)">{{ item.name }}</button>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
const cpn = {
template: '#cpn',
data() {
return {
categories: [
{ id: 'aaa', name: '111' },
{ id: 'bbb', name: '222' },
{ id: 'ccc', name: '333' },
{ id: 'ddd', name: '444' },
{ id: 'eee', name: '555' }
]
}
},
methods: {
handleClick(item) {
this.$emit('item-click', item);
}
}
}
const app = new Vue({
el: '#app',
data: {
},
components: {
cpn
},
methods: {
cpnClick(item) {
console.log(item.name);
}
}
})
</script>
</body>
</html>
父子组件通信-结合双向绑定
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<cpn :number1="num1" :number2="num2"
@num1change="num1change"
@num2change="num2change"></cpn>
</div>
<template id="cpn">
<div>
<h2>props: {{number1}}</h2>
<h2>data: {{dnumber1}}</h2>
<!-- <input type="text" v-model="dnumber1"> -->
<input type="text" :value="dnumber1" @input="num1Input">
<h2>props: {{number2}}</h2>
<h2>data: {{dnumber2}}</h2>
<!-- <input type="text" v-model="dnumber2"> -->
<input type="text" :value="dnumber2" @input="num2Input">
</div>
</template>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
num1: 1,
num2: 0,
},
components: {
cpn: {
template: '#cpn',
props: {
number1: Number,
number2: Number
},
data() {
return {
dnumber1: this.number1,
dnumber2: this.number2
}
},
methods: {
num1Input(event) {
this.dnumber1 = event.target.value;
this.$emit('num1change', this.dnumber1);
this.dnumber2 = this.number1 * 100;
this.$emit('num2change', this.dnumber2);
},
num2Input() {
this.dnumber2 = event.target.value;
this.$emit('num2change', this.dnumber2);
this.dnumber1 = this.dnumber2 / 100;
this.$emit('num1change', this.dnumber1);
}
}
}
},
methods: {
num1change(value) {
this.num1 = parseInt(value);
},
num2change(value) {
this.num2 = parseInt(value);
}
}
})
</script>
</body>
</html>
watch实现
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<cpn :number1="num1" :number2="num2"
@num1change="num1change"
@num2change="num2change"></cpn>
</div>
<template id="cpn">
<div>
<h2>props: {{number1}}</h2>
<h2>data: {{dnumber1}}</h2>
<input type="text" v-model="dnumber1">
<h2>props: {{number2}}</h2>
<h2>data: {{dnumber2}}</h2>
<input type="text" v-model="dnumber2">
</div>
</template>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
num1: 1,
num2: 0,
},
components: {
cpn: {
template: '#cpn',
props: {
number1: Number,
number2: Number
},
data() {
return {
dnumber1: this.number1,
dnumber2: this.number2
}
},
watch: {
dnumber1(newValue) {
this.dnumber2 = newValue * 100;
this.$emit('num1change', newValue);
},
dnumber2(newValue) {
this.dnumber1 = newValue / 100;
this.$emit('num2change', newValue);
}
}
}
},
methods: {
num1change(value) {
this.num1 = parseInt(value);
},
num2change(value) {
this.num2 = parseInt(value);
}
}
})
</script>
</body>
</html>
父访问子-children-refs
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<cpn></cpn>
<cpn></cpn>
<cpn ref="aaa"></cpn>
<cpn></cpn>
<button @click="btnClick">按钮</button>
</div>
<template id="cpn">
<div>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
},
components: {
cpn: {
template: '#cpn',
methods: {
showMessage() {
console.log('showMessage');
}
}
}
},
methods: {
btnClick() {
// 1. 一般不使用,当html结构发生变化的时候,下标要重新获取
// console.log(this.$children[0].showMessage());
// 2. refs方法
console.log(this.$refs.aaa);
}
}
})
</script>
</body>
</html>
子访问父-parent-root
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<cpn></cpn>
</div>
<template id="cpn">
<div>
<h2>我是子组件</h2>
<ccpn></ccpn>
<button @click="btnclick">按钮</button>
</div>
</template>
<template id="ccpn">
<div>
<h2>我是子子组件</h2>
<button @click="ccbtnclick">子子按钮</button>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
},
components: {
cpn: {
template: '#cpn',
data() {
return {
name: '我是cpn组件的name'
}
},
methods: {
btnclick() {
console.log(this.$parent);
}
},
components: {
ccpn: {
template: '#ccpn',
methods: {
ccbtnclick() {
console.log(this.$parent);
console.log(this.$parent.name);
console.log(this.$root);
}
}
}
}
}
}
})
</script>
</body>
</html>
插槽
插槽的基本使用
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<cpn>
<button>按钮</button>
</cpn>
<cpn>
<span>哈哈哈</span>
</cpn>
<cpn>
<i>呵呵呵</i>
</cpn>
<cpn>
</cpn>
</div>
<template id="cpn">
<div>
<h2>我是组件</h2>
<p>我是组件,哈哈哈</p>
<slot>
<button>默认值</button>
</slot>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
},
components: {
cpn: {
template: '#cpn',
}
}
})
</script>
</body>
</html>
具名插槽的使用
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<cpn>
<span slot="left">返回</span>
<span slot="center">标题</span>
</cpn>
</div>
<template id="cpn">
<div>
<h2>我是组件</h2>
<p>我是组件,哈哈哈</p>
<slot name="left"><span>左边</span></slot>
<slot name="center"><span>中间</span></slot>
<slot name="right"><span>右边</span></slot>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
},
components: {
cpn: {
template: '#cpn',
}
}
})
</script>
</body>
</html>
编译作用域的概念
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<cpn v-show="isShow"></cpn>
</div>
<template id="cpn">
<div>
<h2>我是子组件</h2>
<p>我是内容,哈哈哈</p>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
isShow: true
},
components: {
cpn: {
template: '#cpn',
data() {
return {
isShow: false
}
}
}
}
})
</script>
</body>
</html>
作用域插槽的使用
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<!-- 父组件替换插槽的标签,但是内容由子组件来提供 -->
<div id="app">
<cpn></cpn>
<cpn>
<template slot-scope="slot">
<!-- <span v-for="item in slot.data">{{item}}-</span>
-->
<span>{{slot.data.join('-')}}</span>
</template>
</cpn>
<cpn></cpn>
<cpn></cpn>
</div>
<template id="cpn">
<div>
<slot :data="pLanguages">
<ul>
<li v-for="item in pLanguages">{{item}}</li>
</ul>
</slot>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
},
components: {
cpn: {
template: '#cpn',
data() {
return {
pLanguages: ['js', 'java', 'c++', 'go']
}
}
}
}
})
</script>
</body>
</html>