forked from fanruan/fineui
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1546 lines
44 KiB
1546 lines
44 KiB
import { _ } from "@/core"; |
|
|
|
function _classCallCheck(instance, Constructor) { |
|
if (!(instance instanceof Constructor)) { |
|
throw new TypeError("Cannot call a class as a function"); |
|
} |
|
} |
|
|
|
function noop(a, b, c) {} |
|
|
|
function isNative(Ctor) { |
|
return typeof Ctor === "function" && /native code/.test(Ctor.toString()); |
|
} |
|
|
|
const hasProto = "__proto__" in {}; |
|
|
|
const _toString = Object.prototype.toString; |
|
|
|
function isPlainObject(obj) { |
|
return _toString.call(obj) === "[object Object]"; |
|
} |
|
|
|
function isConfigurable(obj, key) { |
|
let configurable = true; |
|
const property = |
|
Object.getOwnPropertyDescriptor && |
|
Object.getOwnPropertyDescriptor(obj, key); |
|
if (property && property.configurable === false) { |
|
configurable = false; |
|
} |
|
|
|
return configurable; |
|
} |
|
|
|
function isExtensible(obj) { |
|
if (Object.isExtensible) { |
|
return Object.isExtensible(obj); |
|
} |
|
let name = ""; |
|
while (obj.hasOwnProperty(name)) { |
|
name += "?"; |
|
} |
|
obj[name] = true; |
|
const returnValue = obj.hasOwnProperty(name); |
|
delete obj[name]; |
|
|
|
return returnValue; |
|
} |
|
|
|
function remove(arr, item) { |
|
if (arr && arr.length) { |
|
const _index = arr.indexOf(item); |
|
if (_index > -1) { |
|
return arr.splice(_index, 1); |
|
} |
|
} |
|
} |
|
|
|
// const bailRE = /[^\w.$]/ |
|
|
|
function parsePath(path) { |
|
// 正常表达式比较慢,能不要的就不要了 |
|
// if (bailRE.test(path)) { |
|
// return |
|
// } |
|
const segments = path.split("."); |
|
|
|
return function (obj) { |
|
for (let i = 0; i < segments.length; i++) { |
|
if (!obj) return; |
|
obj = obj[segments[i]]; |
|
} |
|
|
|
return obj; |
|
}; |
|
} |
|
|
|
const nextTick = (function () { |
|
const callbacks = []; |
|
let pending = false; |
|
let timerFunc = void 0; |
|
|
|
function nextTickHandler() { |
|
pending = false; |
|
const copies = callbacks.slice(0); |
|
callbacks.length = 0; |
|
for (let i = 0; i < copies.length; i++) { |
|
copies[i](); |
|
} |
|
} |
|
|
|
// An asynchronous deferring mechanism. |
|
// In pre 2.4, we used to use microtasks (Promise/MutationObserver) |
|
// but microtasks actually has too high a priority and fires in between |
|
// supposedly sequential events (e.g. #4521, #6690) or even between |
|
// bubbling of the same event (#6566). Technically setImmediate should be |
|
// the ideal choice, but it's not available everywhere; and the only polyfill |
|
// that consistently queues the callback after all DOM events triggered in the |
|
// same loop is by using MessageChannel. |
|
/* istanbul ignore if */ |
|
if (typeof setImmediate !== "undefined" && isNative(setImmediate)) { |
|
timerFunc = function timerFunc() { |
|
setImmediate(nextTickHandler); |
|
}; |
|
} else if ( |
|
typeof MessageChannel !== "undefined" && |
|
(isNative(MessageChannel) || |
|
// PhantomJS |
|
MessageChannel.toString() === "[object MessageChannelConstructor]") |
|
) { |
|
const channel = new MessageChannel(); |
|
const port = channel.port2; |
|
channel.port1.onmessage = nextTickHandler; |
|
timerFunc = function timerFunc() { |
|
port.postMessage(1); |
|
}; |
|
} else if (typeof Promise !== "undefined" && isNative(Promise)) { |
|
/* istanbul ignore next */ |
|
// use microtask in non-DOM environments, e.g. Weex |
|
const p = Promise.resolve(); |
|
timerFunc = function timerFunc() { |
|
p.then(nextTickHandler); |
|
}; |
|
} else { |
|
// fallback to setTimeout |
|
timerFunc = function timerFunc() { |
|
setTimeout(nextTickHandler, 0); |
|
}; |
|
} |
|
|
|
return function queueNextTick(cb, ctx) { |
|
let _resolve = void 0; |
|
callbacks.push(() => { |
|
if (cb) { |
|
try { |
|
cb.call(ctx); |
|
} catch (e) { |
|
console.error(e); |
|
} |
|
} else if (_resolve) { |
|
_resolve(ctx); |
|
} |
|
}); |
|
if (!pending) { |
|
pending = true; |
|
timerFunc(); |
|
} |
|
// $flow-disable-line |
|
if (!cb && typeof Promise !== "undefined") { |
|
return new Promise((resolve, reject) => { |
|
_resolve = resolve; |
|
}); |
|
} |
|
}; |
|
})(); |
|
|
|
let falsy; |
|
const $$skipArray = { |
|
__ob__: falsy, |
|
$accessors: falsy, |
|
$vbthis: falsy, |
|
$vbsetter: falsy |
|
}; |
|
|
|
let uid = 0; |
|
|
|
/** |
|
* A dep is an observable that can have multiple |
|
* directives subscribing to it. |
|
*/ |
|
|
|
const Dep = (function () { |
|
function Dep() { |
|
_classCallCheck(this, Dep); |
|
|
|
this.id = uid++; |
|
this.subs = []; |
|
} |
|
|
|
Dep.prototype.addSub = function addSub(sub) { |
|
this.subs.push(sub); |
|
}; |
|
|
|
Dep.prototype.removeSub = function removeSub(sub) { |
|
remove(this.subs, sub); |
|
}; |
|
|
|
Dep.prototype.depend = function depend() { |
|
if (Dep.target) { |
|
Dep.target.addDep(this); |
|
} |
|
}; |
|
|
|
Dep.prototype.notify = function notify(options) { |
|
// stabilize the subscriber list first |
|
const subs = this.subs.slice(); |
|
for (let i = 0, l = subs.length; i < l; i++) { |
|
subs[i].update(options); |
|
} |
|
}; |
|
|
|
return Dep; |
|
})(); |
|
|
|
// the current target watcher being evaluated. |
|
// this is globally unique because there could be only one |
|
// watcher being evaluated at any time. |
|
|
|
Dep.target = null; |
|
const targetStack = []; |
|
|
|
function pushTarget(_target) { |
|
if (Dep.target) targetStack.push(Dep.target); |
|
Dep.target = _target; |
|
} |
|
|
|
function popTarget() { |
|
Dep.target = targetStack.pop(); |
|
} |
|
|
|
// 如果浏览器不支持ecma262v5的Object.defineProperties或者存在BUG,比如IE8 |
|
// 标准浏览器使用__defineGetter__, __defineSetter__实现 |
|
let canHideProperty = true; |
|
try { |
|
Object.defineProperty({}, "_", { |
|
value: "x" |
|
}); |
|
delete $$skipArray.$vbsetter; |
|
delete $$skipArray.$vbthis; |
|
} catch (e) { |
|
/* istanbul ignore next*/ |
|
canHideProperty = false; |
|
} |
|
|
|
let createViewModel = Object.defineProperties; |
|
let defineProperty = void 0; |
|
|
|
/* istanbul ignore if*/ |
|
if (!canHideProperty) { |
|
if ("__defineGetter__" in {}) { |
|
defineProperty = function defineProperty(obj, prop, desc) { |
|
if ("value" in desc) { |
|
obj[prop] = desc.value; |
|
} |
|
if ("get" in desc) { |
|
obj.__defineGetter__(prop, desc.get); |
|
} |
|
if ("set" in desc) { |
|
obj.__defineSetter__(prop, desc.set); |
|
} |
|
|
|
return obj; |
|
}; |
|
createViewModel = function createViewModel(obj, descs) { |
|
for (const prop in descs) { |
|
if (descs.hasOwnProperty(prop)) { |
|
defineProperty(obj, prop, descs[prop]); |
|
} |
|
} |
|
|
|
return obj; |
|
}; |
|
} |
|
} |
|
|
|
const createViewModel$1 = createViewModel; |
|
|
|
const queue = []; |
|
const activatedChildren = []; |
|
let has = {}; |
|
let waiting = false; |
|
let flushing = false; |
|
let index = 0; |
|
|
|
function resetSchedulerState() { |
|
index = queue.length = activatedChildren.length = 0; |
|
has = {}; |
|
waiting = flushing = false; |
|
} |
|
|
|
function flushSchedulerQueue() { |
|
flushing = true; |
|
let watcher = void 0, |
|
id = void 0, |
|
options = void 0; |
|
|
|
// Sort queue before flush. |
|
// This ensures that: |
|
// 1. Components are updated from parent to child. (because parent is always |
|
// created before the child) |
|
// 2. A component's user watchers are run before its render watcher (because |
|
// user watchers are created before the render watcher) |
|
// 3. If a component is destroyed during a parent component's watcher run, |
|
// its watchers can be skipped. |
|
queue.sort((a, b) => a.id - b.id); |
|
|
|
// do not cache length because more watchers might be pushed |
|
// as we run existing watchers |
|
for (index = 0; index < queue.length; index++) { |
|
watcher = queue[index].watcher; |
|
options = queue[index].options; |
|
id = watcher.id; |
|
has[id] = null; |
|
watcher.run(options); |
|
} |
|
|
|
resetSchedulerState(); |
|
} |
|
|
|
function queueWatcher(watcher, options) { |
|
const id = watcher.id; |
|
if (has[id] == null) { |
|
has[id] = true; |
|
if (!flushing) { |
|
queue.push({ watcher, options }); |
|
} else { |
|
// if already flushing, splice the watcher based on its id |
|
// if already past its id, it will be run next immediately. |
|
let i = queue.length - 1; |
|
while (i > index && queue[i].watcher.id > watcher.id) { |
|
i--; |
|
} |
|
queue.splice(i + 1, 0, { watcher, options }); |
|
} |
|
// queue the flush |
|
if (!waiting) { |
|
waiting = true; |
|
nextTick(flushSchedulerQueue); |
|
} |
|
} |
|
} |
|
|
|
let uid$1 = 0; |
|
|
|
const Watcher = (function () { |
|
function Watcher(vm, expOrFn, cb, options) { |
|
_classCallCheck(this, Watcher); |
|
|
|
this.vm = vm; |
|
// vm._watchers || (vm._watchers = []) |
|
// vm._watchers.push(this) |
|
// options |
|
if (options) { |
|
this.deep = !!options.deep; |
|
this.user = !!options.user; |
|
this.lazy = !!options.lazy; |
|
this.sync = !!options.sync; |
|
} else { |
|
this.deep = this.user = this.lazy = this.sync = false; |
|
} |
|
this.cb = cb; |
|
this.id = ++uid$1; // uid for batching |
|
this.active = true; |
|
this.dirty = this.lazy; // for lazy watchers |
|
this.deps = []; |
|
this.newDeps = []; |
|
this.depIds = new Set(); |
|
this.newDepIds = new Set(); |
|
this.expression = ""; |
|
// parse expression for getter |
|
if (typeof expOrFn === "function") { |
|
this.getter = expOrFn; |
|
} else { |
|
this.getter = parsePath(expOrFn); |
|
if (!this.getter) { |
|
this.getter = function () {}; |
|
} |
|
} |
|
this.value = this.lazy ? undefined : this.get(); |
|
} |
|
|
|
Watcher.prototype.get = function get() { |
|
pushTarget(this); |
|
let value = void 0; |
|
const vm = this.vm; |
|
try { |
|
value = this.getter.call(vm, vm); |
|
} catch (e) { |
|
// if (this.user) { |
|
// } else { |
|
// console.error(e) |
|
// } |
|
} finally { |
|
// "touch" every property so they are all tracked as |
|
// dependencies for deep watching |
|
if (this.deep) { |
|
traverse(value); |
|
} |
|
popTarget(); |
|
this.cleanupDeps(); |
|
} |
|
|
|
return value; |
|
}; |
|
|
|
Watcher.prototype.addDep = function addDep(dep) { |
|
const id = dep.id; |
|
if (!this.newDepIds.has(id)) { |
|
this.newDepIds.add(id); |
|
this.newDeps.push(dep); |
|
if (!this.depIds.has(id)) { |
|
dep.addSub(this); |
|
} |
|
} |
|
}; |
|
|
|
Watcher.prototype.cleanupDeps = function cleanupDeps() { |
|
let i = this.deps.length; |
|
while (i--) { |
|
const dep = this.deps[i]; |
|
if (!this.newDepIds.has(dep.id)) { |
|
dep.removeSub(this); |
|
} |
|
} |
|
let tmp = this.depIds; |
|
this.depIds = this.newDepIds; |
|
this.newDepIds = tmp; |
|
this.newDepIds.clear(); |
|
tmp = this.deps; |
|
this.deps = this.newDeps; |
|
this.newDeps = tmp; |
|
this.newDeps.length = 0; |
|
}; |
|
|
|
Watcher.prototype.update = function update(options) { |
|
/* istanbul ignore else */ |
|
if (this.lazy) { |
|
this.dirty = true; |
|
} else if (this.sync) { |
|
this.run(options); |
|
} else { |
|
queueWatcher(this, options); |
|
} |
|
}; |
|
|
|
Watcher.prototype.run = function run(options) { |
|
if (this.active) { |
|
const value = this.get(); |
|
if ( |
|
value !== this.value || |
|
// Deep watchers and watchers on Object/Arrays should fire even |
|
// when the value is the same, because the value may |
|
// have mutated. |
|
(_.isObject(value) && options && options.refresh) || |
|
this.deep |
|
) { |
|
// set new value |
|
const oldValue = this.value; |
|
this.value = value; |
|
if (this.user) { |
|
try { |
|
this.cb.call(this.vm, value, oldValue, options); |
|
} catch (e) { |
|
console.error(e); |
|
} |
|
} else { |
|
try { |
|
this.cb.call(this.vm, value, oldValue, options); |
|
} catch (e) { |
|
console.error(e); |
|
} |
|
} |
|
} |
|
} |
|
}; |
|
|
|
Watcher.prototype.evaluate = function evaluate() { |
|
this.value = this.get(); |
|
this.dirty = false; |
|
}; |
|
|
|
Watcher.prototype.depend = function depend() { |
|
let i = this.deps.length; |
|
while (i--) { |
|
this.deps[i].depend(); |
|
} |
|
}; |
|
|
|
Watcher.prototype.teardown = function teardown() { |
|
if (this.active) { |
|
// remove self from vm's watcher list |
|
// this is a somewhat expensive operation so we skip it |
|
// if the vm is being destroyed. |
|
remove(this.vm && this.vm._watchers, this); |
|
let i = this.deps.length; |
|
while (i--) { |
|
this.deps[i].removeSub(this); |
|
} |
|
this.active = false; |
|
} |
|
}; |
|
|
|
return Watcher; |
|
})(); |
|
|
|
const seenObjects = new Set(); |
|
|
|
function traverse(val) { |
|
seenObjects.clear(); |
|
_traverse(val, seenObjects); |
|
} |
|
|
|
function _traverse(val, seen) { |
|
let i = void 0, |
|
keys = void 0; |
|
const isA = _.isArray(val); |
|
if (!isA && !_.isObject(val)) { |
|
return; |
|
} |
|
if (val.__ob__) { |
|
const depId = val.__ob__.dep.id; |
|
if (seen.has(depId)) { |
|
return; |
|
} |
|
seen.add(depId); |
|
} |
|
if (isA) { |
|
i = val.length; |
|
while (i--) { |
|
_traverse(val[i], seen); |
|
} |
|
} else { |
|
keys = _.keys(val); |
|
i = keys.length; |
|
while (i--) { |
|
_traverse(val[keys[i]], seen); |
|
} |
|
} |
|
} |
|
|
|
const arrayProto = Array.prototype; |
|
const arrayMethods = []; |
|
_.each( |
|
["push", "pop", "shift", "unshift", "splice", "sort", "reverse"], |
|
(method) => { |
|
const original = arrayProto[method]; |
|
arrayMethods[method] = function mutator() { |
|
for ( |
|
var _len = arguments.length, args = Array(_len), _key2 = 0; |
|
_key2 < _len; |
|
_key2++ |
|
) { |
|
args[_key2] = arguments[_key2]; |
|
} |
|
|
|
const ob = this.__ob__; |
|
let inserted = void 0; |
|
switch (method) { |
|
case "push": |
|
case "unshift": |
|
inserted = args; |
|
break; |
|
case "splice": |
|
inserted = args.slice(2); |
|
break; |
|
} |
|
if (inserted) inserted = ob.observeArray(inserted); |
|
switch (method) { |
|
case "push": |
|
case "unshift": |
|
args = inserted; |
|
break; |
|
case "splice": |
|
if (args.length > 2) { |
|
args = [args[0], args[1]].concat( |
|
inserted ? inserted : [] |
|
); |
|
} |
|
break; |
|
} |
|
const result = original.apply(this, args); |
|
notify(ob.parent, ob.parentKey, ob.dep, true); |
|
|
|
return result; |
|
}; |
|
} |
|
); |
|
|
|
const arrayKeys = _.keys(arrayMethods); |
|
|
|
const observerState = { |
|
shouldConvert: true |
|
}; |
|
|
|
function def(obj, key, val, enumerable) { |
|
Object.defineProperty(obj, key, { |
|
value: val, |
|
enumerable: !!enumerable, |
|
writable: true, |
|
configurable: true |
|
}); |
|
} |
|
|
|
/** |
|
* Observer class that are attached to each observed |
|
* object. Once attached, the observer converts target |
|
* object's property keys into getter/setters that |
|
* collect dependencies and dispatches updates. |
|
*/ |
|
|
|
const Observer = (function () { |
|
function Observer(value) { |
|
_classCallCheck(this, Observer); |
|
|
|
this.value = value; |
|
this.dep = new Dep(); |
|
this.vmCount = 0; |
|
if (_.isArray(value)) { |
|
const augment = hasProto ? protoAugment : copyAugment; |
|
augment(value, arrayMethods, arrayKeys); |
|
this.model = this.observeArray(value); |
|
} else { |
|
this.model = this.walk(value); |
|
} |
|
def(this.model, "__ob__", this); |
|
} |
|
|
|
Observer.prototype.walk = function walk(obj) { |
|
return defineReactive(obj, this); |
|
}; |
|
|
|
Observer.prototype.observeArray = function observeArray(items) { |
|
for (let i = 0, l = items.length; i < l; i++) { |
|
const ob = observe(items[i], this, i); |
|
items[i] = ob ? ob.model : items[i]; |
|
} |
|
|
|
return items; |
|
}; |
|
|
|
return Observer; |
|
})(); |
|
|
|
function protoAugment(target, src, keys) { |
|
/* eslint-disable no-proto */ |
|
target.__proto__ = src; |
|
/* eslint-enable no-proto */ |
|
} |
|
|
|
/* istanbul ignore next */ |
|
function copyAugment(target, src, keys) { |
|
for (let i = 0, l = keys.length; i < l; i++) { |
|
const key = keys[i]; |
|
target[key] = src[key]; |
|
} |
|
} |
|
|
|
function observe(value, parentObserver, parentKey) { |
|
if (!_.isObject(value)) { |
|
return; |
|
} |
|
let ob = void 0; |
|
if (value.__ob__ instanceof Observer) { |
|
ob = value.__ob__; |
|
} else if ( |
|
observerState.shouldConvert && |
|
isExtensible(value) && |
|
(_.isArray(value) || isPlainObject(value)) |
|
) { |
|
ob = new Observer(value); |
|
} |
|
if (ob) { |
|
ob.parent = parentObserver || ob.parent; |
|
ob.parentKey = parentKey; |
|
} |
|
|
|
return ob; |
|
} |
|
|
|
function notify(observer, key, dep, refresh) { |
|
dep.notify({ observer, key, refresh }); |
|
if (observer) { |
|
// 触发a.*绑定的依赖 |
|
_.each(observer._deps, (dep) => { |
|
dep.notify({ observer, key }); |
|
}); |
|
// 触发a.**绑定的依赖 |
|
let parent = observer, |
|
root = observer, |
|
route = key || ""; |
|
while (parent) { |
|
_.each(parent._scopeDeps, (dep) => { |
|
dep.notify({ observer, key }); |
|
}); |
|
if (parent.parentKey != null) { |
|
route = `${parent.parentKey}.${route}`; |
|
} |
|
root = parent; |
|
parent = parent.parent; |
|
} |
|
for (const _key in root._globalDeps) { |
|
const reg = new RegExp(_key); |
|
if (reg.test(route)) { |
|
for (let i = 0; i < root._globalDeps[_key].length; i++) { |
|
root._globalDeps[_key][i].notify({ |
|
observer, |
|
key: _key |
|
}); |
|
} |
|
} |
|
} |
|
} |
|
} |
|
|
|
function defineReactive(obj, observer, shallow) { |
|
const props = {}; |
|
let model = void 0; |
|
_.each(obj, (val, key) => { |
|
if (key in $$skipArray) { |
|
return; |
|
} |
|
const configurable = isConfigurable(obj, key); |
|
const dep = (observer && observer[`__dep${key}`]) || new Dep(); |
|
observer && (observer[`__dep${key}`] = dep); |
|
let childOb = configurable && !shallow && observe(val, observer, key); |
|
props[key] = { |
|
enumerable: true, |
|
configurable: true, |
|
get: function reactiveGetter() { |
|
const value = childOb ? childOb.model : val; |
|
if (Dep.target) { |
|
dep.depend(); |
|
if (childOb) { |
|
childOb.dep.depend(); |
|
if (_.isArray(value)) { |
|
dependArray(value); |
|
} |
|
} |
|
} |
|
|
|
return value; |
|
}, |
|
set: function reactiveSetter(newVal) { |
|
const value = childOb ? childOb.model : val; |
|
if ( |
|
newVal === value || |
|
(newVal !== newVal && value !== value) |
|
) { |
|
return; |
|
} |
|
val = newVal; |
|
childOb = |
|
configurable && !shallow && observe(newVal, observer, key); |
|
if (childOb && value && value.__ob__) { |
|
childOb._scopeDeps = value.__ob__._scopeDeps; |
|
childOb._deps = value.__ob__._deps; |
|
} |
|
obj[key] = childOb ? childOb.model : newVal; |
|
notify(model.__ob__, key, dep); |
|
} |
|
}; |
|
}); |
|
|
|
return (model = createViewModel$1(obj, props)); |
|
} |
|
|
|
function defineReactiveProperty(obj, key, val, shallow) { |
|
const dep = new Dep(); |
|
|
|
const configurable = isConfigurable(obj, key); |
|
if (!configurable) { |
|
return; |
|
} |
|
|
|
if (arguments.length === 2) { |
|
val = obj[key]; |
|
} |
|
|
|
let childOb = !shallow && observe(val); |
|
Object.defineProperty(obj, key, { |
|
enumerable: true, |
|
configurable: true, |
|
get: function reactiveGetter() { |
|
const value = childOb ? childOb.model : val; |
|
if (Dep.target) { |
|
dep.depend(); |
|
if (childOb) { |
|
childOb.dep.depend(); |
|
if (_.isArray(value)) { |
|
dependArray(value); |
|
} |
|
} |
|
} |
|
|
|
return value; |
|
}, |
|
set: function reactiveSetter(newVal) { |
|
const value = childOb ? childOb.model : val; |
|
if (newVal === value || (newVal !== newVal && value !== value)) { |
|
return; |
|
} |
|
|
|
childOb = configurable && !shallow && observe(newVal); |
|
val = newVal; |
|
obj[key] = childOb ? childOb.model : newVal; |
|
dep.notify(); |
|
} |
|
}); |
|
} |
|
|
|
/** |
|
* Set a property on an object. Adds the new property and |
|
* triggers change notification if the property doesn't |
|
* already exist. |
|
*/ |
|
function set(target, key, val) { |
|
if (_.isArray(target)) { |
|
target.length = Math.max(target.length, key); |
|
target.splice(key, 1, val); |
|
|
|
return val; |
|
} |
|
if (_.has(target, key)) { |
|
target[key] = val; |
|
|
|
return val; |
|
} |
|
const ob = target.__ob__; |
|
if (!ob) { |
|
target[key] = val; |
|
|
|
return val; |
|
} |
|
ob.value[key] = val; |
|
target = defineReactive(ob.value, ob); |
|
notify(ob, key, ob.dep); |
|
|
|
return target; |
|
} |
|
|
|
function freeze() { |
|
return Object.freeze.apply(null, arguments); |
|
} |
|
|
|
/** |
|
* Delete a property and trigger change if necessary. |
|
*/ |
|
function del(target, key) { |
|
if (_.isArray(target)) { |
|
target.splice(key, 1); |
|
|
|
return; |
|
} |
|
const ob = target.__ob__; |
|
if (!_.has(target, key)) { |
|
return; |
|
} |
|
if (!ob) { |
|
delete target[key]; |
|
|
|
return target; |
|
} |
|
delete ob.value[key]; |
|
target = defineReactive(ob.value, ob); |
|
notify(ob, key, ob.dep); |
|
|
|
return target; |
|
} |
|
|
|
/** |
|
* Collect dependencies on array elements when the array is touched, since |
|
* we cannot intercept array element access like property getters. |
|
*/ |
|
function dependArray(value) { |
|
for (var e, i = 0, l = value.length; i < l; i++) { |
|
e = value[i]; |
|
e && e.__ob__ && e.__ob__.dep.depend(); |
|
if (_.isArray(e)) { |
|
dependArray(e); |
|
} |
|
} |
|
} |
|
|
|
let falsy$1; |
|
const operators = { |
|
"||": falsy$1, |
|
"&&": falsy$1, |
|
"(": falsy$1, |
|
")": falsy$1 |
|
}; |
|
|
|
function runBinaryFunction(binarys) { |
|
let expr = ""; |
|
for (let i = 0, len = binarys.length; i < len; i++) { |
|
if (_.isBoolean(binarys[i]) || _.has(operators, binarys[i])) { |
|
expr += binarys[i]; |
|
} else { |
|
expr += "false"; |
|
} |
|
} |
|
|
|
return new Function(`return ${expr}`)(); |
|
} |
|
|
|
function routeToRegExp(route) { |
|
route = route |
|
.replace(/\*\*/g, "[a-zA-Z0-9_]+") |
|
.replace(/\*./g, "[a-zA-Z0-9_]+."); |
|
|
|
return `^${route}$`; |
|
} |
|
|
|
function watch(model, expOrFn, cb, options) { |
|
if (isPlainObject(cb)) { |
|
options = cb; |
|
cb = cb.handler; |
|
} |
|
if (typeof cb === "string") { |
|
cb = model[cb]; |
|
} |
|
options = options || {}; |
|
options.user = true; |
|
let exps = void 0; |
|
if ( |
|
_.isFunction(expOrFn) || |
|
!(exps = expOrFn.match(/[a-zA-Z0-9_.*]+|[|][|]|[&][&]|[(]|[)]/g)) || |
|
(exps.length === 1 && expOrFn.indexOf("*") < 0) |
|
) { |
|
const watcher = new Watcher(model, expOrFn, cb, options); |
|
if (options.immediate) { |
|
cb(watcher.value); |
|
} |
|
|
|
return function unwatchFn() { |
|
watcher.teardown(); |
|
}; |
|
} |
|
const watchers = []; |
|
let fns = exps.slice(); |
|
let complete = false, |
|
running = false; |
|
const callback = function callback(index, newValue, oldValue, attrs) { |
|
if (complete === true) { |
|
return; |
|
} |
|
fns[index] = true; |
|
if (runBinaryFunction(fns)) { |
|
complete = true; |
|
cb(newValue, oldValue, attrs); |
|
} |
|
if (options && options.sync) { |
|
complete = false; |
|
running = false; |
|
fns = exps.slice(); |
|
} else { |
|
if (!running) { |
|
running = true; |
|
nextTick(() => { |
|
complete = false; |
|
running = false; |
|
fns = exps.slice(); |
|
}); |
|
} |
|
} |
|
}; |
|
_.each(exps, (exp, i) => { |
|
if (_.has(operators, exp)) { |
|
return; |
|
} |
|
if (exp.indexOf("*") >= 0) { |
|
// a.**或a.*形式 |
|
if ( |
|
/^[1-9a-zA-Z.]+(\*\*$|\*$)/.test(exp) || |
|
exp === "**" || |
|
exp === "*" |
|
) { |
|
const isGlobal = exp.indexOf("**") >= 0; |
|
if (isGlobal) { |
|
// a.**的形式 |
|
exp = exp.replace(".**", ""); |
|
} else { |
|
// a.*的形式 |
|
exp = exp.replace(".*", ""); |
|
} |
|
const getter = |
|
exp === "**" || exp === "*" |
|
? function (m) { |
|
return m; |
|
} |
|
: parsePath(exp); |
|
const v = getter.call(model, model); |
|
const _dep = new Dep(); |
|
if (isGlobal) { |
|
(v.__ob__._scopeDeps || (v.__ob__._scopeDeps = [])).push( |
|
_dep |
|
); |
|
} else { |
|
(v.__ob__._deps || (v.__ob__._deps = [])).push(_dep); |
|
} |
|
const _w = new Watcher( |
|
model, |
|
() => { |
|
_dep.depend(); |
|
|
|
return NaN; |
|
}, |
|
(newValue, oldValue, attrs) => { |
|
callback( |
|
i, |
|
newValue, |
|
oldValue, |
|
_.extend({ index: i }, attrs) |
|
); |
|
}, |
|
options |
|
); |
|
watchers.push(() => { |
|
_w.teardown(); |
|
v.__ob__._scopeDeps && remove(v.__ob__._scopeDeps, _dep); |
|
v.__ob__._deps && remove(v.__ob__._deps, _dep); |
|
}); |
|
|
|
return; |
|
} |
|
// **.a.**的情况,场景:a.b.c, 如果用b.**监听, a被重新赋值b上的_scopeDes就不存在了 |
|
if (/^(\*\*\.)+[1-9a-zA-Z]+(\.\*\*$)/.test(exp)) { |
|
// 先获取到能获取到的对象 |
|
const _paths = exp.split("."); |
|
const _currentModel = model[_paths[1]]; |
|
exp = `${_paths[1]}.**`; |
|
// 补全路径 |
|
let _parent = _currentModel.__ob__.parent, |
|
_root = _currentModel.__ob__; |
|
while (_parent) { |
|
exp = `*.${exp}`; |
|
_root = _parent; |
|
_parent = _parent.parent; |
|
} |
|
const _regStr = routeToRegExp(exp); |
|
const _dep2 = new Dep(); |
|
_root._globalDeps || (_root._globalDeps = {}); |
|
if (_.isArray(_root._globalDeps[_regStr])) { |
|
_root._globalDeps[_regStr].push(_dep2); |
|
} else { |
|
_root._globalDeps[_regStr] = [_dep2]; |
|
} |
|
|
|
const _w2 = new Watcher( |
|
_currentModel, |
|
() => { |
|
_dep2.depend(); |
|
|
|
return NaN; |
|
}, |
|
(newValue, oldValue, attrs) => { |
|
callback( |
|
i, |
|
newValue, |
|
oldValue, |
|
_.extend({ index: i }, attrs) |
|
); |
|
}, |
|
options |
|
); |
|
watchers.push(() => { |
|
if (_root._globalDeps) { |
|
remove(_root._globalDeps[_regStr], _dep2); |
|
|
|
if (_root._globalDeps[_regStr].length === 0) { |
|
delete _root._globalDeps[_regStr]; |
|
_w2.teardown(); |
|
} |
|
} |
|
}); |
|
|
|
return; |
|
} |
|
// 再有结尾有*的就不支持了 |
|
if (exp[exp.length - 1] === "*") { |
|
throw new Error("not support"); |
|
} |
|
// 其他含有*的情况,如*.a,*.*.a,a.*.a |
|
let currentModel = model; |
|
// 先获取到能获取到的对象 |
|
const paths = exp.split("."); |
|
for (let _i = 0, len = paths.length; _i < len; _i++) { |
|
if (paths[_i] === "*") { |
|
break; |
|
} |
|
currentModel = model[paths[_i]]; |
|
} |
|
exp = exp.substr(exp.indexOf("*")); |
|
// 补全路径 |
|
let parent = currentModel.__ob__.parent, |
|
root = currentModel.__ob__; |
|
while (parent) { |
|
exp = `*.${exp}`; |
|
root = parent; |
|
parent = parent.parent; |
|
} |
|
const regStr = routeToRegExp(exp); |
|
const dep = new Dep(); |
|
root._globalDeps || (root._globalDeps = {}); |
|
if (_.isArray(root._globalDeps[regStr])) { |
|
root._globalDeps[regStr].push(dep); |
|
} else { |
|
root._globalDeps[regStr] = [dep]; |
|
} |
|
|
|
const w = new Watcher( |
|
currentModel, |
|
() => { |
|
dep.depend(); |
|
|
|
return NaN; |
|
}, |
|
(newValue, oldValue, attrs) => { |
|
callback( |
|
i, |
|
newValue, |
|
oldValue, |
|
_.extend({ index: i }, attrs) |
|
); |
|
}, |
|
options |
|
); |
|
watchers.push(() => { |
|
if (root._globalDeps) { |
|
remove(root._globalDeps[regStr], dep); |
|
if (root._globalDeps[regStr].length === 0) { |
|
delete root._globalDeps[regStr]; |
|
w.teardown(); |
|
} |
|
} |
|
}); |
|
|
|
return; |
|
} |
|
const watcher = new Watcher( |
|
model, |
|
exp, |
|
(newValue, oldValue, attrs) => { |
|
callback(i, newValue, oldValue, _.extend({ index: i }, attrs)); |
|
}, |
|
options |
|
); |
|
watchers.push(() => { |
|
watcher.teardown(); |
|
}); |
|
}); |
|
|
|
return watchers; |
|
} |
|
|
|
const mixinInjection = {}; |
|
|
|
function getMixins(type) { |
|
return mixinInjection[type]; |
|
} |
|
|
|
function mixin(xtype, cls) { |
|
mixinInjection[xtype] = _.cloneDeep(cls); |
|
} |
|
|
|
const computedWatcherOptions = { lazy: true }; |
|
let REACTIVE = true; |
|
|
|
function initState(vm, state) { |
|
if (state) { |
|
vm.$$state = REACTIVE ? observe(state).model : state; |
|
} |
|
} |
|
|
|
function initComputed(vm, computed) { |
|
const watchers = (vm._computedWatchers = {}); |
|
defineComputed(vm, computed); |
|
for (const key in computed) { |
|
watchers[key] = defineComputedWatcher(vm, computed[key]); |
|
} |
|
} |
|
|
|
function defineComputedWatcher(vm, userDef) { |
|
const context = vm.$$model ? vm.model : vm; |
|
const getter = typeof userDef === "function" ? userDef : userDef.get; |
|
|
|
return new Watcher(context, getter || noop, noop, computedWatcherOptions); |
|
} |
|
|
|
function defineOneComputedGetter(vm, key, userDef) { |
|
const shouldCache = true; |
|
const sharedPropertyDefinition = { |
|
enumerable: true, |
|
configurable: true, |
|
get: noop, |
|
set: noop |
|
}; |
|
if (typeof userDef === "function") { |
|
sharedPropertyDefinition.get = createComputedGetter(vm, key); |
|
sharedPropertyDefinition.set = noop; |
|
} else { |
|
sharedPropertyDefinition.get = userDef.get |
|
? shouldCache && userDef.cache !== false |
|
? createComputedGetter(vm, key) |
|
: userDef.get |
|
: noop; |
|
sharedPropertyDefinition.set = userDef.set ? userDef.set : noop; |
|
} |
|
|
|
return sharedPropertyDefinition; |
|
} |
|
|
|
function defineComputed(vm, computed) { |
|
const props = {}; |
|
for (const key in computed) { |
|
if (!(key in vm)) { |
|
props[key] = defineOneComputedGetter(vm, key, computed[key]); |
|
} |
|
} |
|
vm.$$computed = createViewModel$1({}, props); |
|
} |
|
|
|
function createComputedGetter(vm, key) { |
|
return function computedGetter() { |
|
const watcher = vm._computedWatchers && vm._computedWatchers[key]; |
|
if (watcher) { |
|
if (watcher.dirty) { |
|
watcher.evaluate(); |
|
} |
|
if (REACTIVE && Dep.target) { |
|
watcher.depend(); |
|
} |
|
|
|
return watcher.value; |
|
} |
|
}; |
|
} |
|
|
|
function initWatch(vm, watch$$1) { |
|
vm._watchers || (vm._watchers = []); |
|
for (const key in watch$$1) { |
|
const handler = watch$$1[key]; |
|
if (_.isArray(handler)) { |
|
for (let i = 0; i < handler.length; i++) { |
|
vm._watchers.push(createWatcher(vm, key, handler[i])); |
|
} |
|
} else { |
|
vm._watchers.push(createWatcher(vm, key, handler)); |
|
} |
|
} |
|
} |
|
|
|
function createWatcher(vm, keyOrFn, cb, options) { |
|
if (isPlainObject(cb)) { |
|
options = cb; |
|
cb = cb.handler; |
|
} |
|
if (typeof cb === "string") { |
|
cb = vm[cb]; |
|
} |
|
|
|
return watch( |
|
vm.model, |
|
keyOrFn, |
|
_.bind(cb, vm.$$model ? vm.model : vm), |
|
options |
|
); |
|
} |
|
|
|
function initMethods(vm, methods) { |
|
for (const key in methods) { |
|
vm[key] = |
|
methods[key] == null |
|
? noop |
|
: _.bind(methods[key], vm.$$model ? vm.model : vm); |
|
} |
|
} |
|
|
|
function initMixins(vm, mixins) { |
|
mixins = (mixins || []).slice(0); |
|
|
|
_.each(mixins.reverse(), (mixinType) => { |
|
const mixin$$1 = getMixins(mixinType); |
|
|
|
for (const key in mixin$$1) { |
|
if (typeof mixin$$1[key] !== "function") continue; |
|
|
|
if (_.has(vm, key)) continue; |
|
|
|
vm[key] = _.bind(mixin$$1[key], vm.$$model ? vm.model : vm); |
|
} |
|
}); |
|
} |
|
|
|
function defineProps(vm, keys) { |
|
const props = {}; |
|
// if (typeof Proxy === 'function') { |
|
// return vm.model = new Proxy(props, { |
|
// has: function (target, key) { |
|
// return keys.indexOf(key) > -1; |
|
// }, |
|
// get: function (target, key) { |
|
// if (key in $$skipArray) { |
|
// return props[key] |
|
// } |
|
// if (vm.$$computed && key in vm.$$computed) { |
|
// return vm.$$computed[key] |
|
// } |
|
// if (vm.$$state && key in vm.$$state) { |
|
// return vm.$$state[key] |
|
// } |
|
// return vm.$$model[key] |
|
// }, |
|
// set: function (target, key, val) { |
|
// if (key in $$skipArray) { |
|
// return props[key] = val |
|
// } |
|
// if (vm.$$state && key in vm.$$state) { |
|
// return vm.$$state[key] = val |
|
// } |
|
// if (vm.$$model && key in vm.$$model) { |
|
// return vm.$$model[key] = val |
|
// } |
|
// } |
|
// }) |
|
// } |
|
|
|
const _loop = function _loop(i, len) { |
|
const key = keys[i]; |
|
if (!(key in $$skipArray)) { |
|
props[key] = { |
|
enumerable: true, |
|
configurable: true, |
|
get: function get() { |
|
if (vm.$$computed && key in vm.$$computed) { |
|
return vm.$$computed[key]; |
|
} |
|
if (vm.$$state && key in vm.$$state) { |
|
return vm.$$state[key]; |
|
} |
|
if (vm.$$model && key in vm.$$model) { |
|
return vm.$$model[key]; |
|
} |
|
let p = vm._parent; |
|
while (p) { |
|
if (p.$$context && key in p.$$context) { |
|
return p.$$context[key]; |
|
} |
|
p = p._parent; |
|
} |
|
}, |
|
set: function set(val) { |
|
if (vm.$$state && key in vm.$$state) { |
|
return (vm.$$state[key] = val); |
|
} |
|
if (vm.$$model && key in vm.$$model) { |
|
return (vm.$$model[key] = val); |
|
} |
|
let p = vm._parent; |
|
while (p) { |
|
if (p.$$context && key in p.$$context) { |
|
return (p.$$context[key] = val); |
|
} |
|
p = p._parent; |
|
} |
|
} |
|
}; |
|
} |
|
}; |
|
|
|
for (let i = 0, len = keys.length; i < len; i++) { |
|
_loop(i, len); |
|
} |
|
vm.model = createViewModel$1({}, props); |
|
} |
|
|
|
function defineContext(vm, keys) { |
|
const props = {}; |
|
|
|
const _loop2 = function _loop2(i, len) { |
|
const key = keys[i]; |
|
if (!(key in $$skipArray)) { |
|
props[key] = { |
|
enumerable: true, |
|
configurable: true, |
|
get: function get() { |
|
return vm.model[key]; |
|
}, |
|
set: function set(val) { |
|
return (vm.model[key] = val); |
|
} |
|
}; |
|
} |
|
}; |
|
|
|
for (let i = 0, len = keys.length; i < len; i++) { |
|
_loop2(i, len); |
|
} |
|
vm.$$context = createViewModel$1({}, props); |
|
} |
|
|
|
function getInjectValue(vm, key) { |
|
let p = vm._parent; |
|
while (p) { |
|
if (p.$$context && key in p.$$context) { |
|
return p.$$context[key]; |
|
} |
|
p = p._parent; |
|
} |
|
} |
|
|
|
function getInjectValues(vm) { |
|
const inject = vm.inject || []; |
|
const result = {}; |
|
_.each(inject, (key) => { |
|
result[key] = getInjectValue(vm, key); |
|
}); |
|
|
|
return result; |
|
} |
|
|
|
const Model = (function () { |
|
function Model() { |
|
_classCallCheck(this, Model); |
|
} |
|
|
|
Model.prototype._constructor = function _constructor( |
|
model, |
|
destroyHandler |
|
) { |
|
if (model instanceof Observer || model instanceof Model) { |
|
model = model.model; |
|
} |
|
if (model && model.__ob__) { |
|
this.$$model = model; |
|
} else { |
|
this.options = model || {}; |
|
} |
|
this._parent = Model.target; |
|
const state = _.isFunction(this.state) ? this.state() : this.state; |
|
const computed = this.computed; |
|
const context = this.context; |
|
const inject = this.inject; |
|
const childContext = this.childContext; |
|
const provide = this.provide; |
|
const watch$$1 = this.watch; |
|
const actions = this.actions; |
|
const keys = _.keys(this.$$model) |
|
.concat(_.keys(state)) |
|
.concat(_.keys(computed)) |
|
.concat(inject || []) |
|
.concat(context || []); |
|
const mixins = this.mixins; |
|
defineProps(this, keys); |
|
// deprecated |
|
childContext && defineContext(this, childContext); |
|
provide && defineContext(this, provide); |
|
this.$$model && (this.model.__ob__ = this.$$model.__ob__); |
|
initMixins(this, mixins); |
|
this.init(); |
|
initState(this, _.extend(getInjectValues(this), state)); |
|
initComputed(this, computed); |
|
REACTIVE && initWatch(this, watch$$1); |
|
initMethods(this, actions); |
|
this.created && this.created(); |
|
this._destroyHandler = destroyHandler; |
|
if (this.$$model) { |
|
return this.model; |
|
} |
|
}; |
|
|
|
Model.prototype._init = function _init() {}; |
|
|
|
Model.prototype.init = function init() { |
|
this._init(); |
|
}; |
|
|
|
Model.prototype.destroy = function destroy() { |
|
for (const _key3 in this._computedWatchers) { |
|
this._computedWatchers[_key3].teardown(); |
|
} |
|
_.each(this._watchers, (unwatches) => { |
|
unwatches = _.isArray(unwatches) ? unwatches : [unwatches]; |
|
_.each(unwatches, (unwatch) => { |
|
unwatch(); |
|
}); |
|
}); |
|
this._watchers && (this._watchers = []); |
|
this.destroyed && this.destroyed(); |
|
this.$$model = null; |
|
this.$$computed = null; |
|
this.$$state = null; |
|
this._destroyHandler && this._destroyHandler(); |
|
}; |
|
|
|
return Model; |
|
})(); |
|
|
|
function define(model) { |
|
return REACTIVE ? new Observer(model).model : model; |
|
} |
|
|
|
const reactive = define; |
|
|
|
function config(options) { |
|
options || (options = {}); |
|
if ("reactive" in options) { |
|
REACTIVE = options.reactive; |
|
} |
|
} |
|
|
|
function toJSON(model) { |
|
let result = void 0; |
|
if (_.isArray(model)) { |
|
result = []; |
|
for (let i = 0, len = model.length; i < len; i++) { |
|
result[i] = toJSON(model[i]); |
|
} |
|
} else if (model && isPlainObject(model)) { |
|
result = {}; |
|
for (const _key4 in model) { |
|
if (!_.has($$skipArray, _key4)) { |
|
result[_key4] = toJSON(model[_key4]); |
|
} |
|
} |
|
} else { |
|
result = model; |
|
} |
|
|
|
return result; |
|
} |
|
|
|
const version = "2.0"; |
|
|
|
export const Fix = { |
|
version, |
|
$$skipArray, |
|
mixin, |
|
Model, |
|
define, |
|
reactive, |
|
config, |
|
observerState, |
|
Observer, |
|
observe, |
|
notify, |
|
defineReactive, |
|
defineReactiveProperty, |
|
set, |
|
freeze, |
|
del, |
|
Watcher, |
|
pushTarget, |
|
popTarget, |
|
watch, |
|
toJSON |
|
};
|
|
|