mirror of https://github.com/vuejs/vue.git
125 lines
3.4 KiB
JavaScript
125 lines
3.4 KiB
JavaScript
var Emitter = require('emitter'),
|
|
config = require('./config'),
|
|
observer = new Emitter()
|
|
|
|
var dummyEl = document.createElement('div'),
|
|
ARGS_RE = /^function\s*?\((.+?)\)/,
|
|
SCOPE_RE_STR = '\\.vm\\.[\\.A-Za-z0-9_][\\.A-Za-z0-9_$]*',
|
|
noop = function () {}
|
|
|
|
/*
|
|
* Auto-extract the dependencies of a computed property
|
|
* by recording the getters triggered when evaluating it.
|
|
*
|
|
* However, the first pass will contain duplicate dependencies
|
|
* for computed properties. It is therefore necessary to do a
|
|
* second pass in injectDeps()
|
|
*/
|
|
function catchDeps (binding) {
|
|
observer.on('get', function (dep) {
|
|
binding.deps.push(dep)
|
|
})
|
|
parseContextDependency(binding)
|
|
binding.value.get({
|
|
vm: createDummyVM(binding),
|
|
el: dummyEl
|
|
})
|
|
observer.off('get')
|
|
}
|
|
|
|
/*
|
|
* The second pass of dependency extraction.
|
|
* Only include dependencies that don't have dependencies themselves.
|
|
*/
|
|
function filterDeps (binding) {
|
|
var i = binding.deps.length, dep
|
|
config.log('\n─ ' + binding.key)
|
|
while (i--) {
|
|
dep = binding.deps[i]
|
|
if (!dep.deps.length) {
|
|
config.log(' └─ ' + dep.key)
|
|
dep.subs.push(binding)
|
|
} else {
|
|
binding.deps.splice(i, 1)
|
|
}
|
|
}
|
|
var ctdeps = binding.contextDeps
|
|
if (!ctdeps || !config.debug) return
|
|
i = ctdeps.length
|
|
while (i--) {
|
|
config.log(' └─ ctx:' + ctdeps[i])
|
|
}
|
|
}
|
|
|
|
/*
|
|
* We need to invoke each binding's getter for dependency parsing,
|
|
* but we don't know what sub-viewmodel properties the user might try
|
|
* to access in that getter. To avoid thowing an error or forcing
|
|
* the user to guard against an undefined argument, we staticly
|
|
* analyze the function to extract any possible nested properties
|
|
* the user expects the target viewmodel to possess. They are all assigned
|
|
* a noop function so they can be invoked with no real harm.
|
|
*/
|
|
function createDummyVM (binding) {
|
|
var viewmodel = {},
|
|
deps = binding.contextDeps
|
|
if (!deps) return viewmodel
|
|
var i = binding.contextDeps.length,
|
|
j, level, key, path
|
|
while (i--) {
|
|
level = viewmodel
|
|
path = deps[i].split('.')
|
|
j = 0
|
|
while (j < path.length) {
|
|
key = path[j]
|
|
if (!level[key]) level[key] = noop
|
|
level = level[key]
|
|
j++
|
|
}
|
|
}
|
|
return viewmodel
|
|
}
|
|
|
|
/*
|
|
* Extract context dependency paths
|
|
*/
|
|
function parseContextDependency (binding) {
|
|
var fn = binding.rawGet,
|
|
str = fn.toString(),
|
|
args = str.match(ARGS_RE)
|
|
if (!args) return null
|
|
var depsRE = new RegExp(args[1] + SCOPE_RE_STR, 'g'),
|
|
matches = str.match(depsRE),
|
|
base = args[1].length + 4
|
|
if (!matches) return null
|
|
var i = matches.length,
|
|
deps = [], dep
|
|
while (i--) {
|
|
dep = matches[i].slice(base)
|
|
if (deps.indexOf(dep) === -1) {
|
|
deps.push(dep)
|
|
}
|
|
}
|
|
binding.contextDeps = deps
|
|
binding.compiler.contextBindings.push(binding)
|
|
}
|
|
|
|
module.exports = {
|
|
|
|
/*
|
|
* the observer that catches events triggered by getters
|
|
*/
|
|
observer: observer,
|
|
|
|
/*
|
|
* parse a list of computed property bindings
|
|
*/
|
|
parse: function (bindings) {
|
|
config.log('\nparsing dependencies...')
|
|
observer.isObserving = true
|
|
bindings.forEach(catchDeps)
|
|
bindings.forEach(filterDeps)
|
|
observer.isObserving = false
|
|
config.log('\ndone.')
|
|
}
|
|
} |