怎么将文本{{msg}}正确编译成"hello"

195 阅读2分钟

使用vue,都知道{{msg}},能变成data.msg相对应的值hello

此文目标,实现这种大括号的文本编译。简单粗暴易理解~

{msg} => 'hello'

这里先不关注dom之类的事情,只专注于其中的逻辑。

首先看看以下代码,想想compileText怎么实现

let data = {
  msg:'hello'
}

let text = '{{msg}} world'

function compileText(data,text){
 // 想想怎么实现
}

text = compileText(data,text)
// 想输出 => hello world
console.log(text)

其实这里的逻辑并不难,无外乎先找到{{msg}},然后将整那个字符串替换成data.msg的值。


function compileText(data, text) {
  // 匹配正则
  let reg = /\{\{(.+)\}\}/;
  let newText = text.replace(reg, (...args) => {
    // 拿到msg
    let expr = args[1];
    // data里面msg属性的值
    let value = data[expr];
    return value;
  });
  // 返回替换完的新字符串
  return newText;
}

但是data里可能也有对象,如{msg:'hello',person:{name:'hua'}}

text可能就变成{{person.name}} 喜欢西湖

{person.name} => 'hua'

其实就是在compileText里拿到value值的时候,多些操作。

思考下面的getValue怎么实现

let data = { person: { name: "hua" } };
let expr = "person.name";
function getValue(data, expr) {
  // 想想这里怎么写
}
// 想输出 => hua
console.log(getValue(data, expr));

这里其实就是一层层往下返回,为了写成公用的,会考虑很多层的情况。 理解难点是reduce,如果不明白,可以看入门怎么使用reduce

function getValue(data, expr) {
  // 先将每个key分隔开,放进数组里
  let arr = expr.split(".");
  // 下面就是reduce的使用了,写出init和fn,就基本ok了
  let init = data;
  let fn = (acc, cur) => acc[cur];
  let value = arr.reduce(fn, init);
  return value;
}

然后升级下compileText

function compileText(data, text) {
  // 匹配正则
  let reg = /\{\{(.+)\}\}/;
  let newText = text.replace(reg, (...args) => {
    // 拿到msg
    let expr = args[1];
    // 就改了这里,就能支持多重key
    let value = getValue(data, expr);
    return value;
  });
  // 返回替换完的新字符串
  return newText;
}

// demo演示
let data = {
  person:{name:'hua'}
}
let text = '{{person.name}} 喜欢西湖'

text = compileText(data,text)
// hua 喜欢西湖
console.log(text)