当前位置:首页|资讯

# 前端项目接入sqlite轻量级数据库sql.js指南

作者:red润发布时间:2024-10-23

# 前端项目接入sqlite轻量级数据库sql.js指南


引言


> sql.js 是一个强大的JavaScript库,它使得SQLite数据库能够在网页浏览器中运行。这个开源项目提供了一种方式,让开发者可以在前端环境中实现轻量级的数据库操作,无需依赖服务器端数据存储,极大地增强了Web应用的数据处理能力。通过将SQLite编译成WebAssembly(wasm),sql.js实现了高效的数据库操作,支持标准的SQL查询语言。

>

> 如果你需要在浏览器中处理**大量数据**,并且希望使用 SQL 语法来操作这些数据,那么 SQL.js 将是一个不错的选择。

>

> 如果您不想在主应用程序线程中运行 CPU 密集型 SQL 查询, 您可以使用*更有限的* WebWorker API。

>

> 详细api请移步官方文档[sql.js](https://sql.js.org/#/?id=api-documentation)

> sqlite 官网:https://sql.js.org/#/?id=inside-the-browser

>

> <mark>sqlite 不会持久化缓存数据,只会存在运行内存中,需要自行导入导出文件。</mark>


## 我们的目标


> 本指南将sqlite数据集成到网页项目中,并学习相应api操作,

>

> 实现案例效果

>

> 本教程使用版本<mark>"sql.js": "^1.11.0"</mark>


## 项目中加载sql.js


> 实现的最终效果,浏览器无任何错误,且,会在控制打印`{a:1,b:'world'}`

>

> 您始终可以在 https://github.com/sql-js/sql.js/releases/latest 上找到最新发布的工件。

>

> 对于每个[版本](https://github.com/sql-js/sql.js/releases/),您都会在*发布资产*中找到一个名为的文件。它将包含:`sqljs.zip`

>

> - `sql-wasm.js`:Sql.js 的 Web Assembly 版本。缩小并适合生产。使用这个。如果您使用此项,您还需要包含/发货。`sql-wasm.wasm`

> - `sql-wasm-debug.js`:Web 程序集,Sql.js. Larger 的调试版本,打开了断言。对本地开发有用。如果您使用这个,您将需要包含/发货。`sql-wasm-debug.wasm`

> - `sql-asm.js`:较旧的 asm.js Sql.js 版本。速度较慢且较大。出于兼容性原因提供。

> - `sql-asm-memory-growth.js`:默认情况下,Asm.js不允许内存增长,因为它速度较慢且会取消优化。如果您使用的是 sql-asm.js 并看到此错误 (),请使用此文件。`Cannot enlarge memory arrays`

> - `sql-asm-debug.js`:Sql.js*的 Debug* asm.js 版本。用于本地开发。

> - `worker.*`- 上述库的 Web Worker 版本。更有限的 API。参见 [examples/GUI/gui.js](https://sql.js.org/#/examples/GUI/gui.js) 这是一个很好的例子。


有两个版本,一个是在网页中直接加载的版本,一个是electron等桌面端应用使用的版本


### 1.在网页中加载的版本(本教程的版本),有两种加载方式


1. 可以使用npm包安装(推荐),使用构建工具**webpack**或**vite**等


   > 库下载地址:[sql.js - npm (npmjs.com)](https://www.npmjs.com/package/sql.js)


   ```

   npm i sql.js

   ```


   > sql.js需要依赖sql-wasm.wasm文件,我们需要添加到自己的目录下面引用


   ```

   node_modules

    - sql.js

    - sql-wasm.wasm将这个文件放在自己的dist目录下面

   ```


   > 新建dist目录/


   ```

    dist

    - sql-wasm.wasm

   ```


   > 运行下面的代码,没有报错,且控制台打印对应数据


   ```

   import initSqlJs  from 'sql.js';

   const SQL = await initSqlJs({

   // 这里会加载dist/sql-wasm.wasm

   locateFile: file => `./dist/${file}`

   });

   

   // Create a database

   const db = new SQL.Database();

   

   let sqlstr = "CREATE TABLE hello (a int, b char); \

   INSERT INTO hello VALUES (0, 'hello'); \

   INSERT INTO hello VALUES (1, 'world');";

   db.run(sqlstr); // Run the query without returning anything

   

   // Prepare an sql statement

   const stmt = db.prepare("SELECT * FROM hello WHERE a=:aval AND b=:bval");

   

   // Bind values to the parameters and fetch the results of the query

   const result = stmt.getAsObject({':aval' : 1, ':bval' : 'world'});

   console.log(result); // Will print {a:1, b:'world'}

   

   ```


   


2. 也可以直接在网页中加入(没有使用任何构建工具),需要在 web 静态服务器下访问本文件(推荐 vscode 插件 liveserver)


   > 库下载地址:[sql.js - Libraries - cdnjs - The #1 free and open source CDN built to make life easier for developers](https://cdnjs.com/libraries/sql.js)


   1. 一种是直接在网页头部引入(不推荐)


      > 如果cdn出现问题,会导致不可用


      ```

      <!DOCTYPE html>

      <html>

        <head>

          <meta charset="UTF-8" />

          <link rel="icon" type="image/svg+xml" href="/vite.svg" />

          <meta name="viewport" content="width=device-width, initial-scale=1.0" />

          <title>Vite App</title>

          <script src="https://cdnjs.cloudflare.com/ajax/libs/sql.js/1.11.0/sql-wasm.js" integrity="sha512-tz0jOZaOg9RtWWB6AdxSkINQwIs7S5obj1Dlml9KewZLPTblTWCux5eLtnexBb8kbLUo5crPmjsi8/vI17Vw0w==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>

        </head>

        <body>

          <div id="app"></div>

          <script>

            config = {

            // 从云端加载https://sql.js.org/distsql-wasm.wasm

              locateFile: file => `https://sql.js.org/dist/${file}`

            };

            initSqlJs(config).then(function (SQL) {

              //Create the database

              const db = new SQL.Database();

              // Run a query without reading the results

              db.run('CREATE TABLE test (col1, col2);');

              // Insert two rows: (1,111) and (2,222)

              db.run('INSERT INTO test VALUES (?,?), (?,?)', [1, 111, 2, 222]);

      

              // Prepare a statement

              const stmt = db.prepare('SELECT * FROM test WHERE col1 BETWEEN $start AND $end');

              stmt.getAsObject({ $start: 1, $end: 1 }); // {col1:1, col2:111}

      

              // Bind new values

              stmt.bind({ $start: 1, $end: 2 });

              while (stmt.step()) {

                //

                const row = stmt.getAsObject();

                console.log('Here is a row: ' + JSON.stringify(row));

              }

            });

          </script>

        </body>

      </html>

      

      ```


   2. 一种是将文件下载在自己的文件目录中,然后引入(推荐)


      > 库下载地址:[sql.js - Libraries - cdnjs - The #1 free and open source CDN built to make life easier for developers](https://cdnjs.com/libraries/sql.js)


      > 从下载地址下载两个文件,存在dist目录

      >

      > ```

      > dist

      >  - sql-wasm.wasm

      >  - sql-wasm.js

      > ```

      >

      > 


      ```

      <!DOCTYPE html>

      <html>

        <head>

          <meta charset="UTF-8" />

          <link rel="icon" type="image/svg+xml" href="/vite.svg" />

          <meta name="viewport" content="width=device-width, initial-scale=1.0" />

          <title>Vite App</title>

          <script src="./dist/sql-wasm.js"></script>

        </head>

        <body>

          <div id="app"></div>

          <script>

            config = {

            // 这里请求自己目录下面的.dist/sql-wasm.wasm

              locateFile: file => `./dist/${file}`

            };

            initSqlJs(config).then(function (SQL) {

              //Create the database

              const db = new SQL.Database();

              // Run a query without reading the results

              db.run('CREATE TABLE test (col1, col2);');

              // Insert two rows: (1,111) and (2,222)

              db.run('INSERT INTO test VALUES (?,?), (?,?)', [1, 111, 2, 222]);

      

              // Prepare a statement

              const stmt = db.prepare('SELECT * FROM test WHERE col1 BETWEEN $start AND $end');

              stmt.getAsObject({ $start: 1, $end: 1 }); // {col1:1, col2:111}

      

              // Bind new values

              stmt.bind({ $start: 1, $end: 2 });

              while (stmt.step()) {

                //

                const row = stmt.getAsObject();

                console.log('Here is a row: ' + JSON.stringify(row));

              }

            });

          </script>

        </body>

      </html>

      

      ```


### 2.在node版本(electron等本机桌面应用程序使用)


> 库下载地址:[sqlite3 - npm (npmjs.com)](https://www.npmjs.com/package/sqlite3)


```

npm install sqlite3

```


## 案例


### 持久化缓存


```

<!doctype html>

<html>


<head>

<meta charset="utf8">

<title>Persistent sqlite</title>

<script src="../dist/sql-wasm.js"></script>

</head>


<body>

<p>You have seen this page <span id="views">0</span> times.</p>

<div>

You have been here on the following dates: <ol id="dates"></ol>

</div>

<script>

var baseUrl = '../dist/';


function toBinArray(str) {

var l = str.length,

arr = new Uint8Array(l);

for (var i = 0; i < l; i++) arr[i] = str.charCodeAt(i);

return arr;

}


function toBinString(arr) {

var uarr = new Uint8Array(arr);

var strings = [], chunksize = 0xffff;

// There is a maximum stack size. We cannot call String.fromCharCode with as many arguments as we want

for (var i = 0; i * chunksize < uarr.length; i++) {

strings.push(String.fromCharCode.apply(null, uarr.subarray(i * chunksize, (i + 1) * chunksize)));

}

return strings.join('');

}


// Normally Sql.js tries to load sql-wasm.wasm relative to the page, not relative to the javascript

// doing the loading. So, we help it find the .wasm file with this function.

var config = {

locateFile: filename => `${baseUrl}/${filename}`

}

initSqlJs(config).then(function (SQL) {

var dbstr = window.localStorage.getItem("viewcount.sqlite");

if (dbstr) {

var db = new SQL.Database(toBinArray(dbstr));

} else {

var db = new SQL.Database();

db.run("CREATE TABLE views (date INTEGER PRIMARY KEY)");

}

db.run("INSERT INTO views(date) VALUES (?)", [Date.now()]);


document.getElementById('views').textContent = db.exec("SELECT COUNT(*) FROM views")[0].values[0][0];


var count = 0,

dates = document.getElementById("dates");


db.each("SELECT date FROM views ORDER BY date ASC",

function callback(row) {

var li = document.createElement("li");

li.textContent = new Date(row.date);

dates.appendChild(li);

}, function done() {

var dbstr = toBinString(db.export());

window.localStorage.setItem("viewcount.sqlite", dbstr);

}

);

});


</script>

</body>


</html>

```


### 交互案例


```

<!doctype html>

<html>

<!--Simple Read eval print loop for SQL-->


<head>

<meta charset="utf8">

<title>SQL REPL</title>

<script src="../dist/sql-wasm.js"></script>

</head>


<body>

<input type='text' id='input' placeholder="ENTER SOME SQL" size='50'

value="CREATE TABLE test(val);INSERT INTO test VALUES (666); SELECT * FROM test">

<button id='submit'>Execute</button>

<pre id='result'></pre>

<pre id='error'></pre>

<script>


//Open a blank database

var db;

initSqlJs({ locateFile: filename => `../dist/${filename}` }).then(function (SQL) {

db = new SQL.Database();

});


document.getElementById('submit').onclick = function () {

var sql = document.getElementById('input').value;

var result = '', error = '';

try { result = db.exec(sql); }

catch (e) { error = e; }

document.getElementById('result').innerHTML = JSON.stringify(result, null, '  ');

document.getElementById('error').innerHTML = error;

};

</script>

</body>

```




## 代码解析


### initSqlJs 函数


```

API 中的根对象是 initSqlJs 函数,它接受一个 SqlJsConfig 参数,并返回一个 SqlJs 对象

```


### SqlJs 对象


```

initSqlJs返回主 sql.js 对象 SqlJs 模块,其中包含:

```


- #### Database


  [**Database**](https://sql.js.org/documentation/Database.html) 是主类,表示 SQLite 数据库。


  - 构造函数


    - ```

      const db = new SQL.Database();

      ```


    - ```

      let data = 

       const db = new SQL.Database(data);

      ```


  - `close()`


    - 关闭数据库


  - 注册自定义aggregate


    - ```

      db.create_aggregate("js_sum", {

                  init: () => 0,

                  step: (state, value) => state + value,

                  finalize: state => state

              });

              db.exec("SELECT js_sum(column1) FROM (VALUES (1), (2))"); // = 3

      ```


  - 创建函数


    - ```

           db.create_function("addOne", function (x) {return x+1;})

                db.exec("SELECT addOne(1)") // = 2

      ```


  - 执行 sql 语句,并为每行 result 调用回调。


    - ```

         db.each("SELECT name,age FROM users WHERE age >= $majority", {$majority:18},

                  function (row){console.log(row.name + " is a grown-up.")}

          );

      ```


  - 执行sql


    - ```

      var db = new SQL.Database();

      var res = db.exec(

          "DROP TABLE IF EXISTS test;\n"

          + "CREATE TABLE test (id INTEGER, age INTEGER, name TEXT);"

          + "INSERT INTO test VALUES ($id1, :age1, @name1);"

          + "INSERT INTO test VALUES ($id2, :age2, @name2);"

          + "SELECT id FROM test;"

          + "SELECT age,name FROM test WHERE id=$id1",

          {

              "$id1": 1, ":age1": 1, "@name1": "Ling",

              "$id2": 2, ":age2": 18, "@name2": "Paul"

          }

      );

      ```


  - 将数据库的内容导出到二进制数组。此操作将关闭并重新打开数据库,这将导致程序设置回其默认值。


    - ```

      let res = db.export()

      console.log(res)

      ```


  -  准备sql


    - ```

       const stmt = db.prepare('SELECT * FROM test WHERE col1 BETWEEN $start AND $end');

              stmt.getAsObject({ $start: 1, $end: 1 }); // {col1:1, col2:111}

      

              // Bind new values

              stmt.bind({ $start: 1, $end: 2 });

              while (stmt.step()) {

                //

                const row = stmt.getAsObject();

                console.log('Here is a row: ' + JSON.stringify(row));

              }

      ```


  - 执行sql


    - ```

       db.run(

              "INSERT INTO test VALUES (:age, :name)",

              { ':age' : 18, ':name' : 'John' }

          );

      ```


      


- #### Statement


  #### [**Statement**](https://sql.js.org/documentation/Statement.html) 类用于准备好的语句。


  ```

    var stmt = db.prepare(

          "UPDATE test SET a=@newval WHERE id BETWEEN $mini AND $maxi"

      );

      stmt.bind({$mini:10, $maxi:20, '@newval':5});

  ```


  


## 常用操作


详细api请移步官方文档[sql.js](https://sql.js.org/#/?id=api-documentation)


### 创建数据库/导入数据


```

// 创建一个新的数据库实例

const db = new SQL.Database();

// 创建并加载数据(见案例章节)

var db = new SQL.Database(toBinArray(dbstr));

```


### 查询


```

// 执行一个 INSERT 语句

db.run("INSERT INTO tasks (name, priority) VALUES ('Buy groceries', 'high')");


// 执行一个 SELECT 语句

const result = db.exec("SELECT * FROM tasks WHERE priority='high'");


// 输出结果

console.log(result);


```


### 导出


```

// 导出数据库

const databaseBlob = db.export();

```


## 完结


sqlite确实好用,如无**必要**,勿**增实体**



Copyright © 2024 aigcdaily.cn  北京智识时代科技有限公司  版权所有  京ICP备2023006237号-1