欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

裝飾者模式在日常開發(fā)中的縮影和vue中的使用詳解

 更新時間:2022年12月22日 10:48:44   作者:qb  
這篇文章主要為大家介紹了裝飾者模式在日常開發(fā)中的縮影和vue中的使用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪

一、日常開發(fā)

裝飾者模式以其不改變原對象,并且與原對象有著相同接口的特點,廣泛應(yīng)用于日常開發(fā)和主流框架的功能中。

1、數(shù)據(jù)埋點

假如我們開發(fā)了一個移動端網(wǎng)頁,有圖書搜索、小游戲、音頻播放和視頻播放等主要功能,初期,我們并不知道這幾個功能用戶的使用規(guī)律。

有一天,產(chǎn)品經(jīng)理說,我想要各個功能用戶的使用規(guī)律,并且通過echarts繪制折線圖和柱狀圖,能加嗎?

這就加......

起初:

<button id="smallGameBtn">小游戲</button>
<script>
    var enterSmallGame = function () {
        console.log('進入小游戲')
    }
    document.getElementById('smallGameBtn').onclick = enterSmallGame;
</script>

通過裝飾者模式增加數(shù)據(jù)埋點之后:

<button id="smallGameBtn">小游戲</button>
<script>
     Function.prototype.after= function (afterFn) {
        var selfFn = this;
        return function () {
            var ret = selfFn.apply(this, arguments)
            afterFn.apply(this.arguments)
            return ret
        }
    }
    var enterSmallGame = function () {
        console.log('進入小游戲')
    }
    var dataLog = function () {
        console.log('數(shù)據(jù)埋點')
    }
    enterSmallGame = enterSmallGame.after(dataLog)
    document.getElementById('smallGameBtn').onclick = enterSmallGame;
</script>

定義Function.prototype.after函數(shù),其中通過閉包的方式緩存selfFn,然后返回一個函數(shù),該函數(shù)首先執(zhí)行selfFn,再執(zhí)行afterFn,這里也很清晰的可以看出兩個函數(shù)的執(zhí)行順序。

在當(dāng)前例子中,首先執(zhí)行進入小游戲的功能,然后,再執(zhí)行數(shù)據(jù)埋點的功能。

可以看出,加了數(shù)據(jù)埋點,執(zhí)行函數(shù)是enterSmallGame,不加也是。同時,也未對函數(shù)enterSmallGame內(nèi)部進行修改。

2、表單校驗

假如,我們開發(fā)了登錄頁面,有賬號和密碼輸入框。

我們知道,校驗是必須加的功能。

不加校驗:

賬號:<input id="account" type="text">
密碼:<input id="password" type="password">
<button id="loginBtn">登錄</button>
<script>
    var loginBtn = document.getElementById('loginBtn')
    var loginFn = function () {
        console.log('登錄成功')
    }
    loginBtn.onclick = loginFn;
</script>

通過裝飾者模式加校驗之后:

賬號:<input id="account" type="text">
密碼:<input id="password" type="password">
<button id="loginBtn">登錄</button>
<script>
    var loginBtn = document.getElementById('loginBtn')
    Function.prototype.before = function (beforeFn) {
        var selfFn = this;
        return function () {
            if (!beforeFn.apply(this, arguments)) {
                return;
            }
            return selfFn.apply(this, arguments)
        }
    }
    var loginFn = function () {
        console.log('登錄成功')
    }
    var validateFn = function () {
        var account = document.getElementById('account').value;
        var password = document.getElementById('password').value;
        return account && password;
    }
    loginFn = loginFn.before(validateFn)
    loginBtn.onclick = loginFn;
</script>

定義Function.prototype.before函數(shù),其中通過閉包的方式緩存selfFn,然后返回一個函數(shù),該函數(shù)首先執(zhí)行beforeFn,當(dāng)其返回true時,再執(zhí)行afterFn

在當(dāng)前例子中,首先執(zhí)行表單校驗的功能,然后,提示登錄成功,進入系統(tǒng)。

可以看出,加了表單校驗,執(zhí)行函數(shù)是loginFn,不加也是。同時,也未對函數(shù)loginFn內(nèi)部進行修改。

二、框架功能(vue)

1、數(shù)組監(jiān)聽

vue的特點之一就是數(shù)據(jù)響應(yīng)式,數(shù)據(jù)的變化會改變視圖的變化。但是,數(shù)組中通過下標(biāo)索引的方式直接修改不會引起視圖變化,通過push、pop、shiftunshiftsplice等方式修改數(shù)據(jù)就可以。

這里我們只看響應(yīng)式處理數(shù)據(jù)的核心邏輯Observer

export class Observer {
  value: any;
  dep: Dep;
  vmCount: number; // number of vms that have this object as root $data
  constructor (value: any) {
    if (Array.isArray(value)) {
       protoAugment(value, arrayMethods)
    }
  }
}

如果需要響應(yīng)式處理的數(shù)據(jù)滿足Array.isArray(value),則可通過protoAugment對數(shù)據(jù)進行處理。

function protoAugment (target, src: Object) {
  target.__proto__ = src
}

這里修改目標(biāo)的__proto__ 指向為src,protoAugment(value, arrayMethods)執(zhí)行的含義就是修改數(shù)組的原型指向為arrayMethods

