Vue 用的模板引擎以及对模板引擎的理解

Vue 的模板引擎

Vue 官方文档参考

Vue 使用了基于 HTML 的语法,允许开发者声明式地将 DOM 绑定至底层 Vue 实例的数据。

在底层实现上,Vue 将模板编译成虚拟 DOM 渲染函数。

  • 插值
    • 文本 (mustache)
    • 原始 HTML (v-html)
    • 特性 (v-bind)
  • 指令(带有 v- 前缀的特殊特性)

模板引擎实现

模板引擎参考,以及作出自己的模板引擎

简单的置换型模板引擎代码

const template = '<p>My name is <%name%>. I am <%age%> years old.</p>';
const data = {
  name: 'lwl',
  age: 18,
};
const regexp = /<%([^%>]+)?%>/g; // 全局替换正则
const templateEngin = (template, data) => {
  let match;
  while ((match = regexp.exec(template))) {
    console.log(match);
    template = template.replace(match[0], data[match[1]]);
  }
  return template;
};
const str = templateEngin(template, data);
// ["<%name%>", "name", index: 14, input: "<p>My name is <%name%>. I am <%age%> years old.</p>", groups: undefined]
// ["<%age%>", "age", index: 24, input: "<p>My name is lwl. I am <%age%> years old.</p>", groups: undefined]
console.log(str); // <p>My name is lwl. I am 18 years old.</p>

复杂逻辑模板引擎

const data = {
  name: 'lwl',
  profile: {
    age: 18,
  },
};
const template = '<p>My name is <%this.name%>. I am <%this.profile.age%> years old.</p>';

// 补充知识:通过字符串创建一个函数
const fn = new Function('name', 'console.log("My name is " + name + ".");');
fn('lwl'); // My name is lwl.

