diff --git a/packages/code-generator/src/plugins/component/rax/const.ts b/packages/code-generator/src/plugins/component/rax/const.ts index d604b27e9..a0a07b550 100644 --- a/packages/code-generator/src/plugins/component/rax/const.ts +++ b/packages/code-generator/src/plugins/component/rax/const.ts @@ -2,6 +2,9 @@ export const RAX_CHUNK_NAME = { ClassDidMountBegin: 'RaxComponentClassDidMountBegin', ClassDidMountContent: 'RaxComponentClassDidMountContent', ClassDidMountEnd: 'RaxComponentClassDidMountEnd', + ClassWillUnmountBegin: 'RaxComponentClassWillUnmountBegin', + ClassWillUnmountContent: 'RaxComponentClassWillUnmountContent', + ClassWillUnmountEnd: 'RaxComponentClassWillUnmountEnd', ClassRenderBegin: 'RaxComponentClassRenderBegin', ClassRenderPre: 'RaxComponentClassRenderPre', ClassRenderJSX: 'RaxComponentClassRenderJSX', @@ -9,4 +12,7 @@ export const RAX_CHUNK_NAME = { MethodsBegin: 'RaxComponentMethodsBegin', MethodsContent: 'RaxComponentMethodsContent', MethodsEnd: 'RaxComponentMethodsEnd', + LifeCyclesBegin: 'RaxComponentLifeCyclesBegin', + LifeCyclesContent: 'RaxComponentLifeCyclesContent', + LifeCyclesEnd: 'RaxComponentLifeCyclesEnd', }; diff --git a/packages/code-generator/src/plugins/component/rax/containerClass.ts b/packages/code-generator/src/plugins/component/rax/containerClass.ts index 4ec4b6245..0db104861 100644 --- a/packages/code-generator/src/plugins/component/rax/containerClass.ts +++ b/packages/code-generator/src/plugins/component/rax/containerClass.ts @@ -84,12 +84,33 @@ const pluginFactory: BuilderComponentPluginFactory = () => { linkAfter: [RAX_CHUNK_NAME.ClassDidMountBegin, RAX_CHUNK_NAME.ClassDidMountContent], }); + next.chunks.push({ + type: ChunkType.STRING, + fileType: FileType.JSX, + name: RAX_CHUNK_NAME.ClassWillUnmountBegin, + content: `componentWillUnmount() {`, + linkAfter: [ + CLASS_DEFINE_CHUNK_NAME.Start, + CLASS_DEFINE_CHUNK_NAME.InsVar, + CLASS_DEFINE_CHUNK_NAME.InsMethod, + RAX_CHUNK_NAME.ClassDidMountEnd, + ], + }); + + next.chunks.push({ + type: ChunkType.STRING, + fileType: FileType.JSX, + name: RAX_CHUNK_NAME.ClassWillUnmountEnd, + content: `}`, + linkAfter: [RAX_CHUNK_NAME.ClassWillUnmountBegin, RAX_CHUNK_NAME.ClassWillUnmountContent], + }); + next.chunks.push({ type: ChunkType.STRING, fileType: FileType.JSX, name: RAX_CHUNK_NAME.ClassRenderBegin, content: 'render() {', - linkAfter: [RAX_CHUNK_NAME.ClassDidMountEnd], + linkAfter: [RAX_CHUNK_NAME.ClassDidMountEnd, RAX_CHUNK_NAME.ClassWillUnmountEnd], }); next.chunks.push({ diff --git a/packages/code-generator/src/plugins/component/rax/containerLifeCycle.ts b/packages/code-generator/src/plugins/component/rax/containerLifeCycle.ts index ea9d92ee2..ffbf56cc6 100644 --- a/packages/code-generator/src/plugins/component/rax/containerLifeCycle.ts +++ b/packages/code-generator/src/plugins/component/rax/containerLifeCycle.ts @@ -1,3 +1,4 @@ +import _ from 'lodash'; import { CLASS_DEFINE_CHUNK_NAME, DEFAULT_LINK_AFTER } from '../../../const/generator'; import { RAX_CHUNK_NAME } from './const'; @@ -7,7 +8,6 @@ import { BuilderComponentPlugin, BuilderComponentPluginFactory, ChunkType, - CodeGeneratorError, FileType, ICodeChunk, ICodeStruct, @@ -34,53 +34,86 @@ const pluginFactory: BuilderComponentPluginFactory = (config?) => ...pre, }; - // TODO: Rax 程序的生命周期暂未明确,此处先屏蔽 - // @see https://yuque.antfin-inc.com/mo/spec/spec-low-code-building-schema#XMeF5 + // Rax 先只支持 didMount 和 willUnmount 吧 - // const ir = next.ir as IContainerInfo; + const ir = next.ir as IContainerInfo; + const lifeCycles = ir.lifeCycles; - // if (ir.lifeCycles) { - // const lifeCycles = ir.lifeCycles; - // const chunks = Object.keys(lifeCycles).map(lifeCycleName => { - // const normalizeName = cfg.normalizeNameMapping[lifeCycleName] || lifeCycleName; - // const exportName = cfg.exportNameMapping[lifeCycleName] || lifeCycleName; - // if (normalizeName === 'constructor') { - // return { - // type: ChunkType.STRING, - // fileType: cfg.fileType, - // name: CLASS_DEFINE_CHUNK_NAME.ConstructorContent, - // content: getFuncExprBody( - // (lifeCycles[lifeCycleName] as JSExpression).value, - // ), - // linkAfter: [...DEFAULT_LINK_AFTER[CLASS_DEFINE_CHUNK_NAME.ConstructorStart]], - // }; - // } - // if (normalizeName === 'render') { - // return { - // type: ChunkType.STRING, - // fileType: cfg.fileType, - // name: RAX_CHUNK_NAME.ClassRenderPre, - // content: getFuncExprBody( - // (lifeCycles[lifeCycleName] as JSExpression).value, - // ), - // linkAfter: [RAX_CHUNK_NAME.ClassRenderStart], - // }; - // } + if (lifeCycles && !_.isEmpty(lifeCycles)) { + Object.entries(lifeCycles).forEach(([lifeCycleName, lifeCycleMethodExpr]) => { + const normalizeName = cfg.normalizeNameMapping[lifeCycleName] || lifeCycleName; + const exportName = cfg.exportNameMapping[lifeCycleName] || lifeCycleName; - // return { - // type: ChunkType.STRING, - // fileType: cfg.fileType, - // name: CLASS_DEFINE_CHUNK_NAME.InsMethod, - // content: transformFuncExpr2MethodMember( - // exportName, - // (lifeCycles[lifeCycleName] as JSExpression).value, - // ), - // linkAfter: [...DEFAULT_LINK_AFTER[CLASS_DEFINE_CHUNK_NAME.InsMethod]], - // }; - // }); + next.chunks.push({ + type: ChunkType.STRING, + fileType: cfg.fileType, + name: RAX_CHUNK_NAME.LifeCyclesContent, + content: `${exportName}: (${lifeCycleMethodExpr.value}),`, + linkAfter: [RAX_CHUNK_NAME.LifeCyclesBegin], + }); - // next.chunks.push.apply(next.chunks, chunks); - // } + if (normalizeName === 'didMount') { + next.chunks.push({ + type: ChunkType.STRING, + fileType: cfg.fileType, + name: RAX_CHUNK_NAME.ClassDidMountContent, + content: `this._lifeCycles.${exportName}();`, + linkAfter: [RAX_CHUNK_NAME.ClassDidMountBegin], + }); + } else if (normalizeName === 'willUnmount') { + next.chunks.push({ + type: ChunkType.STRING, + fileType: cfg.fileType, + name: RAX_CHUNK_NAME.ClassWillUnmountContent, + content: `this._lifeCycles.${exportName}();`, + linkAfter: [RAX_CHUNK_NAME.ClassWillUnmountBegin], + }); + } else { + // TODO: print warnings? Unknown life cycle + } + }); + + next.chunks.push({ + type: ChunkType.STRING, + fileType: cfg.fileType, + name: CLASS_DEFINE_CHUNK_NAME.InsVar, + content: `_lifeCycles = this._defineLifeCycles();`, + linkAfter: [CLASS_DEFINE_CHUNK_NAME.Start], + }); + + next.chunks.push({ + type: ChunkType.STRING, + fileType: cfg.fileType, + name: RAX_CHUNK_NAME.LifeCyclesBegin, + content: ` + _defineLifeCycles() { + const __$$lifeCycles = ({ + `, + linkAfter: [RAX_CHUNK_NAME.ClassRenderEnd, CLASS_DEFINE_CHUNK_NAME.InsPrivateMethod], + }); + + next.chunks.push({ + type: ChunkType.STRING, + fileType: cfg.fileType, + name: RAX_CHUNK_NAME.LifeCyclesEnd, + content: ` + }); + + // 为所有的方法绑定上下文 + Object.entries(__$$lifeCycles).forEach(([lifeCycleName, lifeCycleMethod]) => { + if (typeof lifeCycleMethod === 'function') { + __$$lifeCycles[lifeCycleName] = (...args) => { + return lifeCycleMethod.apply(this._context, args); + } + } + }); + + return __$$lifeCycles; + } + `, + linkAfter: [RAX_CHUNK_NAME.LifeCyclesBegin, RAX_CHUNK_NAME.LifeCyclesContent], + }); + } return next; }; diff --git a/packages/code-generator/src/plugins/component/rax/containerMethods.ts b/packages/code-generator/src/plugins/component/rax/containerMethods.ts index 50b3b16a3..12621e6e2 100644 --- a/packages/code-generator/src/plugins/component/rax/containerMethods.ts +++ b/packages/code-generator/src/plugins/component/rax/containerMethods.ts @@ -46,7 +46,11 @@ const pluginFactory: BuilderComponentPluginFactory = (config?) => _defineMethods() { const __$$methods = ({ `, - linkAfter: [RAX_CHUNK_NAME.ClassRenderEnd, CLASS_DEFINE_CHUNK_NAME.InsPrivateMethod], + linkAfter: [ + RAX_CHUNK_NAME.ClassRenderEnd, + CLASS_DEFINE_CHUNK_NAME.InsPrivateMethod, + RAX_CHUNK_NAME.LifeCyclesEnd, + ], }); next.chunks.push({ diff --git a/packages/code-generator/test-cases/rax-app/demo1/expected/demo-project/src/pages/Home/index.jsx b/packages/code-generator/test-cases/rax-app/demo1/expected/demo-project/src/pages/Home/index.jsx index bb5687ef1..1aa9f7e69 100644 --- a/packages/code-generator/test-cases/rax-app/demo1/expected/demo-project/src/pages/Home/index.jsx +++ b/packages/code-generator/test-cases/rax-app/demo1/expected/demo-project/src/pages/Home/index.jsx @@ -28,6 +28,8 @@ class Home$$Page extends Component { this._dataSourceEngine.reloadDataSource(); } + componentWillUnmount() {} + render() { const __$$context = this._context; diff --git a/packages/code-generator/test-cases/rax-app/demo2/expected/demo-project/src/pages/Home/index.jsx b/packages/code-generator/test-cases/rax-app/demo2/expected/demo-project/src/pages/Home/index.jsx index 42be52c51..067cb0830 100644 --- a/packages/code-generator/test-cases/rax-app/demo2/expected/demo-project/src/pages/Home/index.jsx +++ b/packages/code-generator/test-cases/rax-app/demo2/expected/demo-project/src/pages/Home/index.jsx @@ -43,8 +43,16 @@ class Home$$Page extends Component { _utils = this._defineUtils(); + _lifeCycles = this._defineLifeCycles(); + componentDidMount() { this._dataSourceEngine.reloadDataSource(); + + this._lifeCycles.didMount(); + } + + componentWillUnmount() { + this._lifeCycles.willUnmount(); } render() { @@ -179,6 +187,29 @@ class Home$$Page extends Component { return utils; } + _defineLifeCycles() { + const __$$lifeCycles = { + didMount: function didMount() { + this.utils.Toast.show(`Hello ${this.state.user.name}!`); + }, + + willUnmount: function didMount() { + this.utils.Toast.show(`Bye, ${this.state.user.name}!`); + }, + }; + + // 为所有的方法绑定上下文 + Object.entries(__$$lifeCycles).forEach(([lifeCycleName, lifeCycleMethod]) => { + if (typeof lifeCycleMethod === 'function') { + __$$lifeCycles[lifeCycleName] = (...args) => { + return lifeCycleMethod.apply(this._context, args); + }; + } + }); + + return __$$lifeCycles; + } + _defineMethods() { const __$$methods = { hello: function hello() { diff --git a/packages/code-generator/test-cases/rax-app/demo2/schema.json5 b/packages/code-generator/test-cases/rax-app/demo2/schema.json5 index dabdd8aa1..ec1221ca6 100644 --- a/packages/code-generator/test-cases/rax-app/demo2/schema.json5 +++ b/packages/code-generator/test-cases/rax-app/demo2/schema.json5 @@ -52,7 +52,16 @@ ], }, props: {}, - lifeCycles: {}, + lifeCycles: { + didMount: { + type: 'JSExpression', + value: 'function didMount(){\n this.utils.Toast.show(`Hello ${this.state.user.name}!`);\n}', + }, + willUnmount: { + type: 'JSExpression', + value: 'function didMount(){\n this.utils.Toast.show(`Bye, ${this.state.user.name}!`);\n}', + }, + }, methods: { hello: { type: 'JSExpression',