diff --git a/src/runtime/instance/index.js b/src/runtime/instance/index.js index 9ac2afaa3..be823252e 100644 --- a/src/runtime/instance/index.js +++ b/src/runtime/instance/index.js @@ -1,12 +1,12 @@ import Watcher from '../observer/watcher' import { h, patch } from '../vdom/index' import { nextTick, query } from '../util/index' -import stateMixin from './internal/state' +import { initState, setData } from './state' export default function Vue (options) { this.$options = options this._watchers = [] - this._initState() + initState(this) this._el = query(options.el) this._el.innerHTML = '' this._watcher = new Watcher(this, options.render, this._update) @@ -22,7 +22,20 @@ Vue.prototype._update = function (vtree) { this._tree = vtree } +Vue.prototype.$forceUpdate = function () { + this._watcher.run() +} + +Object.defineProperty(Vue.prototype, '$data', { + get () { + return this._data + }, + set (newData) { + if (newData !== this._data) { + setData(this, newData) + } + } +}) + Vue.prototype.__h__ = h Vue.nextTick = nextTick - -stateMixin(Vue) diff --git a/src/runtime/instance/init.js b/src/runtime/instance/init.js new file mode 100644 index 000000000..e69de29bb diff --git a/src/runtime/instance/internal/state.js b/src/runtime/instance/internal/state.js deleted file mode 100644 index 67bbb42bc..000000000 --- a/src/runtime/instance/internal/state.js +++ /dev/null @@ -1,208 +0,0 @@ -import Watcher from '../../observer/watcher' -import Dep from '../../observer/dep' -import { observe } from '../../observer/index' - -import { - warn, - hasOwn, - isReserved, - isPlainObject, - bind -} from '../../util/index' - -export default function (Vue) { - /** - * Accessor for `$data` property, since setting $data - * requires observing the new object and updating - * proxied properties. - */ - - Object.defineProperty(Vue.prototype, '$data', { - get () { - return this._data - }, - set (newData) { - if (newData !== this._data) { - this._setData(newData) - } - } - }) - - /** - * Setup the scope of an instance, which contains: - * - observed data - * - computed properties - * - user methods - * - meta properties - */ - - Vue.prototype._initState = function () { - this._initMethods() - this._initData() - this._initComputed() - } - - /** - * Initialize the data. - */ - - Vue.prototype._initData = function () { - var data = this.$options.data - data = this._data = typeof data === 'function' - ? data() - : data || {} - if (!isPlainObject(data)) { - data = {} - process.env.NODE_ENV !== 'production' && warn( - 'data functions should return an object.', - this - ) - } - // proxy data on instance - var keys = Object.keys(data) - var i = keys.length - while (i--) { - this._proxy(keys[i]) - } - // observe data - observe(data, this) - } - - /** - * Swap the instance's $data. Called in $data's setter. - * - * @param {Object} newData - */ - - Vue.prototype._setData = function (newData) { - newData = newData || {} - var oldData = this._data - this._data = newData - var keys, key, i - // unproxy keys not present in new data - keys = Object.keys(oldData) - i = keys.length - while (i--) { - key = keys[i] - if (!(key in newData)) { - this._unproxy(key) - } - } - // proxy keys not already proxied, - // and trigger change for changed values - keys = Object.keys(newData) - i = keys.length - while (i--) { - key = keys[i] - if (!hasOwn(this, key)) { - // new property - this._proxy(key) - } - } - oldData.__ob__.removeVm(this) - observe(newData, this) - this._digest() - } - - /** - * Proxy a property, so that - * vm.prop === vm._data.prop - * - * @param {String} key - */ - - Vue.prototype._proxy = function (key) { - if (!isReserved(key)) { - // need to store ref to self here - // because these getter/setters might - // be called by child scopes via - // prototype inheritance. - var self = this - Object.defineProperty(self, key, { - configurable: true, - enumerable: true, - get: function proxyGetter () { - return self._data[key] - }, - set: function proxySetter (val) { - self._data[key] = val - } - }) - } - } - - /** - * Unproxy a property. - * - * @param {String} key - */ - - Vue.prototype._unproxy = function (key) { - if (!isReserved(key)) { - delete this[key] - } - } - - /** - * Setup computed properties. They are essentially - * special getter/setters - */ - - function noop () {} - Vue.prototype._initComputed = function () { - var computed = this.$options.computed - if (computed) { - for (var key in computed) { - var userDef = computed[key] - var def = { - enumerable: true, - configurable: true - } - if (typeof userDef === 'function') { - def.get = makeComputedGetter(userDef, this) - def.set = noop - } else { - def.get = userDef.get - ? userDef.cache !== false - ? makeComputedGetter(userDef.get, this) - : bind(userDef.get, this) - : noop - def.set = userDef.set - ? bind(userDef.set, this) - : noop - } - Object.defineProperty(this, key, def) - } - } - } - - function makeComputedGetter (getter, owner) { - var watcher = new Watcher(owner, getter, null, { - lazy: true - }) - return function computedGetter () { - if (watcher.dirty) { - watcher.evaluate() - } - if (Dep.target) { - watcher.depend() - } - return watcher.value - } - } - - /** - * Setup instance methods. Methods must be bound to the - * instance since they might be passed down as a prop to - * child components. - */ - - Vue.prototype._initMethods = function () { - var methods = this.$options.methods - if (methods) { - for (var key in methods) { - this[key] = bind(methods[key], this) - } - } - } -} diff --git a/src/runtime/instance/state.js b/src/runtime/instance/state.js new file mode 100644 index 000000000..7e0df51e7 --- /dev/null +++ b/src/runtime/instance/state.js @@ -0,0 +1,143 @@ +import Watcher from '../observer/watcher' +import Dep from '../observer/dep' +import { observe } from '../observer/index' + +import { + warn, + hasOwn, + isReserved, + isPlainObject, + bind +} from '../util/index' + +export function initState (vm) { + initData(vm) + initComputed(vm) + initMethods(vm) +} + +function initData (vm) { + var data = vm.$options.data + data = vm._data = typeof data === 'function' + ? data() + : data || {} + if (!isPlainObject(data)) { + data = {} + process.env.NODE_ENV !== 'production' && warn( + 'data functions should return an object.', + vm + ) + } + // proxy data on instance + var keys = Object.keys(data) + var i = keys.length + while (i--) { + proxy(vm, keys[i]) + } + // observe data + observe(data, vm) +} + +function noop () {} + +function initComputed (vm) { + var computed = vm.$options.computed + if (computed) { + for (var key in computed) { + var userDef = computed[key] + var def = { + enumerable: true, + configurable: true + } + if (typeof userDef === 'function') { + def.get = makeComputedGetter(userDef, vm) + def.set = noop + } else { + def.get = userDef.get + ? userDef.cache !== false + ? makeComputedGetter(userDef.get, vm) + : bind(userDef.get, vm) + : noop + def.set = userDef.set + ? bind(userDef.set, vm) + : noop + } + Object.defineProperty(vm, key, def) + } + } +} + +function makeComputedGetter (getter, owner) { + var watcher = new Watcher(owner, getter, null, { + lazy: true + }) + return function computedGetter () { + if (watcher.dirty) { + watcher.evaluate() + } + if (Dep.target) { + watcher.depend() + } + return watcher.value + } +} + +function initMethods (vm) { + var methods = vm.$options.methods + if (methods) { + for (var key in methods) { + vm[key] = bind(methods[key], vm) + } + } +} + +function proxy (vm, key) { + if (!isReserved(key)) { + Object.defineProperty(vm, key, { + configurable: true, + enumerable: true, + get: function proxyGetter () { + return vm._data[key] + }, + set: function proxySetter (val) { + vm._data[key] = val + } + }) + } +} + +function unproxy (vm, key) { + if (!isReserved(key)) { + delete vm[key] + } +} + +export function setData (vm, newData) { + newData = newData || {} + var oldData = vm._data + vm._data = newData + var keys, key, i + // unproxy keys not present in new data + keys = Object.keys(oldData) + i = keys.length + while (i--) { + key = keys[i] + if (!(key in newData)) { + unproxy(vm, key) + } + } + // proxy keys not already proxied, + // and trigger change for changed values + keys = Object.keys(newData) + i = keys.length + while (i--) { + key = keys[i] + if (!hasOwn(vm, key)) { + // new property + proxy(vm, key) + } + } + oldData.__ob__.removeVm(vm) + observe(newData, vm) + vm.$forceUpdate() +}