为什么要在js里写SQL?
随着业务的不停增多,前端页面可能出现一些数据逻辑复杂的页面,传统的js逻辑处理起来比较复杂
for example:地区联动查询界面
- 如何在本地存储地区数据,显然每次拉接口是不现实的(当然如果像我们bi系统一样优秀也可以),如果存储在localStorage 和 sessionStorage里,每次使用时,需要有类似JSON.parse类的字符串转化为数组或对象的过程,这个操作在数据量大的时候,会造成页面卡顿,性能极差真的
- 三级地区联动查询复杂,如果要从一个县级地区查询到所属的城市和省份,逻辑会比较复杂
我们思考一下如果用传统js逻辑来写,我们的头脑风暴下,但是免不了用forEach、filter、some、find等各种ES678新方法,我们如果使用各种新方法写出来发现有两个问题:
- 写完之后逻辑很复杂,似乎没有100行代码实现不了(当然有人活儿好,可能不会当程序员)
- 即使写了一大堆注释,但依然晦涩难懂(主要因为代码太多懒得看)
这个时候需要引入我们要灌输给大家的知识了:Web SQL Database 和 IndexedDB
Web SQL Database API 实际上未包含在 HTML 5 规范之中,它是一个独立的规范,它引入了一套使用 SQL 操作客户端数据库的 API,这些 API 有同步的,也有异步的, 同步版本的 API 只在工作线程(Worker Threads)上有用,由于并不是所有的浏览器都支持工作线程,一般情况下,都会使用异步 API。它的核心方法有三个:openDatabase,transaction 和 executeSql。 这些 API 已经被广泛的实现在了不同的浏览器里,尤其是手机端浏览器。虽然 W3C 官方在 2011 年 11 月声明已经不再维护 Web SQL Database 规范,但由于其广泛的实现程度,了解这些 API 对 Web 开发还是非常有必要的。 详细的 Web SQL Database 规范可以参考 官方网站。
Indexed Database,也可简称为 IndexedDB(以前被称作 WebSimpleDB),同样是一个 Web 客户端存储结构化数据的规范,在 2009 年由 Oracle 提出。如果说 Web SQL Databae 在客户端实现了 传统的 SQL 数据库操作,那么 Indexed Database 更类似于 NoSQL 的形式来操作数据库 , 其中最重要的是 Indexed Database 不使用 SQL 作为查询语言。其数据存储可以不需要固定的表格模式,也经常会避免使用 SQL 的 JOIN 操作,并且一般具有水平可扩展性。目前 W3C 官方也把焦点 投到对 Indexed Database 规范的制定当中来,而 Microsoft 和 Mozilla 是对这个规范重要的两个推动者,Firefox 4 以上已经部分实现了 Indexed DB API,并且 IE 10 中也将实现 Indexed DB API。由于在手机等移动设备的浏览器中都没有实现 Indexed DB API,所以其还有一定的局限性,但这并 不妨碍它作为未来的 HTML5 的焦点而存在。详细的 Indexed Database 规范可以参考 官方网站。
浏览器对 Web SQL Database 和 Indexed Database 支持情况
表 1. Web SQL Databse
IE | Firefox | Chrome | Safari | iOS Safari | Android Browser |
---|---|---|---|---|---|
6.0( × ) | 4.0~7.0( × ) | 10.0~13.0( √ ) | 3.1~3.2( √ ) | 3.2( √ ) | 2.1( √ ) |
7.0( × ) | 8.0( × ) | 14.0( √ ) | 4.0( √ ) | 4.0~4.1( √ ) | 2.2( √ ) |
8.0( × ) | 9.0( × ) | 15.0( √ ) | 5.0( √ ) | 4.2~4.3( √ ) | 2.3,3.0( √ ) |
9.0( × ) | 10.0( × ) | 16.0( √ ) | 5.1( √ ) | 5.0( √ ) | 4.0( √ ) |
10.0( × ) | 11.0( × ) | 17.0( √ ) | 6.0( √ ) | ||
12.0( × ) | 18.0( √ ) |
表 2. Indexed Databse
IE | Firefox | Chrome | Safari | iOS Safari | Android Browser |
---|---|---|---|---|---|
6.0( × ) | 4.0~7.0( √ ) | 10.0~13.0( √ ) | 3.1~3.2( × ) | 3.2( × ) | 2.1( × ) |
7.0( × ) | 8.0( √ ) | 14.0( √ ) | 4.0( × ) | 4.0~4.1( × ) | 2.2( × ) |
8.0( × ) | 9.0( √ ) | 15.0( √ ) | 5.0( × ) | 4.2~4.3( × ) | 2.3,3.0( × ) |
9.0( × ) | 10.0( √ ) | 16.0( √ ) | 5.1( × ) | 5.0( × ) | 4.0( × ) |
10.0( √ ) | 11.0( √ ) | 17.0( √ ) | |||
12.0( √ ) | 18.0( √ ) |
Web SQL Database 和 Indexed Database有啥区别?
Indexed Database 更类似于 NoSQL 的形式来操作数据库 , 其中最重要的是 Indexed Database 不使用 SQL 作为查询语言。
讲一下Web SQL Database的简单且核心的操作
- 建立数据库 ( 数据库名称、版本、描述、数据大小 )
let db = openDatabase('mydb', '1.0', '测试用', 2*1024*1024);
- 建表
db.transaction(function(tx){
tx.executeSql("Create Table test(id TEXT,value TEXT)");
});
- 新增资料
db.transaction(function(tx){
tx.executeSql("INSERT INTO test VALUES (1,'data')");
});
- 刪除资料
db.transaction(function(tx){
tx.executeSql("DELETE FROM test WHERE id = 1");
});
- 修改资料
db.transaction(function(tx){
tx.executeSql("UPDATE test SET value = 'other' WHERE id = 1");
});
- 查詢资料
db.transaction(function(tx){
tx.executeSql(
"SELECT id,value FROM test",[],function(tx,result){
for (var i=0 ; i < result.rows.length; i++){
console.log(result.rows.item(i).value);
}
}
);
});
假设以下数据是我们的省市区json,我们如何高效分批插入数据
let site_address = [{ "ID": 1, "TopID": 0, "AddName": "安徽省" }, { "ID": 2, "TopID": 0, "AddName": "北京市" }, { "ID": 3, "TopID": 0, "AddName": "福建省" }, { "ID": 4, "TopID": 0, "AddName": "甘肃省" }, { "ID": 5, "TopID": 0, "AddName": "广东省" }, { "ID": 6, "TopID": 0, "AddName": "广西壮族自治区" }, { "ID": 7, "TopID": 0, "AddName": "贵州省" }, { "ID": 8, "TopID": 0, "AddName": "海南省" }, { "ID": 9, "TopID": 0, "AddName": "河北省" }, { "ID": 10, "TopID": 0, "AddName": "河南省" }, { "ID": 11, "TopID": 0, "AddName": "黑龙江省" }, { "ID": 12, "TopID": 0, "AddName": "湖北省" }, { "ID": 13, "TopID": 0, "AddName": "湖南省" }, { "ID": 14, "TopID": 0, "AddName": "吉林省" }, { "ID": 15, "TopID": 0, "AddName": "江苏省" }, { "ID": 16, "TopID": 0, "AddName": "江西省" }, { "ID": 17, "TopID": 0, "AddName": "辽宁省" }, { "ID": 18, "TopID": 0, "AddName": "内蒙古自治区" }, { "ID": 19, "TopID": 0, "AddName": "宁夏回族自治区" }, { "ID": 20, "TopID": 0, "AddName": "青海省" }, { "ID": 21, "TopID": 0, "AddName": "山东省" }, { "ID": 22, "TopID": 0, "AddName": "山西省" }, { "ID": 23, "TopID": 0, "AddName": "陕西省" }, { "ID": 24, "TopID": 0, "AddName": "上海市" }, { "ID": 25, "TopID": 0, "AddName": "四川省" }, { "ID": 26, "TopID": 0, "AddName": "天津市" }, { "ID": 27, "TopID": 0, "AddName": "西藏自治区" }, { "ID": 28, "TopID": 0, "AddName": "新疆自治区" }, { "ID": 29, "TopID": 0, "AddName": "云南省" }, { "ID": 30, "TopID": 0, "AddName": "浙江省" }, { "ID": 31, "TopID": 0, "AddName": "重庆市" }, { "ID": 32, "TopID": 1, "AddName": "安庆市" }, { "ID": 33, "TopID": 1, "AddName": "蚌埠市" }, { "ID": 34, "TopID": 1, "AddName": "亳州市" }, { "ID": 35, "TopID": 1, "AddName": "池州市" }, { "ID": 36, "TopID": 1, "AddName": "滁州市" }, { "ID": 37, "TopID": 1, "AddName": "阜阳市" }, { "ID": 38, "TopID": 1, "AddName": "合肥市" }, { "ID": 39, "TopID": 1, "AddName": "淮北市" }, { "ID": 40, "TopID": 1, "AddName": "淮南市" }, { "ID": 41, "TopID": 1, "AddName": "黄山市" }, { "ID": 42, "TopID": 1, "AddName": "六安市" }, { "ID": 43, "TopID": 1, "AddName": "马鞍山市" }, { "ID": 44, "TopID": 1, "AddName": "宿州市" }, { "ID": 45, "TopID": 1, "AddName": "铜陵市" }, { "ID": 46, "TopID": 1, "AddName": "芜湖市" }, { "ID": 47, "TopID": 1, "AddName": "宣城市" }, { "ID": 48, "TopID": 2, "AddName": "北京市" }, { "ID": 49, "TopID": 2, "AddName": "市辖区" }, { "ID": 50, "TopID": 3, "AddName": "福州市" }, { "ID": 51, "TopID": 3, "AddName": "龙岩市" }, { "ID": 52, "TopID": 3, "AddName": "南平市" }, { "ID": 53, "TopID": 3, "AddName": "宁德市" }, { "ID": 54, "TopID": 3, "AddName": "莆田市" }, { "ID": 55, "TopID": 3, "AddName": "泉州市" }, { "ID": 56, "TopID": 3, "AddName": "三明市" }, { "ID": 57, "TopID": 3, "AddName": "厦门市" }, { "ID": 58, "TopID": 3, "AddName": "漳州市" }, { "ID": 59, "TopID": 4, "AddName": "白银市" }, { "ID": 60, "TopID": 4, "AddName": "定西市" }, { "ID": 61, "TopID": 4, "AddName": "甘南藏族自治州" }, { "ID": 62, "TopID": 4, "AddName": "嘉峪关市" }, { "ID": 63, "TopID": 4, "AddName": "金昌市" }, { "ID": 64, "TopID": 4, "AddName": "酒泉市" }, { "ID": 65, "TopID": 4, "AddName": "兰州市" }, { "ID": 66, "TopID": 4, "AddName": "临夏回族自治州" }, { "ID": 67, "TopID": 4, "AddName": "陇南市" }, { "ID": 68, "TopID": 4, "AddName": "平凉市" }, { "ID": 69, "TopID": 4, "AddName": "庆阳市" }, { "ID": 70, "TopID": 4, "AddName": "天水市" }, { "ID": 71, "TopID": 4, "AddName": "武威市" }, { "ID": 72, "TopID": 4, "AddName": "张掖市" }, { "ID": 73, "TopID": 5, "AddName": "潮州市" }, { "ID": 74, "TopID": 5, "AddName": "东莞市" }, { "ID": 75, "TopID": 5, "AddName": "佛山市" }, { "ID": 76, "TopID": 5, "AddName": "广州市" }, { "ID": 77, "TopID": 5, "AddName": "河源市" }, { "ID": 78, "TopID": 5, "AddName": "惠州市" }, { "ID": 79, "TopID": 5, "AddName": "江门市" }, { "ID": 80, "TopID": 5, "AddName": "揭阳市" }, { "ID": 81, "TopID": 5, "AddName": "茂名市" }, { "ID": 82, "TopID": 5, "AddName": "梅州市" }, { "ID": 83, "TopID": 5, "AddName": "清远市" }, { "ID": 84, "TopID": 5, "AddName": "汕头市" }, { "ID": 85, "TopID": 5, "AddName": "汕尾市" }, { "ID": 86, "TopID": 5, "AddName": "韶关市" }, { "ID": 87, "TopID": 5, "AddName": "深圳市" }, { "ID": 88, "TopID": 5, "AddName": "阳江市" }, { "ID": 89, "TopID": 5, "AddName": "云浮市" }, { "ID": 90, "TopID": 5, "AddName": "湛江市" }, { "ID": 91, "TopID": 5, "AddName": "肇庆市" }, { "ID": 92, "TopID": 5, "AddName": "中山市" }, { "ID": 93, "TopID": 5, "AddName": "珠海市" }, { "ID": 94, "TopID": 6, "AddName": "百色市" }, { "ID": 95, "TopID": 6, "AddName": "北海市" }, { "ID": 96, "TopID": 6, "AddName": "崇左市" }, { "ID": 97, "TopID": 6, "AddName": "防城港市" }, { "ID": 98, "TopID": 6, "AddName": "贵港市" }, { "ID": 99, "TopID": 6, "AddName": "桂林市" }, { "ID": 100, "TopID": 6, "AddName": "河池市" }]
let db666 = openDatabase('mydb666', '1.0', '测试用', 2 * 1024 * 1024);
db666.transaction(function (tx) {
tx.executeSql("Create Table test(ID TEXT,TopID TEXT, AddName TEXT)");
});
for (var i = 0; i < site_address.length; i += 20) {
let sql = 'insert into test( '
+ 'ID,TopID,AddName'
+ ' ) values ';
let val = ''
site_address.slice(i, i + 20).forEach(element => {
val += "("
+ "'" + (element.ID ? element.ID : "") + "'" + ","
+ "'" + (element.TopID ? element.TopID : "") + "'" + ","
+ "'" + (element.AddName ? element.AddName : "") + "'"
+ "),"
});
sql = sql + val.substr(0, val.length - 1)
db666.transaction(tx => {
tx.executeSql(sql,
[],
(tx, result) => {
console.log(result);
}, (tx, err) => {
console.log(err.message);
})
})
}
分批插入数据原因是因为真正的省市区数据很大量,数据的插入过程也很简单,如果插入数据量少于500条跑起来是能够正常运行的,但超过500条时部分手机就会出错(部分手机一次插入超过500条虽然也能运行,但效率不高),所以建议分批插入
另外我们在做电商网站时候处理商品sku时候建议使用Web SQL Database
英文多sku商品界面,难点在于颜色分类、尺码、价格、库存、限购数量以及对应的图片展示之间有复杂的逻辑关系,用户进行不同的选择时,js要经过多次复杂的查询才能算出结果,如果我们获取数据后处理成扁平化数据存储到Web SQL Database中。
伪代码如下
/**
* 打开数据库
* @returns {Promise.<void>}
*/
openDataBase(){
//打开数据库,没有则创建
db.openDatabase('SkuMenu',1.0,'处理sku问题').then(res=>{
//检测数据库是否存在
db.isExists('sku').then(res=>{
//数据库已经存在,直接使用,将数据交付给页面UI组件
this.setSelectData();
}).catch(e=>{
//数据库不存在,请求接口并处理数据,然后存入数据库
this.getData();
})
}).catch(e=>{
console.err(e);
})
},
/**
* 获取分类数据并存储到数据库
* @returns {Promise.<void>}
*/
async getData(){
//接口请求数据并处理成三个扁平数组
let data = await this.getMenuData();
for(let i in data){
//创建表并存储数据
db.create(i,data[i]);
}
//将数据交付给页面UI组件
this.setSelectData();
}
网友评论