任何返回 DOM 片段的 JavaScript 模板库/系统/引擎/技术?

发布于 2024-12-06 18:51:27 字数 967 浏览 1 评论 0原文

我必须制作一个高速 Web 应用程序,并且需要一个 JavaScript 模板库/系统/引擎/技术来返回 DOM 片段而不是包含 HTML 的字符串。

当然,它应该有一些类似的语言 Resig 的微模板

我期待类似的东西编译后:

function myTemplate(dataToRender){
    var fragment = document.createDocumentFragment();
    fragment = fragment.appendChild(document.createElement('h1'));
    fragment.appendChild(document.createTextNode(dataToRender.title));
    fragment = fragment.parentNode;
    fragment = fragment.appendChild(document.createElement('h2'));
    fragment.appendChild(document.createTextNode(dataToRender.subTitle));
    fragment = fragment.parentNode;
    return fragment;
}

有什么选择吗?

编辑:模板函数不应连接 HTML 字符串。它会降低速度。 JQuery 模板在内部使用字符串。所以 Resig 的微模板。

Edit2:我刚刚在 jsPerf 上做了一个基准测试。这是我在 JavaScript 中做的第一个基准测试,所以有人检查一下(我不确定它是否正确)。

I have to make a high speed web app and I need a JavaScript templating library/system/engine/technique that returns a DOM Fragment instead of a String containing HTML.

Of course it should have some similar language as Resig's Micro-Templating

I am expecting something like this after compilation:

function myTemplate(dataToRender){
    var fragment = document.createDocumentFragment();
    fragment = fragment.appendChild(document.createElement('h1'));
    fragment.appendChild(document.createTextNode(dataToRender.title));
    fragment = fragment.parentNode;
    fragment = fragment.appendChild(document.createElement('h2'));
    fragment.appendChild(document.createTextNode(dataToRender.subTitle));
    fragment = fragment.parentNode;
    return fragment;
}

Is there any option?

Edit: Template function should not concatenate HTML strings. It decreases speed.
JQuery templates are working with strings internally. So Resig's Micro-Templating.

Edit2: I just did a benchmark on jsPerf. It is the first benchmark I did in JavaScript so some check it out(I am not sure if it's correct).

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

半衬遮猫 2024-12-13 18:51:27

查看 jquery 模板。 http://api.jquery.com/category/plugins/templates/

让我们您可以使用“if”、“each”等关键字和未声明的变量创建 html 片段。然后,您可以使用一些值在 JavaScript 的片段上调用“tmpl”,并返回一个 DOM 元素。

Check out jquery templates. http://api.jquery.com/category/plugins/templates/

It let's you create html fragments with keywords like "if", "each", etc and undeclared variables. Then you can call "tmpl" on a fragment from JavaScript with some values, and a DOM element is returned.

指尖凝香 2024-12-13 18:51:27

我在 这个 jsFiddle 中尝试过这个。使用 DOM 方法时,替换一大块内容是最快的,但设置 innerHTML 并不会慢很多,如果您的模板不是很复杂并且不会在字符串中浪费太多时间,则可能是可以接受的操纵。 (这并不奇怪,“快速处理损坏的 HTML”是浏览器应该做的事情,而 innerHTML 是一个古老而流行的属性,可能对其进行了大量优化。)在innerHTML 方法中添加另一个join() 步骤也并没有真正减慢速度。

相反,在 Mac 上的 Chrome 中使用 jQuery.tmpl() 和 DOM 片段方法要慢几个数量级。要么我在 dom_tmpl 函数中做错了什么,要么深度克隆 DOM 节点本质上很慢。

我注释掉了附加测试,因为当您运行整个套件时,它们会冻结选项卡进程 - 数千个到数万个节点被推入文档中可能会以某种方式使 Chrome 感到困惑。单独使用innerHTML 进行附加最终会非常慢,因为字符串最终会变得非常大。

结论似乎是:除非愚蠢地完成或在非常大的字符串上完成,否则在模板库中连接字符串可能不会使其变慢,同时尝试巧妙地克隆块DOM 会的。另外,jQuery.tmpl() 在我的计算机上每秒处理 2000 次左右的操作,在我的 iPhone 4 上每秒处理 500 次左右的操作,如果您针对这些平台,这可能“足够快”。它也与 DOM 片段函数处于相同的范围,这使得后者基本上毫无意义。

如果您主要需要替换现有节点的内容并且您的模板不是很大,请使用 Underscore.js 的模板和 innerHTML。 Underscore.js 似乎对整个字符串进行了十次传递,因此如果您的模板/很大/这可能是一个问题。

如果需要附加到现有节点,可以通过创建包装器元素,设置其 innerHTML,然后将包装器元素附加到目标节点,从而避免序列化和重新解析现有内容。

如果您确实想要速度或者您的模板非常大,您可能需要执行一些操作,例如让服务器端脚本将模板预编译为 Javascript,以创建相应的节点。

(免责声明:我并不声称自己擅长构建测试用例和基准测试,并且仅在 WebKit 中对此进行了测试,您应该根据您的用例进行定制并获取一组更相关的数字。)

更新: 我更新了我的 jsFiddle 基准测试,不使用任何 jQuery 功能,以防其存在(用户数据节点等)是 DOM 节点克隆缓慢的原因。没有多大帮助。

I had a go at this in this jsFiddle. Replacing a chunk of content is fastest when using DOM methods, but setting innerHTML isn't cripplingly slower and probably acceptable if your templates aren't very complex and you won't lose too much time in the string manipulation. (This isn't very surprising, "dealing with broken HTML quickly" is kind of what browsers are supposed to do and innerHTML is an ancient and popular property that probably had lots of optimisation go into it.) Adding another join() step in the innerHTML method also didn't really slow it down.

