国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > JavaScript > 正文

Vue 2.0 中依賴注入 provide/inject組合實戰

2019-11-19 11:18:18
字體:
來源:轉載
供稿:網友

用法

--------------------------------------------------------------------------------

先來看看官網的介紹:

簡單的說,當組件的引入層次過多,我們的子孫組件想要獲取祖先組件得資源,那么怎么辦呢,總不能一直取父級往上吧,而且這樣代碼結構容易混亂。這個就是這對選項要干的事情

provide和inject需要配合使用,它們的含義如下:

provide        ;一個對象或返回一個對象的函數,該對象包含可注入起子孫的屬性,可以使用ES6的Symbols作為key(只有原生支持Symbol才可以)
inject         ;一個字符串數組或一個對象
                 ;字符串數組    ;provide對象里哪些屬性可用
                ;一個對象        ;key是本地的綁定名,value是provide里對應的對象名,也可以是一個對象,此時from屬性是provide里對應的對象名,default屬性是不存在時的默認值

來個實例就明顯了:

<!DOCTYPE html>    <!--例1-->    <html lang="en"> <head>  <meta charset="UTF-8">  <script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>  <title>Document</title>  </head><body>  <div id="app"><child></child></div>  <script>    Vue.component('child',{      inject:['message'],      template:'<p>{{message}}</p>'    })    new Vue({      el:'#app',provide:{message:'Hello Vue!'}    })  </script></body></html>

輸出:Hello Vue!,對應的DOM節點渲染為:

是不是感覺和props的傳值差不多,我們在中間再嵌套一層組件就知道他的用處了,例如:

<!DOCTYPE html>   <!--例2-->    <html lang="en"><head>  <meta charset="UTF-8">  <title>Document</title>  <script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script></head><body>  <div id="app"><test></test></div>  <script>    Vue.component('child',{      inject:['message'],      template:'<p>{{message}}</p>'    })    Vue.component('test',{      template:`<div><child></child></div>`    })    new Vue({      el:'#app',provide:{message:'Hello Vue!'}    })  </script></body></html>

輸出:Hello Vue!,對應的DOM節點渲染為:

 

就是這個用處吧,多層嵌套時還是很方便的

源碼分析

--------------------------------------------------------------------------------

  provide/inject組合的源碼分為三個部分,分別是組件注冊、Vue實例化和組件實例化的過程,如下:

組件注冊時

注冊時會執行Vue.extend()(第4770行),內部會執行mergeOptions()合并一些屬性,mergeOptions如下:

