インタビュアー: Vue の mixin についての理解を教えてください。アプリケーション シナリオは何ですか?

インタビュアー: Vue の mixin についての理解を教えてください。アプリケーション シナリオは何ですか?

1.ミックスインとは何ですか

Mixinメソッド実装を提供するオブジェクト指向プログラミング言語のクラス。mixin他のクラスは、そのクラスのサブクラスでなくても、そのクラスのメソッドにアクセスできます。

Mixinクラスは通常、機能モジュールとして使用され、機能が必要なときに「混合」されます。これにより、コードの再利用が容易になり、多重継承の複雑さが回避されます。

Vue のミックスイン

まずは公式の定義を見てみましょう

mixin(ミックスイン) は、コンポーネント内で再利用可能な機能を配布するための非常に柔軟な方法を提供しますVue

本質は実際にはオブジェクトでありjs、コンポーネント内の関数オプション ( datacomponentsmethods createdなどcomputed)を含めることができます。

mixins共通関数をオブジェクトとしてオプションに渡すだけでよく、コンポーネントがmixinsそのオブジェクトを使用するときに、すべてのmixinsオブジェクトのオプションがコンポーネント自体のオプションに混合されます。

ローカルでグローバルでも融合Vueできる

部分的なミックスイン

コンポーネントプロパティmixinを使用してオブジェクトを定義するoptionsdatamethods

var myMixin = {
    
    
  created: function () {
    
    
    this.hello()
  },
  methods: {
    
    
    hello: function () {
    
    
      console.log('hello from mixin!')
    }
  }
}

コンポーネントはプロパティを通じてオブジェクトmixinsを呼び出しますmixin

Vue.component('componentA',{
    
    
  mixins: [myMixin]
})

このコンポーネントが使用されると、mixin内部でメソッドが混合され、createdライフフックが自動的に実行され、helloメソッドが実行されます。

グローバルミックスイン

Vue.mixin()グローバルミックスインを作成することで

Vue.mixin({
    
    
  created: function () {
    
    
      console.log("全局混入")
    }
})

グローバル ミックスインを使用する場合は、すべてのコンポーネント インスタンス (サードパーティ コンポーネントを含む) に影響するため、特別な注意が必要です。

PS: グローバル ミックスインはプラグインの作成によく使用されます

予防:

コンポーネントにオブジェクトと同じオプションがある場合、再帰的マージが実行されるmixinとコンポーネントのオプションが上書きされます。mixin

ただし、同じオプションがライフサイクルフックの場合は、配列にマージされ、フックが最初に実行されmixin、次にコンポーネントのフックが実行されます。

2. 利用シーン

日常の開発では、異なるコンポーネントで使用する必要がある同じまたは類似のコードに遭遇することがよくありますが、これらのコードの機能は比較的独立しています。

Vueこのとき、mixin関数を通じて同じまたは類似のコードを提案できます。

例えば

内部的に表示を制御するためにmodal使用されるポップアップ ウィンドウ コンポーネントを定義します。isShowing

const Modal = {
    
    
  template: '#modal',
  data() {
    
    
    return {
    
    
      isShowing: false
    }
  },
  methods: {
    
    
    toggleShow() {
    
    
      this.isShowing = !this.isShowing;
    }
  }
}

プロンプト ボックスを定義しますtooltip。これは内部的にisShowing制御されます。

const Tooltip = {
    
    
  template: '#tooltip',
  data() {
    
    
    return {
    
    
      isShowing: false
    }
  },
  methods: {
    
    
    toggleShow() {
    
    
      this.isShowing = !this.isShowing;
    }
  }
}

上記 2 つのコンポーネントを観察すると、2 つのロジックは同じであり、コード制御の表示も同じであることがわかります。これはこの時点でmixin役に立ちます。

まず共通コードを抽出し、mixin

const toggle = {
    
    
  data() {
    
    
    return {
    
    
      isShowing: false
    }
  },
  methods: {
    
    
    toggleShow() {
    
    
      this.isShowing = !this.isShowing;
    }
  }
}

