HTTP超文本传输协议。

TCP 三次握手。

1、客户端 发syn询问 给 服务器
2、服务器 回应询问syn,并发送一个指令ack 客户端
3、客户端 回 ack 服务器。 正式连接成功。 开始客户端请求Http报文 ,服务器响应报文。

三次握手

四次挥手过程

四次挥手

发送一个请求查询用户日志记录

请求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
:method: POST
:scheme: https
:authority: vendor-api-prod.gaoying.com
:path: /customer/logs/

Accept: application/json, text/plain, */*
Content-Type: application/x-www-form-urlencoded //表单提交方式
Origin: http://h5-vendor-dev.gaoying.com
Content-Length: 236
Accept-Language: zh-cn
Host: vendor-api-dev.gaoying.com
User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 13_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148
Referer: http://h5-vendor-dev.gaoying.com/page/customerMain.html?customerId=172821&userId=15243265&token=41cadcb70eed35378a92379871a51181
Accept-Encoding: gzip, deflate, br
Connection: keep-alive

响应

1
2
3
4
5
status: 200
Access-Control-Allow-Origin: *
Content-Type: application/json;charset=UTF-8 //json响应格式
Date: Fri, 27 Dec 2019 05:58:18 GMT
Server: Tengine

请求方式有哪些?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
METHODS: [
'ACL', 'BIND', 'CHECKOUT',
'CONNECT', 'COPY', 'DELETE',
'GET', 'HEAD', 'LINK',
'LOCK', 'M-SEARCH', 'MERGE',
'MKACTIVITY', 'MKCALENDAR', 'MKCOL',
'MOVE', 'NOTIFY', 'OPTIONS',
'PATCH', 'POST', 'PROPFIND',
'PROPPATCH', 'PURGE', 'PUT',
'REBIND', 'REPORT', 'SEARCH',
'SOURCE', 'SUBSCRIBE', 'TRACE',
'UNBIND', 'UNLINK', 'UNLOCK',
'UNSUBSCRIBE'
]

响应码有哪些?

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
STATUS_CODES: {
'100': 'Continue',
'101': 'Switching Protocols',
'102': 'Processing',
'103': 'Early Hints',
'200': 'OK',
'201': 'Created',
'202': 'Accepted',
'203': 'Non-Authoritative Information',
'204': 'No Content',
'205': 'Reset Content',
'206': 'Partial Content',
'207': 'Multi-Status',
'208': 'Already Reported',
'226': 'IM Used',
'300': 'Multiple Choices',
'301': 'Moved Permanently',
'302': 'Found',
'303': 'See Other',
'304': 'Not Modified',
'305': 'Use Proxy',
'307': 'Temporary Redirect',
'308': 'Permanent Redirect',
'400': 'Bad Request',
'401': 'Unauthorized',
'402': 'Payment Required',
'403': 'Forbidden',
'404': 'Not Found',
'405': 'Method Not Allowed',
'406': 'Not Acceptable',
'407': 'Proxy Authentication Required',
'408': 'Request Timeout',
'409': 'Conflict',
'410': 'Gone',
'411': 'Length Required',
'412': 'Precondition Failed',
'413': 'Payload Too Large',
'414': 'URI Too Long',
'415': 'Unsupported Media Type',
'416': 'Range Not Satisfiable',
'417': 'Expectation Failed',
'418': "I'm a Teapot",
'421': 'Misdirected Request',
'422': 'Unprocessable Entity',
'423': 'Locked',
'424': 'Failed Dependency',
'425': 'Unordered Collection',
'426': 'Upgrade Required',
'428': 'Precondition Required',
'429': 'Too Many Requests',
'431': 'Request Header Fields Too Large',
'451': 'Unavailable For Legal Reasons',
'500': 'Internal Server Error',
'501': 'Not Implemented',
'502': 'Bad Gateway',
'503': 'Service Unavailable',
'504': 'Gateway Timeout',
'505': 'HTTP Version Not Supported',
'506': 'Variant Also Negotiates',
'507': 'Insufficient Storage',
'508': 'Loop Detected',
'509': 'Bandwidth Limit Exceeded',
'510': 'Not Extended',
'511': 'Network Authentication Required'
}

客户端请求后,服务器响应携带的https证书

HTTPS:
在HTTP与TCP之间 加入SSL层,一个加密/身份验证层,用于安全的HTTP数据传输。 它是一个URI scheme(抽象标识符体系)。
SSL : 安全套接层 , 在网络传输层基础上对网络连接进行数据加密; 具有校验机制,配备身份证书。
客户端利用公钥对数据加密,服务端收到加密数据后 对数据进行解密;

https证书

Https 服务器端口是443, Https 公共密钥采用 RSA算法加密, 证书信息将在请求响应之前回复给客户端。客户端根据证书信息 本地做一个安全校验,通过后再允许服务端响应返回数据,否则取消本次请求响应。
签名算法:SHA-256 ECDSA

常用加密算法

对称加密
  • 单向散列函数
    MD5 是一种 不可逆的信息摘要算法,严格来说不是加密。
    单向散列函数是一种不可逆的信息摘要算法,无法通过密文还原成明文(注意:是算法上不能实现),对于简单的 MD5 加密(这里的简单是指:明文长度短,且字符单一,并且进行 HASH 操作的时候没有进行加盐处理),是可以通过一些在线工具网站解密出来,实则是利用了彩虹表的技术。
  • AES 加密
    加密原理: 字节代换,行位移,列混合:(矩阵相乘:状态矩阵 和 固定矩阵相乘) ,轮钥加密:轮钥加密是将128位轮密钥Ki同状态矩阵中的数据进行逐位异或操作
  • DES 加密
非对称加密
  • RSA 加密
    https加密流程

快速搭建Vue 普通h5项目页面的网络请求

在学习请求之前,需要掌握好Javascript语言。最基础的知识首先要了解和掌握变量修饰符,Function 和 Object对象。

js 变量修饰符

let 是局部变量,非常适合循环的时候;
const 是只读变量, 初始化的时候就要赋值;只能赋值一次;
var 是全局变量,全局都可以用。

函数 Function

javascript_function
从上图我们看到有个名为open的函数,包含参数,调用者,长度,名字,以及 Proto 对象。该对象里面有 apply ,bind, call 函数。还有一个contructor构造器。

  • Function属性: length,name,prototype
  • Function方法:

    • Function.prototype.apply(), //调用一个函数并将其设置为提供的值。 可以将参数作为Array对象传递
    • Function.prototype.bind(), //创建一个新函数,该新函数在调用时将其设置为提供的值,并在调用新函数时在提供的任何参数之前添加给定的参数序列。
    • Function.prototype.call(), //调用一个函数并将其设置为提供的值。 可以按原样传递参数。
    • Function.prototype.toString() //返回表示函数源代码的字符串。

    //题外话,apply 和 bind 增强代码的扩展性,类似iOS 的category;
    ①动态改变this的指向,也就是this从callObject动态切换为thisArg。
    ②callObject.method.apply(thisArg,thisArray),可以将thisArray转化为arguments,传入到callObject.method内部。
    我们通过以下代码来测试bind 的用法:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    //程序员的黄金年龄 18 ~ 35
    var checkProgramerAgeRange = function (value) {
    if (typeof value !== 'number')
    return false;
    else
    return value >= this.minimum && value <= this.maximum;
    }

    let programmerRange = { minimum: 18, maximum: 35 };

    // 把函数内部的this修改为range对象,
    let boundCheckProgramerAgeRange = checkProgramerAgeRange.bind(programmerRange);
    console.log(boundCheckProgramerAgeRange(12));

Object 对象

Object 继承Function, 扩展了自己的许多方法。

Object 的属性构造器 Object.prototype
Object.assign()   将所有可枚举的自身属性的值从一个或多个源对象复制到目标对象。
Object.create()  用指定的原型对象和属性创建一个新对象。
Object.defineProperty()  定义一个property给指定对象
Object.defineProperties() 定义一个属性 给指定对象
Object.entries() 返回一个装有map的数组。
Object.getOwnPropertyNames() 得到这个对象自己的属性名
Object.getPrototypeOf() 得到指定类型的属性
Object.is() 比较对象
Object.keys() 字符窜属性
Object.values()
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
let stuObj = {room:"english"};
Object.defineProperties(stuObj,{
stuName:{
value:"qiugaoying",
writable:true

/*
configurable, enumerable,
value, writable, get, set
*/
},
age: {
value:18,
writable:true
}
})

