ejs模版实现递归树形结构渲染

  使用过前端模板的同窗们,尤为是使用过nodejs写后台服务的同窗们,应该对ejs模板和jade模板都不陌生。对与ejs模板和jade模板孰强孰弱,载各大论坛中一直争论不休,有说ejs更直观的,也有说jade更优雅、更强大的。我今天不讨论谁好谁坏,而是记录一下这几天发现的一个特殊的使用场景——递归树形结构渲染。html

什么是递归树形结构渲染?前端

  递归树形结构实际上是特指那些父子结构中子级展开后和父级结构相同或相似,并有可能继续展开不断延伸,有点像树形结构中的枝干,每一级的枝干便是上一级枝干的子级,又是下一级枝干的父级,单独从局部上看又是同样的结构,总体又构成了一个井井有条的树。这种结构其实很常见,好比树形文件夹-文件管理、家里的族谱、公司的部门划分、网站分类的菜单等。可是为何又以为在开发中感到很陌生,是由于咱们平时不多写递归的代码,前端的产品形态也不多使用递归和树形结构,若是有就会去掉用别人的三方js库,若是是app或者桌面应用就难免要遍历文件夹来展现,还有就是前端的交互特色更多倾向于多级联动,即点开某一级的时候再去拉数据加载或计算当前的下一级,好比常见的省份组件和日期选择器。node

我遇到的问题——ejs没有递归语法?

  中所周知ejs模板更像是html的扩展,在html的基础上加入了变量和逻辑(条件和循环)以及片断引用。所以常规的状况下是很适合使用,可读性强,先后端的同窗都能轻松上手。可是要实现递归的树形结构就要知足一个条件——本身调用本身。ejs并不支持定义一个函数。可是jade就不一样,jade有mixin的语法,这从某种程度上就知足了要求。webpack

  可是我现有的项目都是ejs模板实现的,局部使用jade又会使项目的模板引擎管理显得很乱。加上另外一个缘由就是个人项目是先后端复用同一套模板,并且这个须要递归的功能是在前端模板来实现的,webpack中使用ejs-loader会有另外一个问题就是不支持include语法。面前有一个方法就是在前端再加一个jade-loader来递归处理树形的数据。web

ejs实现递归树形结构渲染的猜测

  通过一番斗争我找到了在ejs中实现递归树形结构渲染的方法。要记住两个限制:一、尽可能不使用include语法,来保证前端工程中ejs-loader的兼容性;二、ejs模板自己不支持函数定义。后端

  可是这两个不就是递归实现的基本途径吗?是也不是,ejs只是不容许函数的定义,可是却容许函数的调用,即 <%= data %>  中data能够是变量值或表达式,既然是表达式,就说明data能够是某个函数的调用表达式即 <%= data.fn(option) %> 。甚至来讲函数的返回值能够是一个html或者另外一个模板。api

那么函数的定义在哪里完成?

  既然是data.fn 那么fn就是data的一部分,即函数是数据中的一个字段,咱们知道webpack 使用ejs-loader时require进来就是个函数。数组

1 var $$tmpl = require('./tmpl.ejs');
2 // $$tmpl 是个直接能够渲染数据的函数
3 $(body).append($$tmpl(data));

  咱们直接将这个函数传进去,即:app

 1 var $$tmpl = require(' ./tmpl.ejs '); // 一级模板
 2 var $$recursiveTmpl = require('./recursive.ejs'); // 二级模板:局部递归部分的模板,此时$$tmpl 和 $$recursiveTmpl 都是函数
 3 // $$tmpl 是个直接能够渲染数据的函数
 4 $.get('/api/getlist', function (res) {
 5     // 一级模板渲染使用的数据由数据和二级模板函数组成
 6     var data = {
 7         'list': res.list,
 8         'tmplFn': $$recursiveTmpl
 9     }
10 
11     // 将数据传入,一级模板
12     $(body).append($$tmpl(data));
13 })

  js调用的部分就算是完成了,那么模板改怎么写呢?咱们看看一级模板—— ./tmpl.ejs 函数

<p>递归list</p>
<div>
    <%= tmplFn({"list": list, "tmplFn": tmplFn}) %>
</div>

 

咱们再看看二级递归模板—— ./recursive.ejs 

 1 <ul>
 2     <% list.forEach(function(item){ %>
 3         <li>
 4            <span><%= item.title %></span>
 5            <% if (item.subList && item.subList.length > 0) { %>
 6                <span>有下一级</span>
 7                <%= tmplFn ({"list": item.subList , "tmplFn": tmplFn})%>
 8            <% } else { %>
 9                <span>没有下一级了</span>
10            <% } %>
11         </li>
12     <% } %>
13 </ul>

还记得递归调用的特色吗?一、调用自身;二、由特定条件的出口

能够看出二级递归模板是哥不断延展的ul > li > ul > li 的树形结构,再想象递归调用的特色。不难看出

<%= tmplFn ({"list": item.subList , "tmplFn": tmplFn})%>

 

就是不断调用自身的保障,tmplFn就是从最外层的js开始传入的,每次做为属性字段传入,供下一级再次使用。而 if (item.subList && item.subList.length > 0) 就是跳出结束使用的边界条件。

就这样,咱们就实现了ejs的递归调用,并且是在ejs功能不全的前端ejs-loader中兼容的递归调用。

相关文章
相关标签/搜索