Skip to main content

超级赛亚人电子表与 Kaioken

Kaioken 是一个用于构建用户界面的 JavaScript 库。

¥Kaioken is a JavaScript library for building user interfaces.

SheetJS 是一个用于从电子表格读取和写入数据的 JavaScript 库。

¥SheetJS is a JavaScript library for reading and writing data from spreadsheets.

该演示使用 Kaioken 和 SheetJS 来处理和生成电子表格。我们将探讨如何在 "凯欧奥尼茨"(Kaioken 组件)中加载 SheetJS,并比较常见的状态模型和数据流策略。

¥This demo uses Kaioken and SheetJS to process and generate spreadsheets. We'll explore how to load SheetJS in "Kaioponents" (Kaioken components) and compare common state models and data flow strategies.

该演示重点介绍 Kaioken 概念。其他演示涵盖一般部署:

¥This demo focuses on Kaioken concepts. Other demos cover general deployments:

Kaioken 支持被认为是实验性的。

优秀的开源软件会随着用户测试和报告而不断成长。任何问题都应报告给 Kaioken 项目以进行进一步诊断。

¥Great open source software grows with user tests and reports. Any issues should be reported to the Kaioken project for further diagnosis.

安装

¥Installation

"构架" 章节 涵盖了 Yarn 和其他包管理器的安装。

¥The "Frameworks" section covers installation with Yarn and other package managers.

该库可以直接从 JS 或 JSX 代码导入:

¥The library can be imported directly from JS or JSX code with:

import { read, utils, writeFile } from 'xlsx';

内部状态

¥Internal State

各种 SheetJS API 可处理各种数据形状。首选状态取决于应用。

¥The various SheetJS APIs work with various data shapes. The preferred state depends on the application.

对象数组

¥Array of Objects

通常,一些用户会创建一个电子表格,其中包含应加载到网站中的源数据。该工作表将具有已知的列。

¥Typically, some users will create a spreadsheet with source data that should be loaded into the site. This sheet will have known columns.

状态

¥State

示例 总统表 有一个标题行,其中包含 "名称" 和 "索引" 列。自然的 JS 表示是每行一个对象,使用第一行中的值作为键:

¥The example presidents sheet has one header row with "Name" and "Index" columns. The natural JS representation is an object for each row, using the values in the first rows as keys:

SpreadsheetState

pres.xlsx data

[
{ Name: "Bill Clinton", Index: 42 },
{ Name: "GeorgeW Bush", Index: 43 },
{ Name: "Barack Obama", Index: 44 },
{ Name: "Donald Trump", Index: 45 },
{ Name: "Joseph Biden", Index: 46 }
]

Kaioken useState[^1] 钩子可以配置状态:

¥The Kaioken useState[^1] hook can configure the state:

import { useState } from 'kaioken';

/* the kaioponent state is an array of objects */
const [pres, setPres] = useState<any[]>([]);

当提前知道电子表格标题行时,可以进行行输入:

¥When the spreadsheet header row is known ahead of time, row typing is possible:

import { useState } from 'kaioken';

interface President {
Name: string;
Index: number;
}

/* the kaioponent state is an array of presidents */
const [pres, setPres] = useState<President[]>([]);

这些类型信息丰富。它们不强制工作表包含命名列。应使用运行时数据验证库来验证数据集。

¥The types are informative. They do not enforce that worksheets include the named columns. A runtime data validation library should be used to verify the dataset.

当事先不知道文件头时,应使用 any

¥When the file header is not known in advance, any should be used.

更新状态

¥Updating State

SheetJS readsheet_to_json 函数简化了状态更新。它们最适合用于 useAsync[^2]、useEffect[^3] 和 useCallback[^4] 钩子的函数体中。

¥The SheetJS read and sheet_to_json functions simplify state updates. They are best used in the function bodies of useAsync[^2], useEffect[^3] and useCallback[^4] hooks.

当用户加载站点时,useEffect 钩子可以下载并更新状态:

¥A useEffect hook can download and update state when a person loads the site:

import { useEffect } from 'kaioken';
import { read, utils } from 'xlsx';

/* Fetch and update the state once */
useEffect(() => { (async() => {
/* Download from https://xlsx.nodejs.cn/pres.numbers */
const f = await fetch("https://xlsx.nodejs.cn/pres.numbers");
const ab = await f.arrayBuffer();

/* parse */
const wb = read(ab);

/* generate array of presidents from the first worksheet */
const ws = wb.Sheets[wb.SheetNames[0]]; // get the first worksheet
const data: President[] = utils.sheet_to_json<President>(ws); // generate objects

/* update state */
setPres(data); // update state
})(); }, []);

