Analyse des Kompilierungsprozesses des Remax-Frameworks zum Schreiben kleiner Programme mit React (empfohlen)

Analyse des Kompilierungsprozesses des Remax-Frameworks zum Schreiben kleiner Programme mit React (empfohlen)

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.
Informationen zum Prinzip der Vorlagendarstellung finden Sie in diesem Artikel: https://www.jb51.net/article/132635.htm
Das Laufzeitprinzip von remax finden Sie in diesem Artikel: https://www.jb51.net/article/210293.htm
Weitere Informationen zum benutzerdefinierten Renderer von React finden Sie in diesem Artikel: https://www.jb51.net/article/198425.htm

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
Vorlagenbezogen können die Verarbeitungsprinzipien und Grundsätze im Zusammenhang mit der Vorlage hier eingesehen werden: https://www.jb51.net/article/145552.htm
Vorlagen // Vorlagen im Zusammenhang mit Rendering
src/api passt verschiedene globale APIs im Zusammenhang mit WeChat Mini-Programmen an, von denen einige versprochen werden

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;
Remax verwendet Makros, um einige Makros wie useAppEvent und usePageEvent zu ersetzen, indem sie aus remax/runtime importiert werden.

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.
Aber es hat mir auch gezeigt, wie ich mit Codeänderungen umgehen kann.

4. remax-cli Das Remax-Gerüst, das gesamte Remax-Projekt und der in das Miniprogramm generierte Kompilierungsprozess werden hier ebenfalls behandelt.
Sehen wir uns zunächst an, wie eine React-Datei als Seite mit dem nativen Seitenkonstruktor des Miniprogramms verknüpft ist.
Angenommen, der ursprüngliche Seitencode sieht so aus:

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.
Jetzt wurde die React-Komponente auf Seitenebene mit der nativen Miniprogrammseite verknüpft.
Die Verarbeitung der Komponente ist ähnlich. Sie können die Datei remax-cli/src/build/entries/ComponentEntry.ts sehen.

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:
  • So entfernen Sie HTML-Tags in Rich Text und Filtern in Vue-, React- und WeChat-Applets

<<:  Lösung für das Problem, dass Linux in VMware keine Verbindung zum Internet herstellen kann, nachdem der Computer unerwartet heruntergefahren wurde

>>:  Untersuchung der MySQL-Paging-Leistung

Artikel empfehlen

Vier praktische Tipps für JavaScript-String-Operationen

Inhaltsverzeichnis Vorwort 1. Eine Zeichenfolge t...

Grafisches Tutorial zur Installation und Konfiguration von MySQL 5.7.23

Dieser Artikel zeichnet das Installationstutorial...

So zeichnen Sie eine Mindmap in einem Miniprogramm

Inhaltsverzeichnis Was ist eine Mindmap? Wie zeic...

Analysieren Sie CSS, um die Farbfunktion des Bildthemas zu extrahieren (Tipps)

Hintergrund Alles begann, als ein Klassenkamerad ...

Detaillierte Erklärung des Unterschieds zwischen run/cmd/entrypoint in Docker

In Dockerfile können run, cmd und entrypoint zum ...

JavaScript zur Implementierung der mobilen Signaturfunktion

In diesem Artikel wird der spezifische JavaScript...

JQuery implementiert das Ausblenden und Anzeigen von Animationseffekten

In diesem Artikel wird der spezifische Code von J...

MySql legt die angegebenen Benutzerdatenbankansichtsabfrageberechtigungen fest

1. Neuen Benutzer anlegen: 1. Führen Sie eine SQL...

MySQL-Fehler 1290 (HY000) Lösung

Ich habe lange mit einem Problem gekämpft und das...

Eine detaillierte Diskussion der Komponenten in Vue

Inhaltsverzeichnis 1. Komponentenregistrierung 2....