You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
export function transclude (el, options) {
// extract container attributes to pass them down
// to compiler, because they need to be compiled in
// parent scope. we are mutating the options object here
// assuming the same object will be used for compile
// right after this.
if (options) {
// 把el(虚拟节点,如<hello></hello>)元素上的所有attributes抽取出来存放在了选项对象的_containerAttrs属性上
// 使用el.attributes 方法获取el上面,并使用toArray方法,将类数组转换为真实数组
options._containerAttrs = extractAttrs(el)
}
// for template tags, what we want is its content as
// a documentFragment (for fragment instances)
// 判断是否为 template 标签
if (isTemplate(el)) {
// 得到一段存放在documentFragment里的真实dom
el = parseTemplate(el)
}
if (options) {
if (options._asComponent && !options.template) {
options.template = '<slot></slot>'
}
if (options.template) {
// 将el的内容(子元素和文本节点)抽取出来
options._content = extractContent(el)
// 使用options.template 将虚拟节点转化为真实html, <hello></hello> => <div><h1>hello {{ msg }}</h1></div>
// 但不包括未绑定数据, 则上面转化为 => <div><h1>hello</h1></div>
el = transcludeTemplate(el, options)
}
}
// isFragment: node is a DocumentFragment
// 使用nodeType 为 11 进行判断是非为文档片段
if (isFragment(el)) {
// anchors for fragment instance
// passing in `persist: true` to avoid them being
// discarded by IE during template cloning
prepend(createAnchor('v-start', true), el)
el.appendChild(createAnchor('v-end', true))
}
return el
}
function nodeToFragment (node) {
// if its a template tag and the browser supports it,
// its content is already a document fragment. However, iOS Safari has
// bug when using directly cloned template content with touch
// events and can cause crashes when the nodes are removed from DOM, so we
// have to treat template elements as string templates. (#2805)
/* istanbul ignore if */
// 是template元素或者documentFragment,使用stringToFragment转化并保存节点内容
if (isRealTemplate(node)) {
return stringToFragment(node.innerHTML)
}
// script template
if (node.tagName === 'SCRIPT') {
return stringToFragment(node.textContent)
}
// normal node, clone it to avoid mutating the original
var clonedNode = cloneNode(node)
var frag = document.createDocumentFragment()
var child
/* eslint-disable no-cond-assign */
while (child = clonedNode.firstChild) {
/* eslint-enable no-cond-assign */
frag.appendChild(child)
}
trimNode(frag)
return frag
}
Uh oh!
There was an error while loading. Please reload this page.
在上面说了下,在
Vue.prototype.$mount
完成了大部分工作,而在$mount方法里面,最主要的工作量由this._compile(el)
承担;其主要包括transclude(嵌入)、compileRoot(根节点编译)、compile(页面其他的编译);而在这儿主要说明transclude方法;通过对transclude进行网络翻译结果是"嵌入";其主要目的是将页面中自定义的节点转化为真实的html节点;如一个组件
<hello></hello>
其实际dom为<div><h1>hello {{message}}</h1></div>
,源码; 当我们使用时<div><hello></hello></div>
; 会通过transclude将其转化为<div><div><h1>hello {{message}}</h1></div></div>
,见源码注释;那transclude具体干了什么呢,我们先看它的源码:
首先先看如下代码:
而extractAttrs方法如下,其主要根据元素nodeType去判断是否为元素节点,如果为元素节点,且元素有相关属性,则将属性值取出之后,再转为属性数组;最后将属性数组放到options._containerAttrs中,为什么要这么做呢?因为现在的el可能不是真实的元素,而是诸如
<hello class="test"></hello>
,在后面编译过程,需要将其替换为真实的html节点,所以,它上面的属性值都会先取出来预存起来,后面合并到真实html根节点的属性上面;下一句,根据元素nodeName是否为“template”去判断是否为
<template></template>
元素;如果是,则走parseTemplate(el)
方法,并覆盖当前el对象而
parseTemplate
则主要是将传入内容生成一段存放在documentFragment里的真实dom;进入函数,首先判断传入是否已经是一个文档片段,如果已经是,则直接返回;否则,判断传入是否为字符串,如果为字符串, 先判断是否是"#test"这种选择器类型,如果是,通过document.getElementById
方法取出元素,如果文档中有此元素,将通过nodeToFragment
方式,将其放入一个新的节点片段中并赋给frag,最后返回到外面;如果不是选择器类型字符串,则使用stringToFragment
将其生成一个新的节点片段,并返回;如果传入非字符串而是节点(不管是什么节点,可以是元素节点、文本节点、甚至Comment节点等);则直接通过nodeToFragment
生成节点片段并返回;从上面可见,在
parseTemplate
里面最重要的是nodeToFragment
和stringToFragment
;那么,它们又是如何将传入内容转化为新的文档片段呢?首先看nodeToFragment
:其实看源码,很容易理解,首先判断传入内容是否为template元素或者documentFragment或者script标签,如果是,都直接走
stringToFragment
;后面就是先使用document.createDocumentFragment
创建一个文档片段,然后将节点进行循环appendChild到创建的文档片段中,并返回新的片段;那么,
stringToFragment
呢?这个就相对复杂一点了,如下:首先去缓存查看是否已经有,如果有,则直接取缓存数据,减少程序运行;而后,通过正则判断是否为元素文本,如果不是,则说明为正常的文字文本,直接创建文本节点,并放入新建的DocumentFragment中再放入缓存中,并返回最终生成的DocumentFragment;如果是节点文本,则首先对文本进行修正;比如如果传入的是
<td></td>
则需要在其外层添加tr、tbody、table后才能直接使用appendChild将节点添加到文档碎片中,而无法直接添加td元素到div元素中;在最后返回一个DocumentFragment;以上就是parseTemplate及其里面nodeToFragment、stringToFragment的具体实现;然后我们继续回到transclude;
在transclude后续中,重要就是transcludeTemplate方法,其主要就是通过此函数,根据option.template将自定义标签转化为真实内容的元素节点;如
<hello></hello>
这个自定义标签,会根据此标签里面真实元素而转化为真实的dom结构;函数首先会通过上述parseTemplate方法将模版数据转化为一个临时的DocumentFragment,然后根据是否将根元素进行替换,即
option.replace
是否为true进行对应处理,而如果需要替换,主要进行将替换元素上的属性值和模版根元素属性值进行合并,也就是将替换元素上面的属性合并并添加到根节点上面,如果两个上面都有此属性,则进行合并后的作为最终此属性值,如果模板根元素上没有此属性而自定义元素上有,则将其设置到根元素上,即:所以,综上,在compile中,
el = transclude(el, options)
主要是对元素进行处理,将一个简单的自定义标签根据它对应的template模板数据和option的一些配置,进行整合处理,最后返回整理后的元素数据;The text was updated successfully, but these errors were encountered: