vite & react18踩坑记

921 阅读6分钟

断更好几个月啦,主要原因是工作过于繁忙了。

不过这期间也有在好好努力噢~

本文记录了从webpack到vite, 从react17到react18,从http到https展开项目重构的过程中遇到的一些问题及解决方案,希望对你会有所帮助~~~

1. websocket 连接,http 协议下用 ws, https 协议下必须要使用 wss

问题:

image.png

解决方案:

https 相当于使用 http+ssl 认证,使用 https 时 websocket 访问(比如建立链接时)必须要使用 wss。

详细解释:

WebSocket 协议有两个主要版本:“ws”和“wss”。"ws"表示非加密的 WebSocket 连接,“wss"表示加密的它的版本,类似于 HTTP 和 HTTPS 的关系。

“ws”通过 http 传输,"wss"通过 https 传输。在 Https 请求中,每个数据传输包都被加密,在浏览器与服务器角度,安全性要显著高于 Http。

为了保证在 HTTPS 网站下的数据安全与一致性,必须使用“wss"。如果在 HTTPS 网站下使用“ws”,浏览器会报错,因为它将“ws"视为不安全的链接,因此它会阻止建立此类连接。


2. 解决vite项目发版后浏览器缓存问题

问题:更新环境的版本后,再次点开时是旧的前端

下图中,当时最新的发布版本还是 26 号,却显示 22号的

image.png

打开某页面时,页面渲染失败,如下所示

image.png

原因:

vite 默认配置后,打包后 css 和 js 的名字后面都加了哈希值,不会有缓存问题。 但是 index.html 是有缓存的,其中加载的 css\js 文件已经不对了。 于是,出现报错 TypeError: Failed to fetch dynamically imported module。

image.png

解决方案:

① 处理 TypeError: Failed to fetch dynamically imported module

相关 issue:

在 ErrorBoundary 中,捕获到错误这种错误后,自动刷新页面 (不过会看见闪屏,体验感不太好)

 const fetchResourcesErrors = ['Failed to fetch dynamically imported module', 'Importing a module script failed']
   if (fetchResourcesErrors.some((item) => isString(error?.message) && error.message?.includes(item))) {
     window.location.reload()
   }

② 不缓存 index.html

a.前端在 .html 页面加 meta 标签

    <meta http-equiv="pragma" content="no-cache">
    <meta http-equiv="cache-control" content="no-cache, no-store, must-revalidate">
    <meta http-equiv="expires" content="0">

b.后端 nginx 配置,让 index.html 不缓存

add_header Cache-Control "no-cache, no-store";

相关文章:


3. 解决路由跳转页面闪烁问题

# react-router-dom懒加载时出现的闪屏问题解决方法~~


4. 在 React18 中使用 createRoot 代替 render,利用 ref 在组件渲染或更新后调用一个回调函数

React17中,旧的 API 的使用方式,第三个参数即回调函数

import * as ReactDOMClient from 'react-dom/client';
function App() { return ( <div> <h1>Hello World</h1> </div> ); }
const rootElement = document.getElementById("root");
ReactDOMClient.render(<App />, rootElement, () => console.log("renderered"));

React18 中,在新的 API 中,可利用 ref 调用机制解决这个问题

import * as ReactDOMClient from 'react-dom/client';
function App({ callback }) {
    // Callback will be called when the div is first created.
    return ( <div ref={callback}> <h1>Hello World</h1> </div> );
}
const rootElement = document.getElementById("root");
const root = ReactDOMClient.createRoot(rootElement);
root.render(<App callback={() => console.log("renderered")} />);

了解更多:ref 调用时间

① componentDidMount 的时候会调用该函数 在 componentDidMount 事件中可以使用 ref

② 如果 ref 的值发生了变动(旧的函数被新的函数替代),分别调用旧的函数以及新的函数,时间点出现在 componentDidUpdate 之前

