|
@@ -0,0 +1,208 @@
|
|
|
|
|
+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)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|