对于此特定用例(页面加载时获取一次文件),强烈建议使用 useAsync 钩子:

¥For this particular use case (fetching a file once when the page loads), it is strongly recommended to use the useAsync hook:

import { useAsync } from 'kaioken';
import { read, utils } from 'xlsx';

/* Fetch and parse the file */
const { data: pres, loading, error } = useAsync<President[]>(async() => {
/* Download from https://xlsx.nodejs.cn/pres.numbers */
const f = await fetch("https://xlsx.nodejs.cn/pres.numbers");
const ab = await f.arrayBuffer();

/* parse */
const wb = read(ab);

/* generate array of presidents from the first worksheet */
const ws = wb.Sheets[wb.SheetNames[0]]; // get the first worksheet
const data: President[] = utils.sheet_to_json<President>(ws); // generate objects

/* return data -- essentially setting state */
return data;
}, []);

SheetJS 用户报告称,与传统的 useEffect 柔术相比,使用 useAsync 模式更容易推断数据提取。

¥SheetJS users reported that it is easier to reason about data fetching using the useAsync pattern compared to the traditional useEffect jujutsu.

渲染数据

¥Rendering Data

Kaioponents 通常从对象数组中呈现 HTML 表格。TR 表行元素通常是通过映射状态数组生成的,如示例 JSX 代码所示:

¥Kaioponents typically render HTML tables from arrays of objects. The TR table row elements are typically generated by mapping over the state array, as shown in the example JSX code:

Example JSX for displaying arrays of objects
<table>
{/* The `thead` section includes the table header row */}
<thead><tr><th>Name</th><th>Index</th></tr></thead>
{/* The `tbody` section includes the data rows */}
<tbody>
{/* generate row (TR) for each president */}
{pres.map(row => (
<tr>
{/* Generate cell (TD) for name / index */}
<td>{row.Name}</td>
<td>{row.Index}</td>
</tr>
))}
</tbody>
</table>

导出数据

¥Exporting Data

writeFilejson_to_sheet 功能简化了数据导出。它们最适合用于连接到按钮或其他元素的 useCallback[^5] 钩子的功能体中。

¥The writeFile and json_to_sheet functions simplify exporting data. They are best used in the function bodies of useCallback[^5] hooks attached to button or other elements.

当用户单击按钮时,回调可以生成本地文件:

¥A callback can generate a local file when a user clicks a button:

import { useCallback } from 'kaioken';
import { utils, writeFile } from 'xlsx';

/* get state data and export to XLSX */
const exportFile = useCallback(() => {
/* generate worksheet from state */
const ws = utils.json_to_sheet(pres);
/* create workbook and append worksheet */
const wb = utils.book_new();
utils.book_append_sheet(wb, ws, "Data");
/* export to XLSX */
writeFile(wb, "SheetJSKaiokenAoO.xlsx");
}, [pres]);

完整的 Kaioponent

¥Complete Kaioponent

这个完整的 Kaioponent 示例获取一个测试文件并在 HTML 表中显示数据。单击导出按钮后,回调将导出文件。

¥This complete Kaioponent example fetches a test file and displays the data in a HTML table. When the export button is clicked, a callback will export a file.

使用 useAsyncuseEffectuseState 的示例如下所示:

¥Examples using useAsync and useEffect with useState are shown below:

src/SheetJSKaiokenAoO.tsx
import { useAsync, useCallback } from "kaioken";
import { read, utils, writeFileXLSX } from 'xlsx';

interface President {
Name: string;
Index: number;
}

export default function SheetJSKaiokenAoO() {
/* Fetch and parse the file */
const { data: pres, loading, error } = useAsync<President[]>(async() => {
const f = await (await fetch("https://xlsx.nodejs.cn/pres.xlsx")).arrayBuffer();
const wb = read(f); // parse the array buffer
const ws = wb.Sheets[wb.SheetNames[0]]; // get the first worksheet
const data = utils.sheet_to_json<President>(ws); // generate objects
return data;
}, []);

/* get state data and export to XLSX */
const exportFile = useCallback(() => {
const ws = utils.json_to_sheet(pres!);
const wb = utils.book_new();
utils.book_append_sheet(wb, ws, "Data");
writeFileXLSX(wb, "SheetJSKaiokenAoO.xlsx");
}, [pres]);

return (

<table><thead><tr><th>Name</th><th>Index</th></tr></thead>

<tbody>
{ /* generate row for each president */
pres && pres.map(pres => (<tr>
<td>{pres.Name}</td>
<td>{pres.Index}</td>
</tr>))
}
{ /* loading message */
!pres && loading && ( <tr><td colSpan="2">Loading ...</td></tr> )
}
{ /* error message */
!pres && !loading && ( <tr><td colSpan="2">{error.message}</td></tr> )
}
</tbody>

<tfoot><td colSpan={2}>
<button onclick={exportFile}>Export XLSX</button>
</td></tfoot></table>

);
}

