侧边栏壁纸
博主头像
woku博主等级

成功的路上并不拥挤

  • 累计撰写 50 篇文章
  • 累计创建 13 个标签
  • 累计收到 3 条评论
vue

Provide和Inject

woku
2022-03-20 / 0 评论 / 1 点赞 / 98 阅读 / 3,890 字

组件化

以视图为线索,切分成无数的碎片

碎片进行组合,在其他碎片中进行依赖,形成了具有交互性的页面 - 组件树

  • 组件复用性

一个组件可以让其他组件进行依赖

  • 组件可配置性

数据来源可以让依赖组件提供

  • 原则上,组件可以无限极的被依赖,数据就无限极的被传递下去 - 单项数据流。

这导致了组件层级很深时候,数据要穿过所有被依赖的组件。很多层组件对这些数据根本就没有用到(组件中对数据进行强制注册)

设计组件的时候,组件的依赖关系变的简单,组件之间的嵌套关系不能过深(组件扁平化)

如何解决:数据跨级传递

Provide/Inject

父组件Provide出来了一个数据,子组件通过inject注入进来,使用数据

使用ProvideInject会存在下面的问题

  • 父组件Provide的数据默认并不是响应式的,父组件改变了,子组件不会更新
  • 父组件不知道谁用了我Provide的数据
  • 子组件也不知道谁提供了这个数据

那么,什么情况下适合使用ProvideInject

  • 在同一个组件体系下,如果有深度嵌套
  • 在同一个组件体系下,多个层级的多个组件要用到这个数据

使用

app.vue


<template>
<div :class="prefixCls">
  <card-list :list="list"></card-list>
  <button @click="addItem">Add</button>
  </div>
</template>

<script>
  import CardList from './components/card-list'
  export default {
    name: 'app',
    components: {
      CardList
    },
    // provide是一个静态数据
    // provide: {
    //   appTitle: 'this is app title',
    // },
    provide() {
      return {
        appTitle: 'this is app title',
        listLen: this.list.length
      }
    },
    data() {
      return {
        prefixCls: '',
        list: [{
          id: 1,
          name: 'wyg'
        }, {
          id: 2,
          name: 'woku'
        }]
      }
    },
    methods: {
      addItem() {
        this.list.push({
          id: 3,
          name: 'Jone'
        })
      }
    },
  }
</script>

<style lang="scss" scoped>
  
</style>

在子组件,使用inject注入

<template>
    <div :class="prefixCls">
        <h3>app标题为:{{ appTitle }}</h3>
        <p>list长度为: {{ listLen }}</p>
        <p>{{ row.id }}</p>
        <p>{{ row.name }}</p>
    </div>
</template>

<script>
    export default {
        name: 'card-item',
        components: {},
        props: {
            row: {
                type: Object,
                default: () => {
                    return {}
                }
            }
        },
        inject: ['appTitle', 'listLen'],
    }
</script>

<style lang="scss" scoped>

</style>

如果父组件的list新增了一项,子组件不会更新

如何让子组件也更新呢?

第一种方式:

  • 直接Provide父组件实例
  • 子组件Inject这个父组件实例
<template>
  <div :class="prefixCls">
    <card-list :list="list"></card-list>
    <button @click="addItem">Add</button>
  </div>
</template>

<script>
  import CardList from './components/card-list'
  export default {
    name: 'app',
    components: {
      CardList
    },
    // provide() {
    //   return {
    //     appTitle: 'this is app title',
    //     listLen: this.list.length
    //   }
    // },
     provide() {
      return {
        appTitle: 'this is app title',
        appIns: this
      }
    },
    data() {
      return {
        prefixCls: '',
        list: [{
          id: 1,
          name: 'wyg'
        }, {
          id: 2,
          name: 'woku'
        }]
      }
    },
    methods: {
      addItem() {
        this.list.push({
          id: new Date(),
          name: 'Jone'
        })
      }
    },
  }
</script>

<style lang="scss" scoped>

</style>
<template>
    <div :class="prefixCls">
        <h3>app标题为:{{ appTitle }}</h3>
        <p>list长度为: {{ appIns.list.length }}</p>
        <p>{{ row.id }}</p>
        <p>{{ row.name }}</p>
    </div>
</template>

<script>
    export default {
        name: 'card-item',
        components: {},
        props: {
            row: {
                type: Object,
                default: () => {
                    return {}
                }
            }
        },
        inject: ['appTitle', 'appIns'],
    }
</script>

<style lang="scss" scoped>

</style>

vue不推荐这种做法

第二种方式:

使用vue3的computed 函数,返回一个响应式对象

<template>
  <div :class="prefixCls">
    <card-list :list="list"></card-list>
    <button @click="addItem">Add</button>
  </div>
</template>

<script>
  import { computed } from 'vue'
  import CardList from './components/card-list'
  export default {
    name: 'app',
    components: {
      CardList
    },
    // provide() {
    //   return {
    //     appTitle: 'this is app title',
    //     listLen: this.list.length
    //   }
    // },
     provide() {
      return {
        appTitle: 'this is app title',
        listLen: computed(() => this.list.length)
      }
    },
    data() {
      return {
        prefixCls: '',
        list: [{
          id: 1,
          name: 'wyg'
        }, {
          id: 2,
          name: 'woku'
        }]
      }
    },
    methods: {
      addItem() {
        this.list.push({
          id: new Date(),
          name: 'Jone'
        })
      }
    },
  }
</script>

<style lang="scss" scoped>

</style>

使用的时候:

<template>
    <div :class="prefixCls">
        <h3>app标题为:{{ appTitle }}</h3>
        <p>list长度为: {{ listLen.value }}</p>
        <p>{{ row.id }}</p>
        <p>{{ row.name }}</p>
    </div>
</template>

<script>
    export default {
        name: 'card-item',
        components: {},
        props: {
            row: {
                type: Object,
                default: () => {
                    return {}
                }
            }
        },
        inject: ['appTitle', 'listLen'],
    }
</script>

<style lang="scss" scoped>

</style>
1

评论区