关于localstorage:Vue-高德地图-API-Loca-如何使用-连接线图层动画脉冲图层

Vue 高德地图 API Loca 如何应用 连接线图层、动画脉冲图层浏览此文你须要曾经理解并把握的: 曾经会应用惯例地图的生成形式曾经理解如何载入 Loca 插件如果不理解,能够查看我之前的文章:高德地图 Vue 中 加载 数据可视化 Loca 的形式如何应用高德地图 API 做一个路线布局利用,展现自定义路线高德地图 API Loca 3D动画的阐明最终实现的成果:"感激大哥送来的火箭" 一、基础知识官网的连接线例子是一个与我国建交的国家连线图其中用到的两个数据源是: 建交的连接线数据: https://a.amap.com/Loca/static/static/diplomacy-line.json建交的点数据:https://a.amap.com/Loca/static/static/diplomacy-point.json1. 这个视图中蕴含四个图层:一个盛放各省名称的文字图层 AMap.LabelsLayer一个盛放各省坐标点地位动画的图层 Loca.ScatterLayer一个盛放指标点坐标点地位的图层 Loca.ScatterLayer一个显示脉冲连接线的图层 Loca.PulseLinkLayer2. 做一个这样的视图须要哪几个步骤:新建一个 map,并载入 Loca 插件。遍历所有省份数据,生成省份名称图层 AMap.LabelsLayer遍历所有省份数据,生成省份地理坐标标识图层 Loca.ScatterLayer生成指标点的标识图层 Loca.ScatterLayer遍历所有省份数据,每个数据包【含指标点】、【以后省份坐标点】两个坐标点的数据,依据两个地点数据生成一条脉冲连接线。而后生成所有省份的连接线最终使 Loca 的动画动起来即可3. 建设脉冲线须要理解的常识脉冲线的建设过程是这样的: // 建设图层let pulseLayer = new Loca.PulseLinkLayer(图层参数)// 设置数据源pulseLayer.setSource(数据参数)// 设置款式pulseLayer.setStyle(款式参数)4. 用到的数据如何生成Loca 图层接管的数据是 Loca.GeoJSONSource 格局的,而这个对象接管的内容格局是这样的 let locaLayerData = new Loca.GeoJSONSource({ data: { 'type': 'FeatureCollection', // 固定 'features': [ // 这里就是点的数组 { 'type': 'Feature', // 本例中咱们用到的 geometry 有两个格局,一种是 `Point` 一种是 `LineString`, 'geometry': { 'type': 'Point', 'coordinates': [121.504673, 25.046711], // 天文经纬度 }, }, ], },})这是 LineString 格局时的 geometry 构造, ...

March 25, 2022 · 7 min · jiezi

关于localstorage:ExpiredStorage给localstroage增加超时功能-源码解读

https://www.npmjs.com/package...这个库拓展了localStroage。在设置item的时候,会另外再设置一个key用来存储过期工夫。当在取数据的时候判断是否过期并且remove元素。 用法 expiredStorage = new ExpiredStorage();// 60秒后过期expiredStorage.setItem("test", "foobar", 60);// 永不过期expiredStorage.setItem("no_expire", "this will live forever");// 获取数据,如果过期会返回nullvar item = expiredStorage.getItem("test");// 获取剩余时间var timeLeft = expiredStorage.getTimeLeft("test");// 检测key是否过期var isExpired = expiredStorage.isExpired("test"); // 获取所有的key(如果includeExpired是true, 连过期没来的删除的key也一并返回)var keys = expiredStorage.keys(includeExpired); // 返回这个key的详情var data = expiredStorage.peek("test");源码解读构造函数,这块就是一些边界判断,没什么 function ExpiredStorage(storage) { ... this._storage = storage; }重点是setItem和getItem ExpiredStorage.prototype = { // 根底stroage类,在构造函数中赋值 _storage: null, // 工夫戳key的前缀 _expiration_key_prefix: "__expired_storage_ts__", // 获取以后工夫 单位秒 getTimestamp: function() { return Math.floor(((new Date).getTime()) / 1000); }, setItem: function(key, value, expiration) { // set item var ret = this._storage.setItem(key, value); // 存储这个key对应的过期工夫 (仅仅在expiration有值的时候) if (expiration) { this.updateExpiration(key, expiration); } return ret; }, // 更新key的过期工夫,新的key位工夫戳前缀加旧的key updateExpiration: function(key, expiration) { return this._storage.setItem(this._expiration_key_prefix + key, this.getTimestamp() + expiration); }, getItem: function(key) { // 判断是否过期,过期了就删除这一项,返回null if (this.isExpired(key)) { this.removeItem(key); return null; } // 没过期就失常返回 return this._storage.getItem(key); }, // 判断是否过期 isExpired: function(key) { var timeLeft = this.getTimeLeft(key); return timeLeft !== null && timeLeft <= 0; }, // 获取剩余时间 getTimeLeft: function(key) { // 通过工夫戳key拿到过期工夫 var expireTime = parseInt(this._storage.getItem(this._expiration_key_prefix + key)); // 取到值就返回剩余时间 if (expireTime && !isNaN(expireTime)) { return expireTime - this.getTimestamp(); } // 没取到值就返回null return null; }, // 同时删除key和工夫戳key removeItem: function(key) { var ret = this._storage.removeItem(key); this._storage.removeItem(this._expiration_key_prefix + key); return ret; },}非常简单就实现了一个比拟麻烦的需要,这个仓库曾经四年没更新,还放弃着每周1k左右的下载量,拜服!! ...

October 9, 2021 · 1 min · jiezi

关于localstorage:使用浏览器的-Local-Storage-真的安全吗

LocalStorage 是一个 HTML5 网络存储对象,用于将数据存储在客户端——即本地,在用户的计算机上。 本地存储的数据没有到期日期,并且会始终存在,直到被删除。 (相比之下,会话存储是另一个 HTML5 网络存储 API,它会在浏览器敞开时删除存储的数据。) 本地存储是纯 JavaScript。 同样,尽管它依然在用户的设施上生成纯文本文档,但本地存储也容许存储多达 5MB 的数据(与 4KB 的 cookie 相比)。 这使得本地存储的许多乏味利用成为可能,例如治理内容以缩小从服务器申请内容的须要,放慢加载工夫。 只读的localStorage 属性容许你拜访一个Document 源(origin)的对象 Storage;存储的数据将保留在浏览器会话中。localStorage 相似 sessionStorage,但其区别在于:存储在 localStorage 的数据能够长期保留;而当页面会话完结——也就是说,当页面被敞开时,存储在 sessionStorage 的数据会被革除 。 localStorage 最次要的特点是: 在同源的所有标签页和窗口之间共享数据。数据不会过期。它在浏览器重启甚至零碎重启后依然存在。sessionStorage 对象的应用频率比 localStorage 对象低得多。 属性和办法是雷同的,然而它有更多的限度: sessionStorage 的数据只存在于以后浏览器标签页。具备雷同页面的另一个标签页中将会有不同的存储。然而,它在同一标签页下的 iframe 之间是共享的(如果它们来自雷同的源)。数据在页面刷新后依然保留,但在敞开/从新关上浏览器标签页后不会被保留。应用 local storage 的一个例子: 创立一个 textarea 字段,每当其值发生变化时,能够将其“主动保留”。 因而,如果用户不小心敞开了页面,而后从新关上,他会发现之前未实现的输出依然保留在那里。 像这样: <!doctype html><textarea style="width:200px; height: 60px;" id="area" placeholder="Write here"></textarea><br><button onclick="localStorage.removeItem('area');area.value=''">Clear</button><script> area.value = localStorage.getItem('area'); area.oninput = () => { localStorage.setItem('area', area.value) };</script> 如果应用切当,本地存储能够成为功能强大的轻量级数据存储解决方案,但并非没有问题。 以下是应用本地存储可能不是一个好主见的几个起因,具体取决于存储的内容。 Why using LocalStorage might be a bad idea本地存储实质上并不比应用 cookie 更平安。了解了这一点后,该对象可用于存储从平安角度来看无关紧要的数据。然而,以下是重新考虑应用本地存储的几个起因。 ...

July 27, 2021 · 1 min · jiezi

关于localstorage:localstorage时效存储库介绍