import { def } from '../util/index'
const arrayProto = Array.prototype
export const arrayMethods = Object.create(arrayProto)
const methodsToPatch = [
  'push',
  'pop',
  'shift',
  'unshift',
  'splice',
  'sort',
  'reverse'
]
methodsToPatch.forEach(function (method) {
  // cache original method
  const original = arrayProto[method]
  def(arrayMethods, method, function mutator (...args) {
    const result = original.apply(this, args)
    const ob = this.__ob__
    let inserted
    switch (method) {
      case 'push':
      case 'unshift':
        inserted = args
        break
      case 'splice':
        inserted = args.slice(2)
        break
    }
    if (inserted) ob.observeArray(inserted)
    // notify change
    ob.dep.notify()
    return result
  })
})

通過const arrayProto = Array.prototype的方式緩存Array的原型,通過const arrayMethods = Object.create(arrayProto)原型繼承的方式讓arrayMethods上繼承了Array原型上的所有方法。這里有定義了包含pushsplice等方法的數(shù)組methodsToPatch,循環(huán)遍歷methodsToPatch數(shù)組并執(zhí)行def方法:

export function def (obj: Object, key: string, val: any, enumerable?: boolean) {
  Object.defineProperty(obj, key, {
    value: val,
    enumerable: !!enumerable,
    writable: true,
    configurable: true
  })
}

這里的目的是當(dāng)訪問到methodsToPatch中的方法method的時候,先const result = original.apply(this, args)執(zhí)行的原始方法,獲取到方法的執(zhí)行結(jié)果result。然后通過switch (method) 的方式針對不同的方法進行參數(shù)的處理,手動響應(yīng)式的處理,并且進行視圖重新渲染的通知ob.dep.notify()。

整個過程可以看出,是對數(shù)組的原型進行了裝飾者模式的處理。目的是,針對push、popshift、unshiftsplice等方法進行裝飾,當(dāng)通過這些方法進行數(shù)組數(shù)據(jù)的修改時,在執(zhí)行本體函數(shù)arrayProto[method]的同時,還執(zhí)行了手動響應(yīng)式處理和視圖更新通知的操作。

2、重寫掛載

vue還有特點是支持跨平臺,不僅可以使用在web平臺運行,也可以使用在weex平臺運行。

首先定義了公共的掛載方法:

// public mount method
Vue.prototype.$mount = function (
  el?: string | Element,
  hydrating?: boolean
): Component {
  el = el && inBrowser ? query(el) : undefined
  return mountComponent(this, el, hydrating)
}

通過裝飾者模式處理平臺相關(guān)的節(jié)點掛載和模板編譯:

const mount = Vue.prototype.$mount
Vue.prototype.$mount = function (
  el?: string | Element,
  hydrating?: boolean
): Component {
  el = el && query(el)
  /* istanbul ignore if */
  if (el === document.body || el === document.documentElement) {
    process.env.NODE_ENV !== 'production' && warn(
      `Do not mount Vue to <html> or <body> - mount to normal elements instead.`
    )
    return this
  }
  const options = this.$options
  // resolve template/el and convert to render function
  if (!options.render) {
    let template = options.template
    if (template) {
      if (typeof template === 'string') {
        if (template.charAt(0) === '#') {
          template = idToTemplate(template)
          /* istanbul ignore if */
          if (process.env.NODE_ENV !== 'production' && !template) {
            warn(
              `Template element not found or is empty: ${options.template}`,
              this
            )
          }
        }
      } else if (template.nodeType) {
        template = template.innerHTML
      } else {
        if (process.env.NODE_ENV !== 'production') {
          warn('invalid template option:' + template, this)   
        }
        return this
      }
    } else if (el) {
      template = getOuterHTML(el)
    }
    if (template) {
      /* istanbul ignore if */
      if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
        mark('compile')
      }
      const { render, staticRenderFns } = compileToFunctions(template, {
        outputSourceRange: process.env.NODE_ENV !== 'production',
        shouldDecodeNewlines,
        shouldDecodeNewlinesForHref,
        delimiters: options.delimiters,
        comments: options.comments
      }, this)
      options.render = render
      options.staticRenderFns = staticRenderFns
      /* istanbul ignore if */
      if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
        mark('compile end')
        measure(`vue ${this._name} compile`, 'compile', 'compile end')
      }
    }
  }
  // 這里執(zhí)行緩存的與平臺無關(guān)的mount方法
  return mount.call(this, el, hydrating)
}

可以看出,在web平臺中先緩存公共的掛載方法。當(dāng)其處理完平臺相關(guān)的掛載節(jié)點和模板編譯等操作后,再去執(zhí)行與平臺無關(guān)的掛載方法。

總結(jié)

裝飾者模式,是一種可以首先定義本體函數(shù)進行執(zhí)行。然后,在需要進行功能添加的時候,重新定義一個用來裝飾的函數(shù)。

裝飾的函數(shù)可以在本體函數(shù)之前執(zhí)行,也可以在本體函數(shù)之后執(zhí)行,在某一天不需要裝飾函數(shù)的時候,也可以只執(zhí)行本體函數(shù)。因為本體函數(shù)和通過裝飾者模式改造的函數(shù)有著相同的接口,而且也可以不必去熟悉本體函數(shù)內(nèi)部的實現(xiàn)。

以上就是裝飾者模式在日常開發(fā)中的縮影和vue中的使用詳解的詳細(xì)內(nèi)容,更多關(guān)于vue 使用裝飾者模式的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評論