当前位置: 首页 > web >正文

React项目运行环境与执行顺序及动态路由等使用注意点

目录

问题起因:

React项目在开发和生产过程中主要涉及到了两个环境:

文件执行顺序

假如我调用接口,Umi是否会等待这些异步操作完成?

下面是流程图:

解决方案(针对 Ant Design Pro/Umi)

方案 1:运行时动态路由(推荐)

方案 2:构建时预取数据(需插件支持)

方案 3:自定义构建脚本

如果我说:顶级文件执行环境为node,这句话对吗?

里面的一些内容:

1. 基础元数据

2.脚本命令(scripts)

3. 浏览器兼容性(browserslist)

4. 依赖管理

生产依赖(dependencies)

开发依赖(devDependencies)

5. 环境约束(engines)

其他常见环境问题与解决方案

问题1:服务器端渲染(SSR)中的window问题

问题2:环境变量访问

最后,我们在使用过程中需要:


问题起因:

最近遇到了点问题,react项目的动态路由,逻辑这些都写好了,我把后台的数据复制到本地模拟动态路由是可以的,但是呢,当调用接口后数据能够拿到,尴尬的是动态路由渲染不出来,实际上是拿不到,也就是说,执行顺序的问题导致的,也就是环境问题。

下面是我的文件的位置:

注意这是文件的位置:顶级文件!

React项目在开发和生产过程中主要涉及到了两个环境:

一个是window(浏览器环境),一个是node环境

判断是否浏览器环境的话可以通过 if (typeof window !== 'undefined') {}

先node ---> 后window

node的话,主要是用于开发工具链和构建过程:

开发阶段(运行开发服务器、构建工具)和生产阶段(构建生产包)

  • 执行构建工具(Webpack、Babel)

  • 运行开发服务器

  • 处理环境变量

  • 执行测试

相关的文件比如:package.json, webpack.config.js, .env环境变量等

浏览器环境,主要是用于:

运行实际React应用(打包后的代码在浏览器中执行)

  • 渲染用户界面

  • 执行React组件

  • 处理用户交互

相关的文件比如:index.html ,src/index.js,src/App.js,路由配置文件,React组件文件,CSS/SCSS样式文件等

文件执行顺序

  1. Node.js启动开发服务器(npm start)

  2. 加载构建配置(webpack, babel)

  3. 编译源代码(JSX转换、TypeScript编译等)

  4. 浏览器加载HTML入口文件(index.html)

    浏览器加载并解析 public/index.html 文件,这是React应用的入口点。<!-- public/index.html -->
    <!DOCTYPE html>
    <html lang="en">
    <head><meta charset="utf-8" /><title>React App</title>
    </head>
    <body><!-- 根DOM容器 --><div id="root"></div>
    </body>
    </html>
  5. 加载并执行JavaScript入口文件(index.js)

    览器解析到 index.js 文件后,下载并执行该脚本。// src/index.js
    import React from 'react';
    import ReactDOM from 'react-dom/client';
    import './index.css';
    import App from './App';// 定位根DOM节点
    const rootElement = document.getElementById('root');// 创建React根
    const root = ReactDOM.createRoot(rootElement);
  6. 渲染根组件(App.js)

    在入口文件中调用 root.render() 方法,开始渲染根组件。// src/index.js 
    // 渲染根组件
    root.render(<React.StrictMode><App /></React.StrictMode>
    );App.js 组件被加载和初始化,开始构建组件树。// src/App.js
    import React from 'react';
    import './App.css';function App() {return (<div className="App"><header className="App-header"><!-- 应用内容 --></header></div>);
    }export default App;
  7. 初始化路由(React Router配置)

  8. 渲染匹配的路由组件

  9. 组件生命周期执行

对于上面我遇到的问题中:

1. Node.js环境(SSR)没有window对象浏览器API(如 `localStorage`)只有在浏览器环境中才可用

2. 即使没有SSR,在组件的顶层作用域直接访问 `localStorage` 也会在组件挂载前执行

3.在组件渲染的初始阶段(包括路由配置)执行时机过早

4..在客户端渲染时,只会在首次导入时执行一次,可能读取不到最新的值

