GitHubPaperStrike/Pjax,重构自MoOx/pjax。本文介绍顺序执行脚本、破解Promise、BabelPolyfills三个部分,阅读时间约30分钟。首次发布于https://sliphua.work/pjax-in-2021/。使用React和Vue等现代框架的前端开发不需要Pjax,但是Pjax在许多使用Hexo和Hugo等工具生成的静态博客中仍然存在并且很好,可以提供越来越流畅的用户体验。Pjax最初被称为pushState+Ajax。前者是指使用浏览器的HistoryAPI来更新浏览记录。后者的全称是AsynchronousJavaScriptandXML,涵盖了JS中用于发送HTTP请求的一系列技术。MDN文档中还有一个pure-Ajax的概念,涉及到几乎相同的技术和目标。通过JS动态获取和更新页面,提供流畅快速的切换过程,是Pjax作者和网站开发者的初衷。但实际上,Pjax的核心不仅仅是HistoryAPI和Ajax。除了显示内容,浏览器何时以及如何切换页面无法通过pushState完全模拟。顺序脚本执行这个痛苦的部分开始于innerHTML不执行内部脚本。脚本元素的来源有两种,HTML解析器解析和JS生成;有两个主要阶段,准备阶段和执行阶段。执行阶段只能由准备阶段或解析器触发,准备阶段只会在以下三个时刻触发。HTML解析器解析并生成此脚本元素。由JS生成并注入到文档中。由JS生成并注入文档,插入子节点或添加src属性。当使用innerHTML等API赋值时,内部会使用HTML解析器在禁用脚本的独立文档环境中解析字符串。在这个独立的文档环境中,script元素会经历一个准备阶段但不会被执行,字符串被解析后,生成的节点被转移到分配的元素中。由于内部脚本元素不是由JS生成的,因此移动到当前文档不会触发准备阶段,更不用说进一步执行了。因此,在使用innerHTML、outerHTML或DOMParser+replaceWith等方法更新页面的部分内容后,需要对script元素进行特殊处理,重新触发准备阶段。很容易想象,在JS中使用cloneNode等API来复制和替换触发器,但这还有一个陷阱。在脚本准备阶段,在确认每个HTML属性都符合要求后,脚本会被标记为“已经启动”。prepare阶段的第一步是当有这个flag时退出,复制的script元素会保留这个flag。脚本元素有一个标志,指示它是否已“已启动”。最初,脚本元素必须取消设置此标志(脚本块在创建时不会“已经启动”)。如果在被克隆的元素上设置了脚本元素的克隆步骤,则必须在副本上设置“已经开始”标志。——已经开始准备脚本,用户代理必须执行以下操作:如果脚本元素被标记为“已经开始”,然后返回。Thescriptisnotexecuted....(检查并确定脚本的类型。)设置元素的“已启动”标志....——准备脚本因此,要插入一个执行脚本元素,只有当前文档的Methods这样作为createElement构造全新的脚本元素,逐个属性复制。构建一个evalScript函数作为示例:constevalScript=(oldScript)=>{constnewScript=document.createElement('script');//克隆属性和内部文本。oldScript.getAttributeNames().forEach((name)=>{newScript.setAttribute(name,oldScript.getAttribute(name));});newScript.text=oldScript.text;oldScript.replaceWith(newScript);};Sequentialpartialupdatescriptelementexecutionproblem在Pjax早年就已经解决了,上面的文字更多的是介绍了本节中顺序问题的基本概念。如何让页面刷新部分新增脚本的执行顺序符合初始页面加载的脚本执行顺序规范是讨论的重点。当JS动态连续插入多个可执行的console.log(1);`;//日志13524//或13542[...document.body.children].forEach(evalScript);然后参考脚本执行规范。根据规范,可执行的具有compliant属性值的
