提交 bb6a05c0 编写于 作者: W wanganxp

x

上级 7e17f1ed
...@@ -196,20 +196,20 @@ json的条件编译,如不同平台的key名称相同,cli项目下开发者 ...@@ -196,20 +196,20 @@ json的条件编译,如不同平台的key名称相同,cli项目下开发者
在不同平台,引用的静态资源可能也存在差异,通过 static 的条件编译可以解决此问题,static 目录下新建不同平台的专有目录,目录名称均为小写, 在不同平台,引用的静态资源可能也存在差异,通过 static 的条件编译可以解决此问题,static 目录下新建不同平台的专有目录,目录名称均为小写,
|目录名称|说明|版本支持| |目录名称 |说明 |版本支持 |
|:-:|:-:|:-:| |:-: |:-: |:-: |
|app或app-plus|App|| |app-plus |app(推荐使用`app`) | |
|h5|H5(推荐使用`web`| |app |app |uni-app 3.9+ |
|web|Web(同`H5`,HBuilderX3.9.0+)|| |h5 |H5(推荐使用`web`) | |
|web|H5|HBuilderX 3.9.0+| |web |web |uni-app 3.9+ |
|mp-weixin|微信小程序|| |mp-weixin |微信小程序 | |
|mp-alipay|支付宝小程序|| |mp-alipay |支付宝小程序 | |
|mp-baidu|百度小程序|| |mp-baidu |百度小程序 | |
|mp-qq|QQ小程序|| |mp-qq |QQ小程序 | |
|mp-toutiao|抖音小程序|| |mp-toutiao |抖音小程序 | |
|mp-lark|飞书小程序|| |mp-lark |飞书小程序 | |
|mp-kuaishou|快手小程序|| |mp-kuaishou|快手小程序 | |
|mp-jd|京东小程序|| |mp-jd |京东小程序 | |
专有目录下的静态资源只有在特定平台才会编译进去。 专有目录下的静态资源只有在特定平台才会编译进去。
...@@ -230,7 +230,7 @@ json的条件编译,如不同平台的key名称相同,cli项目下开发者 ...@@ -230,7 +230,7 @@ json的条件编译,如不同平台的key名称相同,cli项目下开发者
**注意** **注意**
- 自HBuilderX3.9.0+起,App平台static目录同时支持app、app-plus目录,Web平台static目录同时支持web、h5目录 - 自HBuilderX3.9+起,App平台static目录同时支持app、app-plus目录,Web平台static目录同时支持web、h5目录
### 整体目录条件编译 ### 整体目录条件编译
...@@ -280,12 +280,27 @@ json的条件编译,如不同平台的key名称相同,cli项目下开发者 ...@@ -280,12 +280,27 @@ json的条件编译,如不同平台的key名称相同,cli项目下开发者
::: :::
### 编译器版本的条件编译@uniVersion ### 版本的条件编译@uniVersion
> HBuilderX 3.9+
插件作者的插件,需要适配各种插件使用者,使用者的uni-app版本,可能有很多。
有些问题可以在运行期判断适配,有些则需要在编译器处理,尤其是不处理可能会导致低版本编译失败的情况。
为此,uni-app的条件编译新增了`uniVersion`。在uni-app和uni-app x中均可使用。
<pre v-pre="" data-lang="javascript"><code class="lang-javascript code"><span class="token comment">//<span style="color:#859900;"> #ifdef</span><b style="color:#268BD2"> uniVersion > 3.9</b></span> <pre v-pre="" data-lang="javascript"><code class="lang-javascript code"><span class="token comment">//<span style="color:#859900;"> #ifdef</span><b style="color:#268BD2"> uniVersion > 3.9</b></span>
编译器版本大于3.9时生效 编译器版本大于3.9时生效
<span class="token comment">//<span style="color:#859900;"> #endif</span></span></code></pre> <span class="token comment">//<span style="color:#859900;"> #endif</span></span></code></pre>
注意,从HBuilderX 3.9起,版本号将由三段式字符串改为小数。
即HBuilderX 3.9.1,将改为 3.91。
这样条件编译判断时,仅需输入一个数字即可。
注意低于3.9版本的HBuilderX的条件编译不识别`uniVersion`
### HBuilderX 支持 ### HBuilderX 支持
HBuilderX 为 ``uni-app`` 的条件编译提供了丰富的支持: HBuilderX 为 ``uni-app`` 的条件编译提供了丰富的支持:
......
* [概述](README.md) * [概述](README.md)
* [项目](project.md)
* 教程 * 教程
* [与js开发的差别](tutorial/codegap.md) * [与js开发的差别](tutorial/codegap.md)
* [request联网教程](tutorial/request.md) * [request联网教程](tutorial/request.md)
......
...@@ -76,3 +76,32 @@ export default { ...@@ -76,3 +76,32 @@ export default {
此时 `vue` 组件和 `uvue` 组件的优先级如下: 此时 `vue` 组件和 `uvue` 组件的优先级如下:
-`uni-app x` 中,优先使用 `uvue` 组件,如果不存在 `uvue` 组件,则使用 `vue` 组件。 -`uni-app x` 中,优先使用 `uvue` 组件,如果不存在 `uvue` 组件,则使用 `vue` 组件。
-`uni-app` 中,只支持使用 `vue` 组件。 -`uni-app` 中,只支持使用 `vue` 组件。
## 如何开发同时兼容 uni-app x 和 uni-app 的组件
目前有两种方案:
- 目录下同时提供uvue,vue文件,分别适配 uni-app x 和 uni-app
组件作者在 uvue 和 vue 文件中可以自由使用各自的特性,比如 vue 中可以任意使用 js 或 ts 来书写代码,
如果部分组件逻辑被抽离为单独的文件,需要分别命名为各自环境支持的文件类型,导入时不同平台支持的文件类型也不同,
比如 uvue 文件目前不支持引入js或ts,而 vue 文件不能引入 uts 文件
对于现有的 uni-app 组件,通过新建 uvue 文件来渐进式支持 uni-app x,可以避免对已有 uni-app 项目造成影响
- 仅提供一个vue文件,同时适配 uni-app x 和 uni-app
该方案,需要script节点配置lang="ts",这样才可以在 uni-app 项目中正常书写带有类型的代码,而在 uni-app x 项目中,则会忽略 lang="ts",当做 uts 代码编译。
当需要区分平台或项目类型时,可以使用对应的条件编译。
<!-- 比如,当需要在 css 中区分原生渲染和webview渲染时
可以通过 APP-UVUE(表示在 uni-app x 项目app端的Android和iOS原生渲染)、APP-NVUE(表示在 uni-app 项目app端的nvue页面原生渲染) 区分,
`#ifdef APP-UVUE || APP-NVUE` 可以表示原生渲染,使用 `ifndef` 则可以取反表示为webview渲染,如 `#ifndef APP-UVUE || APP-NVUE`
-->
比如通过 UNI-APP-X 来区分项目类型,更多条件编译见:[详情](https://uniapp.dcloud.net.cn/tutorial/platform.html)
\ No newline at end of file
...@@ -76,3 +76,32 @@ export default { ...@@ -76,3 +76,32 @@ export default {
此时 `vue` 组件和 `uvue` 组件的优先级如下: 此时 `vue` 组件和 `uvue` 组件的优先级如下:
-`uni-app x` 中,优先使用 `uvue` 组件,如果不存在 `uvue` 组件,则使用 `vue` 组件。 -`uni-app x` 中,优先使用 `uvue` 组件,如果不存在 `uvue` 组件,则使用 `vue` 组件。
-`uni-app` 中,只支持使用 `vue` 组件。 -`uni-app` 中,只支持使用 `vue` 组件。
## 如何开发同时兼容 uni-app x 和 uni-app 的组件
目前有两种方案:
- 目录下同时提供uvue,vue文件,分别适配 uni-app x 和 uni-app
组件作者在 uvue 和 vue 文件中可以自由使用各自的特性,比如 vue 中可以任意使用 js 或 ts 来书写代码,
如果部分组件逻辑被抽离为单独的文件,需要分别命名为各自环境支持的文件类型,导入时不同平台支持的文件类型也不同,
比如 uvue 文件目前不支持引入js或ts,而 vue 文件不能引入 uts 文件
对于现有的 uni-app 组件,通过新建 uvue 文件来渐进式支持 uni-app x,可以避免对已有 uni-app 项目造成影响
- 仅提供一个vue文件,同时适配 uni-app x 和 uni-app
该方案,需要script节点配置lang="ts",这样才可以在 uni-app 项目中正常书写带有类型的代码,而在 uni-app x 项目中,则会忽略 lang="ts",当做 uts 代码编译。
当需要区分平台或项目类型时,可以使用对应的条件编译。
<!-- 比如,当需要在 css 中区分原生渲染和webview渲染时
可以通过 APP-UVUE(表示在 uni-app x 项目app端的Android和iOS原生渲染)、APP-NVUE(表示在 uni-app 项目app端的nvue页面原生渲染) 区分,
`#ifdef APP-UVUE || APP-NVUE` 可以表示原生渲染,使用 `ifndef` 则可以取反表示为webview渲染,如 `#ifndef APP-UVUE || APP-NVUE`
-->
比如通过 UNI-APP-X 来区分项目类型,更多条件编译见:[详情](https://uniapp.dcloud.net.cn/tutorial/platform.html)
\ No newline at end of file
# uni-app x项目
## 新建
在HBuilder3.9起,新建uni-app项目界面的底部有一个checkbox:uni-app x。勾选后会新建为uni-app x项目。
![](static/newproject.png)
uni-app x的项目,manifest.json中会多一个节点`"uni-app-x" : {}`。这是HBuilder识别项目类型的标记。
```json
{
"name" : "hello-uniapp x",
"appid" : "__UNI__4517034",
"description" : "",
"versionName" : "1.0.0",
"versionCode" : "100",
"uni-app-x" : {
}
}
```
uni-app x的项目,在左侧项目管理器的图标是圆形的U(之前是方型的U)。
![](static/project-icon.png)
## 项目结构
uni-app x的项目结构与之前基本一致。
主要的差别是没有nativeplugins目录和wxcomponents目录。原因是uni-app x不支持uni-app js引擎版的原生语言插件,不支持微信小程序wxml组件。
基于uts的插件,可以在uni-app x和uni-app上通用。
## 运行
uni-app x的真机运行基座,和uni-app js引擎版不同,是一个绿色圆形的U(之前是方型H),基座名称默认为`uni-app x`(之前叫HBuilder)。
![](static/playground.jpg)
- uni-app x基座,只能运行uni-app x项目,包名是io.dcloud.uniappx
- HBuilder基座,可以运行5+App、wap2app和uni-app js引擎版。包名是io.dcloud.HBuilder
uni-app x运行时,控制台右上角可以选择开启原生日志。
公测版带宽有限,打包自定义基座或正式包时可能更慢。
目前仅支持真机运行,不支持断点debug。
## 发行
uni-app x目前只能打包apk。
暂不支持渠道包。
不支持wgt热更新、不支持安心打包。
下载到项目下的uts插件可打包含入。但内置模块暂不支持拆除,比如video。
\ No newline at end of file
...@@ -88,7 +88,9 @@ uvue支持的vue语法,是按vue3实现的,但一期uvue不支持setup。详 ...@@ -88,7 +88,9 @@ uvue支持的vue语法,是按vue3实现的,但一期uvue不支持setup。详
uvue支持的css语法,是web的子集,类似于nvue的css。仅支持flex布局。但也足以布局出需要的界面。详见[css语法](uni-app-x/css/README.md) uvue支持的css语法,是web的子集,类似于nvue的css。仅支持flex布局。但也足以布局出需要的界面。详见[css语法](uni-app-x/css/README.md)
以上约束特指App端的uvue引擎。如果把uvue页面编译到小程序和web平台,它的script仍然会被编译为js、它的样式遵循web的样式定义。 以上约束特指App端的uvue引擎。如果把uvue页面编译到小程序和web平台,它的script仍然会被编译为js、web的样式也都可以使用。
更多示例代码参考:[hello uni-app x](https://gitcode.net/dcloud/hello-uni-app-x)
## 3. uni的组件 ## 3. uni的组件
...@@ -262,30 +264,6 @@ uni-app js版的“App原生语言插件”无法在 uni-app x 中运行。 ...@@ -262,30 +264,6 @@ uni-app js版的“App原生语言插件”无法在 uni-app x 中运行。
欢迎去[需求墙](https://vote.dcloud.net.cn/#/?name=uni-app%20x)投票,告诉我们你的需求优先级。 欢迎去[需求墙](https://vote.dcloud.net.cn/#/?name=uni-app%20x)投票,告诉我们你的需求优先级。
## 项目标识和区别
在HBuilder3.9起,新建uni-app项目界面的底部有一个checkbox:uni-app x。勾选后会新建为uni-app x项目。
uni-app x的项目,manifest.json中会多一个节点`"uni-app-x" : {}`。这是HBuilder识别项目类型的标记。
```json
{
"name" : "hello-uniapp x",
"appid" : "__UNI__4517034",
"description" : "",
"versionName" : "1.0.0",
"versionCode" : "100",
"uni-app-x" : {
}
}
```
uni-app x的项目,在左侧项目管理器的图标是圆形的U(之前是方型的U)。
![](static/project-icon.png)
uni-app x的真机运行基座,和uni-app js引擎版不复用,是一个绿色圆形的U(之前是方型H),基座名称默认为`uni-app x`(之前叫HBuilder)。
HBuilder基座可以运行5+App、wap2app和uni-app js引擎版。uni-app x基座只能运行uni-app x项目。
## 自动化测试 ## 自动化测试
uni-app x 从源头重视产品质量,第一个版本就支持自动化测试。并为uni-app x产品编写了数万行自动化测试例代码。 uni-app x 从源头重视产品质量,第一个版本就支持自动化测试。并为uni-app x产品编写了数万行自动化测试例代码。
...@@ -349,7 +327,7 @@ uvue的script里写的就是uts,uts就可以直接调原生代码。无所谓 ...@@ -349,7 +327,7 @@ uvue的script里写的就是uts,uts就可以直接调原生代码。无所谓
是的。为三方ide做插件是一个投资大且充满不确定性的事情,官方有限精力会聚焦在自身产品优化上。 是的。为三方ide做插件是一个投资大且充满不确定性的事情,官方有限精力会聚焦在自身产品优化上。
- uni-app x 支持最低的Android版本多少? - uni-app x 支持最低的Android版本多少?
Android 5+。Android4.4可能也可以运行,但官方发版前不会对4.4测试。 Android 5+
- 未来 uni-app js引擎版还维护吗? - 未来 uni-app js引擎版还维护吗?
维护。服务js开发者仍然是DCloud的重点。但nvue和5+将不再维护。 维护。服务js开发者仍然是DCloud的重点。但nvue和5+将不再维护。
......
...@@ -18,11 +18,12 @@ uni.request({ ...@@ -18,11 +18,12 @@ uni.request({
但在uts等强类型语言中无法这样,会报resData[0]无法安全访问、没有plugin_name属性,因为resData是个可为空的any类型,你确实没有为它定义过任何属性。 但在uts等强类型语言中无法这样,会报resData[0]无法安全访问、没有plugin_name属性,因为resData是个可为空的any类型,你确实没有为它定义过任何属性。
在uts中,提供了2种方案: 在uts中,提供了2种方案:
1. 使用[UTSJSONObject](../../uts/data-type.md#UTSJSONObject),不需要提前为json数据定义类型,在使用中通过下标访问并动态转换类型 1. 使用[UTSJSONObject](/uts/data-type.md#UTSJSONObject),不需要提前为json数据定义类型,在使用中通过下标访问并动态转换类型
2. 使用[type](../../uts/data-type.md#type),提前定义json数据类型,在request时通过泛型传入类型,拿到的就是一个有类型的对象,之后的用法和js一样 2. 使用[type](/uts/data-type.md#type),提前定义json数据类型,在request时通过泛型传入类型,拿到的就是一个有类型的对象,之后的用法和js一样
## 方式1:UTSJSONObject ## 方式1:UTSJSONObject
UTSJSONObject是uts的内置对象,它无法使用`.操作符`,但可以用下标和keypath来访问json数据。 UTSJSONObject是uts的内置对象,它无法使用`.操作符`,但可以用下标和keypath来访问json数据。
### UTSJSONObject下标方式
```ts ```ts
// uts写法 // uts写法
// 假使服务器返回的json数据是:{code:0,data:[{"plugin_name":"插件名称A"}]} // 假使服务器返回的json数据是:{code:0,data:[{"plugin_name":"插件名称A"}]}
...@@ -43,7 +44,25 @@ uni.request({ ...@@ -43,7 +44,25 @@ uni.request({
上面代码中打印日志部分是为了方便初学者理解,实际开发时代码行数不会多几行,主要是多几次as做类型转换。 上面代码中打印日志部分是为了方便初学者理解,实际开发时代码行数不会多几行,主要是多几次as做类型转换。
更多详见[UTSJSONObject](../../uts/data-type.md#UTSJSONObject) ### 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和泛型 ## 方式2:type和泛型
...@@ -80,7 +99,7 @@ type IRootType = { ...@@ -80,7 +99,7 @@ type IRootType = {
因type不可嵌套,生成了2个type。注意顺序,Data这个type需写在前面,因为后面要引用它。引用代码执行时如未定义该类型,会报错。 因type不可嵌套,生成了2个type。注意顺序,Data这个type需写在前面,因为后面要引用它。引用代码执行时如未定义该类型,会报错。
- 第二步:把这段类型定义,放在`<script>`根下,也就是export default{}之前。然后给uni.request传入泛型参数`<IRootType>`,返回的res自动转换好了类型,可以直接`.`属性了。 - 第二步:把这段类型定义,放在`<script>`根下,也就是export default{}之前。然后给uni.request传入泛型参数`<IRootType>`,返回的res.data自动转换好了类型,可以直接`.`属性了。
**注意:** 因为`res.data`是对象,所以泛型那里直接使用`<IRootType>`。有的服务器接口返回的`res.data`是数组,就需要在泛型那里写成`<IRootType[]>` **注意:** 因为`res.data`是对象,所以泛型那里直接使用`<IRootType>`。有的服务器接口返回的`res.data`是数组,就需要在泛型那里写成`<IRootType[]>`
...@@ -114,7 +133,7 @@ type IRootType = { ...@@ -114,7 +133,7 @@ type IRootType = {
</script> </script>
``` ```
与UTSJSONObject方式相比,不用使用as做很多转换,虽然需要定义type,但由于有工具可以自动生成type,所以整体使用体验,比UTSJSONObject方式方便一点。 与UTSJSONObject方式相比,type方式在使用数据时可以使用`.操作符`,有代码提示。虽然需要定义type,但由于有工具可以自动生成type,所以整体使用体验,比UTSJSONObject方式方便一点。
type+泛型这个方式,也是ts开发者惯用的方式。 type+泛型这个方式,也是ts开发者惯用的方式。
...@@ -134,11 +153,11 @@ type DataType = { ...@@ -134,11 +153,11 @@ type DataType = {
有了这个类型,再给它json数据进行实例化,就达到了给json数据定义类型的目标。给json数据定义好类型,就可以自由的使用`.操作符`获取属性了。 有了这个类型,再给它json数据进行实例化,就达到了给json数据定义类型的目标。给json数据定义好类型,就可以自由的使用`.操作符`获取属性了。
详见[type](../../uts/data-type.md#type) 详见[type](/uts/data-type.md#type)
而泛型,是一个对方法参数进行通用的类型描述。它告诉一个支持泛型的方法,给方法传入什么类型,方法就会返回什么类型。 而泛型,是一个对方法参数进行通用的类型描述。它告诉一个支持泛型的方法,给方法传入什么类型,方法就会返回什么类型。
不过uts的泛型支持还没有达到ts的泛型完善度,详见[泛型](../../uts/generics.md) 不过uts的泛型支持还没有达到ts的泛型完善度,详见[泛型](/uts/generics.md)
uni.request方法是支持泛型的,这意味着返回结果可以有很多种类型。 uni.request方法是支持泛型的,这意味着返回结果可以有很多种类型。
...@@ -162,13 +181,13 @@ type DataType = { ...@@ -162,13 +181,13 @@ type DataType = {
} }
``` ```
如您不了解null的安全使用,[详见](../../uts/data-type.md#null) 如您不了解null的安全使用,[详见](/uts/data-type.md#null)
- 务必注意res.data返回的是对象还是数组 - 务必注意res.data返回的是对象还是数组
如果res.data返回的是对象,那么泛型调用时直接`uni.request<IRootType>(` 如果res.data返回的是对象,那么泛型调用时直接`uni.request<IRootType>(`
如果res.data返回的是数组,那么传泛型时必须传入数组格式`uni.request<IRootType[]>(` 如果res.data返回的是数组,那么传泛型时必须传入数组格式`uni.request<IRootType[]>(`,注意多了一个`[]`
- type的敏感词转义 - type的敏感词转义
...@@ -176,16 +195,15 @@ type DataType = { ...@@ -176,16 +195,15 @@ type DataType = {
比如`{"a:b":"123","a-b":"456"}`,这些键名对于type来讲都是非法的。转换type就会失败。 比如`{"a:b":"123","a-b":"456"}`,这些键名对于type来讲都是非法的。转换type就会失败。
如果你的服务器数据涉及这类问题且数据格式不可改,那只能改用UTSJSONObject方式。 hx的json转type工具,会对一些敏感符合和关键字自动转义。但也有无法转移的符号和词,详见:[type](/uts/data-type.md#JSON_FIELD)
特殊词清单详见:[type](../../uts/data-type.md#type)
如果你的服务器数据涉及这类问题且数据格式不可改,那只能改用UTSJSONObject方式。
### 完整实例 ### 完整实例
再举一个实际中更常见的例子。联网获取插件市场的插件列表数据,并绑定在模板上,还可以翻页。 再举一个实际中更常见的例子。联网获取插件市场的插件列表数据,并绑定在模板上,还可以翻页。
翻页需要用到[...展开操作符](../../uts/operator.md#展开语法...) 翻页需要用到[...展开操作符](/uts/operator.md#展开语法...)
```vue ```vue
<template> <template>
......
...@@ -1056,12 +1056,14 @@ let rect = { ...@@ -1056,12 +1056,14 @@ let rect = {
- 仅限于web和Android,在iOS上swift不支持`.`操作符。 - 仅限于web和Android,在iOS上swift不支持`.`操作符。
- 在Android上也只支持字面量定义json(因为类型可推导)。如果是`JSON.parse()`转换的,则不能使用。 - 在Android上也只支持字面量定义json(因为类型可推导)。如果是`JSON.parse()`转换的,则不能使用。
2. `[""]` 下标属性 2. `[""]` 下标
`rect["x"]` `rect["x"]`
这是一种通用的方式,不管通过字面量定义的 UTSJSONObject,还是通过 `JSON.parse()`,不管是 web、Android、iOS 哪个平台,都可以使用下标方式访问 UTSJSONObject 属性。 这是一种通用的方式,不管通过字面量定义的 UTSJSONObject,还是通过 `JSON.parse()`,不管是 web、Android、iOS 哪个平台,都可以使用下标方式访问 UTSJSONObject 属性。
但下标返回嵌套的 UTSJSONObject 时,用起来比较麻烦,因为无法判断嵌套节点是对象还是数组,需要再 `as` 才能继续使用下一层数据。 但下标返回的数据,类型是any,想继续使用需要`as`为具体类型。
尤其是有子对象时,需要 `as` 后才能继续访问下一层数据。
```ts ```ts
let rect = { let rect = {
...@@ -1093,7 +1095,7 @@ console.log((rect["border"] as UTSJSONObject[])[0]["color"]); // red ...@@ -1093,7 +1095,7 @@ console.log((rect["border"] as UTSJSONObject[])[0]["color"]); // red
``` ```
如果是 `JSON.parse` 解析的数组,目前只能通过下标访问,无法使用`.`操作符 如果是 `JSON.parse` 解析的数据,只能通过下标访问,无法使用`.`操作符。因为`.`操作符的成立建立在编译器可确定类型的前提,字面量直接赋值可识别类型,`JSON.parse`无法识别类型
```ts ```ts
let listData = JSON.parse(`{"result":true, "count":42}`) as UTSJSONObject let listData = JSON.parse(`{"result":true, "count":42}`) as UTSJSONObject
...@@ -1102,19 +1104,19 @@ console.log(listData["count"]); //42 ...@@ -1102,19 +1104,19 @@ console.log(listData["count"]); //42
console.log(listArr[0]["title"]); //第一组 console.log(listArr[0]["title"]); //第一组
``` ```
多层级下标访问时需要使用 as 转换为 UTSJSONObject 多层级下标访问时需要使用 as 转换为 UTSJSONObject 或 UTSJSONObject[]
```ts ```ts
var j = { var j = {
"test":{ "subobj":{
"a-b": 1 "abc": 1
} }
} }
console.log((j['test'] as UTSJSONObject)['a-b']); console.log((j['subobj'] as UTSJSONObject)['abc']);
``` ```
1. 通过 keyPath 访问 UTSJSONObject 数据 3. 通过 keyPath 访问 UTSJSONObject 数据
`HBuilderX` 3.9.0 之后的版本,UTSJSONObject 提供了另外一种属性访问方式,keyPath。如果你了解 XPath、JSONPath 的话,这个概念类似。 `HBuilderX` 3.9+,UTSJSONObject 提供了另外一种属性访问方式: keyPath。如果你了解 XPath、JSONPath 的话,这个概念类似。
keypath是把`.`操作符作为一个字符串传入了UTSJSONObject的一个方法中,比如`utsObj.getString("address.detailInfo.street")` keypath是把`.`操作符作为一个字符串传入了UTSJSONObject的一个方法中,比如`utsObj.getString("address.detailInfo.street")`
...@@ -1140,24 +1142,29 @@ let utsObj = { ...@@ -1140,24 +1142,29 @@ let utsObj = {
我们可以通过 getString/getNumber/getBoolean/getJSON/getAny 等函数获得指定类型的属性,如果属性不存在,则返回null 我们可以通过 getString/getNumber/getBoolean/getJSON/getAny 等函数获得指定类型的属性,如果属性不存在,则返回null
```ts ```ts
// 打印结果:zhangsan console.log(utsObj.getString("username")) // 打印结果:zhangsan
console.log(utsObj.getString("username")) console.log(utsObj.getNumber("age")) // 打印结果:12
// 打印结果:12 console.log(utsObj.getJSON("address")) // 打印结果:[object]
console.log(utsObj.getNumber("age")) console.log(utsObj.getBoolean("isStudent")) // 打印结果:false
// 打印结果:[object] console.log(utsObj.getString("一个不存在属性")) // 打印结果:null
console.log(utsObj.getJSON("address")) ```
// 打印结果:false
console.log(utsObj.getBoolean("isStudent"))
// 打印结果:null
console.log(utsObj.getString("一个不存在属性"))
需要特别注意的是:属性名 和 属性类型,都要正确,否则不会返回对应的属性结果
```ts
console.log(utsObj.getNumber("age")) // 打印结果:12
console.log(utsObj.getNumber("agee")) // 名字不对,打印结果:null
console.log(utsObj.getString("age")) // 类型不对,打印结果:null
``` ```
为了更方便的访问 UTSJSONObject 中包含的数组中的元素,还可以通过 keyPath 指定数组下标来访问 UTSJSONObject 中的数据, 支持多维数组的访问。 keypath的一大优势就是可以深入数据层级,如下:
```ts ```ts
console.log(utsObj.getString("address.detailInfo.street")) // 结果:the wall street
```
如果数据里包括数组、多维数组,也可以穿透下去获取数据,如下:
```ts
let obj = { let obj = {
"data": [{ "data": [{
"a": "1" "a": "1"
...@@ -1173,28 +1180,12 @@ let obj = { ...@@ -1173,28 +1180,12 @@ let obj = {
] ]
} }
// 打印结果:1 console.log(obj.getString('data[0].a')) // 打印结果:1
console.log(obj.getString('data[0].a')) console.log(obj.getNumber('data[1].a')) // 打印结果:2
// 打印结果:2 console.log(obj.getBoolean('data[2][0].b')) // 打印结果:true
console.log(obj.getNumber('data[1].a')) console.log(obj.getJSON('data[2][1]')) // 打印结果:{"b":"test"}
// 打印结果:true console.log(obj.getArray('data[3]')) // 打印结果:[1, 2, 3]
console.log(obj.getBoolean('data[2][0].b')) console.log(obj.getAny('data[1].a')) // 打印结果:2
// 打印结果:{"b":"test"}
console.log(obj.getJSON('data[2][1]'))
// 打印结果:[1, 2, 3]
console.log(obj.getArray('data[3]'))
// 打印结果:2
console.log(obj.getAny('data[1].a'))
```
这种绑定类型的这对于原生开发者来说比较熟悉。但是需要特别注意的是: 如果属性名正确,但是属性类型不符合,那么不会返回对应的属性结果
```ts
// 打印结果:12
console.log(utsObj.getNumber("age"))
// 打印结果:null
console.log(utsObj.getString("age"))
``` ```
...@@ -1209,26 +1200,9 @@ console.log(utsObj.getAny("age") as Number) ...@@ -1209,26 +1200,9 @@ console.log(utsObj.getAny("age") as Number)
console.log(utsObj.getAny("address") as UTSJSONObject) console.log(utsObj.getAny("address") as UTSJSONObject)
``` ```
在传统的属性访问中,UTSJSONObject 的嵌套是一种比较复杂的情况,需要我们层层解析才能获取数据: 除了直接使用UTSJSONObject外,在 uts 中使用json数据还有2种方式:
1. UTSJSONObject.toMap() 转为Map对象 [见上](#Map)
```ts 2. 把json字符串或对象字面量通过type转为自定义类型,这是ts里经常使用的方式 [见下](#type)
// 获取 utsObj 中的 address属性
let addressObj = utsObj["address"] as UTSJSONObject
// 获取 address 中的 detail 属性
let detailInfoObj = utsObj["detailInfo"] as UTSJSONObject
// 结果:the wall street
let street = utsObj["street"] as String
```
上面的写法,啰嗦且容易出错。因此,我们提供了更易用的 keyPath 写法,帮助开发者摆脱复杂的对象嵌套关系:
```ts
// 结果:the wall street
let street = utsObj.getString("address.detailInfo.street")
```
当然,除了直接使用UTSJSONObject外,在 uts 中使用json数据还有2种方式:
1. UTSJSONObject.toMap() 转为Map对象 [见下](#Map)
2. 把json字符串或对象字面量通过type转为自定义类型,这是ts里经常使用的方式 [详见](type-aliases.md)
### 更多API ### 更多API
...@@ -1286,36 +1260,6 @@ console.log(person.name) //返回zhangsan ...@@ -1286,36 +1260,6 @@ console.log(person.name) //返回zhangsan
但在uts中,由于interface的概念在kotlin和swift有其他用途,所以uts中推荐开发者把json转成一个type,而不是interface。 但在uts中,由于interface的概念在kotlin和swift有其他用途,所以uts中推荐开发者把json转成一个type,而不是interface。
### type 类型的遍历
我们为每个自定义 type 类型提供了迭代器,可以使用 for-in 遍历出 type 类型中的所有属性名
```ts
let person : PersonType = { id: 1, name: "zhangsan", age: 18 }
for (key in person) {
console.log(key) // 输出 "id", "name", "age"
}
```
### type 类型的下标访问
我们为每个自定义 type 类型提供了下标操作,在适当的时机,可以使用下标的方式来读取或者修改 type 类型的属性值。
- 注意:由于通过下标修改属性值,编译阶段不会对下标操作的key值进行校验,所以可能会存在运行期代码失效的情况。为了代码安全,除非在必要时才进行通过下标修改属性的操作。
```ts
let person : PersonType = { id: 1, name: "zhangsan", age: 18 }
console.log(person["id"]) //1
obj["age"] = 25
console.log(obj["age"]) //25
console.log(obj.age) //25
```
> 特别说明:
> type 类型的遍历和下标访问功能自 HBuilderX3.9.0 开始提供。
### 把json数组转为type ### 把json数组转为type
上面的例子中,数据是json对象,下面再来定义一个json数组。 上面的例子中,数据是json对象,下面再来定义一个json数组。
...@@ -1473,8 +1417,13 @@ console.log(person.name); // 返回zhangsan ...@@ -1473,8 +1417,13 @@ console.log(person.name); // 返回zhangsan
使用!断言,是强制编译器信任开发者的写法,编译器放过后,在运行期一旦person为null,调用`person.name`就会崩溃。而使用`person?.name`则不会崩溃,只会返回null。 使用!断言,是强制编译器信任开发者的写法,编译器放过后,在运行期一旦person为null,调用`person.name`就会崩溃。而使用`person?.name`则不会崩溃,只会返回null。
**特殊情况** #### 敏感字和符号@JSON_FIELD
在定义Type时键名必须符合变量命名规则(如第一个字符不能数字,不能包含空格或运算符,不能使用语言保留的关键字等),如果json字符串中的键名不符合变量命名规则,需要添加注释`@JSON_FIELD`定义键名转换规则才能通过JSON.parse解析转type。 在定义Type时键名必须符合变量命名规则(如第一个字符不能数字,不能包含空格或运算符,不能使用语言保留的关键字等),
如果json字符串中的键名不符合变量命名规则,比如有个key的名字叫"a+b",这种json转type会失败。
解决方案是,添加注释`@JSON_FIELD`定义键名转换规则,才能通过JSON.parse解析转type。
```ts ```ts
type ExampleType = { type ExampleType = {
/** /**
...@@ -1485,15 +1434,17 @@ type ExampleType = { ...@@ -1485,15 +1434,17 @@ type ExampleType = {
a_b: string a_b: string
} }
``` ```
以上示例定义的 ExampleType 类型,在 `a_b: string` 声明时添加注释 `@JSON_FIELD "a+b"`,表示 JSON.parse 时会将json字符中的键名"a+b"转换为ExampleType类型的"a_b"属性;JSON.stringify 时会将ExampleType类型的"a_b"属性转换为json字符串中的"a+b"键名。 以上示例定义的 ExampleType 类型,在 `a_b: string` 声明时添加注释 `@JSON_FIELD "a+b"`,表示:
- JSON.parse 时会将json字符中的键名"a+b"转换为ExampleType类型的"a_b"属性;
- JSON.stringify 时会将ExampleType类型的"a_b"属性转换为json字符串中的"a+b"键名。
推荐的转换规则如下: 推荐的转换规则如下:
- 将不合法的字符(如空格、运算符等)转换为下划线“_”,如“a+b”转换为“a_b” - 将不合法的字符(如空格、运算符等)转换为下划线“_”,如“a+b”转换为“a_b”
- 将保留[关键词](keywords.md)(如class、enum等)转换时,在前面添加下划线,如“class”转换为“_class” - 将保留[关键词](keywords.md)(如class、enum等)转换时,在前面添加下划线,如“class”转换为“_class”
- 如果转换后的名称已存在,在后面添加下划线“_”避免冲突,如同时存在“a+b”和“a-b”,分别转换为“a_b”和“a_b_” - 如果转换后的名称已存在,在后面添加下划线`_`避免冲突,如同时存在“a+b”和“a-b”,分别转换为`a_b``a_b_`
以下举例json字符串 “{"a+b":"addition value","a-b":"subtraction value","class":"classification value"}”,应该如何定义 type 才能使用 JSON.parse 转换 以下举例json字符串 `{"a+b":"addition value","a-b":"subtraction value","class":"classification value"}`,应该如何定义 type 才能使用 JSON.parse 转换
```ts ```ts
type SpecialType = { type SpecialType = {
/** /**
...@@ -1529,9 +1480,9 @@ console.log(JSON.stringify(t)) //输出: {"a+b":"value 1","a-b":"value 2","class ...@@ -1529,9 +1480,9 @@ console.log(JSON.stringify(t)) //输出: {"a+b":"value 1","a-b":"value 2","class
#### json转type工具 #### json转type工具
如果json数据属性较多、嵌套较多,那么为json数据编写type类型定义,也是一件繁琐的事情。 如果json数据属性较多、嵌套较多、还涉及转义,那么为json数据编写type类型定义,也是一件繁琐的事情。
HBuilderX 3.9起内置了一个json转type工具,在`json编辑器`中选择一段内容点右键,选择`json转type`,即可根据json数据内容自动推导生成type定义。 HBuilderX 3.9起内置了一个json转type工具,在`json编辑器`中选择一段内容点右键,选择`json转type`,即可根据json数据内容自动推导生成type定义,如果json内容涉及转义,工具也会自动转义
![](../uni-app-x/static/json2type.png) ![](../uni-app-x/static/json2type.png)
...@@ -1566,7 +1517,39 @@ uvue文件中data中的json数据也涉及类型定义。此时注意:type定 ...@@ -1566,7 +1517,39 @@ uvue文件中data中的json数据也涉及类型定义。此时注意:type定
大多数情况下,data里的json数据是空的,联网从服务器取到一段json字符串,然后再赋值并转type。 大多数情况下,data里的json数据是空的,联网从服务器取到一段json字符串,然后再赋值并转type。
由于篇幅较长,示例另见:[request](../uni-app-x/tutorial/request.md) 由于篇幅较长,示例另见:[request教程](../uni-app-x/tutorial/request.md)
### type 类型的遍历
> HBuilderX3.9+
uts 为自定义 type 类型提供了迭代器,可以使用 for-in 遍历出 type 类型中的所有属性名
```ts
let person : PersonType = { id: 1, name: "zhangsan", age: 18 }
for (key in person) {
console.log(key) // 输出 "id", "name", "age"
}
```
### type 类型的下标访问
> HBuilderX3.9+
uts 为自定义 type 类型提供了下标操作,在适当的时机,可以使用下标的方式来读取或者修改 type 类型的属性值。
- 注意:由于通过下标修改属性值,编译阶段不会对下标操作的key值进行校验,所以可能会存在运行期代码失效的情况。为了代码安全,除非在必要时才进行通过下标修改属性的操作。
```ts
let person : PersonType = { id: 1, name: "zhangsan", age: 18 }
console.log(person["id"]) //1
obj["age"] = 25
console.log(obj["age"]) //25
console.log(obj.age) //25
```
## 其他 ## 其他
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册