我们在现代框架中会经常遇到模版插值的操作,究竟是如何实现的我们来探索一波。
先来一个比较简单的插值替换的例子,我们需要将html文件中的{{}}
中的内容,从变量中获取,替换后返回
工具:nodeJS
用例:
思路:fs
模块读取文件,然后用正则表达式替换{{}}
,通过函数传参的方式,将插值替换
const fs = require('fs')
const path = require('path')
const { promisify } = require('util')
const fsReadFile = promisify(fs.readFile)
/**
*
* @param {*} path 文件的路径
* @param {*} renderData 将要替换插值的数据集
* @param {*} cb 外部调用
*/
const renderFile = async (path, renderData, cb) => {
try {
let html = await fsReadFile(path, 'utf8')
html = html.replace(/\{\{([^\}]+)\}\}/g, (match, target) => {
return renderData[target]
})
cb(html)
} catch (error) {
console.warn(error)
}
}
const file = path.resolve(__dirname, 'template.html')
const useCases = { name: 'Johnny Ness', age: 35 }
renderFile(file, useCases, res => {
console.log(res)
})
//替换成功
`<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
Johnny Ness 35
</body>
</html>`
下面我们换一个复杂一点的
用例:
思路:**
这里我们考虑将整个文本作为一个字符串的形式返回,所有的操作都基于字符串的拼接,然后通过外部的函数调用传参的思路,总体来说需要几个步骤
- 替换插值
- 将字符串用
with(renderData){}
包裹,这里的目的是为了让arr
的this
指向传入的renderData
通过这个方式让改变arr
的作用域 - 最后通过
new Function(args)
的方式,接受外部传入的data
然后返回我们拼接完成的字符串
几个重点:
new Function
创建匿名函数,可以通过toString()
来查看创建的函数是否正确with(expression)
将会使得作用域变更到expression
上,这里相当于使得arr
变成了data.arr
- 总体上使用的都是操作字符串的思路
```javascript
const fs = require(‘fs’)
const path = require(‘path’)
const { promisify } = require(‘util’)
const fsReadFile = promisify(fs.readFile)
/*
- @param {*} path 文件的路径
- @param {*} renderData 将要替换插值的数据集
- @param {} cb 外部调用
/
const renderFile = async (path, renderData, cb) => {
const str =
let str = '';\r\n
const head = ‘with(data){\r\nstr += \r\n' const tail = '
}\r\nreturn str’ try { let html = await fsReadFile(path, ‘utf8’) // forEach中的调用我们转化成 ${xxx}的形式,在运行时解决 html = html.replace(/{{([^{]+)}}/g, (match, target) => { return ‘${‘ + target + ‘}’ }) //替换外层的插值,保留内部的插值拼接到字符串,目的是让函数执行并返回 html = html.replace(/{\%([^\%]+)\%}/g, (match, target) => { return ‘\r\n' + target + '\r\nstr +=
‘ }) // 最后拼接完成的字符串 const ret = str + head + html + tail // 通过new Function 创建一个匿名函数 const fn = new Function(‘data’, ret) return cb(fn(renderData)) } catch (error) { console.warn(error) } } const file = path.resolve(__dirname, ‘data.html’) const useCases = { arr: [{ name: ‘Johnny’ }, { name: ‘Mickey’ }, { name: ‘Amy’ }], } renderFile(file, useCases, res => { console.log(res) })
// //上面的用例 将返回 `<!DOCTYPE html>
<li>Johnny</li>
<li>Mickey</li>
<li>Amy</li>
` ```