mirror of
https://github.com/vuejs/vue.git
synced 2024-11-21 20:28:54 +00:00
refactor state
This commit is contained in:
parent
df97df1c05
commit
91a04ad038
@ -1,12 +1,12 @@
|
||||
import Watcher from '../observer/watcher'
|
||||
import { h, patch } from '../vdom/index'
|
||||
import { nextTick, query } from '../util/index'
|
||||
import stateMixin from './internal/state'
|
||||
import { initState, setData } from './state'
|
||||
|
||||
export default function Vue (options) {
|
||||
this.$options = options
|
||||
this._watchers = []
|
||||
this._initState()
|
||||
initState(this)
|
||||
this._el = query(options.el)
|
||||
this._el.innerHTML = ''
|
||||
this._watcher = new Watcher(this, options.render, this._update)
|
||||
@ -22,7 +22,20 @@ Vue.prototype._update = function (vtree) {
|
||||
this._tree = vtree
|
||||
}
|
||||
|
||||
Vue.prototype.$forceUpdate = function () {
|
||||
this._watcher.run()
|
||||
}
|
||||
|
||||
Object.defineProperty(Vue.prototype, '$data', {
|
||||
get () {
|
||||
return this._data
|
||||
},
|
||||
set (newData) {
|
||||
if (newData !== this._data) {
|
||||
setData(this, newData)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
Vue.prototype.__h__ = h
|
||||
Vue.nextTick = nextTick
|
||||
|
||||
stateMixin(Vue)
|
||||
|
0
src/runtime/instance/init.js
Normal file
0
src/runtime/instance/init.js
Normal file
@ -1,208 +0,0 @@
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
143
src/runtime/instance/state.js
Normal file
143
src/runtime/instance/state.js
Normal file
@ -0,0 +1,143 @@
|
||||
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 function initState (vm) {
|
||||
initData(vm)
|
||||
initComputed(vm)
|
||||
initMethods(vm)
|
||||
}
|
||||
|
||||
function initData (vm) {
|
||||
var data = vm.$options.data
|
||||
data = vm._data = typeof data === 'function'
|
||||
? data()
|
||||
: data || {}
|
||||
if (!isPlainObject(data)) {
|
||||
data = {}
|
||||
process.env.NODE_ENV !== 'production' && warn(
|
||||
'data functions should return an object.',
|
||||
vm
|
||||
)
|
||||
}
|
||||
// proxy data on instance
|
||||
var keys = Object.keys(data)
|
||||
var i = keys.length
|
||||
while (i--) {
|
||||
proxy(vm, keys[i])
|
||||
}
|
||||
// observe data
|
||||
observe(data, vm)
|
||||
}
|
||||
|
||||
function noop () {}
|
||||
|
||||
function initComputed (vm) {
|
||||
var computed = vm.$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, vm)
|
||||
def.set = noop
|
||||
} else {
|
||||
def.get = userDef.get
|
||||
? userDef.cache !== false
|
||||
? makeComputedGetter(userDef.get, vm)
|
||||
: bind(userDef.get, vm)
|
||||
: noop
|
||||
def.set = userDef.set
|
||||
? bind(userDef.set, vm)
|
||||
: noop
|
||||
}
|
||||
Object.defineProperty(vm, 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
|
||||
}
|
||||
}
|
||||
|
||||
function initMethods (vm) {
|
||||
var methods = vm.$options.methods
|
||||
if (methods) {
|
||||
for (var key in methods) {
|
||||
vm[key] = bind(methods[key], vm)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function proxy (vm, key) {
|
||||
if (!isReserved(key)) {
|
||||
Object.defineProperty(vm, key, {
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
get: function proxyGetter () {
|
||||
return vm._data[key]
|
||||
},
|
||||
set: function proxySetter (val) {
|
||||
vm._data[key] = val
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function unproxy (vm, key) {
|
||||
if (!isReserved(key)) {
|
||||
delete vm[key]
|
||||
}
|
||||
}
|
||||
|
||||
export function setData (vm, newData) {
|
||||
newData = newData || {}
|
||||
var oldData = vm._data
|
||||
vm._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)) {
|
||||
unproxy(vm, 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(vm, key)) {
|
||||
// new property
|
||||
proxy(vm, key)
|
||||
}
|
||||
}
|
||||
oldData.__ob__.removeVm(vm)
|
||||
observe(newData, vm)
|
||||
vm.$forceUpdate()
|
||||
}
|
Loading…
Reference in New Issue
Block a user