localStorage 时效存储记得当年在优信贷面试时,跟面试官说了在 localstorage 做有时效的存储的想法。 这样的想法其实很多人都会有的,集体也置信很多企业在理论中必定也有这样的实际。 浏览器罕用存储说这个问题前咱们先说下当初前端的集中本地存储。 cookie这个是传统服务器和浏览器都可操作的共享存储。特点: 1. 存储空间想多无限,个别组多为 4m。2. 因为每次传输会后携带,所以过大的会减少申请量,减少申请工夫,升高性能。3. 绝对不太平安。4. 存储为字符串键值对。5. 能够增加时效。sessionSstorage这个是 H5 新加的性能,最大的特点是只存在于浏览器关上的以后 tab 页面。如果敞开 tab 或者不敞开再新关上一个 tab,都不会存在以后这个 tab 的 session 数据。 还有一个特点就是没有时效性。 localStorage这个也是 H5 新加的个性其特点跟 session 一样,只是如果不去被动革除那么会始终存储在用户的电脑上。 除此之外还有其余特点。 1. 只存在于用户本地,相比cookie平安。2. 存储大小一般来说没有限度。什么是 localstorage 时效存储简而言之就是给 Localstorage 或者 sessionStorage 的值增加一个时效,过了这个时效,在获取这个字段的值为空或者从新获取。 这样做的益处就是利用了本地 storage 的安全性,同时也把其时效变得可控,也缩小了每次申请数据的累赘。 场景实用于那些耗时的申请,且该申请的数据对生效也有肯定要求。比方市场地理分布,各种须要缓存的列表等。 解决方案具体的原理非常简单,就是应用对象存储这个数据,而后给该对象增加时效字段,之后在每次获取数据时,进行判断该字段是否过期。 过期后也能够做刷新操作。 cddStore 介绍获取 npm i cdd-lib引入 import { CddStore } from 'cdd-lib'存储数据 import { CddStore } from "cdd-lib";let store = new CddStore([ { key: "name", value: "cdd" }, //默认是半天 { key: "age", value: 23, hours: 4 }, //设置时效是4个小时 { key: "name2", value: "今晚打老虎", days: 0.8, hours: 4 }, //设置时效是0.8天加上4个小时 { key: "session", value: "sdfasf", hours: 0.5, refresh() { return "dfasdfas"; }, }, //设置生效是30分钟,过期后能够应用refresh主动获取]);应用数据 ...

August 10, 2020 · 1 min · jiezi

Vue-项目功能实现router-传递参数并解决刷新页面参数丢失问题

Vue Router 传参方式:1. this.$router.push({ name: '模块名称', params: { // 各参数 } })router.js:export default new Router({ routes: [ { path: '/paramsPassingByRouter', component: ParamsPassingByRouter, children: [ { path: 'paramsMode', name: 'paramsMode', component: ParamsMode } ] } ]})ParamsPassingByRouter.vue:<!-- html --><button @click="paramsMode(testData)">params传参</button><!-- js --><script>export default { data () { return { testData: { id: '20180101', name: '张三', aka: 'z3', age: '18' } } }, methods: { paramsMode (data) { this.$router.push({ name: 'paramsMode', params: this.testData }) } }}</script>ParamsMode.vue:<!-- html --><div class="params-mode">{{ testData }}</div><!-- js --><script>export default { data () { return { testData: {} } }, created () { this.testData = this.$route.params }}</script>效果:url:http://localhost:8081/#/paramsPassingByRouter/paramsMode 页面显示:{"id":"20180101","name":"张三","aka":"z3","age":"18"} ...

July 8, 2019 · 2 min · jiezi

Web-存储技术

一、背景介绍第一个Web存储的技术叫做Cookie,它是网站的身份证。是网站为了辨别用户身份,进行session(服务端的session)跟踪而存储在用户本地终端上的数据,也就是说它是存在电脑硬盘上的,一个很小的txt类型的文件。Cookie每次都会跟随http请求发送到服务端,也就是说每一个http请求都会带上我们的cookie数据,因此它存在一个安全性的问题。 cookie本身也是有很大的局限性的,首先它很小,主流的浏览器最大支持 4096 字节,除了最大字节的限制,每个网站的cookie个数(也就是每一个first每一个域)也是有限制的,一般浏览器是20个。除此之外,cookie还会默认跟随所有http请求发送,即使不需要使用这个cookie来鉴别用户但是它也是会跟随http请求发送的,这样就会造成一个网络资源的浪费。然后部分的浏览器还限制了总的cookie个数300个。 在cookie的诸多局限性下,Web Storage应运而生。Web Storage 解决了很多问题: 比如它支持存储大量数据,支持复杂的本地数据库,而且也不会默认跟随http请求。Web Storage主要是有四个: SessionStorageLocalStorageWebSQLindexedDB二、Cookie的简单介绍 Cookie是HTML4的一个标准,它一般不需要考虑兼容。它是网站的一个身份证,服务器可以针对不同用户,做出不同的响应。cookie存储在用户的机器上是一个纯文本,就是一个txt文件并不是一个脚本,它不能执行东西只负责记录。浏览器每次请求都会带上当前网站的cookie。 Cookie分为两种类型,一种呢是会话cookie,也就是临时性的cookie,退出浏览器或者是关闭即删除; 另一种叫持久cookie,它会一直存在,存在的时间由特定的过期时间或者是有效期来决定。 Cookie的域 Domain决定了当前的一个cookie的权限,哪一个域可以使用这个cookie。 Cookie的路径 Path,下面一个简单的例子: www.baidu.com id="123456" domain="www.baidu.com"www.baidu.com/user id="123456" user="eric" domain="www.baidu.com" path="/user/"www.baidu.com/search id="123456";www.baidu.com/user/search id="123456" user="eric";如上www.baidu.com设置了一个id等于123456,domain是www.baidu.com,然后另外一个跟第一个一样多设置了一个user,id相同,但是多了一个user=“Eric”,它的domain设置成了www.baidu.com,path就到了user下面。这两者设置完成之后,当我们访问www.baidu.com/search时百度只能拿到id,因为user="Eric"是属于user这个域下面的,也就是说在search下面是获取不到的,但是在www.baidu.com/user/search这个时候我们就可以获取到名叫Eric的user。Path也是一种权限的控制只是相较于域domain是低一级的。 Cookie的安全secure,如果这个属性为TRUE,那么网站只有在https的请求下面才会携带当前的cookie。 Cookie的HttpOnly这个属性如果为TRUE,那么就不允许JavaScript操作cookie。 因为cookie是存储在客户端一个独立的文件,因此服务器是无法分辨用户和攻击者的。关于cookie的目的分为两种:一种是跨站点脚本攻击,一种是跨站请求伪造。 三、SessionStorage key-value的键值对,是HTML5新增的一个会话存储对象。 SessionStorage是临时保存在同一窗口,也就是同一标签页的数据。如果当前标签页关闭了,那么SessionStorage也就失效了。这也是SessionStorage最显著的一个特点:单页标签限制。 除此之外,它还有的一些特点有: 同源策略,也就是在同一协议,同一主机名和同一端口下的同一tab只在本地存储,不会跟随http请求发送到服务器存储方式采用key-value键值对,这里面的value只能存字符串类型,如果存其他的会自动转换成字符串。存储上线限制达到了5MB,如果当前存储超出上限新的内容会把旧的内容覆盖但不会报错。属性: sessionStorage.length - 键值对数量sessionStorage.key(int index) -> nullsessionStorage.getItem(string key) -> nullsessionStorage[string key]sessionStorage.setItem(string key, string value)sessionStorage.removeItem(string key)sessionStorage.clear()Json对象 JSON.stringify()JSON.parse()四、LocalStorage LocalStorage也是在浏览器的Application下面有一个Local Storage,它和SessionStorage是十分相似的,同样是key-value键值对,也是HTML5的新增存储对象,它与SessionStorage的特点不同之处在于没有标签页的限制和在浏览器的无痕模式下LocalStorage是不允许读取的,永久性的存储,然后SessionStorage超出限制是覆盖不会报错而LocalStorage超出会报错。 特点: 同源策略,也就是在同一协议,同一主机名和同一端口下的同一tab没有标签页的限制只在本地存储,不会跟随http请求发送到服务器存储方式采用key-value键值对,这里面的value只能存字符串类型,如果存其他的会自动转换成字符串。存储上线限制达到了5MB,如果当前存储超出上限会报错。无痕模式下不可读取永久性存储属性: sessionStorage.length - 键值对数量sessionStorage.key(int index) -> nullsessionStorage.getItem(string key) -> nullsessionStorage[string key]sessionStorage.setItem(string key, string value)sessionStorage.removeItem(string key)sessionStorage.clear()注意事项:LocalStorage和SessionStorage在web view是不可靠的,web view指的是在开发混合APP的时候使用了浏览器来实现我们的APP,这个时候是不可靠的,因为在浏览器崩溃的情况下数据可能没有存进去。 另外一个在IOS浏览器中不可重复setItem,如果重复会报错,然后这个时候我们需要先removeItem再添加item。 监听storage的变化监听storage包括SessionStorage和LocalStorage。然后这里需要提到两个概念:同源和监听同源网页。 同源:协议、域名、端口三者相同,同源的情况下我们可以共享SessionStorage和LocalStorage。同源策略还禁止不同源执行任何脚本。 http://localhost:63342/simpleApp/app/index.html#/(协议) (域名) (端口) (路径)监听同源网页,但是同一网页是无效的 ...

July 8, 2019 · 3 min · jiezi

LocalStorage

先来几道面试题1、a.meituan.com 和 b.meituan.com 这两个域能够共享同一个 localStorage 吗?2、在 webview 中打开一个页面:i.meituan.com/home.html,点击一个按钮,调用 js 桥打开一个新的 webview:i.meituan.com/list.html,这两个分属不同webview的页面能共享同一个 localStorage 吗?3、如果 localStorage 存满了,再往里存东西,或者要存的东西超过了剩余容量,会发生什么?好了带着这些问题我们来往下看 1、基本方法// 用于存入数据window.localStorage.setItem('key', 'value');// 用于读取数据window.localStorage.getItem('key')//清除某个键名对应的键值localStorage.removeItem('key');// 用于清除所有保存的数据window.localStorage.clear()// localStorage.key()接受一个整数作为参数(从零开始),返回该位置对应的键值。localStorage.key(0)// Storage 接口储存的数据发生变化时,会触发 storage 事件,可以指定这个事件的监听函数。利用这个可以实现跨tab页通信window.addEventListener('storage', onStorageChange); 注意点:localStorage只能存String类型的字符串。存对象的时候会变成"[object Object]",因为({key:'xxx'}).toString()//"[object Object]"。这个时候我们可以通过JSON.stringify()。来帮我们实现转化。例如:localStorage.setItem('jsonString', JSON.stringify({key: 'mtt'}))2、作用域localStorage只要在相同的协议、相同的主机名、相同的端口下,就能读取/修改到同一份localStorage数据。这就回答了我们上面的前二个问题了,第一题:由于域名不同,不能进行共享。第二题:二个webview相当于同一个浏览器的不同标签页。所以可以共享。 与sessionStorage 、cookie 对比 localstorage在所有同源窗口中都是共享的;也就是说只要浏览器不关闭,数据仍然存在sessionStorage:不能在不同的浏览器窗口中共享,即使是同一个页面;cookie: 也是在所有同源窗口中都是共享的.也就是说只要浏览器不关闭,数据仍然存在3、数据存储有效期localStorage理论上来说是永久有效的,即不主动清空的话就不会消失,即使保存的数据超出了浏览器所规定的大小,也不会把旧数据清空而只会报错(这里解答了上面的第三题)。但需要注意的是,在移动设备上的浏览器或各Native App用到的WebView里,localStorage都是不可靠的,可能会因为各种原因(比如说退出App、网络切换、内存不足等原因)被清空。 与sessionStorage 、cookie 对比 localStorage:始终有效,窗口或浏览器关闭也一直保存,本地存储,因此用作持久数据;sessionStorage:仅在当前浏览器窗口关闭之前有效;cookie:只在设置的cookie过期时间之前有效,即使窗口关闭或浏览器关闭4、数据存储方面sessionStorage和localStorage不会自动把数据发送给服务器,仅在本地保存。cookie数据始终在同源的http请求中携带(即使不需要),即cookie在浏览器和服务器间来回传递。cookie数据还有路径(path)的概念,可以限制cookie只属于某个路径下$ 5、存储数据大小 cookie数据不能超过4K,同时因为每次http请求都会携带cookie、所以cookie只适合保存很小的数据,如会话标识。sessionStorage和localStorage虽然也有存储大小的限制,但比cookie大得多,可以达到5M或更大Web Storage拥有setItem、getItem、removeItem、clear等方法,不像cookie需要自己封装setCookie、getCookie等方法

April 23, 2019 · 1 min · jiezi

KV存储:Web的第一个内置模块

相信作为web开发者大家都使用过浏览器的本地存储localStorage,它是一个会阻止主线程的同步API,只要使用就可能会阻止页面的交互。我们都知道浏览器有异步的IndexedDB作为存储方案,只是它的API使用方式比localStorage要复杂很多。那么是否有既简单并且又不阻塞主线程的API呢?好消息是Chrome正在尝试一种称为内置模块的新功能,计划发布的第一个内置模块是名为KV Storage的异步键/值存储模块,先来了解一下什么是内置模块。内置模块内置模块就像常规JavaScript 模块一样,唯一区别是它们不必下载,而是随浏览器一起提供。与传统的Web API一样,内置模块必须经过标准化过程 - 每个模块都有自己的规范,需要进行设计审查,并且在发布之前需要Web开发人员和其他浏览器供应商提供支持。与传统的Web API不同,内置模块不会在全局范围内公开 它们只能通过导入获得。不全局暴露内置模块有很多优点:它们不会增加任何开销来启动新的JavaScript运行时上下文,并且不会消耗任何内存或CPU。此外,可以避免与当前上下文变量命名冲突的风险。在支持的浏览器中,您可以使用以下代码导入KV存储模块。import {storage, StorageArea} from ‘std:kv-storage’;KV存储模块KV存储模块的简单性与localStorage API类似,但其API形状实际上更接近 JavaScriptMap。除了getItem(), setItem()以及removeItem(),它还有get(), set()和delete()以及Map类似的方法,如 keys(), values()和 entries()。键名不必是字符串。它们可以是任何结构化可序列化类型。与Map不同的是,所有KV存储方法都返回 promises或 异步迭代器KV存储模块对外暴露两个变量:storage和StorageArea,其中storage是StorageArea名为’default’的默认实例。以下是如何在代码中使用KV存储模块的示例:import {storage} from ‘std:kv-storage’;const main = async () => { const oldPreferences = await storage.get(‘preferences’); document.querySelector(‘form’).addEventListener(‘submit’, async () => { const newPreferences = Object.assign({}, oldPreferences, { // Updated preferences go here… }); await storage.set(‘preferences’, newPreferences); });};main();如果浏览器不支持内置模块怎么办?对于不支持内置模块的浏览器,std:kv-storage无法识别成有效的URL,这时可以通过引入polyfill解决,但如果浏览器支持内置模块,我们更希望的是优先使用内置提供的模块,这就需要一个导入映射的功能,也是chrome正在尝试的一个新功能。导入映射导入映射本质上是一种机制,开发人员可以通过该机制将导入标识符另起别名,这可以为浏览器无法识别std:kv-storage的时侯提供备用路径。代码示例:<!– The import map is inlined into your page –><script type=“importmap”>{ “imports”: { “/path/to/kv-storage-polyfill.mjs”: [ “std:kv-storage”, “/path/to/kv-storage-polyfill.mjs” ] }}</script><!– Then any module scripts with import statements use the above map –><script type=“module”> import {storage} from ‘/path/to/kv-storage-polyfill.mjs’; // Use storage …</script>上面代码中的关键点是URL /path/to/kv-storage-polyfill.mjs 被映射到两个不同的资源:std:kv-storage和 /path/to/kv-storage-polyfill.mjs。因此,当浏览器遇到引用该URL(/path/to/kv-storage-polyfill.mjs)的import语句时,它首先尝试加载std:kv-storage,如果不能,则它会回退到加载 /path/to/kv-storage-polyfill.mjs。# 那么根本不支持模块的浏览器呢?为了使用导入映射有条件地加载内置模块,必须实际使用导入语句,这也意味着必须使用模块脚本,即<script type=“module”>。目前,超过80%的浏览器支持模块,对于不支持模块的浏览器,可以使用module/nomodule技术为旧浏览器兼容。注意,在生成nomodule时,需要包含所有的polyfills,因为不支持模块的浏览器肯定不支持内置模块。PS:欢迎关注公众号「前端新视界」获取前端技术前沿资讯,后台回复“1”领取100本PDF前端电子书籍。 ...

April 13, 2019 · 1 min · jiezi

用Class写一个记住用户离开位置的js插件

前言常见的js插件都很少使用ES6的class,一般都是通过构造函数,而且常常是手写CMD、AMD规范来封装一个库,比如这样:// 引用自:https://www.jianshu.com/p/e65c246beac1;(function(undefined) { “use strict” var _global; var plugin = { // … } _global = (function(){ return this || (0, eval)(’this’); }()); if (typeof module !== “undefined” && module.exports) { module.exports = plugin; } else if (typeof define === “function” && define.amd) { define(function(){return plugin;}); } else { !(‘plugin’ in _global) && (_global.plugin = plugin); }}());但现在都9102年了,是时候祭出我们的ES6大法了,可以用更优雅的的写法来实现一个库,比如这样:class RememberScroll { constructor(options) { … }}export default RememberScroll在这篇文章,博主主要通过分享最近自己写的一个记住页面滚动位置小插件,讲一下如何用class语法配合webpack 4.x和babel 7.x封装一个可用的库。项目地址:Github, 在线Demo:Demo喜欢的朋友希望能点个Star收藏一下,非常感谢。需求来源相信很多同学都会遇到这样一个需求:用户浏览一个页面并离开后,再次打开时需要重新定位到上一次离开的位置。这个需求很常见,我们平时在手机上阅读微信公众号的文章页面就有这个功能。想要做到这个需求,也比较好实现,但博主有点懒,心想有没有现成的库可以直接用呢?于是去GitHub上搜了一波,发现并没有很好的且符合我需求的,于是得自己实现一下。为了灵活使用(只是部分页面需要这个功能),博主在项目中单独封装了这个库,本来是在公司项目中用的,后来想想何不开源出来呢?于是有了这个分享,这也是对自己工作的一个总结。预期效果博主喜欢在做一件事情前先yy一下预期的效果。博主希望这个库用起来尽量简单,最好是插入一句代码就可以了,比如这样:<html><head> <meta charset=“utf-8”> <title>remember-scroll examples</title></head><body> <div id=“content”></div> <script src="../dist/remember-scroll.js"></script> <script> new RememberScroll() </script></body></html>在想要加上记住用户浏览位置的页面上引入一下库,然后new RememberScroll()初始化一下即可。下面就带着这个目标,一步一步去实现啦。设计方案1. 需要存哪些信息?用户浏览页面的位置,主要需要存两个字段:哪个页面和离开时的位置,通过这两个字段,我们才可以在用户第二次打开网站的页面时,命中该页面,并自动跳转到上一次离开的位置。2.存在哪?记住浏览位置,需要将用户离开前的浏览位置记录在客户端的浏览器中。这些信息可以主要存放在:cookie、sessionStorage、localStorage中。存放在cookie,大小4K,空间虽有限但也勉强可以。但cookie是每次请求服务器时都会携带上的,无形中增加了带宽和服务器压力,所以总体来说是不太合适的。存放在sessionStorage中,由于仅在当前会话下有效,用户离开页面sessionStorage就会被清除,所以不能满足我们的需求。存放在localStorage,浏览器可永久保存,大小一般限制5M,满足我们需求。综上,最后我们应该选择localStorage。3. 需注意的问题一个站点可能有很多页面,如何标识是哪个页面呢?一般来说可以用页面的url作为页面的唯一标识,比如:www.xx.com/article/${id},不同的id对应不同的页面。但博主考虑到现在很多站点都是用spa了,而且常见在url后面会带有#xxx的哈希值,如www.xx.com/article/${id}#tag1和www.xx.com/article/${id}#tag2这种情况,这可能表示的是同一个页面的不同锚点,所以用url作为页面的唯一标识不太可靠。因此,博主决定将这个页面唯一标识作为一个参数来让使用者来决定,姑且命名为pageKey,让使用者保证是全站唯一的即可。如果用户访问我们的站点中很多很多的页面,由于localStorage是永久保存的,如何避免localStorage不断累积占用过大?我们的需求可能仅仅是想近期记住即可,即只需要记住用户的浏览位置几天,可能会更希望我们存的数据能够自动过期。但localStorage自身是没有自动过期机制的,一般只能在存数据的时候同时存一下时间戳,然后在使用时判断是否过期。如果只能是在使用时才判断是否清除,而新访问页面时又会生成新的记录,localStorage中始终都会存在至少一条记录的,也就是说无法真正实现自动过期。这里不禁就觉得有点多余了,既然都是会一直保留记录在localStorage中,那干脆就不判断了,咱换一个思路:只记录有限的最新页面数量。举个例子:咱们网站有个文章页:www.xx.com/articles/${id},每个的id表示不同的文章,咱们只记录用户最新访问的5篇文章,即维护一个长度为5的队列。比如当前网站有id从1到100篇文章,用户分别访问第1,2,3,4,5篇文章时,这5篇文章都会记录离开的位置,而当用户打开第六篇文章时,第六条记录入队的同时第一条记录出队,此时localStorage中记录的是2,3,4,5,6这几篇文章的位置,这就保证了localStorage永远不会累积存储数据且旧记录会随着不断访问新页面自动“过期”。为了更灵活一点,博主决定给这个插件添加一个maxLength的参数,表示当前站点下记录的最新的页面最大数量,默认值设为5,如果有小伙伴的需求是记录更多的页面,可以通过这个参数来设置。4. 实现思路我们需要时刻监听用户浏览页面时的滚动条的位置,可以通过window.onscroll事件,获得当前的滚动条位置:scrollTop 。将scrollTop和页面唯一标识pageKey存进localStorage中。用户再次打开之前访问过的页面,在页面初始化时,读取localStorage中的数据,判断页面的pageKey是否一致,若一致则将页面的滚动条位置自动滚动到相应的scrollTop值。是不是很简单?不过实现的过程中需要注意一下细节,比如做一下防抖处理。实现步骤逼逼了这么久,是时候开始撸代码了。1.封装localStorage工具方法工欲善其事,必先利其器。为更好服务接下来的工作,咱们先简单封装一下调用localStorage的几个方法,主要是get,set,remove:// storage.jsconst Storage = { isSupport () { if (window.localStorage) { return true } else { console.error(‘Your browser cannot support localStorage!’) return false } }, get (key) { if (!this.isSupport) { return } const data = window.localStorage.getItem(key) return data ? JSON.parse(data) : undefined }, remove (key) { if (!this.isSupport) { return } window.localStorage.removeItem(key) }, set (key, data) { if (!this.isSupport) { return } const newData = JSON.stringify(data) window.localStorage.setItem(key, newData) }}export default Storage2. class大法class即类,本质上虽然是一个function,但使用class定义一个类会更直观。咱们为即将写的库起个名字为RememberScroll,开始就是如下的样子啦:import Storage from ‘./storage’class RememberScroll { constructor() { }}1.处理传进来的参数我们需要在类的构造函数constructor中接收参数,并覆盖默认参数。还记得上面咱们预期的用法吗?即new RememberScroll({pageKey: ‘myPage’, maxLength: 10})。 constructor (options) { let defaultOptions = { pageKey: ‘_page1’, // 当前页面的唯一标识 maxLength: 5 } this.options = Object.assign({}, defaultOptions, options)}如果没有传参数,就会使用默认的参数,如果传了参数,就使用传进来的参数。this.options就是最终处理后的参数啦。2.页面初始化当页面初始化时,咱们需要做三件事情:从loaclStorage取出缓存列表将滚动条滚动到记录的位置(若有记录的话);注册window.onscroll事件监听用户滚动行为;因此,需要在构造函数中就执行initScroll和addScrollEvent这两个方法:import Storage from ‘./utils/storage’class RememberScroll { constructor (options) { // … this.storageKey = ‘_rememberScroll’ this.list = Storage.get(this.storageKey) || [] this.initScroll() this.addScrollEvent() } initScroll () { // … } addScrollEvent () { // … }}这里咱们将localStorage中的键名命名为_rememberScroll,应该能够尽量避免和平常站点使用localStorage的键名冲突。3.监听滚动事件:addScrollEvent()的实现 addScrollEvent () { window.onscroll = () => { // 获取最新的位置,只记录垂直方向的位置 const scrollTop = document.documentElement.scrollTop || document.body.scrollTop // 构造当前页面的数据对象 const data = { pageKey: this.options.pageKey, y: scrollTop } let index = this.list.findIndex(item => item.pageKey === data.pageKey) if (index >= 0) { // 之前缓存过该页面,则替换掉之前的记录 this.list.splice(index, 1, data) } else { // 如果已经超出长度了,则清除一条最早的记录 if (this.list.length >= this.options.maxLength) { this.list.shift() } this.list.push(data) } // 更新localStorage里面的记录 Storage.set(this.storageKey, this.list) } }ps:这里最好需要做一下防抖处理4.初始化滚动条位置: initScroll()的实现initScroll () { // 先判断是否有记录 if (this.list.length) { // 当前页面pageKey是否一致 let currentPage = this.list.find(item => item.pageKey === this.options.pageKey) if (currentPage) { setTimeout(() => { // 一致,则滚动到对应的y值 window.scrollTo(0, currentPage.y) }, 0) }}细心的同学可能会发现,这里用了setTimeout,而不是直接调用window.scrollTo。这是因为博主在这里遇到坑了,这里涉及到页面加载执行顺序的问题。在执行window.scrollTo前,页面必须是已经加载完成了的,滚动条要已存在才可以滚动对吧。如果页面加载时直接执行,当时的scroll高度可能为0,window.scrollTo执行就会无效。如果页面的数据是异步获取的,也会导致window.scrollTo无效。因此用setTimeout会是比较稳的一个办法。5.将模块export出去最后我们需要将模块export出去,整体代码大概是这个样子:import Storage from ‘./utils/storage’class RememberScroll { constructor (options) { let defaultOptions = { pageKey: ‘_page1’, // 当前页面的唯一标识 maxLength: 5 } this.storageKey = ‘_rememberScroll’ // 参数 this.options = Object.assign({}, defaultOptions, options) // 缓存列表 this.list = Storage.get(this.storageKey) || [] this.initScroll() this.addScrollEvent() } initScroll () { // … } addScrollEvent () { // … }}export default RememberScroll这样就基本完成整个插件的功能啦,是不是很简单哈哈。篇幅原因就不贴具体代码了,可以直接到GitHub上看:remember-scroll打包接下来应该是本文的重点了,首先要清楚为什么要打包?将项目中所用到的js文件合并,只对外输出一个js文件。使项目同时支持AMD,CMD、浏览器<script>标签引入,即umd规范。配合babel,将es6语法转为es5语法,兼容低版本浏览器。PS: 由于webpack和babel更新速度很快,网上很多教程可能早已过时,现在(2019-03)的版本已经是babel 7.3.0,webpack 4.29.6, 本篇文章只分享现在的最新的配置方法,因此本篇文章也是会过时的,读者们请注意版本号。npm init项目咱们先新建一个目录,这里名为:remember-scroll,然后将上面写好的remember-scroll.js放进remember-scroll/src/目录下。PS:一般项目的资源文件都放在src目录下,为了显得专业点,最好将remember-scroll.js改名为index.js。)此时项目还没有package.json文件,因此在根目录执行命令初始化package.json:npm init需要根据提示填写一些项目相关信息。安装webpack和webpack-cli运行webpack命令时需要同时装上webpack-cli:npm i webpack webpack-cli -D配置webpack.config.js在根目录中添加一个webpack.config.js,按照webpack官网的示例代码配置:const path = require(‘path’);module.exports = { entry: ‘./src/index.js’, output: { path: path.resolve(__dirname, ‘dist’), filename: ‘remember-scroll.js’ // 修改下输出的名称 }};然后在package.json的script中配置运行webpack的命令: “scripts”: { “test”: “echo "Error: no test specified" && exit 1”, “dev”: “webpack –mode=development –colors” },这样配置完成,在根目录运行npm run dev,会自动生成dist/remember-scroll.js。此时已经实现了我们的第一个小目标:赚它一个亿,哦不,是将storage.js和index.js合并输出为一个remember-scroll.js。这种简单的打包可以称为:非模块化打包。由于我们在js文件中没有通过AMD的return或者CommonJS的exports或者this导出模块本身,导致模块被引入的时候只能执行代码而无法将模块引入后赋值给其它模块使用。支持umd规范相信很多同学都听过AMD,CommonJS规范了,不清楚的同学可以看看阮一峰老师的介绍:Javascript模块化编程(二):AMD规范。为了让我们的插件同时支持AMD,CommonJS,所以需要将我们的插件打包为umd通用模块。之前看过一篇文章:如何定义一个高逼格的原生JS插件,在没有使用webpack打包时,需要在插件中手写支持这些模块化的代码:// 引用自:https://www.jianshu.com/p/e65c246beac1;(function(undefined) { “use strict” var _global; var plugin = { // … } // 最后将插件对象暴露给全局对象 _global = (function(){ return this || (0, eval)(’this’); }()); if (typeof module !== “undefined” && module.exports) { module.exports = plugin; } else if (typeof define === “function” && define.amd) { define(function(){return plugin;}); } else { !(‘plugin’ in _global) && (_global.plugin = plugin); }}());博主看到这坨东西,也是有点晕,不得不佩服大佬就是大佬。还好现在有了webpack,我们现在只需要写好主体关键代码,webpack会帮我们处理好这些打包的问题。在webpack4中,我们可以将js打包为一个库的形式,详情可看:[Webpack Expose the Library](https://webpack.js.org/guides…。在我们这里只需在output中加上library属性:const path = require(‘path’);module.exports = { entry: ‘./src/index.js’, output: { path: path.resolve(__dirname, ‘dist’), filename: ‘remember-scroll.js’, library: ‘RememberScroll’, libraryTarget: ‘umd’, libraryExport: ‘default’ }};注意libraryTarget为umd,就是我们要打包的目标规范为umd。当我们在html中通过script标签引入这个js时,会在window下注册RememberScroll这个变量(类似引入jQuery时会在全局注册$这个变量)。此时就直接使用RememberScroll这个变量了。<script src="../dist/remember-scroll.js"></script><script> console.log(RememberScroll)</script>这里有个坑需要注意一下,如果没有加上libraryExport: ‘default’,由于我们代码中是export default RememberScroll,打包出来的代码会类似:{ ‘default’: { initScroll () {} }}而我们期望的是这样:{ initScroll () {}}即我们希望的是直接输出default中的内容,而不是隔着一层default。所以这里还要加上libraryExport: ‘default’,打包时只输出default的内容。PS: webpack英文文档看得有点懵逼,这个坑让博主折腾了很久才爬起来,所以特别讲下。刚兴趣的同学可以看下文档:output.libraryExport。到这里,已经实现了我们的第二个小目标:支持umd规范。使用babel-loader上面我们打包出来的js,其实已经可以正常运行在支持es6语法的浏览器中了,比如chrome。但想要运行在IE10,IE11中,还得让神器Babel帮我们一把。PS: 虽然很多人说不考虑兼容IE了,但作为一个通用性的库,古董级的IE7,8,9可以不兼容,但较新版本的IE10,11还是需要兼容一下的。Babel是一个JavaScript转译器,相信大家都听过。由于JavaScript在不断的发展,但是浏览器的发展速度跟不上,新的语法和特性不能马上被浏览器支持,因此需要一个能将新语法新特性转为现代浏览器能理解的语法的转译器,而Babel就是充当了转译器的角色。PS:以前博主一直以为(相信很多刚接触Babel的同学也是这样),只要使用了Babel,就可以放心无痛使用ES6的语法了,然而事情并不是这样。Babel编译并不会做polyfill,Babel为了保证正确的语义,只能转换语法而不会增加或修改原有的属性和方法。要想无痛使用ES6,还需要配合polyfill。不太理解的同学,在这里推荐大家看下这篇文章:21 分钟精通前端 Polyfill 方案,写得非常通俗易懂。总的来说,就是Babel需要配合polyfill来使用。Babel更新比较频繁,网上搜出来的很多配置教程是旧版本的,可能并不适用最新的Babel 7.x,所以我们这里折腾一下最新的webpack4配置Babel方案:babel-loader。1.安装babel-loader,@babel/core、@babel/preset-env。npm install -D babel-loader @babel/core @babel/preset-env core-jscore-js是JavaScript模块化标准库,在@babel/preset-env按需打包时会使用core-js中的函数,因此这里也是要安装的,不然打包的时候会报错。2.修改webpack.config.js配置,添加rulesconst path = require(‘path’);module.exports = { entry: ‘./src/index.js’, output: { path: path.resolve(__dirname, ‘dist’), filename: ‘remember-scroll.js’, library: ‘RememberScroll’, libraryTarget: ‘umd’, libraryExport: ‘default’ }, module: { rules: [ { test: /.m?js$/, exclude: /(node_modules|bower_components)/, use: { loader: ‘babel-loader’ } } ] }};表示.js的代码使用babel-loader打包。3.在根目录新建babel.config.js,参考Babel官网const presets = [ [ “@babel/env”, { targets: { browsers: [ “last 1 version”, “> 1%”, “maintained node versions”, “not dead” ] }, useBuiltIns: “usage”, }, ],];browsers配置的是目标浏览器,即我们想要兼容到哪些浏览器,比如我们想兼容到IE10,就可以写上IE10,然后webpack会在打包时自动为我们的库添加polyfill兼容到IE10。博主这里用的是推荐的参数,来自:npm browserslist,这样就能兼容到大多数浏览器啦。配置好后,npm run dev打包即可。此时,我们已经实现了第三个小目标:兼容低版本浏览器。生产环境打包npm run dev打包出来的js会比较大,一般还需要压缩一下,而我们可以使用webpack的production模式,就会自动为我们压缩js,输出一个生产环境可用的包。在package.json再添加一条build命令: “scripts”: { “test”: “echo "Error: no test specified" && exit 1”, “build”: “webpack –mode=production -o dist/remember-scroll.min.js –colors”, “dev”: “webpack –mode=development –colors” },这里同时指定了输出的文件名为:remember-scroll.min.js,一般生产环境就是使用这个文件啦。发布到npm经过上面的步骤,我们已经写完这个库,有需求的同学可以将库发布到npm,让更多的人可以方便用到你这个库。在发布到npm前,需要修改一下package.json,完善下描述作者之类的信息,最重要的是要添加main入口文件:{ “main”: “dist/remember-scroll.min.js”,}这样别人使用你的库时,可以直接通过import RememberScroll from ‘remember-scroll’来使用remember-scroll.min.js。发布步骤:先到https://www.npmjs.com/注册一个账号,然后验证邮箱。然后在命令行中输入:npm adduser,输入账号密码邮箱登录。运行npm publish上传包,几分钟后就可以在npm搜到你的包了。至此,基本就完成一个插件的开发发布过程啦。不过一个优秀的开源项目,还应该要有详细的说明文档,使用示例等等,大家可以参考下博主这个项目的README.md, 中文README.md。最后文章写了好几天了,可谓呕心沥血,虽然比较啰嗦,但应该比较清楚地交代了如何运用ES6语法从零写一个记住用户离开位置的js插件,也很详细地讲解了如何用最新的webpack打包我们的库,希望能让大家都有所收获,也希望大家能到GitHub上点个Star鼓励一下啦。remember-scroll这个插件其实几个月前就已经发布到npm了,一直比较忙(懒)没写章分享。虽然功能简单但很有诚意,能兼容到IE9。使用起来也非常方便简单,可直接通过script标签cdn引入,也可以在vue中import RememberScroll from ‘remember-scroll’使用。文档中有详细的使用示例:script标签使用方式vue中使用方式vue异步获取数据时使用方式项目地址Github,在线Demo。欢迎大家评论交流,也欢迎PR,同时希望大家能点个Star鼓励一下啦。 ...

March 19, 2019 · 3 min · jiezi

react开发 Markdown文本编译器

我用react开发了一个Markdown文本编辑器主要用到: marked github-markdown-css库https://github.com/ilvseyinfu… 地址在这

March 7, 2019 · 1 min · jiezi

以太坊开发时如何保证DApp本地存储localStorage的安全性?

部署去中心化应用程序dapp会引入一些有趣的安全性考虑因素,这些因素可能不会出现在更传统的开发中。我们如何保证dApp本地存储的安全性?提出这个问题的原因是我们在使用Colony dApp时遇到的一个重要障碍,那就是如何应对在使用IPFS或Swarm等分布式存储系统保持本地存储的dApp数据安全挑战。在本文中,我将从dApp开发人员的角度来看一下这个问题,然后研究一些可能的解决方案。共享本地存储localStorage的问题IPFS运行本地节点node,它与Web服务器捆绑在一起。捆绑的Web服务器使节点可以轻松地相互连接并共享网络中其他位置可能需要的数据。作为一个去中心化的应用程序构建器,你将依赖该Web服务器将你的内容从一个节点推送到另一个节点,从而使其可以根据需要立即供最终用户使用。假设你正在完全去中心化full decentralized并且正在避免使用DNS或Web代理等任何内容来跟踪你的内容在网络上的位置,那么访问dApp的方式通常是通过浏览器使用其查询本地节点哈希,如:http://localhost:8080/QmcefGgoVLMEPyVKZU48XB91T3zmtpLowbMK6TBM1q4Dw/现在,假设在正常使用期间,你的应用程序将在浏览器的localStorage保存数据:可能需要传递一些数据,或者保持本地用户交互的队列,以最大限度地减少链上交易并节省gas成本。浏览器中的本地存储仅限于特定的地址上下文(域和端口)。IPFS节点是获取此上下文的,这意味着通过IPFS Web服务器运行的任何去中心化应用程序将使用具有读写访问权限的相同localStorage。这可能是一个大问题。默认情况下,dApp的某些helper依赖项使用localStorage临时将密钥保存在纯文本中。这些数据不应该被看到的一天。另一个潜在的泄漏问题是保存其内存状态的软件包,以便以后可以恢复。类似Flux-like的库通常(相对)安全,因为它们只在内存中运行,但启用持久性状态会将该内存状态放入localStorage,从而将其打开给潜在的攻击者。缓解问题的策略不幸的是,安全没有灵丹妙药:作为一名dApp开发人员,为安全起见所做的任何调整都可能需要在开发的其他方面做出一些让步。以下是你可以做出的一些妥协:不存储任何数据这当然是最安全的方法,但它有点像烧毁你的房子来摆脱蟑螂。在本地存储数据的dApp中有许多功能和基本行为,删除太多后可能没有应用程序存在的意义了。此外,有许多库默认使用localStorage,你必须手动检查每个依赖项并删除任何需要它的库,否则就得自己修改库。加密一切这在理论上更有前途,特别是因为大多数dApp开发人员已经在看板上保持默认加密。加密的local storage值实际上,加密所有本地存储有点麻烦。要加密数据,必须有一个密钥:但是用户不能将该密钥存储在dApp中,因为它将被放在localStorage,这样做你就将回到原点。一种解决方案是使用钱包:你的dApp可能会以某种方式与区块链进行交互,要求用户解锁其钱包以发送和签署交易。由于无论如何都需要钱包与dApp交互,因此可以使用每个帐户的私钥privatekey来加密本地存储。然而,这也有一些缺点:每次想要与localStorage交互时,您都必须询问用户的纯文本私钥。像MetaMask这样的密钥管理软件不起作用,因为它永远不会暴露用户的私钥。使用Swarm和MistMist是作为dApp和以太坊浏览器构建的,因此它为该问题提供了一些特殊优势。默认情况下,Mist支持Swarm的bzz协议,因此你可以设置一个ens地址指向dApp的哈希值,然后使用Mist无需担心地浏览你的dApp。不幸的是,这只会解决通过Mist访问dApp的用户的问题。运行本地Swarm节点的用户仍然必须通过localhost访问,localhost仍然(可能)将数据泄露给其他dApp。为你的dApp创建一个浏览器扩展通过浏览器扩展程序运行你的应用程序将导致它获得单独的上下文(它将不再在localhost:8080),但它有点减弱了去中心化应用程序的目的,必须要依赖于像Chrome网络商店这样的中央权威机构用于管理和分配。此外,现在你必须为要支持的每个浏览器创建和维护单独的扩展,并通过其自己的特定集中式应用商店进行更新。不爽。创建一个独立的桌面应用程序和以前一样,创建独立应用程序是将dApp分离到自己的上下文的一种方式,这意味着它将获得自己的包装器(在本例中为electron)。独立的桌面应用程序具有额外的好处,可以捆绑外部库和你可能需要的任何其他内容,包括IPFS本身的单独实例。和以前说的一样,要有一些让步:除非你想要专门在bittorrent上分发应用程序,否则你需要找到一个集中托管的解决方案来进行分发和维护。你必须为electron桌面应用程序维护一个单独的存储库。如果你想将IPFS用于任何其他服务,你可能最终会在同一台计算机上运行多个节点,这可能会变得混乱。将你的应用代理到域名通过使用其他Web服务器代理本地节点,有两个优点:首先,现在你的dApp有一个友好的友好的人类可读地址,而不是一个冗长的哈希。其次,你的应用程序将拥有自己的上下文,并且不会共享localStorage。然而,代理确实跨越了“真正的”去中心化,用户将再次不得不依靠中央服务器来访问去中心化的服务。哎。对于去中心化的应用程序开发人员来说,现在还处于早期阶段。这种问题在新兴的“去中心化协议栈”中无处不在“:而且在我们提出更优雅的解决方案之前可能还需要一段时间。将来,在浏览器中支持本机IPFS或Swarm节点可以解决这个问题,并且无需将Web服务器与去中心化的文件存储捆绑在一起。用户可以输入类似ipfs://QmcefGgoVLMEPyVKZU48XB91T3zmtpLowbMK6TBM1q4Dw/并直接访问dApp,并为每个唯一的哈希分配自己的上下文。Mist和IPFS团队意识到了这个问题,并希望将未来版本中的解决方案纳入其中。但是现在,找到我们可以采用的解决方法并与社区的其他人分享它们会很有帮助。如果你是一名开发自己的去中心化应用程序dapp的开发人员,希望本文对没构建代码以避免数据泄漏有所帮助,如果你设计了另一个上面未提及的解决方案,请分享它!感谢Alex Rea和Griffin Hotchkiss帮助起草本文。======================================================================分享一些以太坊、EOS、比特币等区块链相关的交互式在线编程实战教程:java以太坊开发教程,主要是针对java和android程序员进行区块链以太坊开发的web3j详解。python以太坊,主要是针对python工程师使用web3.py进行区块链以太坊开发的详解。php以太坊,主要是介绍使用php进行智能合约开发交互,进行账号创建、交易、转账、代币开发以及过滤器和交易等内容。以太坊入门教程,主要介绍智能合约与dapp应用开发,适合入门。以太坊开发进阶教程,主要是介绍使用node.js、mongodb、区块链、ipfs实现去中心化电商DApp实战,适合进阶。C#以太坊,主要讲解如何使用C#开发基于.Net的以太坊应用,包括账户管理、状态与交易、智能合约开发与交互、过滤器和交易等。EOS教程,本课程帮助你快速入门EOS区块链去中心化应用的开发,内容涵盖EOS工具链、账户与钱包、发行代币、智能合约开发与部署、使用代码与智能合约交互等核心知识点,最后综合运用各知识点完成一个便签DApp的开发。java比特币开发教程,本课程面向初学者,内容即涵盖比特币的核心概念,例如区块链存储、去中心化共识机制、密钥与脚本、交易与UTXO等,同时也详细讲解如何在Java代码中集成比特币支持功能,例如创建地址、管理钱包、构造裸交易等,是Java工程师不可多得的比特币开发学习课程。php比特币开发教程,本课程面向初学者,内容即涵盖比特币的核心概念,例如区块链存储、去中心化共识机制、密钥与脚本、交易与UTXO等,同时也详细讲解如何在Php代码中集成比特币支持功能,例如创建地址、管理钱包、构造裸交易等,是Php工程师不可多得的比特币开发学习课程。tendermint区块链开发详解,本课程适合希望使用tendermint进行区块链开发的工程师,课程内容即包括tendermint应用开发模型中的核心概念,例如ABCI接口、默克尔树、多版本状态库等,也包括代币发行等丰富的实操代码,是go语言工程师快速入门区块链开发的最佳选择。汇智网原创翻译,转载请标明出处。这里是原文

December 18, 2018 · 1 min · jiezi

JavaScript 进阶知识 - HTML5篇

认识HTML5HTML5并不仅仅只是作为HTML标记语言的一个最新版本,更重要的是它制定了Web应用开发的一系列标准,成为第一个将Web做为应用开发平台的HTML语言。HTML5定义了一系列新元素,如新语义标签、智能表单、多媒体标签等,可以帮助开发者创建富互联网应用,还提供了一系列Javascript API,如地理定位、重力感应、硬件访问等,可以在浏览器内实现类原生应用,甚至结合Canvas我们可开发网页版游戏,同时结合CSS3的过渡、转换、动画等特性,可以极大的增强用户体验,提升开发功能的可应用性。我们日常讨论的H5其实是一个泛称,它指的是由HTML5 + CSS3 + Javascript等技术组合而成的一个应用开发平台。2. 语法规范随着Web技术的更新,HTML也先后经历了HTML4.01、XHTML1.0、HTML5几个重要的版本,在版本的演变过程中新增或废弃了一些属性,同时对语法规范也做了一些调整,为了能够保证浏览器可以兼容不同版本语法规范的,我们可以使用<!DOCTYPE>指示浏览器应该如何处理我们的HTML。常用的DOCTYPE声明:1、HTML5<!DOCTYPE html>2、HTML 4.01 Strict4.01的严格版本,该DTD包含所有HTML元素和属性,但不包括展示性的和弃用的元素(比如font)。不允许框架集(Framesets)。<!DOCTYPE HTML PUBLIC “-//W3C//DTD HTML 4.01//EN” “http://www.w3.org/TR/html4/strict.dtd">3、HTML 4.01 Transitional该DTD包含所有HTML元素和属性,包括展示性的和弃用的元素(比如font)。不允许框架集(Framesets)。<!DOCTYPE HTML PUBLIC “-//W3C//DTD HTML 4.01 Transitional//EN” “http://www.w3.org/TR/html4/loose.dtd">4、HTML 4.01 Frameset该DTD等同于HTML 4.01 Transitional,但允许框架集内容。<!DOCTYPE HTML PUBLIC “-//W3C//DTD HTML 4.01 Frameset//EN” “http://www.w3.org/TR/html4/frameset.dtd">5、XHTML 1.0 Strict该DTD包含所有HTML元素和属性,但不包括展示性的和弃用的元素(比如font)。不允许框架集(Framesets)。必须以格式正确的XML来编写标记。<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Strict//EN” “http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">6、XHTML 1.0 Transitional该DTD包含所有HTML元素和属性,包括展示性的和弃用的元素(比如font)。不允许框架集(Framesets)。必须以格式正确的XML来编写标记。<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN” “http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">7、XHTML 1.0 Frameset该DTD等同于XHTML 1.0 Transitional,但允许框架集内容。<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Frameset//EN” “http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">8、XHTML 1.1该DTD等同于XHTML 1.0 Strict,但允许添加模型(例如提供对东亚语系的ruby支持)。<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.1//EN” “http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">3. 语义化标签HTML5提供了新的元素来创建更好的页面结构:标签描述<article> 定义页面独立的内容区域(文章)。<aside> 定义页面的侧边栏内容(侧边栏)。<bdi> 允许您设置一段文本,使其脱离其父元素的文本方向设置。<command> 定义命令按钮,比如单选按钮、复选框或按钮<details> 用于描述文档或文档某个部分的细节<dialog> 定义对话框,比如提示框<summary> 标签包含details元素的标题<figure> 规定独立的流内容(图像、图表、照片、代码等等)。<figcaption>定义<figure>元素的标题<footer> 定义section或document的页脚。<header> 定义了文档的头部区域<mark> 定义带有记号的文本。<meter> 定义度量衡。仅用于已知最大和最小值的度量。<nav> 定义导航链接的部分。<progress> 定义任何类型的任务的进度。<ruby> 定义ruby注释(中文注音或字符)。<rt> 定义字符(中文注音或字符)的解释或发音。<rp> 在ruby注释中使用,定义不支持ruby元素的浏览器所显示的内容。<section> 定义文档中的节(section、区段)。<time> 定义日期或时间。<wbr> 规定在文本中的何处适合添加换行符。本质上新语义标签与<div>、<span>没有区别,只是其具有语义性,使用时除了在HTML结构上需要注意外,其它和普通标签的使用无任何差别,可以理解成<div class=“nav”>相当于<nav>。不要好奇,它只是一个标签!尽量避免全局使用header、footer、aside等语义标签。4. HTML5 浏览器支持对于目前主流的浏览器来说,都已经支持HTML5了,但是到了Internet Explorer 9,IE才开始支持HTML5,对于之前的旧版本,我们就需要考虑到兼容性问题。1、将 HTML5 元素定义为块元素HTML5 新增了几个具有语义化的标签,这些标签都是块级元素,在不支持HTML5新标签的浏览器里,会将这些新的标签解析成行内元素(inline)对待,所以我们只需要在初始化的时候将其转换成块元素(block)即可使用。header, section, footer, aside, nav, main, article, figure { display: block; }2、通过 js 动态创建标签在IE9版本以下,并不能正常解析这些新标签,但是却可以识别通过document.createElement(’tagName’)创建的自定义标签,于是我们的解决方案就是将HTML5的新标签全部通过document.createElement(’tagName’)来创建一遍,这样IE低版本也能正常解析HTML5新标签了。<style> header,section,nav…{ display:block; }</style><script type=“text/javascript”> document.createElement(“header”); document.createElement(“section”); document.createElement(“nav”); . . .</script>注意:通过document.createElement创建出来的标签时行内元素,所以同样的需要将它们转换成块级元素。3、Shiv 解决方案在实际开发中我们更多采用的是通过检测IE浏览器的版本来加载第三方的一个JS库来解决兼容问题,这个库文件会帮自动通过document.createElement(’tagName’)创建所有HTML5的新标签。针对IE浏览器html5shiv是比较好的解决方案。html5shiv主要解决HTML5提出的新的元素不被IE6-8识别,这些新元素不能作为父节点包裹子元素,并且不能应用CSS样式的问题。引入本地html5shiv.min.js文件:<!–[if lte IE 8]> <script type=“text/javascript” src=“html5shiv.min.js”></script><![endif]–>lte:表示小于等于;当浏览器版本小于等于IE8的时候,引用html5shiv.min.js文件。引入远程静态资源库:<!–[if lte IE 8]> <script src=“http://cdn.static.runoob.com/libs/html5shiv/3.7/html5shiv.min.js"></script><![endif]-->以上的注释代码只有在IE浏览器下次才会识别里面的内容,其他浏览器直接当注释识别。完整示例代码:<!DOCTYPE html><html><head><meta charset=“utf-8”><title>兼容性处理</title><!–[if lte IE 8]> <script src=“http://cdn.static.runoob.com/libs/html5shiv/3.7/html5shiv.min.js"></script><![endif]--></head> <body> <h1>我正在处理兼容性</h1> <article>我在IE下也能显示</article> </body></html>注意:html5shiv.js引用代码必须放在<head>元素中,因为IE浏览器在解析HTML5新元素时需要先加载该文件。5. 表单伴随着互联网富应用以及移动开发的兴起,传统的Web表单已经越来越不能满足开发的需求,所以HTML5在Web表单方向也做了很大的改进,如拾色器、日期/时间组件等,使表单处理变的更加高效。5.1 表单控件html5中新增的一些表单控件,例如email属性的输入框,它可以检测你的输入内容是否符合一个邮箱的格式,自动进行表单校验。1、表单域:form我们都知道<form>标签用于为用户输入创建HTML表单。表单能够包含input元素,比如文本字段、复选框、单选框、提交按钮等等。表单还可以包含menus、textarea、fieldset、legend和label元素。表单用于向服务器传输数据。在html5中<form>表单域添加了两个新的属性:“autocomplete: no/yes”、“novalidate”:autocomplete:规定是否启用表单的自动完成功能,默认on。(自动完成允许浏览器预测对字段的输入。当用户在字段开始键入时,浏览器基于之前键入过的值,应该显示出在字段中填写的选项。)。novalidate:如果使用该属性,则提交表单时不进行验证(关闭控件自动校验功能)。2、邮箱:email<form> 邮箱:<input type=“email” name=“email”></form>效果图:3、网址:url协议、域名都要输入进去(https://www.baidu.com,如果直接www.baidu.com会提示输入错误的),否则触发表单校验。<form> 网址:<input type=“url” name=“url”></form>效果图:4、数字:number输入表单数number属性的时候,在表单尾部会有一个上下的箭头,用来选择数字。另外表单里的step属性表示:点击的时候每一次增加或减少的步数;max属性表示:增加到的最大范围,min属性表示减小到的最小范围。<form> 年龄:<input type=“number” name=”” step=“3” max=“120” ></form>效果图:5、电话号码:tel<form> 电话号码: <input type=“tel” name=“tel”></form>6、颜色:color<form> 邮箱:<input type=“color” name=“color”></form>效果图:7、时间:time<form> 时间:<input type=“time” name=“time”></form>效果图:8、日期:date<form> 日期: <input type=“date” name=“date”></form>效果图:9、时间日期:datetime<form> 时间日期: <input type=“datetime” name=“datetime”></form>10、周:week<form> 周: <input type=“week” name=“week”></form>效果图:11、月:month<form> 月: <input type=“month” name=“month”></form>效果图:12、滑块:rangemax:规定允许的最大值,min:规定允许的最小值。<form> 滑块: <input type=“range” name=“range” min=“1” max=“20”></form>效果图:5.2 表单元素不是所有浏览器都支持HTML5新的表单元素的,但是不影响使用,即使不支持仍然可以显示常规的表单。1、datalist在Web设计中,经常会用到如输入框的自动下拉提示,这将大大方便用户的输入。在以前,如果要实现这样的功能,必须要求开发者使用一些Javascript的技巧或相关的框架进行ajax调用,需要一定的编程工作量。但随着HTML5的慢慢普及,开发者可以使用其中的新的DataList标记就能快速开发出十分漂亮的AutoComplete组件的效果。datalist有点类似于select下拉菜单,datalist元素规定输入域的选项列表。列表是通过datalist内的 option元素创建的。如需把datalist绑定到输入域,请用输入域的list属性引用datalist的id:<p> 浏览器版本:<input list=“words”></p><datalist id=“words”> <option value=“Internet Explorer”> <option value=“Firefox”> <option value=“Chrome”> <option value=“Opera”> <option value=“Safari”> <option value=“Sogou”> <option value=“Maxthon”></datalist>效果图:5.3 表单属性在HTML5中, <form>和<input>标签添加了几个新属性。其中<form>标签的autocomplete、novalidate属性,我们在上面都讲过了,现在我们来看看<input>提供了哪些新的属性。1、autocomplete 自动完成autocomplete属性规定form或input域应该拥有自动完成功能。当用户在自动完成域中开始输入时,浏览器应该在该域中显示填写的选项。提示:autocomplete属性有可能在form元素中默认是开启的,而在input元素中是关闭的。需要手动添加"on”。示例代码:<input type=“text” autocomplete=“on”>2、placeholder 占位符placeholder属性提供一种提示(hint),描述输入域所期待的值。简短的提示在用户输入值前会显示在输入域上。示例代码:用户名:<input type=“text” placeholder=“请输入用户名”>效果图:3、autofocus 自动获得焦点autofocus属性是一个boolean属性。autofocus属性规定在页面加载时,域自动地获得焦点。示例代码:用户名:<input type=“text” name=“username” autofocus>4、multiple 多文件上传multiple属性是一个boolean属性。multiple属性规定<input>元素中可选择多个值。示例代码:<form action=“xxx.php”> 选择图片: <input type=“file” name=“img” multiple> <input type=“submit”></form>此时上传文件时就可以同时上传多个文件了。5、form 绑定输入域当一个输入表单input,不在一个表单域form中的时候。通过form属性和表单域的id可以将输入表单绑定到表单域中。示例代码:<form action=“xxx.php” id=“form1”> 用户名: <input type=“text” name=“username”><br> <input type=“submit” value=“提交”></form> 密码: <input type=“text” name=“pwd” form=“form1”>点击提交按钮,表单域外部的“密码”输入框内容也会提交。6、required 必填项required属性是一个boolean属性。required属性规定必须在提交之前填写输入域(不能为空)。示例代码:用户名: <input type=“text” name=“usrname” required>7、pattern 自定义验证pattern属性描述了自定义一个正则表达式用于验证<input>元素的值。示例代码:<form action=”"> 用户名: <input type=“text” name=“username” pattern="[A-Za-z]{3}” title=“三位英文字母”> <input type=“submit”></form>效果图:5.4 表单事件这里给大家介绍两个表单事件oninput:用户输入的时候触发的事件;oninvalid:表单验证无法通过时候触发的事件示例代码:<form action=”"> 用户名:<input type=“text” name=“username” id=“user”> <!– 限定密码格式只能是数字 –> 密 码: <input type=“text” name=“pwd” id=“pwd” pattern="\d+"> <input type=“submit”></form><script type=“text/javascript”> var user = document.getElementById(‘user’); var pwd = document.getElementById(‘pwd’); // 用户输入的时候触发 user.oninput = function(){ alert(1); } // 验证无法通过的时候触发 pwd.oninvalid = function(){ alert(2); }</script>效果图:当验证无法通过的时候,可以通过setCustomValidity方法添加或修改提示内容:pwd.oninvalid = function(){ this.setCustomValidity(“密码格式错误”); }效果图:5.5 表单样式这里主要是说下如何修改placeholder的默认样式。火狐: input::-moz-placeholder{}谷歌: input::-webkit-input-placeholder {}通过双伪元素选择器,即可改变placeholder文字的样式。6. 多媒体标签在HTML5之前,在网页上播放音频/视频的通用方法是利用Flash来播放,但是大多情况下,并非所有用户的浏览器都安装了Flash插件,由此使得处理音频/视频播放变的非常复杂,并且移动设备的浏览器并不支持Flash插件。6.1 音频HTML5通过<audio>标签来解决音频播放的问题。示例代码:<!– 通过src指定音频文件路径即可 –><audio src=”./xxx.mp3”></audio>播放格式由于版权等原因,不同的浏览器可支持播放的格式是不一样的,如下图供参考:处理兼容性在<audio>与</audio>之间你需要插入浏览器不支持的<audio>元素的提示文本 。<audio>元素允许使用多个 <source>元素。<source> 元素可以链接不同的音频文件,浏览器将使用第一个支持的音频文件。<audio> <!– 通过source标签指定多格式音频文件 –> <source src=“xxx.ogg” type=“audio/ogg”> <source src=“xxx.mp3” type=“audio/mpeg”> <source src=“xxx.wav” type=“audio/wav”> 您的浏览器不支持 audio 元素。</audio>音频控制属性通过附加属性可以更友好控制音频的播放.autoplay 自动播放controls 是否显示控制条loop 循环播放6.2 视频HTML5通过<video>标签来解决音频播放的问题。示例代码:<!– 通过src指定视频文件路径即可 –><audio src=”./xxx.mp4"></audio>播放格式由于版权等原因,不同的浏览器可支持播放的格式是不一样的,如下图供参考:处理兼容性<video width=“320” height=“240” controls> <source src=“xxx.mp4” type=“video/mp4”> <source src=“xxx.ogg” type=“video/ogg”> 您的浏览器不支持Video标签。</video>6.3 音频/视频方法通过附加属性可以更加友好的控制音频、视频的播放。1、autoplay 自动播放autoplay属性设置或返回音视频是否在加载后即开始播放。设置autoplay属性:audio|video.autoplay=true|false返回autoplay属性:audio|video.autoplay启用自动播放,并重载视频:var video=document.getElementById(“video1”);video.autoplay = true;video.load();2、buffered 已缓冲部分buffered属性返回TimeRanges对象。TimeRanges对象表示用户的音视频缓冲范围。缓冲范围指的是已缓冲音视频的时间范围。如果用户在音视频中跳跃播放,会得到多个缓冲范围。返回值:TimeRanges对象,表示音视频的已缓冲部分。TimeRanges对象属性:length - 获得音视频中已缓冲范围的数量start(index) - 获得某个已缓冲范围的开始位置end(index) - 获得某个已缓冲范围的结束位置注意:首个缓冲范围的下标是0。示例代码:获得视频的第一段缓冲范围(部分),以秒计:var video = document.getElementById(“video1”);alert(“Start: " + video.buffered.start(0) + " End: " + video.buffered.end(0));3、controls 是否显示控制条controls属性设置或返回浏览器应当显示标准的音视频控件。设置controls属性:audio|video.controls=true|false返回controls属性:audio|video.controls启用视频控件:var video = document.getElementById(“video1”);video.controls = true;4、currentSrc 返回当前资源的URLcurrentSrc熟悉返回当前音频/视频的URL。如果未设置音频/视频,则返回空字符串。获得当前视频的URL:video = document.getElementById(“video1”);alert(video.currentSrc);5、currentTime 当前播放位置(时间s)属性描述autoplay资源加载完成后自动播放视频或音频buffered返回表示音频/视频已缓冲部分的TimeRanges对象controls是否显示控制条currentSrc返回当前音频/视频的URLcurrentTime设置或返回音频/视频中的当前播放位置(以秒计)defaultMuted设置或返回音频/视频默认是否静音defaultPlaybackRate设置或返回音频/视频的默认播放速度duration返回当前音频/视频的长度(以秒计)ended返回音频/视频的播放是否已结束error返回表示音频/视频错误状态的MediaError对象loop设置或返回音频/视频是否应在结束时重新播放mediaGroup设置或返回音频/视频所属的组合(用于连接多个音频/视频元素)muted设置或返回音频/视频是否静音networkState返回音频/视频的当前网络状态paused设置或返回音频/视频是否暂停playbackRate设置或返回音频/视频播放的速度played返回表示音频/视频已播放部分的TimeRanges对象preload设置或返回音频/视频是否应该在页面加载后进行加载readyState返回音频/视频当前的就绪状态seekable返回表示音频/视频可寻址部分的TimeRanges对象seeking返回用户是否正在音频/视频中进行查找src设置或返回音频/视频元素的当前来源startDate返回表示当前时间偏移的Date对象textTracks返回表示可用文本轨道的TextTrackList对象videoTracks返回表示可用视频轨道的VideoTrackList对象volume设置或返回音频/视频的音量7. DOM 扩展7.1 获取元素html5中新添了两个查找元素的属性,分别是:querySelector,querySelectorAll。<ul> <li class=“one”><a href=”#">文字1</a></li> <li class=“two”><a href="#">文字2</a></li> <li class=“three”><a href="#">文字3</a></li></ul><script type=“text/javascript”> //获取元素的方式 var a= document.querySelector(".one a"); a.style.color=“red”; //通过该方式可以将所有对应的元素选中 返回的是一个伪数组 var a1= document.querySelectorAll(“a”); </script>7.2 类名操作html5中新添加了一个操作类名的对象:classList。我们可以通过它里面的方法对元素的类进行操作。1、添加类(add)添加类的时候,获取到元素之后,通过classList的add方法添加一个类名,但是一次只能添加一个类名,否则会报错。<style type=“text/css”> .bgc { width: 300px; height: 300px; background-color: pink; } .fonts{ font-size: 26px; }</style><div>文字</div><script type=“text/javascript”> var div=document.querySelector(“div”); // 添加样式 只能单独添加 div.classList.add(“bgc”); div.classList.add(“fonts”);</script>2、移除类(remove)移除类的时候,获取到元素之后,通过classList的remove方法移除一个类名。<style type=“text/css”> .bgc { width: 300px; height: 300px; background-color: pink; } .fonts{ font-size: 26px; }</style><div class=“bgc fonts”>文字</div><script type=“text/javascript”> var div=document.querySelector(“div”); // 移除样式 只能单独移除 div.classList.remove(“bgc”); div.classList.remove(“fonts”);</script>3、切换类(toggle)当元素上没有某个类时,它就新增这个类;如果元素已经有了这个类,它就是删除它,就是切换操作。<style type=“text/css”> .bgc { width: 300px; height: 300px; background-color: pink; }</style><div class=“bgc”>文字</div><script type=“text/javascript”> var div=document.querySelector(“div”); // 切换样式 因为元素已经有“bac”这个类名了,所以这里是移除的功能 div.classList.toggle(“bgc”);</script>4、是否存在某个类(contains)判断获取的元素中是否存在某个类名,返回值为true或者false。<style type=“text/css”> .bgc { width: 300px; height: 300px; background-color: pink; }</style><div class=“bgc”>文字</div><script type=“text/javascript”> var div=document.querySelector(“div”); // 判断元素是否拥有某个类名 div.classList.contains(“bgc”); // true</script>7.3 自定义属性HTML5规定可以为元素添加非标准的属性,但要添加前缀data-,目的是为元素提供与渲染无关的信息,或者提供语义信息。这些属性可以任意添加、随便命名,只要以data-开头。如:<p data-info=“tags”></p>获取自定义属性(dataset[‘自定义属性名称’])通过Node.dataset[‘info’] 我们便可以获取到自定义的属性值。<p data-info=“describe” data-num=“123”>这是一段描述</p><script> var tag = document.querySelector(‘p’); var data = tag.dataset; console.log(data); // 打印的是一个对象 DOMStringMap console.log(data[‘info’]); // “describe” console.log(data[’num’]); // “123”</script>Node.dataset是以对象形式存在的,当我们为同一个DOM节点指定了多个自定义属性时,Node.dataset则以键值对的形式存储了所有的自定义属性的值。设置自定义属性(dataset[‘自定义属性名称’]=“设定属性值”)通过过Node.dataset[‘info’]=“值” 我们便可以设置自定义的属性值。<p data-info=“describe” data-num=“123”>这是一段描述</p><script> var tag = document.querySelector(‘p’); var data = tag.dataset; data[’name’] = “Ryan”; console.log(data); // 多了一个name属性</script>注意当自定义属性中除了data-之外中间出现“-”连接符时,设置和获取的时候需要将属性名转成驼峰的格式才能正常的设置和获取。<p data-my-info=“information”>这是一段描述!</p><script> var tag = document.querySelector(‘p’); var data = tag.dataset; data[‘myInfo’] = “info”; console.log(data); // {myInfo:“info”}</script>8. 网络状态我们可以通过window.navigator.onLine来检测,用户当前的网络状况,返回一个布尔值通过给window绑定监听事件,可以监测浏览器的一些状态信息网络从无到有时触发“online”方法window.addEventListener(‘online’, function(){ // online是网络从无网到有网的 时候触发})网络从有到无时触发“offline”方法window.addEventListener(‘offline’, function(){ // online是网络从有网到无网的时候触发})9. 地理定位在HTML规范中,增加了获取用户地理信息的API,这样使得我们可以基于用户位置开发互联网应用,即基于位置服务 (Location Base Service)9.1 获取地理信息方式获取地理信息的方式一共有三种分别是:IP地址三维坐标GPS(Global Positioning System,全球定位系统)Wi-Fi手机信号用户自定义数据如下表:下表对不同获取方式的优缺点进行了比较,浏览器会自动以最优的方式去获取用户地理位置数据源优点缺点IP 地址任何地方都可以用,在服务器端处理不精确(经常出错,一般精确到城市级)运算代价大GPS很精确定位时间长,耗电量大;室内效果差;需要额外硬件设备支持Wi-Fi精确,可在室内使用,简单、快捷在乡村这些Wi-Fi接入点少的地区无法实现用手机信号相当准确,可在室内使用,简单、快捷需要能够访问手机或其 modem 设备用户自定义可获得比程序定位服务更准确的位置数据,用户自行输入可能比自动检测更快可能很不准确,特别是当用户位置变更后9.2 隐私HTML5 Geolocation规范提供了一套保护用户隐私的机制。必须先得到用户明确许可,才能获取用户的位置信息。9.3 使用地理定位有两个方法都可以获取到当前的地理定位,只是功能上稍微有点区别。navigator.geolocation.getCurrentPosition(successCallback, errorCallback, options) // 方法:获取当前地理信息navigator.geolocation.watchPosition(successCallback, errorCallback, options) // 重复获取当前地理信息当成功获取地理信息后会调用succssCallback,并返回一个包含位置信息的对象position。position.coords.latitude // 纬度position.coords.longitude // 经度成功对象position:属性描述coords.latitude十进制数的纬度coords.longitude十进制数的经度coords.accuracy位置精度coords.altitude海拔,海平面以上以米计coords.altitudeAccuracy位置的海拔精度coords.heading方向,从正北开始以度计coords.speed速度,以米/每秒计timestamp响应的日期/时间当获取地理信息失败后会调用errorCallback,并返回错误信息error返回无符号的、简短的错误码,详见下表:值相关联的常量描述1PERMISSION_DENIED用户不允许地理定位2POSITION_UNAVAILABLE无法获取当位置3TIMEOUT超时操作示例代码:获取当前位置的经纬度window.navigator.geolocation.getCurrentPosition(function(position){ // 纬度 var lat = position.coords.latitude; // 经度 var long = position.coords.longitude; console.log(‘你当前的纬度为:’ + lat + ‘经度为:’ + long)},function(err){ // 错误时回调信息 if(err.code == 1){ alert(‘没有权限’) }else if(err.code == 2){ alert(‘内部错误’); }else{ alert(‘超时’) }},{ // 超时设置 timeout: 5000});9.4 百度地图的用法结合百度地图的API,我们可以使用它上面的一些功能,比如在地图上定位等等等…进入百度地图开放平台官网:网址:http://lbsyun.baidu.com/找到Web开发 -> javascript API直接找到示例DEMO,复制源代码,需要获取密钥 (自己申请,需要一到两个工作日)<!DOCTYPE html><html><head> <meta http-equiv=“Content-Type” content=“text/html; charset=utf-8” /> <meta name=“viewport” content=“initial-scale=1.0, user-scalable=no” /> <style type=“text/css”> body, html, #allmap { width: 100%; height: 100%; overflow: hidden; margin: 0; font-family: “微软雅黑”; } </style> <script type=“text/javascript” src=“你申请的秘钥”></script> <title>地图展示</title></head><body> <div id=“allmap”></div></body></html><script type=“text/javascript”> // 百度地图API功能 var map = new BMap.Map(“allmap”); // 创建Map实例 map.centerAndZoom(new BMap.Point(116.404, 39.915), 11); // 初始化地图,设置中心点坐标和地图级别 map.addControl(new BMap.MapTypeControl()); //添加地图类型控件 map.setCurrentCity(“北京”); // 设置地图显示的城市 此项是必须设置的 map.enableScrollWheelZoom(true); //开启鼠标滚轮缩放</script>获取当前所在位置,设置到地图上<!DOCTYPE html><html><head> <meta http-equiv=“Content-Type” content=“text/html; charset=utf-8” /> <meta name=“viewport” content=“initial-scale=1.0, user-scalable=no” /> <style type=“text/css”> body, html, #allmap { width: 100%; height: 100%; overflow: hidden; margin: 0; font-family: “微软雅黑”; } </style> <script type=“text/javascript” src=“你申请的秘钥”></script> <title>地图展示</title></head><body> <div id=“allmap”></div></body></html><script type=“text/javascript”> window.navigator.geolocation.getCurrentPosition(function (pos) { // 纬度 var lat = pos.coords.latitude; // 经度 var long = pos.coords.longitude; console.log(‘你当前的纬度为:’ + lat + ‘经度为:’ + long) // 百度地图API功能 var map = new BMap.Map(“allmap”); // 创建Map实例 map.centerAndZoom(new BMap.Point(long, lat), 15); // 初始化地图,设置中心点坐标和地图级别 map.addControl(new BMap.MapTypeControl()); //添加地图类型控件 map.setCurrentCity(“北京”); // 设置地图显示的城市 此项是必须设置的 map.enableScrollWheelZoom(true); //开启鼠标滚轮缩放 }, function (err) { if (err.code == 1) { alert(‘没有权限’) } else if (err.code == 2) { alert(‘内部错误’); } else { alert(‘超时’) } }, { // 超时设置 timeout: 5000 });</script>效果图:10. WEB 存储随着互联网的快速发展,基于网页的应用越来越普遍,同时也变的越来越复杂,为了满足各种各样的需求,会经常性在本地存储大量的数据,传统方式我们以document.cookie来进行存储的,但是由于其存储大小只有4k左右,并且解析也相当的复杂,每一次发送请求都会携带上cookie,会造成带宽的浪费,给开发带来诸多不便,HTML5规范则提出解决方案。web存储的含义是将数据存储到用户的电脑上,这样可以缓解服务器的压力,并且提高体验。10.1 特性设置、读取方便容量较大,sessionStorage约5M,localStorage约20M只能存储字符串,可以将对象JSON.stringify()转成字符串后存储10.2 方法详解setItem(key, value)设置存储内容getItem(key)读取存储内容removeItem(key)删除键值为key的存储内容clear()清空所有存储内容key(n)以索引值来获取键名length 存储的数据的个数示例代码:// 在本地存储了一个键为:username 值为 Ryan’s 的这个一个对象// 在chrome的控制台的application里面的Storage可以查看window.localStorage.setItem(‘username1’,“Ryan’s”);window.localStorage.setItem(‘username2’,“Levi’s”);window.sessionStorage.setItem(‘username1’,“Ryan’s”);window.sessionStorage.setItem(‘username2’,“Levi’s”);// 索引键的名字alert(window.localStorage.key(0));alert(window.sessionStorage.key(0));// 取数据 alert(window.localStorage.getItem(‘username1’));alert(window.sessionStorage.getItem(‘username1’));// 获取本地数据的长度alert(window.localStorage.length);alert(window.sessionStorage.length);// 删除数据window.localStorage.removeItem(‘username1’);window.sessionStorage.removeItem(‘username1’);// 清空所有的数据window.localStorage.clear();window.sessionStorage.clear();10.3 sessionStorage特点:生命周期为关闭当前页面窗口不能多窗口下数据共享(同源策略)通过跳转可以解决,页面跳转的时候可以通过session实现数据共享10.4 localStorage特点:生命周期为永久有效,除非手动删除或用代码删除可以多窗口共享(同源策略)一些不涉及到安全的一些数据(不要太过庞大)都可以存储到本地示例代码:window.localStorage.setItem(‘age’,18)效果图:10.5 差异性比较cookie,session,local三者之间的相同点和不同点相同点:都是存储数据,存储在web端,并且都是同源不同点:(1)cookie只有4K大小 并且每一次请求都会带上cookie体验不好,浪费带宽(2)session和local直接存储在本地,请求不会携带,并且容量比cookie要大的多(3)session是临时会话,当窗口被关闭的时候就清除掉 ,而local永久存在,cookie有过期时间(4)cookie和local都可以支持多窗口共享,而session不支持多窗口共享 但是都支持a链接跳转的新窗口11. 文件读取通过FileReader对象我们可以读取本地存储的文件,可以使用File对象来指定所要读取的文件或数据。其中File对象可以是来自用户在一个<input>元素上选择文件后返回的FileList对象,也可以来自由拖放操作生成的DataTransfer。1、FileList 对象由于HTML5中我们可以通过为表单元素添加multiple属性,因此我们通过<input>上传文件后得到的是一个FileList对象(伪数组形式)。2、FileReader 对象HTML5新增内建对象,可以读取本地文件内容。var reader = new FileReader;可以实例化一个对象。var data = file.files[0];// 创建一个读取对象var fr = new FileReader();// 读取文件fr.readAsDataURL(data);readAsDataURL()以DataURL形式读取文件3、事件监听onload当文读取完成时调用fr.addEventListener(’load’, function(){ //获取读取的结果 //result属性里面存储的就是读取文件的结果 var result = fr.result; })完整代码:<input type=“file” multiple name="" id=""><button>点击读取文件</button><script type=“text/javascript”>var btn = document.querySelector(‘button’);var file = document.querySelector(‘input[type=“file”]’);var fr = [];btn.onclick = function(){ // 读取文件 for(var i = 0; i < file.files.length; i++){ fr[i] = new FileReader(); fr[i].readAsDataURL(file.files[i]); } // fr.readAsDataURL(data); // 读取文件是一个耗时的操作,所以需要用事件监听读取完毕, // load事件是文件读取完毕之后触发的事件 for(var j = 0; j < fr.length; j++){ fr[j].addEventListener(’load’, function(){ //获取读取的结果 //result属性里面存储的就是读取文件的结果 console.log(fr); var result = this.result; // 创建图片对象 var img = document.createElement(‘img’); img.src = result; document.body.appendChild(img); }) } }</script>12. 拖拽在HTML5的规范中,我们可以通过为元素增加draggable=“true"来设置此元素是否可以进行拖拽操作,其中图片、链接默认是开启的。1、拖拽元素页面中设置了draggable=“true"属性的元素,可以被拖拽,其中<img>、<a>标签默认是可以被拖拽的。2、目标元素*页面中任何一个元素都可以成为目标元素。3、事件监听根据元素类型的不同,需要设置不同的事件监听:(1)、拖拽元素ondrag 应用于拖拽元素,整个拖拽过程都会调用ondragstart 应用于拖拽元素,当拖拽开始时调用ondragend 应用于拖拽元素,当拖拽结束时调用(2)、目标元素ondragenter 应用于目标元素,当拖拽元素进入时调用ondragleav 应用于目标元素,当鼠标离开目标元素时调用ondragver 应用于目标元素,当停留在目标元素上时调用ondrop 应用于目标元素,当在目标元素上松开鼠标时调用示例代码: 将图片拖拽到浏览器内显示在div内<style> .info { width: 500px; height: 500px; border: 1px solid #000; position: absolute; left: 50%; top: 50%; line-height: 500px; text-align: center; transform:translate(-50%,-50%); box-shadow: 0 0 10px 2px rgba(0,0,0,.5); border-radius: 5px; } .info img { width: 100%; height: 100%; }</style> <!– 目标元素 –><div class=“info”>将图片拖拽至此</div> <script type=“text/javascript”> var info = document.querySelector(’.info’); // 获取html元素 var oHtml = document.documentElement; // 问题:浏览器默认会将外部拖拽的文件直接打开,我们需要阻止掉 // 将外部文件拖拽进浏览器里面松开鼠标的时候其实就是在html页面上触发了drop事件,我们只需要在drop事件的时候阻止默认事件 oHtml.ondrop = function(e){ // 阻止默认事件 e.preventDefault(); } /drop事件默认是被阻止的,所以还需要在dragover的时候阻止默认事件/ oHtml.ondragover = function(e){ e.preventDefault(); } info.ondrop = function(e){ // 获取外部拖拽进来的文件 // console.log(e); var data = e.dataTransfer.files[0]; var fr = new FileReader(); fr.readAsDataURL(data); fr.addEventListener(’load’,function(){ var result = fr.result; var img = document.createElement(‘img’); img.src = result; info.innerHTML = ‘’; info.appendChild(img); }) }</script>效果图

November 22, 2018 · 5 min · jiezi

如何给localStorage设置一个过期时间?

引言 这个话题其实在上次分享<小程序填坑记里讲过了>已经讲过(大佬可绕过哦~),但后来群里/评论都有些同学,提到了一些疑问,问能否单独整理一篇更为详细的分享,讲解一下细节和完善提到的不足,如是有了下文????。 —— 「 用心分享 做有温度的攻城狮,我是首席填坑官——苏南 」各位大佬早安,这里是@IT·平头哥联盟,我是首席填坑官∙苏南,用心分享 做有温度的攻城狮。公众号:honeyBadger8,群:912594095思考点 从我们接触前端起,第一个熟悉的存储相关的Cookie或者来分析我们生活中密切相关的淘宝、物流、闹钟等事物来说起吧,Cookie从你设置的时候,就会给个时间,不设置默认会话结束就过期;淘宝购物 从你下单付款起,就会给这件货物设置一个收货期限时间,过了这个时间自动认为你收货(即订单结束);闹钟 你设置的提醒时间,其实也就是它的过期时间;再比如与您每天切身相关的产品需求,过完需求,你给出的上线时间,也就是这个需求的过期时间;再通俗点讲,您今年的生日过完到明年生日之间也是相当于设置了有效期时间;以上种种,我们能得出一个结论任何一件事、一个行为动作,都有一个时间、一个节点,甚至我们可以黑localStorage,就是一个完善的API,为什么不能给一个设置过期的机制,因为sessionStorage、Cookie并不能满足我们实际的需求。实现思路 抱歉,黑localStorage不完善,有点夸张了,综合上述的总结,问题就简单了,给localStorage一个过期时间,一切就都so easy ?到底是不是,来看看具体的实现吧:简单回顾//示例一:localStorage.setItem(’test’,1234567);let test = localStorage.getItem(’test’);console.log(typeof test, test); //示例二:localStorage[’name’] = ‘苏南’;console.log(localStorage[’name’]);/输出:“1234567” ,‘苏南’,这里要注意,1234567 存进去时是number 取出来就成string了/重写 set(存入) 方法:首先有三个参数 key、value、expired ,分别对应 键、值、过期时间,过期时间的单位可以自由发挥,小时、分钟、天都可以,注意点:存储的值可能是数组/对象,不能直接存储,需要转换 JSON.stringify,这个时间如何设置呢?在这个值存入的时候在键(key)的基础上扩展一个字段,如:key+’expires’,而它的值为当前 时间戳 + expired过期时间具体来看一下代码 :set(key, value, expired) { /* * set 存储方法 * @ param {String} key 键 * @ param {String} value 值, * @ param {String} expired 过期时间,以分钟为单位,非必须 * @ 由@IT·平头哥联盟-首席填坑官∙苏南 分享,交流:912594095 / let source = this.source; source[key] = JSON.stringify(value); if (expired){ source[${key}__expires__] = Date.now() + 100060expired }; return value;}重写 get(获取) 方法:获取数据时,先判断之前存储的时间有效期,与当前的时间进行对比;但存储时expired为非必须参数,所以默认为当前时间+1,即长期有效;如果存储时有设置过期时间,且在获取的时候发现已经小于当前时间戳,则执行删除操作,并返回空值;注意点:存储的值可能是数组/对象,取出后不能直接返回,需要转换 JSON.parse,具体来看一下代码 :get(key) { / * get 获取方法 * @ param {String} key 键 * @ param {String} expired 存储时为非必须字段,所以有可能取不到,默认为 Date.now+1 * @ 由@IT·平头哥联盟-首席填坑官∙苏南 分享,交流:912594095 / const source = this.source, expired = source[${key}__expires__]||Date.now+1; const now = Date.now(); if ( now >= expired ) { this.remove(key); return; } const value = source[key] ? JSON.parse(source[key]) : source[key]; return value;}重写 remove(删除) 方法:删除操作就简单了,;remove(key) { const data = this.source, value = data[key]; //首席填坑官∙苏南的专栏 delete data[key]; delete data[${key}__expires__]; return value;}优化点:记得上次有个同学,是这么评论的:「 删除缓存能放到constructor里面执行么,放到get里面 不取就一直在那是不是不太好?」;所以本次优化做一个初始化删除操作,清除已经过期的数据;为什么不用for in而是 for ? for in循环遍历对象的属性时,原型链上的所有属性都将被访问,解决方案:使用hasOwnProperty方法过滤或Object.keys会返回自身可枚举属性组成的数组;class storage { constructor(props) { this.props = props || {} this.source = this.props.source || window.localStorage this.initRun(); } initRun(){ / * set 存储方法 * @ param {String} key 键 * @ param {String} value 值,存储的值可能是数组/对象,不能直接存储,需要转换 JSON.stringify * @ param {String} expired 过期时间,以分钟为单位 * @ 由@IT·平头哥联盟-首席填坑官∙苏南 分享,交流:912594095 */ const reg = new RegExp("expires"); let data = this.source; let list = Object.keys(data); if(list.length > 0){ list.map((key,v)=>{ if( !reg.test(key )){ let now = Date.now(); let expires = data[${key}__expires__]||Date.now+1; if (now >= expires ) { this.remove(key); }; }; return key; }); }; }}总结: 以上就是今天为大家总结的分享,您GET到了吗?小程序、sessionStorage、localStorage,都适用,做些许调整即可哦,希望今天的分享能给您带来些许成长,如果觉得不错,记得关注下方公众号哦,每周第一时间为您推最新分享????????。更多文章:easy-mock 最好的备胎没有之一immutability因React官方出镜之使用总结分享!面试踩过的坑,都在这里了~你应该做的前端性能优化之总结大全!如何给localStorage设置一个过期时间?动画一点点 - 如何用CSS3画出懂你的3D魔方?作者:苏南 - 首席填坑官链接:https://blog.csdn.net/weixin_…交流:关注公众号邀请您加入交流群 honeyBadger8本文原创,著作权归作者所有。商业转载请联系@IT·平头哥联盟获得授权,非商业转载请注明原链接及出处。 ...

November 15, 2018 · 2 min · jiezi

使用electron实现百度网盘悬浮窗口功能!

相关依赖里面使用了vuex vue vue-route storeJsstoreJs 用来持久化vuex状态展示介绍说明没有使用electron内置的-webkit-app-region: drag 因为使用他那个有很多问题比如事件无法使用 右键无法使用 以及不能使用手型等!安装安装的时候没有截图 所以就参考下我其他的文章吧storeJs 安装npm install storejs准备写代码配置路由文件export default new Router({ routes: [ {path: ‘/’, name: ‘home’, component: ()=> import(’@/view//home’)}, {path: ‘/suspension’, name: ‘suspension’, component: ()=> import(’@/view/components/suspension’)} ]})写悬浮窗页面页面路径 /src/renderer/view/components/suspension.vue<template> <div id=“suspension”> <div class=“logo”></div> <div class=“content_body”> <div class=“upload”>拖拽上传</div> </div> </div></template><script> export default { name: “suspension”, mounted() { let win = this.$electron.remote.getCurrentWindow(); let biasX = 0; let biasY = 0; let that = this; document.addEventListener(‘mousedown’, function (e) { switch (e.button) { case 0: biasX = e.x; biasY = e.y; document.addEventListener(‘mousemove’, moveEvent); break; case 2: that.$electron.ipcRenderer.send(‘createSuspensionMenu’); break; } }); document.addEventListener(‘mouseup’, function () { biasX = 0; biasY = 0; document.removeEventListener(‘mousemove’, moveEvent) }); function moveEvent(e) { win.setPosition(e.screenX - biasX, e.screenY - biasY) } } }</script><style> * { padding: 0; margin: 0; } .upload { height: 25px; line-height: 25px; font-size: 12px; text-align: center; color: #74A1FA; } .logo { width: 40px; background: #5B9BFE url("../../assets/img/logo@2x.png") no-repeat 2px 3px; background-size: 80%; } .content_body { background-color: #EEF4FE; width: 100%; } #suspension { -webkit-user-select: none; cursor: pointer; overflow: hidden; } #suspension { cursor: pointer !important; height: 25px; border-radius: 4px; display: flex; border: 1px solid #3388FE; }</style>主进程创建悬浮窗页面代码路径: /src/main/window.jsimport {BrowserWindow, ipcMain, screen, Menu, shell, app, webContents} from ’electron’var win = null;const window = BrowserWindow.fromWebContents(webContents.getFocusedWebContents());const winURL = process.env.NODE_ENV === ‘development’ ? http://localhost:9080/#/suspension : file://${__dirname}/index.html/#/suspension;ipcMain.on(‘showSuspensionWindow’, () => { if (win) { if (win.isVisible()) { createSuspensionWindow(); } else { win.showInactive(); } } else { createSuspensionWindow(); }});ipcMain.on(‘createSuspensionMenu’, (e) => { const rightM = Menu.buildFromTemplate([ {label: ‘开始全部任务’, enabled: false}, {label: ‘暂停全部任务’, enabled: false}, {label: ‘本次传输完自动关机’}, {type: ‘separator’}, { label: ‘隐藏悬浮窗’, click: () => { window.webContents.send(‘hideSuspension’, false); win.hide() } }, {type: ‘separator’}, { label: ‘加入qq群’, click: () => { shell.openExternal(’tencent://groupwpa/?subcmd=all&param=7B2267726F757055696E223A3831343237303636392C2274696D655374616D70223A313533393531303138387D0A’); } }, { label: ‘GitHub地址’, click: () => { shell.openExternal(‘https://github.com/lihaotian0607/auth’); } }, { label: ‘退出软件’, click: () => { app.quit(); } }, ]); rightM.popup({});});function createSuspensionWindow() { win = new BrowserWindow({ width: 107, //悬浮窗口的宽度 比实际DIV的宽度要多2px 因为有1px的边框 height: 27, //悬浮窗口的高度 比实际DIV的高度要多2px 因为有1px的边框 type: ’toolbar’, //创建的窗口类型为工具栏窗口 frame: false, //要创建无边框窗口 resizable: false, //禁止窗口大小缩放 show: false, //先不让窗口显示 webPreferences: { devTools: false //关闭调试工具 }, transparent: true, //设置透明 alwaysOnTop: true, //窗口是否总是显示在其他窗口之前 }); const size = screen.getPrimaryDisplay().workAreaSize; //获取显示器的宽高 const winSize = win.getSize(); //获取窗口宽高 //设置窗口的位置 注意x轴要桌面的宽度 - 窗口的宽度 win.setPosition(size.width - winSize[0], 100); win.loadURL(winURL); win.once(‘ready-to-show’, () => { win.show() }); win.on(‘close’, () => { win = null; })}ipcMain.on(‘hideSuspensionWindow’, () => { if (win) { win.hide(); }});store文件路径: /src/renderer/store/modules/suspension.jsimport storejs from ‘storejs’const state = { show: storejs.get(‘showSuspension’)};const actions = { showSuspension: function ({state, commit}) { let status = true; storejs.set(‘showSuspension’, status); state.show = status; }, hideSuspension: function ({state, commit}) { let status = false; storejs.set(‘showSuspension’, status); state.show = status; },};export default ({ state, actions});版权说明里面使用的百度的图标以及UI作为学习使用,请不要作为商业用途!遗留问题在软件关闭之后重启会导致悬浮窗口的位置重置 也曾尝试在主进程中使用store.js 但是不能用!如果想解决这个问题 可以在渲染进程中将拖动的最后坐标保存到storejs中在渲染进程给主进程发送异步消息的时候将坐标携带进去 也可以使用nedb在主进程中存储坐标!github地址使用electron制作百度网盘客户端: https://github.com/lihaotian0…使用electron制作百度网盘悬浮窗: https://github.com/lihaotian0…目前这个开源代码中没有悬浮窗 有时间了会加上去!!! ...

October 22, 2018 · 2 min · jiezi

Session

SessionCookie 和 Session区别与联系由于HTTP协议是无状态的协议,所以服务端需要记录用户的状态时,就需要用某种机制来识具体的用户,这个机制就是Session。典型的场景比如购物车,当你点击下单按钮时,由于HTTP协议无状态,所以并不知道是哪个用户操作的,所以服务端要为特定的用户创建了特定的Session,用用于标识这个用户,并且跟踪用户,这样才知道购物车里面有几件物品。这个Session是保存在服务端的,有一个唯一标识。在服务端保存Session的方法很多,内存、数据库、文件、集群等。服务端如何识别特定的客户?第一次创建Session的时候,服务端会在HTTP协议中告诉客户端,需要在 Cookie 里面记录一个Session ID,以后每次请求把这个会话ID发送到服务器,就可以依据此来识别不同客户端了。如果客户端的浏览器禁用了 Cookie 怎么办?一般这种情况下,会使用一种叫做URL重写的技术来进行会话跟踪,即每次HTTP交互,URL后面都会被附加上一个诸如 sid=xxxxx 这样的参数,服务端据此来识别用户。总结:Session是在服务端保存的一个数据结构,用来跟踪用户的状态,这个数据可以保存在集群、数据库、文件中;Cookie是客户端保存用户信息的一种机制,用来记录用户的一些信息,也是实现Session的一种方式。来源链接:https://www.zhihu.com/questio…什么是session?服务器通过Cookie发送给客户端一个sessionIDsessionID对应服务器里的一小块内存,这里保存着用户的信息,例如登录信息,购物车信息等。每次用户访问服务器的时候,服务器通过浏览器发送来的cookie里的sessionID去读取对应的内存里的信息,以此来知道用户的隐私信息。注意session的好处是防止用户随意篡改cookie,获取别人的信息。如果用户随意篡改了sessionID,那么只能重新登录。因为sessionID是随机数,或者随机数夹杂着一些字母,所以没有可能暴力破解sessionID,获取别的用户的信息。类比:session相当于发会员卡,会员卡上只有卡号(sessionID)。下次去健身房的时候,只要看卡号上,就能确定你本人的去他信息。而cookie相当于把信息都写在会员卡上了。关于session的实现代码演示(nodejs)总结Session 与 Cookie 的关系一般来说,Session 基于 Cookie 来实现。Cookie服务器通过 Set-Cookie 头给客户端一串字符串客户端每次访问相同域名的网页时,必须带上这段字符串客户端要在一段时间内保存这个CookieCookie 默认在用户关闭页面后就失效,后台代码可以任意设置 Cookie 的过期时间大小大概在 4kb 以内SessionSession(不翻译,或翻译为会话)将 SessionID(随机数)通过 Cookie 发给客户端客户端访问服务器时,服务器读取 SessionID服务器有一块内存(哈希表)保存了所有 session通过 SessionID 我们可以得到对应用户的隐私信息,如 id、email这块内存(哈希表)就是服务器上的所有 session

October 16, 2018 · 1 min · jiezi

LocalStorage、SessionStorage

window.sessionStorage和window.localStorage接口用于脚本在浏览器保存数据。LocalStorage基本使用设置window.sessionStorage.setItem(‘key’, ‘value’);window.localStorage.setItem(‘key’, ‘value’);获取window.sessionStorage.getItem(‘key’)window.localStorage.getItem(‘key’)清除localStorage.removeItem(‘key’);window.localStorage.clear()完整用法注意点localStorage是html5技术提供的API,html5中新增加的标签,技术(包括promise,localStorage等),统称为html5session是服务器上存的hash,但localStorage实质也是一个hash,只不过是浏览器上的hashlocalStorage只能存String类型的字符串存函数会转化成字符串。存对象的时候会变成"[object Object]",因为({1:‘xxx’}).toString()//"[object Object]“解决方法:用json来存。例如:localStorage.setItem(‘jsonString’, JSON.stringify({name: ‘mtt’}))使用注意localStorage里的数据和js变量有什么区别?当一个js变量被从新赋值,变量的值当即被改变,但当页面刷新时,变量又回到最初的状态。而localStorage的变量不存在页面里,windows系统存在客户端本地的C盘的一个文件里。简单的使用实例想要只提示用户一次,当下次用户进入这个网站上的时候,不跳出提示框。<script> let already = localStrorage.getItem(‘已经提示了’) if(!already){ alert(‘你好,我们的网站即将改版了’) localStorage.setItrm(‘已经提示了’, true) }</script>总结LocalStorage 跟 HTTP 无关(而cookie是http的一个头)发送HTTP请求时 不会带上 LocalStorage 的值只有相同域名的页面才能互相读取 LocalStorage(没有同源那么严格)每个域名 localStorage 最大存储量为 5Mb 左右(每个浏览器不一样)常用场景:记录有没有提示过用户(没有用的信息,不能记录密码)LocalStorage 永久有效,除非用户主动清理缓存SessionStoragesessionStorage保存的数据用于浏览器的一次会话(session),当会话结束(通常是窗口关闭),数据被清空;localStorage保存的数据长期存在,下一次访问该网站的时候,网页可以直接读取以前保存的数据。除了保存期限的长短不同,这两个对象的其他方面都一致。总结:SessionStorage 在用户关闭页面(会话结束)后就失效。其余的和localstorage一样

October 16, 2018 · 1 min · jiezi

[译]Vuex中使用localStorage实现数据的持久化保存

Vuex中数据的全局化管理可以使得我们数据更加便于管理,但存在一个缺点就是当页面刷新也就是实例重新创建时,数据就会丢失,而使用localStorage就可以使得数据持久化保存,本文就此提供一种解决思路。1. localStorage基础localStorage是一种缓存机制,即时浏览器关闭后依然可以获取到。这使得你可以在页面上保存一些数据,后续的操作也可以继续访问的到。MDN 网站上有详细介绍,在此不再赘述,下面介绍下它的基本用法,非常简单。下面中的key表示获取访问数据的标识符// 存储localStorage.setItem(‘key’,‘value’);// 获取let val = localStorage.getItem(‘key’);注意: localStorage只能是字符串,意味着你想要存储对象和数组的话,localStorage只能先转换成JSON格式的字符串,然后获取的时候,再转换回原理的格式。// 保存数据localStorage.setItem(‘key’, JSON.stringify(object));// 数据获取,需转换let obj = JSON.parse(localStorage.getItem(‘key’));2. VuexVuex是Vue的一个中央数据管理工具,提供了一种全局存储中心,可以保存组件的数据,并且能够获取,更新和响应式变化。除非你做一个很小的应用,还是强力建议你使用Vuex来管理你的数据和状态。最近在开发一个Vueapp应用,碰到个需求,就是,我想要一些数据能够存在localStorage中,无论什么时候更新的时候,这就意味着,当用户关闭它们的浏览器或者电脑的时候,然后重新打开页面,数据依然能够显示在页面上。例如电商应用中,当用户添加商品到购物车的时候,页面关闭再打开是,购物车的物品依然能够展示在页面上展示。2.1 初始化store一个store实例的典型配置// 初始化 storeconst store new Vuex.Store({ state: { count: 1 }, mutations: {}, getters: {}});2.2 存储数据我们希望无论state对象任何时候更新,数据都能够缓存下来,我们可以在actions提交mutation来改变state中的数据。// Subscribe store.subscribe((mutation, state) => { // 保存数据对象为JSON字符串 localStorage.setItem(‘store’, JSON.stringify(state));});2.3 获取数据创建一个方法来获取`localStorage保存的数据,首先需要检查数据是否已经存在const store new Vuex.Store({ state: { count: 1 }, mutations: { initialiseStore(state) { // 检查ID是否存在 if(localStorage.getItem(‘store’)) { } } }, getters: {}});如果数据存在的话我们需要使用replaceState来替换数据。const store new Vuex.Store({ state: { count: 1 }, mutations: { initialiseStore(state) { // 检查ID是否存在 if(localStorage.getItem(‘store’)) { // 用存储的数据替换掉state 对象 this.replaceState( Object.assign(state, JSON.parse(localStorage.getItem(‘store’))) ); } } }, getters: {}});最后一步就是当实例创建的时候提交相应的mutation,我们想要在实例创建的最开始就执行,所以,我们选择在beforeCreate的时候触发。new Vue({ el: ‘#app’, store, beforeCreate() { this.$store.commit(‘initialiseStore’); }});3. 缓存的有效性在最近的一个项目中使用它时,数据的变化并没有引起相应的变化。 这是因为在不清除localStorage的情况下改变了store的结构。 添加值的时候是正常的,但重命名,编辑或更改值时,对应值的类型没有改变。当使用semver进行版本控制的话,可以调用版本号来检查store是否是最新版本。如果在加载时,用户与我的应用版本号相同,则进行缓存处理,否则清除缓存并加载新版本。注意: 会清除整个缓存,因此如果您依赖它来获取用户首选项或类似内容,您可能偏向选择性删除。第一步是加载我们的版本号。 我们存储在package.json中,因此,使用ES6的话很简单:import {version} from ‘./package.json’;但是,如果从git标记缓存您的内容或将其作为变量放在某处,它需要与存储分开访问,以便缓存不会完全覆盖它。接下来,在store中创建一个空字符串,以便在验证后保存该版本号。state: { // Cache version version: ‘’, count: 1},我们现在可以更新initialiseStore,以使用缓存中的版本号检查版本,并根据返回结果执行不同操作initialiseStore(state) { // 检查 store 是否存在 if(localStorage.getItem(‘store’)) { let store = JSON.parse(localStorage.getItem(‘store’)); // 检查存储的版本号是否与当前相同,不同的话加载缓存中的版本 if(store.version == version) { this.replaceState( Object.assign(state, store) ); } else { state.version = version; } }}4. 选择性缓存当只从store中缓存一些元素,可以通过在订阅功能中创建一个新对象并存储它来实现。我们已经在store加载时将缓存与当前存储状态合并,因此不需要更改。store.subscribe((mutation, state) => { let store = { version: state.version, count: 1 }; localStorage.setItem(‘store’, JSON.stringify(store));});参考Vue: Using localStorage with Vuex storeawesome-vue ...

September 3, 2018 · 1 min · jiezi