a.旧的函数被调用时,传递 null

b.新的函数被调用时,传递对象(dom 元素对象或者实例对象)

③ 如果 ref 所在的组件被卸载,会调用函数

相关文章:


5. Warning: [antd: message] Static function can not consume context like dynamic theme.

image.png

利用 App 包裹组件提供可消费 React context 的 message.xxx、Modal.xxx、notification.xxx 的静态方法,可以简化 useMessage 等方法需要手动植入 contextHolder 的问题。

相关文章:


6. vite 加载静态资源-- require is not defined

image.png

相关文章:


7. vite 项目参考

vitejs/awesome-vite

8. global:1 Failed to load module script: Expected a JavaScript module script but the server responded with a MIME type of "text/html". Strict MIME type checking is enforced for module scripts per HTML spec.

__dirname 在 node 模块文件中使用 ~

9. tsconfig 的路径映射和 vite config 的 resolve.alias 有什么区别?

tsconfigpaths选项和vite.config.jsresolve.alias选项主要目的是相似的,即它们都能指定别名路径,使得你能从任何代码文件中,通过简短、易记和绝对的路径来引入其他模块。

但两者还是有以下主要差异:

  1. 适用环境:
    • tsconfig.jsonbaseUrlpaths配置主要应用于 Typescript 编译环境,帮助 Typescript 转译器理解模块路径别名。
    • vite.config.jsresolve.alias配置被 Vite 用来在 javascript bundling 阶段(或开发服务器中)处理模块路径别名。
  2. 关于运行时:
    • Typescript 只是 javascript 的一个超集,所以它会被编译成纯 javascript 以在浏览器或 Node.js 中运行。在这个过程中,Typescript 编译器并不会去解析paths配置中的别名路径,这会导致运行时可能出现“找不到模块”之类的错误。为解决这个问题,我们在打包时应使用vite config.js或 webpack 等工具的别名路径配置。
    • 然而,vite.config.jsresolve.alias在完整构建阶段将别名路径解析成实际路径,因此不存在这个问题。 你可以看到运行时浏览器或 Node.js 能够准确找到模块。

所以,总结一下,tsconfigpaths用在编译阶段,帮助 TypeScript 理解模块别名路径;而vite.config.jsresolve.alias用在打包阶段,转化和解析别名路径以确保在运行时可以正确找到模块。

一般情况下,你需要在你的项目中同时配置tsconfig.json中的paths选项和vite.config.js(或其他 bundler 如 webpack)中的alias选项,两者是需要结合使用的。 这可以确保你在编码时获得正确的类型检查和自动补全能力(由tsconfig.json中的paths提供),同时在运行时能正确找到和加载模块(由vite.config.js中的alias提供)。

10. vite 中如何读取环境变量?

有如下 .env 文件

VITE_APP_TITLE=MyApp
  1. vite.config.ts 中,读取 VITE_APP_TITLE
import { defineConfig, loadEnv } from 'vite'

export default ({ mode }) => {
  // 根据当前工作目录中的 `mode` 加载 .env 文件
  // 设置第三个参数为 '' 来加载所有环境变量,而不管是否有 `VITE_` 前缀。
  const env = loadEnv(mode, process.cwd(), '') || {}
  console.log(env.VITE_APP_TITLE); // 你应该能看到你的环境变量值

  return defineConfig({
    // 你的配置...
  });
};
  1. 你可以在你的客户端 JavaScript 或 TypeScript 代码中这样使用:
if (import.meta.env.VITE_APP_TITLE) {
  console.log(import.meta.env.VITE_APP_TITLE)
}

为了避免 TypeScript 编译错误,你可能需要在全局类型定义文件(例如 src/env.d.ts)中添加以下代码:

interface ImportMetaEnv {
  readonly VITE_APP_TITLE?: string;
  // 和其他环境变量
}

interface ImportMeta {
  readonly env: ImportMetaEnv;
}