art-template 是一个简约、超快的模板引擎。
它采用作用域预声明的技术来优化模板渲染速度,从而获得接近 JavaScript 极限的运行性能,并且同时支持 NodeJS 和浏览器(官网介绍)
支持两种语法:
- 标准语法
- 原始语法
/// 标准语法
{{if user}}
<h2>{{ user.name }}</h2>
{{/if}}
...
/// 原始语法
<% if (user) %>
<h2><%= user.name %></h2>
<% } %>
核心方法
- template (filename, data) // 基于模板名渲染模板
- template.compile (source, options) // 将模板源代码编译成函数
- template.render (source, data, options) // 将模板源代码编译成函数并立刻执行
demo
在浏览器端的例子
npm i -S art-template
npm i -D art-template-loader webpack webpack-cli webpack-dev-server @babel/core @babel/cli @babel/preset-env
这里主要是配合 webpack 以及相关解析 loader 来编写代码
///babel.config.js
const presets = [["@babel/env"]];
module.exports = {
presets
};
...
/// webpack.config.js
const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
entry: "./src/index.js",
output: {
filename: "[name].js"
},
resolve: {
extensions: [".js", ".html"]
},
module: {
rules: [
{
test: /\.js$/,
use: "babel-loader",
exclude: /node_modules/
},
{
test: /\.art/,
use: "art-template-loader"
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: "./src/tpl/index.html"
})
]
};
...
/// tpl/index.html webpack的基本模版
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>art template</title>
</head>
<body>
<div id="app"></div>
</body>
</html>
...
/// index.js
import "art-template/lib/runtime"; // 此处引入的运行时环境,
const render = require("./art/index.art"); // 配合 art-template-loader 使用
// 配置模版数据
const data = {
title: "这是art-template页面",
msg1: "信息一",
msg2: "信息二"
};
const htmlStr = render(data)
const app = document.getElementById("app");
app.innerHTML = htmlStr;
...
/// art/index.art
{{include './header.art'}}
<p>{{msg1}}</p>
<p>{{msg2}}</p>
...
/// hader.art
<h1>{{title}}</h1>
语法
输出
- 标准语法:{{ 数据 }}
- 原始语法:<%= 数据 %>
/// 标准语法
<p>{{ msg1 }}</p>
<p>{{3 > 2 ? 'info' : 'bar' }}</p>
<p>{{ 3 + 2 }}</p>
/// 原始语法
<p><%= msg2 %></p>
<p><%= 3 > 2 ? 'zoo' : 'xo' %></p>
<p><%= 3 + 4 %></p>
原文输出
类似 vue 的 v-html 指令
/// index.js 对应的 data 信息
content: "<h3>content h3</h3>"
...
/// index.art
<div>{{ content }}</div>
// 在页面上解析为 <div><h3>content h3</h3></div>
...
/// 标准语法
<div>{{@ content }}</div>
/// 原始语法
<div><%- content %></div>
条件判断
- 标准语法
- {{ if v1 }} ... {{ else if v2 }} ... {{ else }} ... {{ /if }}
- 原始语法
- <% if (条件) { %> ... <% } else if (条件) {%> ... <% } else { %> ... <%}%>
/// index.js 对应 data
age: 20,
...
/// index.art
/// 标准语法
{{ if age <= 18 }}
<h3>未成年</h3>
{{ else if age < 30}}
<h3>青年</h3>
{{/if}}
/// 原始语法
<% if (age <= 18) { %>
<h3>未成年</h3>
<% } else if (age < 30) { %>
<h3>青年</h3>
<%}%>
循环
- 标准语法
- {{ each 数据 }} {{/each}}
- 原始语法
- <% for () { %> ... <% } %>
/// 标准语法
{{ each target}}
{{$index}} {{$value}}
{{/each}}
/// 原始语法
<% for(let i = 0; i < ttarget.length; i++) {%>
<%= i %> <%= target[i] %>
<% } %>
...
/// index.js 对应 data
listData: ["list1", "list2"],
...
/// 标准语法
<ul>
{{each listData}}
<li>{{$index}} {{$value}}</li>
{{/each}}
</ul>
/// 原始语法
<ul>
<% for(let i = 0; i < listData.length; i++) { %>
<li>
<%= listData[i] %>
</li>
<% } %>
</ul>
子模版
- 标准语法
- {{include 子模版路径}}
- 原始语法
- <% include(子模版路径) %>
{{include './header.art'}}
...
<% include('./header.art') %>
模版继承
指的是一个模版继承另外一个模版(可以称为骨架模版),达到复用公共部分的代码目的
/// 骨架模版
{{block 插槽1}}{{/block}}
{{block 插槽2}}{{/block}}
...
/// 继承使用
// 原始语法
{{extend './layout.art'}}
{{block 插槽1}} ... {{/block}} // 实现插槽
// 标准语法
<% extend('./layout.art') %>
<% block(插槽1, function(){ %> ... <% }) %>
基本例子
/// layout.art
<div id='top'>
{{block 'top'}}{{/block}}
</div>
<div id='content'>
{{block 'content'}}{{/block}}
</div>
...
/// main.art
/// 引入骨架模版,实现插槽
{{ extend './layout.art'}} // 表示继承骨架模版
{{ block 'top'}} // 把骨架模版中的插槽实现
<p>{{ nameTitle }}</p>
{{/block}}
{{ block 'content'}}
<p>{{ contentTitle }}</p>
{{/block}}
...
/// index.js
const render = require("./art/main.art"); // 引入继承模版
const html = render({
nameTitle: "top title",
contentTitle: "content title"
});
过滤器
过滤器是模板变量的一种语法糖,不过在使用时会有些bug,需要避开。由于上面案例中一致使用的是 template.render() 方法,也就没必要去使用 template.defaults.imports
定义模版变量,要引入其运行时,并加挂到上面。
/// index.js
const runtime = require("art-template/lib/runtime"); // 运行时
const echoInfo = str => {
return `${str} echo info`;
};
const printInfo = str => {
return `${str} print`;
};
// 定义模版变量
runtime.echoInfo = echoInfo;
runtime.printInfo = printInfo;
...
/// main.art
{{ extend './layout.art'}}
{{ block 'top'}}
<p>{{ nameTitle }}</p>
{{/block}}
{{ block 'content'}}
<p>{{ contentTitle }}</p>
{{msg1 | echoInfo | printInfo}} // 这么使用模版变量就叫过滤器,可以进行管道操作 这也是过滤器(模版变量)的原始语法
{{/block}}
{{ $imports.echoInfo(msg2) }} // 这就是当作普通变量使用
如果实际编译运行下,会发现最后{{ $imports.echoInfo(msg2) }}
并不能正常渲染,原因是因为其内部解析行时会把处理骨架模版的操作放到最后,如果不是在通过 block
定义的插槽都会发生解析丢失的情况,这时要么把相关代码放到某个插槽中,要么就不使用extend
模版变量
过滤器例子中的 $imports
就是 内置的一个模版变量,可以访问到模板外部的全局变量与导入的变量
变量 | 含义 |
---|---|
$data | 传入模板的数据 |
$imports | 外部导入的变量、以及全局变量 |
字符串输出函数 | |
include | 子模板载入函数 |
extend | 模板继承模板导入函数 |
block | 模板块声明函数 |
上述模版变量都可以直接在模版中使用,就像上例中的 $imports
使用 .art 模版作为 webpack 默认模版
上面例子中,webpack 中使用的还是普通 html 作为 HtmlWebpackPlugin 的模版。虽然 art-template 有相对不错的模版处理能力,但是放到 Vue
、React
面前,真是没法比,直接作为生产力工具还是差点,如果把其作为一个提供默认配置模版的模版引擎来使用,那还是不错的。
/// webpack.config.js
const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
entry: "./src/index.js",
output: {
filename: "[name].js"
},
resolve: {
extensions: [".js", ".html"]
},
module: {
rules: [
{
test: /\.js$/,
use: "babel-loader",
exclude: /node_modules/
},
{
test: /\.art/,
use: "art-template-loader"
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: "./src/tpl/index.art", // 与之前不同的地方,是在这里引入了 .art 模版
templateParameters: { // 这个是用来传递模版所需的变量参数
title: "title info"
}
})
]
};
...
/// tpl/index.art 基本模版
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>{{ title }}</title> // 这里是一个变量
</head>
<body>
<div id="app"></div>
</body>
</html>
这样就可以把 art-template 当作一个在 html-webpack-plugin 中使用的第三方模版,下面是一个模拟的例子
/// webpack.config.js 相关配置参数中加入一个表示环境的 env (这个参数在 Node 环境中很容易得到)
templateParameters: {
title: "title info",
env: "prd"
}
...
/// index.art 模版文件中添加相关判断
{{if env='prd'}}
<script src="http://code.jquery.com/jquery-3.5.1.slim.js"></script>
{{else}}
<script src="http://code.jquery.com/jquery-2.2.4.min.js"></script>
{{/if}}
<title>{{ title }}</title>
在 Node 环境下的使用
在Node环境下,我们可以用其设置服务器的返回模版
const template = require("art-template");
const path = require("path");
const views = path.join(__dirname, "art", "index.art");
const htmlStr = template(views, {
title: "这是art-template页面",
msg1: "信息一",
msg2: "信息二"
});
console.log(htmlStr);
...
/// index.node.js
const http = require("http");
const template = require("art-template"); // 此处引入的是默认,因为这里不适应art-template-loader自动去处理art文件
const path = require("path");
const views = path.join(__dirname, "art", "index.art");
const htmlStr = template(views, {
title: "这是art-template页面",
msg1: "信息一",
msg2: "信息二"
}); // 这里就直接渲染出模版
const server = http.createServer();
server.on("request", (req, res) => {
res.writeHead(200, { "Content-type": "text/html; charset=UTF-8" });
res.end(htmlStr); // 对客户端发送响应数据
});
server.listen(3111, () => {
console.log("服务器启动");
});