注册子组件分为局部注册子组件
和全局注册子组件
;
在vue2.x与vue3.x中局部注册子组件的方式相同。
[1]创建子组件 hello.vue
<template>
<div>
hello1
</div>
</template>
[2]在需要的地方引入
<template>
<div>
<!-- [3]当作标签使用 -->
<hello />
</div>
</template>
<script>
// [1]引入子组件
import Hello from './hello1.vue'
export default {
// [2]注册子组件
components:{
Hello
}
}
</script>
如果某个组件创建后可能在项目的多个页面中使用,可以进行全局注册。
[1]创建子组件 hello.vue
<template>
<div>
hello1
</div>
</template>
[2]在主js文件(默认main.js)中进行全局注册
import hello1 from './views/hello1.vue'
Vue.component('hello', hello1)
[3]在需要使用的页面直接当做标签使用
<template>
<div>
<hello />
div>
template>
vue2.x
import Vue from 'vue'
import App from './App.vue'
new Vue({
render: h => h(App),
}).$mount('#app')
在vue2.x中引入的Vue构造函数上存在component方法,可以使用此方法全局注册子组件
vue3.x
import Vue, { createApp } from 'vue'
import App from './App.vue'
const app = createApp(App)
app.mount('#app')
console.log('vue', Vue) // undefiend
console.log('app', app) // 存在component方法、config配置项等
在vue3.x中可以发现不存在Vue构造函数了,createApp方法的返回值为一个对象里面存在一系列的全局方法和属性。
[1]创建子组件 hello.vue
<template>
<div>
hello1
</div>
</template>
[2]在主js文件(默认main.js)中进行全局注册app.component(‘组件名’,组件)
import hello1 from './views/hello1.vue'
const app = createApp(App)
app.component('hello', hello1)
[3]在需要使用的页面直接当做标签使用
<template>
<div>
<hello />
div>
template>
也可以通过Vue.use进行组件的全局注册
Vue2.x中通过构造函数的use方法进行全局注册
Vue3.x中通过createApp(App).use方法进行全局注册
原理相同,只是语法不同
父组件
左侧是子组件的,右侧是父组件的
<fa>
<son 传递给子组件的属性名=‘传递的值’>son>
<son @子组件使用的方法名=‘父组件方法’>son>
fa>
子组件-在子组件中通过props来接收
// 1.props属性值为1个数组,用来接收父组件传递过来的一系列参数;
props:['属性名']
// 2.props属性值为1个对象,用来接收参数
props:{
属性名:{
type:希望传递过来的属性类型,
default:属性默认值
}
}
props中的属性与data中得属性使用方法相同;
单向数据流
,也就是说父组件将数据传递给子组件后,子组件不能修改该数据的引用;
封装了hello子组件,默认高度为500px;
子组件
<template>
<div :style="{height: barHeight + 'px'}" class="box"></div>
</template>
<script>
export default{
props:{
barHeight:{
type: Number,
default: 500
}
},
mounted(){
console.log('12222', this.barHeight)
}
}
</script>
<style>
.box{
background-color: aqua;
}
</style>
在组件中使用
<template>
<div>
<son-com :barHeight="200"/>
</div>
</template>
<script>
import sonCom from './components/sonCom.vue'
export default {
components:{
sonCom
}
}
</script>
但是在其他页面中应用时,想不设置高度,由内容撑开!
若是父组件不设置此属性,则在子组件会自动设置此默认高度;
若是想内容撑开,则直接设置属性值为null!!!=>设置null表示设置了值,在子组件不会启动设置默认;
<son-com :barHeight="200"/>
父组件通过props传递给子组件的数据是单向数据流,若是子组件想要修改父组件的数据时,可以通过$emit
触发父组件的方法,在父组件中修改该数据
父组件
<son @isSon='isfa'>son>
methods:{
isfa(val):{
console.log('子组件传递过来的值',val)
}
}
子组件
// 当子组件想修改父组件的值或者是子组件想给父组件传值时-相当于调用isfa方法并传入实参为该值;
this.$emit('isSon',值)
v-model其实是props与$emit封装的语法糖,实现值在父子组件中的共用!
父组件给子组件进行传值时属性名为value
(固定),子组件触发修改value属性值的方法为input
事件(固定);
<son :value='value' @input="(val)=>{value=val}">son>
在子组件中使用 value属性名接收父组件传递过来的值,通过input方法去调用父组件的方法去修改值;
props:['value']
this.$emit('input',想要改变的值)
举例说明
<template>
<div>
<son-com :value="value" @input="val=>{value=val}"/>
</div>
</template>
<script>
import sonCom from './components/sonCom.vue'
export default {
components:{
sonCom
},
data () {
return{
value: 200
}
}
}
</script>
<template>
<div>
<span>{{ value }}</span>
<button @click="$emit('input', value+1)">editValue</button>
</div>
</template>
<script>
export default{
props:['value']
}
</script>
v-model简化了父组件的步骤(子组件步骤不变—>还是需要接收和$emit事件触发修改数据)。
在默认的情况下,组件上使用的 v-model 会被解析成名为 value 的 prop 和名为 input 的事件
<son-com v-model="value"/>
<son-com :value="value" @input="val=>{value = val}"/>
input标签上存在input事件
:input框输入过程中value值改变时实时触发,输入每一个字符都会触发(这也是为什么原生html的input标签在vue中可以使用v-model指令的原因)<input v-model="value" />
<input :value="value" @input="e => value = e.target.value"/>
.sync是父组件监听子组件更新某个props请求的语法糖;
父组件
<child :属性名.sync='value'>child>
data(){
return{
value:''
}
}
子组件
props:{
属性名:{
type:String
}
}
this.$emit('update:属性名',传递的值)
这样当子组件修改此属性时,父组件的value属性值就会时时更改了,实现了一个双向绑定了!;
在vue3.x中通过props与$emit进行父子传值的原理相同,只是在setup中不能使用this ,因此在语法上会有所区分。
在vue2.x中使用props配置项定义传入数据属性以及数据类型。接收之后这些属性就会“平铺”在实例化对象身上,若是使用直接通过实例化对象(this)访问即可。
在vue3.x中也是使用props配置项定义传入数据属性以及数据类型。但是在setup中是不能使用this的,setup函数存在两个参数第一个就是props!
export default{
props:[], // [1]声明 -> 告诉vue需要接收哪些参数;
setup(props){
console.log(props) // [2]通过函数传参方式接收参数-> props中的数据也是响应式的(使用proxy)
}
}
将props作为参数传入setup中
<template>
<son name='chaochao' age='18'/>
</template>
<script>
import Son from './components/02-父子组件传值.vue'
export default {
components: {
Son
}
}
</script>
<template>
<div>
<header>我是子组件</header>
</div>
</template>
<script>
export default{
setup(props){
console.log('props', props) // 空的proxy代理对象
}
}
</script>
先声明
,告诉vue你要接收那些参数// 通过props进行接收参数声明
props:['name','age']
setup(props){
console.log('props', props) // proxy {name:'chaochao', age:'18' }
}
在vue3的setup中不能使用this,那么就获取不到$emit方法,那如何修改父组件的数据呢?将emit作为参数传入setup中
setup函数在被调用时会传入两个参数,第一个参数是props ,第二个参数是 context(上下文)->值为一个对象
context:{
attrs:Object, // 等同于 vue2实例化对象中的 $attrs 属性->用于嵌套组件传递数据
emit:Function, // 等同于 vue2实例化对象中的 $emit 属性->用于给父组件传递数据
slots:Array/Object 等同于 vue2实例化对象中的 $slots 属性
}
emit使用:给父组件传递数据
export default{
setup(props,context){
// 声明方法
editData(){
context.emit('方法名', 值)
}
}
}
前提(获取dom):在vue实例化对象上存在$refs
属性,该属性最初为一个空对象。
若是给元素/组件上添加ref属性,则会将该元素/组件添加在$refs组件中
// 子组件
{ref属性名: VueComponent}
// html标签
{ref属性名:div}
通过 元素/组件 获取 元素/组件 的 属性/方法。
步骤:直接调用子组件的属性/方法
1.给子组件的标签上加上ref属性;
2.通过this.$refs.ref属性值
获取子组件;
3. 通过子组件调用子组件的属性/方法;
this.$refs.ref属性值.属性/方法
举例说明
父组件
<template>
<div>
<son-com ref="scom"/>
<button @click="editValue">editValue</button>
</div>
</template>
<script>
import sonCom from './components/sonCom.vue'
export default {
components:{
sonCom
},
data () {
return{
value: 200
}
},
methods:{
editValue(){
this.$refs.scom.editValue()
}
}
}
</script>
子组件
<template>
<div>{{ value }}</div>
</template>
<script>
export default{
data(){
return{
value: 200
}
},
methods:{
editValue(){
this.value = this.value+1
}
}
}
</script>
前提(获取dom): 在vue3.x的setup中是不存在this的,因此不存在this.$refs方法。
此处可以借助ref
方法
<son-com ref="scom"/>
import { onMounted, ref } from 'vue'
const scom = ref()
onMounted(()=>{
console.log('dom', scom.value) // dom元素
})
return {
scom
}
步骤:直接调用子组件的属性/方法
1.给子组件的标签上加上ref属性;
2.通过ref方法创建 与ref属性值同名变量
3. 通过该变量调用子组件的属性/方法;
举例说明
父组件
<template>
<son-com ref="scom"/>
<button @click="editValue">editValue</button>
</template>
<script>
import sonCom from './components/10-son.vue'
import { ref } from 'vue'
export default {
components:{
sonCom
},
setup(){
const scom = ref()
function editValue(){
scom.value.editValue()
}
return {
scom,
editValue
}
}
}
</script>
子组件
<template>
<div>{{ value }}</div>
</template>
<script>
import {ref} from 'vue'
export default{
setup(){
const value = ref(200)
function editValue(){
value.value = value.value+1
}
return {
value,
editValue
}
}
}
</script>
相比于$parents更推荐使用 props 与 $emit 来进行父子通信( $parent 的耦合性较高)。
若是我们想直接调用父组件的方法/获取父组件的属性/元素
1.vue给每个实例化对象添加$parent属性;
2.在子组件中通过this.$parent可以获取到父组件的vue实例化对象;
this.$parent.父组件的方法名/属性名
;
在vue3.x中不能使用this,因此不能使用this.$parent的方式去获取父组件。
虽然不能通过this去获取实例化对象的方法,但是并不代表实例化对象身上的方法不存在。也就是说实例化对象身上的$parent方法依旧存在!
<button @click="editValue($parent)">editValuebutton>
function editValue(parent){
console.log(1111111, parent) // 父组件
}
实例化对象上的其他属性/方法也可以同样的方式获取!
需求:A组件,B组件,C组件,D组件;A组件是B组件的父组件,B组件是C组件的父组件,我们目前是想在A组件将值传递给C组件(多层嵌套) ----> name、age、gender、三个属性
$attrs是vue2.40版本以上添加的;
作为子组件的特性绑定在组件的HTML根元素上
,可以通过inheritAttrs = true/false
来控制这些特性是否显示在dom元素上;$attrs
属性上。
v-blind='$attrs'
绑定即可。通过$attrs可以实现组件嵌套传递
A组件
<template>
<div>
<son-com name="chaochao" :age="18" :gender="1" />
</div>
</template>
<script>
import sonCom from './components/sonCom.vue'
export default {
components:{
sonCom
}
}
</script>
B组件
<template>
<div>
<grand-com v-bind="$attrs"/>
</div>
</template>
<script>
import grandCom from './grandCom.vue'
export default{
components:{
grandCom
},
created(){
console.log('this.props', this.name, this.age, this.grander) // undefiend,因为没有使用props配置项接收
console.log('$attrs', this.$attrs) // {name: 'chaochao', age: 18, gender: 1} 子组件没有使用props接收的属性会添加在实例化对象的$attrs属性中
}
}
</script>
C组件
<template>
<div></div>
</template>
<script>
export default{
props: ['name', 'age', 'gender'],
created(){
console.log('props', this.name, this.age, this.gender) // chaochao 18 1
}
}
</script>
tips:通过$attrs虽然可以实现嵌套传递,但是每一组件都需要绑定 $attrs;
在vue2.x中
若是父组件传递过来的值没有通过props接收,那么这些属性会添加在实例化对象的$attrs属性上。
在vue3.x中
若是父组件传递过来的值没有通过props接收,这些属性同样会添加在实例化对象的$attrs属性上(只是现在setup中不能使用this获取实例化对象);
在setup函数中第二个参数为context(上下文)中存在attrs属性
context: {
attrs //等同于实例化对象上的$attrs = this.$attrs
...
}
因此在vue3.x中v-bind='$attrs'
绑定有两种形式
<template>
<div>
<grand-son v-bind="$attrs"/>
</div>
</template>
<script>
import grandSon from './11-grandson.vue'
export default{
components:{
grandSon
}
}
</script>
<template>
<div>
<grand-son v-bind="$attrs"/>
</div>
</template>
<script>
import grandSon from './11-grandson.vue'
export default{
components:{
grandSon
},
setup(props, context){
return{
$attrs: context.attrs
}
}
}
</script>
当我们想要将子孙组件的数据传递给父组件,并在父组件做某些操作时可以使用$listeners;
需求:存在A、B、C组件,A组件是B组件的父组件、B组件是C组件的父组件,想要在C组件去修改A组件的value属性值;
A组件
<template>
<div>
{{value}}
<B @tochange='editvalue'/>
</div>
</template>
<script>
import B from './B.vue'
export default {
components:{
B
},
data(){
return{
value:'111'
}
},
methods:{
editvalue(val){
this.value = val
}
}
}
</script>
B组件-通过$listeners
进行绑定;
<template>
<div>
<c v-on='$listeners'/>
</div>
</template>
<script>
import C from './C.vue'
export default {
components:{
C
}
}
</script>
C组件-通过==$emit==去触发父组件的方法
<template>
<div>
<button @click="tochangeValue">C-点击</button>
</div>
</template>
<script>
export default {
methods:{
tochangeValue(){
this.$emit('tochange',1111)
}
}
}
</script>
provide/inject是可以给子孙后代进行传值的!
provide(父组件提供数据):提供依赖是一个对象,或者是一个返回对象的函数。里面呢就包含要给子孙后代的东西,也就是属性和属性值;
inject:(子组件接收数据)注入依赖一个字符串数组,或者一个对象,属性值可以是一个对象,包含from和default默认值;
接收的数据会被平铺在组件实例化对象身上。
需求:A组件---->son组件-----grandson组件。我们目前是想在A组件将值传递给grandson组件;
<template>
<div>
<son-com/>
</div>
</template>
<script>
import sonCom from './components/sonCom.vue'
export default {
components:{
sonCom
},
provide(){
return{
name: "chaochao",
age: 18,
gender: 1
}
}
}
</script>
<template>
<div>
<grand-com/>
</div>
</template>
<script>
import grandCom from './grandCom.vue'
export default{
components:{
grandCom
}
}
</script>
<template>
<div></div>
</template>
<script>
export default{
inject: ['name', 'age', 'gender'],
created(){
console.log('inject', this.name, this.age, this.gender) // chaochao 18 1
}
}
</script>
在vue3.x中将 provide、inject配置项被抽取为一个组合API,使用之前需要先引入
import { provide } from 'vue';
provide(注入名, 注入值),
import { inject } from 'vue'
const 变量名 = inject(注入名)
需求:A组件---->son组件-----grandson组件。我们目前是想在A组件将值传递给grandson组件;
<template>
<son-com />
</template>
<script>
import { provide } from 'vue';
import sonCom from './components/10-son.vue'
export default {
components:{
sonCom
},
setup(){
provide('info', {
name: 'chaochao',
age: 18,
gander: 1
} )
}
}
</script>
<template>
<div>
<grand-son/>
</div>
</template>
<script>
import grandSon from './11-grandson.vue'
export default{
components:{
grandSon
}
}
</script>
<template>
<div></div>
</template>
<script>
import { inject } from 'vue'
export default{
setup(){
const info = inject('info')
console.log(1111, info) //1111 {name: 'chaochao', age: 18, gander: 1}
}
}
</script>
vue2.x中与vue3.x中插槽使用语法相同!
插槽详解