Vue思考题_04组件间传值的方式

注册子组件

注册子组件分为局部注册子组件全局注册子组件

  • 局部组件:用一次 导一次 (在用到的地方导入)
  • 全局组件:只需要在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-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中插槽使用语法相同!
插槽详解

兄弟组件通信-eventBus

eventBus进行事件传值

多组件共享数据-vuex

传送门 数据共享vuex

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值