Javascript 字符串模板的简单实现

 更新时间:2016年02月13日 09:44:16   投稿:hebedich  
这篇文章给大家描述的是如何一步步简单的实现Javascript 字符串模板,对于初学javascript的菜鸟们来说应该是篇不错的文章,希望对大家能够有所帮助。

这是源于两年前,当我在做人生中第一个真正意义上的网站时遇到的一个问题

该网站采用前后端分离的方式,由后端的 REST 接口返回 JSON 数据,再由前端渲染到页面上。

同许多初学 Javascript 的菜鸟一样,起初,我也是采用拼接字符串的形式,将 JSON 数据嵌入 HTML 中。开始时代码量较少,暂时还可以接受。但当页面结构复杂起来后,其弱点开始变得无法忍受起来:

  1. 书写不连贯。每写一个变量就要断一下,插入一个 + 和 "。十分容易出错。
  2. 无法重用。HTML 片段都是离散化的数据,难以对其中重复的部分进行提取。
  3. 无法很好地利用 <template> 标签。这是 HTML5 中新增的一个标签,标准极力推荐将 HTML 模板放入 <template> 标签中,使代码更简洁。
  4. 当时我的心情就是这样的:

这TMD是在逗我吗

为了解决这个问题,我暂时放下了手上的项目,花了半个小时实现一个极简易的字符串模板。

需求描述

实现一个 render(template, context) 方法,将 template 中的占位符用 context 填充。要求:

不需要有控制流成分(如 循环、条件 等等),只要有变量替换功能即可
级联的变量也可以展开
被转义的的分隔符 { 和 } 不应该被渲染,分隔符与变量之间允许有空白字符
例子:

render('My name is {name}', {
  name: 'hsfzxjy'
}); // My name is hsfzxjy

render('I am in {profile.location}', {
  name: 'hsfzxjy',
  profile: {
    location: 'Guangzhou'
  }
}); // I am in Guangzhou

render('{ greeting }. \\{ This block will not be rendered }', {
  greeting: 'Hi'
}); // Hi. { This block will not be rendered }

实现

先写下函数的框架:

function render(template, context) {

}

显然,要做的第一件事便是 匹配模板中的占位符。

匹配占位符

匹配的事,肯定是交给正则表达式来完成。那么,这个正则表达式应该长什么样呢?

根据 需求 1、2 的描述,我们可以写出:

var reg = /\{([^\{\}]+)\}/g;

至于需求 3,我第一个想到的概念是 前向匹配,可惜 Javascript 并不支持,只好用一个折衷的办法:

var reg = /(\\)?\{([^\{\}\\]+)(\\)?\}/g;
// 若是第一个或第三个分组值不为空,则不渲染
现在,代码应该是这样:

function render(template, context) {

  var tokenReg = /(\\)?\{([^\{\}\\]+)(\\)?\}/g;

  return template.replace(tokenReg, function (word, slash1, token, slash2) {
    if (slash1 || slash2) { // 匹配到转义字符
      return word.replace('\\', ''); // 如果 分隔符被转义,则不渲染
    }

    // ...
  })
}

占位符替换

嗯,正则表达式确定了,接下来要做的便是替换工作。

根据 需求2,模板引擎不仅要能渲染一级变量,更要渲染多级变量。这该怎么做呢?

其实很简单:将 token 按 . 分隔开,逐级查找即可:

var variables = token.replace(/\s/g, '').split('.'); // 切割 token
var currentObject = context;
var i, length, variable;

// 逐级查找 context
for (i = 0, length = variables.length, variable = variables[i]; i < length; ++i)
  currentObject = currentObject[variable];

return currentObject;

不过,有可能 token 指定的变量并不存在,这时上面的代码便会报错。为了更好的体验,代码最好有一定的容错能力:

var variables = token.replace(/\s/g, '').split('.'); // 切割 token
var currentObject = context;
var i, length, variable;

for (i = 0, length = variables.length, variable = variables[i]; i < length; ++i) {
  currentObject = currentObject[variable];
  if (currentObject === undefined || currentObject === null) return ''; // 如果当前索引的对象不存在,则直接返回空字符串。
}

