- 前端
- 3D渲染
【免费下载链接】react-360
Create amazing 360 and VR content using React
本指南围绕 React 360 官方脚手架工具react-360-cli展开,讲解其安装方式、init命令的完整执行流程、生成项目的每个文件作用,以及驱动整个脚手架的核心「生成器(Generators)」机制。读完本文,你将不仅能熟练使用 CLI 创建 360° 应用,还能理解其内部实现原理,并可以自定义生成器来扩展自己的脚手架。
React 360 的双包架构:运行时与 CLI
React 360 以两个独立 npm 包的形式分发,职责划分非常清晰:
react-360:包含构建交互式 360° 应用所需的全部运行时代码(React 组件、渲染合成器、相机控制、音频视频模块等),作为项目依赖安装。本仓库中的核心实现在 React360/js 目录,对外 API 入口为 React360/React360.js。react-360-cli:命令行脚手架工具,应全局安装(npm install -g react-360-cli),用于快速初始化 React 360 项目。它的全部源码位于 addons/react-360-cli,即本文的主角。
从 CLI 的 package.json 可以看到它的关键元信息:包名为react-360-cli,版本0.1.0,通过"bin": {"react-360": "index.js"}把可执行命令暴露为全局的react-360,运行时仅依赖chalk(用于终端彩色输出),并要求 Node.js 版本>= 6。
快速开始:从零创建一个 React 360 项目
第一步:全局安装 CLI
npm install -g react-360-cli安装完成后,命令行中即出现react-360命令。由于index.js首行带有#!/usr/bin/env nodeshebang(见 index.js),npm 会将其作为可执行脚本链接到全局 bin 目录。
第二步:初始化项目
react-360 init MyProject该命令会:
- 在当前目录下创建名为
MyProject的新项目目录; - 生成 React 360 起步所需的所有文件;
- 自动运行
npm install(若检测到 yarn 则改用 yarn)拉取项目依赖。
第三步:启动开发
cd MyProject npm startnpm start实际执行的是node node_modules/react-360/scripts/packager.js(见生成的 package.json),即启动 Metro 打包器。启动完成后,浏览器访问http://localhost:8081/index.html即可预览应用;直接编辑index.js即可开始修改应用内容。
init命令的完整执行流程(源码级解析)
CLI 入口 index.js 是整个流程的总指挥,它依次完成以下阶段(阶段间以 Promise 链串接):
1. 版本自检
启动时,getLatestVersion()(实现见 src/getLatestVersion.js)会先通过npm config get registry或yarn config get registry读取当前 registry 地址,再向{registry}react-360-cli/latest发起 HTTPS 请求获取最新版本号。若本地版本落后于最新版本,会以红色提示并给出升级命令:
- 使用 yarn 时:
yarn global upgrade react-360-cli - 使用 npm 时:
npm update -g react-360-cli
版本检查的所有异常(网络失败、解析失败等)都会被静默吞掉(resolve null),不影响主流程执行。
2. 命令分发与参数校验
- 无参数或传入
--help时,打印用法并退出; - 仅支持
init子命令,其他命令会输出Unsupported command:并打印用法; init缺项目名时打印用法退出。
3. 项目名校验(两道防线)
CLI 对项目名执行了严格的双重校验:
// 第一道:非法字符正则 if (rawName.match(/[~!@#%^&*()+=;:{}\[\]\/.,<>'"]/)) { ... } // 第二道:必须是合法 JS 变量名(用于 eval 校验,并把 '-'、空白替换为 '_') const name = rawName.replace(/[-\s]+/g, '_'); eval(`const ${name} = null`);第一道防线拦截 shell 特殊字符与标点;第二道防线把MyProject这类名称规范化为合法 JS 标识符(如my-project会变成my_project),因为生成的项目名会被嵌入index.js中作为 React 组件类名与注册名(见下文)。
4. 目标目录检查与创建
CLI 检查当前工作目录下是否已存在同名文件/目录:若存在则报错退出(A directory/file named ... already exists);否则调用fs.mkdirSync(dest)创建项目目录。
5. 生成项目文件
调用generateFiles(source, dest, {name})(实现见 src/generateFiles.js),把generators目录中的模板全部渲染到项目目录,并额外创建空的.watchmanconfig文件(fs.closeSync(fs.openSync(...)))。
6. 复制静态资源
copyAssets(binDir, dest)(实现见 src/copyAssets.js)会把 CLI 包内置的 static_assets 目录(内含默认的 360° 全景图360_world.jpg)原样复制到新项目的static_assets/下。
7. 自动安装依赖
getPackager()(实现见 src/getPackager.js)先探测环境:尝试执行yarn --version(Windows 下输出重定向到 NUL),成功则选择 yarn,否则回退到 npm。随后以cwd: dest方式 spawn 依赖安装进程(Windows 下使用yarn.cmd/npm.cmd):
const cmd = cmdName === 'yarn' ? child_process.spawn(/^win/.test(process.platform) ? 'yarn.cmd' : 'yarn', {...}) : child_process.spawn(/^win/.test(process.platform) ? 'npm.cmd' : 'npm', ['install'], {...});安装进程退出码非 0 时,会提示手动在项目目录内重跑该命令。
8. 完成提示
全部成功后,终端输出进入项目的后续操作指引:
Now enter the new project directory by running `cd MyProject` Run `npm start` to initialize the development server From there, browse to http://localhost:8081/index.html Open `index.js` to begin editing your app.生成的项目结构:每个文件都从哪来、干什么
react-360 init生成的项目由 generators 目录下的模板模块逐一生成。下表列出全部生成文件及其模板来源:
| 生成文件 | 模板来源 | 作用 |
|---|---|---|
index.js | index.generator.js | React 应用入口:定义名为项目名的组件,注册到AppRegistry |
client.js | client.generator.js | 运行时初始化:创建ReactInstance、渲染到默认曲面、加载背景 |
index.html | index.html.generator.js | 页面骨架:加载 bundle 并调用React360.init |
package.json | package.json.generator.js | 项目依赖与 npm scripts |
.babelrc | babelrc.generator.js | Babel 配置,preset 为react-native |
.flowconfig | flowconfig.generator.js | Flow 类型检查配置 |
.gitignore | gitignore.generator.js | Git 忽略规则 |
.watchmanconfig | watchmanconfig.generator.js | Watchman 文件监听配置({}) |
rn-cli.config.js | rn-cli.config.generator.js | Metro 打包器配置 |
static_assets/360_world.jpg | static_assets | 默认 360° 背景图(由 copyAssets 复制) |
index-test.js | tests/index-test.generator.js | Jest 冒烟测试 |
应用入口index.js
模板把项目名注入两处:组件类名与注册名。
export default class ${config.name} extends React.Component { render() { return ( <View style={styles.panel}> <View style={styles.greetingBox}> <Text style={styles.greeting}>Welcome to React 360</Text> </View> </View> ); } }; AppRegistry.registerComponent('${config.name}', () => ${config.name});默认 UI 是一个 1000×600 的半透明白色面板(rgba(255, 255, 255, 0.4)),内嵌黑底蓝边的问候框,字体 30 号。这些样式均通过StyleSheet.create定义。
运行时初始化client.js
import {ReactInstance} from 'react-360-web'; function init(bundle, parent, options = {}) { const r360 = new ReactInstance(bundle, parent, { fullScreen: true, ...options, }); r360.renderToSurface( r360.createRoot('${config.name}', { /* initial props */ }), r360.getDefaultSurface() ); r360.compositor.setBackground(r360.getAssetURL('360_world.jpg')); } window.React360 = {init};其中fullScreen: true表示默认全屏渲染,renderToSurface将根组件渲染到默认圆柱曲面(Cylinder Surface),setBackground加载360_world.jpg作为 360° 环境背景。ReactInstance的完整实现可参考 React360/js/ReactInstance.js。
页面骨架index.html
<div id="container"></div> <script src="./client.bundle?platform=vr"></script> <script> React360.init( 'index.bundle?platform=vr&dev=true', document.getElementById('container'), { bridgeFile: 'NonBlobBridge.js', assetRoot: 'static_assets/', } ); </script>注意platform=vr参数与bridgeFile: 'NonBlobBridge.js'(对应运行时 React360/js/Executor/NonBlobBridge.js),以及assetRoot指向static_assets/目录。
项目package.json
生成的项目依赖固定版本组合:react@16.3.2、react-native@~0.55.4、three@^0.87.0、react-360@~1.1.0、react-360-web@~1.1.0;devDependencies 包含jest、babel-jest、react-devtools、xopen等。scripts 一览:
{ "start": "node node_modules/react-360/scripts/packager.js", "bundle": "node node_modules/react-360/scripts/bundle.js", "open": "node -e \"require('xopen')('http://localhost:8081/index.html')\"", "devtools": "react-devtools", "test": "jest" }其中bundle脚本对应仓库 scripts/bundle.js,start对应 scripts/packager.js;test使用jest(preset 为react-360),配合index-test.js做渲染冒烟测试。
Metro 打包器配置rn-cli.config.js
getAssetExts() { return ['obj', 'mtl']; }, // 识别 3D 模型资产扩展名 getPlatforms() { return ['vr']; }, // 注册 vr 平台 getProvidesModuleNodeModules() { return ['react-native', 'react-360']; },这是 React 360 项目区别于普通 React Native 项目的关键配置:声明vr平台与.obj/.mtl资产类型,正是index.html中platform=vr能生效的底层原因。
Generators 机制:脚手架的核心抽象
README 的核心章节介绍了这一机制:React 360 CLI 依据generators目录下的文件来生成起始项目。这些文件是极简的 JavaScript 模块,从字符串模板产出文件。配置变量可从 CLI 传入生成器,生成器返回一个包含两个键的对象:
filename:要创建的文件名;content(源码中实际读取的是contents字段):文件内容。
两者组合后,在生成器所在目录的相对位置创建文件。例如位于generators/subdir的生成器会在$PROJECT_DIRECTORY/subdir/产出文件。注意源码 generateFiles.js 中读取的是fileData.contents,且会额外回填fileData.path记录生成器相对目录。
接口约定
每个生成器模块导出形如module.exports = config => ({filename, contents})的函数,其中config即 CLI 注入的配置对象。init命令注入的配置是{name}(规范化后的项目名)。例如 index.generator.js 用${config.name}作为类名与注册名,package.json.generator.js 用${config.name}作为包名。
生成器的递归发现
listGenerators.js 负责递归遍历generators目录:对每个条目用fs.stat判断是目录还是文件(实现见 isDirectory.js),目录则递归下降并拼接相对路径,文件则记录其所在目录路径。这样generators/__tests__/index-test.generator.js就会在项目的__tests__/目录下产出index-test.js。
文件落盘
generateFiles.js 把发现的所有生成器一次性require(通过 makeModulePath.js 把相对路径转成可解析的模块路径),调用gen(config)得到{filename, contents, path},再交给createFiles并行执行 createFile.js:后者先用 ensureDir.js 同步创建目标目录(避免并发冲突),再fs.writeFile写入path.join(dest, file.path, file.filename)。
编写自定义生成器的示例
以新增一个README.md模板为例,在generators/下新建readme.generator.js:
module.exports = config => ({ filename: 'README.md', contents: `# ${config.name} This project was generated by react-360-cli. `, });由于生成器是递归发现的普通 JS 模块,重新运行react-360 init后,新项目根目录就会自动出现README.md。这正是「配置变量 + 字符串模板 + 递归目录发现」这套极简抽象带来的可扩展性。
底层辅助工具一览
CLI 的src/目录还包含若干支撑模块,理解它们有助于深度定制:
| 模块 | 职责 |
|---|---|
| createFile.js | 建目录 + 写文件 |
| ensureDir.js | 同步确保目录存在 |
| listGenerators.js | 递归枚举生成器 |
| isDirectory.js | 判断条目是否为目录 |
| makeModulePath.js | 路径转可加载模块路径 |
| generateFiles.js | 编排生成流程 |
| copyAssets.js / copyFile.js | 流式复制静态资产(fs.createReadStream().pipe()) |
| getLatestVersion.js | 检查 npm registry 最新版本 |
| getPackager.js | 探测 yarn / npm |
测试与质量保障
生成的项目自带一个 Jest 冒烟测试index-test.js(模板见tests/index-test.generator.js),通过react-test-renderer渲染../index.js导出的根组件来验证应用能否正常挂载:
import Index from '../index.js'; import renderer from 'react-test-renderer'; it('renders correctly', () => { const tree = renderer.create(<Index />); });项目内运行npm test即可执行。仓库根目录的 jest.config.js 与 babel.config.js 则配置了 CLI 本身及其余子包的测试环境。
常见问题与注意事项
- 项目名规范:避免使用
~!@#%^&*()+=;:{}[]/.,<>'"等字符,-与空白会被自动替换为_;项目名必须是合法 JS 标识符,否则 CLI 拒绝创建。 - 同名冲突:当前目录下已存在同名文件或目录时,CLI 直接退出,不会覆盖任何内容。
- 依赖安装失败:CLI 只负责触发安装;若
npm install/yarn中途失败,进入项目目录手动重跑即可,不会影响已生成的文件。 - 版本提示:CLI 每次启动都会静默检查最新版本,发现落后时给出升级命令;网络异常时该检查自动跳过。
- 平台参数:
index.html中的platform=vr与rn-cli.config.js中声明的vr平台必须保持一致,这是 Metro 正确打包 React 360 应用的前提。 - Node 版本:CLI 自身要求 Node
>= 6;但运行时依赖(react 16.3.2、react-360 1.1.0 等)对现代 Node 环境建议按实际开发环境验证。
通过本文,你不仅掌握了react-360 init的完整使用链路,也深入理解了其「生成器 + 配置注入」的脚手架内核——这套设计让模板维护与项目初始化解耦,是值得在自有工具链中借鉴的轻量方案。更深入的内容,可继续阅读仓库中的 setup.md、example-basicapptemplate.md 与 what-is.md 了解 React 360 的整体概念与示例模板。
- 前端
- 3D渲染
【免费下载链接】react-360
Create amazing 360 and VR content using React
相关推荐
KeyPass加密备份与恢复全攻略:3步打造你的密码安全防线
KeyPass加密备份与恢复全攻略:3步打造你的密码安全防线 在数字时代,密码安全至关重要,而KeyPass作为一款开源离线密码管理器,为用户提供了强大的加密备
前端3D渲染RPFM:6大功能模块深度解析,全面战争MOD开发的终极效率工具
RPFM:6大功能模块深度解析,全面战争MOD开发的终极效率工具 你是否曾经面对《全面战争》庞大的游戏数据感到无从下手?是否在传统MOD工具中经历了漫长的加载等
前端3D渲染OpenReel Video Monorepo开发流实践:pnpm workspaces、WASM构建与多包测试体系
OpenReel Video Monorepo开发流实践:pnpm workspaces、WASM构建与多包测试体系 OpenReel Video 是一个专业级
音视频前端桌面应用
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考