我们在现代框架中会经常遇到模版插值的操作,究竟是如何实现的我们来探索一波。
    先来一个比较简单的插值替换的例子,我们需要将html文件中的{{}}中的内容,从变量中获取,替换后返回
    工具:
    nodeJS
    用例:
    demo.png
    思路:
    fs模块读取文件,然后用正则表达式替换{{}},通过函数传参的方式,将插值替换

    1. const fs = require('fs')
    2. const path = require('path')
    3. const { promisify } = require('util')
    4. const fsReadFile = promisify(fs.readFile)
    5. /**
    6. *
    7. * @param {*} path 文件的路径
    8. * @param {*} renderData 将要替换插值的数据集
    9. * @param {*} cb 外部调用
    10. */
    11. const renderFile = async (path, renderData, cb) => {
    12. try {
    13. let html = await fsReadFile(path, 'utf8')
    14. html = html.replace(/\{\{([^\}]+)\}\}/g, (match, target) => {
    15. return renderData[target]
    16. })
    17. cb(html)
    18. } catch (error) {
    19. console.warn(error)
    20. }
    21. }
    22. const file = path.resolve(__dirname, 'template.html')
    23. const useCases = { name: 'Johnny Ness', age: 35 }
    24. renderFile(file, useCases, res => {
    25. console.log(res)
    26. })
    27. //替换成功
    28. `<!DOCTYPE html>
    29. <html lang="en">
    30. <head>
    31. <meta charset="UTF-8" />
    32. <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    33. <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    34. <title>Document</title>
    35. </head>
    36. <body>
    37. Johnny Ness 35
    38. </body>
    39. </html>`

    下面我们换一个复杂一点的
    用例:
    complex.png
    思路:**
    这里我们考虑将整个文本作为一个字符串的形式返回,所有的操作都基于字符串的拼接,然后通过外部的函数调用传参的思路,总体来说需要几个步骤

    1. 替换插值
    2. 将字符串用with(renderData){}包裹,这里的目的是为了让arrthis指向传入的renderData通过这个方式让改变arr的作用域
    3. 最后通过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>

    1. <li>Johnny</li>
    2. <li>Mickey</li>
    3. <li>Amy</li>

    ` ```