forked from sheetjs/docs.sheetjs.com
		
	
		
			
	
	
		
			284 lines
		
	
	
		
			7.0 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
		
		
			
		
	
	
			284 lines
		
	
	
		
			7.0 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
|  | --- | ||
|  | title: ViteJS | ||
|  | pagination_prev: demos/extensions/index | ||
|  | pagination_next: demos/gsheet | ||
|  | sidebar_custom_props: | ||
|  |   type: bundler | ||
|  | --- | ||
|  | 
 | ||
|  | :::note | ||
|  | 
 | ||
|  | This demo covers static asset imports. For processing files in the browser, the | ||
|  | ["Bundlers" demo](/docs/demos/bundler#vite) 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"](#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"](#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! | ||
|  | 
 | ||
|  | ```js title="vite.config.js" | ||
|  | 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`: | ||
|  | 
 | ||
|  | ```js title="main.js" | ||
|  | 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. | ||
|  | 
 | ||
|  | ```js title="vite.config.js" | ||
|  | 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: | ||
|  | 
 | ||
|  | ```js title="main.js" | ||
|  | 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: | ||
|  | 
 | ||
|  | ```bash | ||
|  | 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 | ||
|  | ``` | ||
|  | 
 | ||
|  | 2) Replace `vite.config.ts` with the following: | ||
|  | 
 | ||
|  | ```js title="vite.config.ts" | ||
|  | 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}'`; | ||
|  |       } | ||
|  |     } | ||
|  |   ] | ||
|  | }); | ||
|  | ``` | ||
|  | 
 | ||
|  | 3) Make a `data` folder and download <https://sheetjs.com/pres.xlsx> : | ||
|  | 
 | ||
|  | ```bash | ||
|  | mkdir -p data | ||
|  | curl -L -o data/pres.xlsx https://sheetjs.com/pres.xlsx | ||
|  | ``` | ||
|  | 
 | ||
|  | ### Pure Data Test
 | ||
|  | 
 | ||
|  | 4) Run the dev server: | ||
|  | 
 | ||
|  | ```bash | ||
|  | npm run dev | ||
|  | ``` | ||
|  | 
 | ||
|  | Open a browser window to the displayed URL (typically http://localhost:5173 ) | ||
|  | 
 | ||
|  | 5) Replace the component `src/components/HelloWorld.vue` with: | ||
|  | 
 | ||
|  | ```html title="src/components/HelloWorld.vue" | ||
|  | <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 | ||
|  | 
 | ||
|  | 6) Stop the dev server and build the site | ||
|  | 
 | ||
|  | ```bash | ||
|  | 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. | ||
|  | 
 | ||
|  | 7) 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
 | ||
|  | 
 | ||
|  | 8) Run the dev server: | ||
|  | 
 | ||
|  | ```bash | ||
|  | npm run dev | ||
|  | ``` | ||
|  | 
 | ||
|  | Open a browser window to the displayed URL. | ||
|  | 
 | ||
|  | 9) Replace the component `src/components/HelloWorld.vue` with: | ||
|  | 
 | ||
|  | ```html title="src/components/HelloWorld.vue" | ||
|  | <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> | ||
|  | ``` | ||
|  | 
 | ||
|  | 10) Stop the dev server and build the site | ||
|  | 
 | ||
|  | ```bash | ||
|  | 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. | ||
|  | 
 | ||
|  | 11) 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. |