function mergeOptions (  //第1451行 parent, child, vm) { {  checkComponents(child); } if (typeof child === 'function') {  child = child.options; } normalizeProps(child, vm); normalizeInject(child, vm);     //對inject進行一次規范化 normalizeDirectives(child); var extendsFrom = child.extends; if (extendsFrom) {  parent = mergeOptions(parent, extendsFrom, vm); } if (child.mixins) {  for (var i = 0, l = child.mixins.length; i < l; i++) {   parent = mergeOptions(parent, child.mixins[i], vm);  } } var options = {}; var key; for (key in parent) {  mergeField(key); } for (key in child) {  if (!hasOwn(parent, key)) {   mergeField(key);  } } function mergeField (key) {  var strat = strats[key] || defaultStrat;  options[key] = strat(parent[key], child[key], vm, key); } return options}

normalizeInject定義如下:

function normalizeInject (options, vm) { //第1398行 var inject = options.inject; if (!inject) { return } var normalized = options.inject = {}; if (Array.isArray(inject)) {           //如果inject是一個數組  for (var i = 0; i < inject.length; i++) {      //遍歷inject   normalized[inject[i]] = { from: inject[i] };    //保存到normalized里面,例如:{foo: {from: "foo"}}  } } else if (isPlainObject(inject)) {        //如果inject是一個對象  for (var key in inject) {   var val = inject[key];   normalized[key] = isPlainObject(val)    ? extend({ from: key }, val)    : { from: val };  } } else {  warn(   "Invalid value for option /"inject/": expected an Array or an Object, " +   "but got " + (toRawType(inject)) + ".",   vm  ); }}

對于例1來說,mergeOptions()之后inject等于:{message: {from: "message"}},如下:

Vue實例化時

執行_init()時會執行mergeOptions()進行數據的合并,對于provide的合并策略等于mergeDataOrFn()函數(和data的合并策略是一樣的,定義在1321行),返回一個匿名函數(第1154行),如下:

function mergeDataOrFn (  //第1154行 parentVal, childVal, vm) { if (!vm) {          //這是組件的分支  // in a Vue.extend merge, both should be functions  if (!childVal) {   return parentVal  }  if (!parentVal) {   return childVal  }  // when parentVal & childVal are both present,  // we need to return a function that returns the  // merged result of both functions... no need to  // check if parentVal is a function here because  // it has to be a function to pass previous merges.  return function mergedDataFn () {   return mergeData(    typeof childVal === 'function' ? childVal.call(this, this) : childVal,    typeof parentVal === 'function' ? parentVal.call(this, this) : parentVal   )  } } else {          //這是非組件的實例,返回一個函數  return function mergedInstanceDataFn () {   // instance merge   var instanceData = typeof childVal === 'function'    ? childVal.call(vm, vm)    : childVal;   var defaultData = typeof parentVal === 'function'    ? parentVal.call(vm, vm)    : parentVal;   if (instanceData) {    return mergeData(instanceData, defaultData)   } else {    return defaultData   }  } }}

然后返回到_init()之后會調用initProvide()初始化provide:

function initProvide (vm) {   //第3619行 var provide = vm.$options.provide;        //嘗試獲取provide if (provide) {                  //如果provide存在,當它是函數時執行該返回,否則直接將provide保存到Vue實例的_provided屬性上  vm._provided = typeof provide === 'function'    ? provide.call(vm)   : provide; }}

返回后_provided等于{message:"Hello Vue!"},如下

e]

組件實例化時

 _init()時會執行initInjections(),經過了前面兩步的處理,這里比較簡單了,直接從父Vue或父Vue的父Vue獲取對應的值即可,如下:

function initInjections (vm) {   //第2681行 初始化inject var result = resolveInject(vm.$options.inject, vm);     //遍歷祖先節點,獲取對應的inject,例如:比如:{foo: "bar"} if (result) {                        //如果獲取了對應的值,則將它變成響應式  toggleObserving(false);  Object.keys(result).forEach(function (key) {   /* istanbul ignore else */   {    defineReactive(vm, key, result[key], function () {   //將key編程響應式,這樣就可以訪問該元素了     warn(      "Avoid mutating an injected value directly since the changes will be " +      "overwritten whenever the provided component re-renders. " +      "injection being mutated: /"" + key + "/"",      vm     );    });   }  });  toggleObserving(true); }}function resolveInject (inject, vm) {      //第3649行 確定Inject inject:例如:{foo: {from: "foo"}} vm:當前組件的實例 if (inject) {                          //如果inject非空  // inject is :any because flow is not smart enough to figure out cached  var result = Object.create(null);               //存儲最后的結果  var keys = hasSymbol   ? Reflect.ownKeys(inject).filter(function (key) {          //如果有符號類型,調用Reflect.ownKeys()返回所有的key,再調用filter    /* istanbul ignore next */    return Object.getOwnPropertyDescriptor(inject, key).enumerable   })   : Object.keys(inject);                       //獲取所有的key,此時keys就是個字符串數組,比如:["foo"]  for (var i = 0; i < keys.length; i++) {                //這里遍歷每個key   var key = keys[i];   var provideKey = inject[key].from;   var source = vm;   while (source) {    if (source._provided && hasOwn(source._provided, provideKey)) {  //如果source存在_provided 且 含有provideKey這個屬性     result[key] = source._provided[provideKey];             //則將值保存到result[key]中     break                                //并跳出while循環    }    source = source.$parent;                     //否則將source賦值給父Vue實例,直到找到對應的providekey為止   }   if (!source) {                           //如果最后source不存在,即沒有從當前實例或祖先實例的_provide找到privideKey這個key    if ('default' in inject[key]) {     var provideDefault = inject[key].default;             //如果有定義defult,則使用默認值     result[key] = typeof provideDefault === 'function'      ? provideDefault.call(vm)      : provideDefault;    } else {     warn(("Injection /"" + key + "/" not found"), vm);    }   }  }  return result                            //返回結果,比如:{foo: "bar"} }}

注:provide 和 inject 綁定并不是可響應的。這是刻意為之的。然而,如果你傳入了一個可監聽的對象,那么其對象的屬性還是可響應的。

總結

以上所述是小編給大家介紹的Vue 2.0 中依賴注入 provide/inject組合實戰,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對武林網網站的支持!
如果你覺得本文對你有幫助,歡迎轉載,煩請注明出處,謝謝!

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 九江县| 都兰县| 麻栗坡县| 若尔盖县| 鄄城县| 广元市| 宜昌市| 涿鹿县| 定州市| 谷城县| 金乡县| 南陵县| 宁陕县| 乐都县| 台州市| 无为县| 二连浩特市| 张北县| 石棉县| 开封县| 博客| 鄂州市| 个旧市| 井冈山市| 广南县| 大港区| 文成县| 揭阳市| 玛纳斯县| 惠安县| 旺苍县| 汤阴县| 措美县| 鄂尔多斯市| 龙川县| 平湖市| 呼玛县| 华容县| 海兴县| 甘孜| 霍州市|