8000 refactor component root compilation · Snoopbobb/vue@e65c895 · GitHub
[go: up one dir, main page]

8000 Skip to content

Commit e65c895

Browse files
committed
refactor component root compilation
1 parent a439fa2 commit e65c895

File tree

5 files changed

+169
-150
lines changed

5 files changed

+169
-150
lines changed

src/compiler/compile.js

Lines changed: 130 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -20,18 +20,9 @@ module.exports = compile
2020
*/
2121

2222
function compile (el, options, partial, transcluded) {
23-
var isBlock = el.nodeType === 11
24-
// link function for param attributes.
25-
var params = options.paramAttributes
26-
var paramsLinkFn = params && !partial && !transcluded && !isBlock
27-
? compileParamAttributes(el, params, options)
28-
: null
2923
// link function for the node itself.
30-
// if this is a block instance, we return a link function
31-
// for the attributes found on the container, if any.
32-
// options._containerAttrs are collected during transclusion.
33-
var nodeLinkFn = isBlock
34-
? compileBlockContainer(options._containerAttrs, params, options)
24+
var nodeLinkFn = options._asComponent && !partial
25+
? compileRoot(el, options)
3526
: compileNode(el, options)
3627
// link function for the childNodes
3728
var childLinkFn =
@@ -52,12 +43,12 @@ function compile (el, options, partial, transcluded) {
5243
*/
5344

5445
function compositeLinkFn (vm, el) {
46+
// save original directive count before linking
47+
// so we can capture the directives created during a
48+
// partial compilation.
5549
var originalDirCount = vm._directives.length
5650
var parentOriginalDirCount =
5751
vm.$parent && vm.$parent._directives.length
58-
if (paramsLinkFn) {
59-
paramsLinkFn(vm, el)
60-
}
6152
// cache childNodes before linking parent, fix #657
6253
var childNodes = _.toArray(el.childNodes)
6354
// if this is a transcluded compile, linkers need to be
@@ -109,36 +100,61 @@ function compile (el, options, partial, transcluded) {
109100
}
110101

111102
/**
112-
* Compile the attributes found on a "block container" -
113-
* i.e. the container node in the parent tempate of a block
114-
* instance. We are only concerned with v-with and
115-
* paramAttributes here.
103+
* Compile the root element of a component. There are
104+
* 4 types of things to process here:
105+
*
106+
* 1. paramAttributes on parent container (child scope)
107+
* 2. v-with on parent container (child scope)
108+
* 3. other attrs on parent container (parent scope)
109+
* 4. attrs on the component template root node, if
110+
* replace:true (child scope)
116111
*
117-
* @param {Object} attrs - a map of attr name/value pairs
118-
* @param {Array} params - param attributes list
112+
* Also, if this is a block instance, we only need to
113+
* compile 1 & 2 here.
114+
*
115+
* @param {Element} el
119116
* @param {Object} options
120117
* @return {Function}
121118
*/
122119

123-
function compileBlockContainer (attrs, params, options) {
124-
if (!attrs) return null
125-
var paramsLinkFn = params
126-
? compileParamAttributes(attrs, params, options)
120+
function compileRoot (el, options) {
121+
var isBlock = el.nodeType === 11 // DocumentFragment
122+
var containerAttrs = options._containerAttrs
123+
var replacerAttrs = options._replacerAttrs
124+
var params = options.paramAttributes
125+
var paramsLinkFn, withLinkFn, parentLinkFn, replacerLinkFn
126+
// 1. paramAttributes
127+
paramsLinkFn = params
128+
? compileParamAttributes(el, containerAttrs, params, options)
127129
: null
128-
var withVal = attrs[config.prefix + 'with']
129-
var withLinkFn = null
130+
// 2. F438 v-with
131+
var withName = config.prefix + 'with'
132+
var withVal = containerAttrs && containerAttrs[withName]
130133
if (withVal) {
131-
var descriptor = dirParser.parse(withVal)[0]
132-
var def = options.directives['with']
133-
withLinkFn = function (vm, el) {
134-
vm._bindDir('with', el, descriptor, def)
134+
containerAttrs[withName] = null
135+
withLinkFn = makeNodeLinkFn([{
136+
name: 'with',
137+
descriptors: dirParser.parse(withVal),
138+
def: options.directives['with']
139+
}])
140+
}
141+
if (!isBlock) {
142+
// 3. container attributes
143+
if (containerAttrs) {
144+
parentLinkFn = compileDirectives(containerAttrs, options)
145+
}
146+
if (replacerAttrs) {
147+
// 4. replacer attributes
148+
replacerLinkFn = compileDirectives(replacerAttrs, options)
135149
}
136150
}
137-
return function blockContainerLinkFn (vm) {
138-
// explicitly passing null to the linkers
139-
// since v-with doesn't need a real element
151+
return function rootLinkFn (vm, el, host) {
152+
// explicitly passing null to paramAttributes and v-with
153+
// linkers because they don't need a real element
140154
if (paramsLinkFn) paramsLinkFn(vm, null)
141155
if (withLinkFn) withLinkFn(vm, null)
156+
if (parentLinkFn) parentLinkFn(vm.$parent, el, host)
157+
if (replacerLinkFn) replacerLinkFn(vm, el, host)
142158
}
143159
}
144160

@@ -194,10 +210,7 @@ function compileElement (el, options) {
194210
linkFn = checkTerminalDirectives(el, options)
195211
// if not terminal, build normal link function
196212
if (!linkFn) {
197-
var dirs = collectDirectives(el, options)
198-
linkFn = dirs.length
199-
? makeNodeLinkFn(dirs)
200-
: null
213+
linkFn = compileDirectives(el, options)
201214
}
202215
}
203216
// if the element is a textarea, we need to interpolate
@@ -213,39 +226,6 @@ function compileElement (el, options) {
213226
return linkFn
214227
}
215228

216-
/**
217-
* Build a link function for all directives on a single node.
218-
*
219-
* @param {Array} directives
220-
* @return {Function} directivesLinkFn
221-
*/
222-
223-
function makeNodeLinkFn (directives) {
224-
return function nodeLinkFn (vm, el, host) {
225-
// reverse apply because it's sorted low to high
226-
var i = directives.length
227-
var dir, j, k, target
228-
while (i--) {
229-
dir = directives[i]
230-
// a directive can be transcluded if it's written
231-
// on a component's container in its parent tempalte.
232-
target = dir.transcluded
233-
? vm.$parent
234-
: vm
235-
if (dir._link) {
236-
// custom link fn
237-
dir._link(target, el)
238-
} else {
239-
k = dir.descriptors.length
240-
for (j = 0; j < k; j++) {
241-
target._bindDir(dir.name, el,
242-
dir.descriptors[j], dir.def, host)
243-
}
244-
}
245-
}
246-
}
247-
}
248-
249229
/**
250230
* Compile a textNode and return a nodeLinkFn.
251231
*
@@ -397,19 +377,19 @@ function makeChildLinkFn (linkFns) {
397377
* Compile param attributes on a root element and return
398378
* a paramAttributes link function.
399379
*
400-
* @param {Element|Object} el
401-
* @param {Array} attrs
380+
* @param {Element|DocumentFragment} el
381+
* @param {Object} attrs
382+
* @param {Array} paramNames
402383
* @param {Object} options
403384
* @return {Function} paramsLinkFn
404385
*/
405386

406-
function compileParamAttributes (el, attrs, options) {
387+
function compileParamAttributes (el, attrs, paramNames, options) {
407388
var params = []
408-
var isEl = el.nodeType
409-
var i = attrs.length
389+
var i = paramNames.length
410390
var name, value, param
411391
while (i--) {
412-
name = attrs[i]
392+
name = paramNames[i]
413393
if (/[A-Z]/.test(name)) {
414394
_.warn(
415395
'You seem to be using camelCase for a paramAttribute, ' +
@@ -419,15 +399,18 @@ function compileParamAttributes (el, attrs, options) {
419399
'http://vuejs.org/api/options.html#paramAttributes'
420400
)
421401
}
422-
value = isEl ? el.getAttribute(name) : el[name]
402+
value = attrs[name]
423403
if (value !== null) {
424404
param = {
425405
name: name,
426406
value: value
427407
}
428408
var tokens = textParser.parse(value)
429409
if (tokens) {
430-
if (isEl) el.removeAttribute(name)
410+
if (el && el.nodeType === 1) {
411+
el.removeAttribute(name)
412+
}
413+
attrs[name] = null
431414
if (tokens.length > 1) {
432415
_.warn(
433416
'Invalid param attribute binding: "' +
@@ -536,62 +519,109 @@ function makeTerminalNodeLinkFn (el, dirName, value, options) {
536519
}
537520

538521
/**
539-
* Collect the directives on an element.
522+
* Compile the directives on an element and return a linker.
540523
*
541-
* @param {Element} el
524+
* @param {Element|Object} elOrAttrs
525+
* - could be an object of already-extracted
526+
* container attributes.
542527
* @param {Object} options
543-
* @return {Array}
528+
* @return {Function}
544529
*/
545530

546-
function collectDirectives (el, options) {
547-
var attrs = _.toArray(el.attributes)
531+
function compileDirectives (elOrAttrs, options) {
532+
var attrs = _.isPlainObject(elOrAttrs)
533+
? mapToList(elOrAttrs)
534+
: elOrAttrs.attributes
548535
var i = attrs.length
549536
var dirs = []
550-
var attr, attrName, dir, dirName, dirDef, transcluded
537+
var attr, name, value, dir, dirName, dirDef
551538
while (i--) {
552539
attr = attrs[i]
553-
attrName = attr.name
554-
transcluded =
555-
options._transcludedAttrs &&
556-
options._transcludedAttrs[attrName]
557-
if (attrName.indexOf(config.prefix) === 0) {
558-
dirName = attrName.slice(config.prefix.length)
540+
name = attr.name
541+
value = attr.value
542+
if (value === null) continue
543+
if (name.indexOf(config.prefix) === 0) {
544+
dirName = name.slice(config.prefix.length)
559545
dirDef = options.directives[dirName]
560546
_.assertAsset(dirDef, 'directive', dirName)
561547
if (dirDef) {
562548
dirs.push({
563549
name: dirName,
564-
descriptors: dirParser.parse(attr.value),
565-
def: dirDef,
566-
transcluded: transcluded
550+
descriptors: dirParser.parse(value),
551+
def: dirDef
567552
})
568553
}
569554
} else if (config.interpolate) {
570-
dir = collectAttrDirective(el, attrName, attr.value,
571-
options)
555+
dir = collectAttrDirective(name, value, options)
572556
if (dir) {
573-
dir.transcluded = transcluded
574557
dirs.push(dir)
575558
}
576559
}
577560
}
578561
// sort by priority, LOW to HIGH
579-
dirs.sort(directiveComparator)
580-
return dirs
562+
if (dirs.length) {
563+
dirs.sort(directiveComparator)
564+
return makeNodeLinkFn(dirs)
565+
}
566+
}
567+
568+
/**
569+
* Convert a map (Object) of attributes to an Array.
570+
*
571+
* @param {Object} map
572+
* @return {Array}
573+
*/
574+
575+
function mapToList (map) {
576+
var list = []
577+
for (var key in map) {
578+
list.push({
579+
name: key,
580+
value: map[key]
581+
})
582+
}
583+
return list
584+
}
585+
586+
/**
587+
* Build a link function for all directives on a single node.
588+
*
589+
* @param {Array} directives
590+
* @return {Function} directivesLinkFn
591+
*/
592+
593+
function makeNodeLinkFn (directives) {
594+
return function nodeLinkFn (vm, el, host) {
595+
// reverse apply because it's sorted low to high
596+
var i = directives.length
597+
var dir, j, k
598+
while (i--) {
599+
dir = directives[i]
600+
if (dir._link) {
601+
// custom link fn
602+
dir._link(vm, el)
603+
} else {
604+
k = dir.descriptors.length
605+
for (j = 0; j < k; j++) {
606+
vm._bindDir(dir.name, el,
607+
dir.descriptors[j], dir.def, host)
608+
}
609+
}
610+
}
611+
}
581612
}
582613

583614
/**
584615
* Check an attribute for potential dynamic bindings,
585616
* and return a directive object.
586617
*
587-
* @param {Element} el
588618
* @param {String} name
589619
* @param {String} value
590620
* @param {Object} options
591621
* @return {Object}
592622
*/
593623

594-
function collectAttrDirective (el, name, value, options) {
624+
function collectAttrDirective (name, value, options) {
595625
var tokens = textParser.parse(value)
596626
if (tokens) {
597627
var def = options.directives.attr

0 commit comments

Comments
 (0)
0