Conversely, using jQuery.tmpl() /and/ the DOM fragment method was orders of magnitude slower in Chrome on Mac. Either I'm doing something wrong in the dom_tmpl function, or deep-cloning DOM nodes is inherently slow.

I commented out the append tests because they froze up the tab process when you run the whole suite - thousands through tens of thousands of nodes shoved into a document probably confuse Chrome somehow. Appending with innerHTML alone ended up glacially slow because the string ends up being really huge.

The conclusion would seem to be: unless done stupidly or on very large strings, concatenating strings in a templating library likely isn't going to be what makes it slow, while trying to be clever with cloning chunks of the DOM will. Also, jQuery.tmpl() handled 2000-ish ops/sec on my computer, and 500-ish on my iPhone 4, this is likely "fast enough" if you're targetting these platforms. It was also in the same ballpark as the DOM fragment function making the latter largely pointless.

If you mostly need to replace the content of existing nodes and your templates aren't very large, use Underscore.js's templating and innerHTML. Underscore.js seems to do ten passes through the whole string, so if your templates /are/ large this could be an issue.

If you need to append to existing nodes, you can avoid serialising and reparsing the existing content by creating a wrapper element, seting its innerHTML, then append the wrapper element to the target node.

If you really want speed or your templates are crazy large, you'll probably have to do something like having a server-side script precompile your templates into Javascript that creates the respective nodes.

