As we all know, the biggest pain point in component development is communication between components. In Vue, Vue provides a variety of ways to communicate components, from basic props/$EMIT to EventBus for sibling component communication, to Vuex for global data management.
Among so many component communication methods, provide/inject looks very arkaline. However, provide/inject also has its uses. Today, we will talk about the application of provide/ Inject in Vue.
What is a dojo.provide/inject
Provide/Inject is an API added in Vue version 2.2.0. The official website is as follows:
This pair of options needs to be used together to allow an ancestor component to inject a dependency into all of its descendants, regardless of how deep the component hierarchy is, and remain in effect as long as the upstream and downstream relationship is established. If you’re familiar with React, this is very similar to the context features of React.
The explanation on the official website is very confusing, so I translate this sentence:
Provide can specify data or methods that we want to provide to descendant components in the ancestor component, while in any descendant component we can use Inject to receive the data or methods provided by provide.
Here’s an example from 🌰 :
// Parent provides 'foo' var Provider = {provide: {foo: 'bar'}, //... } / / Child components into a 'foo' Child = {var inject: [' foo '], created () {the console. The log (enclosing foo) / / = > "bar"} / /... }Copy the code
As you can see, the foo variable quilt component provided by the parent component was successfully received and used.
After we know what provide/inject is, we will use provide/inject.
Provide/Inject global status management
In daily development, we often use Vuex for state management. However, I personally do not like using Vuex, because Vuex is too cumbersome to use in order to keep the state traceable. However, in the project I participated in before, there were few people cooperating, so this function didn’t mean much to me. I only needed the function of providing global state in Vuex.
So, is there a quick and convenient way to achieve global state? Of course, this is one way to use the provide/inject dark tech API.
One way many people might think about it is to pass in variables in the root component and use them in descendant components.
// The root component provides a non-responsive variable to the descendant component export default {provide () {return {text: 'bar'}}} // Descendant components inject 'app' <template> <div>{{this.text}}</div> </template> <script> export default {inject: ['text'], created() {this.text = 'baz'}} </script>Copy the code
The reason for this idea, both true and false, lies in the particularity of provide.
There is a hint about provide/inject in the official website documentation:
Tip: Provide and Inject binding are not responsive. This is intentional. However, if you pass in a listening object, the object’s properties are still responsive.
That is, Vue does not respond to variables in provide. Therefore, for the variables that inject accept to be reactive, provide variables themselves need to be reactive.
Since the various states inside a component are responsive, we inject the component itself directly into the provide component. In this case, we can access all the states in the root component in any descendant component. The root component becomes a container for global state.
The code is as follows:
Export default {provide () {return {app: this}}, data () {return {text: 'bar'}}} // Descendant components inject 'app' <template> <div>{{this.app.text}}</div> </template> <script> export default {inject: [' app '], created () {this. App. Text = 'baz' / / in the template, according to 'baz}} < / script >Copy the code
Some students may ask: why use provide/inject when $root is still available?
In actual development, a project is often developed by several people, and each person may need different global variables. If everyone’s global variables are uniformly defined in the root component, it is bound to cause problems such as variable conflicts.
Provide /inject gateway components of different modules to their descendants can solve this problem perfectly.
Careful dojo.provide/inject
If provide/inject is so useful, then why does Vue officially recommend using Vuex instead of the native API?
As I mentioned earlier, the biggest difference between Vuex and provide/inject is that every change in the global state in Vuex is traceback, whereas in provide/inject variable changes are not controllable. In other words, you don’t know which component changed the global state.
Vue is designed to borrow the unidirectional data flow principle in React (although sync disrupts unidirectional data flow), while provide/inject obviously breaks the unidirectional data flow principle. Imagine if multiple descendant components depend on a state provided by an ancestor, and if one component modifies that state, all components will be affected. On the one hand, this increases the degree of coupling, and on the other hand, makes the data change uncontrollable. This can be a nightmare in multi-person collaborative development.
Here, I summarize two principles for global state management using provide/inject:
- When multiple people collaborate, isolate the scope
- Use one-time data as global state whenever possible
It seems dangerous to use provide/inject for global state management. Is there a better way to use provide/inject? Of course, write components using provide/inject.
Write components using provide/ Inject
The use of provide/ Inject for component development is advocated in the official Vue documentation.
Take elementUI, which I’m familiar with:
In elementUI there is a Button component, whose size is affected by both the outer FormItem component and the size attribute in the outer Form component when used in the Form component.
In a general scenario, we could pass the property values layer by layer, starting with props from the Form. It looks like you only need two layers of pass-through, which is acceptable. However, the next component of a Form does not have to be a FormItem, the next component of a FormItem does not have to be a Button, and other components can be nested between them, that is, the hierarchy is uncertain. If we use props, we can write components that are strongly coupled.
Provide /inject perfectly solve this problem by injecting the component itself (context) into descendant components, which can access the state of the ancestor component regardless of the hierarchy.
Part of the source code is as follows:
// Button component core source code
export default {
name: 'ElButton'.// Get elForm and elFormItem components by inject
inject: {
elForm: {
default: ' '
},
elFormItem: {
default: ' '}},// ...
computed: {
_elFormItemSize() {
return (this.elFormItem || {}).elFormItemSize;
},
buttonSize() {
return this.size || this._elFormItemSize || (this.$ELEMENT || {}).size;
},
/ /...
},
// ...
};
Copy the code
conclusion
In fact, in Vue learning, following the rule of 20 and 80, the common 20% of the API can solve most of the daily problems, the rest of the API feels useless. However, by taking the time to learn about some of the less popular apis, you may discover some unusual landscapes that will help you solve some problems with less effort.