将带数据绑定的字符串替换
@param {Object} from
@param {string} str     原始字符串
@param {string} prefix  前缀
@param {object} ignores 忽略的字符串map
@returns {string}
function replaceString(from, str, prefix, ignores) {
  // 替换字符串中 {{}} 包含的表达式

  // 获取类似 a.b.c 表达式中第一个有效变量名 a
  function getFirstWord(word) {
    return word.match(/[_a-z][\w\d]*/i)[0];
  }

  // 检查类似 a.b.c 格式表达式是否忽略绑定
  function shouldIgnore(word, matchs, n) {
    if (word[0] === '"' || word[0] === "'" || /^\d+$/.test(word)) return true;
    let w = getFirstWord(word);
    if (ignores.hasOwnProperty(w) || (matchs && inText(matchs, n))) {
      return true;
    }
    if (['state', 'props'].indexOf(w) < 0) {
      console.error(`'${from.fromSrc}' 中发现无效变量引用 '${word}'，XML模板中只能引用组件'props'和'state'中的数据。`.red);
    }

    return false;
  }

  if (prefix) {
    prefix += '.';
  } else {
    prefix = '';
  }
  return str.replace(/\{\{([^}]+)\}\}/ig, function (matchs, words) {
    // matchs 是{{xxxxx}}格式的字符串
    // words  是{{}}中间的表达式

    // ...foo
    if (/^\s*\.\.\.[\w_][\w\d\-_.\[\]]*\s*$/.test(words)) {
      let word = words.match(/\s*\.\.\.([\w_][\w\d\-_.\[\]]*)/)[1].trim();
      if (shouldIgnore(word)) {
        return matchs;
      }
      return `{{...${prefix}${word}}}`;
    }

    let isArray = /{{\s*\[/.test(matchs);
    if (!isArray) {
      //支持对象简写
      let arrays = words.split(',');
      if (arrays.length > 1) {
        let isObject = true;
        let props = arrays.map(function (str) {
          if (!isObject) return;
          // str 为对象中的一个属性， 可能为 a:b / a / ...a / ...a.b
          str = str.trim();

          let arr = str.split(':');
          if (arr.length === 1) {
            // 如果属性表达式中不包含冒号

            // 如果为简写属性表达式，例如 {foo}
            if (/^[a-z_][\w\d]*$/i.test(str)) {
              if (ignores[str]) {
                return str + ':' + str;
              }
              return str + ':' + prefix + str;
            }

            // 属性展开表达式 ...foo
            if (/^\.{3}[a-z_][\w\d.\[\]]*$/i.test(str)) {
              let word = str.substr(3);
              if (shouldIgnore(word)) {
                return str;
              }
              return '...' + prefix + word;
            }

            // 判定 ${matchs} 不为对象表达式
            isObject = false;
            return;
          }

          // 存在冒号的对象属性表达式

          let word = arr[1].trim();
          // foo:2.3
          if (/^[\d.]+$/.test(word)) {
            return arr[0] + ':' + word;
          }

          // foo:bar
          // 'foo':bar
          if (shouldIgnore(word)) {
            return str;
          }

          // foo:bar
          // 'foo':bar
          // foo
          return arr[0] + ':' + prefix + word;
        });

        //console.log('isObject', isObject);
        if (isObject) {
          return '{{' + props.join(',') + '}}';
        }
      }
    }


    return matchs.replace(/[^\.\w'"]([a-z_\$][\w\d\._\$]*)/ig, function (match, word, n) {
      if (shouldIgnore(word, matchs, n)) {
        return match;
      }
      return match[0] + prefix + word;
    });
  });
}