文章目录
vue3笔记
vue3学习之旅–邂逅vue3-了解认识Vue3
vue3学习之旅–邂逅vue3-了解认识Vue3(二)
Vue3学习之旅–爱上Vue3–Vue3基础语法(一)–以及vscode基本使用和快速生成代码片段
Vue3学习之旅–爱上Vue3–Vue3的Options-API
vue3学习之旅–vue的表单和开发模式–组件化开发初识
Vue3学习之旅–初识webpack–webpack打包js,css,less等文件以及兼容性处理
Vue3学习之旅–之webpack基础-进阶–webpack5?拿来吧你!
vue3学习之旅–webpack5-babel整合sfc单文件vue组件
Vue3学习之旅-webpack终章–热部署(更新)–跨域代理?拿来吧你!
vue3学习之旅-Vue-CLI及其原理&尤大大新宠Vite-下一代前端开发构建工具Vite:拿来吧你!
Vue3组件化(一)-父子组件通信
组件的嵌套
认识组件的嵌套
前面我们是将所有的逻辑放到一个App.vue中:
- 在之前的案例中,我们只是创建了一个组件App;
- 如果我们一个应用程序将所有的逻辑都放在一个组件中,那么这个组件就会变成非 常的臃肿和难以维护;
- 所以组件化的核心思想应该是对组件进行拆分,拆分成一个个小的组件;
- 再将这些组件组合嵌套在一起,最终形成我们的应用程序;
我们来分析一下下面代码的嵌套逻辑,假如我们将所有的代码逻辑都放到一个App.vue 组件中:
- 我们会发现,将所有的代码逻辑全部放到一个组件中,代码是非常的臃肿和难以维 护的。
- 并且在真实开发中,我们会有更多的内容和代码逻辑,对于扩展性和可维护性来说 都是非常差的。
- 所以,在真实的开发中,我们会对组件进行拆分,拆分成一个个功能的小组件。
组件的拆分
我们可以按照如下的方式进行拆分:
按照如上的拆分方式后,我们开发对应的逻辑只需要去对应的组件编写就可。
App.vue文件
<template lang="html">
<!-- 使用局部组件 -->
<div id="app">
<Header></Header>
<!-- 如果组件不需要填充数据或标签等,可以直接使用单标签 -->
<Main/>
<Footer/>
</div>
</template>
<script>
// 导入vue组件
import Header from "./Header";
import Main from "./Main";
import Footer from "./Footer";
export default {
data() {
return {};
},
// 注册局部组件
components: {
Header,
Main,
Footer,
},
};
</script>
<style scoped>
</style>
Header.vue
<template lang="">
<div id="header">
<h2>哈哈哈</h2>
<h2>header</h2>
</div>
</template>
<script>
export default {}
</script>
<style scoped>
</style>
Main.vue
<template>
<div id="main">
<h2>haha</h2>
<h3>main</h3>
<main-banner></main-banner>
</div>
</template>
<script>
import MainBanner from "./MainBanner.vue";
export default {
components: { MainBanner },
};
</script>
<style scoped>
</style>
MainBanner.vue
<template>
<ul>
<li>商品信息1</li>
<li>商品信息2</li>
<li>商品信息3</li>
<li>商品信息4</li>
<li>商品信息5</li>
</ul>
</template>
<script>
export default {
}
</script>
<style scoped>
</style>
Footer.vue
<template>
<div id="footer">
<h2>main</h2>
<h2>hahah</h2>
</div>
</template>
<script>
export default {
}
</script>
<style scoped>
</style>
组件的通信
组件的通信
上面的嵌套逻辑如下,它们存在如下关系:
- App组件是Header、Main、Footer组件的父组件;
- Main组件是Banner、ProductList组件的父组件;
在开发过程中,我们会经常遇到需要组件之间相互进行通信:
- 比如App可能使用了多个Header,每个地方的Header展示的内容不同,那么我们就需要使用者传递给Header 一些数据,让其进行展示;
- 又比如我们在Main中一次性请求了Banner数据和ProductList数据,那么就需要传递给它们来进行展示;
- 也可能是子组件中发生了事件,需要由父组件来完成某些操作,那就需要子组件向父组件传递事件;
总之,在一个Vue项目中,组件之间的通信是非常重要的环节,所以接下来我们就具体学习一下组件之间是如何相 互之间传递数据的;
父子组件之间通信的方式
父子组件之间如何进行通信呢?
- 父组件传递给子组件:通过props属性;
- 子组件传递给父组件:通过**$emit**触发事件;
父组件传递给子组件
在开发中很常见的就是父子组件之间通信,比如父组件有一些数据,需要子组件来进行展示:
这个时候我们可以通过props来完成组件之间的通信;
什么是Props呢?
- Props是你可以在组件上注册一些自定义的attribute;
- 父组件给这些attribute赋值,子组件通过attribute的名称获取到对应的值;
Props有两种常见的用法:
- 方式一:字符串数组,数组中的字符串就是attribute的名称;
- 方式二:对象类型,对象类型我们可以在指定attribute名称的同时,指定它需要传递的类型、是否是必须的、 默认值等等
Props的数组用法
Props的对象用法
数组用法中我们只能说明传入的attribute的名称,并不能对其进行任何形式的限制,接下来我们来看一下对象的 写法是如何让我们的props变得更加完善的。
当使用对象语法的时候,我们可以对传入的内容限制更多:
- 比如指定传入的attribute的类型;
- 比如指定传入的attribute是否是必传的;
- 比如指定没有传入时,attribute的默认值;
细节一:type类型
那么type的类型都可以是哪些呢?
- String
- Number
- Boolean
- Array
- Object
- Date
- Function
- Symbol(ES6新增的基本类型)
细节二:对象类型的其他写法
细节三:Prop 的大小写命名
Prop 的大小写命名(camelCase vs kebab-case)
- HTML 中的 attribute 名是大小写不敏感的,所以浏览器会把所有大写字符解释为小写字符;
- 这意味着当你使用 DOM 中的模板时,camelCase (驼峰命名法) 的 prop 名需要使用其等价的 kebab-case (短 横线分隔命名) 命名
非Prop的Attribute
什么是非Prop的Attribute呢?
当我们传递给一个组件某个属性,但是该属性并没有定义对应的props或者emits时,就称之为 非Prop的 Attribute;
常见的包括class、style、id属性等;
Attribute继承
当组件有单个根节点时,非Prop的Attribute将自动添加到根节点的Attribute中:
禁用Attribute继承和多根节点
如果我们不希望组件的根元素继承attribute,可以在组件中设置 inheritAttrs: false:
-
禁用attribute继承的常见情况是需要将attribute应用于根元素之外的其他元素;
-
我们可以通过 $attrs来访问所有的 非props的attribute;
多个根节点的attribute
多个根节点的attribute如果没有显示的绑定,那么会报警告,我们必须手动的指定要绑定到哪一个属性上:
如果我们不继承父组件的attribute属性,设置了 inheritAttrs:false
,那么则不会报警告。没有根节点会继承根元素的属性。
子组件传递给父组件
子组件传递给父组件
什么情况下子组件需要传递内容到父组件呢
- 当子组件有一些事件发生的时候,比如在组件中发生了点击,父组件需要切换内容;
- 子组件有一些内容想要传递给父组件的时候;
我们如何完成上面的操作呢?
- 首先,我们需要在子组件中定义好在某些情况下触发的事件名称;
- 其次,在父组件中以v-on的方式传入要监听的事件名称,并且绑定到对应的方法中;
- 最后,在子组件中发生某个事件的时候,根据事件名称触发对应的事件;
自定义事件的流程
我们封装一个CounterOperation.vue的组件:
内部其实是监听两个按钮的点击,点击之后通过 this.$emit的方式发出去事件;
自定义事件的参数和验证
自定义事件的时候,我们也可以传递一些参数给父组件:
在vue3当中,我们可以对传递的参数进行验证:
App.vue
<template>
<div>
<h2>当前计数:{{ counter }}</h2>
<!-- 监听子组件触发的事件 -->
<!-- 子组件触发的是什么事件,我们就需要监听什么事件 -->
<!-- 就如这里:子组件触发的是add和sub事件,那么我们监听的必然是add和sub事件 -->
<!-- 但是我们监听到的事件用的处理函数是任意的,addOne,subOne是任意的 -->
<counter-operation @add="addOne" @sub="subOne" @addN="addN"></counter-operation>
<!-- 注意:监听子组件的事件时,我们绑定的事件处理函数不要带括号,不然无法接收到子组件传递出来的参数 -->
</div>
</template>
<script>
import CounterOperation from './CounterOperation.vue'
export default {
components: { CounterOperation },
data() {
return {
counter: 0
}
},
methods: {
addOne() {
this.counter++
},
subOne() {
this.counter--
},
// 事件处理函数可以接收子组件触发事件时传递给父组件的参数
// 在括号上写的形参就会直接被子组件传递的参数赋值
// 我们可以直接使用,多个参数也是一样
addN(num) {
this.counter += num
}
},
}
</script>
<style scoped>
</style>
CounterOperation.vue
<template>
<div>
<button @click="increment">+</button>
<button @click="decrement">-</button>
<input type="text" v-model.number="num" />
<br />
<button @click="incrementN">+n</button>
</div>
</template>
<script>
export default {
// 子组件会触发的事件,写在 emits属性上
// emits 可以是数组,也可以是对象,用的多的是数组
// 这是Vue3的写法,需要触发的事件需要先注册
// emits: ['add', 'sub', 'addN'],
// emits 对象写法,可以进行参数的验证
emits: {
// null 表示不需要进行参数的验证
add: null,
sub: null,
// 需要验证的话,触发的事件后面是一个验证参数的函数
// 我们可以写成箭头函数
// 返回 true 表示参数验证通过,fasle则验证失败,会报警告
// 注意:验证失败也会把参数传递过去的!!!!
addN: payload => {
console.log(typeof payload);
// 看看是不是number类型
if ((typeof payload) === 'number') return true;
return false;
}
},
data() {
return {
num: 0
}
},
methods: {
increment() {
// 触发我们注册的 add事件
this.$emit('add')
},
decrement() {
// 触发我们注册的 sub 事件
this.$emit('sub')
},
incrementN() {
// 触发addN事件,一次加N个数,所以需要我们把加的数也传递出去
// 需要几个参数就传递几个参数
this.$emit('addN', this.num)
}
},
}
</script>
<style scoped>
button {
margin-right: 20px;
}
</style>