超级赛亚人电子表与 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 项目以进行进一步诊断。
¥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:
Spreadsheet | State |
---|---|
|
Kaioken useState
[^1] 钩子可以配置状态:
¥The Kaioken useState
[^1] hook can configure the state:
- JavaScript
- TypeScript
import { useState } from 'kaioken';
/* the kaioponent state is an array of objects */
const [pres, setPres] = useState([]);
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 read
和 sheet_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:
- JavaScript
- TypeScript
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 objects from first worksheet */
const ws = wb.Sheets[wb.SheetNames[0]]; // get the first worksheet
const data = utils.sheet_to_json(ws); // generate objects
/* update state */
setPres(data); // update state
})(); }, []);
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:
<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
writeFile
和 json_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.
使用 useAsync
和 useEffect
与 useState
的示例如下所示:
¥Examples using useAsync
and useEffect
with useState
are shown below:
- useAsync
- useEffect + useState
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.
import { useCallback, useEffect, useState } from "kaioken";
import { read, utils, writeFileXLSX } from 'xlsx';
interface President {
Name: string;
Index: number;
}
export default function SheetJSKaiokenAoO() {
/* the kaioponent state is an array of presidents */
const [pres, setPres] = useState<President[]>([]);
/* 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_json<President>(ws); // generate objects
setPres(data); // update state
})(); }, []);
/* 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.map(pres => (<tr>
<td>{pres.Name}</td>
<td>{pres.Index}</td>
</tr>))
}
</tbody>
<tfoot><td colSpan={2}>
<button onclick={exportFile}>Export XLSX</button>
</td></tfoot></table>
);
}
How to run the example (click to hide)
- ViteJS
本 demo 在以下环境下进行了测试:
¥This demo was tested in the following environments:
Kaioken | ViteJS | 日期 |
---|---|---|
0.17.0 | 5.2.11 | 2024-05-21 |
-
创建一个新站点。
¥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
-
创建一个新文件
vite.config.ts
,内容如下:¥Create a new file
vite.config.ts
with the following content:
import { defineConfig } from "vite"
import kaioken from "vite-plugin-kaioken"
export default defineConfig({
plugins: [kaioken()],
})
-
编辑
tsconfig.json
并在compilerOptions
中添加"jsx": "preserve"
:¥Edit
tsconfig.json
and add"jsx": "preserve"
withincompilerOptions
:
{
"compilerOptions": {
"jsx": "preserve",
-
将
src/main.ts
替换为以下代码块:¥Replace
src/main.ts
with the following codeblock:
import { mount } from "kaioken";
import App from "./SheetJSKaiokenAoO";
const root = document.getElementById("app");
mount(App, root!);
-
使用原始代码示例创建一个新文件
src/SheetJSKaiokenAoO.tsx
。¥Create a new file
src/SheetJSKaiokenAoO.tsx
using the original code example. -
安装 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
-
打开网络浏览器并访问显示的 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
.
-
建立网站:
¥Build the site:
npm run build
生成的站点将放置在 dist
文件夹中。
¥The generated site will be placed in the dist
folder.
-
启动本地网络服务器:
¥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。要将表添加到页面,当前建议涉及设置 ref
的 innerHTML
属性。
¥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.
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)
- ViteJS
本 demo 在以下环境下进行了测试:
¥This demo was tested in the following environments:
Kaioken | ViteJS | 日期 |
---|---|---|
0.17.0 | 5.2.11 | 2024-05-21 |
-
创建一个新站点。
¥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
-
创建一个新文件
vite.config.ts
,内容如下:¥Create a new file
vite.config.ts
with the following content:
import { defineConfig } from "vite"
import kaioken from "vite-plugin-kaioken"
export default defineConfig({
plugins: [kaioken()],
})
-
编辑
tsconfig.json
并在compilerOptions
中添加"jsx": "preserve"
:¥Edit
tsconfig.json
and add"jsx": "preserve"
withincompilerOptions
:
{
"compilerOptions": {
"jsx": "preserve",
-
将
src/main.ts
替换为以下代码块:¥Replace
src/main.ts
with the following codeblock:
import { mount } from "kaioken";
import App from "./SheetJSKaiokenHTML";
const root = document.getElementById("app");
mount(App, root!);
-
使用原始代码示例创建一个新文件
src/SheetJSKaiokenHTML.tsx
。¥Create a new file
src/SheetJSKaiokenHTML.tsx
using the original code example. -
安装 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
-
打开网络浏览器并访问显示的 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
.
-
建立网站:
¥Build the site:
npm run build
生成的站点将放置在 dist
文件夹中。
¥The generated site will be placed in the dist
folder.
-
启动本地网络服务器:
¥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.