一个代码报错的溯源过程

前言

网络上大家都调侃程序员不看warning,只看error,确实程序和人有一个能跑就行,一个小小的warning,还想参加蟠桃大会... 跑题了 最近在使用一个新的前端框架,发现某些情况下会出现一些意料之外的错误,整个排查过程有些波折,所以想记录一下~

问题的出现

wxt 一个快速开发浏览器插件的框架,其借助了vite构建能力,实现插件开发过程的热更新,对插件开发体验挺好的,支持任意UI描述框架。于是在尝试过程中,我使用了Vue来构建界面,但在content-script的开发过程中,偶然发现更新了script的内容之后,出现了两个问题:

  1. 浏览器热更新失效了,并且console打印了vue的warn。
  1. vite进程中断了,终端控制台报错:
vbnet 复制代码
 ERROR  "default" is not exported by "entrypoints/content/App.vue?vue&type=script&setup=true&lang.ts", imported by "entrypoints/content/App.vue".

问题的排查

查了一下框架github仓库的issue,有人提过content-script的UI无法渲染问题,但无人给出解决方案。那么有两个选择:自己排查 or 放弃。牛马人,当然要试一试排查。

这看似是两个不相干的问题,一个是丢失template、另外一个是没有默认导出属性;但Vue单文件都是由compiler-sfc解析器解析成js的,我们可以暂时先把它看成是一个问题:compiler-sfc解析异常。

排查目标确定

既然假设compiler-sfc解析异常,那就先从compiler-sfc解析产物入手,看它解析之后的js长啥样。

我改了handleClick方法的代码,App.vue文件解析之后,setup方法返回了__returned__变量,Hmm...render函数呢?不见了!难怪vue提示没有模板或者渲染函数。

既然产物有问题,那就对比一下正常情况下的产物应该是什么样的。

显然正常情况下单文件解析器是有返回渲染函数的,哪是哪里出问题了?

对比两者,可以看出异常产物有__returned__变量,那就直接进compiler-sfc看它是怎么处理的。

不是内联模板的情况下,就插入这段代码;没了,线索中断了吗?下一步?

换个方向思考,谁调用了compiler-sfc?在vite解析vue单文件中,使用了@vitejs/plugin-vue插件来解析。好!问题换成:难道@vitejs/plugin-vue在调用compiler-sfc的时候设置了这个inlineTemplate属性?

排查的深入

在查看@vitejs/plugin-vue源码之后,发现是在生产模式下才有可能把它设置成true,没了,线索又断了?

并没有,它引入了一个新方向,热更新(hot updated),并且我知道产物文件是打包生成的,那肯定涉及生产模式。好的,那么问题换成:生产模式与开发模式下,@vitejs/plugin-vue插件做了啥?

错误根源的发现

@vitejs/plugin-vue是在handleHotUpdate和genScriptCode调用resolveScript的,显然一个是热更新用的,一个是打包编译用的。而resolveScript代码里面有两个前置return的判断条件:

  1. 没有script
  2. 存在缓存

在resolveScript打了个日志,再跑一下复现流程,发现修改App.vue文件之后,执行了两次resolveScript第一次由热更新触发,第二次由build构建触发。

(忘记讲了,wxt框架针对插件的content-script脚本,是使用build构建之后,再插入到页面中,并且wxt启动的时候,是background.js content-script.js popup 三种插件模式都使用)

第二次执行resolveScript的时候,没调用compiler-sfc的compileScript

查看代码上下文之后,发现热更新的时候,会设置缓存(热更新缓存用于只替换修改的内容,不会全量替换)

问题就出在了缓存这里,build编译代码的时候,拿的是热更新的代码,导致APP.vue解析成js丢失了一些数据。 那为什么vite的build和server会共用@vitejs/plugin-vue的上下文呢?难道vite设计如此?进vite官网看看!

原来,vite的createServer和build共用同个vite进程,那@vitejs/plugin-vue也只会在vite.config.ts中执行那一次,所以@vitejs/plugin-vue的cached也就被api.createServer和api.build 共用了。

所以,要解决此问题,只能从根源上解决,那就是将wxt的build模式,由node子进程去执行!

意外的解决方案

在最初看到vue单文件解析器的inlineTemplate属性之后,我在wxt框架源码中搜索inline/template/inlineTemplate,发现它默认会给vite的build.sourceMap属性设置成内联inline。程序员的下意识思路就是,试试改成其他的会怎么样?

然后我将其改为false,开发插件要个嘚sourceMap啊...

结果,它就正常工作了!!! 到现在我还没找到sourceMap和解析器inlineTemplate的关联...

后续

给它issue回复了这个临时解决方案~ wxt-issue#538

写在最后

debug源于好奇心,愿好奇心不减,代码永存,脑机永生。

相关推荐
多客:duokelijie2 小时前
代练订单系统选型思路:跨品类游戏接入、订单分层管理、自动结算逻辑拆解!
数据库·mysql·微信小程序·前端框架·uni-app·php·持续部署
ruanno_top5 小时前
倒计时该用 deadline 还是每秒减一?我把两种写法都跑了一遍
vue.js
徐小夕9 小时前
下载量超 30 万的开源智能体 Prime Agent:它把自己从 TypeScript 重写成了 Rust
前端·vue.js·github
toooooop810 小时前
CRMEB 故障排查笔记:安全网关只放行 GET/POST 怎么办
前端·javascript·vue.js·thinkphp6·crmeb
软件技术NINI10 小时前
基于SpringBoot和Vue的项目评审系统
javascript·vue.js·spring boot·后端·spring·毕业设计·毕设
FRing031 天前
vue2干货总结3(computed计算属性、watch监听器)
开发语言·前端·vue.js·vue2
Java小卷1 天前
JuanForm vs FormCreate!Vue3 表单引擎联动配置 PK!
vue.js·低代码
Highcharts.js1 天前
React 中原生支持使用 JSX 组件配置自定义表格 Highcharts Grid
前端·javascript·react.js·前端框架·jsx·highcharts·自定义表格
小郎君6581 天前
Vue 多标签页的加载态设计:为什么我没有用 Suspense 实现 PageLoading
vue.js·算法
yilaisai1 天前
前端开发遇到的一些细节问题整理
vue.js