| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218 |
- var _ = require('../util')
- var Observer = require('../observer')
- var Binding = require('../binding')
- /**
- * Setup the data scope of an instance.
- *
- * We need to setup the instance $observer, which emits
- * data change events. The $observer relays events from
- * the $data's observer, because $data might be swapped
- * and the data observer might change.
- *
- * If the instance has a parent and is not isolated, we
- * also need to listen to parent scope events and propagate
- * changes down here.
- */
- exports._initScope = function () {
- this._initData()
- this._initComputed()
- this._initMethods()
- this._initMeta()
- }
- /**
- * Initialize the data.
- */
- exports._initData = function () {
- // proxy data on instance
- var data = this._data
- var keys = Object.keys(data)
- var i = keys.length
- while (i--) {
- this._proxy(keys[i])
- }
- // observe data
- Observer.create(data)
- }
- /**
- * Swap the isntance's $data. Called in $data's setter.
- *
- * @param {Object} newData
- */
- exports._setData = function (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 (!_.isReserved(key) && !(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 (!this.hasOwnProperty(key) && !_.isReserved(key)) {
- // new property
- this._proxy(key)
- }
- }
- Observer.create(newData)
- this._digest()
- }
- /**
- * Proxy a property, so that
- * vm.prop === vm._data.prop
- *
- * @param {String} key
- */
- exports._proxy = function (key) {
- if (!_.isReserved(key)) {
- // need to store ref to self here
- // because these getter/setters might
- // be called by child instances!
- 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
- */
- exports._unproxy = function (key) {
- delete this[key]
- }
- /**
- * Force update on every watcher in scope.
- */
- exports._digest = function () {
- var i = this._watcherList.length
- while (i--) {
- this._watcherList[i].update()
- }
- var children = this._children
- var child
- if (children) {
- i = children.length
- while (i--) {
- child = children[i]
- if (!child.$options.isolated) {
- child._digest()
- }
- }
- }
- }
- /**
- * Setup computed properties. They are essentially
- * special getter/setters
- */
- function noop () {}
- exports._initComputed = function () {
- var computed = this.$options.computed
- if (computed) {
- for (var key in computed) {
- var def = computed[key]
- if (typeof def === 'function') {
- def = {
- get: _.bind(def, this),
- set: noop
- }
- } else {
- def.get = def.get
- ? _.bind(def.get, this)
- : noop
- def.set = def.set
- ? _.bind(def.set, this)
- : noop
- }
- def.enumerable = true
- def.configurable = true
- Object.defineProperty(this, key, def)
- }
- }
- }
- /**
- * Setup instance methods. Methods must be bound to the
- * instance since they might be called by children
- * inheriting them.
- */
- exports._initMethods = function () {
- var methods = this.$options.methods
- if (methods) {
- for (var key in methods) {
- this[key] = _.bind(methods[key], this)
- }
- }
- }
- /**
- * Initialize meta information like $index, $key & $value.
- */
- exports._initMeta = function () {
- var metas = this.$options._meta
- if (metas) {
- for (var key in metas) {
- this._defineMeta(key, metas[key])
- }
- }
- }
- /**
- * Define a meta property, e.g $index, $key, $value
- * which only exists on the vm instance but not in $data.
- *
- * @param {String} key
- * @param {*} value
- */
- exports._defineMeta = function (key, value) {
- var binding = new Binding()
- Object.defineProperty(this, key, {
- enumerable: true,
- configurable: true,
- get: function metaGetter () {
- if (Observer.target) {
- Observer.target.addDep(binding)
- }
- return value
- },
- set: function metaSetter (val) {
- if (val !== value) {
- value = val
- binding.notify()
- }
- }
- })
- }
|