const templateEngin = (template, data) => {
  const regexp = /<%([^%>]+)?%>/g; // 全局替换正则
  let match;
  let cursor = 0;
  let code = 'var arr = [];\n';
  const addCode = str => {
    code += 'arr.push("' + str.replace(/"/g, '\\"') + '");\n'; // replace 将双引号转义
  };
  while ((match = regexp.exec(template))) {
    console.log(match);
    addCode(template.slice(cursor, match.index)); // 拼接匹配项前面的部分
    addCode(match[1]); // 拼接匹配项
    cursor = match.index + match[0].length;
  }
  addCode(template.slice(cursor)); // 拼接最后的部分
  code += 'return arr.join("");';
  console.log(code);
  return template;
};
const str = templateEngin(template, data);
/*
var arr = [];
arr.push("<p>My name is ");
arr.push("this.name"); <-- 是字符串,需要改成非字符串
arr.push(". I am ");
arr.push("this.profile.age");  <-- 是字符串,需要改成非字符串
arr.push(" years old.</p>");
return arr.join("");
*/

将 js 的代码 (this.name 和 this.profile.age) 改成不带双引号的。

const data = {
  name: 'lwl',
  profile: {
    age: 18,
  },
};
const template = '<p>My name is <%this.name%>. I am <%this.profile.age%> years old.</p>';

// 补充知识:通过字符串创建一个函数
const fn = new Function('name', 'console.log("My name is " + name + ".");');
fn('lwl'); // My name is lwl.

const templateEngin = (template, data) => {
  const regexp = /<%([^%>]+)?%>/g; // 全局替换正则
  let match;
  let cursor = 0;
  let code = 'var arr = [];\n';
  const addCode = (str, isJs) => {
    // <-- 修改1
    code += isJs ? 'arr.push(' + str + ');\n' : 'arr.push("' + str.replace(/"/g, '\\"') + '");\n'; // replace 将双引号转义 <-- 修改2
  };
  while ((match = regexp.exec(template))) {
    console.log(match);
    addCode(template.slice(cursor, match.index)); // 拼接匹配项前面的部分
    addCode(match[1], true); // 拼接匹配项 <-- 修改3
    cursor = match.index + match[0].length;
  }
  addCode(template.slice(cursor)); // 拼接最后的部分
  code += 'return arr.join("");';
  console.log(code);
  return new Function(code.replace(/[\r\t\n]/g, '')).apply(data); // <-- 修改4,返回构造函数
  // return template; // <-- 修改5
};
const str = templateEngin(template, data);
console.log(str); //  <p>My name is lwl. I am 18 years old.</p>

带循环的模板引擎

const data = {
  skills: ['JavaScript', 'HTML', 'CSS'],
};
const template = 'My skills: ' + '<%for (let index in this.skills) {%>' + '<%this.skills[index]%> ' + '<%}%>';
const templateEngin = (template, data) => {
  const regexp = /<%([^%>]+)?%>/g; // 全局替换 <%%> 正则
  const conditionReg = /(^( )?(if|for|else|switch|case|break|{|}))(.*)?/g; // 判断条件语句的关键字等
  let cursor = 0;
  let match;
  let code = 'const arr = [];\n';
  const add = (str, isJs) => {
    code += str.match(conditionReg)
      ? str + '\n'
      : isJs
      ? 'arr.push(' + str + ');\n'
      : 'arr.push("' + str.replace(/"/g, '\\"') + '");\n';
  };
  while ((match = regexp.exec(template))) {
    console.log(match);
    add(template.slice(cursor, match.index));
    add(match[1], true);
    cursor = match.index + match[0].length;
  }
  add(template.slice(cursor));
  code += 'return arr.join("");';
  console.log(code);
  return new Function(code.replace(/\t\r\n/g, '')).apply(data);
};

const str = templateEngin(template, data);
console.log(str); // My skills: JavaScript HTML CSS

实现如下效果:

图片

const data = {
  songs: [
    { name: '刚刚好', singer: '薛之谦', url: 'http://music.163.com/xxx' },
    { name: '最佳歌手', singer: '许嵩', url: 'http://music.163.com/xxx' },
    { name: '初学者', singer: '薛之谦', url: 'http://music.163.com/xxx' },
    { name: '绅士', singer: '薛之谦', url: 'http://music.163.com/xxx' },
    { name: '我们', singer: '陈伟霆', url: 'http://music.163.com/xxx' },
    { name: '画风', singer: '后弦', url: 'http://music.163.com/xxx' },
    { name: 'We Are One', singer: '郁可唯', url: 'http://music.163.com/xxx' },
  ],
};

const template =
  '<div>' +
  '<h1>热歌榜</h1>' +
  '<ol>' +
  '<%for (let index in this.songs){%>' +
  '<li><%this.songs[index].name%>-<%this.songs[index].singer%></li>' +
  '<%}%>' +
  '</ol>' +
  '</div>';

const templateEngin = (template, data) => {
  const reg = /<%([^%>]+)?%>/g; // 全局替换 <%%> 正则
  const conditionReg = /(^( )?(if|for|else|switch|case|break|{|}))(.*)?/g; // 判断条件语句的关键字等
  let cursor = 0;
  let code = 'const arr = [];\n';
  let match;
  const add = (str, isJs) => {
    code += str.match(conditionReg)
      ? str + '\n'
      : isJs
      ? 'arr.push(' + str + ');\n'
      : 'arr.push("' + str.replace(/"/g, '\\"') + '");\n';
  };
  while ((match = reg.exec(template))) {
    console.log(match);
    add(template.slice(cursor, match.index));
    add(match[1], true);
    cursor = match.index + match[0].length;
  }
  add(template.slice(cursor));
  code += 'return arr.join("");';
  console.log(code);
  return new Function(code.replace(/\t\r\n/g, '')).apply(data);
};

const str = templateEngin(template, data);
console.log(str);
document.querySelector('body').innerHTML = str;

参考地址

Last Updated: 7/17/2019, 1:52:39 PM