diff --git a/packages/api-proxy/src/platform/api/system/index.web.js b/packages/api-proxy/src/platform/api/system/index.web.js index 2614aa2562..0e756b73d9 100644 --- a/packages/api-proxy/src/platform/api/system/index.web.js +++ b/packages/api-proxy/src/platform/api/system/index.web.js @@ -139,7 +139,7 @@ const getLaunchOptionsSync = function () { throwSSRWarning('getLaunchOptionsSync API is running in non browser environments') return } - return global.__mpxEnterOptions || {} + return global.__mpxLaunchOptions || {} } export { diff --git a/packages/api-proxy/src/platform/api/system/rnSystem.js b/packages/api-proxy/src/platform/api/system/rnSystem.js index 67c51e8605..f77ad76ef1 100644 --- a/packages/api-proxy/src/platform/api/system/rnSystem.js +++ b/packages/api-proxy/src/platform/api/system/rnSystem.js @@ -42,18 +42,11 @@ const getWindowInfo = function () { } const getLaunchOptionsSync = function () { - const options = global.__mpxEnterOptions || {} - const { path, scene, query } = options - return { - path, - scene, - query - } + return global.__mpxLaunchOptions || {} } const getEnterOptionsSync = function () { - const result = getLaunchOptionsSync() - return result + return global.__mpxEnterOptions || {} } export { diff --git a/packages/core/src/core/mergeOptions.js b/packages/core/src/core/mergeOptions.js index e89414a01a..517c7c41cb 100644 --- a/packages/core/src/core/mergeOptions.js +++ b/packages/core/src/core/mergeOptions.js @@ -168,11 +168,7 @@ function extractObservers (options) { cb = this[cb] } if (typeof cb === 'function') { - if (keyPathArr.length < 2) { - val = [val] - old = [old] - } - cb.call(this, ...val, ...old) + Array.isArray(val) ? cb.call(this, ...val) : cb.call(this, val) } }, deep, diff --git a/packages/core/src/platform/createApp.ios.js b/packages/core/src/platform/createApp.ios.js index b3ef722394..0ff35e24b5 100644 --- a/packages/core/src/platform/createApp.ios.js +++ b/packages/core/src/platform/createApp.ios.js @@ -1,13 +1,12 @@ import transferOptions from '../core/transferOptions' import builtInKeysMap from './patch/builtInKeysMap' -import { makeMap, spreadProp, getFocusedNavigation, hasOwn, extend } from '@mpxjs/utils' +import { makeMap, spreadProp, getFocusedNavigation, hasOwn } from '@mpxjs/utils' import { mergeLifecycle } from '../convertor/mergeLifecycle' import { LIFECYCLE } from '../platform/patch/lifecycle/index' import Mpx from '../index' import { createElement, memo, useRef, useEffect } from 'react' import * as ReactNative from 'react-native' import { Image } from 'react-native' -import { ref } from '../observer/ref' const appHooksMap = makeMap(mergeLifecycle(LIFECYCLE).app) @@ -30,22 +29,24 @@ function filterOptions (options, appData) { return newOptions } -function createAppInstance (appData) { - return extend({}, Mpx.prototype, appData) -} - -export default function createApp (option, config = {}) { +export default function createApp (options) { const appData = {} const { NavigationContainer, createStackNavigator, SafeAreaProvider } = global.__navigationHelper // app选项目前不需要进行转换 - const { rawOptions, currentInject } = transferOptions(option, 'app', false) + const { rawOptions, currentInject } = transferOptions(options, 'app', false) const defaultOptions = filterOptions(spreadProp(rawOptions, 'methods'), appData) - defaultOptions.onAppInit && defaultOptions.onAppInit() // 在页面script执行前填充getApp() global.getApp = function () { return appData } + + defaultOptions.onShow && global.__mpxAppCbs.show.push(defaultOptions.onShow.bind(appData)) + defaultOptions.onHide && global.__mpxAppCbs.hide.push(defaultOptions.onHide.bind(appData)) + defaultOptions.onError && global.__mpxAppCbs.error.push(defaultOptions.onError.bind(appData)) + defaultOptions.onUnhandledRejection && global.__mpxAppCbs.rejection.push(defaultOptions.onUnhandledRejection.bind(appData)) + defaultOptions.onAppInit && defaultOptions.onAppInit() + const pages = currentInject.getPages() || {} const firstPage = currentInject.firstPage const Stack = createStackNavigator() @@ -82,55 +83,48 @@ export default function createApp (option, config = {}) { } global.__mpxAppLaunched = false - - global.__mpxAppFocusedState = ref('show') + global.__mpxAppHotLaunched = false global.__mpxOptionsMap[currentInject.moduleId] = memo((props) => { - const instanceRef = useRef(null) - if (!instanceRef.current) { - instanceRef.current = createAppInstance(appData) - } - const instance = instanceRef.current + const firstRef = useRef(true) const initialRouteRef = useRef({ initialRouteName: firstPage, initialParams: {} }) - - if (!global.__mpxAppLaunched) { + if (firstRef.current) { + // 热启动情况下,app会被销毁重建,将__mpxAppHotLaunched重置保障路由等初始化逻辑正确执行 + global.__mpxAppHotLaunched = false + firstRef.current = false + } + if (!global.__mpxAppHotLaunched) { const { initialRouteName, initialParams } = Mpx.config.rnConfig.parseAppProps?.(props) || {} initialRouteRef.current.initialRouteName = initialRouteName || initialRouteRef.current.initialRouteName initialRouteRef.current.initialParams = initialParams || initialRouteRef.current.initialParams global.__mpxAppOnLaunch = (navigation) => { - global.__mpxAppLaunched = true const state = navigation.getState() Mpx.config.rnConfig.onStateChange?.(state) const current = state.routes[state.index] - global.__mpxEnterOptions = { + const options = { path: current.name, query: current.params, scene: 0, shareTicket: '', referrerInfo: {} } - defaultOptions.onLaunch && defaultOptions.onLaunch.call(instance, global.__mpxEnterOptions) - defaultOptions.onShow && defaultOptions.onShow.call(instance, global.__mpxEnterOptions) + global.__mpxEnterOptions = options + if (!global.__mpxAppLaunched) { + global.__mpxLaunchOptions = options + defaultOptions.onLaunch && defaultOptions.onLaunch.call(appData, options) + } + global.__mpxAppCbs.show.forEach((cb) => { + cb(options) + }) + global.__mpxAppLaunched = true + global.__mpxAppHotLaunched = true } } useEffect(() => { - if (defaultOptions.onShow) { - global.__mpxAppCbs.show.push(defaultOptions.onShow.bind(instance)) - } - if (defaultOptions.onHide) { - global.__mpxAppCbs.hide.push(defaultOptions.onHide.bind(instance)) - } - if (defaultOptions.onError) { - global.__mpxAppCbs.error.push(defaultOptions.onError.bind(instance)) - } - if (defaultOptions.onUnhandledRejection) { - global.__mpxAppCbs.rejection.push(defaultOptions.onUnhandledRejection.bind(instance)) - } - const changeSubscription = ReactNative.AppState.addEventListener('change', (currentState) => { if (currentState === 'active') { let options = global.__mpxEnterOptions diff --git a/packages/core/src/platform/createApp.js b/packages/core/src/platform/createApp.js index 0f35cf97e5..f3e847cb61 100644 --- a/packages/core/src/platform/createApp.js +++ b/packages/core/src/platform/createApp.js @@ -24,19 +24,23 @@ function filterOptions (options, appData) { return newOptions } -export default function createApp (option, config = {}) { - // 在App中挂载mpx对象供周边工具访问,如e2e测试 +export default function createApp (options, config = {}) { + const appData = {} + // app选项目前不需要进行转换 + const { rawOptions, currentInject } = transferOptions(options, 'app', false) const builtInMixins = [{ + // 在App中挂载mpx对象供周边工具访问,如e2e测试 getMpx () { return Mpx } }] - const appData = {} if (__mpx_mode__ === 'web') { builtInMixins.push({ - created () { - Object.assign(this, Mpx.prototype) + beforeCreate () { + // for vue provide vm access Object.assign(this, appData) + }, + created () { const current = this.$root.$options?.router?.currentRoute || {} const options = { path: current.path && current.path.replace(/^\//, ''), @@ -45,48 +49,41 @@ export default function createApp (option, config = {}) { shareTicket: '', referrerInfo: {} } + // web不分冷启动和热启动 global.__mpxEnterOptions = options - this.$options.onLaunch && this.$options.onLaunch.call(this, options) - if (isBrowser) { - if (this.$options.onShow) { - this.$options.onShow.call(this, options) - global.__mpxAppCbs.show.push(this.$options.onShow.bind(this)) - } - if (this.$options.onHide) { - global.__mpxAppCbs.hide.push(this.$options.onHide.bind(this)) - } - if (this.$options.onError) { - global.__mpxAppCbs.error.push(this.$options.onError.bind(this)) - } - if (this.$options.onUnhandledRejection) { - global.__mpxAppCbs.rejection.push(this.$options.onUnhandledRejection.bind(this)) - } - } + global.__mpxLaunchOptions = options + rawOptions.onLaunch && rawOptions.onLaunch.call(appData, options) + global.__mpxAppCbs.show.forEach((cb) => { + cb(options) + }) } }) } else { builtInMixins.push({ onLaunch () { - Object.assign(this, Mpx.prototype) + initAppProvides(rawOptions.provide, this) } }) } - // app选项目前不需要进行转换 - const { rawOptions, currentInject } = transferOptions(option, 'app', false) rawOptions.mixins = builtInMixins const defaultOptions = filterOptions(spreadProp(mergeOptions(rawOptions, 'app', false), 'methods'), appData) if (__mpx_mode__ === 'web') { - global.__mpxOptionsMap = global.__mpxOptionsMap || {} - global.__mpxOptionsMap[currentInject.moduleId] = defaultOptions global.getApp = function () { if (!isBrowser) { console.error('[Mpx runtime error]: Dangerous API! global.getApp method is running in non browser environments') } return appData } + if (isBrowser) { + defaultOptions.onShow && global.__mpxAppCbs.show.push(defaultOptions.onShow.bind(appData)) + defaultOptions.onHide && global.__mpxAppCbs.hide.push(defaultOptions.onHide.bind(appData)) + defaultOptions.onError && global.__mpxAppCbs.error.push(defaultOptions.onError.bind(appData)) + defaultOptions.onUnhandledRejection && global.__mpxAppCbs.rejection.push(defaultOptions.onUnhandledRejection.bind(appData)) + } + global.__mpxOptionsMap = global.__mpxOptionsMap || {} + global.__mpxOptionsMap[currentInject.moduleId] = defaultOptions } else { - initAppProvides(rawOptions) defaultOptions.onAppInit && defaultOptions.onAppInit() const ctor = config.customCtor || global.currentCtor || App ctor(defaultOptions) diff --git a/packages/core/src/platform/export/inject.js b/packages/core/src/platform/export/inject.js index ea73092074..bc387994d0 100644 --- a/packages/core/src/platform/export/inject.js +++ b/packages/core/src/platform/export/inject.js @@ -11,11 +11,10 @@ const providesMap = { global.__mpxProvidesMap = providesMap /** @internal createApp() 初始化应用层 scope provide */ -export function initAppProvides (appOptions) { - const provideOpt = appOptions.provide +export function initAppProvides (provideOpt, instance) { if (provideOpt) { const provided = isFunction(provideOpt) - ? callWithErrorHandling(provideOpt.bind(appOptions), appOptions, 'createApp provide function') + ? callWithErrorHandling(provideOpt.bind(instance), instance, 'createApp provide function') : provideOpt if (isObject(provided)) { providesMap.__app = provided diff --git a/packages/core/src/platform/patch/getDefaultOptions.ios.js b/packages/core/src/platform/patch/getDefaultOptions.ios.js index 5eb13cf28e..bba141723b 100644 --- a/packages/core/src/platform/patch/getDefaultOptions.ios.js +++ b/packages/core/src/platform/patch/getDefaultOptions.ios.js @@ -375,14 +375,10 @@ function usePageStatus (navigation, pageId) { global.__navigationHelper.transitionEndCallback = null } }) - const unWatchAppFocusedState = watch(global.__mpxAppFocusedState, (value) => { - pageStatusMap[pageId] = value - }) return () => { focusSubscription() blurSubscription() - unWatchAppFocusedState() transitionEndSubscription() del(pageStatusMap, pageId) } @@ -453,7 +449,7 @@ export function getDefaultOptions ({ type, rawOptions = {}, currentInject }) { useEffect(() => { if (type === 'page') { - if (!global.__mpxAppLaunched && global.__mpxAppOnLaunch) { + if (!global.__mpxAppHotLaunched && global.__mpxAppOnLaunch) { global.__mpxAppOnLaunch(props.navigation) } proxy.callHook(ONLOAD, [props.route.params || {}]) diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-scroll-view.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-scroll-view.tsx index b058e622dc..918a0cfb59 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-scroll-view.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-scroll-view.tsx @@ -332,6 +332,8 @@ const _ScrollView = forwardRef, S }, props) ) updateScrollOptions(e, { scrollLeft, scrollTop }) + onStartReached(e) + onEndReached(e) if (enableTriggerIntersectionObserver && intersectionObservers) { for (const key in intersectionObservers) { intersectionObservers[key].throttleMeasure()