自学JavaScript第四天- JS 进阶:AJAX Promise Canvas

  • AJAX
    • 使用 XMLHttpRequest
    • 使用 fetch() 方法
    • 处理 AJAX 数据
    • 安全限制
    • 跨域方案 CORS
      • 前端代码
  • Promise 对象
    • 并行执行异步任务
  • Canvas
    • 绘制形状
    • 绘制文本
    • 其他

AJAX

使用 XMLHttpRequest

现代浏览器上写AJAX主要依靠 XMLHttpRequest 对象

// 定义成功的回调函数
function success(text) {var textarea = documnet.getElementById('test-response-text');textarea.value = text;
}
// 定义失败的回调函数
function fail(code) {var textarea = documnet.getElementById('test-response-text');textarea.value = 'Error code:' + code;
}
var request = new XMLHttpRequest();        // 新建 XMLHttpRequest 对象
// 状态发生变化时,函数被回调
// 也可以使用添加事件监听 request.addEventListener('readystatechange', () => {})
request.onreadystatechange = function() {      if (request.readyState === 4) {        // 成功完成// 判断响应结果:if (request.status === 200) {// 成功,通过 responseText 拿到响应文本:return success(request.responseText);} else {// 失败,根据响应码判断失败原因:return fail(request.status);}} else {// HTTP 请求还在继续}
}
// 建立发送的请求,true 表示异步发送
request.open('GET', '/api/categories', true);
// 设置请求头
request.setRequestHeader('Content-Type', 'application/json;charset=utf-8');
// 设置请求体(请求数据)
let data = {'status':'ready'};
// 因为原生 ajax 发送的数据必须是字符串,所以需要序列化
data = JSON.stringify(data);// 发送请求,在此直线需要定义回调函数
request.send(data);

当创建了 XMLHttpRequest 对象后,要先设置 onreadystatechange 的回调函数。在回调函数中,通常我们只需通过 readyState === 4 判断请求是否完成(刚连接上服务器为 1 ,向服务器发送数据是 2,服务器传送数据完成是 4),如果已完成,再根据 status ===200 判断是否是一个成功的响应。

XMLHttpRequest 对象的 open() 方法有3个参数,第一个参数指定是 GET 还是 POST ,第二个参数指定URL地址,第三个参数指定是否使用异步,默认是 true ,所以不用写。如果设置为同步,则浏览器会停止响应,呈现“假死”状态,直到 AJAX 请求完成。

最后调用 send() 方法才真正发送请求。 GET 请求不需要参数, POST 请求需要把body部分以字符串或者 FormData 对象传进去。

<input type='file' id='upload' onchange='upload(this.files[0])'><script>// 自定义 $ 函数,获取 dom 对象function $(domID) {return document.getElementById(domID)}function uploadImg(file) {// 判断文件类型if (file.type.startsWith('image/')) {// 判断文件大小if (file.size <= 1024 * 1024 * 2) {// 设置上传地址let url = "/upload";let request = new XMLHttpRequest();// 创建监听事件request.onload = function (ev) {if (request.status === 200 && request.readyState === 4) {let respText = request.responseText;// 解析为 json 对象let respJson = JSON.parse(respText);console.log(respJson);}};// 创建上传的数据 form 对象let dataform = new FormData();// 添加字段和内容dataform.append('photo', file);// 建立请求request.open('post', url, true);// 发送请求request.send(dataform)} else {// 文件大小超过限制alert('上传的图片大小在2M内');}} else {// 文件类型超过限制alert('只能上传图片文件');}}</script>

使用 fetch() 方法

fetch() 是 XMLHttpRequest 的升级版,用于在 JavaScript 脚本里面发出 HTTP 请求。fetch() 的功能与 XMLHttpRequest 基本相同,但有三个主要的差异

  • fetch() 使用 Promise,不用回调函数,简化了写法
  • 采用模块化设计,API分散在多个对象上(Response、Request、Headers),使用更合理
  • fetch() 通过数据流(Stream对象)处理数据,可以分块读取,有利于提高性能,减少内存占用,适用于大文件或网速慢的场景
function login() {// 设置请求参数let option = {method: 'post',        // 请求方法body: JSON.stringify({      // 请求体数据'name': '张三','pwd': '123456'}),headers: { 'Content-Type': 'application/json'},       // 请求头数据mode: 'cors'      // 请求模式,使用 cors 跨域(只在跨域时使用)};// 参数1是请求地址,参数2是请求参数fetch('http://text.io/login', option).then(response=>response.json()) // 执行的是 resolve,即成功后的处理方法.catch(function (){// 执行的 reject ,即失败的处理方法})
}

处理 AJAX 数据

当 AJAX 拿到响应后,可以使用 request.responseText 获取响应文本。如今大多数数据都是基于 JSON 格式的,所以要使用时也需要将响应文本解析成 JSON 对象。

let json = JSON.parse(request.responseText)     // 将响应文本解析成为 JSON 对象

相应的,发送数据时,也需要将数据对象序列化为 JSON 对象才能发送

let json = JSON.stringify(obj);

安全限制

默认情况下,JavaScript在发送AJAX请求时,URL的域名必须和当前页面完全一致。如果使用别的域名,则会报错,这是因为浏览器的同源策略导致的。完全一致的意思是,域名要相同( www.example.com 和 example.com 不同),协议要相同( http 和 https 不同),端口号要相同(默认是 :80 端口,它和 :8080 就不同)。有的浏览器口子松一点,允许端口不同,大多数浏览器都会严格遵守这个限制。

使用 js 请求外域的URL,主要有四种方法

  • 通过 flash 插件,现在已经被抛弃
  • 通过同源域名下的代理服务器转发
  • 使用 JSONP,但是有个限制,只能使用 GET 请求,并且要求返回 JavaScript。
  • 如果浏览器支持 HTML5 ,可以使用新的跨域策略 CORS,这也是现在推荐使用的方式。

注:由于同源的安全策略是浏览器限制,所以请求的发送和响应是可以进行的,只是浏览器不接受罢了。另此策略不是对所有请求均制约,对 XmlHttpRequest 是制约的,对 img、iframe、script 等有 src 属性的标签不进行制约(所以这些标签可以跨域使用)。

跨域方案 CORS

CORS全称Cross-Origin Resource Sharing,是HTML5规范定义的如何跨域访问资源。

Origin表示本域,也就是浏览器当前页面的域。当JavaScript向外域发起请求后,浏览器收到响应后,首先检查 Access-Control-Allow-Origin 是否包含本域,如果是,则此次跨域请求成功,如果不是,则请求失败,JavaScript将无法获取到响应的任何数据。

可见,跨域能否成功,取决于对方服务器是否愿意给你设置一个正确的 Access-Control-Allow-Origin ,决定权始终在对方手中。

上面这种跨域请求,称之为“简单请求”。简单请求包括GET、HEAD和POST(POST的Content-Type类型 仅限 application/x-www-form-urlencoded 、 multipart/form-data 和 text/plain ),并且不能出现任何自定义头(例如, X-Custom: 12345 ),通常能满足90%的需求。

非简单请求的跨域请求,需要进行预检。PUT、DELETE以及其他类型如 application/json 的POST请求,在发送AJAX请求之前,浏览器会先发送一个 OPTIONS 请求(称为preflighted请求)到这个URL上,询问目标服务器是否接受,服务器必须响应并明确指出允许的Method,浏览器确认服务器响应的 Access-Control-Allow-Methods 头确实包含将要发送的AJAX请求的Method,才会继续发送AJAX,否则,抛出一个错误。除此外,还包括检查是否符合 Access-Control-Allow-Headers 等设置的要求。

由于以 POST 、 PUT 方式传送JSON格式的数据在REST中很常见,所以要跨域正确处理 POST 和 PUT 请求,服务器端必须正确响应 OPTIONS 请求。

前端代码

前端使用 XMLHttpRequest 或 jQuery 没有什么变化,正常发送 ajax 请求即可。对于简单请求后端会添加 Access-Control-Allow-Origin ,然后返回数据,浏览器检查 Access-Control-Allow-Origin 并决定是否接受此响应。对于非简单请求会自动发送 options 请求进行预检,预检完成后,发送负载请求。即所有工作在后端和浏览器进行。另外如果使用 fetch() 方法发送 ajax 请求,则注意需要在请求参数中添加 mode:'cors',表示使用 CORS 跨域请求。

Promise 对象

在JavaScript的世界中,所有代码都是单线程执行的。由于这个“缺陷”,导致JavaScript的所有网络操作,浏览器事件,都必须是异步执行。异步执行可以用回调函数实现。异步操作会在将来某个时间触发一个函数调用(回调),但是正常的写法(例如AJAX)很不好看,而且不利于代码复用,于是就出现了 Promise 对象。

Promise 会“承诺将来会执行”回调函数,Promise有各种开源实现,在ES6中被统一规范,由浏览器直接支持。例如 AJAX 可以写成

// ajax 函数将返回 Promise 对象:
function ajax(method, url, data) {var request = new XMLHttpRequest();return new Promise(function (resolve, reject) {request.onreadystatechange = function() {if (request.readyState === 4) {if (request.status === 200) {resolve(request.responseText);} else {reject(request.status);}}};request.open(method, url);request.send(data);});
}
// 调用 ajax
var log = documnet.getElementById('test-promise-ajax-result');
var p = ajax('GET', '/api/categories');
p.then(function (text) {     // 如果 ajax 成功,获得响应内容log.innerText = text;
}).catch(function (status) {   // 如果 ajax 失败,获得响应代码log.innerText = 'ERROR:' + status;
});

即,将一个异步函数作为参数构建 Promise 对象,此函数有两个参数 resolve 和 reject,分别表示调用 成功 / 失败的回调。那么 Promise 对象就可以通过 then 和 catch 方法调用相应的回调函数。

并行执行异步任务

可以同时执行多个异步任务,并等待其执行结果。使用 Pormise.all() 方法,并将需要执行的 promise 任务对象放在数组中传入即可。

// 同时执行 p1 和 p2, 并在它们都完成后执行 then:
Promise.all([p1, p2]).then(function (results) {console.log(results);       // 获得一个各任务结果组成的 Array
});

有时候,多个异步任务是为了容错。比如,同时向两个URL读取用户的个人信息,只需要获得先返回的结果即可。这种情况下,用 Promise.race() 方法实现,其他后返回的结果会被丢弃。

Canvas

Canvas是HTML5新增的组件,它就像一块幕布,可以用JavaScript在上面绘制各种图表、动画等。没有Canvas的年代,绘图只能借助Flash插件实现,页面不得不用JavaScript和Flash进行交互。有了Canvas,我们就再也不需要Flash了,直接使用JavaScript完成绘制。

一个Canvas定义了一个指定尺寸的矩形框,在这个范围内我们可以随意绘制:

 <canvas id="test-canvas" width="300" height="200"></canvas>

由于浏览器对HTML5标准支持不一致,所以,通常在 <canvas> 内部添加一些说明性HTML代码,如果浏览器支持Canvas,它将忽略 <canvas> 内部的HTML,如果浏览器不支持Canvas,它将显示 <canvas> 内部的HTML:

<!-- HTML代码 -->
<canvas id="test-canvas" width="200" heigth="100"><p>你的浏览器不支持Canvas</p>
</canvas>

getContext('2d') 方法让我们拿到一个 CanvasRenderingContext2D 对象,所有的绘图操作都需要通过这个对象完成。

var ctx = canvas.getContext('2d');

如果需要绘制3D怎么办?HTML5还有一个WebGL规范,允许在Canvas中绘制3D图形:

gl = canvas.getContext("webgl");

绘制形状

Canvas的坐标以左上角为原点,水平向右为X轴,垂直向下为Y轴,以像素为单位,所以每个点都是非负整数。CanvasRenderingContext2D 对象有若干方法来绘制图形:

varcanvas = documnet.getElementById('test-canvas'),ctx = canvas.getContext('2d');
// 绘制形状
ctx.clearRect(0, 0, 200, 200);     // 擦除(0,0)位置大小为200x200的矩形,擦除的意思是把该区域变透明
ctx.fillStyle = '#dddddd';      // 设置颜色
ctx.fillRect(10, 10, 130, 130);        // 把(10,10)位置大小为130x130的矩形涂色
// 利用Path绘制复杂路径
var path = new Path2D();
// 绘制圆弧路径,圆心坐标为(75,75),半径为50,圆弧起点是0,终点的弧度是2倍的PI,逆时针绘画
path.arc(75, 75, 50, 0, Math.PI*2, true);
path.moveTo(110,75);
path.arc(75, 75, 35, 0, Math.PI, false);
ctx.strokeStyle = '#0000ff';        // 设置画笔颜色
ctx.stroke(path);       // 沿预设的路径绘画

绘制文本

绘制文本就是在指定的位置输出文本,可以设置文本的字体、样式、阴影等,与CSS完全一致

varcanvas = documnet.getElementById('test-canvas'),ctx = canvas.getContext('2d');
// 绘制文本
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.shadowOffsetX = 2;
ctx.shadowOffsetY = 2;
ctx.shadowBlur = 2;
ctx.shadowColor = '#666666';
ctx.font = '24px Arial';
ctx.fillStyle = '#333333';
ctx.fillText('带阴影的文字', 20, 40);

其他

Canvas除了能绘制基本的形状和文本,还可以实现动画、缩放、各种滤镜和像素转换等高级操作。如果要实现非常复杂的操作,考虑以下优化方案:

  • 通过创建一个不可见的Canvas来绘图,然后将最终绘制结果复制到页面的可见Canvas中;
  • 尽量使用整数坐标而不是浮点数;
  • 可以创建多个重叠的Canvas绘制不同的层,而不是在一个Canvas中绘制非常复杂的图;
  • 背景图片如果不变可以直接用 <img> 标签并放到最底层;

详细的可以查看文档

Canvas API 文档

自学JavaScript第四天- JS 进阶:AJAX Promise Canvas相关推荐

  1. js进阶ajax函数封装(匿名函数作为参数传递)(封装函数引入文件的方式非常好用)...

    js进阶ajax函数封装(匿名函数作为参数传递)(封装函数引入文件的方式非常好用) 一.总结 2.匿名函数作为参数传递 二.js进阶ajax函数封装 ajax1.js 1 function ajax( ...

  2. 在JavaScript中使用json.js:Ajax项目之POST请求(异步)

    经常在百度搜索框输入一部分关键词后,弹出候选关键热词.现在我们就用Ajax技术来实现这一功能. 一.下载json.js文件 百度搜一下,最好到json官网下载,安全起见. 并与新建的两个文件部署如图 ...

  3. [ JS 进阶 ] Ajax 详解 (2) :XHR 实例 GET 和 POST 异步和同步

    上一篇文章 我们大概知道了XHR对象是什么东东,也都了解了它的一些属性和方法,那么现在具体来实现一下Ajax技术 和 了解下XHR2对象. 1.实现Ajax 先来创建个XHR对象的实例: var xh ...

  4. 自学JavaScript第二天- JS 进阶: 对象 函数

    自学JavaScript第二天- JS 进阶: 对象 函数 对象进阶 构造函数 使用类 类的继承 静态方法 函数进阶 方法 装饰器 高阶函数 map / reduce filter sort ever ...

  5. js进阶 14-6 $.ajax()方法如何使用

    js进阶 14-6 $.ajax()方法如何使用 一.总结 一句话总结:$.ajax([settings])settings可选.用于配置Ajax请求的键值对集合. 1.$.ajax()的特点是什么( ...

  6. 自学JavaScript第一天- JS 基础

    自学JavaScript第一天- JS 基础 JS 写在哪里 注释 行内 js 内部 js 外部 js JS 基础语法 语句 大小写 代码块 折行 变量 声明 var .let.const 及作用域 ...

  7. 前端点滴(JS进阶)(三)---- JavaScript 两链一包

    前端点滴(JS进阶)(三)----倾尽所有 一.作用域链 1. 作用域的概念 2. 作用域链 二.面向对象编程 1. 知识回顾 2. 定义对象 (1)new 内置对象 (2)直接量语法 (3)Es5 ...

  8. html引入原生js,html怎么引用JavaScript?html引入js路径四种方式

    html是静态的页面,如果我们想要实现某些动画效果,就要引入一些js,那么,如何在html中引用一些js文件呢,引入js有哪些方式呢,下面我们来总结一下html引入js路径四种方式. 一:html怎么 ...

  9. ajax总结(四):原生js封装a和jQurey版ajax介绍

    一.为什么要封装? 发现很多地方都要用ajax请求,但是大部分代码都是一样的,所以根据封装的思想,相同的代码封装成函数,在需要用的地方来调用,这样会很方便. 二.js封装ajax过程: 1.先了解结构 ...

最新文章

  1. 32位md5解密_用户名与密码前后加密、后台解密实现方案
  2. [Asp.net MVC]Asp.net MVC5系列——第一个项目
  3. 免输入用户名和密码以及git clone以后想提交代码
  4. linux c 符号表,C中的符号表
  5. html编辑器linux,HTML 编辑器
  6. Ubuntu之Docker安装
  7. Python IDLE换行写,一行写不下,如何换行继续写
  8. 以太坊2.0抵押地址新增13.47万ETH
  9. ThinkPHP5最新URL访问:PATH_INFO和兼容模式
  10. 【Python】Centos + gunicorn+flask 报错ImportError: No module named request
  11. 2015软件测试视频教程
  12. KNN——简单手写体识别
  13. java对接支付宝三方登录
  14. [LeetCode]347. 前 K 个高频元素
  15. 学计算机轻松的表情包,轻松的表情包
  16. 配置树莓派中文环境 及解决 no write since last change
  17. RK3399 Android7.1 ACOVP导致系统死机
  18. 5个超好用的自媒体网站,免费用
  19. 线上事故的善后——事故通告
  20. Exynos4412 移植针对Samsung的Linux-6.1(六)【已解决】SROMC寄存器的数值不正确、无法赋值的问题

热门文章

  1. Xmind使用小技巧
  2. js 打印helloworld
  3. 【松勤软件测试】作为一名软件测试工程师,如何编写优秀的测试用例
  4. mysql查询表中最后一条记录
  5. 尚志一中2021年高考成绩查询,2021-2022学年黑龙江省哈尔滨市呼兰一中、阿城二中、宾县三中、尚志五中联考高二(上)期中英语试卷(完美版).pdf...
  6. 基于bert的股票用户评论的情感分析项目实战+数据+代码可直运行
  7. Pyhon在振动信号处理中的高级应用(十一):监督式特征的选择方法(F_Score、Chi-square Score、mRMR)
  8. python开发cs软件_开发cs软件 c python
  9. 酷我音乐显示服务器怎么办,酷我音乐歌词服务与其他功能的使用方法是什么?...
  10. 导入模板制作 esaypoi 多sheet页导出