docs.sheetjs.com/docz/docs/03-demos/04-static/05-vitejs.md
2023-04-27 05:12:19 -04:00

7.0 KiB

title pagination_prev pagination_next sidebar_custom_props
ViteJS demos/net/index demos/mobile/index
type
bundler

import current from '/version.js';

:::note

This demo covers static asset imports. For processing files in the browser, the "Bundlers" demo includes an example.

:::

Loaders

ViteJS supports static asset imports, but the default raw loader interprets data as UTF-8 strings. This corrupts binary formats like XLSX and XLS, but a custom loader can override the default behavior.

:::note Recommendation

For simple tables of data, "Pure Data Loader" is strongly recommended. The heavy work is performed at build time and the generated site only includes the raw data.

For more complex parsing or display logic, "Base64 Loader" is preferable. Since the raw parsing logic is performed in the page,

:::

Pure Data Loader

For a pure static site, a plugin can load data into an array of row objects. The SheetJS work is performed in the plugin. The library is not loaded in the page!

import { readFileSync } from 'fs';
import { read, utils } from 'xlsx';
import { defineConfig } from 'vite';

export default defineConfig({
  assetsInclude: ['**/*.xlsx'], // xlsx file should be treated as assets

  plugins: [
    { // this plugin handles ?sheetjs tags
      name: "vite-sheet",
      transform(code, id) {
        if(!id.match(/\?sheetjs$/)) return;
        var wb = read(readFileSync(id.replace(/\?sheetjs$/, "")));
        var data = utils.sheet_to_json(wb.Sheets[wb.SheetNames[0]]);
        return `export default JSON.parse('${JSON.stringify(data)}')`;
      }
    }
  ]
});

This loader uses the query sheetjs:

import data from './data.xlsx?sheetjs';

document.querySelector('#app').innerHTML = `<div><pre>
${data.map(row => JSON.stringify(row)).join("\n")}
</pre></div>`;

Base64 Loader

This loader pulls in data as a Base64 string that can be read with XLSX.read. While this approach works, it is not recommended since it loads the library in the front-end site.

import { readFileSync } from 'fs';
import { defineConfig } from 'vite';

export default defineConfig({
  assetsInclude: ['**/*.xlsx'], // mark that xlsx file should be treated as assets

  plugins: [
    { // this plugin handles ?b64 tags
      name: "vite-b64-plugin",
      transform(code, id) {
        if(!id.match(/\?b64$/)) return;
        var path = id.replace(/\?b64/, "");
        var data = readFileSync(path, "base64");
        return `export default '${data}'`;
      }
    }
  ]
});

When importing using the b64 query, the raw Base64 string will be exposed. This can be read directly with XLSX.read in JS code:

import { read, utils } from "xlsx";

/* reference workbook */
import b64 from './data.xlsx?b64';
/* parse workbook and export first sheet to CSV */
const wb = read(b64);
const wsname = wb.SheetNames[0];
const csv = utils.sheet_to_csv(wb.Sheets[wsname]);

document.querySelector('#app').innerHTML = `<div><pre>
<b>${wsname}</b>
${csv}
</pre></div>`;

Complete Demo

:::note

This demo was tested on 2023 January 14 against vite v4.0.4.

:::

Initial Setup

  1. Create a new site using the vue-ts template:
npm create vite@latest sheetjs-vite -- --template vue-ts
cd sheetjs-vite
npm install
npm i --save https://cdn.sheetjs.com/xlsx-latest/xlsx-latest.tgz
  1. Replace vite.config.ts with the following:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { readFileSync } from 'fs';
import { read, utils } from 'xlsx';

export default defineConfig({
  assetsInclude: ['**/*.xlsx'], // xlsx file should be treated as assets

  plugins: [
    vue(),
    { // this plugin handles ?sheetjs tags
      name: "vite-sheet",
      transform(code, id) {
        if(!id.match(/\?sheetjs$/)) return;
        var wb = read(readFileSync(id.replace(/\?sheetjs$/, "")));
        var data = utils.sheet_to_json(wb.Sheets[wb.SheetNames[0]]);
        return `export default JSON.parse('${JSON.stringify(data)}')`;
      }
    },
    { // this plugin handles ?b64 tags
      name: "vite-b64-plugin",
      transform(code, id) {
        if(!id.match(/\?b64$/)) return;
        var path = id.replace(/\?b64/, "");
        var data = readFileSync(path, "base64");
        return `export default '${data}'`;
      }
    }
  ]
});
  1. Make a data folder and download https://sheetjs.com/pres.xlsx :
mkdir -p data
curl -L -o data/pres.xlsx https://sheetjs.com/pres.xlsx

Pure Data Test

  1. Run the dev server:
npm run dev

Open a browser window to the displayed URL (typically http://localhost:5173 )

  1. Replace the component src/components/HelloWorld.vue with:
<script setup lang="ts">
// @ts-ignore
import data from '../../data/pres.xlsx?sheetjs';
</script>

<template>
  <table>
    <tr><th>Name</th><th>Index</th></tr>
    <tr v-for="(row,R) in data" v-bind:key="R">
      <td>{{row.Name}}</td>
      <td>{{row.Index}}</td>
    </tr>
  </table>
</template>

Save and refresh the page. A data table should be displayed

  1. Stop the dev server and build the site
npm run build
npx http-server dist/

The terminal will display a url like http://127.0.0.1:8080. Access that page with a web browser.

  1. To confirm that only the raw data is present in the page, view the page source. The code will reference some script like /assets/index-HASH.js. Open that script. Searching for Bill Clinton reveals the following:
JSON.parse('[{"Name":"Bill Clinton","Index":42}

Searching for BESSELJ should reveal no results. The SheetJS scripts are not included in the final site!

Base64 Test

  1. Run the dev server:
npm run dev

Open a browser window to the displayed URL.

  1. Replace the component src/components/HelloWorld.vue with:
<script setup lang="ts">
// @ts-ignore
import b64 from '../../data/pres.xlsx?b64';
import { read, utils } from "xlsx";
/* parse workbook and convert first sheet to row array */
const wb = read(b64);
const ws = wb.Sheets[wb.SheetNames[0]];
interface IPresident { Name: string; Index: number; };
const data = utils.sheet_to_json<IPresident>(ws);
</script>

<template>
  <table>
    <tr><th>Name</th><th>Index</th></tr>
    <tr v-for="(row,R) in data" v-bind:key="R">
      <td>{{row.Name}}</td>
      <td>{{row.Index}}</td>
    </tr>
  </table>
</template>
  1. Stop the dev server and build the site
npm run build
npx http-server dist/

The terminal will display a url like http://127.0.0.1:8080. Access that page with a web browser.

  1. To confirm that only the raw data is present in the page, view the page source. The code will reference some script like /assets/index-HASH.js. Open that script. Searching for Bill Clinton should yield no results. Searching for BESSELJ should match the code:
425:"BESSELJ"

The SheetJS library is embedded in the final site.