数据和文件是小程序开发非常重要的元素,在前面的章节里,数据和文件等的存储都是在小程序的页面进行渲染、或是页面间传递或与本地手机交互。这一节我们会来介绍数据、文件如何与网络 HTTP 进行数据、文件的对话,如何获取并上传网络数据和文件。
小程序以及很多程序的 API 是预先就已经写好的函数,使我们不需要对底层有太多了解,只需要按照技术文档进行传递参数就能调用出非常复杂的功能。而还有一类 API 则侧重于把数据资源给开放出来,我们可以通过 HTTP 的方式来使用这些数据。
复制以下链接地址,用浏览器打开,看看会返回什么结果:
//知乎日报的最新话题
https://news-at.zhihu.com/api/4/news/latest
//知乎日报某一个话题的内容
https://news-at.zhihu.com/api/4/news/9714883
//v2ex论坛的最新主题
https://www.v2ex.com/api/topics/latest.json
//CNode论坛的最新话题
https://cnodejs.org/api/v1/topics
以上所返回的数据类型都是 json 格式,相信大家应该比较熟悉了。那我们如何把以上数据渲染到我们的小程序页面上呢?
数据是一种资源,比如新闻资讯、电商商品、公众号文章、股市行情、空气质量和天气、地图、词典翻译、快递信息、书籍信息、音乐视频、财务公司信息等等这些都是数据,数据也是一种商品,一种服务,通常它的使用对象是开发者,有些免费,有些也会收取一定的费用,大家可以通过综合性API服务平台聚合API来对API服务有一个基础的了解。
这里推荐几个程序员经常会拿来练手的 API 资源,你可以使用这些 API 来做网站、小程序、移动端(iOS、安卓)、桌面端,也可以用于各种框架比如 Vue、React、Flutter 等等,数据没变,只是解决方案不同。
各大公司的开发平台:比如微信开放平台 提供了微信账号体系的接入,腾讯云API中心 则提供了调用云资源的能力(包含服务器、物联网、人工智能等API)、开源网站Wordpress 也提供API调用的服务,在API资源的开放方面,国外也做得比较领先(国外免费API列表 )。而对于特定的数据资源,也可以通过爬虫等方式来自建。
要渲染从 API 里获取到的数据,首先我们需要对 API 里的字段(属性)到底是干什么的要有一定的了解。比如知乎日报的API字段如下,这个可以从 API 相关的文档里了解到以及需要我们结合 Console.log
来对比了解。
比如
date
: 日期;stories
: 当日新闻;title
: 新闻标题;images
: 图像地址;id
: url
与 share_url
中最后的数字为内容的 id
;top_stories
: 界面顶部轮播的显示内容。 这些在做数据渲染前就需要有所了解。使用开发者工具新建一个 request
页面,然后在 request.js
里的 onLoad
生命周期函数里输入以下代码:
onLoad: function (options) {
wx.request({
url: 'https://news-at.zhihu.com/api/4/news/latest', //知乎日报最新话题
header: {
'content-type': 'application/json' // 默认值
},
success(res) {
console.log('网络请求成功之后获取到的数据',res)
console.log('知乎日报最新话题',res.data)
}
})
},
编译之后,在控制台 Console 你会看到如下报错,你的域名不在域名白名单里面,这是因为小程序只可以跟指定的域名与进行网络通信。
request:fail url not in domain list
解决方法有两种,一是打开开发者工具工具栏右上角的详情,勾选不校验合法域名、业务域名、TLS版本以及HTTPS证书;二是你可以去小程序的管理后台(注册小程序时的页面),点击开发–开发设置,在request合法域名处添加该域名(如果你不想把这个小程序发布上线,没有必要添加)。
编译之后,在控制台 Console 就可以看到打印的 res 对象,以及 res 里的 data 对象。res.data
的数据正是我们使用浏览器打开链接所得到的 json
数据,结合我们之前学到的数据渲染方面的知识,相信大家应该对如何将数据渲染到页面就不会感到陌生了。
在打印的res对象有一些参数,比如cookies
、header
、statusCode
这些是什么意思呢?我们可以来结合技术文档深入了解。
技术文档: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个
小任务:把request
的链接换成 v2ex、cnode 论坛的 API 链接以及知乎日报某一个话题的内容 API,看看是什么结果?你知道返回来的 json 数据的每一条属性代表的意思吗?
既然我们已经从知乎日报的 API 取得了数据,那渲染数据的方法以及如何实现跨页面渲染,在前面的章节我们已经就有所了解了。
使用开发者工具在 request.wxml
里输入 weui
的列表样式(需要引入 weui
框架哦)
<view class="page__bd">
<view class="weui-panel weui-panel_access">
<view class="weui-panel__bd" wx:for="{{stories}}" wx:for-item="stories" wx:key="*item">
<navigator url="" class="weui-media-box weui-media-box_appmsg" hover-class="weui-cell_active">
<view class="weui-media-box__hd weui-media-box__hd_in-appmsg">
<image class="weui-media-box__thumb" mode="widthFix" src="{{stories.images[0]}}" sytle="height:auto"></image>
</view>
<view class="weui-media-box__bd weui-media-box__bd_in-appmsg">
<view class="weui-media-box__title">{{stories.title}}</view>
</view>
</navigator>
</view>
</view>
</view>
然后再在 request.js
的 data
里声明 date
、stories
、top_stories
的初始值(使用的变量和 API 的字段尽量保持一致,这样就不容易混乱)
data: {
date:"",
stories:[],
top_stories:[],
},
在 onLoad 生命周期函数里将数据通过 setData 的方式给赋值给 data:
onLoad: function (options) {
let that=this
wx.request({
url: 'https://news-at.zhihu.com/api/4/news/latest',
header: {
'content-type': 'application/json'
},
success(res) {
let date=res.data.date
let stories=res.data.stories
let top_stories = res.data.top_stories
that.setData({
date,stories,top_stories
})
}
})
},
编译之后,我们就能看到知乎日报的数据就渲染在页面上了。
小任务: top_stories
是界面顶部轮播的显示内容,制作一个 swiper 轮播,将 top_stories
里的内容渲染到轮播上。
打开开发者工具调试工具栏的 AppData 标签页,就能看到从网络 API 里获取到的数据。也可以在此处编辑数据,并及时地反馈到界面上。如果 AppData 里有数据,可以确认页面已经取得 `res` 里的 `data` 数据,如果数据没有渲染到页面,说明列表渲染可能有误。通过这种方式可以诊断页面渲染问题所在。
前面我们获取的只是知乎的最新文章列表,那文章里面的内容呢?通过 API 文档以及我们通过链接访问的结果来看,我们只需要取得了文章的 ID,就能从 API 里获取到文章的详情页内容:
https://news-at.zhihu.com/api/4/news/9714883 //9714883是文章的ID
使用开发者工具新建一个 story
页面,然后在 story.wxml
里输入以下代码:
<view class="page__bd">
<view class="weui-article">
<view class="weui-article__h1">{{title}}</view>
<view class="weui-article__section">
<view class="weui-article__section">
<view class="weui-article__p">
<image class="weui-article__img" src="{{image}}" mode="widthFix" style="width:100%" />
</view>
<view class="weui-article__p">
{{body}}
</view>
<view class="weui-article__p">
知乎链接:{{share_url}}
</view>
</view>
</view>
</view>
</view>
然后再在 request.js
的 data
里声明 title
、body
、image
、share_url
的初始值:
data: {
title:"",
body:"",
image:"",
share_url:"",
},
在 onLoad 生命周期函数里调用 wx.request
获取文章详情页的数据,并通过 setData
的方式给赋值给 data
:
onLoad: function (options) {
let stories_id=9714883
let that = this
wx.request({
url: 'https://news-at.zhihu.com/api/4/news/'+stories_id,
header: {
'content-type': 'application/json'
},
success(res) {
let title = res.data.title
let body=res.data.body
let image=res.data.image
let share_url=res.data.share_url
that.setData({
title,body,image,share_url
})
}
})
编译之后,发现数据虽然渲染出来了,但是存在“乱码”(是 HTML 标签),那这个要如何处理呢?这个就涉及到小程序的富文本解析了。
只需要将富文本对象放在 rich-text
的 nodes
里,就能将富文本解析出来了,比如将上面的{{body}}
替换成以下代码。
<rich-text nodes="{{body}}"></rich-text>
小程序富文本解析的方案还有:Comi ,腾讯 Omi 团队开发的小程序代码高亮和 markdown 渲染组件,Github 地址,具体效果可以在微信小程序里搜索 omiCloud;以及wxPrase,微信小程序富文本解析自定义组件,支持 HTML 及 markdown 解析,Github地址,当你遇到更加复杂的富文本解析时,可以来深入了解。
上面我们只是渲染了单篇文章的详情页,那如何点击文章列表就能渲染与之相应的文章详情页呢?这就回到了我们之前学过的跨页面数据渲染。
首先把 request
页面置于首页,然后再给 request.wxml
里的 navigator
组件的链接上携带文章的 id:
url="/pages/story/story?id={{stories.id}}"
当点击 request
页面的链接时,链接携带的数据就会传到 story
页面的生命周期函数 onLoad
的 options
对象里,将 options
里的 id
,赋值给stories_id
,也就是将文章 id 9714883
修改为 options.id
let stories_id=options.id
这样再来点击 request
页面的链接,不同的链接就会渲染不同的文章详情。
解构赋值也就是从数组 Array 和对象 Object 中提取值,按照对照的位置,对变量进行赋值。比如上面的变量声明,为了能够与 API 里的数据字段一一对应,我们会声明很多变量,知乎日报的 API 还算比较少的,多了就比较复杂了。
let title = res.data.title
let body=res.data.body
let image=res.data.image
let share_url=res.data.share_url
这时可以简写成:
let { title, body, image, share_url}=res.data
知乎日报的 API 是比较开放的,并不需要我们去注册 API 服务就能获取到这些数据,但是大多数情况下,API 是商品服务,需要我们注册,那需要注册的 API 和开放的 API 有什么不同呢?
注册聚合API 并认证,认证之后可以申请开通历史上的今天、图书电商数据等免费的 API 服务,并找到你的与之对应的 AppKey。
替换下面链接你的历史上的今天对应的 key(直接输 AppKey 就行),然后在浏览器打开链接(下面这个是 1.0版)
http://api.juheapi.com/japi/toh?month=9&day=15&key=你的历史上的今天对应的key
也可以选择事件列表的 2.0版(为了讲解方便,下面以1.0版本为主)
http://v.juhe.cn/todayOnhistory/queryEvent.php?date=9/15&key=你的历史上的今天对应的key
通常我们会把拿到的key
放在app.js
的globalData
里,或者在小程序里新建一个config.js
,方便以后全局调用,而不是把key
直接写在页面里。
方法一:写在app.js
里的globalData
,或者新建一个keyData
对象,只要达到全局调用的目的都可以,以globalData
为例
globalData: {
juheKey:"366444.......00ff", //聚合AppKey
},
这种方式调用时首先在页面的js
文件里、Page()
函数的前面使用
const app=getApp()
之后就可以使用app.globalData.juheKey
来调用它了。
方法二:也可以在小程序的根目录或者utils
文件夹新建一个config.js
,然后结合前面模块化的知识,写入以下代码:
module.exports = {
juheKey:"366444.......00ff", //聚合AppKey
}
这种方式调用时我们需要先在页面的Page()
函数前面引入模块化文件
const key = require('../../utils/config.js')
然后就可以使用key.juheKey
调用它了。
将一些通用的数据、函数单独拿出来存放在globalData
里或进行模块化,是在实际开发中会经常使用到的一种方法,它可以让数据、函数更容易管理以及可以重复利用,使得代码更加精简。
使用开发者工具新建一个apidata
页面,然后在apidata.js
的Page()
函数前面输入以下代码:
const app=getApp()
const now = new Date();
const month = now.getMonth()+1 //月份需要+1
const day = now.getDate()
然后再在生命周期函数onLoad
里输入wx.request
数据请求:
onLoad: function (options) {
wx.request({
url: 'http://api.juheapi.com/japi/toh',
data: {
month: month,
day: day,
key: app.globalData.juheKey,
},
header: {
'content-type': 'application/json'
},
success(res) {
console.log(res.data)
}
})
},
wx.request
里的data
就是要传入的参数,我们把month
、day
、key
传入到请求的链接里。它等价于以下链接(注意把data
里的属性值,以免传两次参数)
url: "http://api.juheapi.com/japi/toh?" + "month=" + month + "&day=" + day + "&key=" + app.globalData.juheKey,
要将多个字符串连接起来,可以使用加号+来用作字符串的拼接,如果变量比较多,是不是很麻烦?我们还可以使用模板字符串,模板字符串使用反引号 ``来表示(在电脑键盘esc按键下面)。要在模板字符串中嵌入变量,需要将变量名写在 ${}之中,比如上面的链接也可以写成:
url: `http://api.juheapi.com/japi/toh?month=${month}&day=${day}&key=${app.globalData.juheKey}`,
在控制台我们就可以看到获取到的res.data
数据,至于如何渲染到页面,这里就不多介绍了。
注册和风天气 ,并在控制台的应用管理新建一个应用,获取到该应用的 key
,按照上面的方法将 key
添加到 globalData
里:
globalData: {
heweatherKey:"732c.........0b", //和风天气key
}
通过技术文档我们可以了解到免费版和风天气 API 的必备字段为weather-type
(根据不同的值可以取得不同的数据)和请求参数(其中location
为必备参数)
技术文档:和风常规天气数据API
也就是我们可以通过链接可以获取到数据,注意 now
在问号 ?
的前面,也就是它不是请求的参数, location
和 key
才是。
https://free-api.heweather.net/s6/weather/now?location=beijing&key=你的key
然后再在apidata.js Page()
的data
里初始化声明weathertype
(属性名最好不要有连接符-)和location
:
data: {
weathertype:"now",
location:"beijing" //location的写法有很多种,具体可以参考技术文档
},
然后再在生命周期函数里添加wx.request
请求(onLoad
里可以写多个wx.request
请求)
const weathertype=this.data.weathertype
wx.request({
url: `https://free-api.heweather.net/s6/weather/${weathertype}`,
data: {
location: that.data.location,
key: app.globalData.heweatherKey,
},
header: {
'content-type': 'application/json'
},
success(res) {
console.log(res.data)
}
})
},
在控制台就能看到请求到的res.data
了。如果你想点击按钮切换不同城市以及不同的天气数据类型,结合前面所学的知识,我们只需要通过事件处理函数调用setData
修改weathertype
和location
即可。
在浏览网页的时候我们经常看到汉字或一些字符变成了一个“乱码”,原因就在于链接进行了编码处理。encodeURI()
函数可把字符串作为 URI
进行编码,而decodeURI()
函数则可以进行解码。
在开发者工具的控制台里输入以下代码
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/"))
如果想在小程序中调用地图的 POI 检索(POI,即兴趣点 Point of Interest
,区域内搜索酒店、学校、ATM 等)、 关键词输入提示、地址解析、逆地址解析、行政区划、距离计算、路径规划等数据服务,这时候就需要使用到地图类相关的 API。
地图API:腾讯LBS位置服务
首先在注册后登录,点击控制台 —key 管理—创建新密钥,然后取得 key,key 的格式类似于“43UBZ-----HTBIA”。
然后点击当前 Key 的设置,启动产品里勾选微信小程序和 WebServiceAPI 里的签名校验,获取到地图的 Secret key。这两种 API 的调用方式,小程序都支持。
然后将地图的两个 key,写入到globalData
里
globalData: {
mapKey:"43UBZ-*****-IITUH-*****-2M723-******",//你的key
mapSecretKey:"spZwWz**********Xh20uW", //你的Secret key
}
在WebServiceAPI Key配置 中签名校验里提到我们使用WebServiceAPI
的方法需要对请求路径+”?
”+请求参数+SK
进行拼接,并计算拼接后字符串md5
值,即为签名(sig)。 MD5
是计算机安全领域广泛使用到的一种加密算法,主要用于确保消息传输的完整一致。
解压之后,将 js 文件夹里的md5.min.js
复制粘贴到小程序 utils 文件夹下。然后再在Page()
前面引入这个文件
const md5 = require('../../utils/md5.min.js')
坐标的逆解析就是坐标(latitude
,longitude
)转化为详细的地址名。
技术文档:坐标的逆地址解析
再在apidata.js Page()
的 data 里初始化声明latitude
,longitude
,比如我们用腾讯大厦的经纬度值:
data: {
latitude:"22.540503",
longitude: "113.934528",
},
然后在 onLoad 函数里调用 wx.request
发起 HTTPS 网络请求
onLoad: function (options) {
let that=this
const { latitude, longitude } = that.data
const { mapKey, mapSecretKey}=app.globalData
let SIG = md5("/ws/geocoder/v1?key=" + mapKey + "&location=" + latitude + "," + longitude + mapSecretKey)
wx.request({
url: 'https://apis.map.qq.com/ws/geocoder/v1',
data: {
key: mapKey,
location: `${latitude},${longitude}`,
sig: SIG
},
header: {
'content-type': 'application/json'
},
success(res) {
console.log(res.data)
}
})
},
在控制台 Console 就可以看到当前坐标(latitude
,longitude
)逆解析出来的详细信息。
小程序使用腾讯地图位置服务,还有一种更加简单的方法,具体可以阅读《微信小程序:个性地图使用指南》