模板引擎【免费下载链接】nunjucksA powerful templating engine with inheritance, asynchronous control, and more (jinja2 inspired)项目地址https://gitcode.com/gh_mirrors/nu/nunjucks点击查看免费下载本篇技术指南围绕 NunjucksJinja2 风格的高性能模板引擎支持继承与异步控制的官方 API 文档展开系统讲解其渲染、环境配置、模板加载器、浏览器端预编译、异步渲染以及过滤器/标签扩展的全部 API 用法。读完本文你将掌握nunjucks.configure/render等简化 API、Environment与Template的底层机制、三种内置 Loader 的选型、自定义 Loader 与自定义标签的完整写法以及生产环境预编译的最佳实践。安全警告Nunjucks没有沙箱sandbox执行机制。让用户自行定义模板存在潜在安全风险在服务端可能成为访问敏感数据的攻击向量在客户端可能引入跨站脚本XSS漏洞。请勿在不可信输入上直接执行模板。API 概览Nunjucks 的 API 覆盖了以下几个核心领域模板渲染render、renderString、compile环境配置configure、Environment及其全部方法过滤器与扩展addFilter、addExtension、addGlobal模板加载定制FileSystemLoader、WebLoader、NodeResolveLoader及自定义 Loader浏览器端优化预编译CLI 与 API 两种方式异步渲染异步过滤器、异步扩展、异步 Loader语法定制tags选项简化 APISimplified API如果你不需要对系统做深度定制可以直接使用高层级简化 API 完成模板加载与渲染。它内部维护了一个共享的Environment实例自动根据运行环境Node 或浏览器选择合适的 Loader详见 入口文件 中的configure实现Node 下自动创建FileSystemLoader浏览器下自动创建WebLoader。rendernunjucks.render(name, [context], [callback])按名称name渲染模板context为数据哈希对象。若提供callback渲染完成后以(err, res)形式回调err为可能的错误res为结果若不提供 callbackrender同步返回渲染结果错误直接抛出。var res nunjucks.render(foo.html); var res nunjucks.render(foo.html, { username: James }); nunjucks.render(async.html, function(err, res) { });renderStringnunjucks.renderString(str, context, [callback])与render相同但直接渲染原始字符串而非加载模板var res nunjucks.renderString(Hello {{ username }}, { username: James });compilenunjucks.compile(str, [env], [path])将给定字符串编译为可复用的 NunjucksTemplate对象。注意nunjucks.compile在 入口文件 中会先确保存在默认环境不存在时自动configure()再构造new Template(src, env, path, eagerCompile)。var template nunjucks.compile(Hello {{ username }}); template.render({ username: James });configurenunjucks.configure([path], [opts]);告诉 Nunjucks 模板所在的目录path并通过哈希opts开关各项功能。两个参数可同时提供也可只提供其中一个。path默认为当前工作目录。configure返回一个Environment实例让你在继续使用简化 API 的同时也能添加过滤器、扩展见下文 Environment 章节。opts 可用选项选项默认值说明autoescapetrue是否自动转义含危险字符的输出参见 AutoescapingthrowOnUndefinedfalse输出 null/undefined 值时是否抛出错误trimBlocksfalse自动删除 block/tag 结尾的换行符lstripBlocksfalse自动删除 block/tag 开头的空格watchfalse模板变更时自动重新加载服务端。需安装可选依赖chokidarnoCachefalse不使用缓存每次都重新编译模板服务端web—浏览器端模板加载配置对象useCache默认false开启后模板永不会看到更新、async默认false异步加载模板需配合异步 API 使用express—要安装 nunjucks 的 express 应用实例tagsNunjucks 默认语法自定义 nunjucks 标签语法参见 语法定制代码示例nunjucks.configure(views); // 浏览器中通常使用绝对 URL nunjucks.configure(/views); nunjucks.configure({ autoescape: true }); nunjucks.configure(views, { autoescape: true, express: app, watch: true }); var env nunjucks.configure(views); // 用 env 做点什么重要告诫简化 API如nunjucks.render始终使用最近一次nunjucks.configure的配置。这种隐式状态可能带来意想不到的副作用因此在大多数场景尤其是使用了configure时不推荐使用简化 API而应显式创建环境var env nunjucks.configure(...)后调用env.render(...)等。从源码看入口文件 中的模块级变量e正是这个最后一次配置的全局单例render/renderString/compile都依赖它。installJinjaCompatnunjucks.installJinjaCompat()安装实验性的 Jinja 兼容支持向环境添加 Python 风格的 API。虽然 Nunjucks 并不追求与 Jinja/Python 完全兼容但该功能可帮助需要尽可能贴近 Jinja 的用户。它添加了与 JStrue/false对应的True/False并为数组、对象补充 Python 风格的方法同时支持 Python 的 slice 切片语法。完整实现见 jinja-compat.js该文件通过改写runtime.contextOrFrameLookup、runtime.memberLookup以及存在时Compiler.prototype.assertType、Parser.prototype.parseAggregate等内部方法来实现兼容并保留了uninstall()恢复逻辑。EnvironmentEnvironment是管理模板的核心对象它知道如何加载你的模板也负责加载这些模板通过继承或包含所依赖的内部模板。上面的简化 API 正是替你维护了一个Environment实例。你也可以手动管理它从而指定自定义的模板加载器。构造函数new Environment([loaders], [opts])构造函数接收loaders列表与配置哈希opts。若loaders为 null默认从当前目录或 URL加载可以传单个 Loader也可以传数组——传数组时 Nunjucks 按顺序依次询问直到某个 Loader 找到模板见 environment.js 中的lib.asyncIter(this.loaders, ...)遍历逻辑。opts可用参数为autoescape、throwOnUndefined、trimBlocks、lstripBlocks含义与configure相同express、watch不适用需分别通过env.express等方法配置。environment.js 显示构造函数还会统一处理dev标志控制错误堆栈详略、初始化 loaders 缓存、默认过滤器/测试内置 filters 与 tests 会被批量注册进环境。在 Node 中可用FileSystemLoader通过文件系统加载模板在浏览器中可用WebLoader通过 HTTP 加载或使用预编译模板。若使用简化配置 APINunjucks 会根据环境自动创建合适的 Loader。此外 Node 下还有NodeResolveLoader按 Node 模块解析算法require.resolve从文件系统加载但它默认不启用需要显式传入Environment构造函数。// FileSystemLoader 在 Node 中可用 var env new nunjucks.Environment(new nunjucks.FileSystemLoader(views)); var env new nunjucks.Environment(new nunjucks.FileSystemLoader(views), { autoescape: false }); var env new nunjucks.Environment([new nunjucks.FileSystemLoader(views), new MyCustomLoader()]); // WebLoader 在浏览器中可用 var env new nunjucks.Environment(new nunjucks.WebLoader(/views));另一个值得注意的细节environment.js 会检查全局window.nunjucksPrecompiled如果存在即页面已加载预编译模板会自动把PrecompiledLoader插入到 loaders 队列最前面实现预编译模板优先。renderenv.render(name, [context], [callback])渲染名为name的模板context为可选数据哈希。提供callback时以(err, res)回调否则同步返回渲染字符串var res nunjucks.render(foo.html); var res nunjucks.render(foo.html, { username: James }); nunjucks.render(async.html, function(err, res) { });renderStringenv.renderString(src, [context], [callback])与render相同但渲染原始字符串。源码实现environment.js为new Template(src, this, opts.path)后调用tmpl.render。var res nunjucks.renderString(Hello {{ username }}, { username: James });addFilterenv.addFilter(name, func, [async])注册名为name的自定义过滤器调用时执行func。若过滤器需异步执行async传true见 异步支持。返回env以支持链式调用。实现见 environment.js异步过滤器名会被记录到this.asyncFilters数组供编译期使用。getFilterenv.getFilter(name)获取名为name的过滤器函数不存在时抛出filter not found错误见 environment.js。addExtensionenv.addExtension(name, ext)注册名为name的自定义扩展ext。ext是包含若干特定方法的对象由扩展系统调用。返回env以支持链式调用。见 自定义标签。removeExtension / getExtension / hasExtensionenv.removeExtension(name) env.getExtension(name) env.hasExtension(name)分别用于移除、获取、判断扩展是否存在。移除时源码会同步从extensionsList与extensions哈希中删除见 environment.js。addGlobal / getGlobalenv.addGlobal(name, value) env.getGlobal(name)addGlobal添加对所有模板可见的全局值同名全局值会被覆盖返回env支持链式调用getGlobal获取全局值不存在时抛出global not found错误见 environment.js。模板执行时Context.lookup会优先检查全局变量environment.js。getTemplateenv.getTemplate(name, [eagerCompile], [callback])获取名为name的模板。eagerCompile为true时立即编译而非等到渲染时提供callback时以(err, tmpl)回调否则同步返回。使用异步 Loader 时必须使用异步 API。内置 Loader 不需要。见 异步支持 与 Loader。var tmpl env.getTemplate(page.html); var tmpl env.getTemplate(page.html, true); env.getTemplate(from-async-loader.html, function(err, tmpl) { });从实现上看environment.jsgetTemplate先按 loaders 顺序查询缓存未命中时遍历所有 Loader 调用getSource并通过src.loader loader记录来源、按noCache决定是否写入缓存。expressenv.express(app)将 Nunjucks 安装为 express 应用的渲染引擎。安装后即可按 express 常规方式使用res.render。也可以通过简化 API 的configure传入express选项自动完成。返回env支持链式调用。实现见 express-app.js由env.express(app)转发environment.js。var app express(); env.express(app); app.get(/, function(req, res) { res.render(index.html); });opts.autoescapeenv.opts.autoescape该布尔属性反映全局是否开启了 autoescaping。编写操纵 HTML 的高级过滤器时可能有用——通常你只需返回安全字符串即可但少数场景需要查询此开关。load 事件env.on(load, function(name, source, loader))当某个 Loader 加载模板时会发出load事件可用于在运行时探测依赖关系。参数如下name(String)模板名称source(Object)Loader.getSource的返回结果src(String)模板源码path(String)文件路径或 URLnoCache(Bool)是否不缓存loader发出事件的 Loader 实例该事件在 environment.js 的_initLoaders中被统一转发每个 Loader 的load事件被绑定到环境上的load事件同时 Loader 的update事件也会向上转发。TemplateTemplate对象负责模板字符串的编译与渲染。通常Environment替你管理它但也可以直接使用。注意若模板未与任何环境关联则无法被其他模板 include 或继承。构造函数new Template(src, [env], [path], [eagerCompile])src模板字符串env可选Environment实例用于加载其他模板path描述模板位置的字符串用于调试eagerCompile为true时立即开始编译否则延迟到首次渲染从实现看environment.jssrc既可以是普通字符串也可以是{type: code|string, obj: ...}形式的对象后者用于预编译产物编译在_compile中通过compiler.compile生成 JavaScript 代码并new Function执行environment.js。var tmpl new nunjucks.Template(Hello {{ username }}); tmpl.render({ username: James }); // - Hello Jamesrendertmpl.render(context, [callback])使用可选数据哈希context渲染模板。提供callback时以(err, res)回调见 异步支持否则同步返回渲染字符串。源码中同步调用会走rootRenderFunc并直接返回结果异步则通过callbackAsap保证即使模板本身是同步的回调也总是异步触发environment.js。Loader模板加载器Loader 是一个接收模板名、从某处如文件系统或网络加载模板的对象。内置有以下两个实为三个面向不同场景的 Loader。FileSystemLoadernew FileSystemLoader([searchPaths], [opts])仅 Node 可用。从文件系统加载模板searchPaths为搜索路径数组也可传单个路径默认当前工作目录。opts可选属性watch为true时模板文件变更自动更新需安装可选依赖chokidar源码在 node-loaders.js 中require(chokidar)未安装会直接抛出watch requires chokidar to be installednoCache为true时避免使用缓存每次都重新编译// 从 views 目录加载模板 var env new nunjucks.Environment(new nunjucks.FileSystemLoader(views));实现细节node-loaders.jsgetSource只允许搜索当前目录及其子目录通过路径前缀校验返回{src, path, noCache}结构并发出load事件。另外注意若第二个参数误传布尔值旧版本兼容逻辑会打印警告。NodeResolveLoadernew NodeResolveLoader([opts])同样仅 Node 可用。按 Node 模块解析算法require.resolve从文件系统加载模板。opts与FileSystemLoader相同。实现中node-loaders.js会拒绝以./、../或盘符开头的路径防止目录穿越。WebLoadernew WebLoader([baseURL], [opts])仅浏览器可用。baseURL为加载模板的 URL必须同域默认当前相对目录。opts可选属性useCache为true时模板总是缓存看不到更新。默认关闭缓存因为 HTTP 下无法感知变化、无法做 dirty cache。记住生产环境必须预编译模板async为true时模板异步加载默认同步。使用它时必须使用异步渲染 API给render传 callback该 Loader 还能识别预编译模板并自动使用而不走 HTTP生产环境应始终如此见 预编译。// 从 /views 加载模板 var env new nunjucks.Environment(new nunjucks.WebLoader(/views))实现细节web-loaders.jsgetSource通过XMLHttpRequest加载${baseURL}/${name}并附加时间戳参数做缓存破坏fetch明确禁止在非浏览器环境无window使用。编写自定义 Loader对于更复杂的场景如从数据库加载可以编写自定义 Loader只需创建一个带getSource(name)方法的对象即可name为模板名。function MyLoader(opts) { // 配置 } MyLoader.prototype.getSource function(name) { // 加载模板 // 返回一个对象包含 // - src : String. 模板源码 // - path : String. 模板路径 // - noCache : Bool. 不缓存该模板可选 }如果你希望跟踪模板更新、清除内部缓存以便看到更新则需要继承Loader类从而获得发出事件的能力var MyLoader nunjucks.Loader.extend({ init: function() { // 在此设置监视模板的进程 // 模板变化时调用 this.emit(update, name) }, getSource: function(name) { // 加载模板 } });异步 Loader以上都是同步 LoadergetSource立即返回源码。同步的好处是调用方不必使用异步 API也无需担心异步模板的副作用。但当你需要从数据库加载时异步是必要的。只需给 Loader 加上async: true属性var MyLoader nunjucks.Loader.extend({ async: true, getSource: function(name, callback) { // 加载模板 // ... callback(err, res); } });此时你必须使用异步 API见 异步支持。注意使用异步 Loader 时不能在for循环内加载模板for会编译成命令式 JSfor循环。如需在循环内加载模板必须显式使用asyncEach标签它与for完全相同但支持异步见 注意事项。浏览器端使用浏览器端使用 Nunjucks 需要更多考量因为涉及模板加载与编译耗时。服务端模板编译一次后缓存在内存中无需担心但客户端若在页面加载时编译模板渲染会非常慢。解决方案是将模板预编译为 JavaScript并在页面加载时以单个.js文件引入。开发时你可能想动态加载模板以即时看到修改Nunjucks 为此设计了对应的开发工作流。唯一必须遵守的规则生产环境始终预编译模板。原因不仅是页面加载时编译所有模板很慢更因为同步 HTTP 加载会阻塞整个页面Nunjucks 默认不是异步的。推荐配置注意有两个不同的 js 文件含编译器的nunjucks.js以及不含编译器的nunjucks-slim.js后者约 8K前者约 20K差异在于是否内置编译器。两者的差异概述见 法语版入门指南。这两个文件由 scripts/bundle.js 构建并在 浏览器测试页 与 slim 测试页 中使用。配置一仅生产预编译此配置在开发时动态加载模板可即时看到变更生产时使用预编译模板用script标签或模块加载器引入nunjucks.js渲染模板见上文 简化 API上线时 预编译 模板为 js 文件并加载到页面优化生产环境可改用nunjucks-slim.js替代nunjucks.js因为使用的是预编译模板体积从 20K 降到 8K不含编译器。但这会让 dev/prod 使用不同 js 文件配置更复杂是否值得自行权衡。配置二始终预编译开发与生产都使用预编译模板配置简单、dev/prod 代码无差异。代价是开发时需要自动重编译机制开发时用 grunt 或 gulp 任务监视模板目录变更时自动 预编译 成 js 文件用script标签或模块加载器引入nunjucks-slim.js与templates.js或你命名的预编译 js 文件渲染模板此方法下只需提交templates.js即可用同一份代码部署到生产。预编译Precompilation使用随 Nunjucks 提供的nunjucks-precompile脚本预编译模板。传入目录或文件它会为模板生成全部 JavaScript// 预编译整个目录 $ nunjucks-precompile views templates.js // 逐个预编译模板 $ nunjucks-precompile views/base.html templates.js $ nunjucks-precompile views/index.html templates.js $ nunjucks-precompile views/about.html templates.js之后只需在页面加载templates.js系统会自动使用预编译模板无需任何代码改动。原理见 precompile-global.js默认的precompileGlobal包装器把每个编译产物写入全局window.nunjucksPrecompiled哈希而 environment.js 在构造时发现该全局对象会自动插入PrecompiledLoader。脚本还有多种选项直接运行nunjucks-precompile可查看帮助。注意所有异步过滤器的名称必须传给脚本因为它们在编译期就必须被知晓。用-a传逗号分隔的异步过滤器列表如-a foo,bar,baz。若只用普通同步过滤器则无需任何操作。扩展无法通过该脚本指定。如果你使用了扩展必须使用下面的编程 API。编程 API如果你使用扩展或异步过滤器就需要用编程方式预编译二者都必须在编译期已知。可以直接把Environment对象传给预编译器它会从中读取扩展与过滤器。客户端与服务端应共享同一个Environment对象保证两者同步。precompilenunjucks.precompile(path, [opts])从path预编译一个文件或目录。opts可用选项选项说明name编译字符串时必填的模板名编译文件时可选默认path。编译目录时自动生成名称asFunction生成可调用函数force出错时继续编译env使用的环境从中获取扩展与异步过滤器include要包含的文件/文件夹数组文件夹自动包含、文件自动排除exclude要排除的文件/文件夹数组文件夹自动包含、文件自动排除wrapperfunction(templates, opts)自定义预编译模板的输出格式必须返回字符串templates对象数组含name模板名与template预编译后的 JS 源码字符串opts上述全部选项组成的对象var env new nunjucks.Environment(); // 扩展必须在编译期已知 env.addExtension(MyExtension, new MyExtension()); // 异步过滤器必须在编译期已知 env.addFilter(asyncFilter, function(val, cb) { // 做点什么 }, true); nunjucks.precompile(/dir/to/views, { env: env });precompileStringnunjucks.precompileString(str, [opts])与precompile完全相同但编译的是原始字符串且强制要求name选项见 precompile.js。异步支持Async Support只有在需要异步渲染时才需要读本节。异步渲染没有任何性能优势它只是让自定义过滤器与扩展能够进行异步调用。若不关心这点直接使用普通 API如var res env.render(foo.html)即可无需强制传callback——这就是所有渲染函数中 callback 都可选的原因。自 1.0 起Nunjucks 支持异步渲染自定义过滤器/扩展可以执行数据库查询等异步操作模板渲染会挂起直到 callback 被调用。模板 Loader 也可以是异步的从而支持从数据库等来源加载模板见 编写自定义 Loader。使用异步 Loader 时必须使用异步 API。内置的文件系统与HTTP Loader 都是同步的没有性能问题——因为文件系统有缓存而生产环境应预编译模板、绝不走 HTTP。使用异步功能时必须这样调用异步 APInunjucks.render(foo.html, function(err, res) { // 检查 err 并处理结果 });注意事项Be careful!Nunjucks 默认是同步的因此编写异步模板时必须遵守几条规则始终使用异步 APIrender必须接收回调函数异步过滤器与扩展必须在编译期已知预编译时需显式指定见 预编译若自定义 Loader 是异步的不能在for循环内包含模板因为for会编译为命令式 JSfor循环。必须显式使用asyncEach标签迭代它与同步for完全相同Autoescaping自动转义默认情况下Nunjucks 会转义所有输出出于安全考虑推荐保持开启。若关闭 autoescapingNunjucks 将原样输出所有内容。关闭方法在Environment上把autoescape设为false。var env nunjucks.configure(/path/to/templates, { autoescape: false });语法定制Customizing the Syntax如果希望变量、块、注释使用不同于{{、{%、{#的标记可以用tags选项指定var env nunjucks.configure(/path/to/templates, { tags: { blockStart: %, blockEnd: %, variableStart: $, variableEnd: $, commentStart: #, commentEnd: # } });使用该环境后模板写法如下ul % for item in items % li$ item $/li % endfor % /ul自定义过滤器Custom Filters用Environment的addFilter方法安装自定义过滤器。过滤器就是一个函数第一个参数是被处理对象后续参数为模板中按顺序传入的过滤器参数。var nunjucks require(nunjucks); var env new nunjucks.Environment(); env.addFilter(shorten, function(str, count) { return str.slice(0, count || 5); });这添加了一个返回字符串前count个字符的shorten过滤器count默认 5。用法{# 显示前 5 个字符 #} Un message pour vous : {{ message|shorten }} {# 显示前 20 个字符 #} Un message pour vous : {{ message|shorten(20) }}关键字参数 / 默认参数如 模板语法文档 所述Nunjucks 支持关键字/默认参数你可以编写利用它们的 JS 过滤器。所有关键字参数会作为一个哈希作为最后一个参数传给函数。下面是使用关键字参数的foo过滤器env.addFilter(foo, function(num, x, y, kwargs) { return num (kwargs.bar || 10); })模板中的用法{{ 5 | foo(1, 2) }} - 15 {{ 5 | foo(1, 2, bar3) }} - 8必须把所有位置参数放在关键字参数之前foo(1)合法但foo(1, bar10)不合法。因此不能跳过位置参数只给关键字参数虽然 Python 允许foo(1, y1)这种写法这里不行。异步过滤器异步过滤器接收一个 callback 以推进渲染创建时给addFilter第三个参数传truevar env nunjucks.configure(views); env.addFilter(lookup, function(name, callback) { db.getItem(name, callback); }, true); env.renderString({{ item|lookup }}, function(err, res) { // 用 res 做点什么 });务必以两个参数调用 callbackcallback(err, res)err显然可为 null。注意预编译时必须把全部异步过滤器名称告知预编译器见 预编译。从 environment.js 可见异步过滤器会被登记到env.asyncFilters编译模板时environment.js作为参数传给compiler.compile。自定义标签Custom Tags通过创建自定义标签可以实现更复杂的扩展这允许你使用解析器parserAPI对模板做任意处理。注意预编译时必须为编译安装扩展。此时要使用 预编译 API或 grunt/gulp 任务而非命令行脚本需要创建Environment对象、安装扩展并传给预编译器。一个扩展是至少包含两个字段的 JS 对象tags与parse。扩展注册新的标签名并在标签被使用时接管解析器。tags扩展负责的标签名数组parse模板编译时解析这些标签的方法此外还有一个特殊节点类型CallExtension它允许你在运行时调用扩展上的任意方法下文详解。由于需要直接与 parser API 交互、手动构建 AST这个过程略显繁琐但要做真正复杂的事情这是必要的。以下是常用解析器方法parseSignature([throwErrors], [noParens])解析参数列表。默认要求解析器停在左括号左侧、解析到右括号。但自定义标签通常不用括号因此第二个参数传true表示解析参数列表直到标签块结束参数间用逗号分隔。示例{% mytag foo, bar, baz10 %}parseUntilBlocks(names)解析内容直到遇到names数组中某个名字的块。适合解析标签之间的内容。parser API 尚需更多文档可参考上面的说明与下面的完整示例也可以阅读源码 parser.js。最常见的用法是在运行时处理某些标签的内容——像过滤器但加强版因为不限于单个表达式。通常流程是轻量解析模板然后在扩展中拿到带内容的 callback。这通过CallExtension节点完成它接收扩展实例、要调用的方法、标签中解析出的参数列表、以及内容块列表用parseUntilBlocks解析。例如下面实现一个从 URL 抓取内容并注入页面的扩展function RemoteExtension() { this.tags [remote]; this.parse function(parser, nodes, lexer) { // 获取标签标记 var tok parser.nextToken(); // 解析参数并移到块结束之后。没有括号时必须 // 给第二个参数传 true var args parser.parseSignature(null, true); parser.advanceAfterBlockEnd(tok.value); // 解析主体以及可选的 error 块 var body parser.parseUntilBlocks(error, endremote); var errorBody null; if(parser.skipSymbol(error)) { parser.skip(lexer.TOKEN_BLOCK_END); errorBody parser.parseUntilBlocks(endremote); } parser.advanceAfterBlockEnd(); // 关于 CallExtension 的说明见上文 return new nodes.CallExtension(this, run, args, [body, errorBody]); }; this.run function(context, url, body, errorBody) { var id el Math.floor(Math.random() * 10000); var ret new nunjucks.runtime.SafeString(div id id body() /div); var ajax new XMLHttpRequest(); ajax.onreadystatechange function() { if(ajax.readyState 4) { if(ajax.status 200) { document.getElementById(id).innerHTML ajax.responseText; } else { document.getElementById(id).innerHTML errorBody(); } } }; ajax.open(GET, url, true); ajax.send(); return ret; }; } env.addExtension(RemoteExtension, new RemoteExtension());模板中的用法{% remote /stuff %} 这段内容将被 /stuff 的内容替换 {% error %} 抓取 /stuff 时出错 {% endremote %}异步扩展还有另一个节点CallExtensionAsync它是CallExtension的异步版本。运行时它调用扩展时会额外传入一个参数callback。模板渲染会挂起直到 callback 被调用。上面示例中的run函数将变成this.run function(context, url, body, errorBody, callback) { // 做点异步的事然后调用 callback(err, res) };若你做出了有意思的扩展可参考 Nunjucks 官方 Wiki 的 Custom Tags 页面向社区分享。赞分享模板引擎【免费下载链接】nunjucksA powerful templating engine with inheritance, asynchronous control, and more (jinja2 inspired)项目地址https://gitcode.com/gh_mirrors/nu/nunjucks点击查看免费下载相关推荐Nunjucks API 完全指南渲染、扩展与异步模板引擎的实战手册Nunjucks API 完全指南渲染、扩展与异步模板引擎的实战手册 Nunjucks 是一个受 Jinja2 启发的 JavaScript 模板引擎当前仓模板引擎Vapor Leaf 模板引擎速查清单从依赖集成、视图渲染到自定义标签的完整实战指南Vapor Leaf 模板引擎速查清单从依赖集成、视图渲染到自定义标签的完整实战指南 本指南以仓库中的 Leaf 备忘清单 https://link.gitc文档教程Egg.js模板引擎集成Nunjucks渲染实战Egg.js模板引擎集成Nunjucks渲染实战 你是否在寻找一种简单高效的方式来构建动态网页Egg.js作为基于Node.js和Koa的企业级框架提供了后端Web框架上一篇IBM发布2.58亿参数文档智能模型重新定义多模态处理效率下一篇CL4R1T4S年度报告2025 AI系统透明度研究进展创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
企业数字化 ERP 产品动态
相关推荐
oapi-codegen 代码审查指南:守护生成代码质量与下游兼容性的七项实践 开发工具代码生成API设计 【免费下载链接】oapi-codegen Generate Go client and server boilerplate from OpenAPI 3 specifications 项目地址: https://gitcode.com/gh_mirrors/oa/oapi-codegen 点击查看 免费下载 导读
oapi-codegen 是一个将 OpenAPI 3.x 规范… · 2026/9/25 7:59:15
LTX-Video 部署指南:8GB 显存跑通实时图生视频 LTX-Video 部署指南:8GB 显存跑通实时图生视频 【免费下载链接】LTX-Video Official repository for LTX-Video 项目地址: https://gitcode.com/GitHub_Trending/ltx/LTX-Video
LTX-Video 是 DiT 架构的潜在空间扩散视频生成模型:一张静图喂进去能… · 2026/9/25 7:59:15
华为Atlas 300V 24G推理加速卡部署YOLO实战指南 1. 先回答热搜:Atlas 300V 24G是不是运算加速卡1.1 从昇腾310P看这张卡的“加速”属性这段时间后台一直有人问两个问题,一个是“atlas 300v 24g 是运算加速卡吗”,一个是“atlas 部署yolo”。这俩问题其实可以合成一篇文章来回答,… · 2026/9/25 8:19:02
Atlas 300V 24G推理卡部署YOLOv8全流程实战指南 去年年底团队接了一个工业质检项目,要在工控机里跑实时的目标检测,核心硬件换成了 Atlas 300V 24G 这张推理卡。当时有不少人私信问我,这卡到底是不是运算加速卡,能不能跑 YOLO,部署起来麻不麻烦。刚好这阵子项目进入稳… · 2026/9/25 8:19:02
PDF图片转Word用什么软件?电脑/网页/手机全覆盖实用攻略 日常办公、学习中,大家经常遇到一个难题:拿到图片型PDF、扫描件PDF,普通转换根本没用,转完依旧是无法编辑的图片,手动打字费时又费力。这里先科普一个关键知识点:图片类PDF必须依靠OCR文字识别技术… · 2026/9/25 8:18:56
IT技术岗转网络安全值得吗?成本、路线与就业全景解析 我经常在后台收到类似的提问:干了几年IT技术岗,到底要不要转网络安全?说实话,每次看到这种问题,我都能大概猜到提问者的处境——现有工作不算差,但天花板感越来越明显;网络安全听起来热门、有技… · 2026/9/25 8:18:44
Moto 中 Amazon Managed Prometheus(amp)服务的模拟实现与实战指南 Mock测试 【免费下载链接】moto A library that allows you to easily mock out tests based on AWS infrastructure. 项目地址: https://gitcode.com/gh_mirrors/mo/moto 点击查看 免费下载 Amazon Managed Prometheus(AMP,AWS 的托管 Prom… · 2026/9/25 8:18:31
平头哥倚天720/730/750三代CPU规划解读:微架构迭代与ARM服务器落地实践 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 8:18:31
创维E900V22D刷机全攻略:S905L3SB芯片兼容性解析与救砖实战 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 1:00:31
MQTT协议原理与Broker服务器搭建实战:从Mosquitto到EMQX /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 1:00:37