//可直接覆盖某对象的属性;
Object.defineProperty(stuObj,"stuName",{
value:"gaogao",
writable: true
})

stuObj.age = 20;
console.log(stuObj.stuName); //gaogao
console.log(stuObj.age); //20
console.log(Object.keys(stuObj)); //room 取得公共属性
console.log(Object.values(stuObj)); //room 取得公共属性值
console.log(Object.getOwnPropertyNames(stuObj)); //room,age,stuName 取得对象所有属性
console.log(Object.getOwnPropertyDescriptors(stuObj)); //获取属性描述器
console.log(stuObj["stuName"]); //gaogao

对象的定义

传统方式定义一个对象
1
2
3
4
5
6
7
8
9
10
function Person(name,age){
this.name = name;
this.age = age;
}

//通过函数的prototype公共空间存储方法
Person.prototype.introduce = function(){
console.log("hello,my name is "+this.name);
}
new Person().introduce();
Class方式定义一个对象
1
2
3
4
5
6
7
8
9
10
11
class Person{

constructor(name,age){
this.name = name;
this.age = age;
}

introduce(){
console.log("我的名字是:"+this.name);
}
}

类的本质是对象,对象继承Function; 所以introduce方法还是存储在prototype 公共空间中。操作类函数也可通过Person.prototype 去动态新增新的方法。另外要注意一点类的所有实列共享原型的prototype。

