# 联网
在js环境中,服务器下发的字符串,通过uni.request获取后,会默认转成json对象,开发者可以通过`.操作符`获取对象的属性,绑定到界面上。
```js
// js写法
// 假使服务器返回的json数据是:{code:0,data:[{"plugin_name":"插件名称A"}]}
// 这是一个对象,拥有code、data这2个子属性,data属性的值是一个数组,该数组内又有若干子对象,子对象有属性`plugin_name`。
uni.request({
url: "https://ext.dcloud.net.cn/plugin/uniappx-plugin-list",
success: (res) => {
let resData = res.data.data
console.log(resData[0].plugin_name) // resData是一个js对象数组,通过下标拿到第一个对象,然后通过.来访问对象的plugin_name属性
}
})
```
但在uts等强类型语言中无法这样,会报resData[0]无法安全访问、没有plugin_name属性,因为resData是个可为空的any类型,你确实没有为它定义过任何属性。
在uts中,提供了2种方案:
1. 使用[UTSJSONObject](../uts/data-type.md#utsjsonobject),不需要提前为json数据定义类型,在使用中通过下标访问并动态转换类型
2. 使用[type](../uts/data-type.md#type),提前定义json数据类型,在request时通过泛型传入类型,拿到的就是一个有类型的对象,之后的用法和js一样
## 方式1:UTSJSONObject
UTSJSONObject是uts的内置对象,它无法使用`.操作符`,但可以用下标和keypath来访问json数据。
### UTSJSONObject下标方式
```ts
// uts写法
// 假使服务器返回的json数据是:{code:0,data:[{"plugin_name":"插件名称A"}]}
uni.request({
url: "https://ext.dcloud.net.cn/plugin/uniappx-plugin-list",
success: (res) => {
console.log(res.data) // 这是一个any类型,不能直接使用
console.log(res.data as UTSJSONObject) // 需要把这个any类型,as成UTSJSONObject类型
console.log(res.data as UTSJSONObject["data"]) //UTSJSONObject支持通过下标访问属性data,但返回的仍然是any类型
let resData = (res.data as UTSJSONObject)["data"] as UTSJSONObject[] // as成UTSJSONObject数组
if (resData!=null) {
console.log((resData[0]) // 访问数组的第一个数组项目,仍然是any类型,仍然需要转换
console.log((resData[0] as UTSJSONObject)["plugin_name"]) // 转为UTSJSONObject后通过下标访问plugin_name属性
}
}
})
```
上面代码中打印日志部分是为了方便初学者理解,实际开发时代码行数不会多几行,主要是多几次as做类型转换。
### UTSJSONObject keypath方式
上面的写法是每层数据都as,通过下标来访问。UTSJSONObject还支持keypath,这是一种可穿透多层数据访问的写法,传入一个path字符串,返回相应数据。
```ts
// uts写法
// 假使服务器返回的json数据是:{code:0,data:[{"plugin_name":"插件名称A"}]}
uni.request({
url: "https://ext.dcloud.net.cn/plugin/uniappx-plugin-list",
success: (res) => {
let resData = res.data as UTSJSONObject
if (resData!=null) {
console.log(resData.getString("data[0].plugin_name")) //直接访问data属性的第一个数组项目里的plugin_name属性
}
}
})
```
除了getString,还有getNumber、getBoolean、getJSON、getArray、getAny。只要keypath的路径输入正确、类型正确,就可以取得值。当然path没有代码提示。
更多详见[UTSJSONObject](../uts/data-type.md#utsjsonobject)
## 方式2:type和泛型
为json数据定义一个type的自定义类型,明确好对象的属性名称及类型,然后把这个type通过泛型传给request方法,res拿出来的就是转换好的类型,就可以直接`.操作符`获取属性了。。
- 第一步:用HBuilderX自带的工具,给服务器数据定义类型
把服务器端返回的data里的json数据,复制黏贴到HBuilderX的json编辑器里,点右键,转type。
![](../static/json2type.png)
> 转换功能需要HBuilderX 3.9+、安装了uni-app x真机运行插件、且打开的标签卡是json编辑器。(在ctrl+t新建空白md标签卡,粘贴json数据,会自动切换成json编辑器)
上面的截图是复用了其他图片,如果我们使用`https://ext.dcloud.net.cn/plugin/uniappx-plugin-list`这个服务器接口,服务器返回的数据内容,也就是`res.data`的数据是这样:
```json
{code:200,desc:"",data:[{"plugin_name":"插件名称A","plugin_id":123}]}
```
**注意:** 不要把res和res.data搞错了,要看`res.data`的数据格式,而不是`res`的格式。`res`的格式是不变的。
观察返回的数据,`res.data`返回一个对象,拥有code、desc、data这3个子属性,code是number类型,desc是string类型,而data的类型又是一个数组。
该数组内又是若干子对象,子对象有属性`plugin_id`和`plugin_name`等。
那么使用转换工具,生成的类型定义是这样:
```ts
type Data = {
plugin_id: number;
plugin_name: string;
}
type IRootType = {
code: number;
desc: string;
data: Data[];
}
```
因type不可嵌套,生成了2个type。注意顺序,Data这个type需写在前面,因为后面要引用它。引用代码执行时如未定义该类型,会报错。
- 第二步:把这段类型定义,放在`
```
与UTSJSONObject方式相比,type方式在使用数据时可以使用`.操作符`,有代码提示。虽然需要定义type,但由于有工具可以自动生成type,所以整体使用体验,比UTSJSONObject方式方便一点。
type+泛型这个方式,也是ts开发者惯用的方式。
但不熟悉ts的开发者,可能不了解type和泛型。下面讲解下。
### type和泛型详解
- 什么是type和泛型?
type就是自定义一个类型。下面定义了一个数据类型DataType,该类型有2个属性,`plugin_id`和`plugin_name`,这2个属性的类型分别是number和string。
```ts
type DataType = {
plugin_id : number,
plugin_name : string
}
```
有了这个类型,再给它json数据进行实例化,就达到了给json数据定义类型的目标。给json数据定义好类型,就可以自由的使用`.操作符`获取属性了。
详见[type](../uts/data-type.md#type)
而泛型,是一个对方法参数进行通用的类型描述。它告诉一个支持泛型的方法,给方法传入什么类型,方法就会返回什么类型。
不过uts的泛型支持还没有达到ts的泛型完善度,详见[泛型](../uts/generics.md)
uni.request方法是支持泛型的,这意味着返回结果可以有很多种类型。
所以可以把你定义的DataType类型通过``的方式传给uni.request方法,尖括号要写在方法名和左圆括号中间。
这个方法就会把返回的`res.data`转换为你传入的DataType类型。
- 为何type要定义在export default{}之前?
其实如果type不用于vue data数据的类型,那么type只需要定义在执行前就可以。
但实际开发中,type大多用于data的类型定义,而想给data定义类型,那就得写在data的前面,也就是export default{}之前了。
- 网络数据缺少一些属性怎么办?
网络返回的数据,有可能缺少某些属性,导致报错。测试需要把可为空的属性类型声明为"或null",或者使用`?:`
```ts
type DataType = {
plugin_id : number,
plugin_name : string,
age : number | null, //属性可为null
}
```
如您不了解null的安全使用,[详见](../uts/data-type.md#null)
- 务必注意res.data返回的是对象还是数组
如果res.data返回的是对象,那么泛型调用时直接`uni.request(`
如果res.data返回的是数组,那么传泛型时必须传入数组格式`uni.request(`,注意多了一个`[]`
- type的敏感词转义
服务器返回的json数据,其属性键名有可能存在一些type不支持的字符。
比如`{"a:b":"123","a-b":"456"}`,这些键名对于type来讲都是非法的。转换type就会失败。
hx的json转type工具,会对一些敏感符合和关键字自动转义。但也有无法转移的符号和词,详见:[type](../uts/data-type.md#json-field)
如果你的服务器数据涉及这类问题且数据格式不可改,那只能改用UTSJSONObject方式。
### 完整实例
再举一个实际中更常见的例子。联网获取插件市场的插件列表数据,并绑定在模板上,还可以翻页。
翻页需要用到[...展开操作符](../uts/operator.md#展开语法)
```vue
{{item.plugin_name}}
{{loadingText}}
```
## 注意事项
* request 接口内部通过[特殊方式读取了范型类型](../uts/generics.md#使用限制),不支持传入动态的范型:比如将外层方法的普通范型参数传入 request。所以目前尽量直接使用uni.request而不是封装。如确需封装,可不使用type,改用UTSJSONObject。
* web端request接口目前没有返回UTSJSONObject类型数据而是一个普通对象,后续可能会调整为UTSJSONObject类型,如需兼容多端可以使用下标获取request返回的数据内容。
* web端request接口目前不支持创建传入的泛型的实例