1.概要
前の章では、解析によるテンプレートのASTモデルツリーへの変換について説明しましたが、次に、モデルツリーの最適化と静的アノテーションの作成を続けます。だから問題は、静的なラベル付けとは何ですか?なぜ静的なラベル付けか。
ソースコードのコメントで、次の段落を見つけました。
/**
* Goal of the optimizer: walk the generated template AST tree
* and detect sub-trees that are purely static, i.e. parts of
* the DOM that never needs to change.
*
* Once we detect these sub-trees, we can:
*
* 1. Hoist them into constants, so that we no longer need to
* create fresh nodes for them on each re-render;
* 2. Completely skip them in the patching process.
*/
1.変更する必要のないDOMは静的です。
2.定数として再レンダリングする場合、新しいノードを作成する必要はなく、パッチプロセス中に無視できます(後で紹介します)。
次に、ソースコードの旅を始めます。このモジュールのコードは比較的単純です。
第二に、markStatic
optimizeメソッドは、src / compiler / optimize.jsにあります。
export function optimize (root: ?ASTElement, options: CompilerOptions) {
if (!root) return
isStaticKey = genStaticKeysCached(options.staticKeys || '')
isPlatformReservedTag = options.isReservedTag || no
//1、标注静态节点
markStatic(root)
//2、标注静态根节点
markStaticRoots(root, false)
}
プロセス全体は2つの部分に分かれています。最初の部分は静的ノードでマークされ、2番目の部分は静的ルートノードでマークされています。最初にmarkStaticメソッドを見てください。
function markStatic (node: ASTNode) {
//1、标注节点的状态
node.static = isStatic(node)
//2、对标签节点进行处理
if (node.type === 1) {
// do not make component slot content static. this avoids
// 1. components not able to mutate slot nodes
// 2. static slot content fails for hot-reloading
if (
//非平台保留标签(html,svg)
!isPlatformReservedTag(node.tag) &&
//不是slot标签
node.tag !== 'slot' &&
//不是一个内联模板容器
node.attrsMap['inline-template'] == null
) {
return
}
//递归其子节点,标注状态
for (let i = 0, l = node.children.length; i < l; i++) {
const child = node.children[i]
markStatic(child)
//子节点非静态,则该节点也标注非静态
if (!child.static) {
node.static = false
}
}
//对ifConditions进行循环递归
if (node.ifConditions) {
for (let i = 1, l = node.ifConditions.length; i < l; i++) {
const block = node.ifConditions[i].block
markStatic(block)
if (!block.static) {
node.static = false
}
}
}
}
}
1.最初のステップは、ノードのステータスを判断してマークすることです。
function isStatic (node: ASTNode): boolean {
if (node.type === 2) { //表达式,标注非静态
return false
}
if (node.type === 3) { // 普通文本,标注静态
return true
}
return !!(node.pre || (//v-pre 指令||
!node.hasBindings && // (无动态绑定&&
!node.if && !node.for && // 没有 v-if 和 v-for &&
!isBuiltInTag(node.tag) && // 不是内置的标签,内置的标签有slot和component &&
isPlatformReservedTag(node.tag) && //是平台保留标签&&
!isDirectChildOfTemplateFor(node) &&//不是 template 标签的直接子元素并且没有包含在 for 循环中
Object.keys(node).every(isStaticKey)//结点包含的属性只能有isStaticKey中指定的几个) //以上条件满足则为静态标签
))
}
ノードタイプは式で、ラベルは静的ではなく、通常のテキストは静的です。
v-pre命令(コンパイルなし)は静的としてマークされるか、以下の条件が満たされる場合は静的です。
(1)動的バインディングなし
(2)v-ifとv-forなし
(3)組み込みタグではなく、組み込みタグにはスロットとコンポーネントがあります
(4)プラットフォーム予約タグ(htmlおよびsvgタグ)
(5)いいえテンプレートタグの直接の子要素で、forループには含まれません
(6)ノードに含まれる属性には、isStaticKeyで指定できる属性はいくつかのみです。
2. 2番目のステップは、ノードタイプをラベルとして処理することです。
最初に、スロットのコンテンツに再帰的にラベルを付けずに直接戻ります。そうでない場合は、子ノードをループして再帰的にラベルを付けます。子ノードが静的でない場合、ノードにも静的ではないラベルを付ける必要があります。したがって、ラベル付けプロセス全体はボトムアップであり、最初に子ノードにラベルを付け、次に親ノードにラベルを付け、レイヤーごとに戻ります。
ifConditionsのループも同様のプロセスです。
3、markStaticRoots
markStaticRootsメソッドを引き続き見てください。
function markStaticRoots (node: ASTNode, isInFor: boolean) {
if (node.type === 1) {
//用以标记在v-for内的静态节点,个属性用以告诉renderStatic(_m)对这个节点生成新的key,避免patch error
if (node.static || node.once) {
node.staticInFor = isInFor
}
//一个节点要成为根节点,那么要满足以下条件:
//1、静态节点,并且有子节点,
//2、子节点不能仅为一个文本节点
if (node.static && node.children.length && !(
node.children.length === 1 &&
node.children[0].type === 3
)) {
node.staticRoot = true
return
} else {
node.staticRoot = false
}
//循环递归标记
if (node.children) {
for (let i = 0, l = node.children.length; i < l; i++) {
markStaticRoots(node.children[i], isInFor || !!node.for)
}
}
if (node.ifConditions) {
for (let i = 1, l = node.ifConditions.length; i < l; i++) {
markStaticRoots(node.ifConditions[i].block, isInFor)
}
}
}
}
静的ルートノードになるには、ノードは次の条件を満たす必要があります。
1.これは静的ノードであり、子ノードがあります
。2。子ノードを単なるテキストノードにすることはできません
2番目の条件の主な考慮事項は、静的ルートノードにラベルを付ける利点が小さいことです。
次に、子ノードを再帰的にループし、タグをループします。
前章のテンプレートを例にとると、マーキング後のASTモデルは次のようになります。
{
"type": 1,
"tag": "div",
"attrsList": [
{
"name": "id",
"value": "app"
}
],
"attrsMap": {
"id": "app"
},
"children": [
{
"type": 1,
"tag": "ul",
"attrsList": [],
"attrsMap": {},
"parent": {
"$ref": "$"
},
"children": [
{
"type": 1,
"tag": "li",
"attrsList": [],
"attrsMap": {
"v-for": "item in items"
},
"parent": {
"$ref": "$[\"children\"][0]"
},
"children": [
{
"type": 2,
"expression": "\"\\n itemid:\"+_s(item.id)+\"\\n \"",
"tokens": [
"\n itemid:",
{
"@binding": "item.id"
},
"\n "
],
"text": "\n itemid:{{item.id}}\n ",
"static": false
}
],
"for": "items",
"alias": "item",
"plain": true,
"static": false,
"staticRoot": false,
"forProcessed": true
}
],
"plain": true,
"static": false,
"staticRoot": false
}
],
"plain": false,
"attrs": [
{
"name": "id",
"value": "\"app\""
}
],
"static": false,
"staticRoot": false
}
各ノードにstaticおよびstaticRoot属性が追加されていることがわかります。リーフノードには式が含まれているため、すべての親ノードがfalseとしてマークされます。
V.まとめ
最適化のロジックは比較的単純で、コードはそれほど多くありません。ノードを静的としてマークした後、効率を向上させるために後続のレンダリングとパッチの準備をします。