1
2
3
let student = new Person("qiugaoying",18);
let student2 = new Person("lihua",22);
console.log(student.__proto__ == student2.__proto__); //结果为true。 类的所有实列共享原型的prototype

Ajax 请求

通常,发起一个请求,需要传入基础参数,业务url, 业务参数,设置请求头,和响应方式。 接着对响应成功和失败的处理;
前端通常的请求方式有 表单提交,Ajax异步请求,Jquery也提供了ajax封装的快捷api。另外也有较新的框架Axios。

ajax 的出现是解决局部数据刷新的问题。可通过后台异步调用接口 实现页面局部刷新。浏览器原生有个XMLHttpRequest对象。
xmlHttpRequest
jQuery 出来之后,ajax的调用简化了好多

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ajax({
type: 'POST',
dataType: 'json', //服务端返回的数据类型
contentType: 'application/json' ,//参数类型
url : "http://127.0.0.1/log/list/",
headers:{'Content-Type':'application/json'},//请求头, 内容编码类型 默认 "application/x-www-form-urlencoded"
data: JSON.stringify({"name":"qiugaoying"}), //传参
success: function(result,status,xhr){
console.log(result); //获取结果
},
error: function (xhr,status,error){
console.log(error);
}
});

Axios 框架

Axios 是一个基于 promise 的 HTTP 库,可以用在浏览器和 node.js 中. 我们来观察一下 在axios 的api 方法。
axios

  • 从图可以看出,有个默认属性中有个xmlHttpRequet网络适配器。
  • 可设置基础baseURL , 以及请求头,请求超时等一些基础设置。
  • 转换请求数据和响应数据,支持防御 XSRF。
  • 提供了常用的get,post 等请求。自动转换 JSON 数据。还提供请求和响应的拦截器。
