Разговор о применении Provide/Inject во Vue

внешний интерфейс Vue.js

Как мы все знаем, при разработке компонентов самой большой проблемой является взаимодействие между компонентами. В Vue Vue предоставляет множество методов связи компонентов, от базовых props/$emit до EventBus для связи между родственными компонентами и Vuex для глобального управления данными.

Во многих способах взаимодействия компонентов предоставление/внедрение кажется очень акалинским (отсутствие смысла существования). Однако на самом деле обеспечение/инъекция тоже имеет место быть. Сегодня поговорим о применении Provide/Inject во Vue.

Что такое обеспечить / ввести

Provide/inject — это новый API, добавленный Vue в версии 2.2.0.Официальный сайт выглядит следующим образом:

Эту пару опций необходимо использовать вместе, чтобы позволить компоненту-предку внедрить зависимость во все его потомки, независимо от того, насколько глубока иерархия компонентов, и она всегда будет действовать, пока устанавливаются отношения восходящего и нисходящего потоков. Если вы знакомы с React, это похоже на контекстные функции React.

Объяснение на официальном сайте очень запутанное, поэтому я перевожу эти предложения:

обеспечить может указать в компоненте-предке, что мы хотимпоставкаданные или методы для компонентов-потомков, и в любом компоненте-потомке мы можем использовать инъекцию для получения предоставленияпоставкаданные или методы.

Возьмите официальный сайт 🌰:

// 父级组件提供 'foo'
var Provider = {
  provide: {
    foo: 'bar'
  },
  // ...
}

// 子组件注入 'foo'
var Child = {
  inject: ['foo'],
  created () {
    console.log(this.foo) // => "bar"
  }
  // ...
}

Как видите, переменная foo, предоставленная родительским компонентом, успешно получена и используется дочерним компонентом.

Поняв, что такое «обеспечить/внедрить», давайте снова воспользуемся «предоставлением/внедрением».

Используйте предоставление/внедрение для глобального управления состоянием

В повседневной разработке мы часто используем Vuex для управления состоянием, но лично мне не нравится использовать Vuex, потому что Vuex слишком громоздкий, чтобы использовать его для отслеживания состояния; а проекты, в которых я участвовал раньше, меньше подходят для многозадачности. -человек, эта функция для меня маловажна, мне нужна только функция предоставления глобального состояния в Vuex.

Итак, есть ли удобный и быстрый способ реализовать глобальное состояние? Конечно, это способ использовать черный технологический API для предоставления/внедрения.

Многие люди могут придумать способ: в корневом компоненте передать переменные, а затем использовать их в компонентах-потомках.

// 根组件提供一个非响应式变量给后代组件
export default {
  provide () {
    return {
      text: 'bar'
    }
  }
}

// 后代组件注入 'app'
<template>
	<div>{{this.text}}</div>
</template>
<script>
  export default {
    inject: ['text'],
    created() {
      this.text = 'baz' // 在模板中,依然显示 'bar'
    }
  }
</script>

Эта идея и верна, и неверна из-за специфики обеспечения.

В официальной документации сайта есть такая подсказка о Provide/inject:

намекать:provideа такжеinjectПривязки не реактивны. Это сделано намеренно. Однако, если вы передаете прослушиваемый объект, свойства объекта по-прежнему реагируют.

То есть Vue не будет реактивно обрабатывать переменные в provider. Следовательно, для того, чтобы переменные, принимаемые inject, были чувствительными, переменные, предоставленные провайдером, должны быть чувствительными сами по себе.

Поскольку различные состояния внутри компонента реагируют, мы напрямую внедряем сам компонент в предоставление в корневом компоненте.В это время мы можем произвольно обращаться ко всем состояниям в корневом компоненте в компонентах-потомках, и корневой компонент становится глобальное состояние Контейнер, подумайте, очень ли он похож на контекст в React?

код показывает, как показано ниже:

// 根组件提供将自身提供给后代组件
export default {
  provide () {
    return {
      app: this
    }
  },
  data () {
    return {
      text: 'bar'
    }
  }
}

// 后代组件注入 'app'
<template>
	<div>{{this.app.text}}</div>
</template>
<script>
  export default {
    inject: ['app'],
    created() {
      this.app.text = 'baz' // 在模板中,显示 'baz'
    }
  }
</script>	

Некоторые учащиеся могут спросить: корневой узел по-прежнему можно получить с помощью $root, так зачем нам использовать предоставление/внедрение?

В реальной разработке проект часто разрабатывается несколькими людьми, и каждому человеку могут потребоваться разные глобальные переменные.Если все глобальные переменные единообразно определены в корневом компоненте, это неизбежно вызовет такие проблемы, как конфликты переменных.

Использование предоставления/внедрения из входных компонентов разных модулей в соответствующие дочерние компоненты может прекрасно решить эту проблему.

Используйте обеспечение/внедрение с осторожностью

Поскольку Provide/Inject настолько просты в использовании, то почему VUE официально рекомендуется использовать VUEX, а не оригинальный API?

Я упомянул ранее, Vuex и предоставляющую / предоставить / введенную большую разницу, заключается в том, что каждая модификация глобального состояния в Vuex - отслеживать обратно, а модификация переменных в предоставлении / впрыревших не контролируются, другими словами, вы не знаете, какой компонент модифицировал это глобальное состояние.

Концепция дизайна Vue заимствована из принципа одностороннего потока данных в React (хотя есть такой парень, как синхронизация, который разрушает односторонний поток данных), а предоставление/внедрение явно нарушает принцип одностороннего потока данных. Только представьте, если есть несколько компонентов-потомков, которые одновременно зависят от состояния, предоставленного компонентом-предком, тогда, пока один компонент изменяет состояние, все компоненты будут затронуты. С одной стороны, это увеличивает степень связанности, с другой — делает изменения данных неконтролируемыми. В многопользовательской совместной разработке это становится кошмаром.

Здесь я резюмирую два принципа использования Provide/Inject для глобального управления состоянием:

  1. Когда несколько человек сотрудничают, хорошо поработайте над изоляцией области.
  2. Попробуйте использовать одноразовые данные в качестве глобального состояния

Кажется, что использование Provide/Inject для управления глобальным состоянием кажется опасным, так что есть ли лучший способ использовать Provide/Inject? Конечно, есть, и это для использования Provide/Inject для написания компонентов.

Написание компонентов с использованием Provide/Inject

Использование Provide/Inject для разработки компонентов — это практика, рекомендованная в официальной документации Vue.

Возьмем в качестве примера знакомый elementUI:

В elementUI есть компонент Button (кнопка).При использовании в компоненте Form (формы) на его размер будет влиять свойство size внешнего компонента FormItem и внешнего компонента Form.

Если это обычное решение, мы можем начать с формы через свойства и передавать значения свойств вниз по одному слою за раз. Похоже, нужно просто пройти два слоя и это приемлемо. Однако компонент следующего уровня Form не обязательно является FormItem, а компонент следующего уровня FormItem не обязательно является Button, и между ними могут быть вложены другие компоненты, то есть иерархическая связь неопределенна. Если используются реквизиты, компоненты, которые мы пишем, будут сильно связаны.

Provide/inject может прекрасно решить эту проблему, нужно только внедрить сам компонент (контекст) в потомка, и компонент-потомок может получить доступ к состоянию в компоненте-предке произвольно, независимо от иерархии.

Часть исходного кода выглядит следующим образом:

// Button 组件核心源码
export default {
    name: 'ElButton',
    // 通过 inject 获取 elForm 以及 elFormItem 这两个组件
    inject: {
        elForm: {
            default: ''
        },
        elFormItem: {
            default: ''
        }
    },
    // ...
    computed: {
        _elFormItemSize() {
            return (this.elFormItem || {}).elFormItemSize;
        },
        buttonSize() {
            return this.size || this._elFormItemSize || (this.$ELEMENT || {}).size;
        },
        //...
    },
    // ...
};

Суммировать

Фактически, при изучении Vue, следуя правилу двадцати восьми, 20% API, которые мы обычно используем, могут решить большинство повседневных проблем, а остальные API кажутся менее полезными. Тем не менее, найдите время, чтобы узнать об этих непопулярных API, и, возможно, вы сможете найти какой-нибудь необычный пейзаж, который заставит вас делать больше с меньшими затратами при решении некоторых проблем.