_
2024/4/3 00:14:002549

依赖注入和Ref获取dom

这节课我们来学习在组合式api里面使用依赖注入(provide、inject)和ref获取dom元素。

依赖注入(provide、inject)

我们在组件之间传递数据时一般都是使用props。但这种方式在多级组件之间传递数据是非常繁琐的,我们需要逐级传递,才能把数据传递给最终的组件。例如下图所示:

Prop 逐级透传的过程图示

在我们使用依赖注入之后,可以很方便了帮助我们向嵌套更深的子孙级组件传递数据,例如下图所示:

Provide/inject 模式

在选项式api里面我们其实已经对provide、inject有过学习,所以我们这里就不过多讲解了,我们直接来看在组合式api里面怎么使用provide、inject传递数据。

假设我们有三个组件 App.vue → Footer.vue → DeepChild.vue。然后我们希望在App组件里面能直接传递数据给子孙组件DeepChild。我们先到app组件里面来提供数据。

<script>
import { ref,provide} from 'vue'
    ......
export default{ 
    ......
  setup(){
    provide("message","Hello World!")
  }
}
</script>

完了之后我们再到DeepChild组件里面来获取

<script>
  import {inject} from "vue"
    ......
  export default{
    ......
    setup(){
      const message = inject("message")
      return{
        message
      }
    }
  }
</script>

和响应式数据配合使用

通过响应式数据配合使用实现可修改的依赖注入数据。

提供数据,顺带把数据的修改方式也一并提供过去

<script>
import { ref,provide,readonly} from 'vue'
......
export default{
......
  setup(){
    const message = ref("hello world!")
    provide("message",message)
    provide("changeMessage",(msg)=>{
      message.value=msg
    })
  }
}
</script>

获取数据

<script>
  import {inject} from "vue"
  export default{
    setup(){
      const message = inject("message")
      const changeMessage = inject("changeMessage")
      const changeMsg = ()=>{
        changeMessage("hello vue")
      }
      return{
        message,
        changeMsg 
      }
    }
  }
</script>

这样就实现了数据的修改,但是我们会发现一个问题,我给message传递的是一个ref响应式数据,那我们为何不直接在子组件里面来修改这个响应式数据。比如:

const changeMsg = ()=>{
   message.value="hello vue"
}

其实这样它也是能达到修改目的的,只不过我们不建议这样编写代码,因为我们应该遵守vue的单项数据流原则,使用由数据提供者来修改提供数据。避免编写这样的屎山代码。

为了避免以上代码的出现,我可以使用readonly包装起来让提供的响应式数据变得不修可改。

setup(){
  const message = ref("hello world!")
  provide("message",readonly(message))

  provide("changeMessage",(msg)=>{
    message.value=msg
  })
    
}

代码优化

//App.vue组件
//用一个对象来打包提供的数据,这样代码看起清晰简洁
setup(){
    const message = ref("hello world!")
    provide("message",{
      message:readonly(message),
      setMessage(msg){
        message.value=msg
      }
    })
  }
//DeepChild.vue组件
// 使用对象解构的方式来获取数据和修改方法
setup(){
  const {message,setMessage} = inject("message")
  const changeMsg = ()=>{
    setMessage("hello vue")
  }

  return{
    message,
    changeMsg 
  }
}

ref 获取dom

我们在选项式api里面可以通过给模版设置一个ref属性,然后通过this.$refs.xxx获取到指定版本的dom,如果是组件可以获取组件的实例。

那么我们在组合式api里面怎么实现呢。我们在footer.vue组件里面来给大家讲解一下

<script>
import DeepChild from "./DeepChild.vue"
import {ref,onMounted} from "vue"
export default{
  components:{
     DeepChild
  },
  setup(){
    //通过ref来实名一个null的响应式对象
    const footer = ref(null)
    onMounted(()=>{
      //打印一下deom结果里的innerText属性
      console.log(footer.value.innerText)
    })
    return{
       //这里同样是需要进行返回的
      footer
    }
  }
}

</script>

<template>
    <!--然后将我们声明的空响应式对象名称放指定元素ref属性里-->
  <div ref="footer">
    Footer
    <DeepChild/>
  </div>
</template>