Vue3 TSX Mobile Terminal Project based on Vue Cli4 (I)

Project introduction

It’s been nearly a year since Vue3 was released in preview. In my own trials, the underlying rendering mechanism was optimized better than React Hooks without considering specific optimizations. So it’s time to start experimenting with Vue3.

Since I felt that the Vue Cli4 was mature enough, there was no need to build Webpack from scratch, it would be much more efficient to re-pack it. Therefore, this paper constructs Vue3 framework based on Vue Cli4, mainly including:

  1. Distinguish between multiple environment configurations
  2. Mobile debugging tool Eruda
  3. eslint
  4. stylelint
  5. Px2vw mobile terminal adaptive
  6. HardSource development build acceleration
  7. Babel-plugin-import is introduced on demand
  8. Static resource upload CDN in production environment
  9. Express start
  10. Commonly used Hooks encapsulation
  11. Vue3 TSX common syntax

For this project, I recommend developing everything using the Component API to make better use of the Hooks capabilities. At the same time, in order to improve Vue2
can not be code prompts, etc., the official advocate using VUE template syntax for development, but template is not as flexible as TSX. So you can still use TSX for development in situations that require high flexibility. Template is still recommended for everyday business code development, for reasons explained in Chapter 4. With the Component API, the TSX writing of Vue will not be as awkward as the JSX writing of Vue2 before, and the TSX and Vue files can be seamlessly connected, and you can choose according to the business form and custom.

To avoid being too long, I will divide it into four chapters. This article focuses on project creation, differentiating multi-environment configurations, and integrating eruda mobile debugging tools.

Create a project

First we create the base project with Vue Cli4:

Project creation

vue create yuor-template

We then chose Manually Select Features to customize some of the basic configuration we needed

Vue CLI v4.5.4 ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐ │ │ │ New version available 4.5.4 - > 4.5.12 │ │ Run NPM I - g  @vue/cli to update! │ │ │ └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘? Please pick a preset: Default ([Vue 2] Babel, esLint) Default (Vue 3 Preview) ([Vue 3] Babel, eslint) ❯ Manually select featuresCopy the code

Select desired features

Here we check vUE version selection, Babel, TypeScript, Router, Vuex, CSS preprocessing, Lint

? Check the features needed for your project: ◉ Choose Vue version ◉ Babel ◉ TypeScript infection Progressive Web App (PWA) Support ◉ Router port Vuex CSS pre-processors microblog Linter/Formatter Unit Testing was borne out of infection into E2E TestingCopy the code

Select the Vue version

Since our project is based on Vue3, the version selected here is Vue3

? Choose a version of Vue.js that you want to start the project with
  2.x
❯ 3.x (Preview)
Copy the code

Other options

Because there are more choices here, it is not a waste of space, the configuration is as follows:

Set not to use class component writing, to use Babel for escape, to use history mode for the router, to use dart-Sass style preprocessors, to use ESLint + Standard Config, to do Lint detection when saving, and to place individual component configurations in individual component configuration files

? Please pick a preset: Manually select features
? Check the features needed for your project: Choose Vue version, Babel, TS, Router, Vuex, CSS Pre-processors, Linter
? Choose a version of Vue.js that you want to start the project with 3.x (Preview)
? Use class-style component syntax? No
? Use Babel alongside TypeScript (required for modern mode, auto-detected polyfills, transpiling JSX)? Yes
? Use history mode for router? (Requires proper server setup for index fallback in production) Yes
? Pick a CSS pre-processor (PostCSS, Autoprefixer and CSS Modules are supported by default): Sass/SCSS (with dart-sass)
? Pick a linter / formatter config: Standard
? Pick additional lint features: Lint on save
? Where do you prefer placing config for Babel, ESLint, etc.? In dedicated config files
? Save this as a preset for future projects? (y/N)
Copy the code

Trying to run project

Once the installation is complete, we can go to the new project for a trial run

🎉  Successfully created project vue-plus-example.
👉  Get started with the following commands:

 $ cd vue-plus-example
 $ npm run serve
Copy the code

Distinguish between multiple environment configurations

In the previous step, we created the basic project framework with Vue Cli4. Generally, during actual development, there are local development environment, development environment, test environment, pre-release environment staging environment and production environment. In view of these different situations, we need a different set of configurations to distinguish the configurations such as Bucket for CDN uploading, API address, domain name for jumping to other sites, etc. So let’s configure a multi-environment configuration.

Add an environment configuration file

Json, development.json, test.json, staging. Json, production.json.

