想要开发出一套高质量的小程序,运用框架,组件库是省时省力省心必不可少一部分,随着小程序日渐火爆,各种不同类型的小程序也渐渐更新,其中不乏一些优秀好用的框架/组件库。

1:WeUI 小程序–使用教程

https://weui.io/

官方介绍:WeUI 是一套同微信原生视觉体验一致的基础样式库,由微信官方设计团队为微信内网页和微信小程序量身设计,令用户的使用感知更加统一。小程序开发中最常用到的一款框架,受广大开发人员的欢迎。

在这里插入图片描述

2:美团小程序框架mpvue

Github:https://github.com/Meituan-Dianping/mpvue
官网: http://mpvue.com/

官方介绍:mpvue是一个使用 Vue.js开发小程序的前端框架。框架基于 Vue.js核心,mpvue修改了 Vue.js的 runtime和 compiler实现,使其可以运行在小程序环境中,从而为小程序开发引入了整套Vue.js开发体验。

在这里插入图片描述

3:组件化开发框架wepy

Github地址: https://github.com/Tencent/wepy
官网地址: https://tencent.github.io/wepy

官方介绍:组件化开发,完美解决组件隔离,组件嵌套,组件通信等问题,支持使用第三方 npm 资源,自动处理 npm 资源之间的依赖关系,完美兼容所有无平台依赖的 npm 资源包.
在这里插入图片描述

4:官方框架MINA

地址:https://developers.weixin.qq.com/miniprogram/dev/framework/MINA.html

官方介绍:框架提供了自己的视图层描述语言 WXML 和 WXSS,以及基于 JavaScript 的逻辑层框架,并在视图层与逻辑层间提供了数据传输和事件系统,可以让开发者可以方便的聚焦于数据与逻辑上。

在这里插入图片描述

5:Tina.js 一款轻巧的渐进式微信小程序框架

Tina.js 开源框架地址: https://github.com/tinajs/tina

官方介绍:是一款轻巧的渐进式微信小程序框架,保留 MINA (微信小程序官方框架) 的大部分 API 设计;无论你有无小程序开发经验,都可以轻松过渡上手。

在这里插入图片描述

6:前端框架weweb

地址: https://github.com/wdfe/weweb

官方介绍:weweb是一个兼容小程序语法的前端框架,你可以用小程序的写法,来写web应用。如果你已经有小程序了,通过它你可以将你的小程序运行在浏览器中。

7:微信UI组件库 iView Weapp

https://weapp.iviewui.com/

介绍:iView Weapp 提供了与 iView 一致的 UI 和尽可能相同的接口名称,大幅度降低了学习成本,是一套一套高质量的微信小程序 UI 组件库。

在这里插入图片描述

8:ZanUI-WeApp – 一个颜值高、好用、易扩展的微信小程序 UI 库

https://cnodejs.org/topic/589d625a5c8036f7019e7a4a

官方介绍:ZanUI-WeApp结合了微信的视觉规范,为用户提供更加统一的使用感受。 包含 badge、btn、等共计 17 类组件或元素。

原文

