1.认识组件化开发
组件化开发思想
组件化思想的特点:标准、分治、复用、组合
组件: 一个独立封装的带有 html+css+js 的完整内容
如何书写(vue 的规则)
=> 书写一个对象表示一个组件
=> 注意:
-> 每一个组件对象必须要有一个 template 属性, 表示该组件的 html 结构部分
-> 每一个组件可以有一个 name 属性, 表示该组件的内部名称, 是在 Vue 底层遍历组件的时候使用的
-> 每一个组件可以书写的其他配置项(内容) 和 Vue 实例一模一样
-> **每一个组件自己准备的 数据(属性,函数,侦听器,计算属性,...) 只能在自己组件内使用**
书写一个简单的组件对象
const comOne = {
template: `
<div>
<p>我是组件一</p>
<p>{{ message }}</p>
<button @click="handler"></button>
<p v-xhl>{{ msg }}</p>
</div>
`,
data () {
return {
message: 'hello comOne'
}
},
methods: {
handler () {}
},
computed: {
msg () { return this.message.split('').reverse().join('') }
},
directives: {
xhl: {
mounted (el) { el.style.color = 'red' }
}
}
}
2.组件化开发步骤
+步骤
1.准备一个组件对象
2.把组件对象挂到 vue 实例身上开始使用
+ 步骤
1. 准备一个组件对象
2. 把组件对象挂载到 vue 实例身上开始使用
2-1. 挂载到全局: 谁都可以使用
2-2. 挂载到私有:当前实例或者当期组件才能使用
=>使用组件:可以在使用的位置直接把组件名称当做标签来使用即可
+定义数组的注意
=> 当你在书写组件使用名称的时候,尽量使用两个及两个以上单词组成
<div id="app">
<p>组件化开发</p>
<p>{{ message }}</p>
-----------------------------
<gx-xhl></gx-xhl>
-----------------------------
<gx-two></gx-two>
</div>
<script src="./vue.global.js"></script>
<script>
const { createApp } = Vue
//准备一个组件对象
const comOne = {
template:`
<div>
<p>{{message}}</p>
</div>
`,
data () {
return { message: 'hello comOne' }
}
}
const comOne = {
template:`
<div>
<p>{{message}}</p>
</div>
`,
data () {
return { message: 'hello comOne' }
}
}
// 2.把组件对象挂载到 Vue 实例上
// 2-1. 挂载到全局上
// 语法:vue 实例.component('组件使用名称',组件对象)
// 组件使用名称:在 Vue 实例内使用该组件时候的名称
// app.component('gx-xhl',comOne)
//2-2. 挂载到一个私有组件
//直接把组件挂载到 Vue 实例上
//在 Vue 的实例上身上添加一个 components 配置项,表示该使用实例使用的组件
const app = crateApp({
data () {
return { message: 'hello vue 实例'}
},
// 配置组件
components: {
// gx-xhl 就是改组件使用名称
// comOne 就是该组件的真实内容
'gx-xhl': comOne,
'gx-two': comTwo
}
}).mount('#app')
</script>
3.模拟模块化使用组件
index.html
<div id="app">
<p>组件化开发</p>
<p>{{ message }}</p>
------------------------------------------
<xhl-one></xhl-one>
------------------------------------------
<xhl-two></xhl-two>
</div>
<script src="../vue.global.js"></script>
<script type="module">
import comOne from './comOne.js'
import comTwo from './comTwo.js'
const { createApp } = Vue
createApp({
data () {
return {
message: 'hello Vue 实例'
}
},
components: {
'xhl-one': comOne,
'xhl-two': comTwo
}
}).mount('#app')
</script>
comOne.js
import comThree from './comThree.js'
// 我是一号组件
const template = `
<div>
<p>{{ message }}</p>
********************************
<xhl-three></xhl-three>
********************************
</div>
`
const component = {
template,
data () {
return {
message: 'hello comOne'
}
},
components: {
'xhl-three': comThree
}
}
// 导出该组件
export default component
comThree.js
// 我是一号组件
const template = `
<div>
<p>{{ message }}</p>
</div>
`
const component = {
template,
data () {
return {
message: 'hello comThree'
}
}
}
// 导出该组件
export default component
comTwo.js
// 我是一号组件
const template = `
<div>
<p>{{ message }}</p>
</div>
`
const component = {
template,
data () {
return {
message: 'hello comTwo'
}
}
}
// 导出该组件
export default component
输出结果
4.组件间通信
+分类
=> 父传子:讲父组件的数据给到子组件内
=> 子传父: 将子组件的数据给到父组件内
=> 非父子: 只要不是父子关系都可以
+方式方法:
1.父传子:props down(属性下发)
2.子传父:events up(事件上传)
3. 非父子:
=> 发布订阅模式(global event bus - 全局事件总线)
=> inject / previde
=> $parent / $children
=> $refs
=> vuex - vue 提供的核心插件的一部分(Vue@2 比较多用)
=> pinia - vue 提供的核心插件的一部分(Vue@3 比较多用)
=> slot - 插槽
父传子
+将父组件的数据传递给子组件使用
+ 方法 :props down
关键点
1.组件内的配置项可以书写一个叫做 props的配置项(基础用法,是一个数组)
=> 读取的是改组件使用时候标签名称上的所有属性
2. 组件内的 props 接受的数据可以在组件的 html 结构内使用
=> 组件被使用的位置是父组件的html结构,可以动态绑定父组件的数据方式:
+在组件内以 props 的方法来接受组件标签名上的属性
+在组件标签名上动态绑定父组件的数据
组件内的 props 属性值
=> 可以是一个数组, 用到哪一个接受哪一个
=> 可以是一个对象
=> 对象内的 key 是你要接受的数据
=> 对象内的 value 是你对该数据的限制
注意: 当你的父组件向子组件传递复杂数据类型的时候, 传递的是数据地址
=> 在子组件内改变的时候, 会同时改变父组件内的数据
=> Vue 推荐我们遵循单向数据流
=> 当你想修改子组件内接受的复杂数据类型的时候, 建议你告诉父组件你想改, 让父组件改变原始数据去
父传子
<div id="app">
<p v-bind:s="message">Vue 的生命周期顺序</p>
<p>{{ message }}</p>
<button @click="message += '!'">按钮</button>
<!-- ------------------------ -->
<!-- <xhl-one :s="message" a="100" b="300"></xhl-one> -->
<!-- <xhl-one s="我是 Vue 实例内定义的 message 数据" a="100" b="300"></xhl-one> -->
<!-- <xhl-one b="500"></xhl-one> -->
<!-- ------------------------
<xhl-two :n="num" :m="num"></xhl-two>
<xhl-two :n="num" :z="num"></xhl-two>
<xhl-two :n="num"></xhl-two> -->
------------------------
<xhl-three :n="num" :s="message"></xhl-three>
</div>
<script src="./vue.global.js"></script>
<script>
const comOne = {
template: `
<div>
one 组件 - 在这里使用到 Vue 实例内定义的 message 变量的数据
<p>{{ a }} --- {{ b }} --- {{ s }}</p>
</div>
`,
data () {
return {}
},
methods: {},
props: [ 'a', 'b', 's' ]
}
const comTwo = {
template: `
<div>two 组件 </div>
<p>n : {{ n }}</p>
<p>m : {{ m }}</p>
<p>z : {{ z }}</p>
`,
props: {
n: {
required: true,
type: Number
},
m: {
type: Number,
default: 0
},
z: {
type: Number,
default (param) {
return 999
}
}
}
}
const comThree = {
template: `
<div>
three 组件
<p>n : {{ n }}</p>
<p>s : {{ s }}</p>
</div>
`,
props: [ 'n', 's' ]
}
const { createApp } = Vue
createApp({
data () {
return {
message: '我是 Vue 实例内定义的 message 数据',
num: 100
}
},
components: {
'xhl-one': comOne,
'xhl-two': comTwo,
'xhl-three': comThree,
}
}).mount('#app')
</script>
子传父
<div id="app">
<p>Vue 的生命周期顺序</p>
<p>{{ str }}</p>
<!--
当该 div 发生了 click 行为, 会触发 handler 函数
handler 函数, 是 Vue 实例内准备的函数
-->
<!-- <div @click="handler"></div> -->
<!--
当该 div 发生了 abcd 行为, 会触发 handler 函数
-->
<!-- <div @abcd="handler"></div> -->
<!--
当该 xhl-aaa 组件发生了 abcd 行为, 会触发 handler 函数
-->
<!-- <xhl-aaa @abcd="hanlder"></xhl-aaa> -->
<p>购买数量 : {{ num }}</p>
<p>总价 : {{ num * 100 }}</p>
------------------------
<xhl-one @update="handler"></xhl-one>
------------------------
<!-- 当 two 组件发生 add 行为的时候, 触发 addHandler 函数 -->
<!-- 让自己的 num 修改 -->
<xhl-two @add="addHandler" @add2="addHandler2"></xhl-two>
</div>
<script src="./vue.global.js"></script>
<script>
/*
子传父
+ 考虑时间点:
=> 假设: 我们在打开页面, 初始渲染组件的时候, 就需要把子组件的数据传递给父组件
-> 不会出现的情况, 如果这样的话, 我们可以直接把数据书写在父组件内
=> 一定: 在某些 "行为" 发生的时候, 传递给父组件一些数据进行使用
+ 例子: 购物车(表格类的内容)
=> 当你点击某一个组件内的数量减少的时候
=> 通知父组件
=> 当你点击某一个组件内的选项的时候
传递方式 : events up (事件上传)
+ 依赖一个事件行为
+ 在父组件内 :
=> 当你使用子组件标签的时候, 给添加一个自定义事件, 绑定事件处理函数
=> 在子组件内, 当你需要传递数据的时候, 使用 this.$emit('事件类型') 去触发自定义事件行为
总结:
+ 父传子
=> 在父组件内, 当你使用子组件标签的时候, 把要传递的数据绑定在标签上
=> 在子组件内, 用 props 属性接受
+ 子传父
=> 在父组件内, 当你使用子组件标签的时候, 通过自定义事件的方式, 绑定一个事件处理函数
=> 在子组件内, 当你需要传递数据的时候, 使用 this.$emit() 来触发自定义事件行为
*/
const comOne = {
template: `
<div>
one 组件 - 在这里使用到 Vue 实例内定义的 message 变量的数据
<button @click="oneHandler">我需要把我的数据传递给父组件</button>
</div>
`,
data () {
return {
message: '我是子组件内准备的数据'
}
},
methods: {
oneHandler () {
console.log('点击了 one 组件内的 button 按钮')
// 让当前组件触发一个 update 行为
// 语法: this.$emit('事件类型', 携带参数)
// 这句代码一旦执行, 就会让当前组件触发一个 update 行为
// this => 当前组件
// $emit => 触发行为
this.$emit('update', this.message)
// 当你点击组件内的 button 按钮的时候, 让当前组件触发了一个 update 行为
// 因为组件内的 update 行为发生了, 会触发父组件内绑定 update 相关的事件处理函数
// 就执行了父组件内的 methods 内准备的 handler 函数
}
}
}
// 需求: 点击组件内的 购买数量+ 按钮的时候, 修改父组件内的数据
const comTwo = {
template: `
<div>
我是购物车内的某一项
<button @click="handler">+</button>
<br>
<input type="text" v-model="n" />
<button @click="handler2">确认</button>
</div>
`,
data () {
return {
n: 0
}
},
methods: {
handler () {
// 点击 购买数量+ 按钮了
// 应该让当前组件触发 add 行为
this.$emit('add')
},
handler2 () {
this.$emit('add2', this.n)
}
}
}
const { createApp } = Vue
createApp({
data () {
return {
str: '',
num: 3
}
},
methods: {
handler (s) {
console.group('父组件的 handler 函数')
console.log('xhl-one 组件发生了 update 行为')
console.log(s)
this.str = s
console.groupEnd()
},
addHandler () {
this.num++
},
addHandler2 (n) {
console.log('我要增加 ' + n + ' 个')
this.num += n - 0
}
},
components: {
'xhl-one': comOne,
'xhl-two': comTwo
}
}).mount('#app')
</script>