Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[feat]rn-relatons #1679

Merged
merged 19 commits into from
Jan 21, 2025
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/core/src/convertor/wxToReact.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
import { implemented } from '../core/implement'

// 暂不支持的wx选项,后期需要各种花式支持
const unsupported = ['relations', 'moved', 'definitionFilter', 'onShareAppMessage']
const unsupported = ['moved', 'definitionFilter', 'onShareAppMessage']

function convertErrorDesc (key) {
error(`Options.${key} is not supported in runtime conversion from wx to react native.`, global.currentResource)
Expand Down
3 changes: 2 additions & 1 deletion packages/core/src/platform/builtInMixins/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ export default function getBuiltInMixins ({ type, rawOptions = {} }) {
directiveHelperMixin(),
styleHelperMixin(),
refsMixin(),
i18nMixin()
i18nMixin(),
relationsMixin(type)
]
} else if (__mpx_mode__ === 'web') {
bulitInMixins = [
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import relationsMixin from './relationsMixin.ios'
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

现在不需要加这个额外的.android了,文件条件编译找不到.android会自动去找.ios

export default relationsMixin
101 changes: 101 additions & 0 deletions packages/core/src/platform/builtInMixins/relationsMixin.ios.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import { BEFORECREATE, MOUNTED, BEFOREUNMOUNT } from '../../core/innerLifecycle'
import { isArray } from '@mpxjs/utils'

const relationTypeMap = {
parent: 'child',
ancestor: 'descendant'
}

const isChildNode = (target, instance) => {
let children = target.props.children
if (!children) return
if (!isArray(children)) {
children = [children]
}
return children.some((item = {}) => {
if (item.type?.__mpxBuiltIn) { // 如果是基础节点,继续向下查找
return isChildNode(item, instance)
} else {
return item.type === instance.__getReactFunctionComponent()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

光凭这个ctor也没办法判断当前的instance一定是child吧

}
})
}

export default function relationsMixin (mixinType) {
if (mixinType === 'component') {
return {
[BEFORECREATE] () {
this.__mpxRelations = {}
this.__mpxRelationNodesMap = {}
},
[MOUNTED] () {
this.__mpxCollectRelations()
this.__mpxExecRelations('linked')
},
[BEFOREUNMOUNT] () {
this.__mpxExecRelations('unlinked')
this.__mpxRelations = {}
this.__mpxRelationNodesMap = {}
},
methods: {
getRelationNodes (path) {
return this.__mpxRelationNodesMap(path) || null
},
__mpxCollectRelations () {
Copy link
Collaborator

@hiyuki hiyuki Jan 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

感觉这个循环还有this.__mpxRelations有点多余,如果下面的this.__relation是按需生成的这里可以省略很多逻辑

const relations = this.__mpxProxy.options.relations
if (!relations) return
Object.keys(relations).forEach(path => {
const relation = relations[path]
this.__mpxCheckParent(this, relation, path)
})
},
__mpxCheckParent (current, relation, path) {
const type = relation.type
const target = current.__getRelation()
if (!target) return

// parent 只需要处理一层,ancestor 需要考虑多个层级
if ((type === 'parent' && isChildNode(target, this)) || type === 'ancestor') {
const targetRelation = target.__mpxProxy.options.relations?.[this.__componentPath]
if (targetRelation && targetRelation.type === relationTypeMap[type] && target.__componentPath === path) {
this.__mpxRelations[path] = {
target,
targetRelation,
relation
}
this.__mpxRelationNodesMap[path] = [target]
} else if (type === 'ancestor') {
this.__mpxCheckParent(target, relation, path)
}
}
},
__mpxExecRelations (type) {
Object.keys(this.__mpxRelations).forEach(path => {
const { target, targetRelation, relation } = this.__mpxRelations[path]
const currentPath = this.__componentPath
if (type === 'linked') {
this.__mpxLinkRelationNodes(target, currentPath)
} else if (type === 'unlinked') {
this.__mpxRemoveRelationNodes(target, currentPath)
}
if (typeof targetRelation[type] === 'function') {
targetRelation[type].call(target, this)
}
if (typeof relation[type] === 'function') {
relation[type].call(this, target)
}
})
},
__mpxLinkRelationNodes (target, path) {
target.__mpxRelationNodesMap[path] = target.__mpxRelationNodesMap[path] || [] // 父级绑定子级
target.__mpxRelationNodesMap[path].push(this)
},
__mpxRemoveRelationNodes (target, path) {
const arr = target.__mpxRelationNodesMap[path] || []
const index = arr.indexOf(this)
if (index !== -1) arr.splice(index, 1)
}
}
}
}
}
37 changes: 34 additions & 3 deletions packages/core/src/platform/patch/react/getDefaultOptions.ios.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,14 +68,20 @@ function getRootProps (props) {
return rootProps
}

function createInstance ({ propsRef, type, rawOptions, currentInject, validProps, components, pageId }) {
function createInstance ({ propsRef, type, rawOptions, currentInject, validProps, components, pageId, relation, reactFunctionComponent }) {
const instance = Object.create({
setData (data, callback) {
return this.__mpxProxy.forceUpdate(data, { sync: true }, callback)
},
getPageId () {
return pageId
},
__getRelation () {
return relation
},
__getReactFunctionComponent () {
return reactFunctionComponent
},
__getProps () {
const props = propsRef.current
const propsData = {}
Expand Down Expand Up @@ -127,6 +133,7 @@ function createInstance ({ propsRef, type, rawOptions, currentInject, validProps
}
return null
},
__componentPath: currentInject.componentPath,
__injectedRender: currentInject.render || noop,
__getRefsData: currentInject.getRefsData || noop,
// render helper
Expand Down Expand Up @@ -342,19 +349,33 @@ function usePageStatus (navigation, pageId) {
}, [navigation])
}

const needRelationContext = (options) => {
const relations = options.relations
if (!relations) return false
return Object.keys(relations).some(path => {
const relation = relations[path]
const type = relation.type
return type === 'child' || type === 'descendant'
})
}

const RelationsContext = createContext(null)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

context可以以outputPath为key存一系列


export function getDefaultOptions ({ type, rawOptions = {}, currentInject }) {
rawOptions = mergeOptions(rawOptions, type, false)
const components = Object.assign({}, rawOptions.components, currentInject.getComponents())
const validProps = Object.assign({}, rawOptions.props, rawOptions.properties)
const defaultOptions = memo(forwardRef((props, ref) => {
const instanceRef = useRef(null)
const propsRef = useRef(null)
const reactFunctionComponent = defaultOptions
const pageId = useContext(RouteContext)
const relation = useContext(RelationsContext)
propsRef.current = props
let isFirst = false
if (!instanceRef.current) {
isFirst = true
instanceRef.current = createInstance({ propsRef, type, rawOptions, currentInject, validProps, components, pageId })
instanceRef.current = createInstance({ propsRef, type, rawOptions, currentInject, validProps, components, pageId, relation, reactFunctionComponent })
}
const instance = instanceRef.current
useImperativeHandle(ref, () => {
Expand Down Expand Up @@ -403,7 +424,17 @@ export function getDefaultOptions ({ type, rawOptions = {}, currentInject }) {

useSyncExternalStore(proxy.subscribe, proxy.getSnapshot)

const root = rawOptions.options?.disableMemo ? proxy.effect.run() : useMemo(() => proxy.effect.run(), [proxy.stateVersion])
const runRenderEffect = () => {
if (needRelationContext(rawOptions)) {
return createElement(RelationsContext.Provider, {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

直接包在root上就可以了吧

value: instance
}, proxy.effect.run())
}

return proxy.effect.run()
}

const root = rawOptions.options?.disableMemo ? runRenderEffect() : useMemo(runRenderEffect, [proxy.stateVersion])
if (root) {
const rootProps = getRootProps(props)
rootProps.style = { ...root.props.style, ...rootProps.style }
Expand Down
3 changes: 2 additions & 1 deletion packages/webpack-plugin/lib/react/processScript.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ module.exports = function (script, {
moduleId,
isProduction,
jsonConfig,
outputPath,
builtInComponentsMap,
localComponentsMap,
localPagesMap
Expand Down Expand Up @@ -65,7 +66,7 @@ global.__navigationHelper = {
jsonConfig
})

output += buildGlobalParams({ moduleId, scriptSrcMode, loaderContext, isProduction, ctorType, jsonConfig, componentsMap })
output += buildGlobalParams({ moduleId, scriptSrcMode, loaderContext, isProduction, ctorType, jsonConfig, componentsMap, outputPath })
output += getRequireScript({ ctorType, script, loaderContext })
output += `export default global.__mpxOptionsMap[${JSON.stringify(moduleId)}]\n`
}
Expand Down
4 changes: 3 additions & 1 deletion packages/webpack-plugin/lib/react/script-helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,8 @@ function buildGlobalParams ({
jsonConfig,
componentsMap,
pagesMap,
firstPage
firstPage,
outputPath
}) {
let content = ''
if (ctorType === 'app') {
Expand Down Expand Up @@ -117,6 +118,7 @@ global.currentInject.firstPage = ${JSON.stringify(firstPage)}\n`
content += `global.currentInject.getComponents = function () {
return ${shallowStringify(componentsMap)}
}\n`
content += `global.currentInject.componentPath = '/' + ${JSON.stringify(outputPath)}\n`
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

页面是不是不用注入

}
content += `global.currentModuleId = ${JSON.stringify(moduleId)}\n`
content += `global.currentSrcMode = ${JSON.stringify(scriptSrcMode)}\n`
Expand Down
Loading