Laws are created and discovered, and memory is the derivation and extraction of laws

background

When I was working on vuE2 mobile terminal project, I made a quick scan mobile terminal preview function, which was very useful in the debugging stage, so I wanted to incorporate it into VUE3 project. I thought it was very simple, but I met several problems and got stuck for a long time

  1. Introduction of global variables
  2. Custom instruction
  3. CreateApp directives not shared (render directives already attached to elements)

demand

When developing on the Web side, it is the most convenient choice to scan the code and open it on the browser side if you want to see the effect quickly on the mobile side. At the same time, if you want to preview in the APP, it provides the replication function, through the APP to read and paste content + Scheme jump function to achieve webView synchronous preview.

Implementation approach

  1. Synchronize debug pages with the same IP using HMR
  2. The native IP is dynamically captured during the development phase and injected into the development environment
  3. Build the component, dynamically generate the corresponding debugging URL according to the IP, and generate the corresponding QR code and copy content
  4. The routing query parameter determines whether the component is displayed

implementation

Synchronize debug pages with the same IP using HMR

Both WebPack and Vite provide HMR functionality

The native IP is dynamically captured during the development phase and injected into the development environment

Nodejs can easily get the native IP:

const os = require("os");
function GetIp() {
  let ifaces = os.networkInterfaces();
  for (let i in ifaces) {
    for (let j in ifaces[i]) {
      let alias = ifaces[i][j];
      if (
        alias.family === "IPv4"&& alias.address ! = ="127.0.0.1" &&
        !alias.internal
      ) {
        returnalias.address; }}}}export default GetIp()
Copy the code

Once the IP is obtained, Webpack is injected through process.env

new webpack.DefinePlugin({
      'process.env': {IP: GetIp()}
  })
Copy the code

Vite is injected through define

  define: {
    VITE_DEV_IP: JSON.stringify(GetIp())
  }
Copy the code

Build a component

Only vuE3 components are shown here

<style lang="less" scoped>
.dev-code {
  position: fixed;
  top: 50%;
  transform: translateY(-50%);
  right: 0;
  background-color: #ffffff;
  .tips {
    font-size: 0.18rem;
    color: #000;
    margin: 0.1rem 0;
    text-align: center;
  }
  .close-btn {
    width: 0.4rem;
    height: 0.4rem;
    position: absolute;
    left: 0;
    right: 0;
    bottom: -0.5rem;
    margin: auto;
    img {
      width: 100%;
      height: 100%;
    }
  }
}
</style>
<template>
  <Teleport to="body">
    <section
      class="dev-code"
      v-show="visible == true"
      v-clipboard:copy="url"
      v-clipboard:success="Tip"
    >
      <qrcode :value="url" id="qrcode" />
      <div class="tips">Scan code mobile phone joint modulation</div>
      <div class="tips">Click on the copy link</div>
      <a href="javascript:;" class="close-btn" @click.stop="visible = false">
        <img src="https://xxxcdn.xxx.cn/common/close-grey.png" />
      </a>
    </section>
  </Teleport>
</template>
<script lang="ts" setup>
// Use qr code for debugging
import qrcode from ".. /qrcode.vue";
import { ref } from "vue";
import { toast } from ".. /toast/useToast";
defineProps({
  url: {
    type: String.// Global variables defined by vite define are injected into the window object, so name them uniquely
    default: `${location.protocol}//The ${window.VITE_DEV_IP}:${location.port}${location.pathname }${location.search.replace("debug=1"."")}`,}});const visible = ref(true);
function Tip() {
  toast("Copy has been successfully, go to the browser to open it, pay attention to and computer in the same LAN oh ~");
}
</script>

// Build the render function
import { mountComponent } from ".. /.. /lib/utils/mount-component";
import devCodeConstructor from "./devCode.vue";
const debug = () = > {
  return mountComponent(devCodeConstructor);
};
export default debug;
Copy the code

The rendering function can be referenced in the previous article

The routing query parameter determines whether the component is displayed

Add an interceptor to the router and display the component when the development environment reads the specified Query parameter

router.afterEach((to, from) = > {
  if (import.meta.env.MODE === 'development'&&!!!!! to.query.debug && +to.query.debug ===1) { debug(); }});Copy the code

The problem

Introduction of global variables

Webpack was previously implemented by injecting process.env with DefinePlugin

The new framework uses Vite, which also provides global variable injection, mainly using Dotenv to load additional environment variables from files in your environment directory. However, Dotenv files are static, so I don’t want other developers to have to look up the IP and write it in. To do this, use define options to inject dynamic variables into the window object, but note that the injected data must be a deserialized string for json.stringify, and the string itself should also be deserialized, all for lazy ^_^

Dotenv way into global variables can be import. Meta. Env. Development. The XXX to get, you can refer to the official document, define the options can also refer to the official documentation

Custom instruction

The custom directive is mainly for the replication function, please refer to my previous article

CreateApp directives not shared (render directives already attached to elements)

The new createApp method does not share directives from other createApp instances, so it extends the previous method:

import { Component, createApp } from "vue";
import DirectiveExtensions from ".. /.. /directive";
export function mountComponent(rootComponent: Component) {
  // Add a custom directive
  const app = createApp(rootComponent).use(DirectiveExtensions);
  const root = document.createElement("div");
  document.body.appendChild(root);
  return {
    instance: app.mount(root),
    unmount() {
      app.unmount();
      document.body.removeChild(root); }}; }Copy the code

Implementation effect