Skip to content

Commit 14d8ce2

Browse files
committed
compiler rewrite - observe scope directly and proxy access through vm
1 parent f29be01 commit 14d8ce2

File tree

18 files changed

+151
-185
lines changed

18 files changed

+151
-185
lines changed

examples/todomvc/js/app.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
Vue.config({debug:true})
2+
13
var filters = {
24
all: function () { return true },
35
active: function (completed) { return !completed },

examples/todomvc/js/benchmark.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
// then delete them one by one
55

66
// do not run when testing in PhantomJS
7-
if (navigator.userAgent.indexOf('PhantomJS') === -1) {
7+
if (window.location.search === '?benchmark=1') {
88
runBenchmark()
99
}
1010

src/compiler.js

Lines changed: 47 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -37,13 +37,14 @@ function Compiler (vm, options) {
3737
var el = compiler.setupElement(options)
3838
log('\nnew VM instance:', el.tagName, '\n')
3939

40-
// copy scope properties to vm
41-
var scope = options.scope
42-
if (scope) utils.extend(vm, scope, true)
40+
// init scope
41+
var scope = compiler.scope = options.scope || {}
42+
utils.extend(vm, scope, true)
4343

4444
compiler.vm = vm
4545
def(vm, '$', makeHash())
4646
def(vm, '$el', el)
47+
def(vm, '$scope', scope)
4748
def(vm, '$compiler', compiler)
4849

4950
// keep track of directives and expressions
@@ -85,36 +86,23 @@ function Compiler (vm, options) {
8586

8687
// beforeCompile hook
8788
compiler.execHook('beforeCompile', 'created')
88-
89-
// create bindings for things already in scope
90-
var key, keyPrefix
91-
for (key in vm) {
92-
keyPrefix = key.charAt(0)
93-
if (keyPrefix !== '$' && keyPrefix !== '_') {
94-
compiler.createBinding(key)
95-
}
96-
}
89+
// the user might have set some props on the vm
90+
// so copy it back to the scope...
91+
utils.extend(scope, vm)
92+
// observe the scope
93+
Observer.observe(scope, '', compiler.observer)
9794

9895
// for repeated items, create an index binding
9996
// which should be inenumerable but configurable
10097
if (compiler.repeat) {
101-
vm.$index = compiler.repeatIndex
102-
def(vm, '$collection', compiler.repeatCollection)
98+
scope.$index = compiler.repeatIndex
10399
compiler.createBinding('$index')
104100
}
105101

106102
// now parse the DOM, during which we will create necessary bindings
107103
// and bind the parsed directives
108104
compiler.compile(el, true)
109105

110-
// observe root values so that they emit events when
111-
// their nested values change (for an Object)
112-
// or when they mutate (for an Array)
113-
var i = observables.length, binding
114-
while (i--) {
115-
binding = observables[i]
116-
Observer.observe(binding.value, binding.key, compiler.observer)
117-
}
118106
// extract dependencies for computed properties
119107
if (computed.length) DepsParser.parse(computed)
120108

@@ -374,24 +362,25 @@ CompilerProto.bindDirective = function (directive) {
374362
var binding,
375363
compiler = this,
376364
key = directive.key,
377-
baseKey = key.split('.')[0],
378-
ownerCompiler = traceOwnerCompiler(directive, compiler)
365+
baseKey = key.split('.')[0]
379366

380367
if (directive.isExp) {
381368
// expression bindings are always created on current compiler
382369
binding = compiler.createBinding(key, true, directive.isFn)
383-
} else if (ownerCompiler.vm.hasOwnProperty(baseKey)) {
384-
// If the directive's owner compiler's VM has the key,
385-
// it belongs there. Create the binding if it's not already
386-
// created, and return it.
387-
binding = hasOwn.call(ownerCompiler.bindings, key)
388-
? ownerCompiler.bindings[key]
389-
: ownerCompiler.createBinding(key)
370+
} else if (
371+
hasOwn.call(compiler.scope, baseKey) ||
372+
hasOwn.call(compiler.vm, baseKey)
373+
) {
374+
// If the directive's compiler's VM has the base key,
375+
// it belongs here. Create the binding if it's not created already.
376+
binding = hasOwn.call(compiler.bindings, key)
377+
? compiler.bindings[key]
378+
: compiler.createBinding(key)
390379
} else {
391380
// due to prototypal inheritance of bindings, if a key doesn't exist
392-
// on the owner compiler's VM, then it doesn't exist in the whole
381+
// on the bindings object, then it doesn't exist in the whole
393382
// prototype chain. In this case we create the new binding at the root level.
394-
binding = ownerCompiler.bindings[key] || compiler.rootCompiler.createBinding(key)
383+
binding = compiler.bindings[key] || compiler.rootCompiler.createBinding(key)
395384
}
396385

397386
binding.instances.push(directive)
@@ -419,6 +408,7 @@ CompilerProto.bindDirective = function (directive) {
419408
CompilerProto.createBinding = function (key, isExp, isFn) {
420409

421410
var compiler = this,
411+
scope = compiler.scope,
422412
bindings = compiler.bindings,
423413
binding = new Binding(compiler, key, isExp, isFn)
424414

@@ -443,7 +433,8 @@ CompilerProto.createBinding = function (key, isExp, isFn) {
443433
// this is a root level binding. we need to define getter/setters for it.
444434
compiler.define(key, binding)
445435
} else {
446-
Observer.ensurePath(compiler.vm, key)
436+
// ensure path in scope so it can be observed
437+
Observer.ensurePath(scope, key)
447438
var parentKey = key.slice(0, key.lastIndexOf('.'))
448439
if (!hasOwn.call(bindings, parentKey)) {
449440
// this is a nested value binding, but the binding for its parent
@@ -464,52 +455,35 @@ CompilerProto.define = function (key, binding) {
464455
log(' defined root binding: ' + key)
465456

466457
var compiler = this,
458+
scope = compiler.scope,
467459
vm = compiler.vm,
468-
ob = compiler.observer,
469-
value = binding.value = vm[key], // save the value before redefinening it
470-
type = utils.typeOf(value)
460+
value = binding.value = scope[key] // save the value before redefinening it
471461

472-
if (type === 'Object' && value.$get) {
473-
// computed property
462+
if (utils.typeOf(value) === 'Object' && value.$get) {
474463
compiler.markComputed(binding)
475-
} else if (type === 'Object' || type === 'Array') {
476-
// observe objects later, because there might be more keys
477-
// to be added to it during Observer.ensurePath().
478-
// we also want to emit all the set events after all values
479-
// are available.
480-
compiler.observables.push(binding)
464+
}
465+
466+
if (!(key in scope)) {
467+
scope[key] = undefined
468+
}
469+
470+
if (scope.__observer__) {
471+
Observer.convert(scope, key)
481472
}
482473

483474
Object.defineProperty(vm, key, {
484-
enumerable: true,
485475
get: function () {
486-
var value = binding.value
487-
if (depsOb.active && (!binding.isComputed && (!value || !value.__observer__)) ||
488-
Array.isArray(value)) {
489-
// only emit non-computed, non-observed (primitive) values, or Arrays.
490-
// because these are the cleanest dependencies
491-
ob.emit('get', key)
492-
}
476+
var val = scope[key]
493477
return binding.isComputed
494-
? value.$get()
495-
: value
478+
? val.$get()
479+
: val
496480
},
497481
set: function (newVal) {
498-
var value = binding.value
482+
var val = scope[key]
499483
if (binding.isComputed) {
500-
if (value.$set) {
501-
value.$set(newVal)
502-
}
503-
} else if (newVal !== value) {
504-
// unwatch the old value
505-
Observer.unobserve(value, key, ob)
506-
// set new value
507-
binding.value = newVal
508-
ob.emit('set', key, newVal)
509-
Observer.ensurePaths(key, newVal, compiler.bindings)
510-
// now watch the new value, which in turn emits 'set'
511-
// for all its nested values
512-
Observer.observe(newVal, key, ob)
484+
if (val.$set) val.$set(newVal)
485+
} else {
486+
scope[key] = newVal
513487
}
514488
}
515489
})
@@ -631,28 +605,14 @@ CompilerProto.destroy = function () {
631605

632606
// Helpers --------------------------------------------------------------------
633607

634-
/**
635-
* determine which viewmodel a key belongs to based on nesting symbols
636-
*/
637-
function traceOwnerCompiler (key, compiler) {
638-
if (key.nesting) {
639-
var levels = key.nesting
640-
while (compiler.parentCompiler && levels--) {
641-
compiler = compiler.parentCompiler
642-
}
643-
} else if (key.root) {
644-
while (compiler.parentCompiler) {
645-
compiler = compiler.parentCompiler
646-
}
647-
}
648-
return compiler
649-
}
650-
651608
/**
652609
* shorthand for getting root compiler
653610
*/
654611
function getRoot (compiler) {
655-
return traceOwnerCompiler({ root: true }, compiler)
612+
while (compiler.parentCompiler) {
613+
compiler = compiler.parentCompiler
614+
}
615+
return compiler
656616
}
657617

658618
module.exports = Compiler

src/deps-parser.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,11 @@ var Emitter = require('./emitter'),
99
function catchDeps (binding) {
1010
if (binding.isFn) return
1111
utils.log('\n─ ' + binding.key)
12-
var has = []
12+
var got = utils.hash()
1313
observer.on('get', function (dep) {
14-
if (has.indexOf(dep) > -1) return
15-
has.push(dep)
14+
var has = got[dep.key]
15+
if (has && has.compiler === dep.compiler) return
16+
got[dep.key] = dep
1617
utils.log(' └─ ' + dep.key)
1718
binding.deps.push(dep)
1819
dep.subs.push(binding)

src/directive.js

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ var config = require('./config'),
1515
ARG_RE = /^([\w- ]+):(.+)$/,
1616
FILTERS_RE = /\|[^\|]+/g,
1717
FILTER_TOKEN_RE = /[^\s']+|'[^']+'/g,
18-
NESTING_RE = /^\^+/,
1918
SINGLE_VAR_RE = /^[\w\.\$]+$/
2019

2120
/**
@@ -76,7 +75,6 @@ var DirProto = Directive.prototype
7675
* parse a key, extract argument and nesting/root info
7776
*/
7877
function parseKey (dir, rawKey) {
79-
8078
var key = rawKey
8179
if (rawKey.indexOf(':') > -1) {
8280
var argMatch = rawKey.match(ARG_RE)
@@ -87,20 +85,6 @@ function parseKey (dir, rawKey) {
8785
? argMatch[1].trim()
8886
: null
8987
}
90-
91-
// nesting
92-
var firstChar = key.charAt(0)
93-
dir.root = firstChar === '*'
94-
dir.nesting = firstChar === '^'
95-
? key.match(NESTING_RE)[0].length
96-
: false
97-
98-
if (dir.nesting) {
99-
key = key.slice(dir.nesting)
100-
} else if (dir.root) {
101-
key = key.slice(1)
102-
}
103-
10488
dir.key = key
10589
}
10690

src/directives/component.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,11 @@ module.exports = {
2020
var Ctor = this.compiler.getOption('components', this.arg)
2121
if (!Ctor) utils.warn('unknown component: ' + this.arg)
2222
var options = {
23-
el: this.el,
24-
compilerOptions: {
25-
parentCompiler: this.compiler
26-
}
23+
el: this.el,
24+
compilerOptions: {
25+
parentCompiler: this.compiler
2726
}
27+
}
2828
if (value) {
2929
options.scope = {
3030
model: value

src/directives/on.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ module.exports = {
5151
if (target) {
5252
e.el = target
5353
e.vm = target.vue_viewmodel
54-
e.item = e.vm[compiler.repeatPrefix]
54+
e.item = e.vm.$scope[compiler.repeatPrefix]
5555
handler.call(ownerVM, e)
5656
}
5757
}
@@ -66,7 +66,7 @@ module.exports = {
6666
e.el = e.currentTarget
6767
e.vm = vm
6868
if (compiler.repeat) {
69-
e.item = vm[compiler.repeatPrefix]
69+
e.item = vm.$scope[compiler.repeatPrefix]
7070
}
7171
handler.call(ownerVM, e)
7272
}

src/directives/repeat.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ var mutationHandlers = {
6161
data = col[i]
6262
for (j = 0; j < l; j++) {
6363
vm = vms[j]
64-
if (vm[key] === data) {
64+
if (vm.$scope[key] === data) {
6565
sorted[i] = vm
6666
break
6767
}
@@ -201,7 +201,7 @@ module.exports = {
201201
updateIndexes: function () {
202202
var i = this.vms.length
203203
while (i--) {
204-
this.vms[i].$index = i
204+
this.vms[i].$scope.$index = i
205205
}
206206
},
207207

src/exp-parser.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,10 @@ function getRel (path, compiler) {
5858
? path.slice(0, dot)
5959
: path
6060
while (true) {
61-
if (hasOwn.call(vm, key)) {
61+
if (
62+
hasOwn.call(vm.$scope, key) ||
63+
hasOwn.call(vm, key)
64+
) {
6265
break
6366
} else {
6467
if (vm.$parent) {

0 commit comments

Comments
 (0)