目录
注册子组件
注册子组件分为局部注册子组件
和全局注册子组件
;
- 局部组件:用一次 导一次 (在用到的地方导入)
- 全局组件:只需要在main.js中导入一次,整个项目都可以直接使用(在main.js中导入);
01-局部注册
在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>
-
02-全局注册
如果某个组件创建后可能在项目的多个页面中使用,可以进行全局注册。
vue2.x全局注册
-
[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>
-
vue3.x中进行全局注册
-
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>
-
03-全局注册- use方法
也可以通过Vue.use进行组件的全局注册
Vue2.x中通过构造函数的use方法进行全局注册
Vue3.x中通过createApp(App).use方法进行全局注册
原理相同,只是语法不同
父子组件通信
(1-1)父子组件传值->props与$emit(vue2.x)
[1]父传子props
-
父组件
左侧是子组件的,右侧是父组件的
<fa> <son 传递给子组件的属性名=‘传递的值’></son> <son @子组件使用的方法名=‘父组件方法’></son> </fa>
-
子组件-在子组件中通过props来接收
// 1.props属性值为1个数组,用来接收父组件传递过来的一系列参数; props:['属性名'] // 2.props属性值为1个对象,用来接收参数 props:{ 属性名:{ type:希望传递过来的属性类型, default:属性默认值 } }
props中的属性与data中得属性使用方法相同;
注意问题:
- 通过props进行传值时是
单向数据流
,也就是说父组件将数据传递给子组件后,子组件不能修改该数据的引用;- 简单数据类型不能修改;
- 复杂数据类型不能直接赋值;
举例说明
封装了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"/>
[2]子传父-$emit
父组件通过props传递给子组件的数据是单向数据流,若是子组件想要修改父组件的数据时,可以通过$emit
触发父组件的方法,在父组件中修改该数据
-
父组件
<!-- 左侧是子组件的 右侧是父组件的--> <son @isSon='isfa'></son>
methods:{ isfa(val):{ console.log('子组件传递过来的值',val) } }
-
子组件
// 当子组件想修改父组件的值或者是子组件想给父组件传值时-相当于调用isfa方法并传入实参为该值; this.$emit('isSon',值)
[3]v-model语法糖
本质
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实现
v-model简化了父组件的步骤(子组件步骤不变—>还是需要接收和$emit事件触发修改数据)。
在默认的情况下,组件上使用的 v-model 会被解析成名为 value 的 prop 和名为 input 的事件
- v-model
<son-com v-model="value"/>
- 实际
<son-com :value="value" @input="val=>{value = val}"/>
原生input标签上v-model实现
- 前提:
input标签上存在input事件
:input框输入过程中value值改变时实时触发,输入每一个字符都会触发(这也是为什么原生html的input标签在vue中可以使用v-model指令的原因) - v-model
<input v-model="value" />
- 实际
<input :value="value" @input="e => value = e.target.value"/>
[4].sync与$emit
.sync是父组件监听子组件更新某个props请求的语法糖;
-
父组件
<child :属性名.sync='value'></child>
data(){ return{ value:'' } }
-
子组件
props:{ 属性名:{ type:String } }
this.$emit('update:属性名',传递的值)
-
这样当子组件修改此属性时,父组件的value属性值就会时时更改了,实现了一个双向绑定了!;
(1-2)父子组件传值->props与$emit(vue3.x)
在vue3.x中通过props与$emit进行父子传值的原理相同,只是在setup中不能使用this ,因此在语法上会有所区分。
[1]父传子props
在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' } }
- 因为在接收之前需要
[2]子传父emit
在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('方法名', 值)
}
}
}
(2)获取子组件的属性/方法->$refs
vue2.x
-
前提(获取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>
vue3.x
-
前提(获取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>
(3)获取父组件的属性/方法->$parents
相比于$parents更推荐使用 props 与 $emit 来进行父子通信( $parent 的耦合性较高)。
vue2.x
若是我们想直接调用父组件的方法/获取父组件的属性/元素
-
1.vue给每个实例化对象添加$parent属性;
-
2.在子组件中通过this.$parent可以获取到父组件的vue实例化对象;
this.$parent.父组件的方法名/属性名
;
vue3.x
在vue3.x中不能使用this,因此不能使用this.$parent的方式去获取父组件。
虽然不能通过this去获取实例化对象的方法,但是并不代表实例化对象身上的方法不存在。也就是说实例化对象身上的$parent方法依旧存在!
<button @click="editValue($parent)">editValue</button>
function editValue(parent){
console.log(1111111, parent) // 父组件
}
实例化对象上的其他属性/方法也可以同样的方式获取!
(4)组件嵌套传值
需求:A组件,B组件,C组件,D组件;A组件是B组件的父组件,B组件是C组件的父组件,我们目前是想在A组件将值传递给C组件(多层嵌套) ----> name、age、gender、三个属性
[1-1]嵌套传值-$attrs
概念
$attrs是vue2.40版本以上添加的;
- [1]如果父组件给子组件传递的数据,子组件不使用props接收,那么这些数据将
作为子组件的特性绑定在组件的HTML根元素上
,可以通过inheritAttrs = true/false
来控制这些特性是否显示在dom元素上; - [2] 如果父组件给子组件传递的数据,子组件不使用props接收, 那么这些数据将添加在实例化对象的
$attrs
属性上。- 因此即使我们没有使用props接收,也可以通过$attrs获取并使用父组件传递过来的数据;
- 但是此时不能进行数据类型限制!
- [3] 若是想传递给其子组件,通过
v-blind='$attrs'
绑定即可。
需求实现(vue2.x)
通过$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;
需求实现(3.x)
-
在vue2.x中
若是父组件传递过来的值没有通过props接收,那么这些属性会添加在实例化对象的$attrs属性上。
-
在vue3.x中
若是父组件传递过来的值没有通过props接收,这些属性同样会添加在实例化对象的$attrs属性上(只是现在setup中不能使用this获取实例化对象);
在setup函数中第二个参数为context(上下文)中存在attrs属性
context: { attrs //等同于实例化对象上的$attrs = this.$attrs ... }
因此在vue3.x中
v-bind='$attrs'
绑定有两种形式- 方式1:在template中不需要this也可以使用实例化对象身上的属性
<template> <div> <grand-son v-bind="$attrs"/> </div> </template> <script> import grandSon from './11-grandson.vue' export default{ components:{ grandSon } } </script>
- 方式2: 将上下文中的attrs属性导出使用
<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>
- 方式1:在template中不需要this也可以使用实例化对象身上的属性
[1-2]嵌套传值-$listeners(vue2.x)
当我们想要将子孙组件的数据传递给父组件,并在父组件做某些操作时可以使用$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>
-
[2]嵌套传值-provide/inject(依赖注入)
vue2.x
provide/inject是可以给子孙后代进行传值的!
-
provide(父组件提供数据):提供依赖是一个对象,或者是一个返回对象的函数。里面呢就包含要给子孙后代的东西,也就是属性和属性值;
-
inject:(子组件接收数据)注入依赖一个字符串数组,或者一个对象,属性值可以是一个对象,包含from和default默认值;
接收的数据会被平铺在组件实例化对象身上。
需求:A组件---->son组件-----grandson组件。我们目前是想在A组件将值传递给grandson组件;
- A组件(在A组件提供依赖)
<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>
- son组件
<template> <div> <grand-com/> </div> </template> <script> import grandCom from './grandCom.vue' export default{ components:{ grandCom } } </script>
- grandson组件获取依赖
<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
在vue3.x中将 provide、inject配置项被抽取为一个组合API,使用之前需要先引入
- provide
import { provide } from 'vue';
provide(注入名, 注入值),
- inject
import { inject } from 'vue'
const 变量名 = inject(注入名)
需求:A组件---->son组件-----grandson组件。我们目前是想在A组件将值传递给grandson组件;
- A组件
<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>
- son组件
<template> <div> <grand-son/> </div> </template> <script> import grandSon from './11-grandson.vue' export default{ components:{ grandSon } } </script>
- grandSon
<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>
(5)父子组件-插槽
vue2.x中与vue3.x中插槽使用语法相同!
插槽详解