script标签引入axios 的js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
//构建一个axios实列只读变量,设置基础url
const http = axios.create({
baseURL: 'https://vendor-api-prod.qiugaoying.com',
timeout: 1000,
transformRequest: [function(data) {
// Do whatever you want to transform the data
let ret = ''
for (let it in data) {
ret += encodeURIComponent(it) + '=' + encodeURIComponent(data[it]) + '&'
}
return ret
}]
});
封装基础参数,排序 加密等

对请求的基础参数做一个封装。 大部分请求都需要带上一些基础参数和userId,token 等字段。最后对所有字段key排序,最后转成Json 字符串再加密。以下为示例:

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
const netManager = {
getRequestParams: function(params) {
let userId = localStorage.getItem("userId");
let token = localStorage.getItem("token");

// var userInfo = JSON.parse(userInfoStr);
var obj = Object.assign(params, {
api_version: 1, //接口版本
platform: 2, //平台类型
terminal: 1, //终端类型
nonce: (Math.random().toFixed(8) + '').replace('0.', ''), //随机数;
timestamp: new Date().getTime(), //当前时间戳 毫秒
user_id:123, //userId
token: "ec29c3af0dd998yjhgd22f9y867ws6512", //token
version_code: 100 //当前版本号
});
let sign = MD5(this.strKeySort(obj)).toUpperCase(); //md5加密
obj["Sign"] = sign;
return obj;
},

//key排序
strKeySort: function(obj) {
const newkey = Object.keys(obj).sort();
const newArr = [];
for (let i = 0; i < newkey.length; i++) {
if (obj[newkey[i]] === 'undefined' || obj[newkey[i]] === 'null') {
obj[newkey[i]] = '';
}
newArr.push(`${newkey[i]}=${obj[newkey[i]]}`)
}
const newStr = newArr.join('&') + '&secret_key=gaoyingAESKey';
return newStr;
},
//获取url 后面带的参数;
getUrlKey: function (name) {
return decodeURIComponent((new RegExp('[?|&]' + name + '=' + '([^&;]+?)(&|#|;|$)').exec(location.href) || [, ""])[1].replace(/\+/g, '%20')) || null
}
}
axios实列发起一个请求示例

这里拿请求用户个人主页基础信息为样例,把下面方法放入 Vue 的 methods 中。 加载loading用到的是weiui.js.
以下可以实现一个分页加载的请求

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
var vm = new Vue({
el: '#PageContainer1',
data: {
//分页数据
pageNumber: 1,
pageSize: 10,
gyPageListStart: 0,
hasMoreDataFlag: false,
dataSource: [],
},
methods: {
execBaseCustomerRequest: function(url, listKey) {
var loading = null;
if (this.pageNumber == 1) {
loading = weui.loading('加载中');
}
let pageVue = this; //this在不同的作用域中指向的对象不同;临时存储vue对象
if (this.pageNumber == 1) {
this.gyPageListStart = 0;
}

http.post(url, netManager.getRequestParams({
customer_id: this.customerId,
start: this.gyPageListStart,
size: 10,
})).then(function(response) {
if (loading) {
loading.hide();
}

let dataDic = response.data;

if (dataDic.status == 1) {

var arr = pageVue.dataSource;
if (pageVue.pageNumber == 1) {
arr = [];
}

var dataArr = dataDic.data[listKey];
arr = arr.concat(dataArr);

pageVue.gyPageListStart = dataDic.data.start;
vm.$data.hasMoreDataFlag = (dataDic.data.more == 1);
vm.$data.dataSource = arr;
}
setTimeout(function() {
pageVue.requestLoadingFinished = true;
}, 800);
//重置加载完成的状态;

}).catch(function(error) {
console.log(error);
});
}
}
});