通常,JSX 结构使用三元表达式来测试状态:

¥Typically the JSX structure uses ternary expressions for testing status:

const { data, loading, error } = useAsync(async() => { /* ... */ });

return ( <>
{ data ? (
<b>Data is loaded</b>
) : loading ? (
<b>Loading ...</b>
) : (
<b>{error.message}</b>
)
}
</> );

为了清楚起见,加载和错误消息是分开的。

¥For clarity, the loading and error messages are separated.

How to run the example (click to hide)
测试部署

本 demo 在以下环境下进行了测试:

¥This demo was tested in the following environments:

KaiokenViteJS日期
0.17.05.2.112024-05-21
  1. 创建一个新站点。

    ¥Create a new site.

npm create vite@latest sheetjs-kaioken -- --template vanilla-ts
cd sheetjs-kaioken
npm add --save kaioken
npm add --save vite-plugin-kaioken -D
  1. 创建一个新文件 vite.config.ts,内容如下:

    ¥Create a new file vite.config.ts with the following content:

vite.config.ts (create new file)
import { defineConfig } from "vite"
import kaioken from "vite-plugin-kaioken"

export default defineConfig({
plugins: [kaioken()],
})
  1. 编辑 tsconfig.json 并在 compilerOptions 中添加 "jsx": "preserve"

    ¥Edit tsconfig.json and add "jsx": "preserve" within compilerOptions:

