Published on
976

js动态执行html内容的解决思路

Authors
  • avatar
    Name
    小辉辉
    Twitter

前言

前端开发过程中,一般的html内容都是固定在开发阶段就写好的,但是有些业务场景可能会遇到需要动态插入html内容,比较常见的一个就是微前端的开发,除此之外,有些系统接收外部输入内容,这些内容为了考虑到执行方便,需要往当前页面注入一些css样式或者js第三方模块等,这要是放到最初的前后端融合在一起的单体应用里实现起来就很简单,但是对于目前前后端分离的场景就需要借助js的一些原生方法还有DOM对外开放的一些api了。

这篇文章会从两个方面入手分别讲解下对应的解决思路,一个是提取,另外一个是还原(执行)。

提取方法

拿到一段html后,首先要做的就是得知道html里面有哪些标签,针对简单的内容标签可以直接还原,但是一些特殊的标签,例如link,script,style就需要单独处理了,所以提取也主要是针对这个情况来讲讲解决方案。

  1. 直接创建一个元素,将该元素的innerHTML属性设置为动态html即可,最后遍历元素的children即可
const xxx = ``; // 动态html的内容
const el = document.createElement('div');
el.innerHTML = xxx;
for (const e of el.children) {
  // 处理不同的e元素
}
  1. 第二种实现方案相对更加直接点,直接用正则表达式匹配实现,方案1背后的实现应该也依赖这个

这块的代码就不贴了,核心思想就是使用字符的replace方法,分别匹配上面提到的三个标签,对结果做相关转换。

还原方法

提取结束后,接下来就是还原操作,这边也针对三种不同情况单独说下。

  1. 普通内容标签元素

这种就是常规方案,直接用提取时提到的innerHTML属性,或者时单独appendChild即可

  1. 样式元素

这个分内链和外部样式

// 外部样式
const linkEl = document.createElement('link');
linkEl.setAttribute('rel', 'stylesheet'); // 这步很关键
linkEl.setAttribute('href', 'xxxxxx');
document.head.appendChild(linkEl);
// 内链样式
const styleText = ``;
const styleEl = document.createElement('style');
styleEl.textContent = styleText;
document.head.appendChild(styleEl);
  1. 脚本元素

这里也分外部链接和内链脚本

// 外部链接
const scriptEl = document.createElement('script');
scriptEl.setAttribute('src', 'xxxxx');
scriptEl.onload = () => {
  // 如果存在多个脚本,需要通过该方法确保按顺序执行
}
document.head.appendChild(scriptEl);

需要注意的是,通过该方案实现外部链接加载时,需确保脚本之间没有上下依赖关系,如果有依赖关系,需使用onload来确保该脚本加载完毕后再加载下一个。

// 内链
const scriptEl = document.createElement('script');
scriptEl.textContent = 'xxxx'; // 实际的脚本内容
document.head.appendChild(scriptEl);

另外讲下内链的另外一种实现方案,相对要更加简单,那就是借助eval函数,直接传入脚本内容即可。联想到这里,其实外部脚本还有种解决方案,那就是批量获取到脚本的返回内容(可以接触map生成多个fetch请求,最后借助Promisea.all的功能),最后依次调用eval方法即可。但是目前我们也知道一般情况下不推荐使用eval方法

总结

动态执行html内容有个很重要的前提是必须确保内容是安全的,否则很容易执行非法代码对用户的访问安全带来影响,一般情况下推荐使用创建对应元素标签,设置其相关的属性来执行html内容,对于可能需要动态修改代码内容的场景,这时候可以考虑借助于eval方法来完成。