其他: url 通常会放到一个全局文件中配置
1
2
3
4
5
6
7
const gaoyingAPI = {
customerList: "/customer/list/",
customerLogs: "/customer/logs/",
customerCoupons: "/customer/coupons/",
customerOrders: "/customer/orders/",
customerDetail: "/customer/detail/"
}

Promise 用法简要讲解

从上文中,我们看到http发送post 方法请求之后,通过promise语法 then的回调监听响应结果,通过catch方法 来捕捉异常错误处理。ES6 就支持了Promise的用法。
Promise中文为承诺。有三种状态:pending(进行中)、fulfilled(已成功)、rejected(已失败)。 即状态一旦更改就不能再次改变赋值。这也是承诺的意义。
Promise实列通过.then方法或者.catch方法 返回一个新的Promise实例来实现链式调用。.then方法可以返回普通值或者一个新的promise。不设置返回值时,默认返回null。
Promise有很多用法,但什么情况下需要用到Promise呢? 在需要处理异步耗时任务的时候,可用Promise包装后进行返回。Promise用的时候会遇到很多种情况,Promise也提供了对应的解决方法。

  • 当Promise.then方法返回一个新的Promise时候,下一级的then 将在 新的Promise then 执行 之后执行。
    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
    //Promise then 嵌套 方式一
    new Promise(resolve => {
    console.log("step1:异步请求处理耗时任务");
    setTimeout(() => {
    resolve("qiu");
    }, 1000);
    }).then(value => {
    //第一个接口请求成功拿到qiu
    console.log("step1-result:" + value);
    return new Promise(resolve => {
    console.log("step2-1:异步请求处理耗时任务2");
    setTimeout(() => {
    resolve("qiugao");
    }, 1000);
    //触发第二个接口请求
    }).then(value => {
    //第二个接口请求成功,拿到id2 做第一件事情。
    console.log("step2-1-result-event1:" + value);
    return "qiugaoying";

    }).then(value => {
    //第二个接口请求成功,拿到id2 做第二件事情。
    console.log("step2-1-result-event2:" + value);
    return "hello, my name is qiugaoying";
    });
    }).then(value => {
    //最后全部请求完成,触发汇总刷新。
    console.log("step1-result:" + value);
    });

    /*
    console输出结果:

    step1:异步请求处理耗时任务
    step1-result:qiu
    step2-1:异步请求处理耗时任务2
    step2-1-result-event1:qiugao
    step2-1-result-event2:qiugaoying
    step1-result:hello, my name is qiugaoying
    */

    //Promise then 嵌套,方式二: 把then移到同一级展开。执行打印结果是一样的。
    new Promise(resolve => {
    console.log("step1:异步请求处理耗时任务");
    setTimeout(() => {
    resolve("qiu");
    }, 1000);
    }).then(value => {
    //第一个接口请求成功拿到qiu
    console.log("step1-result:" + value);
    return new Promise(resolve => {
    console.log("step2-1:异步请求处理耗时任务");
    setTimeout(() => {
    resolve("qiugao");
    }, 1000);
    //触发第二个接口请求
    });
    }).then(value => {
    //第二个接口请求成功,拿到id2 做第一件事情。
    console.log("step2-1-result-event1:" + value);
    return "qiugaoying";

    }).then(value => {
    //第二个接口请求成功,拿到id2 做第二件事情。
    console.log("step2-1-result-event2:" + value);
    return "hello, my name is qiugaoying";
    }).then(value => {
    //最后全部请求完成,触发汇总刷新。
    console.log("step1-result:" + value);
    });
  • then 方法提供两个参数,第二个参数可选。一个是名为resolve的成功回调 和 另一个名为reject 的失败回调。通常处理一个失败回调我们会用catch方法去捕捉异常和失败。
    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
    new Promise(resolve => {
    console.log("step1:异步请求处理耗时任务");
    setTimeout(() => {
    resolve("qiu");
    }, 1000);
    }).then(value => {
    //第一个接口请求成功拿到qiu
    console.log("step1-result:" + value);
    return new Promise(resolve => {
    console.log("step2-1:异步请求处理耗时任务");
    setTimeout(() => {
    resolve("qiugao");
    }, 1000);
    //触发第二个接口请求
    });
    }).then(value => {
    //第二个接口请求成功,拿到id2 做第一件事情。
    let hasError = true
    if (hasError) {
    throw new Error("step2-1-result-event1-error报异常");
    //抛出异常 将不再继续执行then, 直接到catch异常;中途如果没有遇到catch 将直接到最后的catch.
    } else {
    console.log("step2-1-result-event1:" + value);
    return "qiugaoying";
    }
    })
    /*.catch(err => {
    console.log("中途catchError会继续往下走:" + err);
    return "qiugaoying"
    //也会返回一个promise实例,并且是resolved状态,不会被最后一个catch 捕捉
    })*/
    .then(value => {
    //第二个接口请求成功,拿到id2 做第二件事情。
    console.log("step2-1-result-event2:" + value);
    return "hello, my name is "+value;
    }).then(value => {
    //最后全部请求完成,触发汇总刷新。
    console.log("step1-result:" + value);
    }).catch(error => {
    console.log("end error:" + error);
    });
  • finally 方法是最终执行的方法(无参) ,无论状态是什么,成功 or 失败 都会执行收尾。
  • Promise 还提供all 方法(接收一个数组,里面值可以是普通函数,或者Promise对象),可用来做一个类似批量处理之后汇总的业务。

    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
     testPromiseAll() {

    function washFood(){
    console.log('做饭第一步:洗菜');
    let hasError = false;
    if(hasError){
    return "发现了一只虫子!洗掉它。";
    }else{
    return '菜洗干净了。';
    }
    }

    function cutFood() {
    console.log('做饭第二步:切菜');
    var p = new Promise(function (resolve, reject) { //做一些异步操作
    setTimeout(function () {
    let cutFoodHasError = true; //控制默契切菜时 是否发生异常
    if(cutFoodHasError){
    reject("呜呜~ 割到手了,流血!")
    }else{
    resolve('切好了菜。');
    }
    }, 1000);
    })
    /* 试试打开这里的注释
    .catch(error=>{
    console.log("切菜异常:"+error); //异常本身就是返回resolve状态,值是null
    console.log("用创口贴止血,继续做菜。");
    });
    */
    return p;
    }

    function cooking() {
    console.log('做饭第三步:炒菜');
    var p = new Promise(function (resolve, reject) { //做一些异步操作
    setTimeout(function () {
    resolve('菜已做好!');
    }, 2000);
    });
    return p;
    }

    //数组里,如果放Promise 一定要返回状态。
    Promise.all([washFood(), cutFood(),cooking()])
    .then((result) => {
    console.log('上桌,吃饭了:'+result);
    console.log(result);
    }).catch(error=>{
    console.log("菜没做成,出现了小事故:"+error);
    })

    /*
    结果:
    1. 当cutFoodHasError = true;打印如下结果:
    菜没做成,出现了小事故:呜呜~ 割到手了,流血!
    2. 当cutFoodHasError = true 且 cutFood方法里的Promise有异常捕捉时:
    切菜异常:呜呜~ 割到手了,流血!
    上桌,吃饭了:菜洗干净了!,,菜已做好
    3. 当cutFoodHasError = false
    上桌,吃饭了:菜洗好了。切好了菜。菜已做好!
    */

    /*
    Promise.all使用总结:
    数组里可以是Promise对象,也可以是别的值,只有Promise会等待状态改变
    当所有的子Promise都完成,该Promise完成,返回值是全部值得数组
    有任何一个失败,该Promise失败,返回值是第一个先失败的子Promise结果
    */
    },
  • Promise 另外一个race 方法与all 不同的是,race 类似竞赛,参数中的Promise实例只要有一个率先改变状态就会触发结果。

    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

    //打印结果: 兔子赢了
    testPromiseRace() {
    function rabbit() {
    console.log('兔子选手');
    var p = new Promise(function (resolve, reject) {
    setTimeout(function () {
    resolve('兔子赢了');
    }, 1000);
    }) .catch(error=>{
    console.log("兔子跑步中出现异常:"+error);
    });
    return p;
    }

    function tortoise() {
    console.log('乌龟选手');
    var p = new Promise(function (resolve, reject) {
    setTimeout(function () {
    resolve('乌龟赢了');
    }, 3000);
    });
    return p;
    }

    Promise.race([rabbit(),tortoise()])
    .then((result) => {
    console.log("比赛结果:"+result);
    })
    }