(Disclaimer: I don't claim to be any good at constructing test cases and benchmarks and only tested this in WebKit, you should tailor this to your use case and get a more relevant set of numbers.)

Update: I updated my jsFiddle benchmark to not use any jQuery features, in case its presence (user data on nodes etc.) was the cause of DOM node cloning being slow. Didn't help much.

是伱的 2024-12-13 18:51:27

我不知道这是否是您要寻找的内容,但是 Underscore.js 有一个模板实用程序< /a>.

另外,jquery 可以返回匹配元素的 DOM

I don't know if this is what you're searching for, but Underscore.js has a template utility.

Also, jquery can return the DOM of a matched element.

青衫儰鉨ミ守葔 2024-12-13 18:51:27

您可以创建代表页面区域的单个对象,甚至可以深入到单个元素级别,并且无需诉诸 DOM 脚本即可完成所有这些操作,因为 DOM 脚本会非常慢。例如:

function buttonFrag(data) {
    this.data = data;
}
buttonFrag.prototype = (function() {
    return {
        _html : function(h) {
            h.push("<h1>",data.title,"</h1>");
            h.push("<h2>",data.subTitle,"</h2>");
        },
        render : function(id) {
            var html = [];
            this._html(html);
            document.getElementById.innerHTML = html.join("");
        }
    }
})();

要实现这一点,您只需创建一个新对象,然后对页面上的 id 调用其渲染方法:

var titleFragObj = new titleFrag({title: "My Title",subTitle: "My Subtitle");
titleFragObj.render("someId");

当然,您可以对渲染方法更有创意,并使用 jQuery 之类的东西写入选择器,然后使用 .html 或 .append 方法,如下所示:

render : function(selectorString, bAppend) {
    var html = [];
    this._html(html);
    var htmlString = html.join("");
    var destContainer = $(selectorString);
    if (bAppend) {
        destContainer.append(htmlString);
    } else {
        destContainer.html(htmlString);
    }
}

在这种情况下,您只需提供一个选择器字符串以及是否要附加到容器的末尾,或完全替换其内容:

        titleFragObj.render("#someId",true);

您甚至可以这样做至于创建一个基础对象,您的所有片段源自,那么您要做的就是重写 _html 方法:

function baseFragement(data) {
        this.data = data;
    }
baseFragment.prototype = (function() {
    return {
        _html : function() {
            //stub
        },
        render : function(selectorString, bAppend) {
            var html = [];
            this._html(html);
            var htmlString = html.join("");
            var destContainer = $(selectorString);
            if (bAppend) {
                destContainer.append(htmlString);
            } else {
                destContainer.html(htmlString);
            }
        }
    };
})();

那么所有后代将看起来像这样:

function titleFrag(data) {
    baseFragment.call(this,data);
}
titleFrag.prototype = new baseFragment();
titleFrag.prototype._html = function() {
    h.push("<h1>",data.title,"</h1>");
    h.push("<h2>",data.subTitle,"</h2>");
}

您可以创建一个完整的小片段生成器库,这些片段生成器源自该公共基类。

You could create individual objects that represent regions of your page or even go down as far as the individual element level, and do all this without resorting to DOM scripting which will be super slow. For instance:

function buttonFrag(data) {
    this.data = data;
}
buttonFrag.prototype = (function() {
    return {
        _html : function(h) {
            h.push("<h1>",data.title,"</h1>");
            h.push("<h2>",data.subTitle,"</h2>");
        },
        render : function(id) {
            var html = [];
            this._html(html);
            document.getElementById.innerHTML = html.join("");
        }
    }
})();

To implement this, you'd simply create a new object then invoke its render method to an id on your page:

var titleFragObj = new titleFrag({title: "My Title",subTitle: "My Subtitle");
titleFragObj.render("someId");

Of course you could get a bit more creative about the render method and use something like jQuery to write to a selector, then use the .html or .append methods like this:

render : function(selectorString, bAppend) {
    var html = [];
    this._html(html);
    var htmlString = html.join("");
    var destContainer = $(selectorString);
    if (bAppend) {
        destContainer.append(htmlString);
    } else {
        destContainer.html(htmlString);
    }
}

In that case you'd just provide a selector string and whether or not you want to append to the end of the container, or completely replace its contents:

        titleFragObj.render("#someId",true);

You could even go so far as to create a base object from which all your fragments descend from, then all you'd do is override the _html method:

function baseFragement(data) {
        this.data = data;
    }
baseFragment.prototype = (function() {
    return {
        _html : function() {
            //stub
        },
        render : function(selectorString, bAppend) {
            var html = [];
            this._html(html);
            var htmlString = html.join("");
            var destContainer = $(selectorString);
            if (bAppend) {
                destContainer.append(htmlString);
            } else {
                destContainer.html(htmlString);
            }
        }
    };
})();

Then all descendants would look something like this:

function titleFrag(data) {
    baseFragment.call(this,data);
}
titleFrag.prototype = new baseFragment();
titleFrag.prototype._html = function() {
    h.push("<h1>",data.title,"</h1>");
    h.push("<h2>",data.subTitle,"</h2>");
}

You could create an entire library of little fragment generators that descend from that common base class.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文