任何人在面对复杂问题的处理方式
组件化开发也是类似的思想
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Titletitle>
head>
<body>
<div id="app">
<my-cpn>my-cpn>
div>
<my-cpn>my-cpn>
<script src="../js/vue.js">script>
<script>
//创建构造器对象
const cpnC = Vue.extend({
template: `
我是标题
我是内容qwq
我是内容23333
`
})
//注册组件
Vue.component('my-cpn', cpnC)
let app = new Vue({
el: '#app',//用于挂载要管理的元素
data: {
//用于定义数据
}
})
script>
body>
html>
全局组件: 组件可以在多个Vue实例中使用
上个案例的代码创建的组件就是全局组件
局部组件: 只能在指定的Vue实例里面使用
注意: 其实在开发中我们一般只创建一个Vue实例
Vue实例中,除了我们之前学习的data,computed,methods属性之外,现在再加上一个属性,components
属性,在这里面定义的组件只能在这个Vue实例中使用
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Titletitle>
head>
<body>
<div id="app">
<cpn>cpn>
div>
<script src="../js/vue.js">script>
<script>
const cpnC = Vue.extend({
//这里的``符号是es6特有的,这个符号括起来的字符串允许换行
template: `就是这么自信!
`
})
let app = new Vue({
el: '#app',//用于挂载要管理的元素
data: {
//用于定义数据
},
components: {
//key是标签名, value是组件构造器
cpn: cpnC
}
})
script>
body>
html>
子组件就是在父组件里面注册的组件
除非子组件也在实例中注册了,不然子组件是无法单独引用的
不多bb,直接上例子
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Titletitle>
head>
<body>
<div id="app">
<cpn2>cpn2>
<cpn1>cpn1>
div>
<script src="../js/vue.js">script>
<script>
const cpnC1 = Vue.extend({
template: `我是儿子!
`,
});
const cpnC2 = Vue.extend({
template: `
我是亲爹!
`,
components: {
cpn1: cpnC1
}
});
//这个其实也是一个组件,这个是最顶部的组件,叫做Root组件
let app = new Vue({
el: '#app',//用于挂载要管理的元素
data: {
},
components: {
cpn2: cpnC2,
}
})
script>
body>
html>
当然,要是父组件中没有注册子组件,那么要想父组件中的子组件能够正常显示,就必须在Root组件中声明子组件
注册全局组件的语法糖
Vue.component('cpn1', {
template: `
我是你爹
`
})
//直接这样写组件就注册好了,可以直接在Vue实例中调用
第二个参数其实是调用了extend()方法的,只不过被隐藏了
注册局部组件的语法糖
components: {
'cpn1': {
template: `
<div>
<h2>我是你爹h2>
div>
` `
}
如果像上面那样将HTML模板写在了JS里面,那么其实就是很不好看,所以我们要抽离
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Titletitle>
head>
<body>
<div id="app">
<cpn1>cpn1>
div>
<script type="text/x-template" id="cpn">
<div>
<h2>我是你爹</h2>
</div>
script>
<script src="../js/vue.js">script>
<script>
let app = new Vue({
el: '#app',
components: {
'cpn1': {
template: '#cpn'
}
}
})
script>
body>
html>
标签 (推荐使用)
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Titletitle>
head>
<body>
<div id="app">
<cpn1>cpn1>
div>
<template id="cpn">
<div>
<h2>我是你爹h2>
div>
template>
<script src="../js/vue.js">script>
<script>
let app = new Vue({
el: '#app',
data: {
},
components: {
'cpn1': {
template: '#cpn'
}
}
})
script>
body>
html>
因为我们的组件是要复用的,我们在不同的页面也能会有多个这个组件,为了保证data的数据不共享,所以我们将data作为一个函数对象返回,这样,每一个组件实例所拥有的data对象都是相互独立的,他们的数据也是独立的,不会相互影响
我们可以想象,每个组件实例指向一个data对象, 每个return的到的data对象所处的推空间的地址是不一样的,所以他们是独立的
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Titletitle>
head>
<body>
<div id="app">
<cpn1>cpn1>
<cpn1>cpn1>
<cpn1>cpn1>
div>
<template id="cpn">
<div>
<h2>计数器{
{count}}h2>
<button @click="increase">+button>
<button @click="decrease">-button>
div>
template>
<script src="../js/vue.js">script>
<script>
let app = new Vue({
el: '#app',
data: {
},
components: {
'cpn1': {
template: '#cpn',
data() {
return {
count: 0
}
},
methods: {
increase() {
this.count++;
},
decrease() {
this.count--;
}
}
}
}
})
script>
body>
html>
父组件中data的数据正常下是子组件是无法调用的,所以我们可以用props
第一种写法就是props后面写一个数组 props: ['新的变量名1','新的变量名2']
可读性比较差,比较少用
第二种写法就是使用对象的写法
支持以下类型
下面我演示了一个父传子传子
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Titletitle>
head>
<body>
<div id="app">
<cpn :movies="movies">cpn>
div>
<template id="cpn">
<cpn1 :movies="movies">cpn1>
template>
<template id="cpn1">
<div>
<ul>
<li v-for="item in movies">{
{item}}li>
ul>
div>
template>
<script src="../js/vue.js">script>
<script>
const cpn1 = Vue.component('cpn1', {
template: "#cpn1",
props: {
movies: Array
}
})
const cpn = {
template: `#cpn`,
props: {
//这种是简写的方法
//movies: Array
movies: {
//我们在这里可以定义很多东西
type: Array,
//对象是一个对象2或者数组的时候,默认值必须是一个函数
default() {
return ["我是你爹"]
}
}
},
component: {
cpn1
}
}
let app = new Vue({
el: '#app',//用于挂载要管理的元素
data: {
//用于定义数据
movies: ['海王', '海贼王', '海尔兄弟']
},
components: {
cpn
}
})
script>
body>
html>
如果在props中取名是用驼峰命名法取名,那么在v-bind的时候就会有问题, **直接写驼峰标识系统就不认,将大写的那个地方换成小写,同时前面加上一个-
符号就可以了 **
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Titletitle>
head>
<body>
<div id="app">
<cpn :c-info="info">cpn>
div>
<template id="cpn">
<h2>{
{cInfo}}h2>
template>
<script src="../js/vue.js">script>
<script>
const cpn = {
template: '#cpn',
props: {
cInfo: {
type: Object,
default() {
return {
name: '你爹'};
}
}
}
}
let app = new Vue({
el: '#app',//用于挂载要管理的元素
data: {
//用于定义数据
info: {
name: 'father',
age:20,
height: 1.88
}
},
components: {
cpn
}
})
script>
body>
html>
我们需要通过自定义事件来完成子组件中的data传递到父组件
在子组件中
通过$emit()
来触发事件
在父组件中
通过v-on
来监听子组件事件
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Titletitle>
head>
<body>
<div id="app">
<cpn @btn-click="cpnClick">
cpn>
div>
<template id="cpn">
<div>
<button v-for="item in categories" @click="btnClick(item)">{
{item.name}}button>
div>
template>
<script src="../js/vue.js">script>
<script>
const cpn = {
template: '#cpn',
data(){
return {
categories: [{
id: 1,
name: '热门推荐'
},{
id: 2,
name: '手机数码'
},{
id: 3,
name: '家用家电'
},{
id: 4,
name: '电脑办公'
}],
id: -1
}
},
methods: {
btnClick(item){
//自定义事件传递到父组件
this.$emit('btn-click',item.id);
}
}
}
let app = new Vue({
el: '#app',
data: {
info: {
name: 'father',
age:20,
height: 1.88
}
},
components: {
cpn
},
methods: {
//这里就是我们在父组件中获取子组件中data的方法的定义
cpnClick(e) {
console.log(e);
}
}
})
script>
body>
html>
注意: 我们在子组件中,不要修改传来的父组件的值,这样是不对的,我们要通过计算属性,或者一个新的data属性来接受这个值,然后再对计算属性或者data修改就行了
我们这里要做一个计时器的案例,组件里面只有两两个按钮,总数在总页面中创建
所以我们在总页面中创建一个num1
, 将这个初值传进子组件,然后子组件中定义一个num2
,num2的初值等于num1,然后子组件中的两个按钮控制num2的值的修改,将num2的值实时传回父组件,并显示
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Titletitle>
head>
<body>
<div id="app">
<cpn @btn-click="cpnClick" :num1="num1">
cpn>
<h2>总数: {
{num1}}h2>
div>
<template id="cpn">
<div>
<button @Click="btnClickAdd">+button>
<button @Click="btnClickDec">-button>
div>
template>
<script src="../js/vue.js">script>
<script>
const cpn = {
template: '#cpn',
data(){
return {
num2: this.num1
}
},
props: {
num1: Number
},
methods: {
btnClickAdd(){
//自定义事件传递到父组件
this.num2++;
this.$emit('btn-click',this.num2);
},
btnClickDec(){
//自定义事件传递到父组件
this.num2--;
this.$emit('btn-click',this.num2);
}
}
}
let app = new Vue({
el: '#app',//用于挂载要管理的元素
data: {
//用于定义数据
num1: 0
},
components: {
cpn
},
methods: {
cpnClick(e) {
this.num1 = e;
}
}
})
script>
body>
html>
除了data,properties,components等属性之外,还有一种特别好用的属性,watch
,它可以监听data中数据的改变,然后做出响应的行为
et app = new Vue({
el: '#app',//用于挂载要管理的元素
data: {
//用于定义数据
num1: 0
},
components: {
cpn
},
methods: {
cpnClick(e) {
this.num1 = e;
}
},
watch: {
//XXX就是你想要监听的变量名,下面这个newValue就是watch监听到的新改变的值,oldValue懂得都懂
XXX(newValue,oldValue) {
方法体
}
}
})
有时候,我的父组件不只是想要和子组件传递数据,有时候我想要调用子组件里面的方法,那么我就用上面这个进行访问
父组件访问子组件
$children
(返回所有的子组件对象的数组) (较少使用)$refs
(返回指定的子组件对象,需在组件的标签上面加上ref属性 ) (如果直接this. r e f s , 那 么 他 会 返 回 所 有 的 带 有 r e f 属 性 的 组 件 的 对 象 的 对 象 , ∗ ∗ 所 以 我 们 一 般 使 用 的 时 候 都 是 ‘ t h i s . refs,那么他会返回所有的带有ref属性的组件的对象的对象 , **所以我们一般使用的时候都是`this. refs,那么他会返回所有的带有ref属性的组件的对象的对象,∗∗所以我们一般使用的时候都是‘this.refs.xxx`xxx就是我们在组件标签上面定义的ref的名字 **)
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Titletitle>
head>
<body>
<div id="app">
<cpn @btn-click="cpnClick" :num1="num1">
cpn>
<cpn @btn-click="cpnClick" :num1="num1" ref="aaa">
cpn>
<cpn @btn-click="cpnClick" :num1="num1" ref="bbb">
cpn>
<h2 @click="btnClick">总数: {
{num1}}h2>
div>
<template id="cpn">
<div>
<button @Click="btnClickAdd">+button>
<button @Click="btnClickDec">-button>
div>
template>
<script src="../js/vue.js">script>
<script>
const cpn = {
template: '#cpn',
data(){
return {
num2: this.num1
}
},
props: {
num1: Number
},
methods: {
btnClickAdd(){
//自定义事件传递到父组件
this.num2++;
this.$emit('btn-click',this.num2);
},
btnClickDec(){
//自定义事件传递到父组件
this.num2--;
this.$emit('btn-click',this.num2);
}
}
}
let app = new Vue({
el: '#app',//用于挂载要管理的元素
data: {
//用于定义数据
num1: 0
},
components: {
cpn
},
methods: {
cpnClick(e) {
this.num1 = e;
},
btnClick() {
//这里打印的就是子组件数组(包含了所有的子组件)
console.log(this.$children);
//这个不行,这个只能打印指定的子组件数组,当指定了具体哪一个的时候,直接返回一个组件对象
console.log(this.$refs.aaa);
}
}
})
script>
body>
html>
开发中不建议使用,了解即可 一个是访问父组件,一个是访问根组件
组件的插槽
怎样封装合适呢?
插槽的基本使用
在template里面加上slot
标签
想要slot有默认值,就在slot标签里面加上一些标签
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Titletitle>
head>
<body>
<div id="app">
<cpn>
<button>按钮button>
cpn>
<cpn>我是你爹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 cpn = {
template: '#cpn'
}
let app = new Vue({
el: '#app',//用于挂载要管理的元素
data: {
//用于定义数据
message: '我是你爹!'
},
components: {
cpn
}
})
script>
body>
html>
当我们定义了多个插槽的时候,如果没有命名,那么我们在自定义组件的时候,我们自定义的内容就会在所有插槽里面显示出来,这不合理,所以我们要给插槽命名
**那么怎么起名呢? 给slot标签一个name属性
, 在自定义的时候,标签加上slot
属性 **
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Titletitle>
head>
<body>
<div id="app">
<cpn>
我是你爹
<span slot="aa">我是自定义的1span>
<span slot="bb">我是自定义的2span>
<span slot="cc">我是自定义的3span>
cpn>
div>
<template id="cpn">
<div>
<slot name="aa"><p>我是1p>slot>
<slot name="bb"><p>我是2p>slot>
<slot name="cc"><p>我是3p>slot>
<slot><p>我是默认p>slot>
div>
template>
<script src="../js/vue.js">script>
<script>
const cpn = {
template: '#cpn'
}
let app = new Vue({
el: '#app',//用于挂载要管理的元素
data: {
//用于定义数据
message: '我是你爹!'
},
components: {
cpn
}
})
script>
body>
html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Titletitle>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
head>
<body>
<div id="app">
<cpn v-show="isShow">cpn>
div>
<template id="cpn">
<div>
<h2>哈哈哈我是组件h2>
<button v-show="isShow">就这?button>
div>
template>
<script src="../js/vue.js">script>
<script>
let app = new Vue({
el: '#app',//用于挂载要管理的元素
data: {
//用于定义数据
isShow: true
},
components: {
cpn: {
template: "#cpn",
data() {
return {
isShow: false
}
}
}
}
})
script>
body>
html>
注意: 凡是在div#app 里面写的所有的数据,都是引用Vue实例的数据
总结一句话就是: 父组件替换插槽的标签,但是内容由子组件来提供。
核心代码: v-scpoe = "自定义的引用时的名字"
和 v-slot:组件的名字 = "自定义的引用时的名字"
现在我有一个需求
<html lang="en" xmlns:v-slot="http://www.w3.org/1999/XSL/Transform" xmlns:slot-scope="">
<head>
<meta charset="UTF-8">
<title>Titletitle>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
head>
<body>
<div id="app">
<cpn>cpn>
<cpn >
<template slot-scope="message">
<span>{
{message.data.join(' - ')}}span>
template>
cpn>
<br/>
<cpn>
<template v-slot="hehe">
<span>{
{hehe.data}}span>
template>
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>
let app = new Vue({
el: '#app',//用于挂载要管理的元素
data: {
//用于定义数据
},
components: {
cpn: {
template: "#cpn",
data() {
return {
pLanguages: ['JavaScript', 'Python', 'Swift', 'Go', 'C++']
}
}
}
}
})
script>
body>
html>