Vue Cli 脚手架项目请求模块的搭建

在vue的基础上,我们可以通过vue cli来管理我们的项目。vue cli 方式的优势在于模块之间可以很好地相互引用,通过package 包来管理配置相关的框架依赖。生态非常丰富,可轻松运用vue-router 来解决路由跳转等问题。
首先第一步是要创建一个 vue cli项目。

基础安装环境

1
2
3
npm install -g @vue/cli
vue create helloworld 方式创建项目
也可以 使用GUI vue ui 来通过网页 手动点击按钮操作 来创建项目

package.json 配置

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
{
"name": "gaoying-cli",
"version": "0.1.0",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint"
},
"dependencies": {
"animate.css": "^3.7.2",
"core-js": "^3.4.3",
"vue": "^2.6.10",
"vue-axios": "^2.1.5",
"vue-router": "^3.1.3",
"vuex": "^3.1.2",
"weui.js": "^1.2.1"
},
"devDependencies": {
"@vue/cli-plugin-babel": "^4.1.0",
"@vue/cli-plugin-router": "^4.1.1",
"@vue/cli-plugin-vuex": "^4.1.1",
"@vue/cli-service": "^4.1.0",
"axios": "^0.19.0",
"vue-template-compiler": "^2.6.10",
"weui": "^2.1.3"
},
"eslintConfig": {
"root": true,
"env": {
"node": true
},
"extends": [
"plugin:vue/essential",
"eslint:recommended"
],
"parserOptions": {
"parser": "babel-eslint"
}
},
"browserslist": [
"> 1%",
"last 2 versions"
]
}