node输出的路由只有本地的,没有从后台或者本地获取的:

我代码里面虽然判断了是否window环境,但是这里node就执行了,所以说下面这个动态路由直接为空数组了,也就是说,动态路由需要后台获取的或者调用api(这里调用接口是异步的,node环境使用axios调用也是一样的,数据是能够拿到的)的就不能放在顶级文件中,需要换位置,一般放在src文件中的某处

(我这里是登录后获取到的用户信息里面包含了,直接缓存到本地了)

可以在组件挂载后,使用useEffect+useState等调用浏览器的api这些

假如我调用接口,Umi是否会等待这些异步操作完成?

我关心的是在构建过程中,Umi是否会等待这些异步操作完成,然后再继续执行后续的静态路由和动态路由的导出操作?

我尝试使用过调用后台的api接口,并让等待其执行后,执行导出静态+动态路由。

但是,目前这个项目是基于(Ant Design Pro ) Umi 框架的,而不是 Next.js,比较遗憾,没啥用。

在Umi框架中,默认情况下,在构建时不会等待你在路由配置文件中进行的异步数据获取,要求是同步的,不能使用await!!!

1. 路由配置文件的作用:`config/routes.tsx`(或类似配置文件)的主要目的是定义路由结构,而不是执行数据获取。它通常是同步的。

2. 数据获取的时机:在Umi项目中,数据获取通常发生在以下环节:

   2.1 - 页面组件内:使用`useEffect`(客户端获取)或通过Umi的服务器端渲染(SSR)     能   力   (在`getInitialProps`或类似方法中)。

   2.2 - 服务端渲染(SSR):如果你启用了SSR,那么数据获取会在服务端进行,但这是在请求时(request time)而不是构建时(build time)。

3. 动态路由的生成:Umi支持动态路由(例如`/user/:id`),但动态路由的生成(即生成多个具体的路由路径)通常需要你在构建时通过插件或脚本预先确定这些路径

Umi本身不会在构建时去调用API获取动态路由的路径列表,动态路由的生成必须在构建前通过脚本准备好

下面是流程图:

解决方案(针对 Ant Design Pro/Umi)

方案 1:运行时动态路由(推荐)

在页面组件内处理异步数据,而不是在路由配置中:

// config/routes.tsx (静态定义路径)
export default [{path: '/dynamic/:id',component: '@/pages/DynamicPage',}
];// src/pages/DynamicPage.tsx
import { useParams } from 'umi';export default function DynamicPage() {const params = useParams<{ id: string }>();const [data, setData] = useState(null);useEffect(() => {const fetchData = async () => {const response = await fetch(`/api/data/${params.id}`);setData(await response.json());};fetchData();}, [params.id]);return data ? <div>{data.content}</div> : <Loading />;
}
方案 2:构建时预取数据(需插件支持)

使用 Umi 插件实现类似 SSG 的功能:

  1. 安装数据获取插件:

    npm install umi-plugin-static-props
  2. 配置 config/config.ts

export default {plugins: ['umi-plugin-static-props'],staticProps: {dynamicPaths: async () => {const res = await fetch('https://api.example.com/dynamic-paths');return res.json().map(id => ({ params: { id } }));},getProps: async ({ params }) => {const res = await fetch(`https://api.example.com/data/${params.id}`);return { props: { data: await res.json() } };}}
};
  1. 3.修改页面组件:
// src/pages/DynamicPage.tsx
export default function DynamicPage({ data }) {return <div>{data.content}</div>;
}// 声明静态属性
DynamicPage.getStaticProps = async ({ params }) => {// 此函数在构建时由插件调用
};
方案 3:自定义构建脚本

在 package.json 中添加预构建脚本:

{"scripts": {"prebuild": "node ./scripts/fetchDynamicRoutes.js","build": "umi build"}
}
// scripts/fetchDynamicRoutes.js
const fs = require('fs');
const fetch = require('node-fetch');(async () => {// 1. 获取动态路由数据const routes = await fetch('https://api.xxx.com/dynamic-paths').then(r => r.json());// 2. 生成路由配置文件const routeConfig = `export default ${JSON.stringify(routes.map(id => ({path: `/dynamic/${id}`,component: '@/pages/DynamicPage',// 注入预获取数据data: ${JSON.stringify(await fetchData(id))} })),null,2)};`;// 3. 写入临时路由文件fs.writeFileSync('./src/.temp/routes.ts', routeConfig);
})();async function fetchData(id) {const res = await fetch(`https://api.xxx.com/data/${id}`);return res.json();
}
// config/routes.tsx
import 'src/.temp/routes'; // 导入生成的配置

