- 示例工程
- 前端
- 移动开发
- 跨平台
【免费下载链接】uni-app
A cross-platform framework using Vue.js
UTSJSONObject 是 uni-app 的 UTS(Uni-app TypeScript)语言内置的核心数据类型,专用于操作 JSON 数据,是 UTS 面向 Kotlin、Swift、ArkTS、JS 多端编译时处理"匿名对象"的统一方案。本文基于当前开源仓库中 utsjsonobject.md 文档,结合>const person: UTSJSONObject = { name: 'Tom', printName: () => { // ... } } // 返回指定键对应的值,如果对象中不存在此键则返回 null。 let name: string = person["name"] as string // get 方法可以简化为使用下标运算符 `[]` 访问 name = person['name'] as string // 增加或更新指定键对应的值。 person.set('name', 'Tom1') // set 方法可以简化为使用下标运算符 `[]` 赋值 person['name'] = 'Tom2'
Kotlin
var person: UTSJSONObject = object : UTSJSONObject() { var name = "Tom" var printName = fun(){ console.log(name) } } // 返回指定键对应的值,如果对象中不存在此键则返回 null。 var name: String = person["name"] as String // get 方法可以简化为使用下标运算符 `[]` 访问 name = person["name"] as String // 增加或更新指定键对应的值。 person["name"] = "Tom1" // set 方法可以简化为使用下标运算符 `[]` 赋值 person["name"] = "Tom2"
Swift
var person: UTSJSONObject = UTSJSONObject([ "name": "Tom", "printName": {() -> Void in} ]) // 返回指定键对应的值,如果对象中不存在此键则返回 null。 var name: String? = person["name"] as? String // get 方法可以简化为使用下标运算符 `[]` 访问 name = person["name"] as? String // 增加或更新指定键对应的值。 person.set("name", "Tom1") // set 方法可以简化为使用下标运算符 `[]` 赋值 person["name"] = "Tom2"
注意:数组不能赋值给对象。UTSJSONObject与UTSJSONObject[](即Array<UTSJSONObject>)是两种类型,接收网络传输的 JSON 数据时必须严格匹配根节点类型,否则会造成代码错误或应用崩溃:
let jo: UTSJSONObject = [{ "x": 1, "y": 2 }] // 错误:数组不能赋值给对象 let jo: UTSJSONObject[] = [{ "x": 1, "y": 2 }] // 正确 let jo: Array<UTSJSONObject> = [{ "x": 1, "y": 2 }] // 正确 let jo = [{ "x": 1, "y": 2 }] // 正确,自动推断为 UTSJSONObject 数组方式二:JSON.parse 解析字符串
通过内置JSON对象(注意JSON与UTSJSONObject是两个不同对象,JSON是全局对象,详见 json.md)解析 JSON 字符串得到 UTSJSONObject。文档给出了三种写法:
UTS
// 写法1 推荐 let person1: UTSJSONObject = JSON.parseObject('{"name":"Tom"}')! // 写法2 推荐 const person2: UTSJSONObject = JSON.parse<UTSJSONObject>('{"name":"Tom"}')! // 写法3:如果 as 转换的实际类型不匹配会导致 crash,建议先通过 instanceof 判断类型再进行 as 转换 const parseRet3 = JSON.parse('{"name":"Tom"}') if (parseRet3 instanceof UTSJSONObject) { const person = parseRet3 as UTSJSONObject }
Kotlin
// 写法1 推荐 var person1: UTSJSONObject = JSON.parseObject("{\"name\":\"Tom\"}")!! // 写法2 推荐 val person2: UTSJSONObject = JSON.parse<UTSJSONObject>("{\"name\":\"Tom\"}")!! // 写法3:先 instanceof 判断再转换 val parseRet3 = JSON.parse("{\"name\":\"Tom\"}") if (parseRet3 is UTSJSONObject) { person = parseRet3 }
Swift
// 写法1 推荐 var person1: UTSJSONObject = JSON.parseObject("{\"name\":\"Tom\"}")! // 写法2 推荐 let person2: UTSJSONObject = JSON.parse("{\"name\":\"Tom\"}", UTSJSONObject.self)! // 写法3:先 instanceof 判断再转换 let parseRet3 = JSON.parse("{\"name\":\"Tom\"}") if parseRet3 is UTSJSONObject { person = parseRet3 as! UTSJSONObject }
关于JSON.parse的几个关键事实(来自 json.md):
- HBuilderX 3.9 之前,
JSON.parse()直接返回UTSJSONObject;但由于网络传入的 JSON 根节点可能是数组而非对象,会导致崩溃。从 HBuilderX 3.9 起,JSON.parse()返回类型改为any,开发者需要自行as指定具体类型; - 更简洁的替代是 3.9 新增的
JSON.parseObject()(根节点为对象)和JSON.parseArray()(根节点为数组); JSON.parse支持传入泛型,如JSON.parse<UTSJSONObject>(json),带泛型的 parse 访问性能更高;但不支持传入动态泛型(如将外层方法的普通泛型参数直接传入);- 若输入的字符串不是合法 JSON 格式,返回
null。
静态方法
UTSJSONObject 提供三个静态方法:keys、assign、assign<T>。
keys(object: UTSJSONObject): Array<string>
以数组形式返回指定 UTSJSONObject 对象内可枚举属性的名称列表。
参数
| 名称 | 类型 | 必填 | 描述 | | :- | :- | :- | :- | | item | UTSJSONObject | 是 | 需要检索的 UTSJSONObject 实例对象 |
返回值
| 类型 | 描述 | | :- | :- | | Array<string> | 可枚举属性的名称列表 |
UTS
let obj = { name: "zhangsan", age: 11 } let objAs = { age: 23 } as UTSJSONObject let ret1 = UTSJSONObject.keys(obj).length console.log(ret1) //2
Kotlin
var obj: UTSJSONObject = object : UTSJSONObject() { var name = "zhangsan" var age: Number = 11 } var ret1 = UTSJSONObject.keys(obj).length console.log(ret1) //2
Swift
let obj = UTSJSONObject(dictionary: [ "name": "zhangsan", "age": 11 ]) let ret1 = UTSJSONObject.keys(obj).length console.log(ret1) //2
uni-app x 兼容性
| Web | Android | iOS | HarmonyOS | iOS(VDOM) UTS 插件 | | :- | :- | :- | :- | :- | | 4.25 | 4.18 | x | 4.61 | 4.23 |
uni-app 兼容性
| Android UTS 插件 | iOS UTS 插件 | HarmonyOS UTS 插件 | | :- | :- | :- | | 4.18 | 4.23 | √ |
assign(...items): UTSJSONObject
允许输入一个或多个 UTSJSONObject 对象,合并后返回一个新的 UTSJSONObject,其中包含全部输入对象的属性字段;存在同名属性时,以后传入的属性为准。
参数
| 名称 | 类型 | 必填 | 描述 | | :- | :- | :- | :- | | items | UTSJSONObject | 是 | 需要被合并的 UTSJSONObject 实例对象 |
返回值
| 类型 | 描述 | | :- | :- | | UTSJSONObject | 合并后的 UTSJSONObject |
UTS
let target = { a: 1, b: 2 }; let source = { b: 4, c: 5 }; // 得到一个 UTSJSONObject 对象 let returnedTarget = UTSJSONObject.assign(target, source);
Kotlin
var target: UTSJSONObject = object : UTSJSONObject() { var a: Number = 1 var b: Number = 2 } var source: UTSJSONObject = object : UTSJSONObject() { var b: Number = 4 var c: Number = 5 } var returnedTarget = UTSJSONObject.assign(target, source) console.log(returnedTarget.toMap().count()) //3 var target1: UTSJSONObject = object : UTSJSONObject() { var a: Number = 1 var b: Number = 2 } var source1: UTSJSONObject = object : UTSJSONObject() { var b: Number = 4 var c: Number = 5 } var returned = UTSJSONObject.assign<UTSJSONObject>(target1, source1) console.log(returned) // {"a": 1, "b": 4, "c": 5}
Swift
let target = UTSJSONObject(dictionary: [ "a": 1, "b": 2 ]); let source = UTSJSONObject(dictionary: [ "b": 4, "c": 5 ]) let returnedTarget = UTSJSONObject.assign(target, source); console.log(returnedTarget.toMap().count) //3 let target1 = UTSJSONObject(dictionary: [ "a": 1, "b": 2 ]); let source1 = UTSJSONObject(dictionary: [ "b": 4, "c": 5 ]) let ret = UTSJSONObject.assign(target1, source1, type: UTSJSONObject.self); console.log(ret) // {"a": 1, "b": 4, "c": 5}
⚠️注意:与 js 中的
Object.assign不同,这里的assign每次返回的都是一个新的对象。
assign<T>(...items: T[]): T
泛型版本,合并后返回新的泛型对象 T,其中包含全部输入对象的属性字段;同名属性以后传入者为准。
参数
| 名称 | 类型 | 必填 | 描述 | | :- | :- | :- | :- | | items | any[] | 是 | 需要被合并的实例对象 |
返回值
| 类型 | 描述 | | :- | :- | | T | null | 合并后的泛型对象 |
UTS
let target1 = { a: 1, b: 2 }; let source1 = { b: 4, c: 5 }; let returned = UTSJSONObject.assign<UTSJSONObject>(target1, source1); console.log(returned)
uni-app x 兼容性(keys / assign / assign<T> 一致)
| Web | Android | iOS | HarmonyOS | iOS(VDOM) UTS 插件 | | :- | :- | :- | :- | :- | | 4.25 | 4.18 | x | 4.61 | 4.23 |
uni-app 兼容性
| Android UTS 插件 | iOS UTS 插件 | HarmonyOS UTS 插件 | | :- | :- | :- | | 4.18 | 4.23 | √ |
实例方法
parse()
将当前 UTSJSONObject 对象转换为某一个具体的类型 T。
返回值
| 类型 | 描述 | | :- | :- | | T | null | 具体的类型 T,如果失败返回 null |
uni-app x 兼容性
| Web | Android | iOS | HarmonyOS | | :- | :- | :- | :- | | x | 3.90 | x | x |
uni-app 兼容性
| Android UTS 插件 | iOS UTS 插件 | HarmonyOS UTS 插件 | | :- | :- | :- | | 3.90 | x | x |
get(key: string): any | null
获取一个属性,返回类型是 any 或 null。
参数:key(string,必填)
返回值
| 类型 | 描述 | | :- | :- | | any | null | 属性存在则返回结果,不存在返回 null |
get方法可简化为下标运算符[]访问:
const person: UTSJSONObject = { name: 'Tom' } let name: string = person["name"] as string // get 的等价写法set(key: string, value: any | null)
添加或更新一个指定的属性。
参数
| 名称 | 类型 | 必填 | | :- | :- | :- | | key | string | 是 | | value | any | 是 |
返回值:void
set方法可简化为下标运算符[]赋值:
person.set('name', 'Tom1') person['name'] = 'Tom2' // set 的等价写法补充:UTSJSONObject暂不支持通过 delete 删除属性;目前仅在 iOS 平台,可通过将属性值设为 null(如
student["age"] = null)删除该属性(来自>const utsObj: UTSJSONObject = {} as any as UTSJSONObject for (let i = 0; i < 100; i++) { utsObj.set('' + i, '' + i) } console.log('--start--') let startTime = Date.now() for (let i = 0; i < 10000; i++) { utsObj.getString('0') }
Kotlin
val utsObj: UTSJSONObject = UTSJSONObject() run { var i: Number = 0 while(i < 100){ utsObj.set("" + i, "" + i) i++ } } console.log("--start--") var startTime = Date.now() run { var i: Number = 0 while(i < 10000){ utsObj.getString("0") i++ } } var spendTime = Date.now() - startTime console.log(spendTime < 800) // true
Swift
let obj = UTSJSONObject() var i = 0 while i < 100 { obj.set("\(i)", "\(i)") i++ } let startTime = Date.now() var j = 0 while j < 10000 { obj.getString("0") j++ } let spendTime = Date.now() - startTime console.log(spendTime < 800) // true
uni-app x 兼容性:Web 4.0,Android 3.90,iOS 4.11,HarmonyOS 4.61。
getString(key, def): string
获取 string 属性,返回 string。key 可以是 keyPath,属性不存在时返回指定的默认值 def。
uni-app x 兼容性:Web 4.51,Android 3.90,iOS 4.11,HarmonyOS 4.61。
getJSON(key): UTSJSONObject | null
获取 UTSJSONObject 属性,返回 UTSJSONObject 或 null。key 支持 keyPath 写法(如"cars[0]"),可以直接穿透数组取子对象:
UTS
let obj = { "cars": [ { name: "car1", value: 100 } ] } let cars: Array<UTSJSONObject> | null = obj.getArray<UTSJSONObject>("cars") cars![0].set("value", 20) let firstCar = obj.getJSON("cars[0]") console.log(firstCar!['value']) //20
Kotlin
var obj: UTSJSONObject = object : UTSJSONObject() { var cars = utsArrayOf( object : UTSJSONObject() { var name = "car1" var value: Number = 100 } ) } var firstCar = obj.getJSON("cars[0]") console.log(firstCar!!["value"]) // 100
Swift
let obj = UTSJSONObject(dictionary: [ "cars": [ UTSJSONObject(dictionary: [ "name": "car1", "value": 100 ]) ] ]) let firstCar = obj.getJSON("cars[0]") console.log(firstCar!["value"]) // 100
uni-app x 兼容性:Web 4.0,Android 3.90,iOS 4.11,HarmonyOS 4.61。
getJSON(key, def): UTSJSONObject
获取 UTSJSONObject 属性,返回 UTSJSONObject。key 可以是 keyPath,属性不存在时返回指定的默认值 def。
uni-app x 兼容性:Web 4.51,Android 3.90,iOS 4.11,HarmonyOS 4.61。
getArray(key): Array<T> | null
获取 Array 属性,返回 Array 或 null,数组元素类型由泛型 T 决定。
参数:key(string,必填)
返回值
| 类型 | 描述 | | :- | :- | | Array<T> | null | 属性名存在且类型为 Array 时返回对应结果,不存在返回 null |
UTS
let obj = { "cars": [ { name: "car1", value: 100 } ] } let cars: Array<UTSJSONObject> | null = obj.getArray<UTSJSONObject>("cars") cars![0].set("value", 20) let firstCar = obj.getJSON("cars[0]") console.log(firstCar!['value']) //20
uni-app x 兼容性:Web 4.0,Android 3.90,iOS 4.11,HarmonyOS 4.61。
getArray(key, def: Array<T>): Array<T>
获取 Array 属性,返回 Array,数组元素类型由泛型 T 决定。key 可以是 keyPath。
uni-app x 兼容性:Web 4.51,Android 3.90,iOS 4.11,HarmonyOS 4.61。
getArray(key): Array<any> | null 与 getArray(key, def: Array<any>): Array<any>
不指定泛型时的重载版本,返回Array<any>。在 HarmonyOS(arkts)上 any 类型使用受限,文档示例中给出了平台条件编译的写法:
let obj = JSON.parseObject('{"name":"tom","tag":["student","user"]}') // #ifdef APP-HARMONY // arkts any类型使用受限 let noGenericArray: (any | null)[] | null = obj!.getArray("tag") // #endif // #ifndef APP-HARMONY // 这里得到是 Array<*> let noGenericArray = obj!.getArray("tag") // #endif // 这里得到是 Array<string> let genericArray = obj!.getArray<string>("tag") console.log(genericArray)Swift 目标下需要手动指定返回值类型以便泛型推断,例如let genericArray: [String]? = obj1!.getArray("tag")。
uni-app x 兼容性
| 方法 | Web | Android | iOS | HarmonyOS | | :- | :- | :- | :- | :- | | getArray(key): Array<any> | null | 4.0 | 3.90 | 4.11 | 4.61 | | getArray(key, def: Array<any>) | 4.51 | 3.90 | x | 4.61 |
toMap(): Map<string, any>
将当前 UTSJSONObject 实例转换为 Map 实例,之后即可用 Map 的 get/set 方式读写(data-type.md 提到 Map 性能更高但对数据格式有要求)。
返回值
| 类型 | 描述 | | :- | :- | | Map<string, any> | 返回 Map<string, any> 类型的 map |
UTS
person1 = JSON.parseObject('{"name":"Tom"}')! person1.toMap().forEach((value: any | null, key: string) => { console.log(key) console.log(value) })
Kotlin
val person0 = JSON.parseObject("{\"name\":\"Tom\"}")!! person0.toMap().forEach(fun(value, key){ console.log(key) console.log(value) })
Swift
person1 = JSON.parseObject("{\"name\":\"Tom\"}")! person1.toMap().forEach { value, key in console.log(key, value) }
uni-app x 兼容性:Web 4.0,Android 3.90,iOS 4.11,HarmonyOS 4.61。
访问 UTSJSONObject 属性的三种方式
(本小节内容主要来自>let rect = { x: 20, y: 12, size: { width: 80, height: 80 }, border: [ {color:"red", witdh:1}, {color:"white", witdh:1} ] }
1..操作符(rect.x)
- web 平台可以正常使用;
- HBuilderX 4.41+ iOS/Android 也支持
.操作符,但返回的数据类型是 any | null,想继续使用需要as为具体类型(如(rect.size as UTSJSONObject).width); - 不支持直接连续
.操作符,每层数据都需要as UTSJSONObject,可以理解为只能直接访问第一层属性; - HBuilderX 5.0+ Android 平台修复了响应式 UTSJSONObject 无法通过
.操作符访问第一层属性的 bug; - HBuilderX 5.08+ 起全平台支持
.访问(见 utsjsonobject.md)。
2.[""]下标(rect["x"])
- 最通用的方式:不论字面量定义还是
JSON.parse()得到,不论 web/Android/iOS,都可用下标访问; - 下标返回的类型是 any,继续使用需要
as; - 有子对象时需先
as再访问下一层:
console.log(rect.size.width) // 报错或受限:嵌套的 UTSJSONObject 仅 web 支持 . 操作符 console.log((rect["size"] as UTSJSONObject)["width"]) // 80,as 后需要整体用 () 括起来 console.log(((rect["border"] as UTSJSONObject[])[0] as UTSJSONObject)["color"]) // red3. 通过 keyPath 访问(HBuilderX 3.9+)
keyPath 把.操作符作为一个字符串传入 getXXX 方法,概念类似 XPath / JSONPath。这是所有 getXXX 方法(getString/getNumber/getBoolean/getJSON/getAny/getArray)的通用能力,key 参数标注"可以是 keyPath"即指此特性。
let utsObj = { "username": "zhangsan", "age": 12, "isStudent": false, "address": { "countyCode": "86", "province": "beijing", "detailInfo": { "street": "the wall street", "buildingNo": "5" } } } console.log(utsObj.getString("username")) // zhangsan console.log(utsObj.getNumber("age")) // 12 console.log(utsObj.getJSON("address")) // [object] console.log(utsObj.getBoolean("isStudent")) // false console.log(utsObj.getString("一个不存在属性")) // null console.log(utsObj.getString("address.detailInfo.street")) // the wall street特别需要注意:属性名与属性类型必须都正确,否则不会返回对应结果:
console.log(utsObj.getNumber("age")) // 12 console.log(utsObj.getNumber("agee")) // 名字不对,返回 null console.log(utsObj.getString("age")) // 类型不对,返回 nullkeyPath 可以穿透数组与多维数组:
let obj = { "data": [ { "a": "1" }, { "a": 2 }, [{ "b": true }, { "b": "test" }], [1, 2, 3] ] } console.log(obj.getString('data[0].a')) // 1 console.log(obj.getNumber('data[1].a')) // 2 console.log(obj.getBoolean('data[2][0].b')) // true console.log(obj.getJSON('data[2][1]')) // {"b":"test"} console.log(obj.getArray('data[3]')) // [1, 2, 3] console.log(obj.getAny('data[1].a')) // 2UTSJSONObject 与 type / Map 的互相转换
与自定义 type 转换
UTSJSONObject 与自定义 type 可以通过 JSON 字符串为桥梁互相转换(原文"常见问题"章节):
UTS
type User = { name: string, age: number } let jsonObj = { name: "张三", age: 12 } // UTSJSONObject => 自定义type let userA = JSON.parse<User>(JSON.stringify(jsonObj)!) console.log(userA!.name) // 自定义type => UTSJSONObject let utsJsonA = JSON.parseObject(JSON.stringify(userA)!) console.log(utsJsonA)
Kotlin
open class User ( @JsonNotNull open var name: String, @JsonNotNull open var age: Number, ) : UTSObject() { } var jsonObj: UTSJSONObject = object : UTSJSONObject() { var name = "张三" var age: Number = 12 } var userA = JSON.parse<User>(JSON.stringify(jsonObj))!! console.log(userA.name) var utsJsonA = JSON.parseObject(JSON.stringify(userA))!! console.log(utsJsonA)
Swift
// 自定义 class 如需使用 JSON.stringify 或 JSON.parse 处理,则需要实现 Codable 协议 // 通常 uts 代码中 Class 的 Codable 协议实现由编译器自动实现,此类代码不建议在混编代码中使用 class User : Codable { var name: String var age: NSNumber public init(_ obj: UTSJSONObject) { self.name = obj["name"] as! String self.age = obj["age"] as! NSNumber } enum CodingKeys: String, CodingKey { case name; case age } required public init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) self.name = try container.decode(String.self, forKey: .name, decoder) self.age = try container.decode(NSNumber.self, forKey: .age, decoder) } func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(name, forKey: .name, encoder) try container.encode(age, forKey: .age, encoder) } } let jsonObj = UTSJSONObject([ "name": "张三", "age": 12 as NSNumber ]) let userA = JSON.parse(JSON.stringify(jsonObj)!, User.self) console.log(userA?.name) let utsJsonA = JSON.parseObject(JSON.stringify(userA)!) console.log(utsJsonA)
与 Map 转换
toMap()即可将 UTSJSONObject 转为 Map(见上文)。反过来,在 Android 平台还可以用 Kotlin 的 HashMap 直接构造 UTSJSONObject(见下文"Android 平台方法")。
Android 平台方法
目前 UTSJSONObject 类型编译到 kotlin 后为io.dcloud.uts.UTSJSONObject,可以与该类原生构造器、toMap()等进行互操作:
UTS
// 创建一个 kotlin hashmap let kotlinMap = new kotlin.collections.HashMap<string,number>() kotlinMap.put("a",111) kotlinMap.put("b",2) // 转换为UTSJSONObject let utsObj = new UTSJSONObject(kotlinMap) console.log(utsObj) // UTSJSONObject 转换为 Map let nextMap = utsObj.toMap() console.log(nextMap)
Kotlin
// 创建一个 kotlin hashmap var kotlinMap = kotlin.collections.HashMap<String, Number>(); kotlinMap.put("a", 111); kotlinMap.put("b", 2); // 转换为UTSJSONObject var utsObj = UTSJSONObject(kotlinMap, UTSSourceMapPosition("utsObj", "pages/index/helloView.uvue", 33, 8)); console.log(utsObj); // UTSJSONObject 转换为 Map var nextMap = utsObj.toMap(); console.log(nextMap);
说明:Kotlin 侧构造 UTSJSONObject 时的第二个参数
UTSSourceMapPosition用于携带源码映射位置信息(文件、行号、列号),辅助报错定位,UTS 源码中由编译器自动生成,无需手动书写。
常见问题与注意事项
目标语言为 js 时 UTSJSONObject 实例方法可以被覆盖
如非必要请勿利用此特性。
在 js 平台,UTSJSONObject 的方法可以被同名属性覆盖。如下代码会将getString覆盖为 1:
const a = { getString: 1 } console.log(a.getString) // 1对象字面量在 js 平台的推导差异
- uts2js 平台同时存在 object 和 UTSJSONObject,且 UTSJSONObject 继承自 object;
- 编译为 kotlin/swift 时,未指定 type 的对象字面量必然被推导为 UTSJSONObject;
- 编译为 js 时,会根据上下文预期类型决定:无法推导预期类型、类型为 any 或类型兼容 UTSJSONObject 时,对象字面量转为 UTSJSONObject;否则推导为 object 或其他所需类型;
- 若希望对象字面量不被转为 UTSJSONObject(例如传给 lodash 等三方 js 库),应显式指定类型,如
{a: 1} as Record<string, any>(该写法仅 web 端支持,详见>赞
- 示例工程
- 前端
- 移动开发
- 跨平台
【免费下载链接】uni-app
A cross-platform framework using Vue.js
相关推荐
uni-app UTSiOS 内置对象完全指南:iOS 平台 UTS 插件系统级 API 详解
uni app UTSiOS 内置对象完全指南:iOS 平台 UTS 插件系统级 API 详解 本篇技术指南系统讲解 uni app 与 uni app x 在
示例工程前端移动开发跨平台Surveyor模型架构深度剖析:Question、Response与Survey的关系
Surveyor模型架构深度剖析:Question、Response与Survey的关系 Surveyor作为一个专业的Rails调查问卷引擎,其模型架构设计精
示例工程前端移动开发跨平台uni-app x UTS 插件中调用 uni API 完全指南:支持清单、类型限制与平台差异解析
uni app x UTS 插件中调用 uni API 完全指南:支持清单、类型限制与平台差异解析 本文以官方文档《UTS中使用uni api》为核心,结合本仓
示例工程前端移动开发跨平台