Office.js 与框架无关,可与任何客户端 JavaScript 框架或库无缝配合工作。 无论是使用 React、Angular、Vue、Svelte 还是任何其他框架进行生成,集成模式都是相同的:确保在应用程序呈现之前 Office.js 初始化。
注意
还可以使用服务器端框架(如 ASP.NET、PHP 和 Java)来生成 Office 加载项,但本文未介绍它们。 本文重点介绍在浏览器中运行的客户端 JavaScript 框架。
本文介绍用于将 Office.js 与客户端 JavaScript 框架集成的通用模式、重要注意事项,并提供跨多个框架的示例。
提示
本文面向使用首选 JavaScript 框架从头开始创建 Office 加载项的开发人员,或将 Office.js 集成到现有框架项目中。 如果使用 Office 加载项或 Microsoft 365 代理工具包的 Yeoman 生成器,则这些工具已提供正确的 Office.js 配置。
先决条件
- 熟悉所选 JavaScript 框架。
- 基本了解 Office 加载项。请参阅 Office 外接程序平台概述。
- 安装用于包管理的 Node.js。
快速入门:通用模式
无论选择哪种框架,都使用以下模式。
- 在 HTML
<head>中引用 CDN 中的 Office.js。 - 调用
Office.onReady()并等待完成。 - Office.js 准备就绪后初始化框架。
// Universal pattern - works with any framework.
Office.onReady((info) => {
// Office.js is now ready.
// Initialize your framework here.
initializeYourFramework();
});
从 CDN 加载 Office.js
必须从 HTML 文件中的内容分发网络 (CDN) 引用 Office JavaScript API。 在 <head> HTML 页面的 部分中,在任何其他脚本标记或框架捆绑包引用之前添加以下<script>标记。
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>My Office Add-in</title>
<!-- Office.js must be loaded from CDN, not bundled -->
<script src="https://appsforoffice.microsoft.com/lib/1/hosted/office.js" type="text/javascript"></script>
<!-- Your framework bundle loads after Office.js -->
</head>
重要
- 从 CDN 加载 Office.js,并在 HTML 文件中引用它。 请勿在 JavaScript 或 TypeScript 代码中导入它。
- Office.js 引用必须出现在 节中
<head>,以确保在加载任何正文元素之前完全初始化 API。 - 不要将 Office.js 与应用程序代码捆绑在一起。 始终从 CDN 引用它。
有关引用 Office.js(包括预览 API 和备用 CDN 终结点)的详细信息,请参阅 引用 Office JavaScript API 库。
在 Office.onReady 之后初始化框架
将 Office.js 与任何框架集成的关键是在回调中 Office.onReady() 初始化应用程序。 此方法可确保在框架开始呈现之前完全初始化 Office.js。 此初始化非常重要,因为 Office.js 需要:
- 从 CDN 下载和缓存 API 库文件。
- 初始化 Office 运行时环境。
- 与 Office 应用程序建立通信。
如果框架在 Office.js 准备就绪之前呈现,则对 Office API 的调用会失败。 通过在 中 Office.onReady()初始化应用程序,可以保证应用程序代码运行时 Office.js 准备就绪。
示例
以下示例显示了跨不同框架的相同集成模式。 模式相同 - 只有框架的初始化方法更改。
React
// src/index.tsx
Office.onReady(() => {
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App />);
});
Angular
// src/main.ts
Office.onReady(() => {
platformBrowserDynamic()
.bootstrapModule(AppModule)
.catch(err => console.error(err));
});
Vue
// src/main.ts
Office.onReady(() => {
createApp(App).mount('#app');
});
苗条
// src/main.ts
Office.onReady(() => {
new App({ target: document.getElementById('app') });
});
没有框架的简单 JavaScript
// src/app.js
Office.onReady((info) => {
document.getElementById('run-button').onclick = run;
if (info.host === Office.HostType.Excel) {
console.log('Running in Excel');
}
});
在应用程序中使用 Office.js API
完成) Office.js 初始化 (Office.onReady() 后,可以在外接程序中的任何位置调用 Office API。 根据需要使用框架的生命周期挂钩或事件处理程序调用 Office API。
// React example: Call an Office JS API in the useEffect lifecycle hook.
import { useEffect, useState } from 'react';
function MyComponent() {
const [data, setData] = useState('');
useEffect(() => {
loadData();
}, []);
async function loadData() {
await Excel.run(async (context) => {
const range = context.workbook.getSelectedRange();
range.load('values');
await context.sync();
// Update component state with the data from Excel.
const value = range.values[0][0];
setData(value);
});
}
return <div>Selected cell: {data}</div>;
}
// Similar patterns for other frameworks:
// Angular: ngOnInit() { this.loadData(); }
// Vue: onMounted(() => { loadData(); })
// Svelte: onMount(() => { loadData(); })
TypeScript 支持
若要为 TypeScript 项目中 Office.js 启用 IntelliSense 和类型检查,请安装 DefinitelyTyped 中的类型定义。
npm install --save-dev @types/office-js
TypeScript 自动识别类型。 代码中不需要 import 语句,因为 Office.js 是从 CDN 全局加载的。
// TypeScript automatically recognizes Office types.
Office.onReady((info: Office.OfficeInfo) => {
if (info.host === Office.HostType.Excel) {
// TypeScript provides IntelliSense for Excel APIs.
}
});
有关详细信息,请参阅 引用 Office JavaScript API 库。
其他注意事项
加载指示器
如果要在初始化 Office.js 时显示加载指示器,请在调用 Office.onReady() 之前显示它,并在回调中隐藏它。
// Show loading indicator.
document.getElementById('loading')!.style.display = 'block';
Office.onReady((info) => {
// Hide loading indicator.
document.getElementById('loading')!.style.display = 'none';
// Initialize framework.
initializeYourFramework();
});
若要使用具有自己的加载状态的框架提供更好的用户体验,请使用可立即显示的简单 HTML/CSS 加载程序。 然后,让框架在装载后接管它。
对话 API 和组件生命周期
Office 对话框 API 在单独的浏览器窗口中打开页面。 此行为对框架应用程序有重要影响:
- 每个对话使用单独的框架实例创建新的 执行上下文 。
- 该对话框运行自己的应用程序代码副本。
- 必须在对话框页中调用
Office.onReady()。 - 主页和对话框窗口 不共享状态。
- 上下文之间 不共享 会话存储。
如果使用框架路由器导航到对话路由,请记住,对话窗口会创建应用程序的全新实例。 它不会重复使用现有实例。
// Main page - opens a dialog.
Office.context.ui.displayDialogAsync(
'https://localhost:3000/dialog-route',
{ height: 50, width: 50 },
(result) => {
if (result.status === Office.AsyncResultStatus.Succeeded) {
const dialog = result.value;
dialog.addEventHandler(Office.EventType.DialogMessageReceived, (arg) => {
// Handle message from dialog.
});
} else {
// Handle error opening the dialog.
console.error(result.error);
}
}
);
// Dialog page - must also call Office.onReady.
Office.onReady(() => {
// This is a separate framework instance.
initializeYourFramework();
});
历史记录 API 解决方法
Office.js 将默认 的 Window.history 方法和 replaceStatepushStatenull替换为 。 如果框架或路由器依赖于这些方法 (React路由器、Vue 路由器、Angular路由器和其他) 中常见的方法,则需要缓存和还原它们。
将此代码添加到 HTML 文件,包装 Office.js 脚本标记:
<head>
<!-- Cache history methods before Office.js loads -->
<script type="text/javascript">
window._historyCache = {
replaceState: window.history.replaceState,
pushState: window.history.pushState
};
</script>
<!-- Load Office.js -->
<script type="text/javascript" src="https://appsforoffice.microsoft.com/lib/1/hosted/office.js"></script>
<!-- Restore history methods after Office.js loads -->
<script type="text/javascript">
window.history.replaceState = window._historyCache.replaceState;
window.history.pushState = window._historyCache.pushState;
</script>
</head>
注意
仅当应用程序使用客户端路由 (React 路由器、Vue 路由器、Angular路由器和其他) 时,才需要此解决方法。 不带路由的静态应用程序不需要此解决方法。
在 Office 应用程序外部进行测试
可以使用浏览器开发人员工具开发和测试加载项的 UI,而无需旁加载到 Office 中。 此方法可在开发过程中实现更快的迭代,并更易于调试 UI 组件。
在 Office 应用程序) 外部的常规浏览器中 (打开加载项时, Office.onReady() 仍会执行,但会同时 null 解析主机和平台属性。
Office.onReady((info) => {
if (info?.host) {
console.log(`Running in ${info.host} on ${info.platform}`);
} else {
console.log('Running outside of Office (development mode)');
}
// Initialize your framework, regardless of whether the add-in is running inside or outside of Office.
initializeYourFramework();
});
生成工具和捆绑程序
新式 JavaScript 框架通常使用 Webpack、Vite、Rollup 或 esbuild 等生成工具。 配置生成时:
- 请勿在 JavaScript 或 TypeScript 代码中导入或捆绑 Office.js。
- 使用
<script>HTML 中的标记从 CDN 加载 Office.js。 - 将捆绑程序配置为视为
Office全局变量。
示例:使用 Vite 进行 TypeScript 配置
如果将 Vite 与 TypeScript 配合使用,则通常不需要为 Office.js 进行特殊的 Vite 配置。 包 @types/office-js 提供必要的类型定义。 但是,如果需要确保 Office.js 类型可用,请验证 :tsconfig.json
// tsconfig.json
{
"compilerOptions": {
"types": ["office-js"]
// ... your other compiler options ...
}
}
示例:Webpack 配置
// webpack.config.js
module.exports = {
externals: {
'office': 'Office'
}
};
默认情况下, Office 外接程序的 Yeoman 生成器 生成的外接程序项目包括正确的生成配置。
网络阻止和防火墙
如果网络筛选器、防火墙或浏览器扩展阻止 Office.js CDN, Office.onReady() 则永远不会解析。 考虑为网络策略可能阻止 CDN 的企业方案实现超时。
let officeInitialized = false;
// Set a timeout.
setTimeout(() => {
if (!officeInitialized) {
console.error('Office.js failed to initialize. Network may be blocking CDN.');
// Show error message to user.
}
}, 10000); // 10 second timeout
Office.onReady((info) => {
officeInitialized = true;
initializeYourFramework();
});
有关 CDN 注意事项的详细信息,请参阅 引用 Office JavaScript API 库。
特定于框架的区域或反应性问题
某些框架使用区域或反应系统来跟踪状态更改。 在极少数情况下,Office API 调用不会触发 UI 更新,因为它们在框架的更改检测区域之外运行。
Angular:如果在调用 Office API 后 UI 未更新,请将代码包装在 中NgZone.run():
import { NgZone } from '@angular/core';
constructor(private zone: NgZone) {}
async loadDataFromExcel() {
let cellValue: string;
// Make Office API call
await Excel.run(async (context) => {
const range = context.workbook.getSelectedRange();
range.load('values');
await context.sync();
cellValue = range.values[0][0];
});
// Update Angular component state inside zone
this.zone.run(() => {
this.myData = cellValue;
});
}