Skip to content

Commit

Permalink
intersectionobserver cr update layout compute
Browse files Browse the repository at this point in the history
  • Loading branch information
human committed Nov 1, 2024
2 parents d16d5e9 + 850c554 commit 56921fe
Show file tree
Hide file tree
Showing 12 changed files with 83 additions and 51 deletions.
1 change: 1 addition & 0 deletions docs-vuepress/api/compile.md
Original file line number Diff line number Diff line change
Expand Up @@ -965,6 +965,7 @@ module.exports = defineConfig({
}
})
```
**注意:** 建议使用`autoVirtualHostRules`配置项,不要使用微信组件内部的 options virtualHost 配置,因为组件内部的 options virtualHost 在跨平台输出时无法进行兼容抹平处理。

### partialCompileRules

Expand Down
4 changes: 2 additions & 2 deletions docs-vuepress/api/extend.md
Original file line number Diff line number Diff line change
Expand Up @@ -773,8 +773,8 @@ Mpx框架项目包体积可以进行分组、分包、页面、冗余Npm包等
entryRules: {
include: ['@somegroup/someSdk/index', '@somegroup/someSdk2/index']
},
// 有的时候你可能希望计算纯 js 入口引入的体积(不包含组件和页面),这种情况下需要使用 noEntryModules
noEntryModules: {
// 有的时候你可能希望计算纯 js 入口引入的体积(不包含组件和页面),这种情况下需要使用 noEntryRules
noEntryRules: {
include: 'src/lib/sdk.js'
}
}
Expand Down
2 changes: 1 addition & 1 deletion docs-vuepress/articles/unit-test.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ In computer programming, unit testing is a software testing method by which indi
- 改进设计,促进重构

此外,单测高覆盖率的项目也会给公司节省大量支出:
![unit-test-money.png](https://cdn.nlark.com/yuque/0/2021/png/116604/1640872105786-b7b230b4-7f65-4a0f-8dbf-e8ba89f61837.png#averageHue=%23fcfbfa&clientId=u39d6bb3b-719d-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=577&id=u3d713c30&margin=%5Bobject%20Object%5D&name=unit-test-money.png&originHeight=1153&originWidth=2770&originalType=binary&ratio=1&rotation=0&showTitle=false&size=227658&status=done&style=none&taskId=u33caf2b6-ceb2-43ca-9da2-c631d845598&title=&width=1385)
![unit-test-money.png](https://cdn.nlark.com/yuque/0/2021/png/116604/1640872105786-b7b230b4-7f65-4a0f-8dbf-e8ba89f61837.png)
据微软统计单测效益结果,如上图展示,绝大多数bug都是在coding阶段产生,并且随着需求开发进度的推进,修复bug的成本会随指数级增长,当我们在unit test阶段发现并修复这个bug是能给公司带来巨大收益的。

下方介绍两种常见的项目单元测试规范:
Expand Down
4 changes: 2 additions & 2 deletions docs-vuepress/guide/advance/size-report.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,8 @@ module.exports = defineConfig({
entryRules: {
include: ['@somegroup/someSdk/index', '@somegroup/someSdk2/index']
},
// 有的时候你可能希望计算纯 js 入口引入的体积(不包含组件和页面),这种情况下需要使用 noEntryModules
noEntryModules: {
// 有的时候你可能希望计算纯 js 入口引入的体积(不包含组件和页面),这种情况下需要使用 noEntryRules
noEntryRules: {
include: 'src/lib/sdk.js'
}
}
Expand Down
2 changes: 1 addition & 1 deletion docs-vuepress/guide/basic/intro.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ Mpx使用类似Vue的单文件开发模式,小程序原本的template/js/style
* [wx:show隐藏显示](./conditional-render.md)
* [component动态组件](./component.md#动态组件)
* [事件处理内联传参](./event.md)
* [模板条件编译](./template.md)
* [模板条件编译](./conditional-render.md)

### 极致性能

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { getFocusedNavigation } from '../../../common/js'

const WindowRefStr = 'window'
const IgnoreTarget = 'ignore'
const DefaultMargin = { top: 0, bottom: 0, left: 0, right: 0 }
let idCount = 0

class RNIntersectionObserver {
Expand All @@ -22,7 +23,7 @@ class RNIntersectionObserver {

this.observerRefs = null
this.relativeRef = null
this.margins = { top: 0, bottom: 0, left: 0, right: 0 }
this.margins = DefaultMargin
this.callback = noop

this.throttleMeasure = this.getThrottleMeasure(options.throttleTime || 100)
Expand All @@ -39,7 +40,7 @@ class RNIntersectionObserver {
}

// 支持传递ref 或者 selector
relativeTo (selector, margins) {
relativeTo (selector, margins = {}) {
let relativeRef
if (isString(selector)) {
relativeRef = this.component.__selectRef(selector, 'node')
Expand All @@ -49,16 +50,16 @@ class RNIntersectionObserver {
}
if (relativeRef) {
this.relativeRef = relativeRef
this.margins = margins || this.margins
this.margins = Object.assign({}, DefaultMargin, margins)
} else {
console.warn(`node ${selector}is not found. The relative node for intersection observer will be ignored`)
}
return this
}

relativeToViewport (margins) {
relativeToViewport (margins = {}) {
this.relativeRef = WindowRefStr
this.margins = margins || this.margins
this.margins = Object.assign({}, DefaultMargin, margins)
return this
}

Expand Down Expand Up @@ -86,13 +87,20 @@ class RNIntersectionObserver {
if (this.windowRect) return this.windowRect
const navigation = getFocusedNavigation()
const screen = Dimensions.get('screen')
const visibleHeight = navigation.layout.height ? (navigation.layout.height + navigation.headerHeight) : screen.height
const navigationLayout = navigation.layout || {
x: 0,
y: 0,
width: screen.width,
height: screen.height
}

const windowRect = {
top: navigation.isCustomHeader ? this.margins.top : navigation.headerHeight,
left: this.margins.left,
right: screen.width - this.margins.right,
bottom: visibleHeight - this.margins.bottom
top: navigationLayout.y + this.margins.top,
left: navigationLayout.x + this.margins.left,
right: navigationLayout.width - this.margins.right,
bottom: navigationLayout.y + navigationLayout.height - this.margins.bottom
}

this.windowRect = windowRect
return this.windowRect
}
Expand Down
22 changes: 19 additions & 3 deletions packages/core/src/core/proxy.js
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ export default class MpxProxy {
this.uid = uid++
this.name = options.name || ''
this.options = options
this.ignoreReactivePattern = this.options.options?.ignoreReactivePattern
// beforeCreate -> created -> mounted -> unmounted
this.state = BEFORECREATE
this.ignoreProxyMap = makeMap(Mpx.config.ignoreProxyWhiteList)
Expand Down Expand Up @@ -135,6 +136,21 @@ export default class MpxProxy {
this.initApi()
}

processIgnoreReactive (obj) {
if (this.ignoreReactivePattern && isObject(obj)) {
Object.keys(obj).forEach((key) => {
if (this.ignoreReactivePattern.test(key)) {
Object.defineProperty(obj, key, {
enumerable: true,
// set configurable to false to skip defineReactive
configurable: false
})
}
})
}
return obj
}

created () {
if (__mpx_dynamic_runtime__) {
// 缓存上下文,在 destoryed 阶段删除
Expand Down Expand Up @@ -254,7 +270,7 @@ export default class MpxProxy {
} else {
this.props = diffAndCloneA(this.target.__getProps(this.options)).clone
}
reactive(this.props)
reactive(this.processIgnoreReactive(this.props))
proxy(this.target, this.props, undefined, false, this.createProxyConflictHandler('props'))
}

Expand Down Expand Up @@ -292,7 +308,7 @@ export default class MpxProxy {
if (isFunction(dataFn)) {
Object.assign(this.data, callWithErrorHandling(dataFn.bind(this.target), this, 'data function'))
}
reactive(this.data)
reactive(this.processIgnoreReactive(this.data))
proxy(this.target, this.data, undefined, false, this.createProxyConflictHandler('data'))
this.collectLocalKeys(this.data)
}
Expand Down Expand Up @@ -424,7 +440,7 @@ export default class MpxProxy {
if (hasOwn(renderData, key)) {
const data = renderData[key]
const firstKey = getFirstKey(key)
if (!this.localKeysMap[firstKey]) {
if (!this.localKeysMap[firstKey] || (this.ignoreReactivePattern && this.ignoreReactivePattern.test(firstKey))) {
continue
}
// 外部clone,用于只需要clone的场景
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { isObject, isArray, dash2hump, isFunction, cached } from '@mpxjs/utils'
import { isObject, isArray, dash2hump, isFunction, cached, getFocusedNavigation } from '@mpxjs/utils'
import { Dimensions, StyleSheet } from 'react-native'
import { getWindowInfo } from '@mpxjs/api-proxy'

function rpx (value) {
const { width } = Dimensions.get('screen')
Expand All @@ -13,8 +12,9 @@ function vw (value) {
return value * width / 100
}
function vh (value) {
const { windowHeight } = getWindowInfo()
return value * windowHeight / 100
const navigation = getFocusedNavigation()
const height = navigation?.layout?.height || Dimensions.get('screen').height
return value * height / 100
}

const unit = {
Expand Down
2 changes: 2 additions & 0 deletions packages/core/src/platform/patch/builtInKeysMap.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ if (__mpx_mode__ === 'web') {
builtInKeys = [
'proto',
'mixins',
'initData',
'mpxCustomKeysForBlend',
'mpxConvertMode',
'mpxFileResource',
Expand All @@ -20,6 +21,7 @@ if (__mpx_mode__ === 'web') {
'dataFn',
'proto',
'mixins',
'initData',
'watch',
'computed',
'mpxCustomKeysForBlend',
Expand Down
53 changes: 29 additions & 24 deletions packages/core/src/platform/patch/react/getDefaultOptions.ios.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useEffect, useLayoutEffect, useSyncExternalStore, useRef, useMemo, createElement, memo, forwardRef, useImperativeHandle, useContext, createContext, Fragment, cloneElement } from 'react'
import { useEffect, useLayoutEffect, useSyncExternalStore, useRef, useMemo, useCallback, createElement, memo, forwardRef, useImperativeHandle, useContext, createContext, Fragment, cloneElement } from 'react'
import * as ReactNative from 'react-native'
import { ReactiveEffect } from '../../../observer/effect'
import { watch } from '../../../observer/watch'
Expand Down Expand Up @@ -416,18 +416,14 @@ export function getDefaultOptions ({ type, rawOptions = {}, currentInject }) {
}))

if (type === 'page') {
const { Provider, useSafeAreaInsets, GestureHandlerRootView, useHeaderHeight } = global.__navigationHelper
const { Provider, useSafeAreaInsets, GestureHandlerRootView } = global.__navigationHelper
const pageConfig = Object.assign({}, global.__mpxPageConfig, currentInject.pageConfig)
const Page = ({ navigation, route }) => {
const rootRef = useRef(null)
const currentPageId = useMemo(() => ++pageId, [])
const intersectionObservers = useRef({})
usePageStatus(navigation, currentPageId)

useLayoutEffect(() => {
rootRef.current?.measureInWindow((x, y, width, height) => {
navigation.layout = { x, y, width, height }
})
const isCustom = pageConfig.navigationStyle === 'custom'
let opt = {}
if (__mpx_mode__ === 'android') {
Expand All @@ -453,39 +449,48 @@ export function getDefaultOptions ({ type, rawOptions = {}, currentInject }) {
...opt
})
}, [])

const rootRef = useRef(null)
const onLayout = useCallback(() => {
rootRef.current?.measureInWindow((x, y, width, height) => {
navigation.layout = { x, y, width, height }
})
}, [])

navigation.insets = useSafeAreaInsets()
navigation.headerHeight = useHeaderHeight()
navigation.isCustomHeader = pageConfig.navigationStyle === 'custom'

return createElement(GestureHandlerRootView,
{
null,
createElement(ReactNative.View, {
style: {
flex: 1,
backgroundColor: pageConfig.backgroundColor || '#ffffff'
},
ref: rootRef
ref: rootRef,
onLayout
},
// todo custom portal host for active route
createElement(Provider,
null,
createElement(RouteContext.Provider,
{
value: currentPageId
},
createElement(IntersectionObserverContext.Provider,
createElement(Provider,
null,
createElement(RouteContext.Provider,
{
value: currentPageId
},
createElement(IntersectionObserverContext.Provider,
{
value: intersectionObservers.current
},
createElement(defaultOptions,
{
navigation,
route,
id: currentPageId
}
createElement(defaultOptions,
{
navigation,
route,
id: currentPageId
}
)
)
)
)
)
// todo custom portal host for active route
)
}
return Page
Expand Down
4 changes: 3 additions & 1 deletion packages/core/src/platform/patch/wx/getDefaultOptions.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,13 @@ function transformProperties (properties) {
} else {
newFiled = Object.assign({}, rawFiled)
}
newFiled.observer = function (value) {
const rawObserver = rawFiled?.observer
newFiled.observer = function (value, oldValue) {
if (this.__mpxProxy) {
this[key] = value
this.__mpxProxy.propsUpdated()
}
rawObserver && rawObserver.call(this, value, oldValue)
}
newProps[key] = newFiled
})
Expand Down
4 changes: 1 addition & 3 deletions packages/webpack-plugin/lib/react/processScript.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ module.exports = function (script, {
import { getComponent } from ${stringifyRequest(loaderContext, optionProcessorPath)}
import { NavigationContainer, createNavigationContainerRef, StackActions } from '@react-navigation/native'
import { createNativeStackNavigator } from '@react-navigation/native-stack'
import { useHeaderHeight } from '@react-navigation/elements';
import { Provider } from '@ant-design/react-native'
import { SafeAreaProvider, useSafeAreaInsets } from 'react-native-safe-area-context'
import { GestureHandlerRootView } from 'react-native-gesture-handler'
Expand All @@ -39,8 +38,7 @@ global.__navigationHelper = {
GestureHandlerRootView: GestureHandlerRootView,
Provider: Provider,
SafeAreaProvider: SafeAreaProvider,
useSafeAreaInsets: useSafeAreaInsets,
useHeaderHeight: useHeaderHeight
useSafeAreaInsets: useSafeAreaInsets
}\n`
const { pagesMap, firstPage } = buildPagesMap({
localPagesMap,
Expand Down

0 comments on commit 56921fe

Please sign in to comment.