vue vite 环境非构建包编译 React 报错定位

在这里插入图片描述


在这里插入图片描述

1. 背景

众所周知,vite 在构建生态的位置,vue 与之更是密切,主流的 vue 库几乎都与 vite 捆绑。

但有些 UI 库 如 @private/ui 并没进行行编译,而是直接将源码发布到了 npm 中,无法实现兼容化,需要消费方去自行处理库中的环境问题,及额外的编译时间。

基于 vue 官方脚手架创建的项目也是捆绑的 vite,但在使用 @private/ui 组件时,开发环境一直编译报错,无法使用。

还得从 vite 下手,看下为什么它无法编译通过。

2. 问题现场

开发环境报错:

为什么会把 @private/ui 编译成了 React.createElement 去创建元素?

编译环境:

正常。

vite 是有两套构建环境的,这种不一致性很麻烦:

问题就出在开发环境的 esbuild 中。

3. vite optimizeDeps

从样是写 tsx,为什么项目中的可以正常执行,而 @private/ui 中的就编译错误?两者明显不在一个构建过程中。
vite 的 optimizeDeps 也没进行配置,怎么会出现预编译的效果。

debugger 编译过程发现,@private/ui 真被自动添加进去了:

查看自动添加逻辑:

https://github.com/vitejs/vite/blob/main/packages/vite/src/node/optimizer/scan.ts#L526

