在编译阶段后创建新的DOM树,或者继续使用原始修改的DOM

does angular create new DOM tree after compile phase or continue working with original modified DOM?

本文关键字:DOM 继续 或者 修改 原始 创建 编译      更新时间:2023-09-26

例如,我有以下HTML:

<div dir-parent>
    <div dir-child1></div>
</div>

在编译阶段,指令dirParent将接收模板DOM元素,该元素内部有带有dirChild1指令的div。如果我不在dirParent的编译函数中更改这个html,那么就会为dirChild1指令调用编译函数。但是,如果我在dirParent的编译函数中删除了<div dir-child1="" some-child1-attr></div>,那么angular将继续处理这个修改的DOM,该DOM不再具有带有dir-child1指令的div,因此dir-child1指令的编译函数永远不会执行。

我假设一旦angular执行完编译函数,它就会在编译函数期间从修改过的html构建新的DOM。这个假设正确吗?这个修改过的DOM似乎被称为实例DOM元素,并且在pre-link阶段可用,所以我假设这是angular构造实例元素DOM树的阶段。

我花了3个小时研究Angular的源代码(它是以难以阅读的方式编写的),找到了问题的答案。Angular不区分内部的模板元素和实例元素,它在开始编译时使用相同的树。以下是源代码的本质,它证明了:

var element = $("body");
compile(element)($rootScope);
function compile(DOMElementsTree) {
    var compositeLinkFn = compileNodes(DOMElementsTree);
    return function publicLinkFn(scope) {
        var $linkNode = DOMElementsTree;
        compositeLinkFn(scope, $linkNode);
        return $linkNode;
    }
}
function compileNodes(nodesList) {
    var linkFns = [];
    nodesList.forEach(function (node) {
        var nodeLinkFn = applyDirectivesToNode(node);
        var childNodes = nodesList.childNodes;
        var childLinkFn = !!childNodes.length ? compileNodes(childNodes) : null;
        if (nodeLinkFn || childLinkFn) {
            linkFns.push(i, nodeLinkFn, childLinkFn);
        }
    });
    return function compositeLinkFn(scope, nodeList) {
        linkFns.forEach(function (linkFn) {
            var nodeLinkFn = linkFn[1];
            var childNodeLinkFn = linkFn[2];
            if (nodeLinkFn) {
                nodeLinkFn(childLinkFn, scope, nodeList);
            } else if (childNodeLinkFn) {
                childLinkFn(scope, nodeList)
            }
        });
    }
}
function applyDirectivesToNode() {
    // this is where compile functions of all directives on a node are executed
}
function nodeLinkFn() {
    // here pre link and post link functions are executed
}
function childLinkFn() {
    // here pre link and post link functions are executed
}

您可以看到,执行指令的compile函数的applyDirectivesToNode没有返回任何新的DOM节点,而是修改了通过引用接收的DOMElementsTreecompositeLinkFn继续使用此DOM实例。

最初,我的问题中有这样一段示例代码:

var $compile = ...; // injected into your code
var scope = ...;
var parent = ...; // DOM element where the compiled template can be appended
var html = '<div ng-bind="exp"></div>';
// Step 1: parse HTML into DOM element
var template = angular.element(html);
// Step 2: compile the template
var linkFn = $compile(template);
// Step 3: link the compiled template with the scope.
var element = linkFn(scope);
// Step 4: Append to DOM (optional)
parent.appendChild(element);

因此,在本摘录中,最后一步处理publicLinkFn函数的结果,即我上面代码中的$linkNode

DOM元素确实是在步骤1中创建的,但它只在浏览器内存中可用,而不会反映在实际的页面DOM中。

由于元素是在编译和链接后从Javascript(var html = ...)创建的,因此需要使用append将其放回页面,以便用户可以实际查看并与之交互

更新我不明白你到底在问什么,但我认为你不会在StackOverflow上找到答案。你需要的是一个论坛或更多关于AngularJS内部功能的讲座。我强烈建议您阅读这篇文章:http://www.jvandemo.com/the-nitty-gritty-of-compile-and-link-functions-inside-angularjs-directives/

Update2根据我的理解(根据上面引用的文章),Angular将首先从dir-parent编译模板,然后使用相同的模板编译其子级。如果您在顶级编译阶段修改了该模板,例如删除了dir-child1,那么实际上没有其他内容可编译,Angular将继续使用pre-link,最后使用post-link