配置main.js

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
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'

import axios from 'axios' //请求网络
import VueAxios from 'vue-axios'
import weui from 'weui.js' //weuiJs
import 'weui' //weui样式
import ymAPI from '@/assets/js/urlConfig.js' //Api
import netManager from '@/assets/js/netManager.js' //请求基础参数拼接
import ymNativeBridge from '@/assets/js/ymNativeBridge.js' //原生事件交互
import animate from 'animate.css'

const ymHttp = axios.create({

baseURL: 'https://vendor-api-prod.gaoying.com',
timeout: 1000,
transformRequest: [function(data) {
// Do whatever you want to transform the data
let ret = ''
for (let it in data) {
ret += encodeURIComponent(it) + '=' + encodeURIComponent(data[it]) + '&'
}
return ret
}]
});

Vue.config.productionTip = false

Vue.prototype.$weui = weui
Vue.prototype.$ymAPI = ymAPI
Vue.prototype.$netManager = netManager
Vue.prototype.$ymNativeBridge = ymNativeBridge
Vue.use(VueAxios, ymHttp,animate)

new Vue({
router,
store,
render: h => h(App)
}).$mount('#app');

请求Demo示例

//和普通h5 请求网络类似,唯一不同的就是通过挂载到Vue上的axios ,可以通过this.$http 取得。this.$netManager 也类似。

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
execBaseCustomerRequest: function(url, listKey) {

var loading = null;
if (this.pageNumber == 1) {
loading = this.$weui.loading('加载中');
this.gyPageListStart = 0;
}
let pageVue = this; //this在不同的作用域中指向的对象不同;临时存储vue对象

this.$http.post(url, this.$netManager.getRequestParams({
customer_id: this.customerId,
start: this.gyPageListStart,
size: 10,
})).then(function(response) {
if (loading) {
loading.hide();
}

let dataDic = response.data;
if (dataDic.status == 1) {

var arr = pageVue.dataSource;
if (pageVue.pageNumber == 1) {
arr = [];
}

var dataArr = dataDic.data[listKey];
arr = arr.concat(dataArr);

pageVue.gyPageListStart = dataDic.data.start;
pageVue.hasMoreDataFlag = (dataDic.data.more == 1);
pageVue.dataSource = arr;
}else{
pageVue.$weui.topTips(dataDic.errorMsg);
}
setTimeout(function() {
pageVue.requestLoadingFinished = true;
}, 800);
//重置加载完成的状态;

}).catch(function(error) {
console.log(error);
});
}