今天在赶着一个项目,突然发现小程序之中使用update不行。数据不能更新
这就是我的代码:

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
const db = wx.cloud.database()
const _ = db.command
// 1、首先看xxx集合里面有没有存在这个人
db.collection('xxx').where({
shId: res.currentTarget.dataset.xinxi._openid
}).get({
success: function(e) {
// 2、没有的话add一个用户
if (e.data.length == 0) {
db.collection('jifen').add({
data: {
~~~~~~~~~~~~~~~
},
// 添加成功、然后修改审核里面的东西
success(al) {
db.collection(xxx).where({
~~~~~~~~~~~~~~~~~~
}).update({
data: {
~~~
},
// 失败的话,直接showToast/成功跳转到原页面
success(value2) {
if (value2.stats.updated == 0) {
~~~
} else {
~~~
}
}
})
}
})
//3、存在这个用户,直接去更新里面的数据
} else {
~~~
})

结果当然是更新不了数据呀~

以下看了官网只有自己的理解:
where适用于对于集合的更新,对于单条数据并没有作用
所以做出了一下修改

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
const db = wx.cloud.database()
const _ = db.command
// 1、首先看xxx集合里面有没有存在这个人
db.collection('xxx').where({
shId: res.currentTarget.dataset.xinxi._openid
}).get({
success: function(e) {
// 2、没有的话add一个用户
if (e.data.length == 0) {
db.collection('jifen').add({
data: {
~~~~~~~~~~~~~~~
},
// 添加成功、然后修改审核里面的东西
success(al) {
db.collection(xxx).doc(_id).update({
data: {
~~~
},
// 失败的话,直接showToast/成功跳转到原页面
success(value2) {
if (value2.stats.updated == 0) {
~~~
} else {
~~~
}
}
})
}
})
//3、存在这个用户,直接去更新里面的数据
} else {
~~~
})

解析

1
2
3
4
5
6
7
8
9
10
db.collection('todos').doc('todo-identifiant-aleatoire').update({
// data 传入需要局部更新的数据
data: {
// 表示将 done 字段置为 true
done: true
},
success(res) {
console.log(res.data)
}
})

问题成功解决,数据修改成功

WeUI 是一套同微信原生视觉体验一致的基础样式库,由微信官方设计团队为微信 Web 开发量身设计,可以令用户的使用感知更加统一。包含按钮button、组件cell、弹框dialog、 进度条progress、 提示toast、文章article、弹出菜单actionsheet、图标icon等各式元素。

1、GitHub网址:https://github.com/weui/weui-wxss/

2、如何使用

使用git克隆到本地

git clone https://github.com/weui/weui-wxss/

在这里插入图片描述

可以看到如下目录:

在这里插入图片描述

我们只需要将weui-wxss-master\dist\style\weui.wxss文件导入到小程序项目的根目录下:

在这里插入图片描述

新建微信小程序项目,将weui.wxss文件导入到小程序项目的根目录下:

在这里插入图片描述

在项目中引用:在app.wxss中加入weui.wxss的引用- @import ‘weui.wxss’;

在这里插入图片描述
然后报错:
在这里插入图片描述
还需将base和widget引入
在这里插入图片描述

使用WeUI样式说明:

1、根组件使用class=”page”

1
<view class="page">

2、页面骨架组件使用class=”page__xxx”(注意是两个下划线)

1
2
3
4
<view class="page__hd">
<view class="page__title">标题</view>
<view class="page__desc">文章</view>
</view>

3、其他组件都已weui-开头后接组件名称,例如class=”weui-footer”

1
<view class="weui-footer">我是页脚</view>

4、组件的子组件样式,例如view.weui-footer组件还有链接和版权信息。

1
2
3
4
5
6
<view class="weui-footer">
<view class='weui-footer__links'>
<navigator url='' class='weui-footer__link'>隔壁小陈</navigator>
</view>
<view class='weui-footer__text'>Email:1724264854@qq.com</view>
</view>

在这里插入图片描述

WeUI标签预览(具体应以WeUI样式标签为准)

在这里插入图片描述
在线体验:
在这里插入图片描述
1、表单:

Button:

weui-btn:按钮

weui-btn mini-btn:小按钮

list:

weui-cells__title:列表组件标题

weui-cell__bd:标题文字

weui-cell__ft: 说明文字

Input:

weui-cells__title:列表组件标题

weui-check: 单选

weui-icon-radio: 单选图标

weui-cell__bd: 标题文字说明

weui-label: 列表

weui-input: 输入

weui-vcode-btn: 获取验证码按钮

weui-vcode-img: 验证码图片

weui-textarea:请输入文本

weui-select: 选择

Slider:滑动(参考小程序文档)

Uploader:

weui-uploader:上传

weui-uploader__title:图片上传

weui-uploader__info: 上传个数

weui-uploader__file: 上传文件

weui-uploader__img:上传图片

2、基础组件

Article:

weui-article:文章标签

weui-article__h1:标题

weui-article__title:章标题

weui-article__section:文章区域

weui-article__h3:节标题

weui-article__p:文字

weui-article__img:图片

Badge:徽章

weui-cells__title:列表组件标题

weui-cell__bd:组件列表行

weui-badge:徽章

Flex:布局

weui-flex:布局区域

weui-flex__item:布局选项

Footer:页脚

weui-footer:页脚名称

weui-footer__text:页脚内容

weui-footer__links:页脚链接

Gallery:画廊(参考小程序文档)

Grid:九宫格

weui-grids:九宫格区域

weui-grid__icon:九宫格图标

weui-grid__label:九宫格列表

Icons:图标

icon-box:图表区域

icon-box__title:图标标题

icon-box__desc:图标说明

Loadmore:加载更多

weui-loading:加载更多标签

weui-loadmore__tips:正在加载

Panel:面板

weui-panel__hd:面板页头

weui-panel__bd:面板主体

weui-media-box__title:面板标题

weui-media-box__desc:面板说明

weui-media-box__info__meta:面板底部文字说明

Preview:表单预览

weui-form-preview:表单预览区域

weui-form-preview__hd:表单预览页头

weui-form-preview__bd:表单预览主体

weui-form-preview__item:表单预览列表区域

weui-form-preview__label:表单预览列表标题

weui-form-preview__value_in-hd:表单预览页头内容

weui-form-preview__value:表单预览列表内容

Progress:进度条

weui-progress:进度条标签

weui-progress__bar:进度条

weui-progress__opr:进度条图标

3、操作反馈

ActionSheet:弹出式菜单

weui-btn-area:按钮标签区域

Dialog:对话框

weui-btn-area:按钮标签区域

weui-btn:按钮

Msg:提示页

weui-btn-area:按钮标签区域

weui-btn:按钮

weui-msg__icon-area:提示图标

weui-msg__text-area:提示区域

weui-msg__title:提示标题

weui-msg__desc:提示说明

weui-msg__opr-area:提示后操作区域

weui-msg__extra-area:额外区域

Picker:选择器

weui-btn-area:按钮区域

Toast:弹出式提示

weui-btn-area:按钮标签区域

weui-btn:按钮

4、导航相关:

Navbar:头部导航

weui-navbar:导航标签

weui-navbar__title:导航标题

weui-navbar__slider:导航滑块

weui-tab__panel:导航面板

weui-tab__content:导航内容

Tabbar:底部导航(参考小程序文档)

5、搜索相关

Searchbar:搜索栏

weui-search-bar:搜索栏区域

weui-search-bar__form:搜索栏表单

weui-search-bar__label:搜索栏列表

weui-icon-search:搜索栏图标

weui-search-bar__text:搜索栏文字

weui-cell__bd:搜索栏文本

1.传统方式实现模板消息

1、获取openId

2、获取模板ID

有两个方法可以获取模版ID

  • 通过模版消息管理接口获取模版ID
  • 在微信公众平台手动配置获取模版ID

登录https://mp.weixin.qq.com 获取模板,如果没有合适的模板,可以申请添加新模板,审核通过后可使用

3、获取access_token

access_token 的有效期目前为2个小时,需定时刷新,重复获取将导致上次获取的 access_token 失效

公众平台的 API 调用所需的 access_token 的使用及生成方式说明:

1. 为了保密 appsecrect,第三方需要一个 access_token 获取和刷新的中控服务器。而其他业务逻辑服务器所使用的access_token 均来自于该中控服务器,不应该各自去刷新,否则会造成 access_token 覆盖而影响业务;
2. 目前 access_token 的有效期通过返回的 expires_in来传达,目前是7200秒之内的值。中控服务器需要根据这个有效时间提前去刷新新access_token。在刷新过程中,中控服务器对外输出的依然是老access_token,此时公众平台后台会保证在刷新短时间内,新老 access_token 都可用,这保证了第三方业务的平滑过渡;
3. access_token 的有效时间可能会在未来有调整,所以中控服务器不仅需要内部定时主动刷新,还需要提供被动刷新access_token 的接口,这样便于业务服务器在 API 调用获知 access_token 已超时的情况下,可以触发access_token 的刷新流程。

开发者可以使用 AppID 和 AppSecret 调用本接口来获取 access_token。AppID 和 AppSecret 可登录微信公众平台官网-设置-开发设置中获得(需要已经绑定成为开发者,且帐号没有异常状态)。AppSecret 生成后请自行保存,因为在公众平台每次生成查看都会导致 AppSecret 被重置。注意调用所有微信接口时均需使用 https 协议。如果第三方不使用中控服务器,而是选择各个业务逻辑点各自去刷新 access_token,那么就可能会产生冲突,导致服务不稳定

以上内容是微信开发文档给出的access_token的说明,总的来说就是两点

(1)access_token是发送模板消息的凭证,需要用微信小程序的AppID 和AppSecret 调用接口来获取,有效时间为2小时
(2)如果在此获取access_token则会导致前一个access_token失效,有多个公众号和小程序的开发者一定要注意这一点!

接口地址:

https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET

GET请求

参数说明 :
| 参数|必填 | 说明|
|–|–| –|
| grant_type | 是 | 获取 access_token 填写 client_credential |
| appid | 是 | 第三方用户唯一凭证 |
| secret | 是 | 第三方用户唯一凭证密钥,即appsecret |

1
2
3
4
5
6
7
function onRequest(request, response, modules) {
var http = modules.oHttp;
//发起Get请求
http('https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=XXX&secret=XXX', function (error, res, body) {
response.send(body);
});
}

结果:

1
2
3
4
5
6
Response Body
{
"code": 200,
"msg":
{"access_token":"6_R8kOFxRLeRtWOq_haIU29XB0hbAoKxFGRqQTZt_HhzCSEvT_xDJ4WLgf9A_dt2pRTCugfUk7IkxR2sfu5zckCwKKWEghCtqlrrQlhdJmCxmkBAaDEdW7AU98D23rUJO5PYruNYOLQ3cpH3iAVTNiADAECW","expires_in":7200}
}

发送模板消息
接口地址:(ACCESS_TOKEN 需换成上文获取到的 access_token)

https://api.weixin.qq.com/cgi-bin/message/wxopen/template/send?access_token=ACCESS_TOKEN

POST请求

POST参数说明:
| 参数|必填 | 说明|
|–|–| –|
| touser | 是 | 接收者(用户)的 openid |
| template_id | 是 | 所需下发的模板消息的id |
| page | 否 | 点击模板卡片后的跳转页面,仅限本小程序内的页面。支持带参数,(示例index?foo=bar)。该字段不填则模板无跳转。 |
| form_id | 是 | 表单提交场景下,为 submit 事件带上的 formId;支付场景下,为本次支付的 prepay_id |
| data | 是 | 模板内容,不填则下发空模板 |
| color| 否 | 模板内容字体的颜色,不填默认黑色 |
| emphasis_keyword| 否 | 模板需要放大的关键词,不填则默认无放大 |

formid说明:页面的

组件,属性report-submit为true时,可以声明为需发模板消息,此时点击按钮提交表单可以获取formId

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var temp = {
"touser": touser,//用户的openid
"template_id": template_id,//模板id
"page": "",
"form_id": formid,//表单id
"data": {
"keyword1": {
"value": title,
"color": "#173177"
},
"keyword2": {
"value": gettime()
},
},
"emphasis_keyword": "keyword1.DATA" //将keyword1放大
}

测试效果:
在这里插入图片描述

2、基于Bmob后端云的模板消息实现

第一种方法实现起来过于繁琐,在使用过程中也要中间服务器的帮助,如果没有中间服务器,或者服务器端的代码不太熟悉,那么Bmob后端云则可以帮助你实现你要的功能

只需要下载按照官网文档在小程序内初始化好Bmob,那么就可以调用Bmob模板消息的方法,如下所示:

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
function send(title, formid, touser){
var Bmob = require('bmob.js');
var temp = {
"touser": touser,
"template_id": "ClYq9lc8bZh26uM993NNIknasAVBdk0wSYnDrkClK40",
"page": "",
"form_id": formid,
"data": {
"keyword1": {
"value": title,
"color": "#173177"
},
"keyword2": {
"value": gettime()
},

},
"emphasis_keyword": "keyword1.DATA"
}

Bmob.sendMessage(temp).then(function (obj) {
console.log('发送成功');
},
function (err) {
common.showTip('失败' + err)
});
}

这样模板消息就解决了!!!

模板template的使用

如下图,我们经常做这样的列表页,课程搜索结果页和课程列表页结构是完全一样的,非常适合使用模板来完成页面搭建。

这样我们就不用写那些重复的代码了,而且修改界面的时候也只需要改动模板一个地方

一、定义模板
1、新建一个template文件夹用来管理项目中所有的模板;
2、新建一个personCourseTmp.wxml文件来定义模板;

3、使用name属性,作为模板的名字。然后在

那我们开始实现吧,建模板2个文件

personCourseTmp.wxml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<template name="personCourseItemTmp">
<view class="courses-list" style='background-image: url("{{cardImage}}")'>

<view class="money-border"> ¥
<text class="money">{{cardMoney}}</text>/课时</view>

<view class="name">
<text class="ename">GillMo</text>
<text class="cname">.小川</text>
</view>

<view class="mark">
<view>{{cardMark}}</view>
</view>

</view>
</template>

样式文件personCourseTmp.wxss

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
.courses-list {
height: 342rpx;
margin: 40rpx auto;
border-top: 2rpx solid #f0f0f0;
margin-top: 10rpx;
flex-direction: column;
align-items: flex-start;
width: 670rpx;
border-radius: 8px;
}

.money-border {
font-family: PingFangSC-Semibold;
font-size: 28rpx;
color: #fff;
letter-spacing: -0.41px;
height: 56rpx;
margin-top: 20rpx;
margin-left: 20rpx;
}

.money {
font-size: 40rpx;
}

.name {
margin-top: 158rpx;
margin-left: 20rpx;
font-family: PingFangSC-Semibold;
color: #fff;
letter-spacing: -0.41px;
}

.ename {
font-size: 40rpx;
}

.cname {
font-size: 30rpx;
}

.mark {
font-family: PingFangSC-Regular;
font-size: 28rpx;
color: #fff;
margin-left: 20rpx;
letter-spacing: -0.41px;
margin-bottom: 18rpx;
}

那我们如何在页面上使用呢,引入样式文件和视图文件

比如我们要在Course.wxss上面引入样式文件

@import “../template/personCourseTmp.wxss”;

只需要在Course.wxss里面加入上面的代码

我们要在Course.wxml上面引入视图文件

< import src=”../template/personCourseTmp.wxml” />

1
2
3
4
<block wx:for="{{goodlist}}" wx:key="idx">
<template is="personCourseItemTmp" data="{{...item}}"></template>
</navigator>
</block>

传数据时item前面加三个点… 模板里面就不需要写item了,

如果要传多个数据到模板

1
2
3
<view class="tab-list" wx:for="{{list}}" wx:key="index">
<template is="day-tab" data="{{item,index:index,currentTarget:currentTarget}}" wx:key="index"></template>
</view>

用逗号分开,item 是对象,index是单个数据,要用键值对,template就介绍到这来

解决问题:
在微信小程序的开发工具中,会发现里面已经有ES6语法转换ES5的设置,但是当你使用了箭头函数在里面使用this.setData会发现报错,这可能又是腾讯莫名的BUG了,建议换回ES5语法使用function(){}写法较为妥当。
在这里插入图片描述

1、单参数

1
2
3
4
5
6
function cheng(a=3){
  return a*a;
}

let cheng = (a=3) => a*a; // 箭头左边为参数 箭头右边为返回值
console.log(cheng(9));

2、多参数

1
2
3
4
5
6
function add(a,b){
  return a+b;
}

let add = (a,b) => a+b; // 默认返回值
console.log(add(3,9));

3、无返回值

1
2
3
4
5
6
function add(a,b){
  console.log(a+b);
}

let add = (a,b) => {console.log(a+b)}; // 如果没有返回值,需要加{}
add(3,9);

4、多行

1
2
3
4
5
6
7
8
9
10
11
function add(a,b){
  console.log(a+b);
  return a+b;
}

let add = (a,b) => {
  console.log(a+b);
  return a+b;
}

console.log(add(3,9));

除了简洁以外,为啥需要改变原来的习惯去使用箭头函数?

它对this的处理与一般的普通函数不一样,箭头函数的this始终指向函数定义时的this,而非执行时

例如

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var obj = {
  x:1,
  func: function(){ console.log(this.x) }, // 此处的 this 代表 obj
  test: function(){
    setTimeout(function(){
      alert(this); // 因为使用了异步,在运行过程中,this发生了指针转移,不再指向obj,而是指向全局 Window对象
      // [object Window]
      this.func();
    },1000);
  }
};、
obj.test();

//报错
// TypeError: this.func is not a function

箭头函数:

1
2
3
4
5
6
7
8
9
10
11
12
var obj = {
  x: 1,
  func: function(){ console.log(this.x) },
  test: function(){
    setTimeout(() => {
      alert(this); // [object Object] 此处的this指向obj
      this.func();
    },1000);
  }
};

obj.test(); // 这回this就指向obj了

很多人都不太了解,到底在什么时候调用promise呢?

  1. 很多调用是异步(发起动作和成功或者失败时调用的函数是两个函数,调用方面没有调用与被调用的关系)的。
  2. 从业务逻辑上说,这些 异步调用按照一个调用顺序调用,需要在第一个成功后,调用第二个,第二个成功后调用第三个,以此类推。
  3. 当业务逻辑用的非常复杂的时,代码逻辑性变得很差。

首先一段代码:

1
2
3
4
5
6
7
8
9
// 当参数a大于10且参数fn2是一个方法时 执行fn2
function fn1(a, fn2) {
if (a > 10 && typeof fn2 == 'function') {
fn2()
}
}
fn1(11, function() {
console.log('this is a callback')
})

一般来说我们会碰到的回调嵌套都不会很多,一般就一到两级,但是某些情况下,回调嵌套很多时,代码就会非常繁琐,会给我们的编程带来很多的麻烦,这种情况俗称——回调地狱。

promise能解决的问题:

  • 回调地狱,代码难以维护, 常常第一个的函数的输出是第二个函数的输入这种现象
  • promise可以支持多个并发的请求,获取并发请求中的数据
  • 这个promise可以解决异步的问题,本身不能说promise是异步的

es6 promise用法大全

Promise是一个构造函数

1
2
3
4
5
6
7
let p = new Promise((resolve, reject) => {
//做一些异步操作
setTimeout(() => {
console.log('执行完成');
resolve('我是成功!!');
}, 2000);
});

Promise的构造函数接收一个参数:函数,并且这个函数需要传入两个参数:

  • resolve :异步操作执行成功后的回调函数
  • reject:异步操作执行失败后的回调函数

    then 链式操作的用法

从表面上看,Promise只是能够简化层层回调的写法,而实质上,Promise的精髓是“状态”,用维护状态、传递状态的方式来使得回调函数能够及时调用,它比传递callback函数要简单、灵活的多。所以使用Promise的正确场景是这样的:

1
2
3
4
5
6
7
8
9
p.then((data) => {
console.log(data);
})
.then((data) => {
console.log(data);
})
.then((data) => {
console.log(data);
});

reject的用法 :

把Promise的状态置为rejected,这样我们在then中就能捕捉到,然后执行“失败”情况的回调。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
let p = new Promise((resolve, reject) => {
//做一些异步操作
setTimeout(function(){
var num = Math.ceil(Math.random()*10); //生成1-10的随机数
if(num<=5){
resolve(num);
}
else{
reject('数字太大了');
}
}, 2000);
});
p.then((data) => {
console.log('resolved',data);
},(err) => {
console.log('rejected',err);
}
);

then中传了两个参数,then方法可以接受两个参数,第一个对应resolve的回调,第二个对应reject的回调。所以我们能够分别拿到他们传过来的数据。

catch的用法

它和then的第二个参数一样,用来指定reject的回调

1
2
3
4
5
p.then((data) => {
console.log('resolved',data);
}).catch((err) => {
console.log('rejected',err);
});

效果和写在then的第二个参数里面一样。不过它还有另外一个作用:在执行resolve的回调(也就是上面then中的第一个参数)时,如果抛出异常了(代码出错了),那么并不会报错卡死js,而是会进到这个catch方法中

例如:

1
2
3
4
5
6
7
p.then((data) => {
console.log('resolved',data);
console.log(somedata); //此处的somedata未定义
})
.catch((err) => {
console.log('rejected',err);
});

all的用法:谁跑的慢,以谁为准执行回调。all接收一个数组参数,里面的值最终都算返回Promise对象

Promise的all方法提供了并行执行异步操作的能力,并且在所有异步操作执行完后才执行回调

1
2
3
4
5
6
7
8
9
10
11
let Promise1 = new Promise(function(resolve, reject){})
let Promise2 = new Promise(function(resolve, reject){})
let Promise3 = new Promise(function(resolve, reject){})

let p = Promise.all([Promise1, Promise2, Promise3])

p.then(funciton(){
// 三个都成功则成功
}, function(){
// 只要有失败,则失败
})

有了all,你就可以并行执行多个异步操作,并且在一个回调中处理所有的返回数据。

race的用法:谁跑的快,以谁为准执行回调

race的使用场景:比如我们可以用race给某个异步请求设置超时时间,并且在超时后执行相应的操作

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
//请求某个图片资源
function requestImg(){
var p = new Promise((resolve, reject) => {
var img = new Image();
img.onload = function(){
resolve(img);
}
img.src = '图片的路径';
});
return p;
}
//延时函数,用于给请求计时
function timeout(){
var p = new Promise((resolve, reject) => {
setTimeout(() => {
reject('图片请求超时');
}, 5000);
});
return p;
}
Promise.race([requestImg(), timeout()]).then((data) =>{
console.log(data);
}).catch((err) => {
console.log(err);
});

requestImg函数会异步请求一张图片,我把地址写为”图片的路径”,所以肯定是无法成功请求到的。timeout函数是一个延时5秒的异步操作。我们把这两个返回Promise对象的函数放进race,于是他俩就会赛跑,如果5秒之内图片请求成功了,那么遍进入then方法,执行正常的流程。如果5秒钟图片还未成功返回,那么timeout就跑赢了,则进入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
// 三个状态 resolved(解决) rejected(拒绝) pending(等待)
// promise中放了一个函数 函数executor 执行器
// resolve和reject都是函数 调用后可以让状态进行改变

// promise解决的问题有
//1) 回调地狱
//2)解决 并发异步,再同一时刻内获取并发的结果
//3) 链式调用 (jquery)

//常用写法
let promise=new Promise((resolve,reject)=>{
setTimeout(()=>{ //这个定时器就相当于我们的异步代码 (比如ajax)
resolve('123')
})

})
promise.then(res=>{
console.log(res) //123
},err=>{

})
//链式调用 说明then返回的是一个Promise的实例
promise.then(res=>{
console.log(res)
},err=>{

}).then(res=>{
console.log(res)
},err=>{

})

执行顺序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//执行顺序
let promise=new Promise((resolve,reject)=>{

resolve('123')
console.log(1)

})
promise.then(res=>{
console.log(res) //123
},err=>{

})
//打印顺序 1,123
//默认promise中的executor(就是Promise中的参数)是同步执行的 //而then中的参数相当于回调

值的穿透

1
2
3
4
5
6
//值的穿透 then不传参数就默认走到下一then中
promise.then().then(res=>{
console.log(res) //123
},err=>{

})

then的问题

1
2
3
4
5
6
7
8
9
10
11
//then的问题
// 最常见的 如果返回的是普通值 直接把值作为外层下一次then的参数
promise.then(res=>{
return 1 //返回除了Promise外的参数 直接把值作为外层下一次then的参数
},err=>{

}).then(res=>{
console.log(res) //1
},err=>{

})

注意

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// then方法调用后,返回的是一个新的promise  //会把这个promise下的所有包括当前返回的promise执行完毕  取到成功或失败的结果
//当做外层下一次then的成功或失败的结果 执行过程中失败就直接取失败 //成功就一直执行 知道完后拿到结果
promise.then((data) => {
return new Promise((resolve,reject)=>{
resolve(new Promise((resolve,reject)=>{
resolve('123456');
}))
})
}, err => {
console.log( err);
}).then(res=>{
console.log(res) //123456
},err=>{

})

手写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
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
class Promise {
constructor(executor) {
// 默认状态是等待态
this.status = 'pending';
//成功后的值
this.value = undefined;
//失败的原因
this.reason = undefined;
// 存放成功的回调 解决异步调用 就是我们常用的写法那种
this.onResolvedCallbacks = [];
// 存放失败的回调 用成功就有失败
this.onRejectedCallbacks = [];
//resolve,reject 我们executor中的参数
let resolve = (data) => {
//当resolve调用时相当于成功 所以改变状态为成功 reject同理
if (this.status === 'pending') {
this.value = data;
this.status = 'resolved';
this.onResolvedCallbacks.forEach(fn => fn());
}
}
let reject = (reason) => {
if (this.status === 'pending') {
this.reason = reason;
this.status = 'rejected';
this.onRejectedCallbacks.forEach(fn => fn());
}
}

try { // 执行时可能会发生异常
executor(resolve, reject);
} catch (e) {
reject(e); // promise失败了
}
}
//then 也有两个参数 成功的回调 和失败的回调
then(onFulFilled, onRejected) {
// 解决onFulFilled,onRejected没有传的问题 值的穿透
onFulFilled = typeof onFulFilled === 'function' ? onFulFilled : y => y;
onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err; };

let promise2; //then的返回值
if (this.status === 'resolved') {
promise2 = new Promise((resolve, reject) => {
// 成功的逻辑 失败的逻辑
setTimeout(() => {//模拟Promise执行顺序
try {
let x = onFulFilled(this.value);
// 看x是不是promise 如果是promise 取他的结果 作为promise2,成功的结果
// 如果要是返回一个普通值 作为promise2,成功的结果

// resolvePromise可以解析x和promise2之间的关系
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
});
}
if (this.status === 'rejected') {
promise2 = new Promise((resolve, reject) => {
setTimeout(() => {
try {
let x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e);
}
}, 0);
});
}
// 当前既没有完成 也没有失败
if (this.status === 'pending') {
// 存放成功的回调
promise2 = new Promise((resolve, reject) => {
this.onResolvedCallbacks.push(() => {
setTimeout(() => {
try {
let x = onFulFilled(this.value);
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e);
}
}, 0)
});
// 存放失败的回调
this.onRejectedCallbacks.push(() => {
setTimeout(() => {
try {
let x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
});
})
}
return promise2; // 调用then后返回一个新的promise
}
}
function resolvePromise(promise2, x, resolve, reject) {
// 判断x是不是promise2
// [规范]里规定了一段代码,这个代码可以实现我们的promise和别人的promise可以进行交互
if (promise2 === x) { // 不能自己等待自己完成
return reject(new TypeError('循环引用'));
}
// x不是null或者是对象或者函数
if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
let called; // 防止成功后调用失败
try { // 防止取then是出现异常 Object.defineProperty
let then = x.then; // 取x的then方法 {then:{}}
if (typeof then === 'function') { // 如果then是函数我就认为它是promise
// call 第一个参数是this ,后面的是成功的回调和失败的回调
then.call(x, y => { // 如果y是promise就继续递归解析promise
if (called) return;
called = true;
resolvePromise(promise2, y, resolve, reject);
}, r => { // 只要失败了就失败了
if (called) return;
called = true;
reject(r);
});
} else { // then是一个普通对象,就直接成功即可1
resolve(x);
}
} catch (e) {
if (called) return;
called = true;
reject(e);
}
} else { // x = 123
resolve(x); // x就是一个普通值
}
}
// promise的语法糖,测试
Promise.deferred = Promise.defer = function () {
let dfd = {};
dfd.promise = new Promise((resolve,reject)=>{
dfd.resolve = resolve;
dfd.reject = reject;
})
return dfd;
}
// npm install promises-aplus-tests -g
// promises-aplus-tests 文件名
module.exports = Promise;
// 写完promise会测试一下

MIME(多功能网际邮件扩充协议)

MIME意为多目Internet邮件扩展,它设计的最初目的是为了在发送电子邮件时附加多媒体数据,让邮件客户程序能根据其类型进行处理。然而当它被HTTP协议支持之后,它的意义就更为显著了。它使得HTTP传输的不仅是普通的文本,而变得丰富多彩。

访问一个网页,获得一个资源后,浏览器通过哪种方式来识别这种资源呢?就是通过媒体资源类型MIME Type,媒体资源类型通过http协议,由web服务器告知浏览器,更详细的说,是通过Content-Type来定义的。
例如:Content-Type: text/HTML

通常只有一些在互联网上获得广泛应用的格式才会获得一个 MIME Type,如果是某个客户端自己定义的格式,一般只能以 application/x- 开头。

常见类型

1
2
3
4
5
6
7
8
9
10
11
12
超文本标记语言文本 .html,.html text/html 
普通文本 .txt text/plain
RTF文本 .rtf application/rtf
GIF图形 .gif image/gif
JPEG图形 .ipeg,.jpg image/jpeg
au声音文件 .au audio/basic
MIDI音乐文件 mid,.midi audio/midi,audio/x-midi
RealAudio音乐文件 .ra, .ram audio/x-pn-realaudio
MPEG文件 .mpg,.mpeg video/mpeg
AVI文件 .avi video/x-msvideo
GZIP文件 .gz application/x-gzip
TAR文件 .tar application/x-tar

Mime-type与Content-type区别

mime-type : 文件后缀名

向web服务器请求一个文件,服务器会根据你的后缀名去匹配对应的值设置为response中content-type的值

content-type是正文媒体类型,浏览器根据content-type的不同来分别处理你返回的东西

例子
tomcat(版本为7)下的conf目录下有个web.xml

1156行开始有个节点为

<mime-mapping>
      <extension>css</extension>
      <mime-type>text/css</mime-type>
</mime-mapping>

我把这个节点修改为成这样

<mime-mapping>
       <extension>css</extension>
       <mime-type>text/cssaa</mime-type>
</mime-mapping>

tomcat7请求一个css文件之后抓包后的结果:
在这里插入图片描述

流程

1.请求css文件。

2.web服务器看到后缀名为css,在对应的配置文件中寻找css的mimetype值。

3.当请求完成后设置content-type的值。

4.游览器根据content-type的值处理返回的文件

刚刚才发现原来Canvas跟SVG是可以对比的!!!

SVG

SVG 是一种使用 XML 描述 2D 图形的语言。
SVG 基于 XML,这意味着 SVG DOM 中的每个元素都是可用的。您可以为某个元素附加 JavaScript 事件处理器。
在 SVG 中,每个被绘制的图形均被视为对象。如果 SVG 对象的属性发生变化,那么浏览器能够自动重现图形。

  1. 不依赖分辨率
  2. 支持事件处理器
  3. 最适合带有大型渲染区域的应用程序(比如谷歌地图)
  4. 复杂度高会减慢渲染速度(任何过度使用 DOM 的应用都不快)
  5. 不适合游戏应用

Canvas

Canvas 通过 JavaScript 来绘制 2D 图形。
Canvas 是逐像素进行渲染的。
在 canvas 中,一旦图形被绘制完成,它就不会继续得到浏览器的关注。如果其位置发生变化,那么整个场景也需要重新绘制,包括任何或许已被图形覆盖的对象。

  1. 依赖分辨率
  2. 不支持事件处理器
  3. 弱的文本渲染能力
  4. 能够以 .png 或 .jpg 格式保存结果图像
  5. 最适合图像密集型的游戏,其中的许多对象会被频繁重绘