tsconfig.json (add highlighted line)
{
"compilerOptions": {
"jsx": "preserve",
  1. src/main.ts 替换为以下代码块:

    ¥Replace src/main.ts with the following codeblock:

src/main.ts (replace contents)
import { mount } from "kaioken";
import App from "./SheetJSKaiokenAoO";

const root = document.getElementById("app");
mount(App, root!);
  1. 使用原始代码示例创建一个新文件 src/SheetJSKaiokenAoO.tsx

    ¥Create a new file src/SheetJSKaiokenAoO.tsx using the original code example.

  2. 安装 SheetJS 依赖并启动开发服务器:

    ¥Install the SheetJS dependency and start the dev server:

npm i --save https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz
npm run dev
  1. 打开网络浏览器并访问显示的 URL (http://localhost:5173)

    ¥Open a web browser and access the displayed URL (http://localhost:5173)

该页面将刷新并显示带有“导出”按钮的表格。单击该按钮,页面将尝试下载 SheetJSKaiokenAoO.xlsx

¥The page will refresh and show a table with an Export button. Click the button and the page will attempt to download SheetJSKaiokenAoO.xlsx.

  1. 建立网站:

    ¥Build the site:

npm run build

生成的站点将放置在 dist 文件夹中。

¥The generated site will be placed in the dist folder.

  1. 启动本地网络服务器:

    ¥Start a local web server:

npx http-server dist

使用 Web 浏览器访问显示的 URL(通常为 http://localhost:8080)并测试该页面。

¥Access the displayed URL (typically http://localhost:8080) with a web browser and test the page.

页面加载时,应用将获取 https://xlsx.nodejs.cn/pres.xlsx 并在表中显示第一个工作表中的数据。"导出 XLSX" 按钮将生成一个可以在电子表格编辑器中打开的工作簿。

¥When the page loads, the app will fetch https://xlsx.nodejs.cn/pres.xlsx and display the data from the first worksheet in a TABLE. The "Export XLSX" button will generate a workbook that can be opened in a spreadsheet editor.

HTML

对象数组方法的主要缺点是列的特定性质。对于更一般的用途,传递数组的数组是可行的。但是,这不能很好地处理合并单元格!

¥The main disadvantage of the Array of Objects approach is the specific nature of the columns. For more general use, passing around an Array of Arrays works. However, this does not handle merge cells[^6] well!

sheet_to_html 函数生成可识别合并和其他工作表功能的 HTML。要将表添加到页面,当前建议涉及设置 refinnerHTML 属性。

¥The sheet_to_html function generates HTML that is aware of merges and other worksheet features. To add the table to the page, the current recommendation involves setting the innerHTML attribute of a ref.

在此示例中,kaioponent 将 ref 连接到 DIV 容器。导出时,可以使用 table_to_book 解析第一个 TABLE 子元素以生成工作簿对象。

¥In this example, the kaioponent attaches a ref to the DIV container. During export, the first TABLE child element can be parsed with table_to_book to generate a workbook object.

src/SheetJSKaiokenHTML.tsx
import { useCallback, useEffect, useRef } from "kaioken";
import { read, utils, writeFileXLSX } from 'xlsx';

export default function SheetJSKaiokenHTML() {
/* the ref is used in export */
const tbl = useRef<Element>(null);

/* Fetch and update the state once */
useEffect(() => { (async() => {
const f = await (await fetch("https://xlsx.nodejs.cn/pres.xlsx")).arrayBuffer();
const wb = read(f); // parse the array buffer
const ws = wb.Sheets[wb.SheetNames[0]]; // get the first worksheet
const data = utils.sheet_to_html(ws); // generate HTML
if(tbl.current == null) return;
tbl.current.innerHTML = data;
})(); }, []);

/* get live table and export to XLSX */
const exportFile = useCallback(() => {
const elt = tbl.current!.getElementsByTagName("TABLE")[0];
const wb = utils.table_to_book(elt);
writeFileXLSX(wb, "SheetJSKaiokenHTML.xlsx");
}, [tbl]);

return ( <>
<button onclick={exportFile}>Export XLSX</button>
<div ref={tbl}/>
</> );
}
How to run the example (click to hide)
测试部署

本 demo 在以下环境下进行了测试:

¥This demo was tested in the following environments:

KaiokenViteJS日期
0.17.05.2.112024-05-21
  1. 创建一个新站点。

    ¥Create a new site.

npm create vite@latest sheetjs-kaioken -- --template vanilla-ts
cd sheetjs-kaioken
npm add --save kaioken
npm add --save vite-plugin-kaioken -D
  1. 创建一个新文件 vite.config.ts,内容如下:

    ¥Create a new file vite.config.ts with the following content:

vite.config.ts (create new file)
import { defineConfig } from "vite"
import kaioken from "vite-plugin-kaioken"

export default defineConfig({
plugins: [kaioken()],
})
  1. 编辑 tsconfig.json 并在 compilerOptions 中添加 "jsx": "preserve"

    ¥Edit tsconfig.json and add "jsx": "preserve" within compilerOptions:

tsconfig.json (add highlighted line)
{
"compilerOptions": {
"jsx": "preserve",
  1. src/main.ts 替换为以下代码块:

    ¥Replace src/main.ts with the following codeblock:

src/main.ts (replace contents)
import { mount } from "kaioken";
import App from "./SheetJSKaiokenHTML";

const root = document.getElementById("app");
mount(App, root!);
  1. 使用原始代码示例创建一个新文件 src/SheetJSKaiokenHTML.tsx

    ¥Create a new file src/SheetJSKaiokenHTML.tsx using the original code example.

  2. 安装 SheetJS 依赖并启动开发服务器:

    ¥Install the SheetJS dependency and start the dev server:

npm i --save https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz
npm run dev
  1. 打开网络浏览器并访问显示的 URL (http://localhost:5173)

    ¥Open a web browser and access the displayed URL (http://localhost:5173)

该页面将刷新并显示带有“导出”按钮的表格。单击该按钮,页面将尝试下载 SheetJSKaiokenHTML.xlsx

¥The page will refresh and show a table with an Export button. Click the button and the page will attempt to download SheetJSKaiokenHTML.xlsx.

  1. 建立网站:

    ¥Build the site:

npm run build

生成的站点将放置在 dist 文件夹中。

¥The generated site will be placed in the dist folder.

  1. 启动本地网络服务器:

    ¥Start a local web server:

npx http-server dist

使用 Web 浏览器访问显示的 URL(通常为 http://localhost:8080)并测试该页面。

¥Access the displayed URL (typically http://localhost:8080) with a web browser and test the page.

页面加载时,应用将获取 https://xlsx.nodejs.cn/pres.xlsx 并在表中显示第一个工作表中的数据。"导出 XLSX" 按钮将生成一个可以在电子表格编辑器中打开的工作簿。

¥When the page loads, the app will fetch https://xlsx.nodejs.cn/pres.xlsx and display the data from the first worksheet in a TABLE. The "Export XLSX" button will generate a workbook that can be opened in a spreadsheet editor.

[^1]: 请参阅 Kaioken 文档中的 useState

¥See useState in the Kaioken documentation.

[^2]: 请参阅 Kaioken 文档中的 useAsync

¥See useAsync in the Kaioken documentation.

[^3]: 请参阅 Kaioken 文档中的 useEffect

¥See useEffect in the Kaioken documentation.

[^4]: 请参阅 Kaioken 文档中的 useCallback

¥See useCallback in the Kaioken documentation.

[^5]: 请参阅 Kaioken 文档中的 useCallback

¥See useCallback in the Kaioken documentation.

[^6]: 详细信息请参见 "合并单元格" 于 "SheetJS 数据模型"

¥See "Merged Cells" in "SheetJS Data Model" for more details.