Here’s an overview of what each file does:

  • Index.js: method class to get configuration files, which encapsulates a series of operations to read configuration based on the current environment variables
  • Local. Json: the configuration of local development environment, singled out the advantage of this file is want to go to when we in the local development in different environment of the debug interface and so on, you just need to copy content from other environment configuration file cover, and avoid direct modify the configuration files of the other environment, may lead to problems after release.
  • Development. json: Configuration of the development environment
  • Test. json: configuration of the test environment
  • Staging. Json: configuration of the pre-publishing environment
  • Production. json: configuration of the production environment

Writing configuration files

The configuration file format here is only a set of structure defined by myself at present, and you can define your own structure according to your own needs. I take the structure of Development. json as an example in the following code, and change the corresponding configuration value for the rest of the environment according to the actual configuration of your own project

// development.json
{
  // Build time configuration
  "buildtime": {
		// Local service IP address and port configuration
    "origin_server": {
      "ip": "127.0.0.1"."port": "5000"
    },
    // CDN configuration. This value needs to be filled in according to the CDN configuration of your project
    // This configuration will be used in the subsequent static resource upload CDN. If static resource upload CDN is not used, this configuration can be omitted
    "cdn": {
      "region": "oss-cn-shenzhen"."accessKeyId": "xxxxxxxxxxxx"."accessKeySecret": "xxxxxxxxxx"."bucket": "frontend".// create a bucket to store static files
      "path": "vue-plus/development/".// Here I use the format of the project name (vue-plus) + environment variable as the path
      "cdnPath": "https://frontend.oss-cn-shenzhen.aliyuncs.com/vue-plus/development/"	// CDN path after upload, used for publicPath}},// Run time configuration
  "runtime": {
    // Identifies the current environment
    "env": "development".// Public API address
    "api": "https://dev-api.wynneit.cn".// API address of the account
    "account": "https://dev-account-api.wynneit.cn".// host is the configuration item in the project that jumps to another site
    "host": {
      // Mobile site domain name
      "mobile": "https://dev-mobile.wynneit.cn"
    },
    // Wechat configuration
    "wechat": {
      // Wechat APPID
      "appId": "wxf8xxxxxxxxxxxa53a"}}}Copy the code

Write configuration read methods

Since the project was ultimately deployed using Docker and Jenkins, I use a custom environment parameter front_env, passed in from Jenkins configuration.

// index.js
const fs = require("fs");
const path = require("path");
const lodash = require("lodash");
const cfgDir = path.join(__dirname);
const env = process.env.front_env;

function generateCfg() {
  // Default configuration path
  const defaultConfigPath = path.join(cfgDir, "development.json");
  // Local configuration path
  const localConfigPath = path.join(cfgDir, "local.json");
  let localConfig = {};
  let envConfig = {};

  if (fs.existsSync(localConfigPath)) {
    localConfig = require(localConfigPath);
  }
  / / to get
  if (env) {
    const envCfgPath = path.join(cfgDir, `${env}.json`);
    if (fs.existsSync(envCfgPath)) {
      envConfig = require(envCfgPath);
    } else {
      console.warn(
        `\nConfiguration file specified by env var ${env} = ${envCfgPath} does not exist.\n`); }}// Get the default configuration
  const defaultConfig = require(defaultConfigPath);

  // Splice the final configuration
  const mixedConfig = lodash.merge({}, defaultConfig, localConfig, envConfig);
  return mixedConfig;
}

module.exports = generateCfg;

Copy the code

Mount the config configuration

Here I have mounted the environment configuration to the Window object, so that we can easily see some of the current environment configuration in Chrome DevTool, convenient for some troubleshooting.

  1. Add config slot in /public/index.html

    <! DOCTYPEhtml>
    <html lang="zh-CN">
      <head>
        <meta charset="utf-8" />
        <meta http-equiv="X-UA-Compatible" content="IE=edge" />
        <meta
          name="viewport"
          content="Width =device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"
        />
        <link rel="icon" href="<%= BASE_URL %>favicon.ico" />
        <title><%= htmlWebpackPlugin.options.title %></title>
        <! -- Add custom configuration -->
        <%= htmlWebpackPlugin.options.config %>
      </head>
      <body>
        <noscript>
          <strong
            >We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work
            properly without JavaScript enabled. Please enable it to
            continue.</strong
          >
        </noscript>
        <div id="app"></div>
        <! -- built files will be auto injected -->
      </body>
    </html>
    Copy the code
  2. Create vue.config.js, set the value of config slot, and set devServer

    // Get the environment configuration
    const cfg = require("./config/index") ();module.exports = {  
      devServer: {
        host: config.buildtime.origin_server.ip,
        port: config.buildtime.origin_server.port,
      },
      chainWebpack: (config) = > {
        // HTML template injection configuration
        config.plugin("html").tap((args) = > {
          // Environment configuration script
          const configScript = ` <! --configArea--><script>window.CUSTOMCONFIG =The ${JSON.stringify(cfg.runtime)}</script><! --endOfConfigArea-->`;
          args[0].config = configScript;
          returnargs; }); }};Copy the code
  3. Once the configuration is complete, run the project and type window.CUSTOMCONFIG into Chrome DevTool to see the configuration of our current environment.

Write the config declaration file

We have successfully mounted the config configuration to window.CUSTOMCONFIG, but because we use TypeScript in our project, we want to use it without error and with the correct code prompt. So we also need to write a declaration file for window.customConfig.

  1. Create the window.d.ts file in the project directory SRC /typings/global.

    Here I use typings as the unified folder for declaration files, and global as the folder for globally related declaration files.

  2. Write the window.d.ts file

    interface CustomConfig {
      // Runtime environment variables
      env: "local" | "development" | "test" | "staging" | "production";
      // Public interface address
      api: string;
      // Interface address of the account
      account:string;
      // Jump to another platform domain name
      host: Record<string.string>;
      // Wechat configuration
      wechat: {
        appId: string,}}interface Window {
      CUSTOMCONFIG: CustomConfig;
    }
    
    Copy the code
  3. Try importing it in main.ts so that we can prompt the code that CUSTOMCONFIG already exists under window. (reboot if vscode is not prompted).

Eruda mobile debugging tool

Since the positioning of this project is on the mobile terminal, I will choose to embed tools such as vConsole or Eruda in the non-production environment during the development of the mobile terminal, so that we can view logs and quickly locate problems during the debugging of the real machine. Because Eruda is more powerful, I chose to embed Eruda here.

Adding Html slots

Still is in the public/index. The HTML to add custom configuration items < % = htmlWebpackPlugin. Options. Eruda % >

<! DOCTYPEhtml>
<html lang="zh-CN">
  <head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta
      name="viewport"
      content="Width =device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"
    />
    <link rel="icon" href="<%= BASE_URL %>favicon.ico" />
    <title><%= htmlWebpackPlugin.options.title %></title>
    <! -- Custom configuration -->
    <%= htmlWebpackPlugin.options.config %>
    <! -- Eruda configuration -->
    <%= htmlWebpackPlugin.options.eruda %>
  </head>
  <body>
    <noscript>
      <strong
        >We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work
        properly without JavaScript enabled. Please enable it to
        continue.</strong
      >
    </noscript>
    <div id="app"></div>
    <! -- built files will be auto injected -->
  </body>
</html>

Copy the code

Add erUDA slot content

Add the eruda configuration to the chainWebpack file in vue.config.js, where CDN is used directly, because this package is only used as a development debugging tool, so there is no need to NPM install into the project. This also prevents erUDA from being packaged into the project to increase the package size of the project.

// Get the environment configuration
const cfg = require("./config/index") ();module.exports = {
  devServer: {
    host: cfg.buildtime.origin_server.ip,
    port: cfg.buildtime.origin_server.port,
  },
  chainWebpack: (config) = > {
    // HTML template injection configuration
    config.plugin("html").tap((args) = > {
      // Embed environment configuration script
      const configScript = ` <! --configArea--><script>window.CUSTOMCONFIG =The ${JSON.stringify(config.runtime)}</script><! --endOfConfigArea-->`;
      args[0].config = configScript;
      // Inject erUDA in non-local development environment and non-production environment
      if(! ["local"."production"].includes(cfg.runtime.env)) {
        const erudaCDN = "/ / cdn.bootcdn.net/ajax/libs/eruda/2.4.1/eruda.min.js";
        const erudaDomCDN = "/ / cdn.jsdelivr.net/npm/[email protected]";
        const erudaScript = ` <! --erudaArea--> <script src="${erudaCDN}"></script>
          <script src="${erudaDomCDN}"></script> <script> eruda.init({ tool: ['console', 'network', 'elements', 'resources', 'snippets', 'sources'], }); eruda.add(erudaDom); </script> <! --endOfRrudaArea-->`;
        args[0].eruda = erudaScript;
      }
      returnargs; }); }};Copy the code

Eruda test

Eruda is not required for Chrome DevTool, so I have excluded the local environment. Json to test whether eruda is installed properly. After finishing the modification, run the project again. We can see that the icon of Eruda has appeared in the lower right corner.