随着 Web 前端技术的发展,越来越多的企业与开发者开始将文档处理和文件管理等能力迁移至纯前端体系中,以提升应用的响应速度、安全性和易用性。
作为当前流行的前端框架之一,React 以其声明式、组件化的优势,广泛应用于构建从后台管理系统、在线文档平台到教育管理系统等各类复杂Web应用。 本文将指导你便捷地在 React 项目中集成使用Spire.OfficeJS,让复杂的文档处理变得简单、可控。
目录:
关于 Spire.OfficeJS
Spire.OfficeJS 是 E-ICEBLUE 推出的纯前端文档编辑组件套件,包含 Word、Excel、PowerPoint、PDF 等多种格式的查看、编辑、创建与转换能力。开发者无需安装 Microsoft Office,也无需依赖后端服务,即可在浏览器中完成从打开到编辑、再到导出的完整文档处理流程。其轻量、高效、易集成的特性,使其非常适合用于构建在线文档平台、知识库、教育平台及各类管理后台。
套件包括以下四款前端编辑器组件:
- Spire.WordJS
- Spire.ExcelJS
- Spire.PresentationJS
- Spire.PDFJS
核心功能:
- 多格式支持:支持 Word、Excel、PPT 预览,编辑与导出,以及 PDF 格式文档的预览
- 在线编辑能力:可在浏览器中直接操作 Office 文档编辑文档内容,例如文本、表格、图片、样式等
- 高性能纯前端运行:基于 WebAssembly,加载轻量、运行高效,无需后端参与
- 灵活集成:可轻松接入 React、Vue、Angular 等框架,原生 JavaScript 中使用也非常便捷
创建 React 项目并集成 Spire.OfficeJS
步骤一:安装 Node.js
下载安装 Node.JS (Node.JS 官方网站:https://nodejs.org/en/download/),安装后可通过打开 cmd 后输入下述命令验证版本:
node -v
npm -v

步骤二:使用 Vite 创建 React 项目
指定项目文件夹位置,cmd 进入执行命令:npm create vite@latest my-officejs-app -- --template react 创建初始化 React 项目。

步骤三:npm 安装依赖
Vscode 打开 my-officejs-app 项目,命令执行:npm install react-router-dom 安装路由管理库,以便页面之间更好地切换显示。
步骤四:集成 Spire.OfficeJS
下载 Spire.OfficeJS 产品包, 在 React 项目 public 文件下新建 spire.cloud 文件夹,并将产品解压包中的web文件夹复制到 spire.cloud 下,路径配置跟 Editor.jsx 中路径保持一致

在项目中构建文件上传与编辑器页面
- App.jsx 中配置以下内容,处理路由及全局文件状态管理
import { createContext, useContext, useState } from 'react';
import { createBrowserRouter, RouterProvider } from 'react-router-dom';
import Home from './Home';
import Editor from './Editor';
// 文件状态 Context
const FileContext = createContext();
export const useFileStore = () => useContext(FileContext);
function FileProvider({ children }) {
const [file, setFile] = useState(null);
const [fileUint8Data, setFileUint8Data] = useState(null);
return (
<FileContext.Provider value={{
file,
fileUint8Data,
setFileData: setFile,
setFileUint8Data
}}>
{children}
</FileContext.Provider>
);
}
const router = createBrowserRouter([
{ path: '/', element: <Home /> },
{ path: '/editor', element: <Editor /> },
]);
function App() {
return (
<FileProvider>
<RouterProvider router={router} />
</FileProvider>
);
}
export default App;
- Home.jsx 中配置以下内容用于文件上传功能,包括拖拽上传、选择本地文件、用 FileReader 转成 Uint8Array、将文件保存到 Context,以及跳转到编辑器页面。
说明:
- FileReader:用于读取本地文件;
- Uint8Array:OfficeJS 的 WebAssembly 接收二进制格式
- useNavigate:React Router 的页面跳转函数
- drag&drop: 浏览器拖拽上传
import { useRef, useEffect } from "react"
import { useFileStore } from './App';
import { useNavigate } from 'react-router-dom';
function Home() {
const { setFileData, setFileUint8Data } = useFileStore();
const navigate = useNavigate();
let dropArea = null;
let fileInput = useRef();
let file = null;
let fileUint8Data = null;
useEffect(() => {
dropArea = document;
// 阻止默认拖放行为
['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
dropArea.addEventListener(eventName, preventDefaults, false);
});
// 处理文件拖放
dropArea.addEventListener('drop', handleDrop, false);
}, [])
const preventDefaults = (e) => {
e.preventDefault();
e.stopPropagation();
}
const handleBtnClick = (e) => {
e.preventDefault();
fileInput.current.click();
}
const handleDrop = async (e) => {
if (e.target && e.target.files)
file = e.target.files[0]
else if (e.dataTransfer && e.dataTransfer.files)
file = e.dataTransfer.files[0];
if (!file) return;
fileUint8Data = await handleFile(file);
setFileData(file);
setFileUint8Data(fileUint8Data);
openDocument();
}
const handleFile = (file) => {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => {
const arrayBuffer = reader.result;
const uint8Array = new Uint8Array(arrayBuffer);
resolve(uint8Array);
};
reader.onerror = (error) => reject(error);
reader.readAsArrayBuffer(file);
});
}
const openDocument = () => {
navigate('/editor')
}
return (
<div>
<div>
<h2>文件上传</h2>
<div>
<p>拖放文件到浏览器</p>
<p style={{ marginLeft: '20px' }}>或</p>
<button ref={fileInput} onClick={handleBtnClick}>选择文件</button>
<input
type="file"
id="fileInput"
ref={fileInput}
onChange={handleDrop}
style={{ display: 'none' }}
/>
</div>
</div>
</div>
)
}
export default Home
- 新增含以下内容的 Editor.jsx 集成 OfficeJS 编辑器
import { useRef, useEffect } from "react"
import { useFileStore } from './App';
import { useNavigate } from 'react-router-dom';
function Editor() {
const { file, fileUint8Data } = useFileStore();
const navigate = useNavigate();
const config = useRef({});
let Editor = useRef(null);
let Api = useRef(null);
let originUrl = window.location.origin;
useEffect(() => {
if (!file) {
navigate('/')
return;
}
// 动态加载SpireCloudEditor.js
loadScript();
}, [])
const loadScript = () => {
var script = document.createElement('script');
// 根据你的产品包路径调整
script.setAttribute('src', '/spire.cloud/web/editors/spireapi/SpireCloudEditor.js');
script.onload = () => initEditor()
document.head.appendChild(script);
}
const initEditor = () => {
let iframeId = 'iframeEditor';
initConfig();
Editor = new SpireCloudEditor.OpenApi(iframeId, config.value);
window.Api = Api = Editor.GetOpenApi();
OnWindowReSize();
}
const initConfig = () => {
config.value = {
"fileAttrs": {
"fileInfo": {
"name": file.name,
"ext": getFileExtension(),
"primary": String(new Date().getTime()),
"creator": "User",
"createTime": new Date().toLocaleString()
},
"sourceUrl": originUrl + "/files/" + file.name,
"createUrl": originUrl + "/open",
"mergeFolderUrl": "",
"fileChoiceUrl": "",
"templates": {}
},
"user": {
"id": "uid-1",
"name": "User",
"canSave": true,
},
"editorAttrs": {
"editorMode": "edit",
"editorWidth": "100%",
"editorHeight": "100%",
"editorType": "document", // document/spreadsheet/presentation
"platform": "desktop",
"viewLanguage": "zh", // en/zh
"isReadOnly": false,
"canChat": true,
"canComment": true,
"canReview": true,
"canDownload": true,
"canEdit": true,
"canForcesave": true,
"embedded": {
"saveUrl": "",
"embedUrl": "",
"shareUrl": "",
"toolbarDocked": "top"
},
// 启用WebAssembly以提升性能
"useWebAssemblyDoc": true,
"useWebAssemblyExcel": true,
"useWebAssemblyPpt": true,
"spireDocJsLicense": "",
"spireXlsJsLicense": "",
"spirePresentationJsLicense": "",
"spirePdfJsLicense": "",
// Serverless模式:直接使用文件数据,无需服务器
"serverless": {
"useServerless": true,
"baseUrl": originUrl,
"fileData": fileUint8Data, // 文件的Uint8Array数据
},
"events": {
"onSave": onFileSave
},
"plugins": {
"pluginsData": []
}
}
};
}
const OnWindowReSize = () => {
let wrapEl = document.getElementById("editor-container");
if (wrapEl) {
wrapEl.style.height = screen.availHeight + "px";
window.scrollTo(0, -1);
wrapEl.style.height = window.innerHeight + "px";
}
}
const getFileExtension = () => {
const filename = file.name.split(/[\\/]/).pop();
return filename.substring(filename.lastIndexOf('.') + 1).toLowerCase() || '';
}
const onFileSave = (data) => {
console.log('保存数据:', data)
// 在这里实现你的保存逻辑
// 例如:发送到服务器、下载文件等
}
return (
<div id="editor-container">
<div id="iframeEditor"></div>
</div>
)
}
export default Editor
- main.jsx 配置以下内容
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import App from './App.jsx'
createRoot(document.getElementById('root')).render(
// 注意:StrictMode会导致Spire编辑器渲染两次,建议禁用
<App />
)
- vite.config.js 中自定义配置Vite 端口
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
export default defineConfig({
server: {
host: '0.0.0.0',
port: 8050
},
plugins: [react()],
})
项目运行
命令执行:npm run dev 运行配置后的项目,浏览器访问http://localhost:8050/ 。

通过”选择文件“上传本地文档打开编辑。

通过“文件“—>”下载为” 可以下载或转换为其它格式。

完整示例下载
常见问题及解答
1. 创建项目时报错 npm或 node 版本不兼容:
可能是 Node.js 版本 问题,建议安装较新版本。
2. 依赖安装失败:
- 清理缓存;
- 删除 node_modules 和 package-lock.json,并重新安装;
- 确保项目目录正确,不要在 Vite 项目外执行安装。
3. Spire.OfficeJS 文件无法加载:
- 确认你已将 web 文件夹复制到对应目录下;
- 确保项目运行时服务器可访问 public 下资源。
申请临时授权
如果您需要去除生成文档中的评估提示或解除功能限制,请联系我们获取有效期 30 天的临时许可证。







