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;