目录

JavaScript入门

操作数组

分隔符join方法

数组push方法

移除最后一项pop方法

操作对象

给已有对象添加属性和值

删除对象的某个属性

常量

字符串的操作

Math对象

Date对象

全局对象wx

方法、属性很多,要多查MDN来找现成的处理方法。

点击事件

页面滚动

页面路由

响应的数据绑定

函数与调用函数

函数的定义和结构

对象的方法(函数)

匿名函数

箭头函数

JavaScript函数的写法

生命周期

链接携带数据

通过URL带参数实现页面数据传递

自定义属性dataset数据集

小程序和页面的生命周期

onLaunch与onShow

用户登录与信息获取

step1:通过button来获取用户信息

step2:将用户信息写进app.js的globalData

that与this

图片、缓存与文件

获取手机相册或拍照的图片

空值的处理

操作图片

获取图片信息:wx.getImageInfo()

预览图片:wx.previewImage()

保存图片到相册:wx.saveImageToPhotosAlbum()

压缩图片:wx.compressImage()[kəm'pres]

上传文件

选择地理位置

模块化与格式化

模块化与引入模块

map

数据缓存Storage

网络API

数据API

渲染网络数据到页面

域名校验与白名单

res对象和res.data对象

HTML标签解析rich-text

解构赋值

调用API的AppID的存放

模板字符串

encodeURI与decodeURI

md5加密算法


JavaScript入门

  1. 控制台Console除了可以显示小程序的错误信息外,还可以用于输入和调试代码。
  2. JavaScript中单引号和双引号都表示字符串。如果字符串中存在双引号,建议最外层用单引号;如果字符串中存在单引号,建议最外层用双引号。并且如果语句是换行的,后面的分号;也可以不写
  3. JavaScript可以使用let语句声明变量,变量的值可以是任何数据类型
  4. 如果函数没有提供返回值,控制台会返回 undefined。
  5. 数组的__proto__里显示的是方法(函数)prototype['proʊtə.taɪp] 原型
  6. 命名规范:JavaScript的项目名、函数名、变量等都应该遵循简洁、语义化的原则。函数名推荐使用驼峰法来命名(camelCase),比如scrollToTop、pageScrollTo,也就是由小写字母开始后续每个单词首字母都大写,长得跟骆驼的驼峰似的。

操作数组

分隔符join方法

join方法将数组元素拼接为字符串,以分隔符分割,默认是逗号,分割。

数组push方法

push()方法向数组的末尾添加一个或更多元素,并返回新数组的长度

 移除最后一项pop方法

pop() 从数组末尾移除最后一项,并返回移除的项的值:

技术文档:MDN数组Array

操作对象

给已有对象添加属性和值

直接输入对象名.新增属性名=值即可

删除对象的某个属性

比如我们想删除movie的img属性,可以通过delete 方法来删除

常量

有些数据我们希望是固定的(写死,不会经常改变),这个时候可以使用const声明创建一个值的只读引用。

比如开发小程序的时候,我们会确定小程序的色系、颜色等,使用const声明,以后直接调用这个常量,这样就不用记那么多复杂的参数,以后想全网改样式,直接改const的内容即可。比如:

1

2

3

4

5

const defaultStyle = {

color: '#7A7E83',

selectedColor: '#3cc51f',

backgroundColor: '#ffffff',

}

字符串的操作

1

2

3

4

5

6

7

8

9

10

11

12

let lesson="云开发技术训练营";

let enname="CloudBase Camp"

console.log(lesson.length);  //返回字符串的长度

console.log(lesson[4]);  //返回在指定位置的字符

console.log(lesson.charAt(4));   //返回在指定位置的字符

console.log(lesson.substring(3,6));  //从索引3开始到6(不包括6)

console.log(lesson.substring(4));  //从索引4开始到结束

console.log(enname.toLowerCase()); //把一个字符串全部变为小写:

console.log(enname.toUpperCase());  //把一个字符串全部变为大写:

console.log(enname.indexOf('oud')); //搜索指定字符串出现的位置:

console.log(enname.concat(lesson)); //连接两个字符串,也可以用+号

console.log(lesson.slice(4)); //提取字符串的某个部分,并以新的字符串返回被提取的部分

Math对象

Math是一个内置对象, 它具有数学常数和函数的属性和方法,但它不是一个函数对象。

Date对象

Date 对象用于处理日期和时间。时间有年、月、日、星期、小时、分钟、秒、毫秒以及时区的概念,因此Date对象属性和方法也显得比较多。

全局对象wx

wx是小程序的全局对象,用于承载小程序能力相关 API。小程序开发框架提供丰富的微信原生 API,可以方便的调起微信提供的能力,如获取用户信息,了解网络状态等。

方法、属性很多,要多查MDN来找现成的处理方法。

点击事件

事件是视图层到逻辑层的通信方式,当我们点击tap触摸touch长按longpress小程序绑定了事件的组件时,就会触发事件,执行逻辑层中对应的事件处理函数

ps:小程序所有组件都支持绑定点击事件(所有组件都有属性bind* / catch*

页面滚动

wx.pageScrollTo(),将页面滚动到目标位置,支持选择器(选中某一组件)和滚动距离两种方式定位

页面路由

页面路由是一个非常重要的概念,打开新页面、页面返回、Tab页面切换、页面重定向等都是也能路由的不同方式。

页面路由API Navigator open-type值 含义
 redirectTo redirect 关闭当前页面(无法返回),跳转到应用内的某个页面。但是不允许跳转到 tabbar 页面。
navigateTo navigate 保留当前页面(有返回按钮),跳转到应用内的某个页面。但是不能跳到 tabbar 页面。
 navigateBack navigateBack 关闭当前页面,返回上一页面或多级页面。
 switchTab switchTab 跳转到 tabBar 页面,并关闭其他所有非 tabBar 页面
reLaunch reLaunch 关闭所有页面,打开到应用内的某个页面

响应的数据绑定

只要对逻辑层data里的数据进行修改,视图层也会做相应的更新,我们称之为响应的数据绑定,而这是通过Page的setData()方法来实现的。

this.setData和 this.data(在事件处理函数中使用data中的变量)都用到了一个关键字 this。 this和中文里的“这个的”有类似的指代作用,在方法中, this 指代该方法所属的对象,比如这里的是Page对象, this.data就是指Page函数对象里的data对象。

函数与调用函数

函数的定义和结构

1

2

3

function 函数名(参数 1, 参数 2, 参数 3) {

代码块内要执行的语句

}

ps:JavaScript允许传入任意个参数而不影响调用,因此传入的参数可以比定义的参数多,但是不能少。也就是说实参的数量可以多于形参但是不能少于形参。

比如,我们使用开发者工具在data.js的Page()函数前,添加如下代码:

1

2

3

4

function greet() {

console.log("你好,欢迎来到云开发训练营");

};

greet(); //调用greet()函数

对象的方法(函数)

在小程序里我们会经常将一个匿名函数赋值给对象的一个属性,而这个属性我们可以称之为对象的方法。

匿名函数

函数声明function在语法上是一个语句,但函数也可以由函数表达式创建,这样的函数没有函数名称(匿名)。

使用开发者工具在data.js的Page()函数前,输入以下代码:

1

2

3

4

let square = function(number) {

return number * number

};

console.log(square(4))//使用console.log()输出变量square

执行后,可以在控制台看到输出的结果为16。上面这个function函数没有函数名,相当于是把函数的返回值赋值给了变量square。

箭头函数

为什么叫箭头函数(Arrow Function),因为它定义一个函数用的就是一个箭头=>,我们来看两个例子,在data.js的Page()函数前输入以下代码:

1

2

3

4

5

6

const multiply = (x, y) => {

return x * y;

}

const sum= (x, y) => x + y;//连{}和return语句都可以省掉

console.log(multiply(20, 4));

console.log(sum(20, 4));

在控制台我们可以看到箭头函数打印的结果。箭头函数相当于匿名函数,它没有函数名,而且也简化了函数定义。箭头函数可以只包含一个表达式,甚至连{ … }和return都可以省略掉。

JavaScript函数的写法

以下这两种写法都是可以执行的:

1

2

scrollToPosition() {

},

1

2

yellowTap:function(){

},

生命周期

链接携带数据

URL链接的特殊字符

  • / 分隔目录和子目录
  • ? 分隔实际的URL和参数
  • & URL中指定的参数间的分隔符
  • = URL中指定的参数的值
  • # 同一个页面的位置标识符,类似于页面的书签;

通过URL带参数实现页面数据传递

使用开发者工具,新建一个lifecyle的页面,以及在home页面新建一个二级页面detail(也就是在pages配置项新建一个pages/home/detail/detail,以及注意将lifecycle设置为首页)然后在lifecyle.wxml里输入以下代码,这里的url也通过?、&、=添加了很多数据:

1

<navigator id="detailshow" url="./../home/detail/detail?id=lesson&uid=tcb&key=tap&ENV=weapp&frompage=lifecycle" class="item-link">点击链接看控制台</navigator>

onload是Page页面的生命周期函数,当页面加载时触发。一个页面只会调用一次,可以在 onLoad 函数的参数中获取打开当前页面路径中的参数

使用开发者工具,在detail.js的onload函数里添加console.log,把onload函数的参数打印出来:

1

2

3

onLoad: function (options) {

console.log(options)

},

options就是我们传递的参数了。

同时如果我们改成这个,可以实现参数值的自定义。

1

<navigator url="./../home/detail/detail?id={{index}}&name={{movies.name}}&img={{movies.img}}&desc={{movies.desc}}" class="weui-media-box weui-media-box_appmsg" hover-class="weui-cell_active">

不过使用链接url传递参数有字节限制以及只能在跨页面中使用,可以用来传递比如页面链接来源,可以追踪用户来自于什么设备、什么App、通过什么方式以及来自哪个朋友的邀请链接;还可以用于一些网页链接的API必备的id、key等。跨多个页面以及传递更多参数、数据等,可以使用公共数据存储app.globalData、数据缓存、数据库以及新增的页面间通信接口getOpenerEventChannel

自定义属性dataset数据集

这里的data-sku、data-spu和data-pid的值以及图片使用的都是京东iphone的数据。这些自定义数据以 data- 开头,多个单词由连字符 – 连接。

1

2

3

<view id="viewclick" style="background-color: red;padding:20px;" data-sku="100000177760" data-spu="100000177756" data-pid="100000177756" data-toggle="Apple iPhone XR" data-jd-color="Red" data-productBrand="Apple" bindtap="clickView">

<image id="imageclick" src="https://img13.360buyimg.com/n7/jfs/t1/842/9/3723/77573/5b997bedE4f438e5b/ccd1077b985c7150.jpg" mode="widthFix" style="width:200rpx" bindtap="clickImage">点击button</image>

</view>

然后再在js里添加事件处理函数clickView,

1

2

3

4

5

6

clickView: function (event) {

console.log('我是view',event)

wx.navigateTo({

url:"/pages/home/detail/detail?id=viewclick&uid=tcb&key=tap&ENV=weapp&frompage=lifecycle"

})

},

clickImage:function(event){

console.log('我是button',event)

wx.navigateTo({

url: "/pages/home/detail/detail?id=imageclick&uid=tcb&key=tap&ENV=weapp&frompage=lifecycle"

})

},

当我们点击红色空白处(非图片区域)时,只会触发clickView,target与currentTarget的值相同。而当我们点击图片时,就会触发两个事件处理函数

我们点击的是图片image组件,却分别触发了绑定在image组件以及image的父级(上一级)组件view的事件处理函数,我们称这为事件冒泡

注意这时clickView事件对象的currentTarget和target的值就不相同了。在点击图片的情况下只有在clickView事件对象的currentTarget里看到dataset获取到了view组件的自定义数据。

同时从detail页面的打印(注意两个事件的链接有id的值不同)可以看出,点击图片,跳转到的是图片绑定的事件指定的页面,页面的id为imageclick。

我们再来观察dataset的值,发现jdColor以及productbrand,这是因为dataset会把连字符写法会转换成驼峰写法,而大写字符会自动转成小写字符。data-jd-color变成了jdColor,而data-productBrand转成了productbrand。也就是说我们点击组件,从事件对象的dataset里,我们可以通过event.currentTarget.dataset来获取组件的自定义数据。此外,currentTarget指向事件所绑定的元素(比如点击图片以后,图片的父节点函数被迫触发,所以clickView输出的仍然是"viewclick"),而target始终指向事件发生时的元素。(触发的原因)
bind会触发父节点的事件(冒泡),catch不会触发父节点时间(不冒泡)

点击iPhone图片:

小程序和页面的生命周期

App()函数注册小程序,Page()函数注册小程序中的一个页面,他们都接受的是对象Object类型的参数,包含一些生命周期函数和事件处理函数。App() 必须在 app.js 中调用,必须调用且只能调用一次。开发者可以添加任意的函数或数据变量到  Object参数中,用  this 可以访问

onLaunch与onShow

onLaunch是监听小程序的初始化,初始化完成时触发,全局只会触发一次,所以在这里我们可以用来执行获取用户登录信息的函数等一些非常核心的数据,如果onLaunch的函数过多,会影响小程序的启动速度。

onShow是在小程序启动,或从后台进入前台显示时触发,也就是它会触发很多次,在这里就不大适合放获取用户登录信息的函数啦。这两者的区别要注意。

用户登录与信息获取

技术文档:获取用户登录凭证wx.login、获取用户当前权限设置wx.getSetting

wx.login:获取登陆code

wx.getSetting:获取用户已经授权给我们的权限情况

wx.getUserInfo:获取用户信息

ps:由于scope.userInfo是一个属性名,无法使用点表示法res.authSetting.scope.userInfo来获取到它的值(会误认为是authSetting属性下的scope属性的usrInfo属性值),所以要用获取对象属性的另外一种表示方法,叫括号表示法,也就是用中括号[]围住属性名,属性名需用单引号或双引号围住。res.authSetting["scope.userInfo"]

step1:通过button来获取用户信息

添加一个button,配置如下:

一是 open-type="getUserInfo",必须是这个值;

二是绑定事件处理函数的属性名为bindgetuserinfo(类似于bindtap,但是属性名必须为bindgetuserinfo,至于事件处理函数的名称可以自定义)

1

<button open-type="getUserInfo" bindgetuserinfo="getUserInfomation"> 点击获取用户信息 </button>

step2:将用户信息写进app.js的globalData

先在data中提前定义userInfo:{},然后添加函数

1

2

3

4

5

6

7

getUserInfomation: function (event) {

console.log('getUserInfomation打印的事件对象',event)

app.globalData.userInfo = event.detail.userInfo

this.setData({

userInfo: event.detail.userInfo,

})

},

getApp():获取到小程序全局唯一的 App 实例,指向App

页首定义let app = getApp()

然后写值app.globalData.userInfo = event.detail.userInfo

如果要调用globalData的值:console.log('user页面打印的globalData', app.globalData.userInfo)

that与this

this的指向情况非常复杂,尽管哪个对象调用函数,函数里面的this就指向哪个对象,说起来非常简单,但是场景太多,大家在开发时不必强行理解,死记硬背,把this打印出来即可。我们可以将回调函数success的this打印出来,

方法有两种,一种是使用箭头函数,箭头函数继承的是外部对象的this。

第二种方法是使用that指代,在函数外先把this指向的对象保存起来let that=this,在函数中再用that代替(that是随便起的名)

图片、缓存与文件

获取手机相册或拍照的图片

技术文档:wx.chooseImage()

1

2

3

4

5

6

7

8

9

10

11

12

13

14

chooseImg:function(){

let that=this

wx.chooseImage({

count: 1,

sizeType: ['original', 'compressed'],

sourceType: ['album', 'camera'],

success(res) {

const imgurl = res.tempFilePaths

that.setData({

imgurl

})

}

})

},

空值的处理

如果image组件的url为空值,image组件会展示默认的宽度300px、高度225px(会随css而改变大小)的空白区域,处理的方法有三种:

方法一:我们可以给imgurl弄一张初始图片的链接,为了让界面更加美观、交互性更好,通常都会设置一个默认的图片,比如默认的头像,当用户上传时,setData就会取代初始图片;

方法二:判断imgurl是否有内容,比如我们可以加一层逻辑判断,当Page()里的data下的imgurl属性非空时,组件才会显示;空时就不显示。

1

2

3

<view wx:if="{{!!imgurl}}">

<image mode="widthFix" src="{{imgurl}}"></image>

</view>

 方法三:这个方法和方法二类似,设置一个img是否为空的变量,然后控制hidden属性

操作图片

使用wx.getImageInfo() 都可以获取到该图片的宽度、高度、路径、格式以及拍照方向。

获取图片信息wx.getImageInfo()

异步与同步

我们前面也提及过异步,那什么会有异步呢?因为JavaScript是单线程的编程语言,就是从上到下、一行一行去执行代码,类似于排队一样一个个处理,第一个不处理完,就不会处理后面的。但是遇到网络请求、I/O操作(比如上面的读取图片信息)以及定时函数(后面会涉及)以及类似于成功反馈的情况,等这些不可预知时间的任务都执行完再处理后面的任务,肯定不行,于是就有了异步处理

把要等待其他函数执行完之后,才能执行的函数(比如读取图片信息)放到回调函数里,先不处理,等图片上传成功之后再来处理,这就是异步。比如wx.showToast()消息提示框,可以放到回调函数里,当API调用成功之后再来显示提示消息。回调函数相当于是异步的一个解决方案。

预览图片wx.previewImage()

预览图片就是在新页面里全屏打开图片,预览的过程中用户可以进行保存图片、发送给朋友、放大等操作。可以预览一张照片或者多张照片。

wx.previewImage({current: '', // 当前显示图片的http链接urls: [] // 需要预览的图片http链接列表
})

 保存图片到相册:wx.saveImageToPhotosAlbum()

小程序不支持直接将网络图片保存到本地手机的系统相册,支持临时文件路径和小程序本地路径。

压缩图片:wx.compressImage()[kəm'pres]

上传文件

技术文档:wx.chooseMessageFile()

小程序支持上传其他文件,但是我们只能从客户端会话(会话框)里(也就是微信单聊、群聊的聊天记录里)选择其他格式的文件。

选择地理位置

技术文档:wx.chooseLocation()

模块化与格式化

模块化与引入模块

在实际开发中,日期、时间的处理经常会使用到,但是使用Date对象所获取到的时间格式与我们想要展现的形式是有非常大的差异的。这时我们可以把时间的处理抽离成为一个单独的 js 文件比如util.js(util是utility的缩写,表示程序集,通用程序等意思),作为一个模块。

把通用的模块放在util.js或者common.js,把util.js放在utils文件夹里等就跟把css放在style文件夹,把页面放在pages文件夹,把图片放在images文件夹里是一样的道理,尽管文件夹或文件的名称你可以任意修改,但是为了代码的可读性,文件结构的清晰,推荐大家采用这种一看就懂的方式。

使用开发者工具在小程序根目录新建一个utils文件夹,再在文件夹下新建util.js文件,在util.js里输入以下代码(也就是参考模板小程序的logs页面调用的util.js)

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

const formatTime = date => {

const year = date.getFullYear()  //获取年

const month = date.getMonth() + 1  //获取月份,月份数值需加1

const day = date.getDate()  //获取一月中的某一天

const hour = date.getHours() //获取小时

const minute = date.getMinutes()  //获取分钟

const second = date.getSeconds() //获取秒

return [year, month, day].map(formatNumber).join('/') + ' ' + [hour, minute, second].map(formatNumber).join(':')  //会单独来讲解这段代码的意思

}

const formatNumber = n => {  //格式化数字

n = n.toString() //将数值Number类型转为字符串类型,不然不能拼接

return n[1] ? n : '0' + n //三目运算符,如果字符串n第2位存在也就是为2位数,那么直接返回n;如果不存在就给n前面加0

}

module.exports = {  //模块向外暴露的对象,使用require引用该模块时可以获取

formatTime: formatTime,

formatNumber: formatNumber

}

我们再来在file.js里调用这个模块文件util.js,也就是在file.js的Page()对象前面使用require引入util.js文件(需要引入模块文件相对于当前文件的相对路径,不支持绝对路径)

1

const util = require('../../utils/util.js')

然后再在onLoad页面生命周期函数里打印看看这段时间处理的代码到底做了什么效果,这里也要注意调用模块里的函数的方式。

1

2

3

4

5

onLoad: function (options) {

console.log('未格式化的时间',new Date())

console.log('格式化后的时间',util.formatTime(new Date()))

console.log('格式化后的数值',util.formatNumber(9))

},

util.formatTime()就调用了模块里的函数,通过控制台打印的日志可以看到日期时间格式的不同,比如:

1

2

未格式化的时间 Mon Sep 02 2019 11:25:18 GMT+0800 (中国标准时间)

格式化后的时间 2019/09/02 11:25:18

显然格式化后的日期时间的展现形式更符合我们的日常习惯,而9这个数值被转化成了字符串”09″。那这段格式化日期时间的代码是怎么实现的呢?这里就涉及到高阶函数的知识,一般函数调用参数,而高阶函数会调用其他函数,也就是把其他函数作为参数。

map

相信格式化数字的代码比较好理解,如果是15日里的15,由于n[1]是15的第二位数字5,为true会直接return返回n,也就是15;比如9月里的数字9,n[1]不存在,也就是没有第二位数,于是执行 '0' + n给它加一个0,变成09;而formatNumber是一个箭头函数。

1

2

3

4

const formatNumber = n => {  //格式化数字

n = n.toString() //将数值Number类型转为字符串类型,不然不能拼接

return n[1] ? n : '0' + n //三目运算符,如果字符串n第2位存在也就是为2位数,那么直接返回n;如果不存在就给n前面加0

}

而格式化日期时间则涉及到map,比如下面的这段代码就有map,

1

return [year, month, day].map(formatNumber).join('/') + ' ' + [hour, minute, second].map(formatNumber).join(':')

map也是一个数据结构,它背后的知识非常复杂,但是我们只需了解它是做什么的就可以,如果你想对数组里面的每一个值进行函数操作并且返回一个新数组,那你可以使用map

上面这段代码就是对数组[year, month, day][hour, minute, second]里面的每一个数值都进行格式化数字的操作,这一点我们可以在file.js的onLoad里打印看效果就明白了

1

2

3

4

onLoad: function (options) {

console.log('2019年9月2日map处理后的结果', [2019,9,2].map(util.formatNumber))

console.log('上午9点13分4秒map处理后的结果', [9, 13, 4].map(util.formatNumber))

},

从控制台打印的结果就可以看到数组里面的数字被格式化处理,有两位数的不处理,没有2位数的前面加0,而且返回的也是数组。至于数组Array的join方法,就是将数组元素拼接为字符串,以分隔符分割,上面[year, month, day]分隔符为”/”,[hour, minute, second]的分隔符为”:”。

数据缓存Storage

技术文档:wx.setStorageSync()、wx.getStorageSync()

保存文件到本地缓存

注意:保存图片wx.saveImageToPhotosAlbum()保存文件wx.saveFile()是完全不同的概念,保存图片是把图片保存到手机本地相册;而保存文件则是把图片由临时文件(选择了图片以后会生成临时路径)移动到本地存储里,而本地存储每个小程序用户只有10M的空间。

技术文档:wx.saveFile()

在了解logs的数据缓存案例之前,我们先来看一个将上传的图片由临时文件保存到缓存的案例,使用开发者工具在file.wxml里输入以下代码:

1

2

3

4

5

6

<view>临时文件的图片</view>

<image mode="widthFix" src="{{tempFilePath}}" style="width:100px"></image>

<view>缓存保存的图片</view>

<image mode="widthFix" src="{{savedFilePath}}" style="width:100px"></image>

<button  bindtap="chooseImage">请选择文件</button>

<button  bindtap="saveImage">保存文件到缓存</button>

然后在file.js的data里初始化临时文件的路径tempFilePath和本地缓存的路径savedFilePath:

1

2

3

4

data: {

tempFilePath: '',

savedFilePath: '',

},

再在file.js里添加事件处理函数chooseImage和saveImage(函数名有别于之前的chooseImg和saveImg,不要弄混了哦):

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

chooseImage:function() {

const that = this

wx.chooseImage({

count: 1,

success(res) {

that.setData({

tempFilePath: res.tempFilePaths[0]

})

}

})

},

saveImage:function() {

const that = this

wx.saveFile({

tempFilePath: this.data.tempFilePath,

success(res) {

that.setData({

savedFilePath: res.savedFilePath

})

wx.setStorageSync('savedFilePath', res.savedFilePath)//个人觉得这句话的意思应该是把res.savedFilePath这个路径命名成savedFilePath,以便下次用

},

})

},

还没有完~我们还需要在file.js的onLoad生命周期函数里将缓存里存储的路径赋值给本地缓存的路径savedFilePath:

1

2

3

4

5

onLoad: function (options) {

this.setData({

savedFilePath: wx.getStorageSync('savedFilePath')

})

},

编译之后,点击请上传文件的button,会触发chooseImage事件处理函数,然后调用上传图片的API wx.chooseImage,这时会将图片上传到临时文件,并将取得的临时文件地址赋值给tempFilePath,有了tempFilePath,图片就能渲染出来了。

然后再点击保存文件到缓存的button,会触发saveImage事件处理函数,然后保存文件API wx.saveFile,将tempFilePath里的图片保存到缓存,并将取得的缓存地址赋值给savedFilePath(注意tempFilePath也就是临时路径是保存文件的必备参数),这时缓存保存的图片就渲染到页面了。然后会再来调用缓存API wx.setStorageSync(),将缓存文件的路径保存到缓存的key savedFilePath里面。有些参数名称相同但是含义不同,这个要注意

通过wx.setStorageSync()保存到缓存里的数据,可以使用wx.getStorageSync()获取出来,我们在onLoad里把获取出来的缓存文件路径再赋值给savedFilePath。

编译页面,看看临时文件与缓存文件的不同,临时文件由于小程序的编译会被清除掉,而缓存文件有10M的空间,只要用户不刻意删除,它就会一直在。

缓存的好处非常多,比如用户的浏览文章、播放视频的进度(看了哪些文章,给个特别的样式,免得用户不知道看到哪里了)、用户的登录信息(用户登录一次,可以很长时间不用再登录)、自定义的模板样式(用户选择自己喜欢的样式,下次打开小程序还是一样)、最经常使用的小图片(保存在缓存,下次打开小程序速度更快)等等。

logs的数据缓存处理

我们再回头看logs的缓存案例,在小程序app.js的生命周期函数onLaunch里输入以下代码,也就是在小程序初始化的时候就执行日志进行记录:

1

2

3

4

5

6

7

8

9

10

11

//  ||为逻辑与,就是声明logs为获取缓存里的logs记录,没有时就为空数组

var logs = wx.getStorageSync('logs') || []

//unshift()是数组的操作方法,它会将一个或多个元素添加到数组的开头,这样最新的记录就放在数组的最前面,

//这里是把Date.now()获取到的时间戳放置到数组的最前面

logs.unshift(Date.now())

//将logs数据存储到缓存指定的key也就是logs里面

wx.setStorageSync('logs', logs)

console.log(logs)

console.log(Date.now())

当我们不断编译,logs数组里面的记录会不断增加,增加的值都是时间戳。那如何把缓存里面的日志给渲染到页面呢?

在file.wxml里添加以下代码,由于logs是数组,我们使用列表渲染,这里有个数组的index值,由于index是从0开始记录,给index加1,符合我们日常使用习惯。

1

2

3

<view wx:for="{{logs}}" wx:for-item="log">

<view>{{index + 1}}. {{log}}</view>

</view>

然后在file.js的data里初始化logs

1

2

3

data: {

logs: []

},

然后再在file.js的生命周期函数onLoad里把缓存里的日志取出来通过setData赋值给data里的logs

1

2

3

4

5

6

7

onLoad: function () {

this.setData({

logs: (wx.getStorageSync('logs') || []).map(log => {

return util.formatTime(new Date(log))

})

})

},

缓存有同步API和异步API的区别,结合之前我们了解的同步和异步的知识,看看缓存的同步API与异步API的区别。

网络API

数据API

小程序以及很多程序的API是预先就已经写好的函数,使我们不需要对底层有太多了解,只需要按照技术文档进行传递参数就能调用出非常复杂的功能。而还有一类API则侧重于把数据资源给开放出来,我们可以通过HTTP的方式来使用这些数据。

  • 聚合API:一个比较全面的综合性API服务平台
  • 即速API:也是提供一些综合性的API服务
  • V2EX API:v2ex论坛是很多程序员经常会光顾的综合性技术论坛
  • CNode API:Nodejs交流论坛
  • 和风天气:含天气预报、空气质量、实况天气等数据
  • Github API:Github是所有程序员都(必须)会使用的网站
  • 知乎日报API:知乎日报API分析

渲染网络数据到页面

域名校验与白名单

小程序只可以跟指定的域名进行网络通信,以及这个域名必须是https及备案过的。

res对象和res.data对象

request以后,res.data的数据正是我们使用浏览器打开链接所得到的json数据。

技术文档:wx.request网络数据请求

  • statusCode:开发者服务器返回的 HTTP 状态码,也就是指示HTTP请求是否成功,其中200为请求成功404请求失败,更多状态码的知识可以查阅MDN HTTP响应代码
  • header:开发者服务器返回的 HTTP消息头,其中Content-Type为服务器文档的MIME 类型,API的MIME类型通常为 "application/json; charset=UTF-8",建议服务器返回值使用 UTF-8 编码(如果你有服务器的话)。
  • wx.request只能发起 HTTPS 请求,默认超时时间为60s,最大并发限制为10个

打开开发者工具调试工具栏的AppData标签页,就能看到从网络API里获取到的数据。也可以在此处编辑数据,并及时地反馈到界面上。如果AppData里有数据,可以确认页面已经取得res里的data数据,如果数据没有渲染到页面,说明列表渲染可能有误。通过这种方式可以诊断页面渲染问题所在。

HTML标签解析rich-text

有些API返回的数据中会包含HTML标签,这时只需要将富文本对象放在rich-text的nodes里,就能将富文本解析出来了,比如将上面的{{body}}替换成以下代码。

1

<rich-text nodes="{{body}}"></rich-text>

小程序富文本解析的方案还有:Comi ,腾讯 Omi 团队开发的小程序代码高亮和 markdown 渲染组件,Github地址,具体效果可以在微信小程序里搜索omiCloud;以及wxPrase,微信小程序富文本解析自定义组件,支持HTML及markdown解析,Github地址,当你遇到更加复杂的富文本解析时,可以来深入了解。

解构赋值

解构赋值也就是从数组Array和对象Object中提取值,按照对照的位置,分别对变量进行赋值。

1

2

3

4

let title = res.data.title

let body=res.data.body

let image=res.data.image

let share_url=res.data.share_url

可以简写成:

1

let { title, body, image, share_url}=res.data

调用API的AppID的存放

方法一:写在app.js里的globalData,或者新建一个keyData对象,只要达到全局调用的目的都可以,以globalData为例

1

2

3

globalData: {

juheKey:"366444.......00ff", //聚合AppKey

},

这种方式调用时首先在页面的js文件里、Page()函数的前面使用

1

const app=getApp()

之后就可以使用app.globalData.juheKey来调用它了。

方法二:也可以在小程序的根目录或者utils文件夹新建一个config.js,然后结合前面模块化的知识,写入以下代码:

1

2

3

module.exports = {

juheKey:"366444.......00ff", //聚合AppKey

}

这种方式调用时我们需要先在页面的Page()函数前面引入模块化文件

1

const key = require('../../utils/config.js')

然后就可以使用key.juheKey调用它了。

模板字符串

1

url: "http://api.juheapi.com/japi/toh?" + "month=" + month + "&day=" + day + "&key=" + app.globalData.juheKey,

要将多个字符串连接起来,可以使用加号+来用作字符串的拼接,如果变量比较多,是不是很麻烦?我们还可以使用模板字符串,模板字符串使用反引号 ``来表示(在电脑键盘esc按键下面)。要在模板字符串中嵌入变量,需要将变量名写在${}之中,比如上面的链接也可以写成:

1

url: `http://api.juheapi.com/japi/toh?month=${month}&day=${day}&key=${app.globalData.juheKey}`,

encodeURI与decodeURI

在浏览网页的时候我们经常看到汉字或一些字符变成了一个“乱码”,原因就在于链接进行了编码处理。encodeURI() 函数可把字符串作为 URI 进行编码,而decodeURI()函数则可以进行解码。

在开发者工具的控制台里输入以下代码

1

2

3

console.log(encodeURI("北京"))

console.log(decodeURI("%e9%85%92%e5%ba%97"))

console.log(decodeURI("https://hackwork.org/handbook/python/174/%e5%86%99%e5%87%ba%e7%ac%ac%e4%b8%80%e8%a1%8cpython%e4%bb%a3%e7%a0%81/"))

md5加密算法

MD5是计算机安全领域广泛使用到的一种加密算法,主要用于确保消息传输的完整一致

md5依赖:md5开源项目下载链接

解压之后,将js文件夹里的md5.min.js复制粘贴到小程序utils文件夹下。然后再在Page()前面引入这个文件

1

const md5 = require('../../utils/md5.min.js')

小程序云开发学习笔记(二)相关推荐

  1. 微信小程序云开发 mysql_微信小程序云开发学习笔记(一)云数据库

    云开发配置的环境:cloud-learning 云开发环境初始化准备 需要: APPID 操作: 在创建项目时,填入APPID并选择不使用云函数 进入到开发者页面,点击左上角的云开发并选择开通 设置云 ...

  2. 微信小程序云开发学习笔记(二)云函数

    一.创建一个云开发的小程序 1. 如下图所示创建小程序 2. 删除无关文件 删除前: 删除后: 3. 删除无关代码 index.wxml index.wxss 内的代码全部清空,index.js保留如 ...

  3. 微信小程序云开发学习笔记

    (官网) 1.创建小程序选择云开发 对应的环境 在app.js中配置环境Id //app.js App({onLaunch: function () {if (!wx.cloud) {console. ...

  4. 微信小程序云开发学习笔记No.03——(文件存储)

    上传文件 wx.cloud.uploadFile //回调风格的API上传文件,会返回一个uploadTask对象 const uploadTask = wx.cloud.uploadFile({cl ...

  5. 微信小程序云开发入门(二)-数据库详解

    微信小程序云开发入门(二)-数据库详解 接上一篇:微信小程序云开发入门(一) 摘要: 因为微信小程序云数据库有点类似传统的关系型数据库,但又有所不同.所以刚入手的时候会有点困扰,经过一段时间的学习和摸 ...

  6. 2021微信小程序云开发学习路线【视频加笔记】

    从零开始学习小程序开发,小程序云开发的学习资料和路线!!想入门小程序请看下面内容~ 包括:云数据库,云函数,云托管,小程序云开发和服务器开发的项目实战,零基础学习JavaScript,小程序语法,cm ...

  7. 微信小程序--云开发学习

    这两周因为没有布置任务,主要进行微信小程序云开发的学习 ················· 开发者可以使用云开发开发微信小程序.小游戏,无需搭建服务器,即可使用云端能力. 云开发为开发者提供完整的原生 ...

  8. 微信小程序云开发学习

    1.参考视频教程:微信小程序云开发基础到实战 云数据库 云函数 云存储 云调用[新视觉]_哔哩哔哩_bilibili 2.参考笔记: 本来想自己做一份笔记的,后来发现已经有人做了份非常不错的笔记了,再 ...

  9. 【微信小程序云开发】笔记

    获取AppID 云开发必须要有AppID,去链接(https://mp.weixin.qq.com/wxopen/waregister?action=step1)使用邮箱进行注册,注册后使用AppID ...

最新文章

  1. node.js 原型污染攻击的分析与利用
  2. iphone全部机型_【每日一技】iPhone重启手机和关机后开机有什么区别
  3. Oracle varchar类型数值排序问题
  4. TRUNCATE TABLE 与 DELETE在删除整个表的所有记录时的区别
  5. 【李宏毅2020 ML/DL】P56 Transformer and its variant | New Architecture
  6. oracle union orderby,Oracle UNION和ORDER BY的奇怪问题
  7. WPF实现竖向排列并换行显示
  8. ngrinder安装
  9. 第五课 基本数据类型
  10. HDU 6191 2017广西邀请赛Query on A Tree:可持久化01字典树(区间抑或最大值查询)
  11. 服务器无线网卡禁止怎么办,台式机无线网卡被禁用了如何解决
  12. 【学习笔记】欧拉筛法(线性筛素数)
  13. 今日学习在线编程题:小码哥的烦恼
  14. 如何在python3.x中使用pip3安装you-get及下载网页视频资源
  15. 微信相关账号需要哪些资质
  16. 客户端与服务器端通信方式总结
  17. 如何实现单片机按键长按和短按功能
  18. 微信公众平台开发-分享接口的实现
  19. ABAP 如何发布odata服务
  20. 谷歌浏览器下载完整版(非在线下载安装)

热门文章

  1. 虚言妙诀终虚见,面试躬行是致知,Python技术面试策略与技巧实战记录
  2. 吉米_王:MySQL开启log_bin日志,误删数据库时你最后的救命稻草
  3. 骨灰级的魔兽伤害计算(包括物理和…
  4. 步进电机(四相五线为例子)步进角度和工作原理介绍
  5. cursor的所有样式
  6. 中国提取市场趋势报告、技术动态创新及市场预测
  7. macos 切换账户_如何在macOS上设置访客用户帐户
  8. VMware下OSSIM 5.2.0的下载、安装和初步使用(图文详解)
  9. IOS微信分享,或者app内分享微信图片不显示
  10. 卷积神经网络图像分类的性能评估指标有哪些