提交 5a0eea13 编写于 作者: dcloud_wdl's avatar dcloud_wdl

[转正] git merge origin/alpha

<script lang="uts">
import { state, setLifeCycleNum, setAppLaunchPath, setAppShowPath } from './store/index.uts'
import { state, setLifeCycleNum, setAppLaunchPath, setAppShowPath } from './store/index.uts'
let firstBackTime = 0
export default {
// #ifndef APP-ANDROID
mixins: [
{
data() {
return {
appMixinDataMsg: 'App.uvue mixin data msg'
let firstBackTime = 0
export default {
// #ifndef APP-ANDROID
mixins: [
{
data() {
return {
appMixinDataMsg: 'App.uvue mixin data msg'
}
}
}
}],
// #endif
onLaunch: function (options) {
console.log(options)
// 自动化测试
setLifeCycleNum(state.lifeCycleNum + 1000)
setAppLaunchPath(options.path)
console.log('App Launch')
// #ifdef UNI-APP-X && APP-ANDROID
const performance = uni.getPerformance()
const observer : PerformanceObserver = performance.createObserver((entryList : PerformanceObserverEntryList) => {
console.log('observer:entryList.getEntries()')
console.log(entryList.getEntries())
})
observer.observe({
entryTypes: ['render', 'navigation'],
} as PerformanceObserverOptions)
}],
// #endif
},
onShow: function (options) {
// 自动化测试
setLifeCycleNum(state.lifeCycleNum + 100)
setAppShowPath(options.path)
console.log('App Show')
},
onHide: function () {
// 自动化测试
setLifeCycleNum(state.lifeCycleNum - 100)
console.log('App Hide')
},
// #ifdef APP-ANDROID
onLastPageBackPress: function () {
// 自动化测试
setLifeCycleNum(state.lifeCycleNum - 1000)
console.log('App LastPageBackPress')
if (firstBackTime == 0) {
uni.showToast({
title: '再按一次退出应用',
position: 'bottom',
onLaunch: function (options) {
console.log(options)
// 自动化测试
setLifeCycleNum(state.lifeCycleNum + 1000)
setAppLaunchPath(options.path)
console.log('App Launch')
// #ifdef UNI-APP-X && APP-ANDROID
const performance = uni.getPerformance()
const observer : PerformanceObserver = performance.createObserver((entryList : PerformanceObserverEntryList) => {
console.log('observer:entryList.getEntries()')
console.log(entryList.getEntries())
})
firstBackTime = Date.now()
setTimeout(() => {
firstBackTime = 0
}, 2000)
} else if (Date.now() - firstBackTime < 2000) {
firstBackTime = Date.now()
uni.exit()
}
},
onExit() {
console.log('App Exit')
},
// #endif
onError: function(error: any) {
console.log('App Error', error)
setLifeCycleNum(state.lifeCycleNum + 100)
},
methods: {
checkLaunchPath() : boolean {
const HOME_PATH = 'pages/index/index'
if (state.appLaunchPath != HOME_PATH) {
return false
}
if (state.appShowPath != HOME_PATH) {
return false
observer.observe({
entryTypes: ['render', 'navigation'],
} as PerformanceObserverOptions)
// #endif
},
onShow: function (options) {
// 自动化测试
setLifeCycleNum(state.lifeCycleNum + 100)
setAppShowPath(options.path)
if(this.globalPropertiesStr === 'default string'){
setLifeCycleNum(state.lifeCycleNum + 10)
}
return true
console.log('App Show')
},
// #ifndef APP-ANDROID
checkAppMixin() : boolean {
if(this.globalMixinDataMsg1 != '通过 defineMixin 定义全局 mixin data') {
return false
onHide: function () {
// 自动化测试
setLifeCycleNum(state.lifeCycleNum - 100)
console.log('App Hide')
},
// #ifdef APP-ANDROID
onLastPageBackPress: function () {
// 自动化测试
setLifeCycleNum(state.lifeCycleNum - 1000)
console.log('App LastPageBackPress')
if (firstBackTime == 0) {
uni.showToast({
title: '再按一次退出应用',
position: 'bottom',
})
firstBackTime = Date.now()
setTimeout(() => {
firstBackTime = 0
}, 2000)
} else if (Date.now() - firstBackTime < 2000) {
firstBackTime = Date.now()
uni.exit()
}
if(this.appMixinDataMsg != 'App.uvue mixin data msg') {
return false
},
onExit() {
console.log('App Exit')
},
// #endif
onError: function(error: any) {
console.log('App Error', error)
setLifeCycleNum(state.lifeCycleNum + 100)
},
methods: {
checkLaunchPath() : boolean {
const HOME_PATH = 'pages/index/index'
if (state.appLaunchPath != HOME_PATH) {
return false
}
if (state.appShowPath != HOME_PATH) {
return false
}
return true
},
// #ifndef APP-ANDROID
checkAppMixin() : boolean {
if(this.globalMixinDataMsg1 != '通过 defineMixin 定义全局 mixin data') {
return false
}
if(this.appMixinDataMsg != 'App.uvue mixin data msg') {
return false
}
return true
}
return true
// #endif
}
// #endif
}
}
</script>
<style>
@import './styles/common.css';
@import './styles/common.css';
.list-item-text {
line-height: 36px;
}
.list-item-text {
line-height: 36px;
}
.split-title {
margin: 20px 0 5px;
padding: 5px 0;
border-bottom: 1px solid #dfdfdf;
}
.split-title {
margin: 20px 0 5px;
padding: 5px 0;
border-bottom: 1px solid #dfdfdf;
}
.btn-view {
margin: 10px 0;
padding: 10px;
border: 1px solid #dfdfdf;
border-radius: 3px;
}
.btn-view {
margin: 10px 0;
padding: 10px;
border: 1px solid #dfdfdf;
border-radius: 3px;
}
</style>
<style>
.text-red{
color: red;
}
</style>
\ No newline at end of file
</style>
## 1.0.9
* update 4.29.2024093009
## 1.0.28
* update 4.36.2024112612-alpha
## 1.0.8
* update 4.28.2024092502
## 1.0.27
* update 4.35.2024112402-alpha
## 1.0.26
* update 4.34.2024112020-alpha
## 1.0.25
* update 4.33.2024111702-alpha
## 1.0.24
* update 4.32.2024110103-alpha
## 1.0.23
* update 4.31.2024102414-alpha
## 1.0.22
* update 4.28.2024092105-alpha
......
<template>
<view>
<text class="uni-common-mt bold component-for-h-function">component for h()</text>
<slot />
</view>
</template>
<template>
<view>
<text class="uni-common-mt bold component-for-h-function">component for h()</text>
<text id="comp-for-h-function-msg">{{msg}}</text>
</view>
</template>
<script>
export default {
props: {
msg: {
type: String
}
}
}
</script>
<template>
<view>
<text class="uni-common-mt bold component-for-h-function">component for h() with slot</text>
<slot />
</view>
</template>
const path = require('path')
module.exports = {
testTimeout: 10000,
reporters: ['default'],
watchPathIgnorePatterns: ['/node_modules/', '/dist/', '/.git/'],
moduleFileExtensions: ['js', 'json'],
rootDir: __dirname,
testMatch: ['<rootDir>/pages/**/**/*.test.js'],
testMatch: ["<rootDir>/pages/App.test.js"],
testPathIgnorePatterns: ['/node_modules/'],
setupFilesAfterEnv: ['<rootDir>/jest-setup.js'],
testSequencer: path.join(__dirname, "testSequencer.js")
}
{
"id": "hello-uvue",
"name": "hello-uvue",
"displayName": "hello-uvue",
"version": "1.0.9",
"id": "hello-uvue-alpha",
"name": "hello-uvue-alpha",
"displayName": "hello-uvue-alpha",
"version": "1.0.28",
"description": "uvue的vue语法示例工程",
"main": "env.js",
"scripts": {
......
......@@ -8,5 +8,8 @@ describe("app launch & show options", () => {
if (!process.env.uniTestPlatformInfo.startsWith('android')) {
expect(await page.callMethod("checkAppMixin")).toBe(true)
}
const lifeCycleNum = await page.callMethod('getLifeCycleNum')
expect(lifeCycleNum).toBe(1110)
})
})
......@@ -51,7 +51,7 @@
num: 10,
arr: [4, 5, 6]
} as Obj,
refElement: null,
refElement: null as UniElement | null,
refElementIsSame: false
}
},
......
......@@ -63,7 +63,7 @@ export default {
default: false
},
providePageObject: {
type: Object as PropType<UTSJSONObject>,
type: UTSJSONObject,
default: (): UTSJSONObject => {
return {
title: 'default provide page object title',
......@@ -94,7 +94,7 @@ export default {
default: 'test inject string default value'
},
testInjectObjectDefaultValue: {
type: Object as PropType<UTSJSONObject>,
type: UTSJSONObject,
default(): UTSJSONObject {
return {
title: 'test inject object default value title',
......
......@@ -39,7 +39,7 @@ export default {
default: false
},
providePageObject: {
type: Object as PropType<UTSJSONObject>,
type: UTSJSONObject,
default: (): UTSJSONObject => {
return {
title: 'default provide page object title',
......
const PAGE_PATH = "/pages/component-instance/methods/call-method-easycom-uni-modules-composition"
let page
beforeAll(async () => {
page = await program.reLaunch(PAGE_PATH)
await page.waitFor('view')
})
it('callMethodTest', async () => {
// a[[]] only issue 8582
if (process.env.uniTestPlatformInfo.toLowerCase().startsWith('web')) {
expect(1).toBe(1)
return
}
const platformInfo = process.env.uniTestPlatformInfo.toLocaleLowerCase()
const isIOS = process.env.uniTestPlatformInfo.toLowerCase().startsWith('ios')
// ios_simulator ios 17.0
// xcodoe 15 以下的版本环境不满足
if (
isIOS &&
(platformInfo.indexOf('14.') != -1 ||
platformInfo.indexOf('13.') != -1 ||
platformInfo.indexOf('12.') != -1)
) {
expect(1).toBe(1)
return
}
const delay = () =>
new Promise((resolve, _) => {
setTimeout(() => {
resolve('')
}, 1500)
})
await page.callMethod('onButtonClick')
await delay()
const resStr1 = await page.$("#isNumListValid")
const resStr2 = await page.$("#isObjListValid")
expect(await resStr1.text()).toBe(`true`)
expect(await resStr2.text()).toBe(`true`)
})
<template>
<view>
<call-easy-method-uni-modules ref="callEasyMethod1"></call-easy-method-uni-modules>
<!-- #ifdef APP -->
<view>---</view>
<test-props id="btn1" :numList="numList" :objList='objList' @buttonclick='onButtonClick'
@numListChange='numListChange' @objListChange='objListChange'
style="width: 80px;height: 30px;background-color: lightblue"></test-props>
<view style="flex-direction: row ;">
<text>isNumListValid: </text>
<text id='isNumListValid'>{{isNumListValid}}</text>
</view>
<view style="flex-direction: row ;">
<text>isObjListValid: </text>
<text id='isObjListValid'>{{isObjListValid}}</text>
</view>
<!-- #endif -->
</view>
</template>
<script setup lang="uts">
import { testInOtherFile } from './call-method-easycom-uni-modules'
import { testInOtherFile } from './call-method-easycom-uni-modules'
import { ref, isProxy, isRef } from 'vue'
const delay = (): Promise<string> =>
new Promise((resolve, _) => {
setTimeout(() => {
resolve('')
}, 1000)
// #ifdef APP
import { PropsChangeEvent } from '@/uni_modules/test-props'
// #endif
const delay = () : Promise<string> =>
new Promise((resolve, _) => {
setTimeout(() => {
resolve('')
}, 1000)
})
const callEasyMethod1 = ref<CallEasyMethodUniModulesComponentPublicInstance | null>(null)
const numList = ref<number[]>([1]) // 传递 props
const objList = ref<any[]>([])
const isNumListValid = ref(false)
const isObjListValid = ref(false)
const callMethod1 = () => {
// 调用组件的 foo1 方法
callEasyMethod1.value?.foo1?.()
}
const callMethod2 = () => {
// 调用组件的 foo2 方法并传递 1个参数
callEasyMethod1.value?.foo2?.(Date.now())
}
const callMethod3 = () => {
// 调用组件的 foo3 方法并传递 2个参数
callEasyMethod1.value?.foo3?.(Date.now(), Date.now())
}
const callMethod4 = () => {
// 调用组件的 foo4 方法并传递 callback
callEasyMethod1.value?.foo4?.(() => {
console.log('callback')
})
}
const callMethod5 = () => {
// 注意: 返回值可能为 null,当前例子一定不为空,所以加了 !
const result = callEasyMethod1.value?.foo5?.('string5') as string
console.log(result) // string1
}
const callMethodTest = (text : string) : string | null => {
const result = callEasyMethod1.value?.foo5?.(text) as string
return result
}
const callMethodInOtherFile = (text : string) : string => {
return testInOtherFile(callEasyMethod1.value!, text)
}
// #ifdef APP-ANDROID
const numListChange = (res : Map<string, Map<string, any>>) => {
const value = res['detail']!['value'] as number[]
const isArray = Array.isArray(value)
const isLengthGt0 = value.length > 0
isNumListValid.value = isArray && isLengthGt0
}
// #endif
// #ifdef APP-IOS
const numListChange = (res : any) => {
const value = res['detail']!['value'] as number[]
const isArray = Array.isArray(value)
const isLengthGt0 = value.length > 0
console.log('is array',isArray,'is length>0',isLengthGt0,'res',isArray && isLengthGt0);
isNumListValid.value = isArray && isLengthGt0
}
// #endif
// #ifdef APP-ANDROID
const objListChange = (res : Map<string, Map<string, any>>) => {
const value = res['detail']!['value'] as any[]
const isArray = Array.isArray(value)
const isLengthGt0 = value.length > 0
isObjListValid.value = isArray && isLengthGt0
}
// #endif
// #ifdef APP-IOS
const objListChange = (res : any) => {
const value = res['detail']!['value'] as any[]
const isArray = Array.isArray(value)
const isLengthGt0 = value.length > 0
console.log('is array',isArray,'is length>0',isLengthGt0,'res',isArray && isLengthGt0);
isObjListValid.value = isArray && isLengthGt0
}
// #endif
const onButtonClick = () => {
// 改变 props: 观察 props 返回值为非响应式值
numList.value = [3, 2, 1]
objList.value = [{ id: '3' }, { id: '4' }]
}
const call = async () : Promise<void> => {
callMethod1()
await delay()
callMethod2()
await delay()
callMethod3()
await delay()
callMethod4()
await delay()
callMethod5()
}
onReady(() => {
call()
})
const callEasyMethod1 = ref<CallEasyMethodUniModulesComponentPublicInstance | null>(null)
const callMethod1 = () => {
// 调用组件的 foo1 方法
callEasyMethod1.value?.foo1?.()
}
const callMethod2 = () => {
// 调用组件的 foo2 方法并传递 1个参数
callEasyMethod1.value?.foo2?.(Date.now())
}
const callMethod3 = () => {
// 调用组件的 foo3 方法并传递 2个参数
callEasyMethod1.value?.foo3?.(Date.now(), Date.now())
}
const callMethod4 = () => {
// 调用组件的 foo4 方法并传递 callback
callEasyMethod1.value?.foo4?.(() => {
console.log('callback')
defineExpose({
callMethodTest,
callMethodInOtherFile,
onButtonClick
})
}
const callMethod5 = () => {
// 注意: 返回值可能为 null,当前例子一定不为空,所以加了 !
const result = callEasyMethod1.value?.foo5?.('string5') as string
console.log(result) // string1
}
const callMethodTest = (text: string): string | null => {
const result = callEasyMethod1.value?.foo5?.(text) as string
return result
}
const callMethodInOtherFile = (text: string): string => {
return testInOtherFile(callEasyMethod1.value!, text)
}
const call = async (): Promise<void> => {
callMethod1()
await delay()
callMethod2()
await delay()
callMethod3()
await delay()
callMethod4()
await delay()
callMethod5()
}
onReady(() => {
call()
})
defineExpose({
callMethodTest,
callMethodInOtherFile
})
</script>
\ No newline at end of file
</script>
const PAGE_PATH = "/pages/component-instance/methods/call-method-easycom-uni-modules-options"
let page
beforeAll(async () => {
page = await program.reLaunch(PAGE_PATH)
await page.waitFor('view')
})
it('callMethodTest', async () => {
// app only issue 8582
if (process.env.uniTestPlatformInfo.toLowerCase().startsWith('web')) {
expect(1).toBe(1)
return
}
// ios_simulator ios 17.0
// xcodoe 15 以下的版本环境不满足
const platformInfo = process.env.uniTestPlatformInfo.toLocaleLowerCase()
const isIOS = process.env.uniTestPlatformInfo.toLowerCase().startsWith('ios')
if (
isIOS &&
(platformInfo.indexOf('14.') != -1 ||
platformInfo.indexOf('13.') != -1 ||
platformInfo.indexOf('12.') != -1)
) {
expect(1).toBe(1)
return
}
const delay = () =>
new Promise((resolve, _) => {
setTimeout(() => {
resolve('')
}, 1500)
})
await page.callMethod('onButtonClick')
await delay()
const resStr1 = await page.$("#isNumListValid")
const resStr2 = await page.$("#isObjListValid")
expect(await resStr1.text()).toBe(`true`)
expect(await resStr2.text()).toBe(`true`)
})
<template>
<view>
<call-easy-method-uni-modules ref="callEasyMethod1"></call-easy-method-uni-modules>
<!-- #ifdef APP -->
<view>---</view>
<test-props id="btn1" :numList="numList" :objList='objList' @buttonclick='onButtonClick'
@numListChange='numListChange' @objListChange='objListChange'
style="width: 80px;height: 30px;background-color: lightblue"></test-props>
<view style="flex-direction: row ;">
<text>isNumListValid: </text>
<text id='isNumListValid'>{{isNumListValid}}</text>
</view>
<view style="flex-direction: row ;">
<text>isObjListValid: </text>
<text id='isObjListValid'>{{isObjListValid}}</text>
</view>
<!-- #endif -->
</view>
</template>
<script lang="uts">
import { testInOtherFile } from './call-method-easycom-uni-modules'
import { testInOtherFile } from './call-method-easycom-uni-modules'
const delay = (): Promise<string> =>
new Promise((resolve, _) => {
setTimeout(() => {
resolve('')
}, 1000)
})
const delay = () : Promise<string> =>
new Promise((resolve, _) => {
setTimeout(() => {
resolve('')
}, 1000)
})
export default {
data() {
return {
callEasyMethod1: null as CallEasyMethodUniModulesComponentPublicInstance | null
}
export default {
data() {
return {
callEasyMethod1: null as CallEasyMethodUniModulesComponentPublicInstance | null,
isWatched: false,
changeTimes: 0,
numList: [1] as number[], // 传递 props
objList: [] as any[],
isNumListValid: false,
isObjListValid: false
}
},
onReady() {
// 通过组件 ref 属性获取组件实例, 组件标签名首字母大写,驼峰+ComponentPublicInstance
this.callEasyMethod1 = this.$refs['callEasyMethod1'] as CallEasyMethodUniModulesComponentPublicInstance
this.call()
},
methods: {
async call() : Promise<void> {
this.callMethod1()
await delay()
this.callMethod2()
await delay()
this.callMethod3()
await delay()
this.callMethod4()
await delay()
this.callMethod5()
},
callMethod1() {
// 调用组件的 foo1 方法
this.callEasyMethod1?.foo1?.()
},
callMethod2() {
// 调用组件的 foo2 方法并传递 1个参数
this.callEasyMethod1?.foo2?.(Date.now())
},
callMethod3() {
// 调用组件的 foo3 方法并传递 2个参数
this.callEasyMethod1?.foo3?.(Date.now(), Date.now())
},
callMethod4() {
// 调用组件的 foo4 方法并传递 callback
this.callEasyMethod1?.foo4?.(() => {
console.log('callback')
})
},
callMethod5() {
// 注意: 返回值可能为 null,当前例子一定不为空,所以加了 !
const result = this.callEasyMethod1?.foo5?.('string5') as string
console.log(result) // string1
},
callMethodTest(text : string) : string | null {
const result = this.callEasyMethod1?.foo5?.(text) as string
return result
},
callMethodInOtherFile(text : string) : string {
return testInOtherFile(this.callEasyMethod1!, text)
},
// #ifdef APP-ANDROID
numListChange(res : Map<string, Map<string, any>>) {
const value = res['detail']!['value'] as number[]
const isArray = Array.isArray(value)
const isLengthGt0 = value.length > 0
this.isNumListValid = isArray && isLengthGt0
},
onReady() {
// 通过组件 ref 属性获取组件实例, 组件标签名首字母大写,驼峰+ComponentPublicInstance
this.callEasyMethod1 = this.$refs['callEasyMethod1'] as CallEasyMethodUniModulesComponentPublicInstance
// #endif
// #ifdef APP-IOS
numListChange(res : any) {
const value = res['detail']!['value'] as number[]
const isArray = Array.isArray(value)
const isLengthGt0 = value.length > 0
this.isNumListValid = isArray && isLengthGt0
},
// #endif
this.call()
// #ifdef APP-ANDROID
objListChange(res : Map<string, Map<string, any>>) {
const value = res['detail']!['value'] as number[]
const isArray = Array.isArray(value)
const isLengthGt0 = value.length > 0
this.isObjListValid = isArray && isLengthGt0
},
methods: {
async call(): Promise<void> {
this.callMethod1()
await delay()
this.callMethod2()
await delay()
this.callMethod3()
await delay()
this.callMethod4()
await delay()
this.callMethod5()
},
callMethod1() {
// 调用组件的 foo1 方法
this.callEasyMethod1?.foo1?.()
},
callMethod2() {
// 调用组件的 foo2 方法并传递 1个参数
this.callEasyMethod1?.foo2?.(Date.now())
},
callMethod3() {
// 调用组件的 foo3 方法并传递 2个参数
this.callEasyMethod1?.foo3?.(Date.now(), Date.now())
},
callMethod4() {
// 调用组件的 foo4 方法并传递 callback
this.callEasyMethod1?.foo4?.(() => {
console.log('callback')
})
},
callMethod5() {
// 注意: 返回值可能为 null,当前例子一定不为空,所以加了 !
const result = this.callEasyMethod1?.foo5?.('string5') as string
console.log(result) // string1
},
callMethodTest(text: string): string | null {
const result = this.callEasyMethod1?.foo5?.(text) as string
return result
},
callMethodInOtherFile(text: string): string {
return testInOtherFile(this.callEasyMethod1!, text)
// #endif
// #ifdef APP-IOS
objListChange(res : any) {
const value = res['detail']!['value'] as number[]
const isArray = Array.isArray(value)
const isLengthGt0 = value.length > 0
this.isObjListValid = isArray && isLengthGt0
},
// #endif
onButtonClick() {
// 改变 props: 观察 props 返回值为非响应式值
console.log('button click');
this.numList = [3, 2, 1]
this.objList = [{ id: '3' }, { id: '4' }]
}
}
}
}
</script>
\ No newline at end of file
</script>
......@@ -24,33 +24,30 @@ defineOptions({
mixins: [mixins],
name: "$options",
_customKey: "custom key"
})
type DataInfo = {
name: string
customKey: string
mixinDataStr: string
})
type DataInfo = {
name: string
customKey: string
mixinDataStr: string
}
const dataInfo = reactive({
name: "",
customKey: "",
mixinDataStr: ""
} as DataInfo)
const dataInfo = reactive({
name: "",
customKey: "",
mixinDataStr: ""
} as DataInfo)
onMounted(() => {
const instance = getCurrentInstance()!.proxy!
// #ifdef APP-ANDROID
dataInfo.name = instance.$options.name
// #endif
// #ifndef APP-ANDROID
dataInfo.name = instance.$options.name!
// #ifndef APP-ANDROID
dataInfo.customKey = instance.$options._customKey
dataInfo.mixinDataStr = instance.$options.data!({})!['str']
// #endif
})
defineExpose({
dataInfo
})
defineExpose({
dataInfo
})
</script>
......@@ -19,12 +19,12 @@
<script lang="uts">
import mixins from "./mixins.uts"
type DataInfo = {
name: string
customKey: string
mixinDataStr: string
}
type DataInfo = {
name: string
customKey: string
mixinDataStr: string
}
export default {
mixins: [mixins],
......@@ -32,20 +32,17 @@ export default {
_customKey: "custom key",
data() {
return {
dataInfo: {
name: "",
customKey: "",
mixinDataStr: "",
dataInfo: {
name: "",
customKey: "",
mixinDataStr: "",
} as DataInfo
}
},
mounted() {
// #ifdef APP-ANDROID
this.dataInfo.name = this.$options.name
// #endif
// #ifndef APP-ANDROID
this.dataInfo.name = this.$options.name!
this.dataInfo.customKey = this.$options._customKey
// #ifndef APP-ANDROID
this.dataInfo.customKey = this.$options._customKey
// @ts-ignore
this.dataInfo.mixinDataStr = this.$options.data({})['str']
// #endif
......
......@@ -39,7 +39,7 @@ defineProps({
default: false
},
obj: {
type: Object as PropType<UTSJSONObject>,
type: UTSJSONObject,
default: (): UTSJSONObject => ({})
},
arr: {
......
......@@ -40,7 +40,7 @@
default: false
},
obj: {
type: Object as PropType<UTSJSONObject>,
type: UTSJSONObject,
default: (): UTSJSONObject => ({})
},
arr: {
......
......@@ -14,7 +14,7 @@
type: Number,
},
obj: {
type: Object as PropType<UTSJSONObject>,
type: UTSJSONObject,
}
},
emits: ['compUpdateObj'],
......
<template>
<template>
<!-- #ifdef APP -->
<scroll-view style="flex: 1">
<!-- #endif -->
<view class="page">
<!-- v-bind attribute -->
<button id="disabled-btn" class="mb-10" :disabled="true">
......@@ -25,13 +28,27 @@
<Foo :title="dataInfo.fooProps.title" :num="dataInfo.fooProps.num" :obj="dataInfo.fooProps.obj" />
<!-- v-bind props -->
<Foo checked />
<Foo checked />
<!-- 绑定对象 -->
<Foo id="bindObj1" v-bind="{ title: dataInfo.fooProps.title,num: dataInfo.fooProps.num,obj: dataInfo.fooProps.obj }" />
<!-- 绑定对象合并-->
<Foo id="bindObj2" v-bind="{ title: dataInfo.fooProps.title,num: dataInfo.fooProps.num,obj: dataInfo.fooProps.obj }" title="foo title override" />
<!-- 绑定对象合并-->
<Foo id="bindObj3" title="foo" v-bind="{ title: dataInfo.fooProps.title,num: dataInfo.fooProps.num,obj: dataInfo.fooProps.obj }" />
<!-- 绑定对象合并(UTSJSONObject)-->
<Foo id="bindObj4" v-bind="fooProps" title="foo title(json) override" />
<!-- 绑定对象合并(UTSJSONObject)-->
<Foo id="bindObj5" title="foo" v-bind="fooProps" />
<!-- v-bind in style -->
<!-- #ifdef WEB -->
<view class="mb-10 v-bind-css"></view>
<!-- #endif -->
</view>
</view>
<!-- #ifdef APP -->
</scroll-view>
<!-- #endif -->
</template>
<script setup lang="uts">
......@@ -60,7 +77,12 @@
},
vBindClassBackgroundColor: 'red',
vBindClassRpxHeight: '300rpx'
} as DataInfo)
} as DataInfo)
const fooProps = reactive({
title: 'foo title(json)',
num: 2,
})
defineExpose({
dataInfo
......@@ -73,6 +95,5 @@
background-color: v-bind(dataInfo.vBindClassBackgroundColor);
height: v-bind(dataInfo.vBindClassRpxHeight);
}
/* #endif */
</style>
\ No newline at end of file
<template>
<template>
<!-- #ifdef APP -->
<scroll-view style="flex: 1">
<!-- #endif -->
<view class="page">
<!-- v-bind attribute -->
<button id="disabled-btn" class="mb-10" :disabled="true">:disabled true</button>
......@@ -21,13 +24,29 @@
<Foo :title="dataInfo.fooProps.title" :num="dataInfo.fooProps.num" :obj="dataInfo.fooProps.obj" />
<!-- v-bind props -->
<Foo checked />
<Foo checked />
<!-- 绑定对象 -->
<Foo v-bind="{ title: dataInfo.fooProps.title,num: dataInfo.fooProps.num,obj: dataInfo.fooProps.obj }" />
<Foo v-bind="fooProps"/>
<Foo id="bindObj1" v-bind="{ title: dataInfo.fooProps.title,num: dataInfo.fooProps.num,obj: dataInfo.fooProps.obj }" />
<!-- 绑定对象合并 v-bind 在前 -->
<Foo v-bind="{ title: dataInfo.fooProps.title,num: dataInfo.fooProps.num,obj: dataInfo.fooProps.obj }" id="bindObj2" :title="dataInfo.fooProps.title + ' override'" />
<!-- 绑定对象合并 v-bind 在后 -->
<Foo id="bindObj3" title="foo" v-bind="{ title: dataInfo.fooProps.title,num: dataInfo.fooProps.num,obj: dataInfo.fooProps.obj }" />
<!-- 绑定对象合并 v-bind 在中间(UTSJSONObject)-->
<Foo id="bindObj4" v-bind="fooProps" title="foo title(json) override" />
<!-- 绑定对象合并(UTSJSONObject)-->
<Foo id="bindObj5" title="foo" v-bind="fooProps" />
<!-- v-bind in style -->
<!-- #ifdef WEB -->
<view class="mb-10 v-bind-css"></view>
<!-- #endif -->
</view>
</view>
<!-- #ifdef APP -->
</scroll-view>
<!-- #endif -->
</template>
<script lang="uts">
......@@ -60,7 +79,11 @@
},
vBindClassBackgroundColor: 'red',
vBindClassRpxHeight: '300rpx'
} as DataInfo
} as DataInfo,
fooProps:{
title: 'foo title(json)',
num: 2,
}
}
}
}
......@@ -72,6 +95,5 @@
background-color: v-bind(dataInfo.vBindClassBackgroundColor);
height: v-bind(dataInfo.vBindClassRpxHeight);
}
/* #endif */
</style>
\ No newline at end of file
......@@ -44,8 +44,19 @@ describe('v-bind', () => {
const fooPropsNum = await page.$('#foo-props-num')
expect(await fooPropsNum.text()).toBe(dataInfo.fooProps.num.toString())
const fooPropsObjName = await page.$('#foo-props-obj-name')
expect(await fooPropsObjName.text()).toBe(dataInfo.fooProps.obj.name)
expect(await fooPropsObjName.text()).toBe(dataInfo.fooProps.obj.name)
const bindObj1 = await page.$('#bindObj1')
expect(await (await bindObj1.$('#foo-props-title')).text()).toBe(dataInfo.fooProps.title)
const bindObj2 = await page.$('#bindObj2')
expect(await (await bindObj2.$('#foo-props-title')).text()).toBe(dataInfo.fooProps.title+' override')
const bindObj3 = await page.$('#bindObj3')
expect(await (await bindObj3.$('#foo-props-title')).text()).toBe(dataInfo.fooProps.title)
const bindObj4 = await page.$('#bindObj4')
expect(await (await bindObj4.$('#foo-props-title')).text()).toBe(`foo title(json) override`)
const bindObj5 = await page.$('#bindObj5')
expect(await (await bindObj5.$('#foo-props-title')).text()).toBe(`foo title(json)`)
if (isWeb) {
const vBindCss = await page.$('.v-bind-css')
expect(await vBindCss.style('backgroundColor')).toBe('rgb(255, 0, 0)')
......
......@@ -8,11 +8,18 @@
<text>v-model:msg in Foo:</text>
<text id="model-msg-text">{{ msg }}</text>
</view>
<view class="mb-10 flex justify-between flex-row">
<text>defineModel num:</text>
<text id="model-num-text">{{ num }}</text>
</view>
<view class="mb-10 flex justify-between flex-row">
<text>defineModel strArr:</text>
<text id="model-str-arr-text">{{ JSON.stringify(strArr) }}</text>
</view>
<view class="mb-10 flex justify-between flex-row">
<text>defineModel numArr:</text>
<text id="model-num-arr-text">{{ JSON.stringify(numArr) }}</text>
</view>
<button class="mb-10" id="update-value-btn" @click="updateValue">
update value
</button>
......@@ -28,9 +35,14 @@ const msg = defineModel('msg', { type: String, default: 'default msg' })
const num = defineModel('num', { type: Number, default: 1 })
const strArr = defineModel<string[]>('strArr', { default: () => [] as string[] })
const numArr = defineModel('numArr', {type: Array as PropType<number[]>, required: true })
const updateValue = () => {
modelValue.value += '1'
msg.value += '2'
num.value++
strArr.value.push(`${strArr.value.length}`)
numArr.value.push(numArr.value.length)
}
</script>
......@@ -19,18 +19,28 @@ describe('defineModel', () => {
const modelMsgInput = await page.$('#model-msg-input')
expect(await modelMsgInput.value()).toBe('msg')
const modelNumText = await page.$('#model-num-text')
expect(await modelNumText.text()).toBe('1')
const modelNumText = await page.$('#model-num-text')
expect(await modelNumText.text()).toBe('1')
const modelStrArrText = await page.$('#model-str-arr-text')
expect(await modelStrArrText.text()).toBe('["0"]')
const modelNumArrText = await page.$('#model-num-arr-text')
expect(await modelNumArrText.text()).toBe('[0]')
const updateValueBtn = await page.$('#update-value-btn')
await updateValueBtn.tap()
await updateValueBtn.tap()
expect(await modelNumText.text()).toBe('2')
expect(await modelValueText.text()).toBe('str1')
expect(await modelValueInput.value()).toBe('str1')
expect(await modelMsgText.text()).toBe('msg2')
expect(await modelMsgInput.value()).toBe('msg2')
expect(await modelMsgInput.value()).toBe('msg2')
expect(await modelStrArrText.text()).toBe('["0","1"]')
expect(await modelNumArrText.text()).toBe('[0,1]')
const handleModelValueUpdateRes = await page.$('#handle-model-value-update-res')
expect(await handleModelValueUpdateRes.text()).toBe('str1')
......
......@@ -3,8 +3,11 @@
<Foo
v-model="str"
v-model:msg="msg"
v-model:strArr="strArr"
v-model:numArr="numArr"
@update:modelValue="handleModelValueUpdate"
@update:msg="handleModelMsgUpdate" />
@update:msg="handleModelMsgUpdate"
/>
<input class="mb-10 input" id="model-value-input" v-model="str" />
<input class="mb-10 input" id="model-msg-input" v-model="msg" />
<view class="mb-10 flex justify-between flex-row">
......@@ -23,6 +26,8 @@ import Foo from './Foo-composition.uvue'
const str = ref('str')
const msg = ref('msg')
const strArr = ref<string[]>(['0'])
const numArr = ref<number[]>([0])
const handleModelValueUpdateRes = ref('')
const handleModelValueUpdate = (val : string) => {
......
<template>
<view id="foo">
component Foo
</view>
</template>
<template>
<view class="page">
<button id="toggle-btn" @click="toggleShow">toggle show/hide</button>
<view class="mt-10" id="v-show-element" v-show="dataInfo.show">
点击上方按钮,切换显示/隐藏
</view>
</view>
</template>
<script setup lang="uts">
type DataInfo = {
show: boolean
}
const dataInfo = reactive({
show: true
} as DataInfo)
const toggleShow = () => {
dataInfo.show = !dataInfo.show
}
defineExpose({
dataInfo
})
</script>
<template>
<view class="page">
<button id="toggle-btn" @click="toggleShow">toggle show/hide</button>
<text>点击上方按钮,切换下方 view 显示/隐藏</text>
<text>show default true: {{dataInfo.showDefaultTrue}}</text>
<view class="mt-10 default-true" id="v-show-element-default-true" v-show="dataInfo.showDefaultTrue"></view>
<text>show default false: {{dataInfo.showDefaultFalse}}</text>
<view class="mt-10 default-false" id="v-show-element-default-false" v-show="dataInfo.showDefaultFalse"></view>
<Foo v-show="dataInfo.showDefaultFalse" />
</view>
</template>
<script setup lang="uts">
import Foo from './Foo.uvue'
type DataInfo = {
showDefaultTrue : boolean
showDefaultFalse : boolean
}
const dataInfo = reactive({
showDefaultTrue: true,
showDefaultFalse: false
} as DataInfo)
const toggleShow = () => {
dataInfo.showDefaultTrue = !dataInfo.showDefaultTrue
dataInfo.showDefaultFalse = !dataInfo.showDefaultFalse
}
defineExpose({
dataInfo
})
</script>
<style>
.default-true,
.default-false {
display: flex;
width: 100px;
height: 50px;
}
.default-true {
background-color: greenyellow;
}
.default-false {
background-color: antiquewhite;
}
</style>
\ No newline at end of file
<template>
<view class="page">
<button id="toggle-btn" @click="toggleShow">toggle show/hide</button>
<view class="mt-10" id="v-show-element" v-show="dataInfo.show">
点击上方按钮,切换显示/隐藏
</view>
</view>
</template>
<script lang="uts">
type DataInfo = {
show: boolean
}
export default {
data() {
return {
dataInfo: {
show: true
} as DataInfo
}
},
methods: {
toggleShow() {
this.dataInfo.show = !this.dataInfo.show
}
}
}
</script>
<template>
<view class="page">
<button id="toggle-btn" @click="toggleShow">toggle show/hide</button>
<text>点击上方按钮,切换下方 view 显示/隐藏</text>
<text>show default true: {{dataInfo.showDefaultTrue}}</text>
<view class="mt-10 default-true" id="v-show-element-default-true" v-show="dataInfo.showDefaultTrue"></view>
<text>show default false: {{dataInfo.showDefaultFalse}}</text>
<view class="mt-10 default-false" id="v-show-element-default-false" v-show="dataInfo.showDefaultFalse"></view>
<Foo v-show="dataInfo.showDefaultFalse" />
</view>
</template>
<script lang="uts">
import Foo from './Foo.uvue'
type DataInfo = {
showDefaultTrue : boolean
showDefaultFalse : boolean
}
export default {
components: { Foo },
data() {
return {
dataInfo: {
showDefaultTrue: true,
showDefaultFalse: false
} as DataInfo
}
},
methods: {
toggleShow() {
this.dataInfo.showDefaultTrue = !this.dataInfo.showDefaultTrue
this.dataInfo.showDefaultFalse = !this.dataInfo.showDefaultFalse
}
}
}
</script>
<style>
.default-true,
.default-false {
display: flex;
width: 100px;
height: 50px;
}
.default-true {
background-color: greenyellow;
}
.default-false {
background-color: antiquewhite;
}
</style>
\ No newline at end of file
......@@ -6,23 +6,33 @@ describe('v-show', () => {
const test = async (page) => {
let dataInfo = await page.data('dataInfo')
expect(dataInfo.show).toBe(true)
const vShowElement = await page.$('#v-show-element')
expect(await vShowElement.style('display')).toBe('flex')
expect(dataInfo.showDefaultTrue).toBe(true)
expect(dataInfo.showDefaultFalse).toBe(false)
const vShowElementDefaultTrue = await page.$('#v-show-element-default-true')
expect(await vShowElementDefaultTrue.style('display')).toBe('flex')
const vShowElementDefaultFalse = await page.$('#v-show-element-default-false')
expect(await vShowElementDefaultFalse.style('display')).toBe('none')
const foo = await page.$('#foo')
expect(await foo.style('display')).toBe('none')
const toggle = await page.$('#toggle-btn')
await toggle.tap()
dataInfo = await page.data('dataInfo')
expect(dataInfo.show).toBe(false)
expect(await vShowElement.style('display')).toBe('none')
expect(dataInfo.showDefaultTrue).toBe(false)
expect(dataInfo.showDefaultFalse).toBe(true)
expect(await vShowElementDefaultTrue.style('display')).toBe('none')
expect(await vShowElementDefaultFalse.style('display')).toBe('flex')
expect(await foo.style('display')).toBe('flex')
await toggle.tap()
dataInfo = await page.data('dataInfo')
expect(dataInfo.show).toBe(true)
expect(await vShowElement.style('display')).toBe('flex')
expect(dataInfo.showDefaultTrue).toBe(true)
expect(dataInfo.showDefaultFalse).toBe(false)
expect(await vShowElementDefaultTrue.style('display')).toBe('flex')
expect(await vShowElementDefaultFalse.style('display')).toBe('none')
expect(await foo.style('display')).toBe('none')
}
it('v-show options API', async () => {
......
<template>
<view class="page">
<button id="trigger-error" @click="triggerError">trigger error</button>
<button class='mt-10' id="trigger-timeout-error" @click="triggerTimeoutError">trigger timeout error</button>
</view>
</template>
<script setup lang="uts">
import { state } from '@/store/index.uts'
import { state } from '@/store/index.uts'
onReady(() => {
throw new Error('error in error composition page onReady')
})
const triggerError = () => {
throw new Error('trigger error in throw error composition page')
}
const triggerTimeoutError = () => {
setTimeout(() => {
throw new Error('setTimeout trigger error in throw error composition page')
}, 10)
}
// 自动化测试
const getLifeCycleNum = () : number => {
......
<template>
<view class="page">
<button id="trigger-error" @click="triggerError">trigger error</button>
</view>
</template>
<script lang="uts">
import { state } from '@/store/index.uts'
export default {
onReady(){
throw new Error('error in error options page onReady')
},
methods: {
triggerError(){
throw new Error('trigger error in throw error options page')
},
// 自动化测试
getLifeCycleNum() : number {
return state.lifeCycleNum
}
},
}
</script>
<template>
<view class="page">
<button id="trigger-error" @click="triggerError">trigger error</button>
<button class='mt-10' id="trigger-timeout-error" @click="triggerTimeoutError">trigger timeout error</button>
</view>
</template>
<script lang="uts">
import { state } from '@/store/index.uts'
export default {
onReady() {
throw new Error('error in error options page onReady')
},
methods: {
triggerError() {
throw new Error('trigger error in throw error options page')
},
triggerTimeoutError() {
setTimeout(() => {
throw new Error('setTimeout trigger error in throw error options page')
}, 10)
},
// 自动化测试
getLifeCycleNum() : number {
return state.lifeCycleNum
}
},
}
</script>
\ No newline at end of file
const OPTIONS_PAGE_PATH = '/pages/error/throw-error/throw-error-options'
const COMPOSITION_PAGE_PATH = '/pages/error/throw-error/throw-error-composition'
const HOME_PAGE_PATH = '/pages/index/index'
describe('throw error', () => {
let page
let lifeCycleNum
const initLifecycle = async () => {
page = await program.reLaunch(HOME_PAGE_PATH)
await page.waitFor('view')
await page.callMethod('setLifeCycleNum', 0)
lifeCycleNum = await page.callMethod('getLifeCycleNum')
expect(lifeCycleNum).toBe(0)
}
const test = async (pagePath) => {
await initLifecycle()
page = await program.reLaunch(pagePath)
await page.waitFor('view')
expect(page.path).toBe(pagePath.substring(1))
lifeCycleNum = await page.callMethod('getLifeCycleNum')
expect(lifeCycleNum).toBe(100)
const triggerErrorBtn = await page.$('#trigger-error')
await triggerErrorBtn.tap()
lifeCycleNum = await page.callMethod('getLifeCycleNum')
expect(lifeCycleNum).toBe(200)
page = await program.navigateTo(HOME_PAGE_PATH)
await page.waitFor('view')
expect(page.path).toBe(HOME_PAGE_PATH.substring(1))
}
it('onError options API', async () => {
await test(OPTIONS_PAGE_PATH)
})
it('onError composition API', async () => {
await test(COMPOSITION_PAGE_PATH)
})
afterAll(async () => {
const resetLifecycleNum = 1100
await page.callMethod('setLifeCycleNum', resetLifecycleNum)
lifeCycleNum = await page.callMethod('getLifeCycleNum')
expect(lifeCycleNum).toBe(resetLifecycleNum)
})
})
const platformInfo = process.env.uniTestPlatformInfo.toLocaleLowerCase()
const isAndroid = platformInfo.includes('android')
const isIos = platformInfo.includes('ios')
const OPTIONS_PAGE_PATH = '/pages/error/throw-error/throw-error-options'
const COMPOSITION_PAGE_PATH = '/pages/error/throw-error/throw-error-composition'
const HOME_PAGE_PATH = '/pages/index/index'
describe('throw error', () => {
let page
let lifeCycleNum
const initLifecycle = async () => {
page = await program.reLaunch(HOME_PAGE_PATH)
await page.waitFor('view')
await page.callMethod('setLifeCycleNum', 0)
lifeCycleNum = await page.callMethod('getLifeCycleNum')
expect(lifeCycleNum).toBe(0)
}
const test = async (pagePath) => {
await initLifecycle()
page = await program.reLaunch(pagePath)
await page.waitFor('view')
expect(page.path).toBe(pagePath.substring(1))
lifeCycleNum = await page.callMethod('getLifeCycleNum')
expect(lifeCycleNum).toBe(100)
const triggerErrorBtn = await page.$('#trigger-error')
await triggerErrorBtn.tap()
lifeCycleNum = await page.callMethod('getLifeCycleNum')
expect(lifeCycleNum).toBe(200)
if (isAndroid || isIos) {
const triggerTimeoutErrorBtn = await page.$('#trigger-timeout-error')
await triggerTimeoutErrorBtn.tap()
await page.waitFor(500)
lifeCycleNum = await page.callMethod('getLifeCycleNum')
expect(lifeCycleNum).toBe(300)
}
page = await program.navigateTo(HOME_PAGE_PATH)
await page.waitFor('view')
expect(page.path).toBe(HOME_PAGE_PATH.substring(1))
}
it('onError options API', async () => {
await test(OPTIONS_PAGE_PATH)
})
it('onError composition API', async () => {
await test(COMPOSITION_PAGE_PATH)
})
afterAll(async () => {
const resetLifecycleNum = 1110
await page.callMethod('setLifeCycleNum', resetLifecycleNum)
lifeCycleNum = await page.callMethod('getLifeCycleNum')
expect(lifeCycleNum).toBe(resetLifecycleNum)
})
})
\ No newline at end of file
......@@ -647,8 +647,8 @@ export default {
{
id: 'directive',
name: '指令',
pages: [
// #ifndef APP-IOS
pages: [
// #ifndef APP-IOS
{
id: 'v-html',
name: 'v-html',
......@@ -664,7 +664,7 @@ export default {
url: 'v-html-composition'
},
]
},
},
// #endif
{
id: 'v-show',
......@@ -1227,13 +1227,13 @@ export default {
// 自动化测试
checkLaunchPath() : boolean {
const app = getApp()
return app.checkLaunchPath()
return app.vm!.checkLaunchPath()
},
// #ifndef APP-ANDROID
// 自动化测试
checkAppMixin() : boolean {
const app = getApp()
return app.checkAppMixin()
return app.vm.checkAppMixin()
}
// #endif
}
......
......@@ -57,7 +57,7 @@ onUnload(() => {
onBeforeMount(() => {
// 自动化测试
setLifeCycleNum(state.lifeCycleNum + 1)
console.log('component for lifecycle test mounted')
console.log('component for lifecycle test onBeforeMount')
})
onMounted(() => {
......@@ -90,10 +90,16 @@ onUnmounted(() => {
console.log('component for lifecycle test unmounted')
})
// TODO: app-android 不触发
onActivated(() => { })
// TODO: app-android 不触发
onDeactivated(() => { })
onActivated(() => {
// 自动化测试
setLifeCycleNum(state.lifeCycleNum + 1)
console.log('component for lifecycle test onActivated')
})
onDeactivated(() => {
// 自动化测试
setLifeCycleNum(state.lifeCycleNum - 1)
console.log('component for lifecycle test onDeactivated')
})
const updateTitle = () => {
title.value = 'component for lifecycle test updated'
......
......@@ -54,6 +54,16 @@
setLifeCycleNum(state.lifeCycleNum - 1);
console.log('component for lifecycle test unmounted');
},
activated() {
// 自动化测试
setLifeCycleNum(state.lifeCycleNum + 1);
console.log('component for lifecycle test activated');
},
deactivated() {
// 自动化测试
setLifeCycleNum(state.lifeCycleNum - 1);
console.log('component for lifecycle test deactivated');
},
methods: {
updateTitle() {
this.title = 'component for lifecycle test updated';
......
......@@ -4,6 +4,9 @@ const HOME_PATH = '/pages/index/index'
describe('component-lifecycle', () => {
let page
let lifeCycleNum
const platformInfo = process.env.uniTestPlatformInfo.toLocaleLowerCase()
const isAndroid = platformInfo.includes('android')
const isIos = platformInfo.includes('ios')
beforeAll(async () => {
page = await program.reLaunch(HOME_PATH)
await page.waitFor(700)
......@@ -16,15 +19,29 @@ describe('component-lifecycle', () => {
await page.waitFor(700)
})
afterAll(async () => {
const resetLifecycleNum = 1100
const resetLifecycleNum = 1110
await page.callMethod('setLifeCycleNum', resetLifecycleNum)
lifeCycleNum = await page.callMethod('getLifeCycleNum')
expect(lifeCycleNum).toBe(resetLifecycleNum)
})
it('onLoad onPageShow onReady onBeforeMount onMounted', async () => {
it('onLoad onPageShow onReady onBeforeMount onMounted onActivated', async () => {
lifeCycleNum = await page.callMethod('pageGetLifeCycleNum')
// TODO: android 组合式 API 不触发 onActivated
expect(lifeCycleNum).toBe(isAndroid ? 112 : 113)
})
it('onDeactivated', async () => {
// TODO: android 组合式 API 不触发 onActivated onDeactivated
const toggleAliveComponentBtn = await page.$('#toggle-alive-component-btn')
await toggleAliveComponentBtn.tap()
lifeCycleNum = await page.callMethod('pageGetLifeCycleNum')
expect(lifeCycleNum).toBe(112)
await toggleAliveComponentBtn.tap()
lifeCycleNum = await page.callMethod('pageGetLifeCycleNum')
// TODO: android 端 组合式 API 不触发 activated
expect(lifeCycleNum).toBe(isAndroid ? 112 : 113)
await page.callMethod('pageSetLifeCycleNum', 0)
})
it('onBeforeUpdate onUpdated', async () => {
......@@ -56,17 +73,18 @@ describe('component-lifecycle', () => {
page = await program.navigateTo(HOME_PATH)
await page.waitFor('view')
lifeCycleNum = await page.callMethod('getLifeCycleNum')
expect(lifeCycleNum).toBe(-10)
// App 端页面离开返回不触发 keepAlive 组件 activated deactivated, 详见 https://issues.dcloud.net.cn/pages/issues/detail?id=7419
expect(lifeCycleNum).toBe(isIos || isAndroid ? -10 : -11)
page = await program.navigateBack()
await page.waitFor('view')
lifeCycleNum = await page.callMethod('pageGetLifeCycleNum')
expect(lifeCycleNum).toBe(0)
await page.callMethod('pageSetLifeCycleNum', 0)
})
it('beforeUnmount unmounted onUnload onBackPress', async () => {
it('onDeactivated beforeUnmount unmounted onUnload onBackPress', async () => {
page = await program.navigateBack()
lifeCycleNum = await page.callMethod('getLifeCycleNum')
expect(lifeCycleNum).toBe(-112)
// TODO: android 组合式 API 不触发 onDeactivated
expect(lifeCycleNum).toBe(isAndroid ? -112 : -113)
await page.callMethod('setLifeCycleNum', 0)
})
})
\ No newline at end of file
......@@ -4,8 +4,12 @@
<!-- #endif -->
<view class="page container">
<text class="mb-10">component lifecycle 组合式 API</text>
<child-component @updateIsScroll="updateIsScroll" />
<button class="mt-10" @click="scrollToBottom">scrollToBottom</button>
<keep-alive>
<component :is="aliveComponent" @updateIsScroll="updateIsScroll" />
</keep-alive>
<button class="mt-10" @click="scrollToBottom">scrollToBottom</button>
<button id="toggle-alive-component-btn" class="mt-10" @click="toggleAliveComponent">toggle alive component</button>
<button class="mt-10" @click="navigateToHome">navigateTo home</button>
</view>
<!-- #ifdef APP -->
</scroll-view>
......@@ -16,6 +20,8 @@
import ChildComponent from './ChildComponentComposition.uvue'
import { state, setLifeCycleNum } from '@/store/index.uts'
const aliveComponent = shallowRef<any | null>(ChildComponent)
const isScrolled = ref(false)
// 自动化测试
......@@ -24,7 +30,7 @@
}
// 自动化测试
const pageSetLifeCycleNum = (num : number) => {
const pageSetLifeCycleNum = (num : number) => {
setLifeCycleNum(num)
}
......@@ -44,6 +50,10 @@
scrollTop: 3000,
})
}
const toggleAliveComponent = () => {
aliveComponent.value = aliveComponent.value == null ? ChildComponent : null
}
const updateIsScroll = (val : boolean) => {
isScrolled.value = val
......@@ -52,6 +62,12 @@
// 自动化测试
const getIsScrolled = () : boolean => {
return isScrolled.value
}
const navigateToHome = () => {
uni.navigateTo({
url: '/pages/index/index'
})
}
defineExpose({
......
......@@ -2,39 +2,48 @@ const PAGE_PATH = '/pages/lifecycle/component/component-options'
const HOME_PATH = '/pages/index/index'
describe('component-lifecycle', () => {
let page
let lifeCycleNum
beforeAll(async () => {
page = await program.reLaunch(HOME_PATH)
await page.waitFor(700)
const initLifecycleNum = 0
await page.callMethod('setLifeCycleNum', initLifecycleNum)
lifeCycleNum = await page.callMethod('getLifeCycleNum')
expect(lifeCycleNum).toBe(initLifecycleNum)
let page
let lifeCycleNum
beforeAll(async () => {
page = await program.reLaunch(HOME_PATH)
await page.waitFor(700)
const initLifecycleNum = 0
await page.callMethod('setLifeCycleNum', initLifecycleNum)
lifeCycleNum = await page.callMethod('getLifeCycleNum')
expect(lifeCycleNum).toBe(initLifecycleNum)
page = await program.navigateTo(PAGE_PATH)
await page.waitFor(700)
})
afterAll(async () => {
const resetLifecycleNum = 1100
await page.callMethod('setLifeCycleNum', resetLifecycleNum)
lifeCycleNum = await page.callMethod('getLifeCycleNum')
expect(lifeCycleNum).toBe(resetLifecycleNum)
})
page = await program.navigateTo(PAGE_PATH)
await page.waitFor(700)
})
afterAll(async () => {
const resetLifecycleNum = 1110
await page.callMethod('setLifeCycleNum', resetLifecycleNum)
lifeCycleNum = await page.callMethod('getLifeCycleNum')
expect(lifeCycleNum).toBe(resetLifecycleNum)
})
it('beforeCreate created beforeMount mounted', async () => {
lifeCycleNum = await page.callMethod('getLifeCycleNum')
expect(lifeCycleNum).toBe(4)
})
it('beforeUpdate updated', async () => {
const updateTitleBtn = await page.$('.component-lifecycle-btn')
await updateTitleBtn.tap()
lifeCycleNum = await page.callMethod('getLifeCycleNum')
expect(lifeCycleNum).toBe(6)
})
it('beforeUnmount unmounted', async () => {
page = await program.navigateBack()
lifeCycleNum = await page.callMethod('getLifeCycleNum')
expect(lifeCycleNum).toBe(4)
})
it('beforeCreate created beforeMount mounted activated', async () => {
lifeCycleNum = await page.callMethod('getLifeCycleNum')
expect(lifeCycleNum).toBe(5)
})
it('deactivated', async () => {
const toggleAliveComponentBtn = await page.$('#toggle-alive-component-btn')
await toggleAliveComponentBtn.tap()
lifeCycleNum = await page.callMethod('getLifeCycleNum')
expect(lifeCycleNum).toBe(4)
await toggleAliveComponentBtn.tap()
lifeCycleNum = await page.callMethod('getLifeCycleNum')
expect(lifeCycleNum).toBe(5)
})
it('beforeUpdate updated', async () => {
const updateTitleBtn = await page.$('.component-lifecycle-btn')
await updateTitleBtn.tap()
lifeCycleNum = await page.callMethod('getLifeCycleNum')
expect(lifeCycleNum).toBe(7)
})
it('deactivated beforeUnmount unmounted', async () => {
page = await program.navigateBack()
lifeCycleNum = await page.callMethod('getLifeCycleNum')
expect(lifeCycleNum).toBe(4)
})
})
\ No newline at end of file
<template>
<view class="page">
<text class="mb-10">component lifecycle 选项式 API</text>
<child-component />
<keep-alive>
<component :is="aliveComponent" />
</keep-alive>
<button id="toggle-alive-component-btn" class="mt-10" @click="toggleAliveComponent">toggle alive component</button>
<button class="mt-10" @click="navigateToHome">navigateTo home</button>
</view>
</template>
......@@ -11,11 +15,24 @@ import { state } from '@/store/index.uts'
export default {
components: { ChildComponent },
data(){
return {
aliveComponent: ChildComponent as any | null,
}
},
methods: {
// 自动化测试
getLifeCycleNum(): number {
return state.lifeCycleNum
},
toggleAliveComponent(){
this.aliveComponent = this.aliveComponent == null ? ChildComponent : null
},
navigateToHome() {
uni.navigateTo({
url: '/pages/index/index'
})
}
},
}
</script>
......@@ -15,7 +15,7 @@ const initLifecycle = async () => {
}
const testPageLifecycle = async (pagePath) => {
// onLoad onShow onReady onResize
page = await program.reLaunch(pagePath)
page = await program.reLaunch(pagePath)
await page.waitFor(1000)
lifeCycleNum = await page.callMethod('pageGetLifeCycleNum')
expect(lifeCycleNum).toBe(120)
......@@ -60,7 +60,7 @@ const testPageLifecycle = async (pagePath) => {
await page.waitFor(700)
lifeCycleNum = await page.callMethod('pageGetLifeCycleNum')
expect(lifeCycleNum).toBe(120)
page = await program.navigateBack()
page = await program.navigateBack()
await page.waitFor('view')
lifeCycleNum = await page.callMethod('getLifeCycleNum')
expect(lifeCycleNum).toBe(10)
......@@ -72,21 +72,21 @@ describe('app-lifecycle', () => {
page = await program.reLaunch(HOME_PATH)
await page.waitFor(700)
lifeCycleNum = await page.callMethod('getLifeCycleNum')
expect(lifeCycleNum).toBe(1100)
expect(lifeCycleNum).toBe(1110)
})
it('onLastPageBackPress', async () => {
it('onLastPageBackPress', async () => {
if (process.env.uniTestPlatformInfo.startsWith('android')) {
page = await program.navigateBack()
await page.waitFor(700)
lifeCycleNum = await page.callMethod('getLifeCycleNum')
expect(lifeCycleNum).toBe(100)
expect(lifeCycleNum).toBe(110)
}
})
})
describe('page-lifecycle', () => {
it('page-lifecycle options API', async () => {
await initLifecycle()
it('page-lifecycle options API', async () => {
await initLifecycle()
await testPageLifecycle(OPTIONS_PAGE_PATH)
})
......@@ -96,7 +96,7 @@ describe('page-lifecycle', () => {
})
afterAll(async () => {
const resetLifecycleNum = 1100
const resetLifecycleNum = 1110
await page.callMethod('setLifeCycleNum', resetLifecycleNum)
lifeCycleNum = await page.callMethod('getLifeCycleNum')
expect(lifeCycleNum).toBe(resetLifecycleNum)
......
const PAGE_PATH = '/pages/reactivity/core/reactive/reactive'
describe('reactive', () => {
let page = null
beforeAll(async () => {
page = await program.reLaunch(PAGE_PATH)
await page.waitFor('view')
})
it('basic', async () => {
const count = await page.$('#count')
expect(await count.text()).toBe('0')
const objStr = await page.$('#obj-str')
expect(await objStr.text()).toBe('default str')
const objNum = await page.$('#obj-num')
expect(await objNum.text()).toBe('0')
const objArr = await page.$('#obj-arr')
expect(await objArr.text()).toBe('["a","b","c"]')
const updateCountBtn = await page.$('#update-count-btn')
await updateCountBtn.tap()
expect(await count.text()).toBe('1')
const updateObjStrBtn = await page.$('#update-obj-str-btn')
await updateObjStrBtn.tap()
expect(await objStr.text()).toBe('new str')
const updateObjNumBtn = await page.$('#update-obj-num-btn')
await updateObjNumBtn.tap()
expect(await count.text()).toBe('2')
expect(await objNum.text()).toBe('2')
const updateObjArrBtn = await page.$('#update-obj-arr-btn')
await updateObjArrBtn.tap()
expect(await objArr.text()).toBe('["a","b","c","d"]')
})
let page = null
beforeAll(async () => {
page = await program.reLaunch(PAGE_PATH)
await page.waitFor('view')
})
it('basic', async () => {
const count = await page.$('#count')
expect(await count.text()).toBe('0')
const objStr = await page.$('#obj-str')
expect(await objStr.text()).toBe('default str')
const objNum = await page.$('#obj-num')
expect(await objNum.text()).toBe('0')
const objArr = await page.$('#obj-arr')
expect(await objArr.text()).toBe('["a","b","c"]')
const arr1 = await page.$('#arr1')
expect(await arr1.text()).toBe('[]')
const updateCountBtn = await page.$('#update-count-btn')
await updateCountBtn.tap()
expect(await count.text()).toBe('1')
const updateObjStrBtn = await page.$('#update-obj-str-btn')
await updateObjStrBtn.tap()
expect(await objStr.text()).toBe('new str')
const updateObjNumBtn = await page.$('#update-obj-num-btn')
await updateObjNumBtn.tap()
expect(await count.text()).toBe('2')
expect(await objNum.text()).toBe('2')
const updateObjArrBtn = await page.$('#update-obj-arr-btn')
await updateObjArrBtn.tap()
expect(await objArr.text()).toBe('["a","b","c","d"]')
const count1 = await page.$('#count1')
expect(await count1.text()).toBe('1')
const updateObj_A_B_C_Btn = await page.$('#update-obj1-a-b-c-btn')
await updateObj_A_B_C_Btn.tap()
expect(await count1.text()).toBe('2')
const updateArr1Btn = await page.$('#update-arr1-btn')
await updateArr1Btn.tap()
expect(await arr1.text()).toBe(JSON.stringify([1, 2, 3]))
const updateArr1ReactiveBtn = await page.$('#update-arr1-reactive-btn')
await updateArr1ReactiveBtn.tap()
expect(await arr1.text()).toBe(JSON.stringify([4, 5, 6]))
})
})
\ No newline at end of file
<template>
<view class="page">
<view class="flex justify-between flex-row mb-10">
<text>count:</text>
<text id="count">{{ count }}</text>
<view class="page">
<view class="flex justify-between flex-row mb-10">
<text>count:</text>
<text id="count">{{ count }}</text>
</view>
<view class="flex justify-between flex-row mb-10">
<text>obj.str:</text>
<text id="obj-str">{{ obj['str'] }}</text>
</view>
<view class="flex justify-between flex-row mb-10">
<text>obj.num:</text>
<text id="obj-num">{{ obj['num'] }}</text>
</view>
<view class="flex justify-between flex-row mb-10">
<text>obj.arr:</text>
<text id="obj-arr">{{ JSON.stringify(obj['arr']) }}</text>
</view>
<view class="flex justify-between flex-row mb-10">
<text>count1:</text>
<text id="count1">{{ count1 }}</text>
</view>
<view class="flex justify-between flex-row mb-10">
<text>obj1.a.b.c:</text>
<text id="obj1-a-b-c">{{ obj1.getString('a.b.c') }}</text>
</view>
<view class="flex justify-between flex-row mb-10">
<text>arr1(spread):</text>
<text id="arr1">{{ JSON.stringify(arr1) }}</text>
</view>
<button class='mb-10' id="update-count-btn" @click="updateCount">update count</button>
<button class='mb-10' id="update-obj-str-btn" @click="updateObjStr">update obj.str</button>
<button class='mb-10' id="update-obj-num-btn" @click="updateObjNum">update obj.num</button>
<button class='mb-10' id="update-obj-arr-btn" @click="updateObjArr">update obj.arr</button>
<button class='mb-10' id="update-obj1-a-b-c-btn" @click="updateObj1_A_B_C">update obj1.a.b.c</button>
<button class='mb-10' id="update-arr1-btn" @click="updateArr1(false)">update arr1 without reactive</button>
<button class='mb-10' id="update-arr1-reactive-btn" @click="updateArr1(true)">update arr1 with reactive</button>
</view>
<view class="flex justify-between flex-row mb-10">
<text>obj.str:</text>
<text id="obj-str">{{ obj['str'] }}</text>
</view>
<view class="flex justify-between flex-row mb-10">
<text>obj.num:</text>
<text id="obj-num">{{ obj['num'] }}</text>
</view>
<view class="flex justify-between flex-row mb-10">
<text>obj.arr:</text>
<text id="obj-arr">{{ JSON.stringify(obj['arr']) }}</text>
</view>
<button class='mb-10' id="update-count-btn" @click="updateCount">update count</button>
<button class='mb-10' id="update-obj-str-btn" @click="updateObjStr">update obj.str</button>
<button class='mb-10' id="update-obj-num-btn" @click="updateObjNum">update obj.num</button>
<button class='mb-10' id="update-obj-arr-btn" @click="updateObjArr">update obj.arr</button>
</view>
</template>
<script setup lang="uts">
const count = ref(0)
// TODO: 待支持后补充泛型示例
const obj = reactive({
str: 'default str',
num: count,
arr: ['a', 'b', 'c']
})
const updateObjStr = () => {
obj['str'] = 'new str';
}
const updateObjNum = () => {
obj['num'] = (obj['num'] as number) + 1
}
const updateCount = () => {
count.value++
}
const updateObjArr = () => {
(obj['arr'] as string[]).push('d')
}
const count = ref(0)
// TODO: 待支持后补充泛型示例
const obj = reactive({
str: 'default str',
num: count,
arr: ['a', 'b', 'c']
})
const updateObjStr = () => {
obj['str'] = 'new str';
}
const updateObjNum = () => {
obj['num'] = (obj['num'] as number) + 1
}
const updateCount = () => {
count.value++
}
const updateObjArr = () => {
(obj['arr'] as string[]).push('d')
}
const obj1 = reactive({
a: { b: { c: 'c' } }
})
const count1 = ref(0)
watchEffect(() => {
count1.value++
// 测试getString等keyPath触发依赖收集
obj1.getString("a.b.c")
})
function updateObj1_A_B_C() {
((obj1["a"] as UTSJSONObject)["b"] as UTSJSONObject)["c"] = "c1-" + Date.now()
}
const arr1 = ref<number[]>([])
function test(...args : number[]) {
arr1.value = args
}
function updateArr1(isReactive : boolean) {
if (isReactive) {
test(...reactive([4, 5, 6]))
} else {
test(...[1, 2, 3])
}
}
</script>
\ No newline at end of file
......@@ -156,7 +156,7 @@
watch: {
obj: {
handler(obj : Obj, prevObj ?: Obj) {
if (prevObj === null) {
if (prevObj == null) {
this.watchObjRes = `obj: {"num":${obj.num},"str":"${obj.str}","bool":${obj.bool},"arr":${JSON.stringify(obj.arr)}}, prevObj: ${JSON.stringify(prevObj)}`
} else {
// #ifdef WEB
......
<template>
<view>
<slot name="header"></slot>
<slot name="footer"></slot>
</view>
</template>
\ No newline at end of file
<script setup lang="uts">
import CompForHFunction from '@/components/CompForHFunction.uvue'
import CompForHFunction from '@/components/CompForHFunction.uvue'
import CompForHFunctionWithSlot from '@/components/CompForHFunctionWithSlot.uvue'
import Foo from './Foo.uvue'
const msg = ref('default msg')
// 故意外部声明为UTSJSONObject
const msgProps = { class: 'uni-common-mt msg', style: { color: 'blue' } }
const render = ():VNode => h('view', { class: 'page' }, [
h(CompForHFunction, {}, (): VNode[] => [h('text', { class: 'comp-slot' }, 'component slot')]),
h('text', { class: 'uni-common-mt msg', style: { color: 'blue' } }, msg.value),
h(CompForHFunctionWithSlot, {}, () : VNode[] => [h('text', { class: 'comp-slot' }, 'component slot')]),
h(CompForHFunction, { msg: msg.value }),
h('text', msgProps, msg.value),
h(Foo, null, {
header: (): VNode[] => [h('text', { id: "header" }, 'header')],
footer: (): VNode[] => [h('text', { id: "footer" }, 'footer')]
}),
h(
'button',
{
......
<script lang="uts">
import CompForHFunction from '@/components/CompForHFunction.uvue'
export default {
data() {
return {
msg: 'default msg'
}
},
render(): VNode {
return h('view', { class: 'page' }, [
h(CompForHFunction, {}, (): VNode[] => [h('text', { class: 'comp-slot' }, 'component slot')]),
h('text', { class: 'uni-common-mt msg', style: { color: 'blue' } }, this.msg),
h(
'button',
{
class: 'uni-common-mt btn',
type: 'primary',
onClick: () => {
this.msg = 'new msg'
}
},
'click'
)
])
}
}
</script>
<style>
.btn {
color: red;
}
<script lang="uts">
import CompForHFunction from '@/components/CompForHFunction.uvue'
import CompForHFunctionWithSlot from '@/components/CompForHFunctionWithSlot.uvue'
import Foo from './Foo.uvue'
// 故意外部声明为UTSJSONObject
const msgProps = { class: 'uni-common-mt msg', style: { color: 'blue' } }
export default {
data() {
return {
msg: 'default msg'
}
},
render() : VNode {
return h('view', { class: 'page' }, [
h(CompForHFunctionWithSlot, {}, () : VNode[] => [h('text', { class: 'comp-slot' }, 'component slot')]),
h(CompForHFunction, { msg: this.msg }),
h('text', msgProps, this.msg),
h(Foo, null, {
header: (): VNode[] => [h('text', { id: "header" }, 'header')],
footer: (): VNode[] => [h('text', { id: "footer" }, 'footer')]
}),
h(
'button',
{
class: 'uni-common-mt btn',
type: 'primary',
onClick: () => {
this.msg = 'new msg'
}
},
'click'
)
])
}
}
</script>
<style>
.btn {
color: red;
}
</style>
\ No newline at end of file
......@@ -18,19 +18,26 @@ describe('render-function render', () => {
const ComForRenderFunction = await page.$('.component-for-h-function')
expect(await ComForRenderFunction.text()).toEqual(
'component for h()'
'component for h() with slot'
)
const compSlot = await page.$('.comp-slot')
expect(await compSlot.text()).toEqual('component slot')
let msgEl = await page.$('.msg')
expect(await msgEl.text()).toEqual('default msg')
compForHFunctionMsg = await page.$('#comp-for-h-function-msg')
expect(await compForHFunctionMsg.text()).toEqual('default msg')
const btnEl = await page.$('.btn')
expect(await btnEl.property('type')).toBe('primary')
await btnEl.tap()
msgEl = await page.$('.msg')
expect(await msgEl.text()).toEqual('new msg')
compForHFunctionMsg = await page.$('#comp-for-h-function-msg')
expect(await compForHFunctionMsg.text()).toEqual('new msg')
expect(await (await page.$('#header')).text()).toEqual('header')
expect(await (await page.$('#footer')).text()).toEqual('footer')
}
it('render options API', async () => {
......
const Sequencer = require("@jest/test-sequencer").default
const sortTestFilePaths = [
"pages/App.test.js",
]
class CustomSequencer extends Sequencer {
sort(tests) {
// 测试例排序
const sortedTests = sortTestFilePaths
.map((filePath) => {
return tests.find((test) => test.path.endsWith(filePath))
})
.filter(Boolean)
return [...new Set([...sortedTests, ...tests])]
}
}
module.exports = CustomSequencer
......@@ -4,10 +4,17 @@
<script>
export default {
props: {
},
data() {
return {
result: ''
}
},
emits:['propsChanged'],
watch: {
},
methods: {
foo1() {
......@@ -28,4 +35,4 @@
}
}
}
</script>
\ No newline at end of file
</script>
{
"id": "test-props",
"displayName": "test-props",
"version": "1.0.0",
"description": "test-props",
"keywords": [
"test-props"
],
"repository": "",
"engines": {
"HBuilderX": "^3.7.0"
},
"dcloudext": {
"type": "component-uts",
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "",
"data": "",
"permissions": ""
},
"npmurl": ""
},
"uni_modules": {
"dependencies": [],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "u",
"aliyun": "u",
"alipay": "u"
},
"client": {
"Vue": {
"vue2": "u",
"vue3": "u"
},
"App": {
"app-android": "u",
"app-ios": "u"
},
"H5-mobile": {
"Safari": "u",
"Android Browser": "u",
"微信浏览器(Android)": "u",
"QQ浏览器(Android)": "u"
},
"H5-pc": {
"Chrome": "u",
"IE": "u",
"Edge": "u",
"Firefox": "u",
"Safari": "u"
},
"小程序": {
"微信": "u",
"阿里": "u",
"百度": "u",
"字节跳动": "u",
"QQ": "u",
"钉钉": "u",
"快手": "u",
"飞书": "u",
"京东": "u"
},
"快应用": {
"华为": "u",
"联盟": "u"
}
}
}
}
}
\ No newline at end of file
# test-props
### 开发文档
[UTS 语法](https://uniapp.dcloud.net.cn/tutorial/syntax-uts.html)
[UTS API插件](https://uniapp.dcloud.net.cn/plugin/uts-plugin.html)
[UTS 组件插件](https://uniapp.dcloud.net.cn/plugin/uts-component.html)
[Hello UTS](https://gitcode.net/dcloud/hello-uts)
\ No newline at end of file
import { PropsChangeEvent, PropsChangeEventDetail } from './index.uts'
export class PropChangeEventImpl extends UniCustomEvent<PropsChangeEventDetail> implements PropsChangeEvent {
constructor(detail : PropsChangeEventDetail) {
super("propChange", detail);
}
}
import { IPropsChangeEvent,IPropsChangeEventDetail } from '../interface.uts'
export type PropsChangeEvent = IPropsChangeEvent
export type PropsChangeEventDetail = IPropsChangeEventDetail
<template>
<view>
</view>
</template>
<script lang="uts">
/**
* 引用 Android 系统库
* [可选实现,按需引入]
*/
import TextUtils from 'android.text.TextUtils';
import Button from 'android.widget.Button';
import View from 'android.view.View';
import { IObjItem ,IPropsChangeEvent} from '../interface.uts'
import {PropChangeEventImpl} from './event.uts'
/**
* 引入三方库
* [可选实现,按需引入]
*
* 在 Android 平台引入三方库有以下两种方式:
* 1、[推荐] 通过 仓储 方式引入,将 三方库的依赖信息 配置到 config.json 文件下的 dependencies 字段下。详细配置方式[详见](https://uniapp.dcloud.net.cn/plugin/uts-plugin.html#dependencies)
* 2、直接引入,将 三方库的aar或jar文件 放到libs目录下。更多信息[详见](https://uniapp.dcloud.net.cn/plugin/uts-plugin.html#android%E5%B9%B3%E5%8F%B0%E5%8E%9F%E7%94%9F%E9%85%8D%E7%BD%AE)
*
* 在通过上述任意方式依赖三方库后,使用时需要在文件中 import
* import { LottieAnimationView } from 'com.airbnb.lottie.LottieAnimationView'
*/
/**
* UTSAndroid 为平台内置对象,不需要 import 可直接调用其API,[详见](https://uniapp.dcloud.net.cn/uts/utsandroid.html#utsandroid)
*/
//原生提供以下属性或方法的实现
export default {
/**
* 组件名称,也就是开发者使用的标签
*/
name: "test-props",
/**
* 组件涉及的事件声明,只有声明过的事件,才能被正常发送
*/
emits: ['buttonclick', 'numListChange', 'objListChange'],
/**
* 属性声明,组件的使用者会传递这些属性值到组件
*/
props: {
"buttontext": {
type: String,
default: "点击触发"
},
numList: {
type: Array as PropType<number[]>,
default: () => [] as number[]
},
objList: {
type: Array as PropType<IObjItem[]>,
default: () => [] as IObjItem[]
}
},
/**
* 组件内部变量声明
*/
data() {
return {}
},
/**
* 属性变化监听器实现
*/
watch: {
"buttontext": {
/**
* 这里监听属性变化,并进行组件内部更新
*/
handler(newValue : string, oldValue : string) {
if (!TextUtils.isEmpty(newValue) && newValue != oldValue) {
this.$el?.setText(newValue);
}
},
immediate: false // 创建时是否通过此方法更新属性,默认值为false
},
numList: {
handler(newVal : number[], oldVal : number[]) {
let detail = new Map<string, number[]>()
detail.set("value", newVal)
let data = new Map<string, any>()
data.set("detail", detail)
// const event = new PropChangeEventImpl(newVal)
this.$emit('numListChange', data)
},
immediate: true
},
objList: {
handler(newVal : any[], oldVal : any[]) {
let detail = new Map<string, any>()
detail.set("value", newVal)
let data = new Map<string, any>()
data.set("detail", detail)
this.$emit('objListChange', data)
},
immediate: true
}
},
/**
* 规则:如果没有配置expose,则methods中的方法均对外暴露,如果配置了expose,则以expose的配置为准向外暴露
* ['publicMethod'] 含义为:只有 `publicMethod` 在实例上可用
*/
expose: ['doSomething'],
methods: {
/**
* 对外公开的组件方法
*
* uni-app中调用示例:
* this.$refs["组件ref"].doSomething("uts-button");
*
* uni-app x中调用示例:
* 1、引入对应Element
* import { UtsButtonElement(组件名称以upper camel case方式命名 + Element) } from 'uts.sdk.modules.utsComponent(组件目录名称以lower camel case方式命名)';
* 2、(this.$refs["组件ref"] as UtsButtonElement).doSomething("uts-button");
* 或 (uni.getElementById("组件id") as UtsButtonElement).doSomething("uts-button");
*/
doSomething(param : string) {
console.log(param);
},
/**
* 内部使用的组件方法
*/
privateMethod() {
}
},
/**
* [可选实现] 组件被创建,组件第一个生命周期,
* 在内存中被占用的时候被调用,开发者可以在这里执行一些需要提前执行的初始化逻辑
*/
created() {
},
/**
* [可选实现] 对应平台的view载体即将被创建,对应前端beforeMount
*/
NVBeforeLoad() {
},
/**
* [必须实现] 创建原生View,必须定义返回值类型
* 开发者需要重点实现这个函数,声明原生组件被创建出来的过程,以及最终生成的原生组件类型
* (Android需要明确知道View类型,需特殊校验)
*/
NVLoad() : Button {
let button = new Button($androidContext!);
button.setText("点击触发");
button.setOnClickListener(new ButtonClickListener(this));
return button;
},
/**
* [可选实现] 原生View已创建
*/
NVLoaded() {
},
/**
* [可选实现] 原生View布局完成
*/
NVLayouted() {
},
/**
* [可选实现] 原生View将释放
*/
NVBeforeUnload() {
},
/**
* [可选实现] 原生View已释放,这里可以做释放View之后的操作
*/
NVUnloaded() {
},
/**
* [可选实现] 组件销毁
*/
unmounted() {
},
/**
* [可选实现] 自定组件布局尺寸,用于告诉排版系统,组件自身需要的宽高
* 一般情况下,组件的宽高应该是由终端系统的排版引擎决定,组件开发者不需要实现此函数
* 但是部分场景下,组件开发者需要自己维护宽高,则需要开发者重写此函数
*/
NVMeasure(size : UTSSize) : UTSSize {
// size.width = 300.0.toFloat();
// size.height = 200.0.toFloat();
return size;
}
}
/**
* 定义按钮点击后触发回调的类
* [可选实现]
*/
class ButtonClickListener extends View.OnClickListener {
/**
* 如果需要在回调类或者代理类中对组件进行操作,比如调用组件方法,发送事件等,需要在该类中持有组件对应的原生类的对象
* 组件原生类的基类为 UTSComponent,该类是一个泛型类,需要接收一个类型变量,该类型变量就是原生组件的类型
*/
private comp : UTSComponent<Button>;
constructor(comp : UTSComponent<Button>) {
super();
this.comp = comp;
}
/**
* 按钮点击回调方法
*/
override onClick(v ?: View) {
console.log("按钮被点击");
// 发送事件
this.comp.$emit("buttonclick");
}
}
</script>
<style>
</style>
<template>
<view class="defaultStyles">
</view>
</template>
<script lang="uts">
/**
* 引用 iOS 系统库
* [可选实现,按需引入]
*/
import {
UIButton,
UIControl
} from "UIKit"
/**
* 引入三方库
* [可选实现,按需引入]
*
* 在 iOS 平台引入三方库有以下两种方式:
* 1、通过引入三方库framework 或者.a 等方式,需要将 .framework 放到 ./Frameworks 目录下,将.a 放到 ./Libs 目录下。更多信息[详见](https://uniapp.dcloud.net.cn/plugin/uts-plugin.html#ios-平台原生配置)
* 2、通过 cocoaPods 方式引入,将要引入的 pod 信息配置到 config.json 文件下的 dependencies-pods 字段下。详细配置方式[详见](https://uniapp.dcloud.net.cn/plugin/uts-ios-cocoapods.html)
*
* 在通过上述任意方式依赖三方库后,使用时需要在文件中 import:
* 示例:import { LottieAnimationView, LottieAnimation, LottieLoopMode } from 'Lottie'
*/
/**
* UTSiOS、UTSComponent 为平台内置对象,不需要 import 可直接调用其API,[详见](https://uniapp.dcloud.net.cn/uts/utsios.html)
*/
import { UTSComponent } from "DCloudUTSFoundation"
import { IObjItem } from '../interface.uts'
//原生提供以下属性或方法的实现
export default {
data() {
return {
};
},
/**
* 组件名称,也就是开发者使用的标签
*/
name: "test-props",
/**
* 组件涉及的事件声明,只有声明过的事件,才能被正常发送
*/
emits: ['buttonclick', 'numListChange', 'objListChange'],
/**
* 属性声明,组件的使用者会传递这些属性值到组件
*/
props: {
/**
* 字符串类型 属性:buttontext 需要设置默认值
*/
"buttontext": {
type: String,
default: "点击触发"
},
numList: {
type: Array as PropType<number[]>,
default: () => [] as number[]
},
objList: {
type: Array as PropType<IObjItem[]>,
default: () => [] as IObjItem[]
}
},
/**
* 组件内部变量声明
*/
/**
* 属性变化监听器实现
*/
watch: {
"buttontext": {
/**
* 这里监听属性变化,并进行组件内部更新
*/
handler(newValue : String, oldValue : String) {
this.$el.setTitle(newValue, for = UIControl.State.normal)
},
/**
* 创建时是否通过此方法更新属性,默认值为false
*/
immediate: false
},
numList: {
handler(newVal : number[], oldVal : number[]) {
let detail = new Map<string, any>()
detail.set("value", newVal)
let data = new Map<string, any>()
data.set("detail", detail)
this.$emit('numListChange', data)
},
immediate: true
},
objList: {
handler(newVal : any[], oldVal : any[]) {
let detail = new Map<string, any>()
detail.set("value", newVal)
let data = new Map<string, any>()
data.set("detail", detail)
this.$emit('objListChange', data)
},
immediate: true
}
},
/**
* 规则:如果没有配置expose,则methods中的方法均对外暴露,如果配置了expose,则以expose的配置为准向外暴露
* ['publicMethod'] 含义为:只有 `publicMethod` 在实例上可用
*/
expose: ['doSomething'],
methods: {
/**
* 对外公开的组件方法
* 在uni-app中调用组件方法,可以通过指定ref的方式,例如指定uts-button 标签的ref 为 ’button‘, 调用时使用:this.$refs["button"].doSomething('message');
*/
doSomething(paramA : string) {
// 这是组件的自定义方法
console.log(paramA, 'this is in uts-button component')
},
/**
* 内部使用的组件方法
*/
},
/**
* 组件被创建,组件第一个生命周期,
* 在内存中被占用的时候被调用,开发者可以在这里执行一些需要提前执行的初始化逻辑
* [可选实现]
*/
created() {
},
/**
* 对应平台的view载体即将被创建,对应前端beforeMount
* [可选实现]
*/
NVBeforeLoad() {
},
/**
* 创建原生View,必须定义返回值类型
* 开发者需要重点实现这个函数,声明原生组件被创建出来的过程,以及最终生成的原生组件类型
* [必须实现]
*/
NVLoad() : UIButton {
//必须实现
buttonClickListsner = new ButtonClickListsner(this)
let button = new UIButton()
button.setTitle(this.buttontext, for = UIControl.State.normal)
// 在 swift target-action 对应的方法需要以OC的方式来调用,那么OC语言中用Selector来表示一个方法的名称(又称方法选择器),创建一个Selector可以使用 Selector("functionName") 的方式。
const method = Selector("buttonClickAction")
if (buttonClickListsner != null) {
button.addTarget(buttonClickListsner!, action = method, for = UIControl.Event.touchUpInside)
}
return button
},
/**
* 原生View已创建
* [可选实现]
*/
NVLoaded() {
/**
* 通过 this.$el 来获取原生控件。
*/
this.$el.setTitle(this.buttontext, for = UIControl.State.normal)
},
/**
* 原生View布局完成
* [可选实现]
*/
NVLayouted() {
},
/**
* 原生View将释放
* [可选实现]
*/
NVBeforeUnload() { },
/**
* 原生View已释放,这里可以做释放View之后的操作
* [可选实现]
*/
NVUnloaded() {
},
/**
* 组件销毁
* [可选实现]
*/
unmounted() { }
/**
* 更多组件开发的信息详见:https://uniapp.dcloud.net.cn/plugin/uts-component.html
*/
}
/**
* 定义按钮点击后触发回调的类
* [可选实现]
*/
class ButtonClickListsner {
/**
* 如果需要在回调类或者代理类中对组件进行操作,比如调用组件方法,发送事件等,需要在该类中持有组件对应的原生类的对象。
* 组件原生类的基类为 UTSComponent,该类是一个泛型类,需要接收一个类型变量,该类型变量就是原生组件的类型。
*/
private component : UTSComponent<UIButton>
constructor(component : UTSComponent<UIButton>) {
this.component = component
super.init()
}
/**
* 按钮点击回调方法
* 在 swift 中,所有target-action (例如按钮的点击事件,NotificationCenter 的通知事件等)对应的 action 函数前面都要使用 @objc 进行标记。
* [可选实现]
*/
@objc buttonClickAction() {
console.log("按钮被点击")
// 发送事件
this.component.__$$emit("buttonclick");
}
}
/**
* 定义回调类或者代理类的实例
* [可选实现]
*/
let buttonClickListsner : ButtonClickListsner | null = null
</script>
<style>
</style>
export type IObjItem = {
id : string
}
export type IPropsChangeEventDetail = {
value : number
}
export interface IPropsChangeEvent extends UniVideoEvent {
detail : IPropsChangeEventDetail[]
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册