• HTML5学习总结——本地存储


     一、HTML4客户端存储

    B/S架构的应用大量的信息存储在服务器端,客户端通过请求响应的方式从服务器获得数据,这样集中存储也会给服务器带来相应的压力,有些数据可以直接存储在客户端,传统的Web技术中会使用Cookie,但Cookie有一些缺点,为了说明这个缺点我们先看看当提交表单时会有那些信息会被浏览器收集后发送到服务器。

     

    1.1、提交表单发送到服务器的信息

    1)、带name的可用表单元素

    2)、url

    3)、客户端请求头部信息

    4)、cookie

    <%@ page language="java" contentType="text/html; charset=utf-8"
        pageEncoding="utf-8"%>
    <%
    //定义一个cookie对象
    Cookie cookie=new     Cookie("color", "blue");
    //设置过期时间为365小时,以秒为单位
    cookie.setMaxAge(60*60*365);
    //添加cookie
    response.addCookie(cookie);
     %>
    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="utf-8">
    <meta name="viewport"
        content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
    <title>提交表单测试</title>
    </head>
    <body>
        <form action="target.do?id=1" method="post">
            <h2>提交表单测试</h2>
            <p>
                名称:<input type="text" name="txtName" id="txtName1" />
            </p>
            <p>
                价格:<input type="text" name="txtPrice" id="txtPrice1" value="888" readonly="readonly"/>
            </p>
            <p>
                数量:<input type="text" name="txtAmount" id="txtAmount1" value="999"  disabled="disabled"/>
            </p>
            <input type="hidden" id="key" value="123445">
            <input type="submit" value="提交" />
        </form>
    </body>
    </html>

    运行结果:

    服务器在响应头部中声明要求客户端浏览器指定设置cookie color=blue的工作,且指定了过期时间,会将cookie信息记录在本地,查看结果如下:

    当提交信息给服务器时cookie将收集后返回服务器,同时也会将url、带name可用的表单及请求头部信息如user-agent等,结果如下:

    1.2、客户端本地存储概要

    顾名思义客户端本地存储就是将信息存储在客户端电脑上,cookie就是一种典型的传统客户端存储,长期以来本地存储能力一直是桌面应用区别于Web应用的一个主要优势,作为Web应用程序而言,新一代的HTML标准对数据的本地存储提出了更高的要求。传统的Web数据存储方式一直来使用的是Cookie,但Cookie有以下缺陷:

    a)、cookie会被附加在每个HTTP请求中,所以无形中增加了流量。
    b)、由于在HTTP请求中的cookie是明文传递的,所以安全性成问题。
    c)、Cookie的大小限制在4 KB左右,容量达不到要求。

    HTML5中的Web Storage,称为Web本地存储,在Web客户端储存数据的功能,用键值对的形式保存数据,曾经属于HTML5的规范,目前已经被独立出来形成单独的规范体系。本地存储优势:

    a)、统一的标准,兼容性高(IE8、各大浏览器支持)

    b)、数据存储量大

    c)、无需安装插件

    d)、减少网络流量

    e)、更加适合移动端

    HTML5 提供了四种在客户端存储数据的新方法,即localStorage 、sessionStorage、globalStorage、Web Sql Database。 前面三个适用于存储较少的数据,而Web Sql Database适用于存储大型的,复杂的数据,我习惯把前面的三个称之为小存储。 IE8、Firefox3.6、Chrome5、Safari4、Opera10,事实证明各个浏览器在API方面的实现基本上一致,存在一定的兼容性问题,但不影响正常使用。

    在chrome浏览器中可以使用开发者工具查看到各种不同的本地存储方式,如下图所示:

    Web SQL Database 和 Indexed Database 都是在客户端存储大量结构化数据的解决方案。Web SQL Database 实现了传统的基于 SQL 语句的数据库操作,而 Indexed Database 实现了 NoSQL 的存储方式。

    二、localStorage

    localStorage:将数据保存在客户端本地的硬件设备(通常指硬盘,但也可以是其他硬件设备)中,即使浏览器被关闭了,该数据仍然存在,下次打开浏览器访问网站时仍然可以继续使用。

    2.1、添加

    代码:

     

    <script type="text/javascript">
    		//添加
    		function Add() {
    		localStorage.name = "小明";
    		localStorage.setItem("sex", "男");
    		localStorage["age"] = 23;
    	}		
    </script>
    

      

    运行结果如下:

    2.2、取值

     代码:

    //取值
    function Get() {
    	var msg = document.getElementById("msg");
    	msg.innerHTML += localStorage.name+"<br/>"+
    	localStorage["sex"]+"<br/>"+
    	localStorage.getItem("age");
    	}
    

     运行结果:

     

    2.3、修改

     代码:

           //修改
    	function Update() {
    		localStorage.name = "小成";
    		//如果不存在就添加,如果存在就修改
    		localStorage.setItem("sex", "男");
    		localStorage["age"] = 20;
    	}
    

      

      运行结果:

     

    2.4 删除

    //删除
    function Delete() {
    		//删除单个
    		localStorage.removeItem("name");
    		}
    
    		function DeleteAll() {
    		//删除全部
    		localStorage.clear();
    		}
    

      结果:

    完整代码:

    <!DOCTYPE html>
    <html>
    
    	<head>
    		<meta charset="UTF-8">
    		<title>Local Storage本地存储</title>
    		<script type="text/javascript">
    			//添加
    			function Add() {
    				localStorage.name = "小明";
    				localStorage.setItem("sex", "男");
    				localStorage["age"] = 23;
    			}
    			//取值
    			function Get() {
    				var msg = document.getElementById("msg");
    				msg.innerHTML += localStorage.name+"<br/>"+
    				localStorage["sex"]+"<br/>"+
    				localStorage.getItem("age")+"<br/>";
    			}
    
    			//修改
    			function Update() {
    				localStorage.name = "小成";
    				//如果不存在就添加,如果存在就修改
    				localStorage.setItem("sex", "男");
    				localStorage["age"] = 20;
    			}
    			//删除
    			function Delete() {
    				//删除单个
    				localStorage.removeItem("name");
    			}
    
    			function DeleteAll() {
    				//删除全部
    				localStorage.clear();
    			}
    			
    		</script>
    	</head>
    
    	<body>
    		<input type="button" name="btnAdd" id="btnAdd" value="添加" onclick="Add()" />
    		<input type="button" name="btnGet" id="btnGet" value="取值" onclick="Get()" />
    		<input type="button" name="btnUpdate" id="btnUpdate" value="修改" onclick="Update()" />
    		<input type="button" name="btnDelete" id="btnDelete" value="删除单个" onclick="Delete()" />
    		<input type="button" name="btnDeleteAll" id="btnDeleteAll" value="删除全部" onclick="DeleteAll()" />
    		<a href="d02.html" target="_blank">d02</a>
    		<h3 id="msg"></h3>
    	</body>
    
    </html>
    

      

    2.5、跨页面与跨域

    当关闭浏览器,下次再打开时,值仍然存在。可以跨页面,不能跨域。我们在d01页面中添加了值,在d02页面中仍然可以访问,在整个同域下都可以访问。

     

    2.6、存储位置与SQLite

    localStorage与cookie不一样,它存储在一个数据库文件中,默认位置在:C:UsersAdministratorAppDataLocalGoogleChromeUser DataDefaultdatabaseshttp_localhost_*

    使用SQLite数据库管理工具,打开后看到的结果,这里以taobao存储客户端的localStorage为例:

    提示:SQLite,是一款轻型的免费开源的数据库,是遵守ACID的关系型数据库管理系统,它包含在一个相对小的C库中。它是D.RichardHipp建立的公有领域项目。它的设计目标是嵌入式的,而且目前已经在很多嵌入式产品中使用了它,它占用资源非常的低,在嵌入式设备中,可能只需要几百K的内存就够了。它能够支持Windows/Linux/Unix等等主流的操作系统,同时能够跟很多程序语言相结合,比如 Tcl、C#、PHP、Java等,还有ODBC接口,同样比起Mysql、PostgreSQL这两款开源的世界著名数据库管理系统来讲,它的处理速度比他们都快。SQLite第一个Alpha版本诞生于2000年5月。 至2015年已经有15个年头,SQLite也迎来了一个版本 SQLite 3已经发布。

    SQLiteSpy管理工具下载:http://pan.baidu.com/s/1i5JQtBf

    2.7、用途、练习与兼容性

    所有需要将少量(不超过4M)数据存储在客户端的需求都适用,如密码,用户偏好(profile)等

    <!DOCTYPE html>
    <html>
    
    	<head>
    		<meta charset="UTF-8">
    		<title>登录</title>
    		<style type="text/css">
    			body {
    				background: #C0C0C0;
    			}
    			
    			p {
    				font-size: 11pt;
    			}
    			
    			fieldset {
    				background: darkseagreen;
    				border: 2px ridge gray;
    				border-radius: 8px;
    				 500px;
    				margin: 0 auto;
    				padding: 15px 50px 15px 50px;
    			}
    			
    			#btnLogin {
    				 60px;
    				height: 30px;
    				color: white;
    				font-size: 13pt;
    				background: gray;
    				border-radius: 5px;
    			}
    			
    			#btnLogin:hover {
    				background: darkseagreen;
    				cursor: pointer;
    			}
    		</style>
    	</head>
    
    	<body>
    		<fieldset>
    			<legend>用户登录</legend>
    			<p>用户名:<input type="text" name="txtLoginName" id="txtLoginName" value="" placeholder="请输入用户名" /></p>
    			<p>   密码:<input type="password" name="txtPwd" id="txtPwd" value="" placeholder="请输入密码" /></p>
    			<p>记住密码:<input type="checkbox" name="chkRemeber" id="chkRemeber" value="" /></p>
    			<p><input type="button" name="" id="btnLogin" value="登录" onclick="login()" /></p>
    			<p>登录次数:<label id=lblcount></label></p>
    		</fieldset>
    	</body>
    	<script type="text/javascript">
    		if(localStorage.count == "NaN") {
    			localStorage.count = 0;
    		}
    
    		if(localStorage.loginname != null) {
    			document.getElementById("txtLoginName").value = localStorage.loginname;
    
    			if(localStorage.pwd != null) {
    				document.getElementById("txtPwd").value = localStorage.pwd;
    			}
    		}
    
    		function login() {
    			localStorage.loginname = document.getElementById("txtLoginName").value;
    			var chk = document.getElementById("chkRemeber");
    			if(chk.checked) {
    				localStorage.pwd = document.getElementById("txtPwd").value;
    			}
    			localStorage.count = (parseInt(localStorage.count) + 1);
    
    			document.getElementById("lblcount").innerHTML = localStorage.count;
    			window.location.href = "index.html";
    		}
    	</script>
    
    </html>
    

      运行结果:

     

     

    三、sessionStorage

    将数据临时保存在客户端session对象中。session对象就是会话对象,session中存储的数据独立于每个客户,该数据会随着浏览器的关闭而消失。

    sessionStorage的操作api与localStorage基本一样,在不手动清除的情况下localStorage永久保存,而sessionStorage只是临时暂存。

     

    3.1、sessionStorage使用

    <!DOCTYPE html>
    <html>
    
    	<head>
    		<meta charset="UTF-8">
    		<title>sessionStorage本地存储</title>
    		<script type="text/javascript">
    			//添加
    			function Add() {
    				sessionStorage.name = "小明";
    				sessionStorage.setItem("sex", "男");
    				sessionStorage["age"] = 23;
    			}
    			//取值
    			function Get() {
    				var msg = document.getElementById("msg");
    				msg.innerHTML += sessionStorage.name+"<br/>"+
    				sessionStorage["sex"]+"<br/>"+
    				sessionStorage.getItem("age");
    			}
    
    			//修改
    			function Update() {
    				sessionStorage.name = "小红";
    				sessionStorage.setItem("sex", "女");
    				sessionStorage["age"] = 21;
    			}
    			//删除
    			function Delete() {
    				//删除单个
    				sessionStorage.removeItem("name");
    			}
    
    			function DeleteAll() {
    				//删除全部
    				sessionStorage.clear();
    			}
    			
    		</script>
    	</head>
    
    	<body>
    		<input type="button" name="btnAdd" id="btnAdd" value="添加" onclick="Add()" />
    		<input type="button" name="btnGet" id="btnGet" value="取值" onclick="Get()" />
    		<input type="button" name="btnUpdate" id="btnUpdate" value="修改" onclick="Update()" />
    		<input type="button" name="btnDelete" id="btnDelete" value="删除单个" onclick="Delete()" />
    		<input type="button" name="btnDeleteAll" id="btnDeleteAll" value="删除全部" onclick="DeleteAll()" />
    		<a href="d04.html" target="_blank">d02</a>
    		<h3 id="msg"></h3>
    	</body>
    
    </html>
    

     运行结果:

     可以实现在页面间传值,比如可以临时存储用户信息。

     

    3.2、Web本地存储事件监听 

    当程序修改localStorage与sessionStorage时将触发全局事件。

    当setItem(),removeItem()或者clear() 方法被调用,并且数据真的发生了改变时,就会触发storage事件,如果需要进行监听数据处理,通过以下方法:
    window.addEventListener(event,handleEvent, capture)
    event:设置成storage
    handleEvent:事件处理函数
    capture:事件处理顺序,一般设置成false,表示采用冒泡方式处理

    handleEvent处理事件的函数会接收到一个StorageEvent对象,该对象有以下属性:
    key:被修改的键。
    oldValue:修改前的值(如果是增加新的键值,则该属性为null)
    newValue:修改后的值(如果是删除键值,则该属性为null)
    url/uri:触发当前存储事件的页面的url

    注意:storage改变的时候,触发这个事件会调用所有同域下其他窗口的storage事件,不过它本身触发storage即当前窗口是不会触发这个事件的(当然ie这个特例除外,它包含自己本事也会触发storage事件)

     修改d02页面,监听值的变化。

    复制代码
    <!DOCTYPE html>
    <html>
        <head>
            <meta charset="UTF-8">
            <title>获得localStorage的值</title>
        </head>
        <body>
            <script type="text/javascript">
                console.log(localStorage.price);
                
                window.addEventListener("storage",function(obj){
                    alert(obj.oldValue+","+obj.newValue+","+obj.url);
                },true);
            </script>
        </body>
    </html>

    复制代码

    运行结果如下:

    3.3、cookie、sessionStorage、localStorage比较

     

    四、Web SQL Database 

    Web SQL Database 引入了一套使用 SQL 来操纵客户端数据库(client-side database)的 API,这些 API 是异步的(asynchronous),规范中所使用的 SQL 语言为 SQLite。Web SQL Database API 实际上未包含在 HTML 5 规范之中,它是一个独立的规范,它引入了一套使用 SQL 操作客户端数据库的 API,这些 API 有同步的,也有异步的, 同步版本的 API 只在工作线程(Worker Threads)上有用,由于并不是所有的浏览器都支持工作线程,一般情况下,都会使用异步 API。兼容情况如下:

    Web SQL Database可以让开发人员使用SQL语句操作客户端浏览器中嵌入的SQLite数据库 ,给开发人员提供了方便。对于简单的数据,使用sessionStorage和localStorage能够很好地完成存取,但是对于处理复杂的关系型数据,它就力不从心了。这也是 HTML 5 的“Web SQLDatabase”API 接口的应用所在。我把它理解成一个Html5环境下可以用Js执行CRUD的Web数据库

    三个核心方法
    openDatabase:这个方法使用现有数据库或创建新数据库创建数据库对象。
    transaction:这个方法允许我们根据情况控制事务提交或回滚。
    executeSql:这个方法用于执行真实的SQL查询。

    4.1、创建数据库

    使用openDatabase创建或打开数据库,如果存在就打开,如果不存在就创建,语法如下:

    openDatabase(a,b,c,d,e);

    a).数据库名称。
    b).版本号 目前为1.0
    c).对数据库的描述
    d).设置数据的大小,以Byte为单位
    e).回调函数(可省略)

        //创建数据库:Books
    	var db = openDatabase("Books", 1.0, "图书", 1024 * 1024 * 3, function() {
    	if(db)
    	log("创建数据库成功!");
    	});
    

    4.2 建表:

        //创建表
        function createTable() {
    		db.transaction(function(tx) {
    		tx.executeSql("create table  if not exists book(id integer primary key autoincrement,bookname text,price double,publish text)", [],
    				function(tx, result) {
    				log("创建表book成功!");
    			},
    			function(tx, error) {
    			log("创建失败!" + error.Message)
    			});
    		});
    		}
    

    4.3 删除表:

    //删除表
    function dropTable() {
    		db.transaction(function(tx) {
    			tx.executeSql("drop table book", [],
    				function(tx, result) {
    				log("删除表book成功!");
    				},
    				function(tx, error) {
    					log("删除失败!" + error.Message)
    				});
    				});
    			}
    

     4.4 demo全代码:

    <!DOCTYPE html>
    <html>
    
    	<head>
    		<meta charset="UTF-8">
    		<title>Web Database 的操作</title>
    
    	</head>
    
    	<body>
    		<button onclick="createTable()" value="">创建表</button>
    		<button onclick="dropTable()" value="">删除表</button>
    		<table border="1" cellspacing="0" cellpadding="0" id=tab_book width="98%" style="text-align: center;">
    			<tr>
    				<th>编号</th>
    				<th>书名</th>
    				<th>价格</th>
    				<th>出版社</th>
    				<th>删除</th>
    				<th>编辑</th>
    			</tr>
    		</table>
    		<fieldset>
    			<legend>添加数据</legend>
    			<input type="hidden" name="hidid" id="hidid" value="" />
    			<p><label for="bookname">    书名:</label><input type="text" name="bookname" id="bookname" value="" /></p>
    			<p><label for="price">    价格:</label><input type="text" name="price" id="price" value="" /></p>
    			<p><label for="publish">出版社:</label><input type="text" name="publish" id="publish" value="" />
    				<p><input type="button" name="add" id="add" value="添加" onclick="insert()" />
    					<input type="button" name="update" id="update" value="修改" onclick="update()" /></p>
    		</fieldset>
    		<h4 id="msg"></h4>
    
    		<script src="js/jquery-1.10.2.min.js" type="text/javascript" charset="utf-8"></script>
    		<script type="text/javascript">
    			//创建数据库:Books
    			var db = openDatabase("Books", 1.0, "图书", 1024 * 1024 * 3, function() {
    				if(db)
    					log("创建数据库成功!");
    			});
    
    			//创建表
    			function createTable() {
    				db.transaction(function(tx) {
    					tx.executeSql("create table  if not exists book(id integer primary key autoincrement,bookname text,price double,publish text)", [],
    						function(tx, result) {
    							log("创建表book成功!");
    						},
    						function(tx, error) {
    							log("创建失败!" + error.Message)
    						});
    				});
    			}
    
    			//删除表
    			function dropTable() {
    				db.transaction(function(tx) {
    					tx.executeSql("drop table book", [],
    						function(tx, result) {
    							log("删除表book成功!");
    						},
    						function(tx, error) {
    							log("删除失败!" + error.Message)
    						});
    				});
    			}
    
    			//添加数据
    			function insert() {
    				db.transaction(function(tx) {
    					tx.executeSql("insert into book(bookname ,price ,publish) values(?,?,?)", [$("#bookname").val(), $("#price").val(), $("#publish").val()],
    						function(tx, result) {
    							log("添加数据成功!");
    							show();
    						},
    						function(tx, error) {
    							log("添加数据失败!" + error.Message)
    						});
    				});
    			}
    			show();
    			//显示数据
    			function show() {
    				$("#tab_book tr:gt(0)").remove();
    				db.transaction(function(tx) {
    					tx.executeSql("select id,bookname,price,publish from book", [],
    						function(tx, result) {
    							for(var i = 0; i < result.rows.length; i++) {
    								var tr = $("<tr/>");
    								$("<td/>").text(result.rows.item(i)["id"]).appendTo(tr);
    								$("<td/>").text(result.rows.item(i)["bookname"]).appendTo(tr);
    								$("<td/>").text(result.rows.item(i)["price"]).appendTo(tr);
    								$("<td/>").text(result.rows.item(i)["publish"]).appendTo(tr);
    								var del = $("<a href='#' onclick='del(" + result.rows.item(i)['id'] + ",this)'>删除</a>");
    								var audit = $("<a href='#' onclick='edit(" + result.rows.item(i)['id'] + ",this)'>编辑</a>");
    								$("<td/>").append(del).appendTo(tr);
    								$("<td/>").append(audit).appendTo(tr);
    								tr.appendTo("#tab_book");
    							}
    						},
    						function(tx, error) {
    							log("查询数据失败!" + error.Message)
    						});
    				});
    			}
    			//删除数据			
    			function del(id, link) {
    				db.transaction(function(tx) {
    					tx.executeSql("delete from book where id=?", [id],
    						function(tx, result) {
    							log("删除数据成功!");
    							$(link).closest("tr").remove();
    						},
    						function(tx, error) {
    							log("删除数据失败!" + error.Message)
    						});
    				});
    			}
    
    			//编辑数据
    			function edit(id) {
    				db.transaction(function(tx) {
    					tx.executeSql("select id,bookname,price,publish from book where id=?", [id],
    						function(tx, result) {
    							$("#bookname").val(result.rows.item(0)["bookname"]);
    							$("#price").val(result.rows.item(0)["price"]);
    							$("#publish").val(result.rows.item(0)["publish"]);
    							$("#hidid").val(result.rows.item(0)["id"]);
    						},
    						function(tx, error) {
    							log("编辑数据失败!" + error.Message)
    						});
    				});
    			}
    			//修改数据
    			function update() {
    				db.transaction(function(tx) {
    					tx.executeSql("update book set bookname=?,price=? ,publish=? where id=?", [$("#bookname").val(), $("#price").val(), $("#publish").val(), $("#hidid").val()],
    						function(tx, result) {
    							log("修改数据成功!");
    							show();
    						},
    						function(tx, error) {
    							log("修改数据失败!" + error.Message)
    						});
    				});
    			}
    
    			//显示信息
    			function log(msg) {
    				$("#msg")[0].innerHTML += msg;
    			}
    		</script>
    	</body>
    
    </html>
    

      结果页面展示:

    4.8、数据库位置

    D:UsersAdministratorAppDataLocalGoogleChromeUser DataDefaultdatabases

    4.9、封装JavaScript

    前面的示例中javascript方法都直接暴露在window下,有可能与别的js冲突,可以进行简单封装。

    简单对象封装示例:

    复制代码
            var ticker={
                n:0,
                add:function()
                {
                    this.n++;
                },
                show:function()
                {
                    alert(this.n);
                }
            }
            ticker.add();
            ticker.add();
            ticker.show();
    复制代码

    运行结果:2 

     第一次封装后的代码,在整个window对象中只暴露dbApp对象,代码如下:

    <!DOCTYPE html>
    <html>
    
    	<head>
    		<meta charset="UTF-8">
    		<title>Web Database 的操作</title>
    
    	</head>
    
    	<body>
    		<div>
    			<button  value="" id="btnCreateTable">创建表</button>
    			<button  value="" id="btnDropTable">删除表</button>
    			<table border="1" cellspacing="0" cellpadding="0" id=tab_book width="98%" style="text-align: center;">
    				<tr>
    					<th>编号</th>
    					<th>书名</th>
    					<th>价格</th>
    					<th>出版社</th>
    					<th>删除</th>
    					<th>编辑</th>
    				</tr>
    			</table>
    			<fieldset>
    				<legend>添加数据</legend>
    				<input type="hidden" name="hidid" id="hidid" value="" />
    				<p><label for="bookname">    书名:</label><input type="text" name="bookname" id="bookname" value="" /></p>
    				<p><label for="price">    价格:</label><input type="text" name="price" id="price" value="" /></p>
    				<p><label for="publish">出版社:</label><input type="text" name="publish" id="publish" value="" />
    					<p><input type="button" name="btnAdd" id="btnAdd" value="添加"  />
    						<input type="button" name="btnUpdate" id="btnUpdate" value="修改" /></p>
    			</fieldset>
    			<h4 id="msg"></h4>
    		</div>
    		<script src="js/jquery-1.10.2.min.js" type="text/javascript" charset="utf-8"></script>
    		<script type="text/javascript">
    			//[封装一:]封装js
    			var appDb = {
    				//打开数据库
    				opendb: function() {
    					this.db=openDatabase("Books", 1.0, "图书", 1024 * 1024 * 3, function() {
    						if(db)
    							appDb.log("创建数据库成功!");
    					});
    				},
    				//初始化
    				init: function() {
    				    this.opendb();
    					this.bindEvent();
    					this.show();
    					appDb.log("初始化成功!");			
    				},
    				//绑定事件
    				bindEvent:function(){
    					$("#btnCreateTable").click(appDb.createTable);
    					$("#btnDropTable").click(appDb.dropTable);
    					$("#btnAdd").click(appDb.insert);
    					$("#btnUpdate").click(appDb.update);
    				},
    				//创建表
    				createTable: function() {
    					appDb.db.transaction(function(tx) {
    						tx.executeSql("create table  if not exists book(id integer primary key autoincrement,bookname text,price double,publish text)", [],
    							function(tx, result) {
    								appDb.log("创建表book成功!");
    							},
    							function(tx, error) {
    								appDb.log("创建失败!" + error.Message)
    							});
    					});
    				},
    				//删除表
    				dropTable: function() {
    					appDb.db.transaction(function(tx) {
    						tx.executeSql("drop table book", [],
    							function(tx, result) {
    								appDb.log("删除表book成功!");
    							},
    							function(tx, error) {
    								appDb.log("删除失败!" + error.Message)
    							});
    					});
    				},
    				//添加数据
    				insert: function() {
    					appDb.db.transaction(function(tx) {
    						tx.executeSql("insert into book(bookname ,price ,publish) values(?,?,?)", [$("#bookname").val(), $("#price").val(), $("#publish").val()],
    							function(tx, result) {
    								appDb.log("添加数据成功!");
    								appDb.show();
    							},
    							function(tx, error) {
    								appDb.log("添加数据失败!" + error.Message)
    							});
    					});
    				},
    				//显示数据
    				show: function() {
    					$("#tab_book tr:gt(0)").remove();
    					appDb.db.transaction(function(tx) {
    						tx.executeSql("select id,bookname,price,publish from book", [],
    							function(tx, result) {
    								for(var i = 0; i < result.rows.length; i++) {
    									var tr = $("<tr/>");
    									$("<td/>").text(result.rows.item(i)["id"]).appendTo(tr);
    									$("<td/>").text(result.rows.item(i)["bookname"]).appendTo(tr);
    									$("<td/>").text(result.rows.item(i)["price"]).appendTo(tr);
    									$("<td/>").text(result.rows.item(i)["publish"]).appendTo(tr);
    									var del = $("<a href='#' onclick='appDb.del(" + result.rows.item(i)['id'] + ",this)'>删除</a>");
    									var audit = $("<a href='#' onclick='appDb.edit(" + result.rows.item(i)['id'] + ",this)'>编辑</a>");
    									$("<td/>").append(del).appendTo(tr);
    									$("<td/>").append(audit).appendTo(tr);
    									tr.appendTo("#tab_book");
    								}
    							},
    							function(tx, error) {
    								appDb.log("查询数据失败!" + error.Message)
    							});
    					});
    				},
    				//删除数据			
    				del: function(id, link) {
    					appDb.db.transaction(function(tx) {
    						tx.executeSql("delete from book where id=?", [id],
    							function(tx, result) {
    								appDb.log("删除数据成功!");
    								$(link).closest("tr").remove();
    							},
    							function(tx, error) {
    								appDb.log("删除数据失败!" + error.Message)
    							});
    					});
    				},
    				//编辑数据
    				edit: function(id) {
    					appDb.db.transaction(function(tx) {
    						tx.executeSql("select id,bookname,price,publish from book where id=?", [id],
    							function(tx, result) {
    								$("#bookname").val(result.rows.item(0)["bookname"]);
    								$("#price").val(result.rows.item(0)["price"]);
    								$("#publish").val(result.rows.item(0)["publish"]);
    								$("#hidid").val(result.rows.item(0)["id"]);
    							},
    							function(tx, error) {
    								appDb.log("编辑数据失败!" + error.Message)
    							});
    					});
    				},
    				//修改数据
    				update: function() {
    						if($("#hidid").val()) {
    							appDb.db.transaction(function(tx) {
    								tx.executeSql("update book set bookname=?,price=? ,publish=? where id=?", [$("#bookname").val(), $("#price").val(), $("#publish").val(), $("#hidid").val()],
    									function(tx, result) {
    										appDb.log("修改数据成功!");
    										appDb.show();
    										$("#hidid").val("");
    									},
    									function(tx, error) {
    										appDb.log("修改数据失败!" + error.Message)
    									});
    							});
    						} else {
    							appDb.log("请先选择要操作的记录!")
    						}
    					},
    					//显示信息
    				log: function(msg) {
    					$("#msg")[0].innerHTML += msg+"<br/>";
    			}
    			}
    			appDb.init();
    		</script>
    	</body>
    
    </html>
    

      从上面的代码可以发现操作数据库,执行sql的方法存在大量的冗余,可以优化,优化后的代码如下:

    <!DOCTYPE html>
    <html>
    
    	<head>
    		<meta charset="UTF-8">
    		<title>Web Database 的操作</title>
    
    	</head>
    
    	<body>
    		<div>
    			<button value="" id="btnCreateTable">创建表</button>
    			<button value="" id="btnDropTable">删除表</button>
    			<table border="1" cellspacing="0" cellpadding="0" id=tab_book width="98%" style="text-align: center;">
    				<tr>
    					<th>编号</th>
    					<th>书名</th>
    					<th>价格</th>
    					<th>出版社</th>
    					<th>删除</th>
    					<th>编辑</th>
    				</tr>
    			</table>
    			<fieldset>
    				<legend>添加数据</legend>
    				<input type="hidden" name="hidid" id="hidid" value="" />
    				<p><label for="bookname">    书名:</label><input type="text" name="bookname" id="bookname" value="" /></p>
    				<p><label for="price">    价格:</label><input type="text" name="price" id="price" value="" /></p>
    				<p><label for="publish">出版社:</label><input type="text" name="publish" id="publish" value="" />
    					<p><input type="button" name="btnAdd" id="btnAdd" value="添加" />
    						<input type="button" name="btnUpdate" id="btnUpdate" value="修改" /></p>
    			</fieldset>
    			<h4 id="msg"></h4>
    		</div>
    		<script src="js/jquery-1.10.2.min.js" type="text/javascript" charset="utf-8"></script>
    		<script type="text/javascript">
    			//[封装二:]封装js
    			var appDb = {
    				//打开数据库
    				opendb: function() {
    					this.db = openDatabase("Books", 1.0, "图书", 1024 * 1024 * 3, function() {
    						if(db)
    							appDb.log("创建数据库成功!");
    					});
    				},
    				//初始化
    				init: function() {
    					this.opendb();
    					this.show();
    					this.bindEvent();					
    					appDb.log("初始化成功!");
    				},
    				//绑定事件
    				bindEvent: function() {
    					$("#btnCreateTable").click(appDb.createTable);
    					$("#btnDropTable").click(appDb.dropTable);
    					$("#btnAdd").click(appDb.insert);
    					$("#btnUpdate").click(appDb.update);
    				},
    				//通用sql
    				exeSql: function(sql, title, param, callback) {
    					title || 操作;
    					appDb.db.transaction(function(tx) {
    						tx.executeSql(sql, param || [],
    							function(tx, result) {
    								appDb.log(title + "成功!");
    								if(callback)
    								callback(result);
    							},
    							function(tx, error) {
    								appDb.log(title + "失败!" + error.Message)
    							});
    					});
    				},
    				//创建表
    				createTable: function() {
    					appDb.exeSql("create table  if not exists book(id integer primary key autoincrement,bookname text,price double,publish text)", "创建表");
    				},
    				//删除表
    				dropTable: function() {
    					appDb.exeSql("drop table book", "删除表");
    				},
    				//添加数据
    				insert: function() {
    					appDb.exeSql("insert into book(bookname ,price ,publish) values(?,?,?)", "添加数据", [$("#bookname").val(), $("#price").val(), $("#publish").val()], function(result) {
    						appDb.show();
    					});
    				},				
    				//显示数据
    				show: function() {					
    					appDb.exeSql("select id,bookname,price,publish from book", "显示", [], function(result) {	
    						$("#tab_book tr:gt(0)").remove();
    						for(var i = 0; i < result.rows.length; i++) {
    							var tr = $("<tr/>");
    							$("<td/>").text(result.rows.item(i)["id"]).appendTo(tr);
    							$("<td/>").text(result.rows.item(i)["bookname"]).appendTo(tr);
    							$("<td/>").text(result.rows.item(i)["price"]).appendTo(tr);
    							$("<td/>").text(result.rows.item(i)["publish"]).appendTo(tr);
    							var del = $("<a href='#' onclick='appDb.del(" + result.rows.item(i)['id'] + ",this)'>删除</a>");
    							var audit = $("<a href='#' onclick='appDb.edit(" + result.rows.item(i)['id'] + ",this)'>编辑</a>");
    							$("<td/>").append(del).appendTo(tr);
    							$("<td/>").append(audit).appendTo(tr);
    							tr.appendTo("#tab_book");
    						}
    					});
    				},
    				//删除数据			
    				del: function(id, link) {
    					appDb.exeSql("delete from book where id=?", "删除数据", [id], function(result) {
    						$(link).closest("tr").remove();
    					});
    				},
    				//编辑数据
    				edit: function(id) {
    					appDb.exeSql("select id,bookname,price,publish from book where id=?", "编辑数据", [id], function(result) {
    						$("#bookname").val(result.rows.item(0)["bookname"]);
    						$("#price").val(result.rows.item(0)["price"]);
    						$("#publish").val(result.rows.item(0)["publish"]);
    						$("#hidid").val(result.rows.item(0)["id"]);
    					});
    				},
    				//修改数据
    				update: function() {
    					if($("#hidid").val()) {
    						appDb.exeSql("update book set bookname=?,price=? ,publish=? where id=?", "修改", [$("#bookname").val(), $("#price").val(), $("#publish").val(), $("#hidid").val()], function() {
    							appDb.show();
    							$("#hidid").val("");
    						});
    					} else {
    						appDb.log("请先选择要操作的记录!")
    					}
    				},
    				//显示信息
    				log: function(msg) {
    					$("#msg")[0].innerHTML += msg + "<br/>";
    				}
    			}
    			appDb.init();
    </script> </body> </html>

    五、移动端打包与运行

    5.1、在手机端直接访问Web站点

    将手机与电脑连接到同一个网段,比如可以使用wifi

    查看本机ip地址,有时需要将本地连接禁用,查看ip地址的指令是ipconfig

    在手机端使用浏览器查看结果如下:

     

    5.2、打包成app安装运行

    这里使用HBuilder打包成apk的安装包,安装打包结果如下:

     

    5.3、套用移动端UI框架

    这里使用HBuilder内置的MUI为例,新增d06.html,页面脚本如下:

    代码如下:

    <!DOCTYPE html>
    <html>
    
    	<head>
    		<meta charset="utf-8">
    		<title>简易书架</title>
    		<meta name="viewport" content="width=device-width, initial-scale=1,maximum-scale=1,user-scalable=no">
    		<meta name="apple-mobile-web-app-capable" content="yes">
    		<meta name="apple-mobile-web-app-status-bar-style" content="black">
    		<!--标准mui.css-->
    		<link rel="stylesheet" href="css/mui.min.css">
    		<!--App自定义的css-->
    		<link rel="stylesheet" type="text/css" href="css/app.css" />
    		<style>
    			* {
    				font-family: "microsoft yahei";
    			}
    			
    			.cart {
    				 30px;
    				height: 25px;
    				text-align: right;
    				cursor: pointer;
    			}
    			
    			button {
    				border-radius: 5px;
    				height: 30px;
    				 50px;
    				padding: 5px;
    				background-color: #6495ED;
    				color: white;
    				cursor: pointer;
    			}
    			
    			button:hover {
    				background-color: darkorange;
    			}
    			
    			.title {
    				margin: 20px 15px 10px;
    				color: #6d6d72;
    				font-size: 15px;
    			}
    			
    			.oa-contact-cell.mui-table .mui-table-cell {
    				padding: 11px 0;
    				vertical-align: middle;
    			}
    			
    			.oa-contact-cell {
    				position: relative;
    				margin: -11px 0;
    			}
    			
    			.oa-contact-avatar {
    				 75px;
    			}
    			
    			.oa-contact-avatar img {
    				border-radius: 50%;
    			}
    			
    			.oa-contact-content {
    				 100%;
    			}
    			
    			.oa-contact-name {
    				margin-right: 20px;
    			}
    			
    			.oa-contact-name,
    			oa-contact-position {
    				float: left;
    			}
    			
    			html,body{height:100%;}
    .wrap{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;
    display:flex;-webkit-box-orient:vertical;-webkit-flex-direction:column;
    -ms-flex-direction:column;flex-direction:column;100%;height:100%;}
    .header,.footer{height:40px;line-height:40px;background-color:#D8D8D8;text-align:center;}
    .main{-webkit-box-flex:1;-webkit-flex:1;-ms-flex:1;flex:1;100%;}
    		</style>
    	</head>
    
    	<body>
    		<header class="mui-bar mui-bar-nav">
    			<a class="mui-action-back mui-icon mui-icon-left-nav mui-pull-left"></a>
    			<h1 class="mui-title">简易书架</h1>
    		</header>
    		<!--导航-->
    		<nav class="mui-bar mui-bar-tab">
    			<a class="mui-tab-item mui-active" href="#tabbar">
    				<span class="mui-icon mui-icon-home"></span>
    				<span class="mui-tab-label">最新图书</span>
    			</a>
    
    			<a class="mui-tab-item" href="#tabbar-with-chat">
    				<span class="mui-icon mui-icon-email"><span class="mui-badge">9</span></span>
    				<span class="mui-tab-label">新书上架</span>
    			</a>
    			<a class="mui-tab-item" href="#tabbar-with-contact">
    				<span class="mui-icon mui-icon-contact"></span>
    				<span class="mui-tab-label">图书管理</span>
    			</a>
    
    			<a class="mui-tab-item" href="#tabbar-with-map">
    				<span class="mui-icon mui-icon-gear"></span>
    				<span class="mui-tab-label">设置</span>
    			</a>
    		</nav>
    		<div class="mui-content">
    
    			<div id="tabbar" class="mui-control-content mui-active">
    
    				<ul class="mui-table-view mui-table-view-chevron">
    					<li id="switch" class="mui-table-view-cell">
    						定时轮播
    						<div class="mui-switch">
    							<div class="mui-switch-handle"></div>
    						</div>
    					</li>
    				</ul>
    			
    			<div id="slider" class="mui-slider">
    				<div class="mui-slider-group mui-slider-loop">
    					额外增加的一个节点(循环轮播:第一个节点是最后一张轮播)
    					<div class="mui-slider-item mui-slider-item-duplicate">
    						<a href="#">
    							<img src="images/yuantiao.jpg">
    						</a>
    					</div>
    					<!-- 第一张 -->
    					<div class="mui-slider-item">
    						<a href="#">
    							<img src="images/shuijiao.jpg">
    						</a>
    					</div>
    					<!-- 第二张 -->
    					<div class="mui-slider-item">
    						<a href="#">
    							<img src="images/muwu.jpg">
    						</a>
    					</div>
    					<!-- 第三张 -->
    					<div class="mui-slider-item">
    						<a href="#">
    							<img src="images/cbd.jpg">
    						</a>
    					</div>
    					<!-- 第四张 -->
    					<div class="mui-slider-item">
    						<a href="#">
    							<img src="images/yuantiao.jpg">
    						</a>
    					</div>
    					<!-- 额外增加的一个节点(循环轮播:最后一个节点是第一张轮播) -->
    					<div class="mui-slider-item mui-slider-item-duplicate">
    						<a href="#">
    							<img src="images/shuijiao.jpg">
    						</a>
    					</div>
    				</div>
    				<div class="mui-slider-indicator">
    					<div class="mui-indicator mui-active"></div>
    					<div class="mui-indicator"></div>
    					<div class="mui-indicator"></div>
    					<div class="mui-indicator"></div>
    				</div> <div >
    				<!--<button value="" id="btnCreateTable">创建表</button>
    				<button value="" id="btnDropTable">删除表</button>-->
    				<button value="" id="btnRefresh">刷新</button>
    				</div>
    				<div class="title">我的购物车</div>
    				<ul id="books_List_cart" class="mui-table-view mui-table-view-chevron">
    				</ul>
    				<div class="title">
    					图书列表
    				</div>
    				<ul id="books_List" class="mui-table-view mui-table-view-chevron">
    				</ul>
    			</div>
    		</div>
    			<div id="tabbar-with-chat" class="mui-control-content">
    				<div class="title">添加图书</div>
    			
    				<p><label for="bookname">书名:</label><input type="text" name="bookname" id="bookname" value="" /></p>
    				<p><label for="price">价格:</label><input type="text" name="price" id="price" value="" /></p>
    				<p><label for="publish">出版社:</label>
    					<input type="text" name="publish" id="publish" list="dl1" value="" />
    					<datalist id="dl1">
    						<option>北京出版社</option>
    						<option>玉林出版社</option>
    						<option>清华出版社</option>
    						<option>复旦出版社</option>
    						<option>河北出版社</option>
    					</datalist>
    					
    						<p><input type="button" name="btnAdd" id="btnAdd" value="添加" /></p>
    		
    			</div>
    			
    			<div id="tabbar-with-contact" class="mui-control-content">
    				<div class="wrap">
    			      <div class="header">图书管理</div>
    			 <div class="main">			 
    				<ul id="books_List2" class="mui-table-view mui-table-view-striped mui-table-view-condensed">
    				</ul>		
    			
    				<input type="hidden" name="hidid" id="hidid" value="" />
    				<p><label for="ubookname">书名:</label><input type="text" name="ubookname" id="ubookname" value="" /></p>
    				<p><label for="uprice">价格:</label><input type="text" name="uprice" id="uprice" value="" /></p>
    				<p>
    				<label for="upublish">出版社:</label>
    				<input type="text" name="upublish" id="upublish" list="dl1" value="" />					
    				<input type="button" name="btnUpdate" id="btnUpdate" value="修改" />
    				</p>
    		
    			</div>
    			<div class="footer"></div>
    			</div>
    			  </div>
    			
    			
    			<div id="tabbar-with-map" class="mui-control-content">
    				<div class="title">这是div模式选项卡中的第4个子页面,该页面展示一个常见的设置示例.</div>
    				<ul class="mui-table-view">
    					<li class="mui-table-view-cell">
    						<a class="mui-navigate-right">
    							新消息通知
    						</a>
    					</li>
    				</ul>
    
    			</div>
    		</div>
    
    	</body>
    	<script src="js/jquery-1.10.2.min.js" type="text/javascript" charset="utf-8"></script>
    	<script src="js/mui.min.js"></script>
    
    	<script>
    		//[封装二:]封装js
    		var appDb = {
    			//打开数据库
    			opendb: function() {
    				this.db = openDatabase("Books", 1.0, "图书", 1024 * 1024 * 3, function() {
    					if(db)
    						appDb.log("创建数据库成功!");
    				});
    			},
    			//初始化
    			init: function() {
    				this.opendb();
    				this.createTable();
    				this.show("#books_List");
    				this.show("#books_List2");
    				this.bindEvent();
    				appDb.log("初始化成功!");
    			},
    			//绑定事件
    			bindEvent: function() {
    				//$("#btnCreateTable").click(appDb.createTable);
    				//$("#btnDropTable").click(appDb.dropTable);
    				$("#btnAdd").click(appDb.insert);
    				$("#btnUpdate").click(appDb.update);
    				$("#btnDelete").click(appDb.del);
    				$("#btnRefresh").click(appDb.Refresh)
    			},
    			Refresh:function(){
    				appDb.show("#books_List2");
    					appDb.show("#books_List");
    			},
    			//通用sql
    			exeSql: function(sql, title, param, callback) {
    				title || 操作; //如果title为空,则取后面的'操作'
    				appDb.db.transaction(function(tx) {
    					tx.executeSql(sql, param || [],
    						function(tx, result) {
    							appDb.log(title + "成功!");
    							if(callback)
    								callback(result);
    						},
    						function(tx, error) {
    							appDb.log(title + "失败!" + error.Message)
    						});
    				});
    			},
    			//创建表
    			createTable: function() {
    				appDb.exeSql("create table  if not exists book(id integer primary key autoincrement,bookname text,price double,publish text,imgSrc text)", "创建表");
    			},
    			//删除表
    			dropTable: function() {
    				appDb.exeSql("drop table book", "删除表");				
    			},
    			//添加数据
    			insert: function() {
    				var id = parseInt(Math.random(8)*10);
    				
    				appDb.exeSql("insert into book(bookname ,price ,publish,imgSrc) values(?,?,?,?)", 
    				"添加数据", [$("#bookname").val(), $("#price").val(), $("#publish").val(), "images/"+id+".png"], function(result) {
    					appDb.show("#books_List2");
    					appDb.show("#books_List");
    				});
    			},
    			//显示数据 :bl为参数
    			show: function(bl) {
    				if(bl == null) {
    					bl = "#books_List";
    				}
    				appDb.exeSql("select id,bookname,price,publish,imgSrc from book", "显示", [], function(result) {
    					$(bl + " li").remove();
    					for(var i = 0; i < result.rows.length; i++) {
    						var li = $("<li class='mui-table-view-cell mui-media'/>");
    						var a = $("<a class='mui-navigate-right'/>");
    						var img = $("<img class='mui-media-object mui-pull-left'  src='" + result.rows.item(i)['imgSrc'] + "'/>");
    						var div = $("<div class='mui-media-body'/>");
    						var p = $("<p class='mui-ellipsis'/>");
    						a.appendTo(li);
    						img.appendTo(a);
    						div.append(result.rows.item(i)["bookname"]).appendTo(a);
    						p.text("价格仅售:" + result.rows.item(i)["price"] + "¥").appendTo(div);
    						if(bl == "#books_List") {
    							$("<p style='line-height: 30px;padding: 10px;'><img src='images/cart.png' class='cart'/><a onclick='appDb.Addcart(" + result.rows.item(i)['id'] + ")' href='#' >加入购物车</a><p/>").appendTo(p);
    						} else {
    							var del = $("<button onclick='appDb.del(" + result.rows.item(i)['id'] + ",this)'>删除</button>");
    							var audit = $("<button onclick='appDb.edit(" + result.rows.item(i)['id'] + ",this)'>编辑</button>");
    							del.appendTo(div);
    							audit.appendTo(div);
    						}
    
    						li.appendTo(bl);
    					}
    				});
    			},
    			//加入购物车
    			Addcart: function(id) {
    				appDb.exeSql("select id,bookname,price,publish,imgSrc from book where id=?", "显示", [id], function(result) {
    					var li = $("<li class='mui-table-view-cell mui-media'/>");
    					var a = $("<a class='mui-navigate-right'/>");
    					var img = $("<img class='mui-media-object mui-pull-left' src='" + result.rows.item(0)['imgSrc'] + "'/>");
    					var div = $("<div class='mui-media-body'/>");
    					var p = $("<p class='mui-ellipsis'/>");
    					a.appendTo(li);
    					img.appendTo(a);
    					div.append(result.rows.item(0)["bookname"]).appendTo(a);
    					p.text("价格" + result.rows.item(0)["price"] + "¥" + "  数量:1" ).appendTo(div);
    					//p.append("<input type='checkbox'/>").appendTo(div);
    					li.appendTo("#books_List_cart");
    				});
    			},
    			//删除数据			
    			del: function(id, link) {
    				appDb.exeSql("delete from book where id=?", "删除数据", [id], function(result) {
    					$(link).closest("li").remove();
    					appDb.show("#books_List2");
    					appDb.show("#books_List");
    				});
    			},
    			//编辑数据
    			edit: function(id) {
    				appDb.exeSql("select id,bookname,price,publish,imgSrc from book where id=?", "编辑数据", [id], function(result) {
    					//把数据加载到文本框中
    					$("#ubookname").val(result.rows.item(0)["bookname"]);
    					$("#uprice").val(result.rows.item(0)["price"]);
    					$("#upublish").val(result.rows.item(0)["publish"]);
    					$("#hidid").val(result.rows.item(0)["id"]);
    					
    				});
    			},
    			//修改数据
    			update: function() {
    				if($("#hidid").val()) {
    					appDb.exeSql("update book set bookname=?,price=? ,publish=? where id=?", "修改", 
    					[$("#ubookname").val(), $("#uprice").val(), $("#upublish").val(),  $("#hidid").val()],
    						function() {
    							appDb.show("#books_List2");
    							$("#hidid").val("");							
    						});
    				} else {
    					appDb.log("请先选择要操作的记录!")
    				}
    			},
    			//显示信息
    			log: function(msg) {
    				mui.toast(msg);
    			}
    		}
    
    		appDb.init();
    		//加载图片
    		
    		mui.init({
    			swipeBack: true //启用右滑关闭功能
    		});
    
    		var slider = mui("#slider");
    		document.getElementById("switch").addEventListener('toggle', function(e) {
    			if(e.detail.isActive) {
    				slider.slider({
    					interval: 5000
    				});
    			} else {
    				slider.slider({
    					interval: 0
    				});
    			}
    		});
    	</script>
    
    </html>
    

    六、示例下载

    github:https://github.com/oxl12345/Storage_1_1.git

      

  • 相关阅读:
    JS中的getter与setter
    Node.js中exports与module.exports的区别
    JS中的匿名函数自执行、函数声明与函数表达式
    JS实现千分位
    JS中的new操作符原理解析
    JS中null与undefined的区别
    JavaScript中callee与caller,apply与call解析
    解决vue路由与锚点冲突
    jQuery中deferred的对象使用
    Vue的生命周期
  • 原文地址:https://www.cnblogs.com/ouxinlian/p/6086894.html
Copyright © 2020-2023  润新知