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

news2024/11/24 20:25:46

在这里插入图片描述


在这里插入图片描述

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 volume
    filter: /^[\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 crawling
        if (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 crawling
        return {
          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.coloradmin.cn/o/1672063.html

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

相关文章

LabVIEW学习记录3 - 自定义函数

LabVIEW学习记录3 - 自定义函数 一、LabVIEW学习记录二、自定义函数及函数调用 一、LabVIEW学习记录 【labVIEW】学习记录LabVIEW学习记录2 - MySQL数据库连接与操作LabVIEW学习记录 - 实时显示时间LabVIEW学习记录4-局部变量、全局变量、共享变量 二、自定义函数及函数调用 …

【vue2项目经验总结:部署到服务器之后出现所有数据渲染失败的问题】

原因是因为在没部署到服务器之前前端为了解决跨域问题使用了代理,但是在项目部署到服务器之后,前端通常不再需要使用代理,因为代理的作用是在开发过程中帮助前端应用程序与后端服务进行通信,解决跨域访问等问题。在开发阶段&#…

海洋环境保护论文阅读记录

海洋环境保护 论文1:Critical role of wave–seabed interactions in the extensive erosion of Yellow River estuarine sediments 波浪-海床相互作用在黄河河口广泛侵中的关键作用 estuatine 河口的,港湾的 erodibility侵蚀度 sediment erodibility …

Springboot HelloWorld

新建一个maven工程 引入依赖项 <modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.2.11.RELEASE</version><…

LLM实战:LLM微调加速神器-Unsloth + LLama3

1. 背景 五一结束后&#xff0c;本qiang~又投入了LLM的技术海洋中&#xff0c;本期将给大家带来LLM微调神器&#xff1a;Unsloth。 正如Unsloth官方的对外宣贯&#xff1a;Easily finetune & train LLMs; Get faster with unsloth。微调训练LLM&#xff0c;可以显著提升速…

(项目)-KDE巡检报告(模板

金山云于12月26日对建行共计【30】个KDE集群,合计【198】台服务器进行了巡检服务。共发现系统风险【135】条,服务风险【1912】条,服务配置风险【368】条。 一、系统风险 1、风险分析(图片+描述) (1)磁盘使用率高 问题描述多个集群的多台服务器磁盘使用率较高,远超过…

UniAD大模型开路,智能车驶入AGI时代

作者 |老缅 编辑 |德新 在刚刚结束不久的北京车展上&#xff0c;除一众明星车型亮相&#xff0c;供应链企业也开始大秀肌肉&#xff0c;其中尤其以端到端大模型为代表&#xff0c;焕新一代的智驾技术栈掀起了新一轮热潮。 作为首个提出感知决策一体化自动驾驶通用模型的公司&…

计算机网络(第八版 谢希仁 编著) 期末复习大纲

一.每章总结 第一章&#xff1a;分组交换&#xff0c;计网定义、范围划分&#xff0c;性能指标&#xff0c;五层体系结构&#xff0c;TCP/IP体系结构 第二章&#xff1a;物理层&#xff0c;码元&#xff0c;基带调制(数字信号->数字信号&#xff0c;也叫编码)&#xff0c;带…

mysql group by 细节介绍

mysql中group by的用法是配合聚合函数&#xff0c;利用分组信息进行统计&#xff0c;语句如“select name,sum(id) from test group by name,number”。 先来看下表1&#xff0c;表名为test&#xff1a; 执行如下SQL语句&#xff1a; SELECT name FROM test GROUP BY name 你…

专业音频修复软件:iZotope RX 11 for Mac 激活版

iZotope RX 专为满足后期制作专业人士的苛刻需求而设计的一款专业音频修复软件。iZotope RX 10添加了新的特性和功能&#xff0c;以解决当今后期项目中存在的一些最常见的修复问题&#xff0c;使其成为音频后期制作的最终选择。虽然包含许多其他新功能&#xff0c;但这里是新的…

Postman基础功能-断言与日志

若能脱颖而出&#xff0c;何必苦苦融入。大家好&#xff0c;在 API 测试的领域中&#xff0c;Postman 是一款极为强大且广泛使用的工具。其中&#xff0c;断言和日志调试功能扮演着至关重要的角色。 一、介绍 断言允许我们在测试过程中验证 API 的响应是否符合预期。通过设定各…

数字人解决方案——AniTalker声音驱动肖像生成生动多样的头部说话视频算法解析

1.概述 AniTalker是一款先进的AI驱动的动画生成工具&#xff0c;它超越了简单的嘴唇同步技术&#xff0c;能够精准捕捉并再现人物的面部表情、头部动作以及其他非言语的微妙动态。这不仅意味着AniTalker能够生成嘴型精准同步的视频&#xff0c;更重要的是&#xff0c;它还能够…

使用LangChain和Neo4j快速创建RAG应用

大家好&#xff0c;Neo4j 通过集成原生的向量搜索功能&#xff0c;增强了其对检索增强生成&#xff08;RAG&#xff09;应用的支持&#xff0c;这标志着一个重要的里程碑。这项新功能通过向量索引搜索处理非结构化文本&#xff0c;增强了 Neo4j 在存储和分析结构化数据方面的现…

1-3ARM_GD32点亮LED灯

简介&#xff1a; 最多可支持 112 个通用 I/O 引脚(GPIO)&#xff0c;分别为 PA0 ~ PA15&#xff0c;PB0 ~ PB15&#xff0c;PC0 ~ PC15&#xff0c;PD0 ~ PD15&#xff0c;PE0 ~ PE15&#xff0c;PF0 ~ PF15 和 PG0 ~ PG15&#xff0c;各片上设备用其来实现逻辑输入/输出功能。…

基于SpringBoot+微信小程序的订餐(点餐)配送系统设计与实现+毕业论文(12000字)

系统介绍 本微信小程序在线订餐系统管理员功能可以修改个人中心&#xff0c;用户管理&#xff0c;菜品分类管理&#xff0c;菜品信息管理&#xff0c;订单信息管理&#xff0c;取消订单管理&#xff0c;订单配送管理&#xff0c;菜品评价管理以及系统管理。微信小程序用户可以…

【玄机平台】应急响应

前言&#xff1a; 感谢玄机平台靶机的提供&#xff0c;让我学到了不少东西 平台题解 &#xff1a; 第一章 应急响应-webshell查杀 1.黑客webshell里面的flag flag{xxxxx-xxxx-xxxx-xxxx-xxxx} ssh连接 下载/var/www/html源码&#xff08;finsehll连直接下&#xff09;压缩丢…

日志的基本用法

目标 1. 掌握如何设置日志级别 2. 掌握如何设置日志格式 3. 掌握如何将日志信息输出到文件中 1. logging模块 Python中有一个标准库模块logging可以直接记录日志 1.1 基本用法 import logging logging.debug("这是一条调试信息") logging.info("这是一条…

【C++杂货铺】红黑树

目录 &#x1f308;前言&#x1f308; &#x1f4c1; 红黑树的概念 &#x1f4c1; 红黑树的性质 &#x1f4c1; 红黑树节点的定义 &#x1f4c1; 红黑树的插入操作 &#x1f4c1; 红黑树和AVL树的比较 &#x1f4c1; 全代码展示 &#x1f4c1; 总结 &#x1f308;前言…

C#【进阶】常用泛型数据结构类

常用泛型数据结构类 文章目录 常用泛型数据结构类1、List1、List的本质2、声明3、增删查改4、遍历思考 存储基类类型列表 2、Dictionary1、Dictionary的本质2、声明3、增删查改4、遍历思考1 数字对应的大写思考 2 字母出现的次数 3、顺序存储和链式存储1、数据结构2、线性表3、…

前端铺子-NodeJS后端:基于Node.js构建高效后端服务的探索与实践

一、引言 随着前端技术的快速发展&#xff0c;越来越多的开发者开始关注前后端分离的开发模式。前端铺子作为一个旨在服务前端开发者的开源项目&#xff0c;近期推出了基于Node.js的后端系统。该系统通过整合Node.js、Nodemon和MySQL等技术&#xff0c;为前端开发者提供了一个…