对于大多数 Ant Design Pro 项目,建议采用方案 1(运行时获取)结合 客户端缓存,既能保持开发简单性,又能提供良好用户体验。

如果需要 SEO 支持,可配合方案 2 或方案 3 实现部分路由的静态化。

如果我说:顶级文件执行环境为node,这句话对吗?

 React应用的构建过程(编译、打包通常Node.js环境中进行。

// Webpack/Vite 等构建工具在 Node 环境中处理 React 文件
//  所有 import/require 语句由 Node 处理
import React from 'react';

 React应用的运行环境主要是浏览器(客户端)。

 如果使用服务端渲染(SSR),则部分代码(包括顶级文件)会在Node.js服务器环境中执行。

举例:

// 这个文件可能被 Node 和浏览器执行 (SSR 时)
export default function Page({ data }) { // Node 获取 data (getServerSideProps)return (<div>{/* 这部分 DOM 操作在浏览器执行 */}<button onClick={() => console.log('Click!')}>{data.title} {/* 文本由 SSR 注入 */}</button></div>)
}// 这段只在 Node 执行 (Next.js)
export async function getServerSideProps() {const res = await fetch('https://api.example.com/data'); // Node 环境请求return { props: { data: await res.json() } };
}

因此,原话“react的顶级文件执行环境为node”是片面的

正确的描述应该是:React 应用的源码处理和构建阶段在 Node 环境中运行,但运行时逻辑主要在浏览器环境执行。服务端渲染时组件代码会在 Node 环境先执行一次。

这里的话简单讲下node的这个package.json文件吧

package.json 是 Node 项目的 “配置中心”,统筹 元数据、脚本、依赖、环境约束

简单介绍下下面这个文件吧

package.json里面的一些内容:

1. 基础元数据

{"name": "ant-design-pro","version": "6.0.0","private": true,"description": "An out-of-box UI solution for enterprise applications"
}
  • name:项目名称,用于标识项目(若发布到 npm 仓库,名称需唯一)。
  • version:项目版本号,遵循 语义化版本规范主版本.次版本.补丁版本)。
  • private:设为 true 时,项目不会被发布到 npm 公共仓库(避免私有项目误发布)。
  • description:项目功能描述,方便开发者理解项目定位。

