使用 Webpack 打包表
Webpack 是一个用于生成静态站点的现代构建工具。它有一个强大的 JavaScript 驱动的插件系统 [^1]
¥Webpack is a modern build tool for generating static sites. It has a robust JavaScript-powered plugin system[^1]
SheetJS 是一个用于从电子表格读取和写入数据的 JavaScript 库。
¥SheetJS is a JavaScript library for reading and writing data from spreadsheets.
该演示使用 Webpack 和 SheetJS 从电子表格中提取数据并在 HTML 表格中显示内容。我们将探讨如何在 Webpack 5 资源插件中加载 SheetJS 并生成在网页中使用的数据。
¥This demo uses Webpack and SheetJS to pull data from a spreadsheet and display the content in an HTML table. We'll explore how to load SheetJS in a Webpack 5 Asset Plugin and generate data for use in webpages.
"Webpack 5 演示" 创建了一个由 XLSX 电子表格支持的完整网站。
¥The "Webpack 5 Demo" creates a complete website powered by a XLSX spreadsheet.
该演示涵盖静态资源导入。为了在浏览器中处理文件,"打包器" 演示 包含在浏览器脚本中导入 SheetJS 库的示例。
¥This demo covers static asset imports. For processing files in the browser, the "Bundlers" demo includes an example of importing the SheetJS library in a browser script.
Webpack 5 资源模块
¥Webpack 5 Asset Module
Webpack 5 支持资源模块。通过一个特殊的选项,加载器将接收可以解析的 NodeJS 缓冲区。开发服务器甚至会在开发模式下查看文件并重新加载页面!
¥Webpack 5 supports asset modules. With a special option, the loader will receive NodeJS Buffers that can be parsed. The dev server will even watch the files and reload the page in development mode!
SheetJS NodeJS 模块 可以从 Webpack 加载器脚本导入。
¥The SheetJS NodeJS module can be imported from Webpack loader scripts.
下图描绘了练习册华尔兹:
¥The following diagram depicts the workbook waltz:
Webpack 配置
¥Webpack Config
Webpack 配置通常保存到 webpack.config.js
。
¥The Webpack configuration is normally saved to webpack.config.js
.
所需设置
¥Required Settings
module.rules
是控制模块合成的规则对象数组。[^2] 对于 SheetJS Webpack 集成,需要以下属性:
¥module.rules
is an array of rule objects that controls module synthesis.[^2]
For the SheetJS Webpack integration, the following properties are required:
-
test
描述规则是否相关。如果该属性是正则表达式,Webpack 将根据test
属性测试文件名。¥
test
describes whether the rule is relevant. If the property is a regular expression, Webpack will test the filename against thetest
property. -
use
列出了将处理与test
匹配的文件的加载器。加载器是使用加载器对象的loader
属性指定的。¥
use
lists the loaders that will process files matching thetest
. The loaders are specified using theloader
property of the loader object.
以下示例指示 Webpack 当文件名以 .numbers
或 .xls
或 .xlsx
或 .xlsb
结尾时使用 sheetjs-loader.js
脚本:
¥The following example instructs Webpack to use the sheetjs-loader.js
script
when the file name ends in .numbers
or .xls
or .xlsx
or .xlsb
:
// ...
module.exports = {
// ...
module: {
rules: [
{
/* `test` matches file extensions */
test: /\.(numbers|xls|xlsx|xlsb)$/,
/* use the loader script */
use: [ { loader: './sheetjs-loader' } ]
}
]
}
};
推荐设置
¥Recommended Settings
强烈建议启用其他 Webpack 功能:
¥It is strongly recommended to enable other Webpack features:
-
resolve.alias
定义路径别名。如果数据文件存储在一个文件夹中,则别名可确保每一页都可以引用使用相同名称的文件 [^3]。¥
resolve.alias
defines path aliases. If data files are stored in one folder, an alias ensures that each page can reference the files using the same name[^3]. -
devServer.hot
启用 "热模块更换"[^4],确保保存电子表格时页面将在开发模式下刷新。¥
devServer.hot
enables "hot module replacement"[^4], ensuring that pages will refresh in development mode when spreadsheets are saved.
以下示例指示 Webpack 将 ~
视为项目的根(因此 ~/data/pres.xlsx
引用数据文件夹中的 pres.xlsx
)并启用实时重新加载:
¥The following example instructs Webpack to treat ~
as the root of the project
(so ~/data/pres.xlsx
refers to pres.xlsx
in the data folder) and to enable
live reloading:
// ...
module.exports = {
// ...
resolve: {
alias: {
/* `~` root of the project */
"~": __dirname
}
},
// ...
/* enable live reloading in development mode */
devServer: { static: './dist', hot: true }
};
SheetJS 加载器
¥SheetJS Loader
SheetJS 加载器脚本必须保存到 Webpack 配置 (sheetjs-loader.js
) 中引用的脚本中。
¥The SheetJS loader script must be saved to the script referenced in the Webpack
configuration (sheetjs-loader.js
).
与 ViteJS 一样,Webpack 会将数据解释为 UTF-8 字符串。这会损坏二进制格式,包括 XLSX 和 XLS。要抑制此行为并指示 Webpack 传递 NodeJS Buffer
对象,加载器脚本必须导出设置为 true
[^5] 的 raw
属性。
¥As with ViteJS, Webpack will interpret data as
UTF-8 strings. This corrupts binary formats including XLSX and XLS. To suppress
this behavior and instruct Webpack to pass a NodeJS Buffer
object, the loader
script must export a raw
property that is set to true
[^5].
基本导出预计是加载函数。加载器接收文件字节作为缓冲区,可以使用 SheetJS read
方法 [^6] 对其进行解析。read
返回 SheetJS 工作簿对象 [^7]。
¥The base export is expected to be the loader function. The loader receives the
file bytes as a Buffer, which can be parsed with the SheetJS read
method[^6].
read
returns a SheetJS workbook object[^7].
此演示中的加载器将解析工作簿,提取第一个工作表,并使用 sheet_to_json
方法 [^8] 生成行对象数组:
¥The loader in this demo will parse the workbook, pull the first worksheet, and
generate an array of row objects using the sheet_to_json
method[^8]:
const XLSX = require("xlsx");
function loader(content) {
/* since `loader.raw` is true, `content` is a Buffer */
const wb = XLSX.read(content);
/* pull data from first worksheet */
var data = XLSX.utils.sheet_to_json(wb.Sheets[wb.SheetNames[0]]);
return `export default JSON.parse('${JSON.stringify(data)}')`;
}
/* ensure the function receives a Buffer */
loader.raw = true;
/* export the loader */
module.exports = loader;
资源导入
¥Asset Imports
可以使用该插件导入电子表格。假设 pres.xlsx
存储在 data
子文件夹中,则可以从任何脚本导入 ~/data/pres.xlsx
:
¥Spreadsheets can be imported using the plugin. Assuming pres.xlsx
is stored
in the data
subfolder, ~/data/pres.xlsx
can be imported from any script:
import data from '~/data/pres.xlsx';
/* `data` is an array of objects from data/pres.xlsx */
const elt = document.createElement('div');
elt.innerHTML = "<table><tr><th>Name</th><th>Index</th></tr>" +
data.map((row) => `<tr>
<td>${row.Name}</td>
<td>${row.Index}</td>
</tr>`).join("") +
"</table>";
document.body.appendChild(elt);
Webpack 5 演示
¥Webpack 5 Demo
该演示最后一次测试是在 2024 年 4 月 6 日,针对 Webpack 5.91.0
¥This demo was last tested on 2024 April 06 against Webpack 5.91.0
初始设置
¥Initial Setup
-
创建一个新的骨架项目:
¥Create a new skeleton project:
mkdir sheetjs-wp5
cd sheetjs-wp5
npm init -y
npm install webpack@5.91.0 webpack-cli@5.1.4 webpack-dev-server@5.0.4 --save
mkdir -p dist
mkdir -p src
mkdir -p data
-
安装 SheetJS NodeJS 模块:
¥Install the SheetJS NodeJS module:
npm i --save https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz
-
将以下内容保存到
dist/index.html
:¥Save the following to
dist/index.html
:
<!DOCTYPE html>
<html>
<head>
<title>SheetJS + Webpack 5</title>
</head>
<body>
<script src="main.js"></script>
</body>
</html>
-
将以下内容保存到
src/index.js
:¥Save the following to
src/index.js
:
import data from '~/data/pres.xlsx';
const elt = document.createElement('div');
elt.innerHTML = "<table><tr><th>Name</th><th>Index</th></tr>" +
data.map((row) => `<tr>
<td>${row.Name}</td>
<td>${row.Index}</td>
</tr>`).join("") +
"</table>";
document.body.appendChild(elt);
-
将以下内容保存到
webpack.config.js
:¥Save the following to
webpack.config.js
:
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
filename: 'main.js',
path: path.resolve(__dirname, 'dist'),
},
devServer: {
static: './dist',
hot: true,
},
resolve: {
alias: {
"~": __dirname
}
},
module: {
rules: [
{
test: /\.(numbers|xls|xlsx|xlsb)$/,
use: [ { loader: './sheetjs-loader' } ]
}
]
}
};
-
将以下内容保存到
sheetjs-loader.js
:¥Save the following to
sheetjs-loader.js
:
const XLSX = require("xlsx");
function loader(content) {
/* since `loader.raw` is true, `content` is a Buffer */
const wb = XLSX.read(content);
/* pull data from first worksheet */
var data = XLSX.utils.sheet_to_json(wb.Sheets[wb.SheetNames[0]]);
return `export default JSON.parse('${JSON.stringify(data)}')`;
}
/* ensure the function receives a Buffer */
loader.raw = true;
module.exports = loader;
-
下载 https://xlsx.nodejs.cn/pres.xlsx 并保存到
data
文件夹:¥Download https://xlsx.nodejs.cn/pres.xlsx and save to the
data
folder:
curl -L -o data/pres.xlsx https://xlsx.nodejs.cn/pres.xlsx
实时重载测试
¥Live Reload Test
-
在 Excel 等电子表格编辑器中打开测试文件
data/pres.xlsx
。¥Open the test file
data/pres.xlsx
in a spreadsheet editor like Excel. -
启动开发服务器:
¥Start the development server:
npx webpack serve --mode=development
终端将打印开发服务器的 URL:
¥The terminal will print URLs for the development server:
<i> [webpack-dev-server] Project is running at:
<i> [webpack-dev-server] Loopback: http://localhost:8080/
-
在网络浏览器中打开
Loopback
地址 (http://localhost:8080
)。¥Open the
Loopback
address (http://localhost:8080
) in a web browser.
它应该显示一个包含 "名称" 和 "索引" 列的总统表格
¥It should display a table of Presidents with "Name" and "Index" columns
-
向电子表格添加新行(将
A7
设置为 "SheetJS 开发",将B7
设置为 47)并保存文件。¥Add a new row to the spreadsheet (set
A7
to "SheetJS Dev" andB7
to 47) and save the file.
保存文件后,页面应自动刷新以显示新数据。
¥After saving the file, the page should automatically refresh with the new data.
静态站点测试
¥Static Site Test
-
停止 Webpack 并构建站点:
¥Stop Webpack and build the site:
npx webpack --mode=production
最终站点将放置在 dist
文件夹中。
¥The final site will be placed in the dist
folder.
-
启动本地 Web 服务器来托管
dist
文件夹:¥Start a local web server to host the
dist
folder:
npx http-server dist
该命令将打印 URL 列表。
¥The command will print a list of URLs.
-
打开上一步 (
http://localhost:8080
) 中打印的 URL 之一并确认显示相同的数据。¥Open one of the URLs printed in the previous step (
http://localhost:8080
) and confirm that the same data is displayed.
要验证该页面是否独立于电子表格,请对文件进行一些更改并保存。该页面不会自动更新。
¥To verify that the page is independent of the spreadsheet, make some changes to the file and save. The page will not automatically update.
要验证数据是否已添加到页面,请将 main.js
附加到 URL (http://localhost:8080/main.js
) 并查看源。来源将包括总统名称。它不会包含 SheetJS 库引用!
¥To verify that the data was added to the page, append main.js
to the URL
(http://localhost:8080/main.js
) and view the source. The source will include
president names. It will not include SheetJS library references!
[^1]: 请参阅 Webpack 文档中的 "插件"。
¥See "Plugins" in the Webpack documentation.
[^2]: 请参阅 Webpack 文档中的 module.rules
。
¥See module.rules
in the Webpack documentation.
[^3]: 请参阅 Webpack 文档中的 resolve.alias
。
¥See resolve.alias
in the Webpack documentation.
[^4]: 请参阅 Webpack 文档中的 "模块热更换"。
¥See "Hot Module Replacement" in the Webpack documentation.
[^5]: 请参阅 Webpack 文档中的 "生的" 加载器。
¥See "Raw" Loader in the Webpack documentation.
[^6]: 见 read
于 "读取文件"
[^7]: 见 "工作簿对象"
¥See "Workbook Object"
[^8]: 见 sheet_to_json
于 "实用工具"