return currentObject;

把所有的代码组合在一起,便得到了最终的版本:

function render(template, context) {

  var tokenReg = /(\\)?\{([^\{\}\\]+)(\\)?\}/g;

  return template.replace(tokenReg, function (word, slash1, token, slash2) {
    if (slash1 || slash2) { 
      return word.replace('\\', '');
    }

    var variables = token.replace(/\s/g, '').split('.');
    var currentObject = context;
    var i, length, variable;

    for (i = 0, length = variables.length, variable = variables[i]; i < length; ++i) {
      currentObject = currentObject[variable];
      if (currentObject === undefined || currentObject === null) return '';
    }

    return currentObject;
  })
}

除去空白行,一共 17 行。

将函数挂到 String 的原型链

甚至,我们可以通过修改原型链,实现一些很酷的效果:

String.prototype.render = function (context) {
  return render(this, context);
};

之后,我们便可以这样调用啦:

"{greeting}! My name is { author.name }.".render({
  greeting: "Hi",
  author: {
    name: "hsfzxjy"
  }
});
// Hi! My name is hsfzxjy.

相关文章

  • 微信小程序web-view无法打开该页面不支持打开的解决方法

    微信小程序web-view无法打开该页面不支持打开的解决方法

    小程序现在日渐成熟,功能也越来越强大,我们今天来一起看看小程序跳转的问题,下面这篇文章主要给大家介绍了关于微信小程序web-view无法打开该页面不支持打开的解决方法,需要的朋友可以参考下
    2023-01-01
  • 如何解决webpack-dev-server代理常切换问题

    如何解决webpack-dev-server代理常切换问题

    通常我们有一份webpack.dev.config.js使用webpack-dev-server的proxy,代理到开发服务器,来解决本地跨域问题。假如项目变大,可能需要proxy到不同环境,那么如何解决webpack-dev-server代理常切换问题,下面就一起来了解一下
    2019-01-01
  • JavaScript CSS解析B站的弹幕可以不挡人物原理及技巧

    JavaScript CSS解析B站的弹幕可以不挡人物原理及技巧

    这篇文章主要为大家介绍了JavaScript CSS解析B站的弹幕可以不挡人物原理及技巧,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-01-01
  • JavaScript时间格式整理大全(附大量示例)

    JavaScript时间格式整理大全(附大量示例)

    在JavaScript中时间格式转换是一个常见的需求,可以通过多种方式实现,这篇文章主要介绍了JavaScript时间格式的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下
    2025-04-04
  • JavaScript数组去重的两种方法推荐

    JavaScript数组去重的两种方法推荐

    下面小编就为大家带来一篇JavaScript数组去重的两种方法推荐。小编觉得听错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看
    2016-04-04
  • 微信小程序实现顶部搜索框

    微信小程序实现顶部搜索框

    这篇文章主要为大家详细介绍了微信小程序实现顶部搜索框,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-05-05
  • 纯文字版返回顶端的js代码

    纯文字版返回顶端的js代码

    这篇文章介绍了纯文字版返回顶端的js代码,有需要的朋友可以参考一下
    2013-08-08
  • TS如何从目录中提取所有指定扩展名的文件

    TS如何从目录中提取所有指定扩展名的文件

    .ts是一种高清视频封装格式文件,全称为MPEG2-TS,TS表示TransportStream,这篇文章主要介绍了TS如何从目录中提取所有指定扩展名的文件,需要的朋友可以参考下
    2023-05-05
  • IE6下通过a标签点击切换图片的问题

    IE6下通过a标签点击切换图片的问题

    IE6已经让人恶心很久了, Google旗下Youtube决定在3月13日起停止对IE6的支持. 但愿能加速IE6更换的速度.
    2010-11-11
  • 微信小程序后端(java)开发流程的详细步骤

    微信小程序后端(java)开发流程的详细步骤

    这篇文章主要介绍了微信小程序后端开发流程的详细步骤,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-11-11

最新评论