2.脚本命令(scripts

"scripts": { ... }
  • 用于定义 npm 脚本,通过 npm run <脚本名> 运行命令(如 npm run start 启动开发服务)。
  • 常见脚本:start(开发环境启动)、build(生产打包)、test(单元测试)等(具体内容因项目而异)。

3. 浏览器兼容性(browserslist

"browserslist": ["> 1%","last 2 versions","not ie <= 10"
]
  • 定义项目 支持的浏览器范围,影响以下工具:
    • Babel:决定哪些 ES6+ 语法需要转译为 ES5(兼容旧浏览器)。
    • Autoprefixer:决定哪些 CSS 属性需要添加浏览器前缀(如 -webkit--moz-)。
  • 规则解析:
    • > 1%:覆盖 全球市场份额超过 1% 的浏览器(数据来自 Can I Use)。
    • last 2 versions:每个浏览器的 最后两个正式版本
    • not ie <= 10排除 IE 10 及更早版本(即不支持 IE 10 以下浏览器)。

4. 依赖管理

生产依赖(dependencies
"dependencies": { ... }
 
  • 项目 运行时必须的依赖(如 React、Ant Design 组件库、业务逻辑库等),会被打包到生产环境。
开发依赖(devDependencies
"devDependencies": { ... }

 
  • 仅 开发阶段需要的依赖(如 Webpack、Babel、ESLint、测试框架等),生产环境无需安装。

5. 环境约束(engines

"engines": {"node": ">=12.0.0"
}

 
  • 指定项目运行所需的 Node.js 版本范围(这里要求 Node.js ≥ 12.0.0)。
  • 作用:确保团队成员 / CI 环境使用兼容的 Node 版本,避免因版本差异导致的问题。

 举例:

{"name": "my-react-app","version": "0.1.0","scripts": {// Node.js环境下执行的脚本"start": "react-scripts start","build": "react-scripts build","test": "react-scripts test"},"dependencies": {// 运行时依赖(浏览器环境)"react": "^18.2.0","react-dom": "^18.2.0"},"devDependencies": {// 开发依赖(Node.js环境)"webpack": "^5.75.0","babel-loader": "^9.1.2"}
}

当然,除了上面这些还有其他的一些需要注意的:

其他常见环境问题与解决方案

问题1:服务器端渲染(SSR)中的window问题

// 错误:直接访问window对象
const isMobile = window.innerWidth < 768;// 正确:使用useEffect和useState
const [isMobile, setIsMobile] = useState(false);
useEffect(() => {const handleResize = () => {setIsMobile(window.innerWidth < 768);};window.addEventListener('resize', handleResize);handleResize(); // 初始调用return () => window.removeEventListener('resize', handleResize);
}, []);

问题2:环境变量访问

// .env文件(Node.js环境)
API_URL=https://api.example.com// React组件中访问(浏览器环境)
// 错误:process.env是Node.js环境变量
const apiUrl = process.env.API_URL;// 正确:使用REACT_APP_前缀
// .env文件:REACT_APP_API_URL=https://api.example.com
const apiUrl = process.env.REACT_APP_API_URL;

最后,我们在使用过程中需要:

  • 始终在useEffect中访问浏览器API(localStorage, window等)
  • 为异步操作添加加载状态
  • 使用环境变量时添加REACT_APP_前缀
  • 避免在模块顶层作用域访问浏览器API
  • 在SSR中使用动态导入(dynamic import)延迟加载浏览器相关组件
  • 使用自定义hook封装环境相关逻辑

 所有依赖浏览器环境的操作都应在组件挂载后执行(useEffect)

上面内容经供参考,包含个人看法,不是很准确,有点模糊,可能存在问题,有问题请指正,感谢!


----------到底啦----------


http://www.xdnf.cn/news/16209.html

相关文章:

  • 数据结构系列之AVL树
  • 1、黑马点评复盘(短信登录-Session或Redis实现)
  • 不同地区的主要搜索引擎工具
  • 嵌入式linux下的NES游戏显示效果优化方案:infoNES显示效果优化
  • GaussDB view视图的用法
  • now能减少mysql的压力吗
  • 重写 与 重载
  • OpenCV(02)图像颜色处理,灰度化,二值化,仿射变换
  • 优化 Elasticsearch JVM 参数配置指南
  • 浙大Fast Lab:融合3D激光雷达与强化学习的「端到端导航」,让无人机“飞”在点云上!
  • 【Linux-云原生-笔记】keepalived相关
  • OSPF路由协议——上
  • Android MediaCodec 的使用和源码实现分析
  • VSCode 开发 STM32 - clangd 带来的极致补全体验
  • Zipformer
  • ZKmall开源商城微服务架构实战:Java 商城系统的模块化拆分与通信之道
  • 小白做投资测算,如何快速上手?
  • 反向传播及优化器
  • 《WebGL打造高性能3D粒子特效系统:从0到1的技术探秘》
  • QooCam3 App 版本更新
  • 开源的语音合成大模型-Cosyvoice使用介绍
  • 《互联网信息服务算法推荐管理规定》解读
  • web安全 | docker复杂环境下的内网打点
  • 聊聊 Flutter 在 iOS 真机 Debug 运行出现 Timed out *** to update 的问题
  • ZLMediaKit流媒体服务器WebRTC页面显示:使用docker部署
  • 软件开发、项目开发基本步骤
  • Spark实现WorldCount执行流程图
  • iOS上使用WebRTC推拉流的案例
  • C++ std::list概念与使用案例
  • 深入解析预训练语言模型在文本生成中的革命性应用:技术全景与未来挑战