Remax ist ein von Ant entwickeltes Open-Source-Framework zur Entwicklung kleiner Programme mit React, das eine Lösung ohne Syntaxbeschränkungen zur Laufzeit anwendet. Die gesamte Forschung ist hauptsächlich in drei Teile unterteilt: Laufzeitprinzip, Prinzip der Vorlagenwiedergabe und Kompilierungsprozess. Nach dem Lesen der meisten vorhandenen Artikel konzentrieren sie sich hauptsächlich auf die Laufzeit- und Vorlagenwiedergabeprinzipien von Reamx, aber die Einführung in den Prozess der Kompilierung des gesamten React-Codes in ein Miniprogramm wurde noch nicht gesehen. Dieser Artikel soll diese Lücke schließen. Die Grundstruktur von Remax:1. Die Laufzeitumgebung remax-runtime bietet benutzerdefinierte Renderer, die Verpackung von Hostkomponenten und Konfigurationsgeneratoren von React-Komponenten bis hin zu Apps, Seiten und Komponenten von Miniprogrammen // Benutzerdefinierter Renderer-Export { standardmäßig als Render} von „./render“; // Konfigurationsverarbeitung von app.js zum Miniprogramm App-Konstruktor export { Standard als createAppConfig } von './createAppConfig'; // Eine Reihe von Anpassungsprozessen von React an das Miniprogramm Page Page Builder export { default as createPageConfig } from './createPageConfig'; // Eine Reihe von Anpassungsprozessen von React-Komponenten an den Komponentenkonstruktor des Miniprogramms „custom component export { default as createComponentConfig } from './createComponentConfig'; // export { Standard als createNativeComponent } aus './createNativeComponent'; // Hostkomponenten wie View, Button, Canvas usw. generieren, die vom Applet-Export bereitgestellt werden { Standard als createHostComponent } von './createHostComponent'; exportiere { createPortal} aus './ReactPortal'; exportiere { RuntimeOptions, PluginDriver } aus '@remax/framework-shared'; exportiere * aus './hooks'; importiere { ReactReconcilerInst } aus './render'; exportiere const unstable_batchedUpdates = ReactReconcilerInst.batchedUpdates; Standard exportieren { unstable_batchedUpdates, }; 2. Remax-WeChat-Applet-bezogener Adapter importiere { promisify } von '@remax/framework-shared'; Deklarieren Sie const wx: WechatMiniprogram.Wx; exportiere const canIUse = wx.canIUse; exportiere const base64ToArrayBuffer = wx.base64ToArrayBuffer; exportiere const arrayBufferToBase64 = wx.arrayBufferToBase64; exportiere const getSystemInfoSync = wx.getSystemInfoSync; exportiere const getSystemInfo = promisify(wx.getSystemInfo); src/types/config.ts wird verwendet, um den Konfigurationsinhalt in Bezug auf Seite und App des Miniprogramms anzupassen. /** Seitenkonfigurationsdatei */ // Referenz: https://developers.weixin.qq.com/miniprogram/dev/reference/configuration/page.html Exportschnittstelle PageConfig { /** * Standardwert: #000000 * Hintergrundfarbe der Navigationsleiste, z. B. #000000 */ Hintergrundfarbe der Navigationsleiste?: Zeichenfolge; /** * Standardwert: weiß * Titelfarbe der Navigationsleiste, unterstützt nur Schwarz / Weiß */ navigationBarTextStyle?: „schwarz“ | „weiß“; /** Globale Konfigurationsdatei */ // Referenz: https://developers.weixin.qq.com/miniprogram/dev/reference/configuration/app.html Schnittstelle AppConfig exportieren { /** * Seitenpfadliste */ Seiten: Zeichenfolge[]; /** * Globales Standardfensterverhalten */ Fenster?: { /** * Standardwert: #000000 * Hintergrundfarbe der Navigationsleiste, z. B. #000000 */ Hintergrundfarbe der Navigationsleiste?: Zeichenfolge; /** * Standardwert: weiß * Titelfarbe der Navigationsleiste, unterstützt nur Schwarz / Weiß */ navigationBarTextStyle?: „weiß“ | „schwarz“; src/types/component.ts Anpassung öffentlicher Eigenschaften, Ereignisse und anderer Eigenschaften im Zusammenhang mit integrierten WeChat-Komponenten importiere * als React von „react“; /** Öffentliche Eigenschaften der integrierten WeChat-Komponente*/ // Referenz: https://developers.weixin.qq.com/miniprogram/dev/framework/view/component.html Schnittstelle BaseProps exportieren { /** Benutzerdefinierte Attribute: Wenn ein Ereignis für eine Komponente ausgelöst wird, wird es an die Ereignishandlerfunktion gesendet*/ schreibgeschützter Datensatz?: DOMStringMap; /** Eindeutige Kennung der Komponente: Die gesamte Seite muss eindeutig sein*/ ID?: Zeichenfolge; /** Komponentenstilklasse: die im entsprechenden WXSS definierte Stilklasse*/ Klassenname?: Zeichenfolge; /** Inline-Stile von Komponenten: Inline-Stile, die dynamisch festgelegt werden können*/ Stil?: React.CSSProperties; /**Ob die Komponente angezeigt wird: Standardmäßig werden alle Komponenten angezeigt*/ versteckt?: Boolesch; /**Animationsobjekt: erstellt von `wx.createAnimation`*/ Animation?: Array<Datensatz<Zeichenfolge, beliebig>>; // Referenz: https://developers.weixin.qq.com/miniprogram/dev/framework/view/wxml/event.html /** Wird beim Klicken ausgelöst*/ beim Tippen?: (Ereignis: Berührungsereignis) => ungültig; /** Wird beim Klicken ausgelöst*/ beim Klicken?: (Ereignis: TouchEvent) => void; /**Fingerberührungsaktion startet*/ beiBerührungsstart?: (Ereignis: Berührungsereignis) => void; src/hostComponents dient zum Verpacken und Anpassen von WeChat Mini Program-Hostkomponenten; node.ts dient zum Anpassen von Mini Program-bezogenen Eigenschaften an React-Spezifikationen. export const alias = { id: "ID", Klassenname: "Klasse", Stil: 'Stil', Animation: "Animation", Quelle: „Quelle“, Schleife: 'Schleife', Kontrollen: 'Kontrollen', Poster: 'Poster', Name: "Name", Autor: 'Autor', beiFehler: 'Binderfehler', bei Wiedergabe: "Bindplay", onPause: 'Bindpause', beiZeitUpdate: 'Bindezeitupdate', onEnded: 'gebunden', }; exportiere const props = Objekt.values(alias); Verschiedene Komponenten werden auch mit createHostComponent generiert importiere * als React von „react“; importiere { createHostComponent } aus '@remax/runtime'; // WeChat wird nicht mehr gepflegt export const Audio: React.ComponentType = createHostComponent('audio'); createHostComponent generiert Reacts Element importiere * als React von „react“; importiere { RuntimeOptions } aus '@remax/framework-shared'; Exportiere Standardfunktion createHostComponent<P = any>(Name: Zeichenfolge, Komponente?: React.ComponentType<P>) { wenn (Komponente) { Renditekomponente; } const Komponente = React.forwardRef((props, ref: React.Ref<any>) => { const { Kinder = [] } = Requisiten; : Let Element = React.createElement(Name, { ...Eigenschaften, Ref.}, untergeordnete Elemente); Element = RuntimeOptions.get('pluginDriver').onCreateHostComponentElement(Element) als React.DOMElement<beliebig, beliebig>; Rücklaufelement; }); returniere RuntimeOptions.get('pluginDriver').onCreateHostComponent(Komponente); } 3. remax-macro Laut der offiziellen Beschreibung ist remax-macro ein Makro, das auf babel-plugin-macros basiert; das sogenannte Makro ist ein statischer Ersatz von Zeichenfolgen während der Kompilierung, und Javascript hat keinen Kompilierungsprozess. Die Art und Weise, wie Babel Makros implementiert, besteht darin, den Code in einen AST-Baum zu kompilieren und dann den AST-Syntaxbaum zu bearbeiten, um den ursprünglichen Code zu ersetzen. Den ausführlichen Artikel finden Sie hier: https://zhuanlan.zhihu.com/p/64346538; importiere { createMacro} aus „babel-plugin-macros“; importiere createHostComponentMacro aus „./createHostComponent“; importiere requirePluginComponentMacro aus „./requirePluginComponent“; importiere requirePluginMacro aus „./requirePlugin“; importiere usePageEventMacro aus „./usePageEvent“; importiere useAppEventMacro aus „./useAppEvent“; Funktion remax({ Referenzen, Status }: { Referenzen: { [Name: Zeichenfolge]: NodePath[] }; Status: beliebig }) { Referenzen.createHostComponent?.forEach(Pfad => createHostComponentMacro(Pfad, Status)); Referenzen.requirePluginComponent?.forEach(Pfad => requirePluginComponentMacro(Pfad, Status)); Referenzen.requirePlugin?.forEach(Pfad => requirePluginMacro(Pfad)); const importer = Schrägstrich(Status.Datei.Opts.Dateiname); Store.appEvents.delete(Importeur); Store.pageEvents.delete(Importeur); Referenzen.useAppEvent?.forEach(Pfad => useAppEventMacro(Pfad, Status)); Referenzen.usePageEvent?.forEach(Pfad => usePageEventMacro(Pfad, Status)); } Exportieren Sie die Deklarationsfunktion „createHostComponent“<P = any>( Name: Zeichenfolge, Requisiten: Array<Zeichenfolge | [Zeichenfolge, Zeichenfolge]> ): React.ComponentType<P>; Export-Deklarationsfunktion requirePluginComponent<P = any>(pluginName: string): React.ComponentType<P>; Export-Deklarationsfunktion requirePlugin<P = any>(PluginName: Zeichenfolge): P; Exportieren Sie die Deklarationsfunktion usePageEvent (Eventname: PageEventName, Rückruf: (...Params: any[]) => any): void; Exportieren Sie die Deklarationsfunktion useAppEvent (Eventname: AppEventName, Rückruf: (...Params: any[]) => any): void; exportiere Standard-ErstelleMakro(remax); importiere * als t aus '@babel/types'; importiere { Schrägstrich} von '@remax/shared'; importiere { NodePath } von '@babel/traverse'; Store aus „@remax/build-store“ importieren; importiere insertImportDeclaration aus „./utils/insertImportDeclaration“; const PACKAGE_NAME = "@remax/runtime"; const FUNCTION_NAME = "useAppEvent"; Funktion getArguments(callExpression: NodePath<t.CallExpression>, Importeur: Zeichenfolge) { const args = callExpression.node.arguments; const eventName = args[0] als t.StringLiteral; const Rückruf = args[1]; Store.appEvents.set(Importeur, Store.appEvents.get(Importeur)?.add(Ereignisname.Wert) ?? neues Set([Ereignisname.Wert])); return [Ereignisname, Rückruf]; } exportiere Standardfunktion useAppEvent(Pfad: NodePath, Status: beliebig) { const Programm = Status.Datei.Pfad; const importer = Schrägstrich(Status.Datei.Opts.Dateiname); const Funktionsname = insertImportDeclaration(Programm, Funktionsname, Paketname); const callExpression = path.findParent(p => t.isCallExpression(p)) als NodePath<t.CallExpression>; const [Ereignisname, Rückruf] = getArguments(Anrufausdruck, Importeur); callExpression.replaceWith(t.callExpression(t.bezeichner(funktionsname), [ereignisname, rückruf])); } Ich persönlich finde, dass dieses Design etwas zu kompliziert ist, was möglicherweise mit dem Design von remax zusammenhängt. In remax/runtime wird useAppEvent tatsächlich aus remax-framework-shared exportiert. 4. remax-cli Das Remax-Gerüst, das gesamte Remax-Projekt und der in das Miniprogramm generierte Kompilierungsprozess werden hier ebenfalls behandelt. importiere * als React von „react“; importiere {Ansicht, Text, Bild} aus „remax/wechat“; Stile aus „./index.css“ importieren; Standard exportieren () => { zurückkehren ( <Klassenname anzeigen={styles.app}> <Klassenname anzeigen={styles.header}> <Bild Quelle="https://gw.alipayobjects.com/mdn/rms_b5fcc5/afts/img/A*OGyZSI087zkAAAAAAAAAAABkARQnAQ" Klassenname={styles.logo} alt="Logo" /> <Klassenname anzeigen={styles.text}> Bearbeiten Sie <Text className={styles.path}>src/pages/index/index.js</Text>, um zu starten</View> </Anzeigen> </Anzeigen> ); }; Dieser Teil wird im Code remax-cli/src/build/entries/PageEntries.ts verarbeitet. Sie können sehen, dass der Quellcode hier geändert wurde. Die Funktion createPageConfig in der Laufzeit wird eingeführt, um die von der React-Komponente und der nativen Seite des Miniprogramms benötigten Eigenschaften auszurichten, und der native Seitenkonstruktor wird aufgerufen, um die Seite zu instanziieren. importiere * als Pfad von „Pfad“; importiere VirtualEntry aus „./VirtualEntry“; exportiere Standardklasse PageEntry erweitert VirtualEntry { Ausgabequelle() { Rückkehr ` importiere { createPageConfig } von '@remax/runtime'; Eintrag aus './${path.basename(this.filename)}' importieren; Seite (createPageConfig(Eintrag, '${this.name}')); `; } } createPageConfig ist dafür verantwortlich, die React-Komponente in den benutzerdefinierten Rendering-Container von remax einzubinden und gleichzeitig die verschiedenen Lebenszyklen der Miniprogrammseite mit den verschiedenen von remax bereitgestellten Hooks zu verknüpfen. exportiere Standardfunktion createPageConfig(Seite: React.ComponentType<any>, Name: string) { const app = getApp() als beliebige; const config: beliebig = { Daten: { Wurzel: { Kinder: [], }, modalRoot: { Kinder: [], }, }, WrapperRef: React.createRef<any>(), Lebenszyklus-Rückruf: {}, beim Laden (dies: beliebig, Abfrage: beliebig) { const PageWrapper = createPageWrapper(Seite, Name); diese.pageId = generatePageId(); dies.lifecycleCallback = {}; this.data = { // Die in Page definierten Daten sind eigentlich eine von remax im Speicher generierte gespiegelte Baumwurzel: { Kinder: [], }, modalRoot: { Kinder: [], }, }; diese.Abfrage = Abfrage; // Generieren Sie den Container, der für den benutzerdefinierten Renderer definiert werden muss. this.container = new Container(this, 'root'); this.modalContainer = neuer Container(dieser, 'modalRoot'); // Hier React-Komponenten auf Seitenebene generieren const pageElement = React.createElement(PageWrapper, { Seite: diese, Abfrage, modalContainer: dieser.modalContainer, Verweis: this.wrapperRef, }); wenn (App && App._mount) { dieses.Element = createPortal(Seitenelement, dieser.Container, diese.Seiten-ID); app._mount(dies); } anders { // Zum Rendern benutzerdefinierten Renderer aufrufen this.element = render(pageElement, this.container); } //Rufen Sie die Hook-Funktion im Lebenszyklus auf. return this.callLifecycle(Lifecycle.load, query); }, beimEntladen(dies: beliebig) { dies.callLifecycle(Lifecycle.unload); dies.entladen = wahr; dies.container.clearUpdate(); app._unmount(dies); }, Container ist der Stammcontainer, der gemäß der benutzerdefinierten Rendering-Spezifikation von React definiert ist. Schließlich wird die native setData-Methode des Applets in der applyUpdate-Methode aufgerufen, um die gerenderte Ansicht zu aktualisieren. Update anwenden() { wenn (this.stopUpdate || this.updateQueue.length === 0) { zurückkehren; } const startTime = neues Date().getTime(); wenn (Typ von diesem.Kontext.$spliceData === 'Funktion') { let $batchedUpdates = (Rückruf: () => void) => { Rückruf(); }; wenn (Typ von diesem.Kontext.$batchedUpdates === 'Funktion') { $batchedUpdates = dieser.Kontext.$batchedUpdates; } $batchedUpdates(() => { diese.updateQueue.map((update, index) => { let callback = undefiniert; wenn (Index + 1 === diese.UpdateQueue.Länge) { Rückruf = () => { nativeEffector.run(); /* istanbul, nächstes ignorieren */ wenn (RuntimeOptions.get('debug')) { console.log(`setData => Rückrufzeit: ${new Date().getTime() - startTime}ms`); } }; } wenn (update.type === 'spleißen') { dieser.Kontext.$spliceData( { [diese.normalizeUpdatePath([...update.path, 'Kinder'])]: [ update.start, aktualisieren.löschenAnzahl, ...Elemente aktualisieren, ], }, Rückruf ); } wenn (update.type === 'set') { dieser.Kontext.setData( { [this.normalizeUpdatePath([...update.pfad, update.name])]: update.wert, }, Rückruf ); } }); }); diese.updateQueue = []; zurückkehren; } const updatePayload = this.updateQueue.reduce<{ [Schlüssel: Zeichenfolge]: beliebig }>((acc, update) => { wenn (update.node.isDeleted()) { Rückgabe gem. } wenn (update.type === 'spleißen') { acc[this.normalizeUpdatePath([...update.path, 'nodes', update.id.toString()])] = update.items[0] || null; wenn (update.kinder) { acc[this.normalizeUpdatePath([...update.path, 'children'])] = (update.children || []).map(c => c.id); } } anders { acc[this.normalizeUpdatePath([...update.pfad, update.name])] = update.wert; } Rückgabe gem. }, {}); // Aktualisiere die gerenderte Ansicht this.context.setData(updatePayload, () => { nativeEffector.run(); /* istanbul, nächstes ignorieren */ wenn (RuntimeOptions.get('debug')) { console.log(`setData => Rückrufzeit: ${new Date().getTime() - startTime}ms`, updatePayload); } }); diese.updateQueue = []; } Die Aktualisierung des Containers erfolgt in der Rendermethode in der Renderdatei. Funktion getPublicRootInstance(Container: ReactReconciler.FiberRoot) { const containerFiber = container.aktuell; wenn (!containerFiber.child) { gibt null zurück; } ContainerFiber.child.stateNode zurückgeben; } exportiere Standardfunktion rendern(rootElement: React.ReactElement | null, Container: Container | AppContainer) { //Erstellen Sie einen Root-Container, falls dieser nicht existiert wenn (!container._rootContainer) { container._rootContainer = ReactReconcilerInst.createContainer(container, false, false); } ReactReconcilerInst.updateContainer(rootElement, container._rootContainer, null, () => { // ignorieren }); gibt an, dass die Instanz von „PublicRootInstance“ (container._rootContainer) nicht vorhanden ist. } Darüber hinaus werden die hier gerenderten Komponenten tatsächlich von createPageWrapper umschlossen, hauptsächlich um einige Forward-Ref-bezogene Vorgänge abzuwickeln. importiere * als Pfad von „Pfad“; importiere VirtualEntry aus „./VirtualEntry“; exportiere Standardklasse ComponentEntry erweitert VirtualEntry { Ausgabequelle() { Rückkehr ` importiere { createComponentConfig } von '@remax/runtime'; Eintrag aus './${path.basename(this.filename)}' importieren; Komponente (createComponentConfig(Eintrag)); `; } } Für gewöhnliche Komponenten kompiliert remax sie in benutzerdefinierte Komponenten. Die benutzerdefinierten Komponenten von Miniprogrammen bestehen aus JSON, WXML, WXSS und JS. Die Verarbeitung von React-Komponenten zu diesen Dateien erfolgt in remax-cli/src/build/webpack/plugins/ComponentAsset, um WXML-, WXSS- und JS-Dateien zu generieren. exportiere Standardklasse ComponentAssetPlugin { Erbauer: Erbauer; Cache: SourceCache = neuer SourceCache(); Konstruktor(Erbauer: Erbauer) { dieser.builder = Erbauer; } anwenden(Compiler: Compiler) { compiler.hooks.emit.tapAsync(PLUGIN_NAME, async (Kompilierung, Rückruf) => { const { Optionen, API } = this.builder; const meta = api.getMeta(); const { Einträge } = this.builder.entryCollection; warte auf Promise.all( Array.from(Einträge.Werte()).map(asynchrone Komponente => { if (!(Komponenteninstanz von ComponentEntry)) { gibt Promise.resolve() zurück; } const chunk = Zusammenstellung.chunks.find(c => { return c.name === Komponentenname; }); const module = [...getModules(chunk), komponente.dateiname]; lass Vorlage versprechen; wenn (Optionen.turboRenders) { // Turboseite templatePromise = createTurboTemplate(this.builder.api, Optionen, Komponente, Module, Meta, Kompilierung); } anders { templatePromise = createTemplate(Komponente, Optionen, Meta, Kompilierung, this.cache); } warte auf Promise.all([ warte auf TemplatePromise, warte auf createManifest(this.builder, Komponente, Kompilierung, this.cache), ]); }) ); Rückruf(); }); } } Die Seitendateireihe wird in remax-cli/src/build/webpack/plugins/PageAsset verarbeitet. Gleichzeitig wird die Abhängigkeitsbeziehung zwischen Seite und benutzerdefinierten Komponenten in createMainifest analysiert und die Zuordnungsbeziehung von usingComponents automatisch generiert. Damit ist dieser Artikel über die Analyse des Kompilierungsprozesses des Remax-Frameworks zum Schreiben kleiner Programme mit React (empfohlen) abgeschlossen. Weitere relevante Inhalte zum Schreiben kleiner Programme mit React finden Sie in früheren Artikeln auf 123WORDPRESS.COM oder durchsuchen Sie die verwandten Artikel weiter unten. Ich hoffe, dass jeder 123WORDPRESS.COM in Zukunft unterstützen wird! Das könnte Sie auch interessieren:
|
>>: Untersuchung der MySQL-Paging-Leistung
Die MySQL-Installation ist in eine Installationsv...
Inhaltsverzeichnis Vorwort 1. Eine Zeichenfolge t...
Dieser Artikel zeichnet das Installationstutorial...
Vorwort Manchmal benötigen wir eine Floating-Effe...
Inhaltsverzeichnis Was ist eine Mindmap? Wie zeic...
Hintergrund Alles begann, als ein Klassenkamerad ...
In Dockerfile können run, cmd und entrypoint zum ...
<!--[wenn lte IE 6]> <![endif]--> Sich...
In diesem Artikel wird der spezifische JavaScript...
In diesem Artikel wird der spezifische Code von J...
Inhaltsverzeichnis Manuelle Bereitstellung 1. Ers...
1.1 Einführung in Speicher-Engines 1.1.1 Dateisys...
1. Neuen Benutzer anlegen: 1. Führen Sie eine SQL...
Ich habe lange mit einem Problem gekämpft und das...
Inhaltsverzeichnis 1. Komponentenregistrierung 2....