art template 简明教程

2,942 阅读2分钟

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>&#60;h3&#62;content h3&#60;/h3&#62;</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外部导入的变量、以及全局变量
print字符串输出函数
include子模板载入函数
extend模板继承模板导入函数
block模板块声明函数

上述模版变量都可以直接在模版中使用,就像上例中的 $imports

使用 .art 模版作为 webpack 默认模版

上面例子中,webpack 中使用的还是普通 html 作为 HtmlWebpackPlugin 的模版。虽然 art-template 有相对不错的模版处理能力,但是放到 VueReact 面前,真是没法比,直接作为生产力工具还是差点,如果把其作为一个提供默认配置模版的模版引擎来使用,那还是不错的。

/// 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("服务器启动");
});