-
什么是组件? 组件是一个单独功能模块的封装,有属于自己的HTML模板,也应该有属于自己的数据data ,还有methods;组件模板必须有一个根(常用div) -
组件的基本使用:(含语法糖,含模板抽离,含对象抽离)这里是局部组件注册方式
基本步骤就是: 先创建一个组件mycpn,在里面写一个模板 template ,template通过 id 分离到页面上,之后将组件 mycpn 放到 vue实例对象里面的components 属性里面 进行注册,这样就可以在页面中直接使用mycpn这个组件了
```javascript
<body>
<!-- 在这里我们就不使用 Vue.extend()方法创建组件构造器了 -->
<div id="app">
<mycpn></mycpn>
</div>
<template id="cpn">
<!-- 在子组件模板里需要一个根 这个根我们可以搞个div -->
<div>
<h3>这是我创建的第一个组件 组件名为 mycpn 以后就使用这个方式来创建使用组件</h3>
</div>
</template>
<script>
// 注册组件 使用语法糖
const mycpn = {
// 子组件将数据存放在data中 且 data属性为一个函数
// 原因:如果data是一个对象,所有组件都会用一个data,就会产生连锁反应(组件的复用),使用函数每次都会返回新的对象,不会产生影响
data() {
return {
}
},
template: '#cpn',
methods: {},
components: {}
}
const app = new Vue({
el: '#app',
data: {},
methods: {},
components: {
// 对象字面量属性增强写法
mycpn
}
});
</script>
-
全局组件和局部组件 全局组件 使用 Vue.component() 方法 局部组件在vue实例里面创建 components 里面注册 <body>
<div id="app">
<mycpn></mycpn>
<hr>
<mycpn1></mycpn1>
</div>
<template id="cpn">
<div>
<h4>这是全局组件的注册方式</h4>
</div>
</template>
<template id="cpn1">
<div>
<h4>这是局部组件的注册方式</h4>
</div>
</template>
<script>
// 1.全局组件 使用 Vue.component() 方法
Vue.component('mycpn', {
template: '#cpn'
})
// 2.局部组件在vue实例里面创建 components 里面注册
const app = new Vue({
el: '#app',
data: {},
methods: {},
components: {
mycpn1: {
template: '#cpn1'
}
}
// 也可以将 mycpn1 组件抽出去 写成 const mycpn1 = { template: '#cpn1'}
});
</script>
</body>
-
父组件和子组件 按我自己的理解就是:有俩个组件(组件1和组件2),组件 1 ,2 分别有自己的模板template,组件1放到组件2里面注册(components下面) 然后组件2 放到vue实例里面注册。二者都注册完之后,特别注意:组件1要在组件2模板里使用 之后将组件2放到页面上使用时,就可以看到组件1和组件2。这就是所谓的父子组件,其中组件1为组件2的孩子。 <body>
<div id="app">
<mycpn></mycpn>
</div>
<template id="cpn">
<div>
<h3>我是父组件</h3>
<hr>
<!-- 注意: 子组件注册完之后 要在父模板里使用 这样当父组件加载到页面上时才能将子组件显示出来 -->
<cpn1></cpn1>
</div>
</template>
<template id="ccpn">
<div>
<h3>我是子组件</h3>
</div>
</template>
<script>
const cpn1 = {
template: '#ccpn'
}
// 将cpn1 组件放到 mycpn 里面注册 mycpn组件放到 Vue实例里面注册
const mycpn = {
template: '#cpn',
components: {
cpn1
}
}
const app = new Vue({
el: '#app',
data: {},
methods: {},
components: {
mycpn
}
});
</script>
</body>
-
组件中数据的存放问题(data 属性里面存)
-
组件是不能直接访问Vue实例对象里面的数据的 ; -
组件有自己存放数据的地方 data属性 ; -
只不过 data 是一个函数而且返回的是一个对象 对象里面存放数据 -
原因:Vue让每个组件对象都返回一个新的对象,因为如果是同一个对象的,组件在多次使用后会相互影响。 <body>
<div id="app">
<cpn></cpn>
</div>
<template id="cpn">
<div>
<h3>这是组件,现在我需要获取组件的数据</h3>
<span>这是子组件里面的数据:{{students}}</span>
<ul>
<h4>这是获取组件里面的数组对象数据</h4>
<li v-for='items in books'>{{items.name}} {{items.price}}</li>
</ul>
</div>
</template>
<script>
const cpn = {
template: '#cpn',
data() {
return {
// 存放一个对象
students: {
name: '方敬',
age: 22,
height: 180
},
// 存放一个数组对象
books: [{
id: 01,
name: '斗罗大陆',
price: '$180.00'
},
{
id: 02,
name: '斗破苍穹',
price: '$189.00'
},
{
id: 03,
name: '武动乾坤',
price: '$182.00'
}
]
}
}
}
const app = new Vue({
el: '#app',
data: {},
methods: {},
components: {
cpn
}
});
</script>
</body>
-
组件通信—父组件向子组件传递数据 方法: 在子组件中通过 props 来声明接收父组件的数据 props 的值有俩种方式: 方式一:字符串数组,数组中的字符串就是传递时的名称。 方式二:对象,对象可以设置传递时的类型,也可以设置默认值等。
对象方法用的较多(在子组件<mycpn></mycpn> 用v-bind绑定传值 : 子组件的数据=父组件的数据,:mymessage=‘message’ ) 
方式一:字符串数组 <body>
<div id="app">
<mycpn :mymessage='message'></mycpn>
</div>
<template id="cpn">
<div>
<!-- join 将数组转换成字符串 -->
<h4>这是从父组件接收过来的数组数据:{{mymessage.join()}}</h4>
</div>
</template>
<script>
// 父组件向子组件传递数据
// 方法: 在子组件中通过 props 来声明接收父组件的数据
// props 的值有俩种方式:方式一:字符串数组,数组中的字符串就是传递时的名称。
//方式二:对象,对象可以设置传递时的类型,也可以设置默认值等。
const mycpn = {
template: '#cpn',
// 声明需要从父亲接收到的数据
props: ['mymessage']
}
const app = new Vue({
el: '#app',
data: {
// 字符串数组
message: ['方敬是', '傻逼'],
},
methods: {},
components: {
mycpn
}
});
</script>
</body>
方式二:对象 <body>
<div id="app">
<mycpn :cmessage='cmessage'></mycpn>
</div>
<template id="cpn">
<div>
<!-- join 将数组转换成字符串 -->
<h4>这是从父组件接收过来的对象数据: {{cmessage.name}} {{cmessage.age}}</h4>
</div>
</template>
<script>
// 父组件向子组件传递数据
const mycpn = {
template: '#cpn',
// 声明需要从父亲接收到的数据
props: {
cmessage: {
type: Object,
default() {
return {}
}
}
}
}
const app = new Vue({
el: '#app',
data: {
cmessage: {
name: '刺客短裤',
age: 18
}
},
methods: {},
components: {
mycpn
}
});
</script>
</body>
-
组件通信—子组件向父组件传递 数据/事件(通过自定义事件) 在子组件中,通过$emit()来触发事件。 在父组件中,通过v-on来监听子组件事件。 
需求:我们之前做过一个两个按钮+1和-1,点击后修改counter。 我们整个操作的过程还是在子组件中完成,但是之后的展示交给父组件。 这样,我们就需要将子组件中的counter,传给父组件的某个属性,比如total。
<body>
<div id="app">
<!-- v-on 监听自定义事件 -->
<childcpn @increment='totalChange' @decrement='totalChange'></childcpn>
<h3>点击次数 : {{total}}</h3>
</div>
<template id="cpn">
<div>
<h3>计数:{{counter}}</h3>
<button @click='increment'>+</button>
<button @click='decrement'>-</button>
</div>
</template>
<script>
// 子组件向父组件传递 数据/事件 通过自定义事件
// 在子组件中,通过$emit()来触发事件。
// 在父组件中,通过v-on来监听子组件事件。
const childcpn = {
template: '#cpn',
data() {
return {
counter: 0
}
},
methods: {
increment() {
this.counter++
// 在这里使用 $emit 来触发increment事件 并将数据传过去 在父组件中使用v-on来监听子组件事件
this.$emit('increment', this.counter)
},
decrement() {
this.counter--
// 在这里使用 $emit 来触发increment事件 并将数据传过去 在父组件中使用v-on来监听子组件事件
this.$emit('decrement', this.counter)
}
}
}
const app = new Vue({
el: '#app',
data: {
total: 0
},
methods: {
// 监听事件之后 在父组件里来接收数据
totalChange(counter) {
this.total = counter
}
},
components: {
childcpn
}
});
</script>
</body>
-
组件访问—父组件访问子组件($refs,少用 $children) $refs的使用
- $refs和ref指令通常是一起使用的。
- 首先,我们通过ref给某一个子组件绑定一个特定的ID。在父组件模板里,找到子组件的使用并绑上ID。
- 其次,通过this.$refs.ID就可以访问到该组件了。
 <body>
<div id="app">
<childcpn1 ref='child1'></childcpn1>
<childcpn2 ref='child2'></childcpn2>
<button @click='showmessge()'>点击</button>
</div>
<template id="cpn1">
<div>
<h3>我是第一个孩子 哈哈哈哈哈</h3>
</div>
</template>
<template id="cpn2">
<div>
<h3>我是第二个孩子 嘻嘻嘻嘻嘻</h3>
</div>
</template>
<script>
// 通过 $refs 来访问子组件 给子组件绑定 ref 相当于id
const childcpn1 = {
template: '#cpn1',
data() {
return {
message: '方敬是傻逼'
}
},
methods: {
bnClick() {
console.log('......点击了');
}
}
}
const childcpn2 = {
template: '#cpn2',
data() {
return {
message: '方敬不是傻逼'
}
}
}
const app = new Vue({
el: '#app',
data: {},
methods: {
showmessge() {
console.log(this.$refs.child1.message);
// 调用第一个孩子的方法
console.log(this.$refs.child1.bnClick());
console.log(this.$refs.child2.message);
}
},
components: {
childcpn1,
childcpn2
}
});
</script>
</body>
-
组件访问—子组件访问父组件($root 直接访问祖宗,避免多重套娃的连锁反应 少用 $parent)子访问父 在子模板里写操作  -
slot 插槽的基本使用 作用:以让使用者根据自己的需求,决定插槽中插入什么内容 是搜索框,还是文字,还是菜单。由调用者自己来决定 使用: 就是在子组件的模板里添加一个 <slot></slot> 标签 <body>
<div id="app">
<mycpn>
<a href="">百度</a>
</mycpn>
<mycpn>
<h3>这是我替换插槽的内容 哈哈哈哈哈</h3>
</mycpn>
<mycpn></mycpn>
</div>
<template id="cpn">
<div>
<h4>下面是插槽哦,里面的内容可以替换</h4>
<slot>我是插槽哦,我可以被替换成其他哦</slot>
<hr>
</div>
</template>
<script>
// 插槽的作用: 可以让使用者根据自己的需求,决定插槽中插入什么内容 是搜索框,还是文字,还是菜单。由调用者自己来决定
const mycpn = {
template: '#cpn'
}
const app = new Vue({
el: '#app',
data: {},
methods: {},
components: {
mycpn
}
});
</script>
-
具名插槽的使用(这里使用Vue 3 的 v-slot ) 注意: v-slot 是在组件里面添加一个 template 标签 然后在这个标签里写v-slot 并不是在 模板里面写v-slot v-slot: 的语法糖为 #  注:区分俩个template的不同 -
作用域插槽 (父组件替换插槽的标签,但是内容由子组件来提供) 使用场景:父组件向子组件拿数据 我们选择将包含所有插槽prop的对象命名为 slotProps 需求:
子组件中包括一组数据,比如:pLanguages: [‘JavaScript’, ‘Python’, ‘Swift’, ‘Go’,‘C++’] 需要在多个界面进行展示: 某些界面是以水平方向一一展示的, 某些界面是以列表形式展示的, 某些界面直接展示一个数组
这里也使用 Vue3 的 v-slot:default = 'slotProps' 子组件模板template里的 slot 用 :data='子组件里的数据' 来绑定数据  <body>
<div id="app">
<mycpn>
<!-- 水平显示 -->
<template v-slot:default='slotProps'>
<!-- join() 将数组转换成字符串 -->
<span>{{slotProps.data}}</span>
<span>{{slotProps.data.join()}}</span>
</template>
</mycpn>
<mycpn>
<!-- 列表形式显示 -->
<template v-slot:default='slotProps'>
<ul>
<li v-for='item in slotProps.data'>{{item}}</li>
</ul>
</template>
</mycpn>
</div>
<template id="cpn">
<div>
<slot :data='pLanguages'></slot>
<hr>
</div>
</template>
<script>
const mycpn = {
template: '#cpn',
data() {
return {
pLanguages: ['JavaScript', 'Python', 'Swift', 'Go', 'C++']
}
}
}
const app = new Vue({
el: '#app',
data: {},
methods: {},
components: {
mycpn
}
});
</script>
</body>
 -
编译的作用域 父组件模板的所有东西都会在父级作用域内编译; 子组件模板的所有东西都会在子级作用域内编译; 就近原则,往上查 。 在谁家用谁的东西,在vue模板就去找vue实例 ,组件的模板找自己的作用域。 -
不懂就看官方文档:https://v3.cn.vuejs.org/
|