xml地图|网站地图|网站标签 [设为首页] [加入收藏]

正规赌博十大app排名

当前位置:网上十大正规赌博平台 > 正规赌博十大app排名 > 网上十大正规赌博平台浅谈移动前端的最佳实践

网上十大正规赌博平台浅谈移动前端的最佳实践

来源:http://www.nb-machinery.com 作者:网上十大正规赌博平台 时间:2019-10-29 00:06

关于作者:cucr

网上十大正规赌博平台 1

新浪微博:@hop_ping 个人主页 · 我的文章 · 17

网上十大正规赌博平台 2

前端本地文件操作与上传

2017/12/07 · JavaScript · 1 评论 · 文件

原文出处: 人人网FED博客   

前端无法像原生APP一样直接操作本地文件,否则的话打开个网页就能把用户电脑上的文件偷光了,所以需要通过用户触发,用户可通过以下三种方式操作触发:

  1. 通过input type=”file” 选择本地文件
  2. 通过拖拽的方式把文件拖过来
  3. 在编辑框里面复制粘贴

第一种是最常用的手段,通常还会自定义一个按钮,然后盖在它上面,因为type=”file”的input不好改变样式。如下代码写一个选择控件,并放在form里面:

JavaScript

<form> <input type="file" id="file-input" name="fileContent"> </form>

1
2
3
<form>
    <input type="file" id="file-input" name="fileContent">
</form>

然后就可以用FormData获取整个表单的内容:

把input的value和formData打印出来是这样的:

网上十大正规赌博平台 3

可以看到文件的路径是一个假的路径,也就是说在浏览器无法获取到文件的真实存放位置。同时FormData打印出来是一个空的Objet,但并不是说它的内容是空的,只是它对前端开发人员是透明的,无法查看、修改、删除里面的内容,只能append添加字段。

FormData无法得到文件的内容,而使用FileReader可以读取整个文件的内容。用户选择文件之后,input.files就可以得到用户选中的文件,如下代码:

JavaScript

