裝飾者模式在日常開(kāi)發(fā)中的縮影和vue中的使用詳解
一、日常開(kāi)發(fā)
裝飾者模式以其不改變?cè)瓕?duì)象,并且與原對(duì)象有著相同接口的特點(diǎn),廣泛應(yīng)用于日常開(kāi)發(fā)和主流框架的功能中。
1、數(shù)據(jù)埋點(diǎn)
假如我們開(kāi)發(fā)了一個(gè)移動(dòng)端網(wǎng)頁(yè),有圖書(shū)搜索、小游戲、音頻播放和視頻播放等主要功能,初期,我們并不知道這幾個(gè)功能用戶(hù)的使用規(guī)律。
有一天,產(chǎn)品經(jīng)理說(shuō),我想要各個(gè)功能用戶(hù)的使用規(guī)律,并且通過(guò)echarts繪制折線(xiàn)圖和柱狀圖,能加嗎?
這就加......
起初:
<button id="smallGameBtn">小游戲</button>
<script>
var enterSmallGame = function () {
console.log('進(jìn)入小游戲')
}
document.getElementById('smallGameBtn').onclick = enterSmallGame;
</script>
通過(guò)裝飾者模式增加數(shù)據(jù)埋點(diǎn)之后:
<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('進(jìn)入小游戲')
}
var dataLog = function () {
console.log('數(shù)據(jù)埋點(diǎn)')
}
enterSmallGame = enterSmallGame.after(dataLog)
document.getElementById('smallGameBtn').onclick = enterSmallGame;
</script>
定義Function.prototype.after函數(shù),其中通過(guò)閉包的方式緩存selfFn,然后返回一個(gè)函數(shù),該函數(shù)首先執(zhí)行selfFn,再執(zhí)行afterFn,這里也很清晰的可以看出兩個(gè)函數(shù)的執(zhí)行順序。
在當(dāng)前例子中,首先執(zhí)行進(jìn)入小游戲的功能,然后,再執(zhí)行數(shù)據(jù)埋點(diǎn)的功能。
可以看出,加了數(shù)據(jù)埋點(diǎn),執(zhí)行函數(shù)是enterSmallGame,不加也是。同時(shí),也未對(duì)函數(shù)enterSmallGame內(nèi)部進(jìn)行修改。
2、表單校驗(yàn)
假如,我們開(kāi)發(fā)了登錄頁(yè)面,有賬號(hào)和密碼輸入框。
我們知道,校驗(yàn)是必須加的功能。
不加校驗(yàn):
賬號(hào):<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>
通過(guò)裝飾者模式加校驗(yàn)之后:
賬號(hào):<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ù),其中通過(guò)閉包的方式緩存selfFn,然后返回一個(gè)函數(shù),該函數(shù)首先執(zhí)行beforeFn,當(dāng)其返回true時(shí),再執(zhí)行afterFn。
在當(dāng)前例子中,首先執(zhí)行表單校驗(yàn)的功能,然后,提示登錄成功,進(jìn)入系統(tǒng)。
可以看出,加了表單校驗(yàn),執(zhí)行函數(shù)是loginFn,不加也是。同時(shí),也未對(duì)函數(shù)loginFn內(nèi)部進(jìn)行修改。
二、框架功能(vue)
1、數(shù)組監(jiān)聽(tīng)
vue的特點(diǎn)之一就是數(shù)據(jù)響應(yīng)式,數(shù)據(jù)的變化會(huì)改變視圖的變化。但是,數(shù)組中通過(guò)下標(biāo)索引的方式直接修改不會(huì)引起視圖變化,通過(guò)push、pop、shift、unshift和splice等方式修改數(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ù)滿(mǎn)足Array.isArray(value),則可通過(guò)protoAugment對(duì)數(shù)據(jù)進(jìn)行處理。
function protoAugment (target, src: Object) {
target.__proto__ = src
}
這里修改目標(biāo)的__proto__ 指向?yàn)?code>src,protoAugment(value, arrayMethods)執(zhí)行的含義就是修改數(shù)組的原型指向?yàn)?code>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
})
})
通過(guò)const arrayProto = Array.prototype的方式緩存Array的原型,通過(guò)const arrayMethods = Object.create(arrayProto)原型繼承的方式讓arrayMethods上繼承了Array原型上的所有方法。這里有定義了包含push和splice等方法的數(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)訪(fǎng)問(wèn)到methodsToPatch中的方法method的時(shí)候,先const result = original.apply(this, args)執(zhí)行的原始方法,獲取到方法的執(zhí)行結(jié)果result。然后通過(guò)switch (method) 的方式針對(duì)不同的方法進(jìn)行參數(shù)的處理,手動(dòng)響應(yīng)式的處理,并且進(jìn)行視圖重新渲染的通知ob.dep.notify()。
整個(gè)過(guò)程可以看出,是對(duì)數(shù)組的原型進(jìn)行了裝飾者模式的處理。目的是,針對(duì)push、pop、shift、unshift和splice等方法進(jìn)行裝飾,當(dāng)通過(guò)這些方法進(jìn)行數(shù)組數(shù)據(jù)的修改時(shí),在執(zhí)行本體函數(shù)arrayProto[method]的同時(shí),還執(zhí)行了手動(dòng)響應(yīng)式處理和視圖更新通知的操作。
2、重寫(xiě)掛載
vue還有特點(diǎn)是支持跨平臺(tái),不僅可以使用在web平臺(tái)運(yùn)行,也可以使用在weex平臺(tái)運(yùn)行。
首先定義了公共的掛載方法:
// 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ò)裝飾者模式處理平臺(tái)相關(guān)的節(jié)點(diǎn)掛載和模板編譯:
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í)行緩存的與平臺(tái)無(wú)關(guān)的mount方法
return mount.call(this, el, hydrating)
}
可以看出,在web平臺(tái)中先緩存公共的掛載方法。當(dāng)其處理完平臺(tái)相關(guān)的掛載節(jié)點(diǎn)和模板編譯等操作后,再去執(zhí)行與平臺(tái)無(wú)關(guān)的掛載方法。
總結(jié)
裝飾者模式,是一種可以首先定義本體函數(shù)進(jìn)行執(zhí)行。然后,在需要進(jìn)行功能添加的時(shí)候,重新定義一個(gè)用來(lái)裝飾的函數(shù)。
裝飾的函數(shù)可以在本體函數(shù)之前執(zhí)行,也可以在本體函數(shù)之后執(zhí)行,在某一天不需要裝飾函數(shù)的時(shí)候,也可以只執(zhí)行本體函數(shù)。因?yàn)楸倔w函數(shù)和通過(guò)裝飾者模式改造的函數(shù)有著相同的接口,而且也可以不必去熟悉本體函數(shù)內(nèi)部的實(shí)現(xiàn)。
以上就是裝飾者模式在日常開(kāi)發(fā)中的縮影和vue中的使用詳解的詳細(xì)內(nèi)容,更多關(guān)于vue 使用裝飾者模式的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Vue路由傳參頁(yè)面刷新后參數(shù)丟失原因和解決辦法
這幾天在開(kāi)發(fā)中遇見(jiàn)的一個(gè)關(guān)于路由傳參后,頁(yè)面刷新數(shù)據(jù)丟失的問(wèn)題,下面這篇文章主要給大家介紹了關(guān)于Vue路由傳參頁(yè)面刷新后參數(shù)丟失原因和解決辦法,需要的朋友可以參考下2022-12-12
vue3+vite:src使用require動(dòng)態(tài)導(dǎo)入圖片報(bào)錯(cuò)的最新解決方法
這篇文章主要介紹了vue3+vite:src使用require動(dòng)態(tài)導(dǎo)入圖片報(bào)錯(cuò)和解決方法,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-04-04
el-input限制輸入正整數(shù)的兩種實(shí)現(xiàn)方式
el-input框是Element UI庫(kù)中的一個(gè)輸入框組件,用于接收用戶(hù)的輸入,這篇文章主要介紹了el-input限制輸入正整數(shù),需要的朋友可以參考下2024-02-02
vue打包上傳服務(wù)器加載提示錯(cuò)誤Loading chunk {n} failed
這篇文章主要為大家介紹了vue打包上傳服務(wù)器加載提示錯(cuò)誤Loading chunk {n} failed解決方法,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-08-08
使用vue-cli創(chuàng)建項(xiàng)目并webpack打包的操作方法
本文給大家分享使用vue-cli創(chuàng)建項(xiàng)目基于webpack模板打包的配置方法,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧2021-07-07

