scope.js 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. var _ = require('../util')
  2. var Observer = require('../observer')
  3. var Dep = require('../observer/dep')
  4. /**
  5. * Setup the scope of an instance, which contains:
  6. * - observed data
  7. * - computed properties
  8. * - user methods
  9. * - meta properties
  10. */
  11. exports._initScope = function () {
  12. this._initData()
  13. this._initComputed()
  14. this._initMethods()
  15. this._initMeta()
  16. }
  17. /**
  18. * Initialize the data.
  19. */
  20. exports._initData = function () {
  21. // proxy data on instance
  22. var data = this._data
  23. var i, key
  24. // make sure all props properties are observed
  25. var props = this.$options.props
  26. if (props) {
  27. i = props.length
  28. while (i--) {
  29. key = _.camelize(props[i])
  30. if (!(key in data)) {
  31. data[key] = null
  32. }
  33. }
  34. }
  35. var keys = Object.keys(data)
  36. i = keys.length
  37. while (i--) {
  38. key = keys[i]
  39. if (!_.isReserved(key)) {
  40. this._proxy(key)
  41. }
  42. }
  43. // observe data
  44. Observer.create(data).addVm(this)
  45. }
  46. /**
  47. * Swap the isntance's $data. Called in $data's setter.
  48. *
  49. * @param {Object} newData
  50. */
  51. exports._setData = function (newData) {
  52. newData = newData || {}
  53. var oldData = this._data
  54. this._data = newData
  55. var keys, key, i
  56. // unproxy keys not present in new data
  57. keys = Object.keys(oldData)
  58. i = keys.length
  59. while (i--) {
  60. key = keys[i]
  61. if (!_.isReserved(key) && !(key in newData)) {
  62. this._unproxy(key)
  63. }
  64. }
  65. // proxy keys not already proxied,
  66. // and trigger change for changed values
  67. keys = Object.keys(newData)
  68. i = keys.length
  69. while (i--) {
  70. key = keys[i]
  71. if (!this.hasOwnProperty(key) && !_.isReserved(key)) {
  72. // new property
  73. this._proxy(key)
  74. }
  75. }
  76. oldData.__ob__.removeVm(this)
  77. Observer.create(newData).addVm(this)
  78. this._digest()
  79. }
  80. /**
  81. * Proxy a property, so that
  82. * vm.prop === vm._data.prop
  83. *
  84. * @param {String} key
  85. */
  86. exports._proxy = function (key) {
  87. // need to store ref to self here
  88. // because these getter/setters might
  89. // be called by child instances!
  90. var self = this
  91. Object.defineProperty(self, key, {
  92. configurable: true,
  93. enumerable: true,
  94. get: function proxyGetter () {
  95. return self._data[key]
  96. },
  97. set: function proxySetter (val) {
  98. self._data[key] = val
  99. }
  100. })
  101. }
  102. /**
  103. * Unproxy a property.
  104. *
  105. * @param {String} key
  106. */
  107. exports._unproxy = function (key) {
  108. delete this[key]
  109. }
  110. /**
  111. * Force update on every watcher in scope.
  112. */
  113. exports._digest = function () {
  114. var i = this._watcherList.length
  115. while (i--) {
  116. this._watcherList[i].update()
  117. }
  118. var children = this._children
  119. i = children.length
  120. while (i--) {
  121. var child = children[i]
  122. if (child.$options.inherit) {
  123. child._digest()
  124. }
  125. }
  126. }
  127. /**
  128. * Setup computed properties. They are essentially
  129. * special getter/setters
  130. */
  131. function noop () {}
  132. exports._initComputed = function () {
  133. var computed = this.$options.computed
  134. if (computed) {
  135. for (var key in computed) {
  136. var userDef = computed[key]
  137. var def = {
  138. enumerable: true,
  139. configurable: true
  140. }
  141. if (typeof userDef === 'function') {
  142. def.get = _.bind(userDef, this)
  143. def.set = noop
  144. } else {
  145. def.get = userDef.get
  146. ? _.bind(userDef.get, this)
  147. : noop
  148. def.set = userDef.set
  149. ? _.bind(userDef.set, this)
  150. : noop
  151. }
  152. Object.defineProperty(this, key, def)
  153. }
  154. }
  155. }
  156. /**
  157. * Setup instance methods. Methods must be bound to the
  158. * instance since they might be called by children
  159. * inheriting them.
  160. */
  161. exports._initMethods = function () {
  162. var methods = this.$options.methods
  163. if (methods) {
  164. for (var key in methods) {
  165. this[key] = _.bind(methods[key], this)
  166. }
  167. }
  168. }
  169. /**
  170. * Initialize meta information like $index, $key & $value.
  171. */
  172. exports._initMeta = function () {
  173. var metas = this.$options._meta
  174. if (metas) {
  175. for (var key in metas) {
  176. this._defineMeta(key, metas[key])
  177. }
  178. }
  179. }
  180. /**
  181. * Define a meta property, e.g $index, $key, $value
  182. * which only exists on the vm instance but not in $data.
  183. *
  184. * @param {String} key
  185. * @param {*} value
  186. */
  187. exports._defineMeta = function (key, value) {
  188. var dep = new Dep()
  189. Object.defineProperty(this, key, {
  190. enumerable: true,
  191. configurable: true,
  192. get: function metaGetter () {
  193. if (Observer.target) {
  194. Observer.target.addDep(dep)
  195. }
  196. return value
  197. },
  198. set: function metaSetter (val) {
  199. if (val !== value) {
  200. value = val
  201. dep.notify()
  202. }
  203. }
  204. })
  205. }