github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/public/libs/vue-1.0.24/src/instance/internal/state.js (about) 1 import Watcher from '../../watcher' 2 import { compileAndLinkProps } from '../../compiler/index' 3 import Dep from '../../observer/dep' 4 import { 5 observe, 6 defineReactive 7 } from '../../observer/index' 8 9 import { 10 warn, 11 query, 12 hasOwn, 13 isReserved, 14 isPlainObject, 15 bind 16 } from '../../util/index' 17 18 export default function (Vue) { 19 /** 20 * Accessor for `$data` property, since setting $data 21 * requires observing the new object and updating 22 * proxied properties. 23 */ 24 25 Object.defineProperty(Vue.prototype, '$data', { 26 get () { 27 return this._data 28 }, 29 set (newData) { 30 if (newData !== this._data) { 31 this._setData(newData) 32 } 33 } 34 }) 35 36 /** 37 * Setup the scope of an instance, which contains: 38 * - observed data 39 * - computed properties 40 * - user methods 41 * - meta properties 42 */ 43 44 Vue.prototype._initState = function () { 45 this._initProps() 46 this._initMeta() 47 this._initMethods() 48 this._initData() 49 this._initComputed() 50 } 51 52 /** 53 * Initialize props. 54 */ 55 56 Vue.prototype._initProps = function () { 57 var options = this.$options 58 var el = options.el 59 var props = options.props 60 if (props && !el) { 61 process.env.NODE_ENV !== 'production' && warn( 62 'Props will not be compiled if no `el` option is ' + 63 'provided at instantiation.', 64 this 65 ) 66 } 67 // make sure to convert string selectors into element now 68 el = options.el = query(el) 69 this._propsUnlinkFn = el && el.nodeType === 1 && props 70 // props must be linked in proper scope if inside v-for 71 ? compileAndLinkProps(this, el, props, this._scope) 72 : null 73 } 74 75 /** 76 * Initialize the data. 77 */ 78 79 Vue.prototype._initData = function () { 80 var dataFn = this.$options.data 81 var data = this._data = dataFn ? dataFn() : {} 82 if (!isPlainObject(data)) { 83 data = {} 84 process.env.NODE_ENV !== 'production' && warn( 85 'data functions should return an object.', 86 this 87 ) 88 } 89 var props = this._props 90 // proxy data on instance 91 var keys = Object.keys(data) 92 var i, key 93 i = keys.length 94 while (i--) { 95 key = keys[i] 96 // there are two scenarios where we can proxy a data key: 97 // 1. it's not already defined as a prop 98 // 2. it's provided via a instantiation option AND there are no 99 // template prop present 100 if (!props || !hasOwn(props, key)) { 101 this._proxy(key) 102 } else if (process.env.NODE_ENV !== 'production') { 103 warn( 104 'Data field "' + key + '" is already defined ' + 105 'as a prop. To provide default value for a prop, use the "default" ' + 106 'prop option; if you want to pass prop values to an instantiation ' + 107 'call, use the "propsData" option.', 108 this 109 ) 110 } 111 } 112 // observe data 113 observe(data, this) 114 } 115 116 /** 117 * Swap the instance's $data. Called in $data's setter. 118 * 119 * @param {Object} newData 120 */ 121 122 Vue.prototype._setData = function (newData) { 123 newData = newData || {} 124 var oldData = this._data 125 this._data = newData 126 var keys, key, i 127 // unproxy keys not present in new data 128 keys = Object.keys(oldData) 129 i = keys.length 130 while (i--) { 131 key = keys[i] 132 if (!(key in newData)) { 133 this._unproxy(key) 134 } 135 } 136 // proxy keys not already proxied, 137 // and trigger change for changed values 138 keys = Object.keys(newData) 139 i = keys.length 140 while (i--) { 141 key = keys[i] 142 if (!hasOwn(this, key)) { 143 // new property 144 this._proxy(key) 145 } 146 } 147 oldData.__ob__.removeVm(this) 148 observe(newData, this) 149 this._digest() 150 } 151 152 /** 153 * Proxy a property, so that 154 * vm.prop === vm._data.prop 155 * 156 * @param {String} key 157 */ 158 159 Vue.prototype._proxy = function (key) { 160 if (!isReserved(key)) { 161 // need to store ref to self here 162 // because these getter/setters might 163 // be called by child scopes via 164 // prototype inheritance. 165 var self = this 166 Object.defineProperty(self, key, { 167 configurable: true, 168 enumerable: true, 169 get: function proxyGetter () { 170 return self._data[key] 171 }, 172 set: function proxySetter (val) { 173 self._data[key] = val 174 } 175 }) 176 } 177 } 178 179 /** 180 * Unproxy a property. 181 * 182 * @param {String} key 183 */ 184 185 Vue.prototype._unproxy = function (key) { 186 if (!isReserved(key)) { 187 delete this[key] 188 } 189 } 190 191 /** 192 * Force update on every watcher in scope. 193 */ 194 195 Vue.prototype._digest = function () { 196 for (var i = 0, l = this._watchers.length; i < l; i++) { 197 this._watchers[i].update(true) // shallow updates 198 } 199 } 200 201 /** 202 * Setup computed properties. They are essentially 203 * special getter/setters 204 */ 205 206 function noop () {} 207 Vue.prototype._initComputed = function () { 208 var computed = this.$options.computed 209 if (computed) { 210 for (var key in computed) { 211 var userDef = computed[key] 212 var def = { 213 enumerable: true, 214 configurable: true 215 } 216 if (typeof userDef === 'function') { 217 def.get = makeComputedGetter(userDef, this) 218 def.set = noop 219 } else { 220 def.get = userDef.get 221 ? userDef.cache !== false 222 ? makeComputedGetter(userDef.get, this) 223 : bind(userDef.get, this) 224 : noop 225 def.set = userDef.set 226 ? bind(userDef.set, this) 227 : noop 228 } 229 Object.defineProperty(this, key, def) 230 } 231 } 232 } 233 234 function makeComputedGetter (getter, owner) { 235 var watcher = new Watcher(owner, getter, null, { 236 lazy: true 237 }) 238 return function computedGetter () { 239 if (watcher.dirty) { 240 watcher.evaluate() 241 } 242 if (Dep.target) { 243 watcher.depend() 244 } 245 return watcher.value 246 } 247 } 248 249 /** 250 * Setup instance methods. Methods must be bound to the 251 * instance since they might be passed down as a prop to 252 * child components. 253 */ 254 255 Vue.prototype._initMethods = function () { 256 var methods = this.$options.methods 257 if (methods) { 258 for (var key in methods) { 259 this[key] = bind(methods[key], this) 260 } 261 } 262 } 263 264 /** 265 * Initialize meta information like $index, $key & $value. 266 */ 267 268 Vue.prototype._initMeta = function () { 269 var metas = this.$options._meta 270 if (metas) { 271 for (var key in metas) { 272 defineReactive(this, key, metas[key]) 273 } 274 } 275 } 276 }