$("#file-input").on("change", function() { let fileReader = new FileReader(), fileType = this.files[0].type; fileReader.onload = function() { if (/^image/.test(fileType)) { // 读取结果在fileReader.result里面 $(`<img src="${this.result}">`).appendTo("body"); } } // 打印原始File对象 console.log(this.files[0]); // base64方式读取 fileReader.readAsDataURL(this.files[0]); });

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$("#file-input").on("change", function() {
    let fileReader = new FileReader(),
        fileType = this.files[0].type;
    fileReader.onload = function() {
        if (/^image/.test(fileType)) {
            // 读取结果在fileReader.result里面
            $(`<img src="${this.result}">`).appendTo("body");
        }
    }
    // 打印原始File对象
    console.log(this.files[0]);
    // base64方式读取
    fileReader.readAsDataURL(this.files[0]);    
});

把原始的File对象打印出来是这样的:

网上十大正规赌博平台 4

它是一个window.File的实例,包含了文件的修改时间、文件名、文件的大小、文件的mime类型等。如果需要限制上传文件的大小就可以通过判断size属性有没有超,单位是字节,而要判断是否为图片文件就可以通过type类型是否以image开头。通过判断文件名的后缀可能会不准,而通过这种判断会比较准。上面的代码使用了一个正则判断,如果是一张图片的话就把它赋值给img的src,并添加到dom里面,但其实这段代码有点问题,就是web不是所有的图片都能通过img标签展示出来,通常是jpg/png/gif这三种,所以你应该需要再判断一下图片格式,如可以把判断改成:

JavaScript

/^image/[jpeg|png|gif]/.test(this.type)

1
/^image/[jpeg|png|gif]/.test(this.type)

然后实例化一个FileReader,调它的readAsDataURL并把File对象传给它,监听它的onload事件,load完读取的结果就在它的result属性里了。它是一个base64格式的,可直接赋值给一个img的src.

使用FileReader除了可读取为base64之外,还能读取为以下格式:

JavaScript

fileReader.readAsDataURL(this.files[0]); // 以二进制字符串方式读取,结果是二进制内容的utf-8形式,已被废弃了 fileReader.readAsBinaryString(this.files[0]); // 以原始二进制方式读取,读取结果可直接转成整数数组 fileReader.readAsArrayBuffer(this.files[0]);

1
2
3
4
5
6
7
fileReader.readAsDataURL(this.files[0]);
// 以二进制字符串方式读取,结果是二进制内容的utf-8形式,已被废弃了
fileReader.readAsBinaryString(this.files[0]);
// 以原始二进制方式读取,读取结果可直接转成整数数组
fileReader.readAsArrayBuffer(this.files[0]);

其它的主要是能读取为ArrayBuffer,它是一个原始二进制格式的结果。把ArrayBuffer打印出来是这样的:

网上十大正规赌博平台 5

可以看到,它对前端开发人员也是透明的,不能够直接读取里面的内容,但可以通过ArrayBuffer.length得到长度,还能转成整型数组,就能知道文件的原始二进制内容了:

JavaScript

let buffer = this.result; // 依次每字节8位读取,放到一个整数数组 let view = new Uint8Array(buffer); console.log(view);

1
2
3
4
let buffer = this.result;
// 依次每字节8位读取,放到一个整数数组
let view = new Uint8Array(buffer);
console.log(view);

如果是通过第二种拖拽的方式,应该怎么读取文件呢?如下html(样式略):

JavaScript

<div class="img-container"> drop your image here </div>

1
2
3
<div class="img-container">
    drop your image here
</div>

这将在页面显示一个框:

网上十大正规赌博平台 6

然后监听它的拖拽事件:

JavaScript

$(".img-container").on("dragover", function (event) { event.preventDefault(); }) .on("drop", function(event) { event.preventDefault(); // 数据在event的dataTransfer对象里 let file = event.originalEvent.dataTransfer.files[0]; // 然后就可以使用FileReader进行操作 fileReader.readAsDataURL(file); // 或者是添加到一个FormData let formData = new FormData(); formData.append("fileContent", file); })

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$(".img-container").on("dragover", function (event) {
    event.preventDefault();
})
 
.on("drop", function(event) {
    event.preventDefault();
    // 数据在event的dataTransfer对象里
    let file = event.originalEvent.dataTransfer.files[0];
 
    // 然后就可以使用FileReader进行操作
    fileReader.readAsDataURL(file);
 
    // 或者是添加到一个FormData
    let formData = new FormData();
    formData.append("fileContent", file);
})

数据在drop事件的event.dataTransfer.files里面,拿到这个File对象之后就可以和输入框进行一样的操作了,即使用FileReader读取,或者是新建一个空的formData,然后把它append到formData里面。

第三种粘贴的方式,通常是在一个编辑框里操作,如把div的contenteditable设置为true:

JavaScript

<div contenteditable="true"> hello, paste your image here </div>

1
2
3
<div contenteditable="true">
      hello, paste your image here
</div>

粘贴的数据是在event.clipboardData.files里面:

JavaScript

$("#editor").on("paste", function(event) { let file = event.originalEvent.clipboardData.files[0]; });

1
2
3
$("#editor").on("paste", function(event) {
    let file = event.originalEvent.clipboardData.files[0];
});

但是Safari的粘贴不是通过event传递的,它是直接在输入框里面添加一张图片,如下图所示:

网上十大正规赌博平台 7

它新建了一个img标签,并把img的src指向一个blob的本地数据。什么是blob呢,如何读取blob的内容呢?

blob是一种类文件的存储格式,它可以存储几乎任何格式的内容,如json:

JavaScript

let data = {hello: "world"}; let blob = new Blob([JSON.stringify(data)], {type : 'application/json'});

1
2
3
let data = {hello: "world"};
let blob = new Blob([JSON.stringify(data)],
  {type : 'application/json'});

为了获取本地的blob数据,我们可以用ajax发个本地的请求:

JavaScript

$("#editor").on("paste", function(event) { // 需要setTimeout 0等图片出来了再处理 setTimeout(() => { let img = $(this).find("img[src^='blob']")[0]; console.log(img.src); // 用一个xhr获取blob数据 let xhr = new XMLHttpRequest(); xhr.open("GET", img.src); // 改变mime类型 xhr.responseType = "blob"; xhr.onload = function () { // response就是一个Blob对象 console.log(this.response); }; xhr.send(); }, 0); });

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$("#editor").on("paste", function(event) {
    // 需要setTimeout 0等图片出来了再处理
    setTimeout(() => {
        let img = $(this).find("img[src^='blob']")[0];
        console.log(img.src);
        // 用一个xhr获取blob数据
        let xhr = new XMLHttpRequest();
        xhr.open("GET", img.src);
        // 改变mime类型
        xhr.responseType = "blob";
        xhr.onload = function () {
            // response就是一个Blob对象
            console.log(this.response);
        };
        xhr.send();
    }, 0);
});

上面代码把blob打印出来是这样的:

网上十大正规赌博平台 8

能得到它的大小和类型,但是具体内容也是不可见的,它有一个slice的方法,可用于切割大文件。和File一样,可以使用FileReader读取它的内容:

JavaScript

function readBlob(blobImg) { let fileReader = new FileReader(); fileReader.onload = function() { console.log(this.result); } fileReader.onerror = function(err) { console.log(err); } fileReader.readAsDataURL(blobImg); } readBlob(this.response);

1
2
3
4
5
6
7
8
9
10
11
12
function readBlob(blobImg) {
    let fileReader = new FileReader();
    fileReader.onload = function() {
        console.log(this.result);
    }
    fileReader.onerror = function(err) {
        console.log(err);
    }
    fileReader.readAsDataURL(blobImg);
}
 
readBlob(this.response);

除此,还能使用window.URL读取,这是一个新的API,经常和Service Worker配套使用,因为SW里面常常要解析url。如下代码:

JavaScript

function readBlob(blobImg) { let urlCreator = window.URL || window.webkitURL; // 得到base64结果 let imageUrl = urlCreator.createObjectURL(this.response); return imageUrl; } readBlob(this.response);

1
2
3
4
5
6
7
8
function readBlob(blobImg) {
    let urlCreator = window.URL || window.webkitURL;
    // 得到base64结果
    let imageUrl = urlCreator.createObjectURL(this.response);
    return imageUrl;
}
 
readBlob(this.response);

关于src使用的是blob链接的,除了上面提到的img之外,另外一个很常见的是video标签,如youtobe的视频就是使用的blob:

网上十大正规赌博平台 9

这种数据不是直接在本地的,而是通过持续请求视频数据,然后再通过blob这个容器媒介添加到video里面,它也是通过URL的API创建的:

JavaScript

let mediaSource = new MediaSource(); video.src = URL.createObjectURL(mediaSource); let sourceBuffer = mediaSource.addSourceBuffer('video/mp4; codecs="avc1.42E01E, mp4a.40.2"'); sourceBuffer.appendBuffer(buf);

1
2
3
4
let mediaSource = new MediaSource();
video.src = URL.createObjectURL(mediaSource);
let sourceBuffer = mediaSource.addSourceBuffer('video/mp4; codecs="avc1.42E01E, mp4a.40.2"');
sourceBuffer.appendBuffer(buf);

具体我也没实践过,不再展开讨论。

上面,我们使用了三种方式获取文件内容,最后得到:

  1. FormData格式
  2. FileReader读取得到的base64或者ArrayBuffer二进制格式

如果直接就是一个FormData了,那么直接用ajax发出去就行了,不用做任何处理:

JavaScript

let form = document.querySelector("form"), formData = new FormData(form), formData.append("fileName", "photo.png"); let xhr = new XMLHttpRequest(); // 假设上传文件的接口叫upload xhr.open("POST", "/upload"); xhr.send(formData);

1
2
3
4
5
6
7
8
let form = document.querySelector("form"),
    formData = new FormData(form),
formData.append("fileName", "photo.png");
 
let xhr = new XMLHttpRequest();
// 假设上传文件的接口叫upload
xhr.open("POST", "/upload");
xhr.send(formData);

如果用jQuery的话,要设置两个属性为false:

JavaScript

$.ajax({ url: "/upload", type: "POST", data: formData, processData: false, // 不处理数据 contentType: false // 不设置内容类型 });

1
2
3
4
5
6
7
$.ajax({
    url: "/upload",
    type: "POST",
    data: formData,
    processData: false,  // 不处理数据
    contentType: false   // 不设置内容类型
});

因为jQuery会自动把内容做一些转义,并且根据data自动设置请求mime类型,这里告诉jQuery直接用xhr.send发出去就行了。

观察控制台发请求的数据:

网上十大正规赌博平台 10

可以看到这是一种区别于用&连接参数的方式,它的编码格式是multipart/form-data,就是上传文件form表单写的enctype:

JavaScript

<form enctype="multipart/form-data" method="post"> <input type="file" name="fileContent"> </form>

1
2
3
<form enctype="multipart/form-data" method="post">
    <input type="file" name="fileContent">
</form>

如果xhr.send的是FormData类型话,它会自动设置enctype,如果你用默认表单提交上传文件的话就得在form上面设置这个属性,因为上传文件只能使用POST的这种编码。常用的POST编码是application/x-www-form-urlencoded,它和GET一样,发送的数据里面,参数和参数之间使用&连接,如:

key1=value1&key2=value2

特殊字符做转义,这个数据POST是放在请求body里的,而GET是拼在url上面的,如果用jq的话,jq会帮你拼并做转义。

而上传文件用的这种multipart/form-data,参数和参数之间是且一个相同的字符串隔开的,上面的是使用:

——WebKitFormBoundary72yvM25iSPYZ4a3F

这个字符通常会取得比较长、比较随机,因为要保证正常的内容里面不会出现这个字符串,这样内容的特殊字符就不用做转义了。

请求的contentType被浏览器设置成:

Content-Type:

multipart/form-data; boundary=—-WebKitFormBoundary72yvM25iSPYZ4a3F

后端服务通过这个就知道怎么解析这么一段数据了。(通常是使用的框架处理了,而具体的接口不需要关心应该怎么解析)

如果读取结果是ArrayBuffer的话,也是可以直接用xhr.send发送出去的,但是一般我们不会直接把一个文件的内容发出去,而是用某个字段名等于文件内容的方式。如果你读取为ArrayBuffer的话再上传的话其实作用不是很大,还不如直接用formData添加一个File对象的内容,因为上面三种方式都可以拿到File对象。如果一开始就是一个ArrayBuffer了,那么可以转成blob然后再append到FormData里面。

使用比较多的应该是base64,因为前端经常要处理图片,读取为base64之后就可以把它画到一个canvas里面,然后就可以做一些处理,如压缩、裁剪、旋转等。最后再用canvas导出一个base64格式的图片,那怎么上传base64格式的呢?

第一种是拼一个表单上传的multipart/form-data的格式,再用xhr.sendAsBinary发出去,如下代码:

JavaScript

let boundary = "----------boundaryasoifvlkasldvavoadv"; xhr.sendAsBinary([ // name=data boundary, 'Content-Disposition: form-data; name="data"; filename="' fileName '"', 'Content-Type: ' "image/" fileType, '', atob(base64Data), boundary, //name=imageType boundary, 'Content-Disposition: form-data; name="imageType"', '', fileType, boundary '--' ].join('rn'));

1
2
3
4
5
6
7
8
9
10
11
12
13
let boundary = "----------boundaryasoifvlkasldvavoadv";
xhr.sendAsBinary([
    // name=data
    boundary,
        'Content-Disposition: form-data; name="data"; filename="' fileName '"',
        'Content-Type: ' "image/" fileType, '',
        atob(base64Data), boundary,
    //name=imageType
    boundary,
        'Content-Disposition: form-data; name="imageType"', '',
        fileType,
    boundary '--'
].join('rn'));

上面代码使用了window.atob的api,它可以把base64还原成原始内容的字符串表示,如下图所示:

网上十大正规赌博平台 11

btoa是把内容转化成base64编码,而atob是把base64还原。在调atob之前,需要把表示内容格式的不属于base64内容的字符串去掉,即上面代码第一行的replace处理。

这样就和使用formData类似了,但是由于sendAsBinary已经被deprecated了,所以新代码不建议再使用这种方式。那怎么办呢?

可以把base64转化成blob,然后再append到一个formData里面,下面的函数(来自b64-to-blob)可以把base64转成blob:

JavaScript

contentType = contentType || ''; sliceSize = sliceSize || 512; var byteCharacters = atob(b64Data); var byteArrays = []; for (var offset = 0; offset < byteCharacters.length; offset = sliceSize) { var slice = byteCharacters.slice(offset, offset sliceSize); var byteNumbers = new Array(slice.length); for (var i = 0; i < slice.length; i ) { byteNumbers[i] = slice.charCodeAt(i); } var byteArray = new Uint8Array(byteNumbers); byteArrays.push(byteArray); } var blob = new Blob(byteArrays, {type: contentType}); return blob; }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
    contentType = contentType || '';
    sliceSize = sliceSize || 512;
    var byteCharacters = atob(b64Data);
    var byteArrays = [];
    for (var offset = 0; offset < byteCharacters.length; offset = sliceSize) {
      var slice = byteCharacters.slice(offset, offset sliceSize);
      var byteNumbers = new Array(slice.length);
      for (var i = 0; i < slice.length; i ) {
        byteNumbers[i] = slice.charCodeAt(i);
      }
      var byteArray = new Uint8Array(byteNumbers);
      byteArrays.push(byteArray);
    }
    var blob = new Blob(byteArrays, {type: contentType});
    return blob;
}

然后就可以append到formData里面:

JavaScript

let blob = b64toBlob(b64Data, "image/png"), formData = new FormData(); formData.append("fileContent", blob);

1
2
3
let blob = b64toBlob(b64Data, "image/png"),
    formData = new FormData();
formData.append("fileContent", blob);

这样就不用自己去拼一个multipart/form-data的格式数据了。

上面处理和上传文件的API可以兼容到IE10 ,如果要兼容老的浏览器应该怎么办呢?

可以借助一个iframe,原理是默认的form表单提交会刷新页面,或者跳到target指定的那个url,但是如果把ifrmae的target指向一个iframe,那么刷新的就是iframe,返回结果也会显示在ifame,然后获取这个ifrmae的内容就可得到上传接口返回的结果。

如下代码:

JavaScript

iframe.display = "none"; iframe.name = "form-iframe"; document.body.appendChild(iframe); // 改变form的target form.target = "form-iframe"; iframe.onload = function() { //获取iframe的内容,即服务返回的数据 let responseText = this.contentDocument.body.textContent || this.contentWindow.document.body.textContent; }; form.submit();

1
2
3
4
5
6
7
8
9
10
11
12
13
iframe.display = "none";
iframe.name = "form-iframe";
document.body.appendChild(iframe);
// 改变form的target
form.target = "form-iframe";
iframe.onload = function() {
    //获取iframe的内容,即服务返回的数据
    let responseText = this.contentDocument.body.textContent
            || this.contentWindow.document.body.textContent;
};
form.submit();

form.submit会触发表单提交,当请求完成(成功或者失败)之后就会触发iframe的onload事件,然后在onload事件获取返回的数据,如果请求失败了的话,iframe里的内容就为空,可以用这个判断请求有没有成功。

使用iframe没有办法获取上传进度,使用xhr可以获取当前上传的进度,这个是在XMLHttpRequest 2.0引入的:

JavaScript

xhr.upload.onprogress = function (event) { if (event.lengthComputable) { // 当前上传进度的百分比 duringCallback ((event.loaded / event.total)*100); } };

1
2
3
4
5
6
xhr.upload.onprogress = function (event) {
    if (event.lengthComputable) {
        // 当前上传进度的百分比
        duringCallback ((event.loaded / event.total)*100);
    }
};

这样就可以做一个真实的loading进度条。

本文讨论了3种交互方式的读取方式,通过input控件在input.files可以得到File文件对象,通过拖拽的是在drop事件的event.dataTransfer.files里面,而通过粘贴的paste事件在event.clipboardData.files里面,Safari这个怪胎是在编辑器里面插入一个src指向本地的img标签,可以通过发送一个请求加载本地的blob数据,然后再通过FileReader读取,或者直接append到formData里面。得到的File对象就可以直接添加到FormData里面,如果需要先读取base64格式做处理的,那么可以把处理后的base64转化为blob数据再append到formData里面。对于老浏览器,可以使用一个iframe解决表单提交刷新页面或者跳页的问题。

总之,前端处理和上传本地文件应该差不多就是这些内容了,但是应该还有好多细节没有提及到,读者可通过本文列的方向自行实践。如果有其它的上传方式还请告知。

1 赞 收藏 1 评论

网上十大正规赌博平台 12

快的假象

除了真实手段优化代码处理尺寸,降低请求数,仍然有一些带有“欺骗”性质的技术可以做首页加载的优化,比如lazyload、fake页

使用IndexedDB

现在,你已经有机会熟悉了一些的整体概念,下一步是开始实现基于IndexedDB的应用程序。第一个步骤需要统一IndexedDB在不同浏览器的实现。您可以很容易地添加各种厂商特性的选项的检查,同时在window对象上把它们设置为官方对象相同的名称。下面的清单展示了window.indexedDB,window.IDBTransaction,window.IDBKeyRange的最终结果是如何都被更新,它们被设置为相应的浏览器的特定实现。

JavaScript

window.indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB; window.IDBTransaction = window.IDBTransaction || window.webkitIDBTransaction || window.msIDBTransaction; window.IDBKeyRange = window.IDBKeyRange || window.webkitIDBKeyRange || window.msIDBKeyRange;

1
2
3
4
5
6
7
8
9
10
window.indexedDB = window.indexedDB ||
                   window.mozIndexedDB ||
                   window.webkitIndexedDB ||
                   window.msIndexedDB;
window.IDBTransaction = window.IDBTransaction ||
                   window.webkitIDBTransaction ||
                   window.msIDBTransaction;
window.IDBKeyRange = window.IDBKeyRange ||
                   window.webkitIDBKeyRange ||
                   window.msIDBKeyRange;

现在,每个数据库相关的全局对象持有正确的版本,应用程序可以准备使用IndexedDB开始工作。

Application cache

Application cache是HTML5新增api,虽然都是存储,却与localstorage、cookie不太相同,Application cache存储的是一般是静态资源,允许浏览器请求这些资源时不必通过网络,设计得当的情况可以取代Hybrid的存储静态资源,使用Application cache主要优点是:

使用Application cache可以提升网站载入速度,主要体现在请求传输上,把一些http请求转为本地读取,有效地降低网络延迟,降低http请求,使用简单,还节约流量何乐而不为?

1
使用Application cache可以提升网站载入速度,主要体现在请求传输上,把一些http请求转为本地读取,有效地降低网络延迟,降低http请求,使用简单,还节约流量何乐而不为?

而无论什么存储技术都会有空间限制(据说是5M),这里更新的机制是最为重要的,这里是我们使用的结论:

application cache是绝对值得使用的,是可以锦上添花。但怎么用,用多少是需要考虑的点。由于原理上,application cache是把manifest上的资源一起下载下来,所以manifest里的内容不宜过多,数据量不宜过大;由于manifest的解析通常以页面刷新为触发点,且更新的缓存不会立即被使用,所以缓存的资源应以静态资源、更新频率比较低的资源为主。另外要做好对manifest文件的管理,由于清单内文件不可访问或manifest更新不及时造成的一些问题。

执行用户界面特定代码

现在所有特定于数据库的代码被封装在app.db模块中,用户界面特定代码可以使用此模块来与数据库交互。用户界面特定代码的完整清单(index.ui.js)可以在清单3中得到,完整的(index.html)页面的HTML源代码可以在清单4中得到。

fake页

我们应该避免页面长时间白页,所以会出现fake页的概念,页面渲染仅仅需要HTML以及CSS,这个便是第一个优化点,js对于显示不是必须,ajax也不是。

若是任由js、ajax加载完成再渲染页面,用户很有可能失去耐心,所以搞一些内嵌的css以及通用的html在首页似乎是一个不错的选择

一个静态HTML页面,装载首屏的基本内容,让首页快速显示,然后js加载结束后会马上重新渲染整个页面,这个样子,用户就可以很快的看到页面响应,给用户一个快的错觉

错误冒泡

当然,有时候,请求可能不会按预期完成。IndexedDB API通过错误冒泡功能来帮助跟踪和管理错误。如果一个特定的请求遇到错误,你可以尝试在请求对象上处理错误,或者你可以允许错误通过调用栈冒泡向上传递。这个冒泡天性,使得你不需要为每个请求实现特定错误处理操作,而是可以选择只在一个更高级别上添加错误处理,它给你一个机会,保持你的错误处理代码简洁。本文中实现的例子,是在一个高级别处理错误,以便更细粒度操作产生的任何错误冒泡到通用的错误处理逻辑。

移动革命——Hybrid

Hybrid技术将前端推到了前所未有的高度,但是Hybrid开发中本身也有一些需要注意的地方,这里如果出现了设计上的失误会对后期业务团队开发带问题,有几点可以注意

另一种选择

浏览器支持本地数据库并不是从IndexedDB才开始实现,它是在WebSQL实现之后的一种新方法。类似IndexedDB,WebSQL是一个客户端数据库,但它作为一个关系数据库的实现,使用结构化查询语言(SQL)与数据库通信。WebSQL的历史充满了曲折,但底线是没有主流的浏览器厂商对WebSQL继续支持。

如果WebSQL实际上是一个废弃的技术,为什么还要提它呢?有趣的是,WebSQL在浏览器里得到稳固的支持。Chrome, Safari, iOS Safari, and Android 浏览器都支持。另外,并不是这些浏览器的最新版本才提供支持,许多这些最新最好的浏览器之前的版本也可以支持。有趣的是,如果你为WebSQL添加支持来支持IndexedDB,你突然发现,许多浏览器厂商和版本成为支持浏览器内置数据库的某种化身。

因此,如果您的应用程序真正需要一个客户端数据库,你想要达到的最高级别的采用可能,当IndexedDB不可用时,也许您的应用程序可能看起来需要选择使用WebSQL来支持客户端数据架构。虽然文档数据库和关系数据库管理数据有鲜明的差别,但只要你有正确的抽象,就可以使用本地数据库构建一个应用程序。

框架选择

移动前端依旧离不开框架,而且框架呈变化状态,以我厂为例,我们几轮框架选型是:

① 多页应用 jQuery

② jQuery mobile(这个坑谁用谁知道)

③ 开始webapp模式(jQuery requireJS Backbone underscore)

④ 瘦身(zepto requireJS Backbone View部分 underscore)

……

移动大潮来临后,浏览器基本的兼容得到了保证,所以完整的jQuery变得不是那么必须,因为尺寸原因,所以一般被zepto替换,zepto与jQuery有什么差异呢?

浏览器支持

也许在开发Web应用程序最重要的问题是:“浏览器是否支持我想要做的?“尽管浏览器对IndexedDB的支持在继续增长,采用率并不是我们所希望的那样普遍。图3显示了caniuse.com网站的报告,支持IndexedDB的为66%多一点点。最新版本的火狐,Chrome,Opera,Safar,iOS Safari,和Android完全支持IndexedDB,Internet Explorer和黑莓部分支持。虽然这个列表的支持者是令人鼓舞的,但它没有告诉整个故事。

网上十大正规赌博平台 13

图3:浏览器对IndexedDB的支持,来自caniuse.com

只有非常新版本的Safari和iOS Safari 支持IndexedDB。据caniuse.com显示,这只占大约0.01%的全球浏览器使用。IndexedDB不是一个你认为能够理所当然得到支持的现代Web API,但是你将很快会这样认为。

浏览器并发数

我之前碰到一次图片加载阻塞js的案例,其出现原因就是浏览器并发数限制,这里以一个图为例:

网上十大正规赌博平台 14

chrome在请求资源下会有所限制,移动端的限制普遍在6个左右,这个时候在并发数被占满时,你的ajax便会被搁置,这在webapp中情况更加常见,所以网络限制的情况下请求数控制是必要的,而且可以降低服务器端的压力。

应用概述

在本教程中,您将学习如何创建一个使用IndexedDB存储数据的模块化JavaScript应用程序。为了了解应用程序是如何工作的,参考图4,它描述了任务应用程序处于空白状态。从这里您可以为列表添加新任务。图5显示了录入了几个任务到系统的画面。图6显示如何删除一个任务,图7显示了正在编辑任务时的应用程序。

网上十大正规赌博平台 15

图4:空白的任务应用程序

网上十大正规赌博平台 16

图5:任务列表

网上十大正规赌博平台 17

图6:删除任务

网上十大正规赌博平台 18

图7:编辑任务
现在你熟悉的应用程序的功能,下一步是开始为网站铺设基础。

预加载

这里的预加载是在浏览器空闲的时候加载后续页面所需资源,是一种浪费用户流量的行为,属于以空间换时间的做法,但是这个实施难度比较高。

预加载的前提是不影响主程序的情况下偷偷的加载,也就是在浏览器空闲的时候加载,但是浏览器空闲似乎变得不可控制

浏览器空闲不可判断(如果您知道请留言),我们判断的标准是当前没有dom事件操作,没有ajax

1
浏览器空闲不可判断(如果您知道请留言),我们判断的标准是当前没有dom事件操作,没有ajax

可以看出,由于浏览器没有空闲的回调,所以我们只能自己实现,这类的实现不太靠谱,我们的预加载做的就很粗暴,要做预加载需要注意以下几点:

① 浏览器空闲需要一个判断机制 ② 每次空闲时需要有一个队列一点一点的加载资源,否则请求一旦发出很容易影响主逻辑 ③ 做好预加载资源队列的匹配算法,可以是业务团队配置

1
2
3
① 浏览器空闲需要一个判断机制
② 每次空闲时需要有一个队列一点一点的加载资源,否则请求一旦发出很容易影响主逻辑
③ 做好预加载资源队列的匹配算法,可以是业务团队配置

打开数据库的请求生命周期

IndexedDB使用事件生命周期管理数据库的打开和配置操作。图2演示了一个打开的请求在一定的环境下产生upgrade need事件。

网上十大正规赌博平台 19

图2:IndexedDB打开请求的生命周期

所有与数据库的交互开始于一个打开的请求。试图打开数据库时,您必须传递一个被请求数据库的版本号的整数值。在打开请求时,浏览器对比你传入的用于打开请求的版本号与实际数据库的版本号。如果所请求的版本号高于浏览器中当前的版本号(或者现在没有存在的数据库),upgrade needed事件触发。在uprade need事件期间,你有机会通过添加或移除stores,键和索引来操纵object stores。

如果所请求的数据库版本号和浏览器的当前版本号一致,或者升级过程完成,一个打开的数据库将返回给调用者。

全站IScroll化

全站IScroll化一般为了解决:

① fixed问题

② webapp中view独享“scrollTop”

③ webapp page 切换动画顺畅,因为scrollTop与长短页问题

④ 嫌弃原生的scroll不够平滑

这里还是不建议全站使用IScroll这类技术,IScroll可能带来,header消失、文本框消失、可视区域便小等问题,现在还是小范围弹出层使用就好,某天overflow: scroll兼容问题得到解决,区域滚动便不再难了。

这里倒不是一味抵制IScroll全站化,如果页面dom结构简单,如果页面文本框比较少,又做过充分调研,IScroll化带来的页面切换效果还是很赞的,正是道不虚行,只在人也。

结论

随着应用程序的需求的增长,你会发现在客户端高效存储大量的数据的优势。IndexedDB是可以在浏览器中直接使用且支持异步事务的文档数据库实现。尽管浏览器的支持可能不能保障,但在合适的情况下,集成IndexedDB的Web应用程序具有强大的客户端数据的访问能力。

在大多数情况下,所有针对IndexedDB编写的代码是天然基于请求和异步的。官方规范有同步API,但是这种IndexedDB只适合web worker的上下文中使用。这篇文章发布时,还没有浏览器实现的同步格式的IndexedDB API。

一定要保证代码在任何函数域外对厂商特定的indexedDB, IDBTransaction, and IDBKeyRange实例进行了规范化且使用了严格模式。这允许您避免浏览器错误,当在strict mode下解析脚本时,它不会允许你对那些对象重新赋值。

你必须确保只传递正整数的版本号给数据库。传递到版本号的小数值会四舍五入。因此,如果您的数据库目前版本1,您试图访问1.2版本,upgrade-needed事件不会触发,因为版本号最终评估是相同的。

立即执行函数表达式(IIFE)有时叫做不同的名字。有时可以看到这样的代码组织方式,它称为self-executing anonymous functions(自执行匿名函数)或self-invoked anonymous functions(自调用匿名函数)。为进一步解释这些名称相关的意图和含义,请阅读Ben Alman的文章Immediately Invoked Function Expression (IIFE) 。

Listing 1: Implementing the getAll function

JavaScript

getAll: function (callback) { db.open(function () { var store = db.getObjectStore(), cursor = store.openCursor(), data = []; cursor.onsuccess = function (e) { var result = e.target.result; if (result && result !== null) { data.push(result.value); result.continue(); } else { callback(data); } }; }); },

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
getAll: function (callback) {
 
    db.open(function () {
 
        var
            store = db.getObjectStore(),
            cursor = store.openCursor(),
            data = [];
 
        cursor.onsuccess = function (e) {
 
            var result = e.target.result;
 
            if (result &&
                result !== null) {
 
                data.push(result.value);
                result.continue();
 
            } else {
 
                callback(data);
            }
        };
 
    });
},

Listing 2: Full source for database-specific code (index.db.js)

JavaScript

// index.db.js ; window.indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB; window.IDBTransaction = window.IDBTransaction || window.webkitIDBTransaction || window.msIDBTransaction; window.IDBKeyRange = window.IDBKeyRange || window.webkitIDBKeyRange || window.msIDBKeyRange; (function(window){ 'use strict'; var db = { version: 1, // important: only use whole numbers! objectStoreName: 'tasks', instance: {}, upgrade: function (e) { var _db = e.target.result, names = _db.objectStoreNames, name = db.objectStoreName; if (!names.contains(name)) { _db.createObjectStore( name, { keyPath: 'id', autoIncrement: true }); } }, errorHandler: function (error) { window.alert('error: ' error.target.code); debugger; }, open: function (callback) { var request = window.indexedDB.open( db.objectStoreName, db.version); request.onerror = db.errorHandler; request.onupgradeneeded = db.upgrade; request.onsuccess = function (e) { db.instance = request.result; db.instance.onerror = db.errorHandler; callback(); }; }, getObjectStore: function (mode) { var txn, store; mode = mode || 'readonly'; txn = db.instance.transaction( [db.objectStoreName], mode); store = txn.objectStore( db.objectStoreName); return store; }, save: function (data, callback) { db.open(function () { var store, request, mode = 'readwrite'; store = db.getObjectStore(mode), request = data.id ? store.put(data) : store.add(data); request.onsuccess = callback; }); }, getAll: function (callback) { db.open(function () { var store = db.getObjectStore(), cursor = store.openCursor(), data = []; cursor.onsuccess = function (e) { var result = e.target.result; if (result && result !== null) { data.push(result.value); result.continue(); } else { callback(data); } }; }); }, get: function (id, callback) { id = parseInt(id); db.open(function () { var store = db.getObjectStore(), request = store.get(id); request.onsuccess = function (e){ callback(e.target.result); }; }); }, 'delete': function (id, callback) { id = parseInt(id); db.open(function () { var mode = 'readwrite', store, request; store = db.getObjectStore(mode); request = store.delete(id); request.onsuccess = callback; }); }, deleteAll: function (callback) { db.open(function () { var mode, store, request; mode = 'readwrite'; store = db.getObjectStore(mode); request = store.clear(); request.onsuccess = callback; }); } }; window.app = window.app || {}; window.app.db = db; }(window));

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
// index.db.js
 
;
 
window.indexedDB = window.indexedDB ||
                   window.mozIndexedDB ||
                   window.webkitIndexedDB ||
                   window.msIndexedDB;
 
window.IDBTransaction = window.IDBTransaction ||
                   window.webkitIDBTransaction ||
                   window.msIDBTransaction;
 
window.IDBKeyRange = window.IDBKeyRange ||
                   window.webkitIDBKeyRange ||
                   window.msIDBKeyRange;
 
(function(window){
 
    'use strict';
 
    var db = {
 
        version: 1, // important: only use whole numbers!
 
        objectStoreName: 'tasks',
 
        instance: {},
 
        upgrade: function (e) {
 
            var
                _db = e.target.result,
                names = _db.objectStoreNames,
                name = db.objectStoreName;
 
            if (!names.contains(name)) {
 
                _db.createObjectStore(
                    name,
                    {
                        keyPath: 'id',
                        autoIncrement: true
                    });
            }
        },
 
        errorHandler: function (error) {
            window.alert('error: ' error.target.code);
            debugger;
        },
 
        open: function (callback) {
 
            var request = window.indexedDB.open(
                db.objectStoreName, db.version);
 
            request.onerror = db.errorHandler;
 
            request.onupgradeneeded = db.upgrade;
 
            request.onsuccess = function (e) {
 
                db.instance = request.result;
 
                db.instance.onerror =
                    db.errorHandler;
 
                callback();
            };
        },
 
        getObjectStore: function (mode) {
 
            var txn, store;
 
            mode = mode || 'readonly';
 
            txn = db.instance.transaction(
                [db.objectStoreName], mode);
 
            store = txn.objectStore(
                db.objectStoreName);
 
            return store;
        },
 
        save: function (data, callback) {
 
            db.open(function () {
 
                var store, request,
                    mode = 'readwrite';
 
                store = db.getObjectStore(mode),
 
                request = data.id ?
                    store.put(data) :
                    store.add(data);
 
                request.onsuccess = callback;
            });
        },
 
        getAll: function (callback) {
 
            db.open(function () {
 
                var
                    store = db.getObjectStore(),
                    cursor = store.openCursor(),
                    data = [];
 
                cursor.onsuccess = function (e) {
 
                    var result = e.target.result;
 
                    if (result &&
                        result !== null) {
 
                        data.push(result.value);
                        result.continue();
 
                    } else {
 
                        callback(data);
                    }
                };
 
            });
        },
 
        get: function (id, callback) {
 
            id = parseInt(id);
 
            db.open(function () {
 
                var
                    store = db.getObjectStore(),
                    request = store.get(id);
 
                request.onsuccess = function (e){
                    callback(e.target.result);
                };
            });
        },
 
        'delete': function (id, callback) {
 
            id = parseInt(id);
 
            db.open(function () {
 
                var
                    mode = 'readwrite',
                    store, request;
 
                store = db.getObjectStore(mode);
 
                request = store.delete(id);
 
                request.onsuccess = callback;
            });
        },
 
        deleteAll: function (callback) {
 
            db.open(function () {
 
                var mode, store, request;
 
                mode = 'readwrite';
                store = db.getObjectStore(mode);
                request = store.clear();
 
                request.onsuccess = callback;
            });
 
        }
    };
 
    window.app = window.app || {};
    window.app.db = db;
 
}(window));

Listing 3: Full source for user interface-specific code (index.ui.js)

JavaScript

// index.ui.js ; (function ($, Modernizr, app) { 'use strict'; $(function(){ if(!Modernizr.indexeddb){ $('#unsupported-message').show(); $('#ui-container').hide(); return; } var $deleteAllBtn = $('#delete-all-btn'), $titleText = $('#title-text'), $notesText = $('#notes-text'), $idHidden = $('#id-hidden'), $clearButton = $('#clear-button'), $saveButton = $('#save-button'), $listContainer = $('#list-container'), $noteTemplate = $('#note-template'), $emptyNote = $('#empty-note'); var addNoTasksMessage = function(){ $listContainer.append( $emptyNote.html()); }; var bindData = function (data) { $listContainer.html(''); if(data.length === 0){ addNoTasksMessage(); return; } data.forEach(function (note) { var m = $noteTemplate.html(); m = m.replace(/{ID}/g, note.id); m = m.replace(/{TITLE}/g, note.title); $listContainer.append(m); }); }; var clearUI = function(){ $titleText.val('').focus(); $notesText.val(''); $idHidden.val(''); }; // select individual item $listContainer.on('click', 'a[data-id]', function (e) { var id, current; e.preventDefault(); current = e.currentTarget; id = $(current).attr('data-id'); app.db.get(id, function (note) { $titleText.val(note.title); $notesText.val(note.text); $idHidden.val(note.id); }); return false; }); // delete item $listContainer.on('click', 'i[data-id]', function (e) { var id, current; e.preventDefault(); current = e.currentTarget; id = $(current).attr('data-id'); app.db.delete(id, function(){ app.db.getAll(bindData); clearUI(); }); return false; }); $clearButton.click(function(e){ e.preventDefault(); clearUI(); return false; }); $saveButton.click(function (e) { var title = $titleText.val(); if (title.length === 0) { return; } var note = { title: title, text: $notesText.val() }; var id = $idHidden.val(); if(id !== ''){ note.id = parseInt(id); } app.db.save(note, function(){ app.db.getAll(bindData); clearUI(); }); }); $deleteAllBtn.click(function (e) { e.preventDefault(); app.db.deleteAll(function () { $listContainer.html(''); addNoTasksMessage(); clearUI(); }); return false; }); app.db.errorHandler = function (e) { window.alert('error: ' e.target.code); debugger; }; app.db.getAll(bindData); }); }(jQuery, Modernizr, window.app));

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
// index.ui.js
 
;
 
(function ($, Modernizr, app) {
 
    'use strict';
 
    $(function(){
 
        if(!Modernizr.indexeddb){
            $('#unsupported-message').show();
            $('#ui-container').hide();
            return;
        }
 
        var
          $deleteAllBtn = $('#delete-all-btn'),
          $titleText = $('#title-text'),
          $notesText = $('#notes-text'),
          $idHidden = $('#id-hidden'),
          $clearButton = $('#clear-button'),
          $saveButton = $('#save-button'),
          $listContainer = $('#list-container'),
          $noteTemplate = $('#note-template'),
          $emptyNote = $('#empty-note');
 
        var addNoTasksMessage = function(){
            $listContainer.append(
                $emptyNote.html());
        };
 
        var bindData = function (data) {
 
            $listContainer.html('');
 
            if(data.length === 0){
                addNoTasksMessage();
                return;
            }
 
            data.forEach(function (note) {
              var m = $noteTemplate.html();
              m = m.replace(/{ID}/g, note.id);
              m = m.replace(/{TITLE}/g, note.title);
              $listContainer.append(m);
            });
        };
 
        var clearUI = function(){
            $titleText.val('').focus();
            $notesText.val('');
            $idHidden.val('');
        };
 
        // select individual item
        $listContainer.on('click', 'a[data-id]',
 
            function (e) {
 
                var id, current;
 
                e.preventDefault();
 
                current = e.currentTarget;
                id = $(current).attr('data-id');
 
                app.db.get(id, function (note) {
                    $titleText.val(note.title);
                    $notesText.val(note.text);
                    $idHidden.val(note.id);
                });
 
                return false;
            });
 
        // delete item
        $listContainer.on('click', 'i[data-id]',
 
            function (e) {
 
                var id, current;
 
                e.preventDefault();
 
                current = e.currentTarget;
                id = $(current).attr('data-id');
 
                app.db.delete(id, function(){
                    app.db.getAll(bindData);
                    clearUI();
                });
 
                return false;
        });
 
        $clearButton.click(function(e){
            e.preventDefault();
            clearUI();
            return false;
        });
 
        $saveButton.click(function (e) {
 
            var title = $titleText.val();
 
            if (title.length === 0) {
                return;
            }
 
            var note = {
                title: title,
                text: $notesText.val()
            };
 
            var id = $idHidden.val();
 
            if(id !== ''){
                note.id = parseInt(id);
            }
 
            app.db.save(note, function(){
                app.db.getAll(bindData);
                clearUI();
            });
        });
 
        $deleteAllBtn.click(function (e) {
 
            e.preventDefault();
 
            app.db.deleteAll(function () {
                $listContainer.html('');
                addNoTasksMessage();
                clearUI();
            });
 
            return false;
        });
 
        app.db.errorHandler = function (e) {
            window.alert('error: ' e.target.code);
            debugger;
        };
 
        app.db.getAll(bindData);
 
    });
 
}(jQuery, Modernizr, window.app));

Listing 3: Full HTML source (index.html)

JavaScript

<!doctype html> <html lang="en-US"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>Introduction to IndexedDB</title> <meta name="description" content="Introduction to IndexedDB"> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css"> <link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs /font-awesome/4.1.0/css/font-awesome.min.css" > <link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs /font-awesome/4.1.0/fonts/FontAwesome.otf" > <link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs /font-awesome/4.1.0/fonts/fontawesome-webfont.eot" > <link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs /font-awesome/4.1.0/fonts/fontawesome-webfont.svg" > <link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs /font-awesome/4.1.0/fonts/fontawesome-webfont.ttf" > <link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs /font-awesome/4.1.0/fonts/fontawesome-webfont.woff" > <style> h1 { text-align: center; color:#999; } ul li { font-size: 1.35em; margin-top: 1em; margin-bottom: 1em; } ul li.small { font-style: italic; } footer { margin-top: 25px; border-top: 1px solid #eee; padding-top: 25px; } i[data-id] { cursor: pointer; color: #eee; } i[data-id]:hover { color: #c75a6d; } .push-down { margin-top: 25px; } #save-button { margin-left: 10px; } </style> <script src="//cdnjs.cloudflare.com/ajax/libs/modernizr /2.8.2/modernizr.min.js" ></script> </head> <body class="container"> <h1>Tasks</h1> <div id="unsupported-message" class="alert alert-warning" style="display:none;"> <b>Aww snap!</b> Your browser does not support indexedDB. </div> <div id="ui-container" class="row"> <div class="col-sm-3"> <a href="#" id="delete-all-btn" class="btn-xs"> <i class="fa fa-trash-o"></i> Delete All</a> <hr/> <ul id="list-container" class="list-unstyled"></ul> </div> <div class="col-sm-8 push-down"> <input type="hidden" id="id-hidden" /> <input id="title-text" type="text" class="form-control" tabindex="1" placeholder="title" autofocus /><br /> <textarea id="notes-text" class="form-control" tabindex="2" placeholder="text"></textarea> <div class="pull-right push-down"> <a href="#" id="clear-button" tabindex="4">Clear</a> <button id="save-button" tabindex="3" class="btn btn-default btn-primary"> <i class="fa fa-save"></i> Save</button> </div> </div> </div> <footer class="small text-muted text-center">by <a href="" target="_blank">Craig Shoemaker</a> <a href="" target="_blank"> <i class="fa fa-twitter"></i></a> </footer> <script id="note-template" type="text/template"> <li> <i data-id="{ID}" class="fa fa-minus-circle"></i> <a href="#" data-id="{ID}">{TITLE}</a> </li> </script> <script id="empty-note" type="text/template"> <li class="text-muted small">No tasks</li> </script> <script src="//ajax.googleapis.com/ajax/libs /jquery/1.11.1/jquery.min.js"></script> <script src="index.db.js" type="text/javascript"></script> <script src="index.ui.js" type="text/javascript"></script> </body> </html>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
<!doctype html>
<html lang="en-US">
    <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <title>Introduction to IndexedDB</title>
        <meta name="description"
              content="Introduction to IndexedDB">
        <meta name="viewport"
              content="width=device-width, initial-scale=1">
        <link rel="stylesheet"
              href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css">
        <link rel="stylesheet"
              href="//cdnjs.cloudflare.com/ajax/libs
/font-awesome/4.1.0/css/font-awesome.min.css" >
        <link rel="stylesheet"
              href="//cdnjs.cloudflare.com/ajax/libs
/font-awesome/4.1.0/fonts/FontAwesome.otf" >
        <link rel="stylesheet"
              href="//cdnjs.cloudflare.com/ajax/libs
/font-awesome/4.1.0/fonts/fontawesome-webfont.eot" >
        <link rel="stylesheet"
              href="//cdnjs.cloudflare.com/ajax/libs
/font-awesome/4.1.0/fonts/fontawesome-webfont.svg" >
        <link rel="stylesheet"
              href="//cdnjs.cloudflare.com/ajax/libs
/font-awesome/4.1.0/fonts/fontawesome-webfont.ttf" >
        <link rel="stylesheet"
              href="//cdnjs.cloudflare.com/ajax/libs
/font-awesome/4.1.0/fonts/fontawesome-webfont.woff" >
        <style>
            h1 {
                text-align: center;
                color:#999;
            }
 
            ul li {
                font-size: 1.35em;
                margin-top: 1em;
                margin-bottom: 1em;
            }
 
            ul li.small {
                font-style: italic;
            }
 
            footer {
                margin-top: 25px;
                border-top: 1px solid #eee;
                padding-top: 25px;
            }
 
            i[data-id] {
                cursor: pointer;
                color: #eee;
            }
 
            i[data-id]:hover {
                color: #c75a6d;
            }
 
            .push-down {
                margin-top: 25px;
            }
 
            #save-button {
                margin-left: 10px;
            }
        </style>
        <script src="//cdnjs.cloudflare.com/ajax/libs/modernizr
/2.8.2/modernizr.min.js" ></script>
    </head>
    <body class="container">
        <h1>Tasks</h1>
        <div id="unsupported-message"
             class="alert alert-warning"
             style="display:none;">
            <b>Aww snap!</b> Your browser does not support indexedDB.
        </div>
        <div id="ui-container" class="row">
            <div class="col-sm-3">
 
                <a href="#" id="delete-all-btn" class="btn-xs">
                    <i class="fa fa-trash-o"></i> Delete All</a>
 
                <hr/>
 
                <ul id="list-container" class="list-unstyled"></ul>
 
            </div>
            <div class="col-sm-8 push-down">
 
                <input type="hidden" id="id-hidden" />
 
                <input
                       id="title-text"
                       type="text"
                       class="form-control"
                       tabindex="1"
                       placeholder="title"
                       autofocus /><br />
 
                <textarea
                          id="notes-text"
                          class="form-control"
                          tabindex="2"
                          placeholder="text"></textarea>
 
                <div class="pull-right push-down">
 
                    <a href="#" id="clear-button" tabindex="4">Clear</a>
 
                    <button id="save-button"
                            tabindex="3"
                            class="btn btn-default btn-primary">
                                <i class="fa fa-save"></i> Save</button>
                </div>
            </div>
        </div>
        <footer class="small text-muted text-center">by
            <a href="http://craigshoemaker.net" target="_blank">Craig Shoemaker</a>
            <a href="http://twitter.com/craigshoemaker" target="_blank">
                <i class="fa fa-twitter"></i></a>
        </footer>
        <script id="note-template" type="text/template">
            <li>
                <i data-id="{ID}" class="fa fa-minus-circle"></i>
                <a href="#" data-id="{ID}">{TITLE}</a>
            </li>
        </script>
        <script id="empty-note" type="text/template">
            <li class="text-muted small">No tasks</li>
        </script>
        <script src="//ajax.googleapis.com/ajax/libs
/jquery/1.11.1/jquery.min.js"></script>
        <script src="index.db.js" type="text/javascript"></script>
        <script src="index.ui.js" type="text/javascript"></script>
    </body>
</html>

赞 1 收藏 评论

CSS冗余的解决方案

对前端具有实际推动作用的,我觉得有以下技术:

① jQuery,解决IE时代令人头疼的兼容问题

② 移动浪潮,让HTML5与CSS3流行起来

③ requireJS,模块化加载技术让前端开发能协同作战,也一定限度的避免了命名污染

④ Hybrid,Hybrid技术将前端推向了一个前所未有的高度,这门技术让前端肆无忌惮的侵占着native的份额

如果说接下来会有一门技术会继续推动前端技术发展,有可能是web components,或者出现了新的设备。

web component是前端几项技术的融合,里面有一项功能为shadow dom,shadow dom是一种浏览器行为,他允许在document文档中渲染时插入一个独立的dom子树,但这个dom树与主dom树完全分离的,不会互相影响。以一个组件为例,是这个样子的:

网上十大正规赌博平台 20

一个组件就只有一个div了,这是一件很棒的事情,但实际的支持情况不容乐观:

网上十大正规赌博平台 21

然后web components还会有一些附带的问题:

① css与容器一起出现,而没有在一个文件中,在很多人看来很“奇怪”,我最初也觉得有点怪

② 大规模使用后,用于装载HTML的容器组件如何处理,仍然没有一个很好的方案

③ 对于不支持的情况如何做降级,如何最小化代码

④ 没有大规模使用的案例,至少国内没有很好的验证过

其中shadow dom思想也是解决css重复的一个办法,以一个页面为例,他在原来的结构是这个样子的:

网上十大正规赌博平台 22

JavaScript

main.css view1.js view1.html view2.js view2.css 开发的时候是这个样子: view1.css view1.js view1.html 最终发布是这个样子: view1.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
main.css
 
view1.js
view1.html
 
view2.js
view2.css
 
开发的时候是这个样子:
 
view1.css
view1.js
view1.html
 
最终发布是这个样子:
view1.js

网上十大正规赌博平台 23

这一切归功于requireJS与grunt打包工具,这里给一个实际的例子:

网上十大正规赌博平台 24

这里最终会被打包编译为一个文件:

网上十大正规赌博平台 25

这样的话版本UI升级只与js有关系,requireJS配置即可,这里只是UI的应用,很容易便可以扩展到page view级别,使用得当的话妈妈再也不用关心我们的版本升级以及css冗余了

这里处理降级时,会给css加前缀,如一个组件id为ui,其中的css会编译为 #ui * {} #ui div {} 由于css选择器是由右至左的,这种代码产生的搜索消耗是一个缺点,但是与尺寸的降低比起来便不算什么

1
2
3
4
这里处理降级时,会给css加前缀,如一个组件id为ui,其中的css会编译为
#ui * {}
#ui div {}
由于css选择器是由右至左的,这种代码产生的搜索消耗是一个缺点,但是与尺寸的降低比起来便不算什么

对象仓库

object store是IndexedDB数据库的基础。如果你使用过关系数据库,通常可以将object store等价于一个数据库表。Object stores包括一个或多个索引,在store中按照一对键/值操作,这提供一种快速定位数据的方法。

当你配置一个object store,你必须为store选择一个键。键在store中可以以“in-line”或“out-of-line”的方式存在。in-line键通过在数据对象上引用path来保障它在object store的唯一性。为了说明这一点,想想一个包括电子邮件地址属性Person对象。您可以配置你的store使用in-line键emailAddress,它能保证store(持久化对象中的数据)的唯一性。另外,out-of-line键通过独立于数据的值识别唯一性。在这种情况下,你可以把out-of-line键比作一个整数值,它(整数值)在关系数据库中充当记录的主键。

图1显示了任务数据保存在任务的object store,它使用in-line键。在这个案例中,键对应于对象的ID值。

版本轮替——哪些能删的痛点

数月后,20多个团队悉数切入到最新的框架,另一个令人头疼的问题马上又出来了,虽然大家样式都接入到最新的风格了,但是老的样式哪些能删?哪些不能删又是一个令人头疼的问题。

几个月前维护CSS同事嫌工资低了,换了一个同事维护全站基础css;再过了一段时间,组织架构调整,又换了一个同事维护;再过了一段时间,正在维护css的同事觉得自己级别低了,在公司内部等待晋级确实熬不住,于是也走了。这个基础css俨然变成了一笔烂账,谁也不敢删,谁也不愿意动,动一下错一下。

这个问题表面上看是一个css问题,其实这是一个前端难题,也是过度解耦,拆分机制不正确带来的麻烦。

CSS是前端不可分割的一部分,HTML模板与Javascript可以用requireJS处理,很大程度上解决了javascript变量污染的问题,css一般被一起分离了出来,单独存放。一个main.css包含全站重置的样式,表单、列表、按钮的基础样式,完了就是全站基础的UI组件。

总有业务团队在实际做项目时会不自主的使用main.css中的一些功能,如果只是使用了基础的重置还好,但是一旦真的使用其中通用的表单、列表等便2B了

main.css的初衷当然是将各个业务团队通用的部分提炼出来,事实上也该这样做,但理想很丰满,现实很残酷,不同的人对SEO、对语义化对命名的理解不太一样,换一个人就会换一套东西。第一批项目上线后,过了几个月,开发人员成长非常巨大,对原来的命名结构,完全不削一顾,自己倒腾出一套新的东西,让各个团队换上去,其它团队面对这种要求是及其头疼的,因为各个团队会有自己的CSS团队,这样一搞势必该业务团队的HTML结构与CSS要被翻新一次,这样的意义是什么,便不太明显了。2个星期过去了,新一批“规范化”的结构终于上线了,2个月后所有的业务团队全部接了新的结构,似乎皆大欢喜,但是那个同事被另一个团公司挖过去当前端leader了,于是一大群草泥马正在向业务团队的菊花奔腾过去!这里的建议是:

业务团队不要依赖于框架的任何dom结构与css样式,特别不要将UI组件中的dom结构与样式单独抠出来使用,否则就准备肥皂吧

1
业务团队不要依赖于框架的任何dom结构与css样式,特别不要将UI组件中的dom结构与样式单独抠出来使用,否则就准备肥皂吧

铺设基础

这个例子从实现这样一个模块开始,它负责从数据库读取数据,插入新的对象,更新现有对象,删除单个对象和提供在一个object store删除所有对象的选项。这个例子实现的代码是通用的数据访问代码,您可以在任何object store上使用。

这个模块是通过一个立即执行函数表达式(IIFE)实现,它使用对象字面量来提供结构。下面的代码是模块的摘要,说明了它的基本结构。

JavaScript

(function (window) { 'use strict'; var db = { /* implementation here */ }; window.app = window.app || {}; window.app.db = db; }(window));

1
2
3
4
5
6
7
8
(function (window) {
    'use strict';
    var db = {
        /* implementation here */
    };
    window.app = window.app || {};
    window.app.db = db;
}(window));

用这样的结构,可以使这个应用程序的所有逻辑封装在一个名为app的单对象上。此外,数据库相关的代码在一个叫做db的app子对象上。

这个模块的代码使用IIFE,通过传递window对象来确保模块的适当范围。使用use strict确保这个函数的代码函数是按照(javascript严格模式)严格编译规则。db对象作为与数据库交互的所有函数的主要容器。最后,window对象检查app的实例是否存在,如果存在,模块使用当前实例,如果不存在,则创建一个新对象。一旦app对象成功返回或创建,db对象附加到app对象。

本文的其余部分将代码添加到db对象内(在implementation here会评论),为应用程序提供特定于数据库的逻辑。因此,如你所见本文后面的部分中定义的函数,想想父db对象移动,但所有其他功能都是db对象的成员。完整的数据库模块列表见清单2。

尺寸——慢的根源

兵无定势,水无常形,按照之前所说,我们选取了对我们最优的框架,做出来的网站应该很快,但第一轮需求结束后有第二轮,第二轮需求结束后有第三轮,网站版本会从1.1-X.1,业务的增长以及市场份额的角力带来的是一月一发布,一季一轮替,没有不变的道理。

框架最大的敌人是需求,代码最大的敌人是变更,最开始使用的是自己熟悉的技术,突然一天多出了一些莫名其妙的场景:

① webapp模式很不错,为了快速业务发展,将接入Hybrid技术,并且使用一套代码

② 微信入口已经很火了,为了快速业务发展,将接入微信入口,并且使用一套代码

③ UI组件已经旧了,换一批ios8风格的组件吧

④ 全站样式感觉跟不上潮流了,换一套吧

网站变慢的核心原因是尺寸的膨胀,尺寸优化才是前端优化的最重要命题,①、②场景是不可预知场景,面对这种不可预知场景,会写很多桥接的代码,而这类代码往往最后都会证明是不好的!

框架首次处理未知场景所做的代码,往往不是最优的,如Hybrid、如微信入口

1
框架首次处理未知场景所做的代码,往往不是最优的,如Hybrid、如微信入口

剩下两个场景是可预见的改变,但是此类变更会带来另一个令人头疼的问题,新老版本交替。业务20多个业务团队,不可能一个版本便全部改变,便有个逐步推进的过程。

全站样式替换/对未知场景的代码优化,很多时候为了做到透明,会产生冗余代码,为了做兼容,常常有很长一段时间新老代码共存的现象

1
全站样式替换/对未知场景的代码优化,很多时候为了做到透明,会产生冗余代码,为了做兼容,常常有很长一段时间新老代码共存的现象

于是不可预知造成的尺寸膨胀,经过重构优化,而为了做兼容,居然会造成尺寸进一步的增加

所谓优化不一定马上便有效果,开发人员是否扛得住这种压力,是否有全团队推动的能力会变得比本身技术能力更加重要

1
所谓优化不一定马上便有效果,开发人员是否扛得住这种压力,是否有全团队推动的能力会变得比本身技术能力更加重要

事实上的情况复杂的多,以上只是一厢情愿的以“接口统一”、“透明升级”为前提,但是透明的代价是要在重构代码中做兼容,而兼容又本身是需要重构掉的东西,当兼容产生的代码比优化还多的时候,我们可能就会放弃兼容,而提供一套接口完全不统一的东西;更加真实情况是我们根本不会去做这种对比,便直接将老接口废掉,这个时候造成的影响是“天怒人怨”,但是我们爽了,爽了的代价是单个团队的推动安抚。

这里请参考angularJS升级,新浪微博2.0接口与1.1不兼容问题,这里的微信接口提出,难保一年后不会完全推翻……

所以,尺寸变大的主要原因是因为冗余代码的产生,如何消除冗余代码是一个重点,也是一个难点。

IndexedDB是否适合我的应用程序?

现在最关键的问题:“IndexedDB是否适合我的应用程序?“像往常一样,答案是肯定的:“视情况而定。“首先当你试图在客户端保存数据时,你会考虑HTML5本地存储。本地存储得到广泛浏览器的支持,有非常易于使用的API。简单有其优势,但其劣势是无法支持复杂的搜索策略,存储大量的数据,并提供事务支持。

IndexedDB是一个数据库。所以,当你想为客户端做出决定,考虑你如何在服务端选择一个持久化介质的数据库。你可能会问自己一些问题来帮助决定客户端数据库是否适合您的应用程序,包括:

  • 你的用户通过浏览器访问您的应用程序,(浏览器)支持IndexedDB API吗 ?
  • 你需要存储大量的数据在客户端?
  • 你需要在一个大型的数据集合中快速定位单个数据点?
  • 你的架构在客户端需要事务支持吗?

如果你对其中的任何问题回答了“是的”,很有可能,IndexedDB是你的应用程序的一个很好的候选。

MVC框架选择

MVC框架流行的有Backbone、angularJS、reactJS、canJS等,我个人比较熟悉Backbone与canJS,近期也在整理canJS的一些笔记

首先提一下Backbone,我认为其最优秀的就是其View一块的实现,Backbone的View规范化了dom事件的使用,避免了事件滥用,避免了事件“失效”

但是Backbone的路由处理一块很弱,事实上一点用也没有,而且就算view一块的继承关系也非常难以处理,extend实现是:

JavaScript

var extend = function (protoProps, staticProps) { var parent = this; var child; // The constructor function for the new subclass is either defined by you // (the "constructor" property in your `extend` definition), or defaulted // by us to simply call the parent's constructor. if (protoProps && _.has(protoProps, 'constructor')) { child = protoProps.constructor; } else { child = function () { return parent.apply(this, arguments); }; } // Add static properties to the constructor function, if supplied. _.extend(child, parent, staticProps); // Set the prototype chain to inherit from `parent`, without calling // `parent`'s constructor function. var Surrogate = function () { this.constructor = child; }; Surrogate.prototype = parent.prototype; child.prototype = new Surrogate; // Add prototype properties (instance properties) to the subclass, // if supplied. if (protoProps) _.extend(child.prototype, protoProps); // Set a convenience property in case the parent's prototype is needed // later. child.__super__ = parent.prototype; return child; };

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
var extend = function (protoProps, staticProps) {
  var parent = this;
  var child;
 
  // The constructor function for the new subclass is either defined by you
  // (the "constructor" property in your `extend` definition), or defaulted
  // by us to simply call the parent's constructor.
  if (protoProps && _.has(protoProps, 'constructor')) {
    child = protoProps.constructor;
  } else {
    child = function () { return parent.apply(this, arguments); };
  }
 
  // Add static properties to the constructor function, if supplied.
  _.extend(child, parent, staticProps);
 
  // Set the prototype chain to inherit from `parent`, without calling
  // `parent`'s constructor function.
  var Surrogate = function () { this.constructor = child; };
  Surrogate.prototype = parent.prototype;
  child.prototype = new Surrogate;
 
  // Add prototype properties (instance properties) to the subclass,
  // if supplied.
  if (protoProps) _.extend(child.prototype, protoProps);
 
  // Set a convenience property in case the parent's prototype is needed
  // later.
  child.__super__ = parent.prototype;
 
  return child;
};

JavaScript

child.__super__ = parent.prototype;

1
child.__super__ = parent.prototype;

这是一段极为糟糕的设计,他是将parent原型的指向给到了类的的属性上,这里可以看做静态方法,那么我在实际使用的时候要如何使用呢?

我在内部原型链上或者实例方法一般使用this便能指向本身,但是却不能执行本类的方法,如果要使用指向构造函数我需要这么做:

JavaScript

this.constructor this.constructor.__super__

1
2
this.constructor
this.constructor.__super__

如果我这里想要执行父类的一个方法,还得关注起作用域指向,于是只能这样写

JavaScript

this.constructor.__super__.apply(this, arguments)

1
this.constructor.__super__.apply(this, arguments)

而我总是认为javascript的construct未必非常靠谱,于是整个人都不好了,所以在一轮使用后,基本便放弃Backbone了,但是Backbone优秀的一面也不能抹杀,我们可以借鉴Backbone实现一些更加适合项目的基础架子

Backbone另一个令人诟病的地方是其插件少,其实这里有点苛刻,移动端才兴起不久,webapp的项目又少,这里没有是很正常,别人的插件也未必能用的顺心。

angularJs我本身没有实际使用过,不好评价,根据一些朋友的实际使用情况可以得出一个结论:

JavaScript

规定的非常死,业务代码可保持一致,入门简单深入难,一旦出现问题,不太好改,对技术要求较高

1
规定的非常死,业务代码可保持一致,入门简单深入难,一旦出现问题,不太好改,对技术要求较高

这里各位根据实际情况选择就好,我这里的建议还是自己读懂一个MV*的框架,抽取需要的重写,像angularJS一次升级,之前的项目如何跟着升级,这些问题很头疼也很实际。

上次抱着解决webappSEO难题时候对reactJS有所接触,其源码洋洋洒洒10000行,没有一定功力与时间还是暂时不碰为好。

canJS学习成本与Backbone差不多,我这边准备出系列学习笔记,好不好后面调研再说。

总结一句:不建议直接将业务库框架直接取来使用,更不建议使用过重的业务框架,最好是能明白框架想要解决的问题,与自己项目的实际需求,自己造轮子知根知底。

设计典范

IndexedDB的架构很像在一些流行的服务器端NOSQL数据库实现中的设计典范类型。面向对象数据通过object stores(对象仓库)进行持久化,所有操作基于请求同时在事务范围内执行。事件生命周期使你能够控制数据库的配置,错误通过错误冒泡来使用API管理。

CSS Sprites

CSS Sprites可以有效的减低请求数,偶尔还可以降低请求量,但是随着发展,可能会有以下问题:

① 新增难,特别是css维护工作换人的情况下

② 删除难,这个问题更加明显,1年后,前端风格已经换了两批了,这里要知道哪些图标还在用,哪些没用变得非常困难

③ 调整难,一个图标刚开始是红色,突然需要变成蓝色,这类需求会让这个工作变得不轻松

④ 响应式,这个更会导致指数级的增长,背景图要随着宽度缩放这种需求更加讨厌

这里放一张做的很好的图:

网上十大正规赌博平台 26

由图所示,这里是对尺寸做了一定区分的,但是这里仍然不是最优,其实以上很多图标可以直接由CSS3实现,这里举两个案例:

(svg)

网上十大正规赌博平台 27

(CSS3)

网上十大正规赌博平台 28

这里优劣之分各位自己判断,我反正完全偏向了CSS3……

本文由网上十大正规赌博平台发布于正规赌博十大app排名,转载请注明出处:网上十大正规赌博平台浅谈移动前端的最佳实践

关键词: mg4355登录首页

上一篇:移动端自适应方案,应用要知道的那些事

下一篇:没有了