mustache 渲染列表项 index

一、模板介绍

1.简单介绍

mustache是一种无逻辑的模板引擎,用于将数据渲染到模板上,然后可以将渲染过后的结构输出到任何地方,HTML、File……。 官方GitHub。
mustache是通过一种上下文环境(Context)将数据渲染到模板上。

2.Context简单说明

Context对应着数据对象本身,一开始的时候 Context 对应的就是传递的整个数据。当渲染列表时,Context的指向就会发生变化,每渲染到列表的某一项时,Context 就指向某一项的数据,同时内部的parent指针,着缓存着,上一级的Context。每次渲染数据时,先从当前的 Context指针上根据 key 去寻找对应的 value,如果当前 Context 指针没有对应的 key,则到上一级 Context 去匹配数据,依次类推。其实整个 Context 的模式就对应着数据的层级结构。

3.简单使用

使用都比较简单,常用的指令就只有几个, {{ }}、{{{ }}}、{{@ }}、{{# }}{{/ }}、{{^ }}{{/ }};下面直接贴出代码,在代码的注释里有一些注意事项。
模板:

数据以及渲染:
var data = {
	d: 1,
	name: 'name',
	age: 20,
	subdata: [{
		title: '数据1',
		desc: '数据描述1'
	}, {
		title: '数据2',
		desc: '数据描述2'
	}],
	/* 以下可以通过 函数 控制 if|for 逻辑 */
	/*
	{{^}} 指令用于判断的是:不存在、null, undefined, false, 0, or NaN,
		或者 空字符串(没有trim) 或者 一个空的集合
	{{#}} 指令和 {{^}} 指令相反
	 */
	adult: function() {
		// this 指向的是整个数据本身
		return this.age >= 18;
	}
};
var tpl = document.querySelector("#demoTpl").innerHTML;
document.write(Mustache.render(tpl, data));
/*
Mustache 在 parse(template) 的时候会对模板进行缓存,
方便下次相同模板直接渲染数据而不用再解析,提升渲染性能。
当页面模板较多的时候, 建议在页面最后或者定期清理模板所有缓存的模板引擎;
释放一些一次性的模板引擎的缓存(释放资源,避免内存占用过多);
然后一些动态的引擎等下一次渲染的时候,会自动缓存后续。
 */
Mustache.clearCache();

二、渲染列表 index 解决方案

1.在渲染前重新组装数据

var subdata = [{
	title: '数据1',
	desc: '数据描述1'
}, {
	title: '数据2',
	desc: '数据描述2'
}];
for (var i = 0, sl = subdata.length; i < sl; i++) {
	var currItem = subdata[i];
	// 重新组装数据, 将 index 组装到列表项中
	Object.assign(currItem, {index: i});
	subdata[i] = currItem;
}

2.通过一个全局变量

var template = "{{#subdata}}{{index}}{{/subdata}}";
ar dataIndex = -1; // 全局变量缓存index
var data = {
	subdata: [{
		title: '数据1',
		desc: '数据描述1'
	}, {
		title: '数据2',
		desc: '数据描述2'
	}],
	// 声明一个 index 函数来改变和获取全局缓存的 index
	index: function() {
		return ++dataIndex;
	}
};

3.修改源代码

下面谈谈,我自己对于源代码的简单修改, 让源代码支持 列表 index。重点就是对于 Context 的修改。我直接贴代码,在代码的注释里,有变动提示。

a. 源代码 355 行, Context 构造函数

/**
 * Represents a rendering context by wrapping a view object and
 * maintaining a reference to the parent context.
 * Context (view, parentContext) --> Context (view, parentContext, itemIndex)
 * @param {Integer} itemIndex 列表项的 index
 */
function Context (view, parentContext, itemIndex) {
  this.view = view;
  this.itemIndex = itemIndex; // 如果是渲染的列表的item, 带上 index
  this.cache = { '.': this.view };
  this.parent = parentContext;
}

b. 修改 Context push 函数

/**
 * Creates a new context using the given view with this context as the parent.
 * push(view) --> push(view, itemIndex)
 * @param {Number} [itemIndex] [构造列表项 Context 时, 可以带上 item index, 以上引擎支持 渲染列表的 index]
 */
 Context.prototype.push = function push (view, itemIndex) {
   return new Context(view, this, itemIndex);
 };

c. 修改 Context lookup 函数

/**
  * Returns the value of the given name in this context, traversing
  * up the context hierarchy if the value is absent in this context's view.
  * 如果是渲染的集合数据,并且 name 为 'index|$index',
  *   则渲染Array index;如果数据本身有 name 字段,则以数据本身的name优先]
  */
 Context.prototype.lookup = function lookup (name) {
    var cache = this.cache, itemIndex = this.itemIndex; // 获取列表项的 index
    var value;
    if (cache.hasOwnProperty(name)) {
      value = cache[name];
    }  else if((name === 'index' || name === '$index') && itemIndex >= 0) { // 渲染 列表 index
      // 为了避免有时 index 冲突, 添加 $index
      value = itemIndex;
    } else {
	………

d. 修改 Writer --> renderSection 函数

if (isArray(value)) {
  for (var j = 0, valueLength = value.length; j < valueLength; ++j) {
    // 这里 context.push(value[j]) --> context.push(value[j], j) 传递列表项的 index
    buffer += this.renderTokens(token[4], context.push(value[j], j), partials, originalTemplate);
  }
}
这样就基本改完了,然后就可以通过 {{index}}、{{$index}}的方式获取index了。

以上只是我自己的一些更改,如果觉得有什么不足或者有其它更好的解决方案,可以在评论中留言交流。

最后附上改版后(v 2.3.0) 的资源: mustache-index

你可能感兴趣的:(javascript)