一个小项目向大前端的演进

505 阅读3分钟

背景介绍

最开始这是一个用于对比两个xlsx文件内容差异的小工具,随着想法不断迸发,开始了去往大前端的征途。。。
以下环节记录了逐步演进的过程。 项目地址:github.com/jarlion/xls…

目的

对比两个xlsx文件对比他们的内容差异。

初始项目

使用 nodejs 脚本方式实现

初始化

  • 新建一个项目目录(xlxs-comparer)
  • 创建基本配置
npm init

按照提示填写项目信息,最终项目目录下生成package.json

使用 TS 作为开发语言(可选)

  • 全局安装
npm i -g typescript

这时就可以在命令行工具使用 tsc 命令。

  • 生成配置(tsconfig.json)
tsc --init

项目目录下生成了tsconfig.json
打开tsconfig.json根据需要修改一下

// 修改目标可以使用新的语法或者api,例如:String.replaceAll()
"target": "es2016",
// 设置源码路径
"rootDir": "./src",
// 方便调试
"sourceMap": true,
// ts 转化后的 js 路径,修改完这里 package.json 的 main 入口可能也需要修改
"outDir": "./bin/",
  • 为了方便我在 package.json,添加个脚本
    观察 *.ts 文件变化自动编译为 *.js
"scripts": {
    "tsc-w": "tsc -w",
    ...
}

添加完成把这个脚本跑起来就可以开始写代码了。

使用标准的命令方式(可选)

如果不选,可以直接 node xxx.js 启动入口文件;
选了就标准规范一些。

  • 安装
npm i -S command
  • 使用
    例如 index.json 代码如下,有疑问可以查看 commander 教程。
import { Command } from 'commander';
import { init } from './main';

/**
 * 命令入口
 */
const program = new Command();

program
    .name('xlsx-comparer')
    .alias('xc')
    .description('xlsx 比较')
    .version('0.0.2')

    .option('-p, --port <number>', '网页端口', '8100')
    .option('-s, --source <string>', '源文件 *.xlsx', ',')
    .option('-t, --target <string>', '目标文件 *.xlsx')
    .option('-k, --key <string>', '主键名 num|str')
    .option('-i, --include-columns <string[,]>', '包含的列 string[]', '')
    .option('-e, --excludeColumns <string[,]>', '排除的列 string[]', '')
    .option('-h, --hide', '隐藏模式, 此模式不显示没有差异的列', false)
    .option('-w, --web', '是否启动 Web 服务器', false)
    .action((options) => {
        init(options);
    });

program.parse();

读取本地文件

为了 ts 能很好的提示,安装一下声明

npm i -D @types/nodes

nodejsfs模块,有相关 api 例如:

import { copyFileSync, existsSync, mkdirSync, readdirSync, readFileSync, rmdirSync, statSync, unlinkSync, WriteFileOptions, writeFileSync } from "fs";

需要注意的是这些 api 使用的路径要处理好。

  • 使用绝对路径(这个妥妥的),但是写起来比较麻烦,不够灵活,例如:D:\\workspaces\\nodejs\\xlxs-comparer\\test\\a.xlsx
  • 使用相对路径:
  1. process.cwd() 当前项目的路径(推荐)
  2. __dirname 当前 js 文件路径(慎用)。别忘了我们的源文件路径 src 和项目路径 bin 是不一样的。 由于原生的 api 用起来有点繁琐自己封装了常用的文件操作方法:src/utils/osUtil.ts

解析 XLSX 文件

使用 xlsx 库可以读取 *.xlsx 并且解析为 json 数据

  • 安装
npm i -S xlsx
  • 使用
    简单封装了读取方法,示例: src/utils/xlsxUtil.ts
    还可以自定义表头等等,在此不展开,具体使用请参考 xlsx 使用教程。
import { readFile, Sheet2JSONOpts, utils } from 'xlsx';

export function readXlsx<R>(path: string, sheetIndex: number = 0, opts?: Sheet2JSONOpts | undefined): R[] {

    const wb = readFile(path);
    const sheetName: string = wb.SheetNames[sheetIndex];
    if (!wb.Sheets[sheetName]) {
        console.error(`> !!! 找不到 Sheet ${sheetName}`);
        return [];
    }
    const sheet = wb.Sheets[sheetName];
    const rows = utils.sheet_to_json<R>(sheet, opts);
    return rows;
}

一阵捣鼓之后,两个 xlsx 文件对比完成了。
添加一个命令到 package.json

"scripts": {
    ...
    "xc": "node ./bin/server/index.js xc -w -s test/a.xlsx -t test/b.xlsx -k A -i B,C",
}

运行之,很好,但是不完美。
因为对比结果是有了,该如何反馈用户?这里出现了分岔:
1)把结果输出文档:例如直接写 *.txt,*.json,*.xlsx,完结散花。 2)继续完成用户体验,见下文。

前后端分离

上文说到,已经得到 xlsx 对比结果,能不能直观的展示给用户呢?
答案是肯定的 nodejs 就是个服务端,跑起来,把生成个 html ,用浏览器打开,完美。
这就是传统 (原始) 的服务端渲染。

WEB 服务端

使用 express 吧,使用方便(教程丰富)

  • 安装
npm i -S express
npm i -D @types/express
  • 使用
    目前只是用作 Web 服务器,怎么简单怎么来,例如:src\server\Web.ts
import express from 'express';
import { Server } from 'http';

/**
 * 网页服务器
 * 用于回显对比结果
 */
export class Web {
    app!: Server;
    constructor(root: string, port = 8100, callback?: Function) {
        this.app = express()
            .use(express.static(root)).listen(port, () => {
                console.log(`-> web on http://localhost:${port}`);
                if (callback) callback();
            });
    }
}

PS:为了防止端口冲突,port 改为传入,从

Web 客户端

新建一个 src/client/目录,客户端文件都放进这里。

  • 客户端构建
    这里可以选择使用 vue,react 等生客户端文件,怎么简单怎么来,这里不做展开。
    使用自己写的 src/utils/htmlUtil.ts 辅助生成主文件 main.html
    样式文件,存入 src/client/css/
    主要处理页面显示效果。
  • 部署
    src/client/ 下的静态文件同步到 bin/client/ 目录。
  • 启动服务端
// 启动 web 服务器
    if (web) {
        new Web(root,
            Number(port),
            () =>
                openUrl(`http://localhost:${port}/main.html`)
        );
    }
  • 打开浏览器访问
    noedjs 调用命令打开浏览器
/**
 * 使用浏览器打开链接
 * @param url 
 */
export function openUrl(url: string): void {
    // 判断平台
    switch (process.platform) {
        // Mac 使用open
        case "darwin":
            spawn('open', [url]);
            break;
        // Windows使用start
        case "win32":
            exec(`start ${url}`);
            // TODO 环境变量缺少 system32/
            // console.log(process.env.PATH);
            // spawn('start', [url]).on('error', function (err) { console.error(`!!! ${err.stack}`); });
            break;
        // Linux等使用xdg-open
        default:
            spawn('xdg-open', [url]);
    }
};

下一步

封装 npm 类库

在线编辑

封装 Elctron 客户端

封装 RN 移动端