| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262 |
- var _ = require('../util')
- var compiler = require('../compiler')
- var Observer = require('../observer')
- var Dep = require('../observer/dep')
- /**
- * Setup the scope of an instance, which contains:
- * - observed data
- * - computed properties
- * - user methods
- * - meta properties
- */
- exports._initScope = function () {
- this._initProps()
- this._initMeta()
- this._initMethods()
- this._initData()
- this._initComputed()
- }
- /**
- * Initialize props.
- */
- exports._initProps = function () {
- var options = this.$options
- var el = options.el
- var props = options.props
- this._propsUnlinkFn = el && props
- ? compiler.compileAndLinkProps(
- this, el, props
- )
- : null
- if (props && !el) {
- _.warn(
- 'Props will not be compiled if no `el` option is ' +
- 'provided at instantiation.'
- )
- }
- }
- /**
- * Initialize the data.
- */
- exports._initData = function () {
- var propsData = this._data
- var optionsDataFn = this.$options.data
- var optionsData = optionsDataFn && optionsDataFn()
- if (optionsData) {
- this._data = optionsData
- for (var prop in propsData) {
- if (
- !optionsData.hasOwnProperty(prop) ||
- propsData[prop] !== undefined
- ) {
- optionsData[prop] = propsData[prop]
- }
- }
- }
- var data = this._data
- // proxy data on instance
- var keys = Object.keys(data)
- var i, key
- i = keys.length
- while (i--) {
- key = keys[i]
- if (!_.isReserved(key)) {
- this._proxy(key)
- }
- }
- // observe data
- Observer.create(data).addVm(this)
- }
- /**
- * Swap the isntance's $data. Called in $data's setter.
- *
- * @param {Object} newData
- */
- exports._setData = function (newData) {
- newData = newData || {}
- var oldData = this._data
- this._data = newData
- var keys, key, i
- // copy props.
- // this should only happen during a v-repeat of component
- // that also happens to have compiled props.
- var props = this.$options.props
- if (props) {
- i = props.length
- while (i--) {
- key = props[i]
- if (key !== '$data' && !newData.hasOwnProperty(key)) {
- newData.$set(key, oldData[key])
- }
- }
- }
- // 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)
- }
- }
- oldData.__ob__.removeVm(this)
- Observer.create(newData).addVm(this)
- this._digest()
- }
- /**
- * Proxy a property, so that
- * vm.prop === vm._data.prop
- *
- * @param {String} key
- */
- exports._proxy = function (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._watchers.length
- while (i--) {
- this._watchers[i].update()
- }
- var children = this._children
- i = children.length
- while (i--) {
- var child = children[i]
- if (child.$options.inherit) {
- 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 userDef = computed[key]
- var def = {
- enumerable: true,
- configurable: true
- }
- if (typeof userDef === 'function') {
- def.get = _.bind(userDef, this)
- def.set = noop
- } else {
- def.get = userDef.get
- ? _.bind(userDef.get, this)
- : noop
- def.set = userDef.set
- ? _.bind(userDef.set, this)
- : noop
- }
- 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 dep = new Dep()
- Object.defineProperty(this, key, {
- enumerable: true,
- configurable: true,
- get: function metaGetter () {
- dep.depend()
- return value
- },
- set: function metaSetter (val) {
- if (val !== value) {
- value = val
- dep.notify()
- }
- }
- })
- }
|