// bare imports: record and externalize ----------------------------------
build.onResolve({// avoid matching windows volumefilter: /^[\w@][^:]/,},async ({ path: id, importer, pluginData }) => {if (moduleListContains(exclude, id)) {return externalUnlessEntry({ path: id });}if (depImports[id]) {return externalUnlessEntry({ path: id });}const resolved = await resolve(id, importer, {custom: {depScan: { loader: pluginData?.htmlType?.loader },},});if (resolved) {if (shouldExternalizeDep(resolved, id)) {return externalUnlessEntry({ path: id });}if (isInNodeModules(resolved) || include?.includes(id)) {// dependency or forced included, externalize and stop crawlingif (isOptimizable(resolved, config.optimizeDeps)) {depImports[id] = resolved;}return externalUnlessEntry({ path: id });} else if (isScannable(resolved, config.optimizeDeps.extensions)) {const namespace = htmlTypesRE.test(resolved) ? "html" : undefined;// linked package, keep crawlingreturn {path: path.resolve(resolved),namespace,};} else {return externalUnlessEntry({ path: id });}} else {missing[id] = normalizePath(importer);}}
);

可以看到只要是项目源码直接引用的,js 类型的包就会被自动添加进去。

4. 解决

这里要注意所有在预处理过程中的 esbuild 配置,一定要在optimizeDeps.esbuildOptions 中配置,而不是esbuild,两个流程读取的配置不一样,详情看源码。

4.1 解法一:esbuild jsx 重写

esbuild 提供了 jsx 相关的配置重写,可以直接将React.createElement重写为 h
https://www.typescriptlang.org/tsconfig/#jsx
https://www.typescriptlang.org/tsconfig/#jsxFactory
https://www.typescriptlang.org/tsconfig/#jsxFragmentFactory

{jsxFactory: 'h',jsxFragment: 'Fragment'
}

编译后:

// 最初编译结果
return React.createElement(React.Fragment, null, slots.handler && React.createElement(GridItem,{row: props.row,column: "1 / -1",...bindings},slots.handler()
)// 修改后编译结果
return h(Fragment, null, slots.handler && h(GridItem,{row: props.row,column: "1 / -1",...bindings},slots.handler()
)

可以看到正常了,但又报错了:

esbuild 提供了 jsxImportSource 来解决这种问题,但必须符合下面要求:
https://esbuild.github.io/api/#jsx-import-source

import { createElement } from "your-pkg";
import { Fragment, jsx, jsxs } from "your-pkg/jsx-runtime";
import { Fragment, jsxDEV } from "your-pkg/jsx-dev-runtime";

然而 vue 完全没这种包。

esbuild 还有一个 inject的配置:
https://esbuild.github.io/api/#inject

不太好的方式是,直接把 React 定义到全局变量中:

// inject.js
const { h, Fragment } = require("vue");window.React = {createElement: h,Fragment: Fragment,
};// vite.config
inject: ["./inject.js"],

可以正常工作了。

esbuild 提供了另一种方式:

import { h, Fragment } from "vue";export { h as "React.createElement", Fragment as "React.Fragment" };

但报错:

✘ [ERROR] Using a string as a module namespace identifier name is not supported in the configured target environment (“chrome87”, “edge88”, “es2020”, “firefox78”, “safari14” + 2 overrides)

看到 esbuild 的 define 的定义:https://esbuild.github.io/api/#define
在线编译效果:https://esbuild.github.io/try/#YgAwLjE5LjIALS1pbmplY3Q6Li9wcm9jZXNzLWN3ZC1zaGltLmpzIC0tdGFyZ2V0PWVzNiAtLWRlZmluZTpwcm9jZXNzLmN3ZD1wcm9jZXNzQ3dkU2hpbQAAcHJvY2Vzcy1jd2Qtc2hpbS5qcwBleHBvcnQgbGV0IHByb2Nlc3NDd2RTaGltID0gKCkgPT4gJycAZQBlbnRyeS5qcwBjb25zb2xlLmxvZyhwcm9jZXNzLmN3ZCgpKQo

结合起来重写配置:

// inject.js
export { h, Fragment } from "vue";// config
inject: ["./inject.js"],
define: {"React.createElement": "h","React.Fragment": "Fragment",
}

可以正常工作。

4.2 解法二:移除 @private/ui 预编译

更快的方式是把 @private/ui 从预编译中移除,但会增加加载时长。

exclude: ["@private/ui"];

5. 其它

由于没编译,组件库内非 es 的模块还会出问题,还要项目上去做预编译才能正常使用:

export default defineConfig({plugins: [vue(), vueJsx()],optimizeDeps: {include: ["lodash.uniq","lodash.get","lodash.set",// ...],esbuildOptions: {inject: ["./inject.js"],define: {"React.createElement": "h","React.Fragment": "Fragment",},},},esbuild: {},
});

6. 总结

vite 固然好,但多编译环境还是会出现对不齐的问题,一些配置在 vite 官网中也讲的不是很清楚,还是得抠源码看具体实现细节。

另外对于库的开发者来讲,一定要提供编译好后的代码给开发者,包括脚本和样式,默认美好。


微信搜索“好朋友乐平”关注公众号。

github原文地址

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/11536.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

vs-qt中无法加载qsqlite驱动,但是单独新建demo测试却又是正常的。。。

开发环境: Vs2015 + qt5.12 背景: 接手了一个项目,可以编译过去,也可以运行,, 但是登录一直失败,,但是数据库文件也是正常的。。。 最主要的是环境和同事的是一样的,,,但是他那边可以加载成功,我这边不可以。。 后来单独在vs中创建了一个demo,用来测试QSqlData…

Ubuntu虚拟机上推荐一款免费好用的git版本管理工具

工具叫: gitg 软件界面如下: FR:徐海涛(hunkxu)

开眼了,自动化测试还能这样用?

持续集成的自动化测试通常需要将代码、测试用例与持续集成工具进行绑定,以实现自动运行。然而,Apipost的自动化测试功能需要手动操作,并且需要手动查看测试结果。 为了解决这个问题,Apipost推出了持续集成功能,方便同…

【Shell】正则表达式的操作实例

正则表达式是一个描述一组字符串的模式 是由普通字符和元字符组成的字符集,而这个字符集匹配(或指定)一个模式。 正则表达式的操作实例 (一)概述1.定义2.作用3.类型 (二)字符串匹配实例&#xf…

【论文阅读】Machine Learning, Linear Algebra, and More: Is SQL All You Need?

文章目录 摘要一、介绍二、SQL算法原语2.1、Variables2.2、Functions2.3、Conditions2.4、Loops2.5、Errors 三、案例研究3.1、对数据库友好的SQL映射3.2、性能结果 四、结论以及未来工作 摘要 尽管SQL在简单的分析查询中无处不在,但它很少用于更复杂的计算&#xf…

文件流-ASCII文件(中北大学-程序设计基础(2))

目录 题目 源码 结果示例 题目 编写程序实现以下功能:【要求处理ASCII文件】 (1)按职工号由小到大的顺序将5个员工的数据(包括号码、姓名、年龄和工资)输出到磁盘文件中保存; (2&#xff…

自动驾驶决策规划——坐标转换

以下内容来自b站up主忠厚老实的老王,视频链接:自动驾驶决策规划算法序章 总纲与大致目录_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV1hP4y1p7es/?spm_id_from333.999.0.0&vd_sourced36e625f376908cfa88ef5ecf2fb0ed8侵删。 决策规划算法…

自动故障转移

这里询问,你的插槽是从哪里移动过来的? all:代表全部,也就是三个节点各转移一部分具体的id:目标节点的iddone:没有了 这里我们要从7001获取,因此填写7001的id: 填完后,…

力扣75. 颜色分类

Problem: 75. 颜色分类 文章目录 题目描述思路及解法复杂度Code 题目描述 思路及解法 由于题目只提供0,1,2分别代表颜色红、白、蓝,并按此排序,那么我们可以遍历两次数组,第一次将0,全部放到数组前面一部分…

移动 App 入侵与逆向破解技术-iOS 篇

如果您有耐心看完这篇文章,您将懂得如何着手进行app的分析、追踪、注入等实用的破解技术,另外,通过“入侵”,将帮助您理解如何规避常见的安全漏洞,文章大纲: 简单介绍ios二进制文件结构与入侵的原理介绍入…

软考考试需要达到多少分才能及格?

当然是45分!45分!45分!而且是各科45! 初级和中级考两科 综合知识考试时长为150分钟,笔试,选择题(上午9:00-11:30) 案例分析考试时长为90分钟,笔试,问答题&…

计算机毕业设计 | vue+springboot图书借阅 书籍管理系统(附源码)

1. 开发目的 实现图书的智能化、信息化和简单化;实现图书信息的增加、删除、修改、查找、借阅、还书、收藏的显示操作及实时数据库的提交和更改和对普通用户的增、删、改、查;提高图书管理员工作信息报送及反馈的工作效率,减轻管理员的劳动负…

设计模式Java实现-迭代器模式

✨这里是第七人格的博客✨小七,欢迎您的到来~✨ 🍅系列专栏:设计模式🍅 ✈️本篇内容: 迭代器模式✈️ 🍱 本篇收录完整代码地址:https://gitee.com/diqirenge/design-pattern 🍱 楔子 很久…

苍穹外卖Day06笔记(复习了jwt的加密解密和传递)

疯玩了一个月,效率好低,今天开始捡起来苍穹外卖~ 1. 为什么不需要单独引入HttpClient的dependency? 因为我们在sky-common的pom.xml中已经引入了aliyun-sdk-oss的依赖,而这个依赖低层就引入了httpclinet的依赖,根据依…

JavaEE技术之SpringCloud(Nacos注册中心、Nacos配置中心、Sentinel实现熔断与限流)

文章目录 SpringCloud Alibaba1、简介1.1 背景1.2 Nacos主要功能1.3 Nacos和SpringBoot、SpringCloud版本选择 2、Nacos注册中心2.1 案例准备2.2 Nacos注册中心下载启动2.2.1 下载2.2.2 解压启动2.2.3 nacos-server访问测试 2.3 nacos注册中心客户端整合2.3.1 订单服务整合naco…

【UE】仿原神实现无限道路延伸的开场效果

目录 效果 步骤 一、无限生成砖块 二、制作门 三、停止移动并生成门 四、进入门 效果 步骤 一、无限生成砖块 1. 新建一个Basic关卡,再新建一个Pawn类,这里命名为“BP_MyPawn” 打开“BP_MyPawn”,添加一个胶囊体碰撞组件和一个摄像…

工器具管理(基于若依)

文章目录 前言一、工器具管理项目总览 二、入库功能1. 前端1.1 界面展示1.2 具体操作实现1.3 js文件 2. 后端2.1 工器具信息回显2.2 工器具入库 三、领用功能1. 前端1.1 界面展示1.2 具体实现操作1.3 js文件 2. 后端2.1 工器具信息回显2.2 工器具领用 遇到的问题1. 同一页面展示…

基于springboot+vue的自习室管理和预约系统(全套)

一、系统架构 前端:vue | element-ui | html 后端:springboot | mybatis-plus 环境:jdk1.8 | mysql | maven | nodejs 二、代码及数据库 三、功能介绍 01. web端-首页1 02. web端-首页2 03. web端-注册 04. web端-登录 05. w…

Apollo9.0 Control模块算法源码学习

参考资料 Apollo控制算法_哔哩哔哩_bilibili

Python自动化测试 | 如何使用Robot Framework进行自动化测试?

你还在手动测试?不妨了解一下更高效、准确且简单的测试方法——使用Python的Robot Framework进行自动化测试。 什么是Robot Framework? Robot Framework是一款开源的Python自动化测试框架,它基于关键字驱动的思想,具有易读、易扩…