RubyLouvre/avalon

View on GitHub
src/vmodel/Action.js

Summary

Maintainability
A
2 hrs
Test Coverage
import {
    avalon
} from '../seed/core'

import {
    runActions,
    collectDeps
} from './transaction'

import {
    createGetter,
    createSetter
} from "../parser/index"

export var actionUUID = 1
    //需要重构
export function Action(vm, options, callback) {
    for (var i in options) {
        if (protectedMenbers[i] !== 1) {
            this[i] = options[i]
        }
    }

    this.vm = vm
    this.observers = []
    this.callback = callback
    this.uuid = ++actionUUID
    this.ids = ''
    this.mapIDs = {} //这个用于去重
    this.isAction = true
    var expr = this.expr
        // 缓存取值函数
    if (typeof this.getter !== 'function') {
        this.getter = createGetter(expr, this.type)
    }
    // 缓存设值函数(双向数据绑定)
    if (this.type === 'duplex') {
        this.setter = createSetter(expr, this.type)
    }
    // 缓存表达式旧值
    this.value = NaN
        // 表达式初始值 & 提取依赖
    if (!(this.node)) {
        this.value = this.get()
    }
}

Action.prototype = {
    getValue() {
        var scope = this.vm
        try {
            return this.getter.call(scope, scope)
        } catch (e) {
            avalon.log(this.getter + ' exec error')
        }
    },

    setValue(value) {
        var scope = this.vm
        if (this.setter) {
            this.setter.call(scope, scope, value)
        }
    },

    // get --> getValue --> getter
    get(fn) {
        var name = 'action track ' + this.type

        if (this.deep) {
            avalon.deepCollect = true
        }

        var value = collectDeps(this, this.getValue)
        if (this.deep && avalon.deepCollect) {
            avalon.deepCollect = false
        }

        return value
    },

    /**
     * 在更新视图前保存原有的value
     */
    beforeUpdate() {
        return this.oldValue = getPlainObject(this.value)
    },


    update(args, uuid) {
        var oldVal = this.beforeUpdate()
        var newVal = this.value = this.get()
        var callback = this.callback
        if (callback && this.diff(newVal, oldVal, args)) {
            callback.call(this.vm, this.value, oldVal, this.expr)
        }
        this._isScheduled = false
    },
    schedule() {
        if (!this._isScheduled) {
            this._isScheduled = true
            if (!avalon.uniqActions[this.uuid]) {
                avalon.uniqActions[this.uuid] = 1
                avalon.pendingActions.push(this)
            }

            runActions() //这里会还原_isScheduled


        }
    },
    removeDepends() {
        var self = this
        this.observers.forEach(function(depend) {
            avalon.Array.remove(depend.observers, self)
        })
    },

    /**
     * 比较两个计算值是否,一致,在for, class等能复杂数据类型的指令中,它们会重写diff复法
     */
    diff(a, b) {
        return a !== b
    },

    /**
     * 销毁指令
     */
    dispose() {
        this.value = null
        this.removeDepends()
        if (this.beforeDispose) {
            this.beforeDispose()
        }
        for (var i in this) {
            delete this[i]
        }
    }

}

function getPlainObject(v) {
    if (v && typeof v === 'object') {
        if (v && v.$events) {
            return v.$model
        } else if (Array.isArray(v)) {
            let ret = []
            for (let i = 0, n = v.length; i < n; i++) {
                ret.push(getPlainObject(v[i]))
            }
            return ret
        } else {
            let ret = {}
            for (let i in v) {
                ret[i] = getPlainObject(v[i])
            }
            return ret
        }
    } else {
        return v
    }
}

export var protectedMenbers = {
    vm: 1,
    callback: 1,

    observers: 1,
    oldValue: 1,
    value: 1,
    getValue: 1,
    setValue: 1,
    get: 1,

    removeDepends: 1,
    beforeUpdate: 1,
    update: 1,
    //diff
    //getter
    //setter
    //expr
    //vdom
    //type: "for"
    //name: "ms-for"
    //attrName: ":for"
    //param: "click"
    //beforeDispose
    dispose: 1
}