模块之间的引用和导出

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
import MD5 from './md5.js'
export default {
getRequestParams: function(params) {
let userId = localStorage.getItem("userId");
let token = localStorage.getItem("token");
......
}
}

//md5.js 文件中
var MD5 = function (string) {
......
}
module.exports = MD5

//接口配置urlConfig.js 文件中
const ymAPI = {

//客户信息
customerList: "/customer/list/",
customerLogs: "/customer/logs/",
customerCoupons: "/customer/coupons/",
customerOrders: "/customer/orders/",
customerDetail: "/customer/detail/"
}
module.exports = ymAPI

export 在js文件中可以有多个.

1
2
3
4
5
6
7
8
//testExport.js
export const person = { name: "qiugaoying",sex:1, job:"software Programmer"}

export const addressInfo = {province:"广东省", city:"guangzhou"}

export const introduct = function(){
console.log("my name is qiugaoying");
}

export 有多个的时候 ,导入必须用花括号括起来.
1
2
import {person,addressInfo} from "../../utils/testExport.js"
import * as allExp from "../../utils/testExport.js" //加载全部export,取个别名

总结

  1. export default 只能导出一个对象。import 和 export 同一js文件中可以导入或导出多次。
  2. module.exports 和 exports 都是 node 端在用,两者指向同一内存块,可以导出变量 或 函数,对象。
  3. exports 变量是在模块的文件级作用域内可用的,且在模块执行之前赋值给 module.exports。

H5 和 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

//原生调用app
export default {
callApp:function(handlerMethod, parameters) {

if (!window.WeixinJSBridge || !WeixinJSBridge.invoke) {
var handlerInterface = 'YunMiaoNative';
var dic = {
'handlerInterface': handlerInterface,
'function': handlerMethod, //调用原生的指令
'parameters': parameters //传递的参数;
};
if (/(iPhone|iPad|iPod|iOS)/i.test(navigator.userAgent)) {
window.webkit.messageHandlers[handlerInterface].postMessage(dic);
} else { //安卓
app.ymAppWebClick(); //dic
}
} else {
//包含微信功能,比如可唤起小程序。
}
},
//原生调用app 并传递参数给H5
callbackH5: function(handlerMethod, parameters, callbackMethod) {
if (!window.WeixinJSBridge || !WeixinJSBridge.invoke) {
var handlerInterface = 'YunMiaoH5';
var dic = {
'handlerInterface': handlerInterface,
'function': handlerMethod, //调用原生的指令
'parameters': parameters, //传递的参数;
'callbackMethod': callbackMethod //回调的函数
};
if (/(iPhone|iPad|iPod|iOS)/i.test(navigator.userAgent)) {
window.webkit.messageHandlers[handlerInterface].postMessage(dic);
} else { //安卓
app.ymAppWebClick(JSON.stringify(dic));
}
} else {
//包含微信功能,比如可唤起小程序。
}
}
}

评论