Enjoy 自定义指令去除空格与换行

    今天在 jfinal 俱乐部有同学碰到个需求,希望在调用模板函数时去除 html 标签前后的空格与换行,需要处理的模板函数定义如下:

#define test()
<div>
   <span>
     模块一
   </span>
</div>
#end

   以上模板函数的内容需要在 html 中嵌入的 javascript 代码中使用:

var str = "#@test()";

   由于调用模板函数 #@test() 后生成的内容是有空格与换行的,javascript 部分最终生成的结果如下:

var str = "<div>
  <span>
    模块一
  </span>
</div>
";

   以上代码显然是不能工作的 js 片段。能正常工作的 js 代码片段是下面这样的:

var str = "<div><span>模块一</span></div>";

   也就是说调用模板函数生成的内容需要去除其中的空格与换行。解决办法是通过扩展 CallDirective 实现一个自己的 MyCallDirecitve,代码如下:

import java.io.BufferedReader;
import java.io.StringReader;
import com.jfinal.template.Env;
import com.jfinal.template.TemplateException;
import com.jfinal.template.ext.directive.CallDirective;
import com.jfinal.template.io.CharWriter;
import com.jfinal.template.io.FastStringWriter;
import com.jfinal.template.io.Writer;
import com.jfinal.template.stat.Scope;
import com.jfinal.template.stat.ast.Define;

/**
 * 调用模板函数,并去除文本行前后空格与换行字符
 */
public class MyCallDirective extends CallDirective {
	
	public void exec(Env env, Scope scope, Writer writer) {
		Object funcNameValue = funcNameExpr.eval(scope);
		if (funcNameValue == null) {
			if (nullSafe) {
				return ;
			}
			throw new TemplateException("模板函数名为 null", location);
		}
		
		if (!(funcNameValue instanceof String)) {
			throw new TemplateException("模板函数名必须是字符串", location);
		}
		
		Define func = env.getFunction(funcNameValue.toString());
		
		if (func == null) {
			if (nullSafe) {
				return ;
			}
			throw new TemplateException("模板函数未找到 : " + funcNameValue, location);
		}
		
		
		// -------------------------------------------------------------
		
		CharWriter charWriter = new CharWriter(64);
		FastStringWriter fsw = new FastStringWriter();
		charWriter.init(fsw);
		try {
			func.call(env, scope, paraExpr, charWriter);
		} finally {
			charWriter.close();
		}
		
		
		// -------------------------------------------------------------
		
		String content = fsw.toString();
		fsw.close();
		
		try (BufferedReader br = new BufferedReader(new StringReader(content))) {
			String line;
			while ((line=br.readLine()) != null) {
				fsw.append(line.trim());
			}
			
			write(writer, fsw.toString());
		} catch (Exception e) {
			throw new TemplateException(e.getMessage(), location);
		}
	}
}

    代码主体是照抄 CallDirective,并在 exec 中做了少许改进,对每行内容进行了一个 trim() 操作,然后再输出到 Writer 中。

    要使用上述 MyCallDirective 指令,需要如下配置:

engine.addDirective("myCall", MyCallDirective.class, false);

   然后就可以在模板中使用了:

var str = "#myCall('test')";

   最终生成的 js 片段如下:

var str = "<div><span>模块一</span></div>";

   完美解决,打完收工。


   通过使用上面的 MyCallDirective 指令,可以实现 html 模板内容压缩的功能,也就是去除 html 模板文本行前后的空白字符,可以节省 web 项目的网络流量,对于访问量巨大的大型 web 项目十分有益。

    具体到现有的 jfinal 最佳实践项目中,只需将以往的模板函数调用改成 #myCall 形式即可,主要有如下几处:

### 在 index.html 这类模板文件中
#myCall("layout")

### 在 layout.html 中
#myCall("main")

   极其方便

评论区

琴海森林

2020-04-05 00:37

用JFinal就是简单,一言不合就拿源码出来撸

杜福忠

2020-04-06 09:09

如果单纯给JS用,还可以使用 `来处理,var str = `#@test()`;

JFinal

2020-04-06 12:35

@杜福忠 字符 ` 在 js 中支持字符串换行?

杜福忠

2020-04-06 12:59

@JFinal `string` 是模板字符串,ES2015新增的符号,目前主流浏览器都支持,IE就不行了

SuperEric

2020-04-06 20:13

这功能实用,扩展起来很快~

小徐同学

2020-04-07 09:06

点赞+收藏

凉凉凉凉凉

2020-04-13 12:03

@杜福忠 冷知识,波总也一头雾水

张韦南

2020-04-13 13:21

非常感谢,已解决问题

沉默是金

2020-04-24 14:21

貌似和我的场景不太一样- -看不懂,我只想#compress htm #end html压缩成一行

沉默是金

2020-04-24 14:32

那就是这样用,感觉有点别扭,最好可以直接输出压缩内容
#define index()
html
#end
#myCall('index')

JFinal

2020-04-30 00:47

@沉默是金 jfinal enjoy 新加的压缩功能,一行配置打完收工,爽得不行:
https://jfinal.com/share/2094

沉默是金

2020-05-06 10:26

@JFinal 波总优秀~感谢支持