2 つのコンポーネントを使用するには、導入するだけで済みます。mixin

const Modal = {
    
    
  template: '#modal',
  mixins: [toggle]
};
 
const Tooltip = {
    
    
  template: '#tooltip',
  mixins: [toggle]
}

Mixin上記の小さな例を通して、いくつかの再利用可能な関数をカプセル化することが非常に興味深く、便利で、実用的であることがわかります。

3. ソースコード分析

Vue.mixinから始める

ソースの場所: /src/core/global-api/mixin.js

export function initMixin (Vue: GlobalAPI) {
    
    
  Vue.mixin = function (mixin: Object) {
    
    
    this.options = mergeOptions(this.options, mixin)
    return this
  }
}

merOptions主にメソッドを呼び出す

ソースの場所: /src/core/util/options.js

export function mergeOptions (
  parent: Object,
  child: Object,
  vm?: Component
): Object {
    
    

if (child.mixins) {
    
     // 判断有没有mixin 也就是mixin里面挂mixin的情况 有的话递归进行合并
    for (let i = 0, l = child.mixins.length; i < l; i++) {
    
    
    parent = mergeOptions(parent, child.mixins[i], vm)
    }
}

  const options = {
    
    } 
  let key
  for (key in parent) {
    
    
    mergeField(key) // 先遍历parent的key 调对应的strats[XXX]方法进行合并
  }
  for (key in child) {
    
    
    if (!hasOwn(parent, key)) {
    
     // 如果parent已经处理过某个key 就不处理了
      mergeField(key) // 处理child中的key 也就parent中没有处理过的key
    }
  }
  function mergeField (key) {
    
    
    const strat = strats[key] || defaultStrat
    options[key] = strat(parent[key], child[key], vm, key) // 根据不同类型的options调用strats中不同的方法进行合并
  }
  return options
}

上記のソース コードから、次の点がわかります。

  • 優先再帰処理mixins
  • まず merge をトラバースしparentマージするメソッドをkey呼び出して、それを変数に保存します。mergeFieldoptions
  • 再度トラバースしchild、マージしてマージしてparentkeyマージmergeFieldするメソッドを呼び出し、変数に保存します。options
  • 関数によってmergeField結合される

以下に、Vueマージ戦略のいくつかのタイプを示します。

  • 交換
  • 組み合わせた
  • キューの種類
  • オーバーレイタイプ

交換

交換タイプにはpropsmethodsinject、が組み込まれています。computed

strats.props =
strats.methods =
strats.inject =
strats.computed = function (
  parentVal: ?Object,
  childVal: ?Object,
  vm?: Component,
  key: string
): ?Object {
    
    
  if (!parentVal) return childVal // 如果parentVal没有值,直接返回childVal
  const ret = Object.create(null) // 创建一个第三方对象 ret
  extend(ret, parentVal) // extend方法实际是把parentVal的属性复制到ret中
  if (childVal) extend(ret, childVal) // 把childVal的属性复制到ret中
  return ret
}
strats.provide = mergeDataOrFn

同じ名前のpropsmethodsinjectは、computed後のものに置き換えられます。

組み合わせた

マージ タイプには次のものがあります。data

strats.data = function(parentVal, childVal, vm) {
    
        
    return mergeDataOrFn(
        parentVal, childVal, vm
    )
};

function mergeDataOrFn(parentVal, childVal, vm) {
    
        
    return function mergedInstanceDataFn() {
    
            
        var childData = childVal.call(vm, vm) // 执行data挂的函数得到对象
        var parentData = parentVal.call(vm, vm)        
        if (childData) {
    
                
            return mergeData(childData, parentData) // 将2个对象进行合并                                 
        } else {
    
                
            return parentData // 如果没有childData 直接返回parentData
        }
    }
}

function mergeData(to, from) {
    
        
    if (!from) return to    
    var key, toVal, fromVal;    
    var keys = Object.keys(from);   
    for (var i = 0; i < keys.length; i++) {
    
    
        key = keys[i];
        toVal = to[key];
        fromVal = from[key];    
        // 如果不存在这个属性,就重新设置
        if (!to.hasOwnProperty(key)) {
    
    
            set(to, key, fromVal);
        }      
        // 存在相同属性,合并对象
        else if (typeof toVal =="object" && typeof fromVal =="object") {
    
    
            mergeData(toVal, fromVal);
        }
    }    
    return to
}

mergeDataこの関数は、マージされるデータのすべての属性を調べて、さまざまな状況に応じてマージします。

  • ターゲット データ オブジェクトに現在の属性が含まれていない場合は、setマージするメソッドを呼び出します (実際には、set メソッドはマージと再割り当てのメソッドです)。
  • ターゲット データ オブジェクトに現在の属性が含まれており、現在の値が純粋なオブジェクトである場合、現在のオブジェクトの値を再帰的にマージします。これは、オブジェクトが新しい属性を持たないようにするために行われます。

キューに入れられたマージは次のとおりです: すべてのライフタイムとwatch

function mergeHook (
  parentVal: ?Array<Function>,
  childVal: ?Function | ?Array<Function>
): ?Array<Function> {
    
    
  return childVal
    ? parentVal
      ? parentVal.concat(childVal)
      : Array.isArray(childVal)
        ? childVal
        : [childVal]
    : parentVal
}

LIFECYCLE_HOOKS.forEach(hook => {
    
    
  strats[hook] = mergeHook
})

// watch
strats.watch = function (
  parentVal,
  childVal,
  vm,
  key
) {
    
    
  // work around Firefox's Object.prototype.watch...
  if (parentVal === nativeWatch) {
    
     parentVal = undefined; }
  if (childVal === nativeWatch) {
    
     childVal = undefined; }
  /* istanbul ignore if */
  if (!childVal) {
    
     return Object.create(parentVal || null) }
  {
    
    
    assertObjectType(key, childVal, vm);
  }
  if (!parentVal) {
    
     return childVal }
  var ret = {
    
    };
  extend(ret, parentVal);
  for (var key$1 in childVal) {
    
    
    var parent = ret[key$1];
    var child = childVal[key$1];
    if (parent && !Array.isArray(parent)) {
    
    
      parent = [parent];
    }
    ret[key$1] = parent
      ? parent.concat(child)
      : Array.isArray(child) ? child : [child];
  }
  return ret
};

ライフサイクルフックはwatch配列にマージされ、正の順序で走査されて実行されます。

オーバーレイタイプ

重複するマージは次のとおりです: componentdirectivesfilters

strats.components=
strats.directives=

strats.filters = function mergeAssets(
    parentVal, childVal, vm, key
) {
    
        
    var res = Object.create(parentVal || null);    
    if (childVal) {
    
     
        for (var key in childVal) {
    
    
            res[key] = childVal[key];
        }   
    } 
    return res
}

重ね合わせタイプは、主にプロトタイプ チェーンのレイヤーごとの重ね合わせによって行われます。

まとめ:

  • 置換戦略にはprops、、、、、methodsが含まれますinjectcomputedこれらは、古いパラメータを同じ名前の新しいパラメータに置き換えます。
  • マージ戦略は、メソッドdataによってsetマージおよび再割り当てを行うことです。
  • キュータイプの戦略にはライフサイクル関数があり、watchその原理は関数を配列に格納し、それらを順番に走査して順次実行することです。
  • 重ね合わせタイプにはcomponentdirectives、 、filters、および プロトタイプ チェーンを通じてレイヤーごとに重ね合わされます。

参考文献

  • https://zhuanlan.zhihu.com/p/31018570
  • https://juejin.cn/post/6844904015495446536#Heading-1
  • https://juejin.cn/post/6844903846775357453

おすすめ

転載: blog.csdn.net/weixin_52898349/article/details/132358801