motivation

I started working on the front end of vue and Angular last March. In October last year, I began to study and study the UNIAPP framework and put it into production. I used uniAPP for nearly one year to write wechat mini programs and H5. After the official launch of Xin Butler small program, vite2.0 release, plus Qiu said that he is studying vuE3 + Vite front-end framework.

My heart is itching and my hand is itching, so I look for information step by step and try to step on the hole to fill the hole, which is to build a basic project package that is more than (MEI) (you) (BAO) whole (CUO). It is not easy to keep thinking about filling pits, so I think about writing an article to record the problems encountered in the process, if lucky enough to help you, that…… Do you have to do it with one button and three links?

Address first: gitlab.com

Since vite1.0

Vite is essentially a Web development build tool. Vite is French for “fast” /vit/. During the development process, the coding service was imported into the native ES module and used for production through rollup.

  • Lightning fast cold server startup
  • Instant Hot Module Replacement (HMR)
  • True on-demand compilation

In short, fast. I started building because Vite2.0 had just been released and I thought I’d start with 1.0. If you want to build 2.0 directly, I will explain the difference between 1.0 and 2.0 and how to transform it later. My advice is always read the official documentation, always read it!!

New project

My Version of Node is 14.15.0, and I should be fine above 12.0.0 if it makes sense.

npm create vite-app project-name
Copy the code

This is the first pit I encountered, because I created the project by importing an empty folder directly into vscode and executing the command. Therefore, there will be an extra layer of directory in vscode, and vscode keeps reporting errors but does not affect the operation, so that the project dependencies are integrated, I had to worry about this mistake for two or three days, and asked teacher qiu’s demo for comparison.

Eliminated the impossibles, whatever remains, however improbable, must be the truth!

Make a bold assumption, delete the subdirectories, move everything to the root directory, problem solved.

Well said Holmes! But since there were no errors, now I try to create a sub-folder again and throw all the items into it, but it doesn’t come back. Therein lies the beauty of programming. It was possible that the VScode esLint plugin could not find the.eslintrc.js file in the root directory. After confirming the problem, I will record it clearly. Keep going down.

Integrate with ESLint, Pritter, and typescript

  • eslint

Because the problem with the demo given by Teacher Qiu is that ESLint has integration problems. So there’s a lot of holes in this.

npm add --dev eslint eslint-plugin-vue
Copy the code

I integrated esLint with the idea of being able to scan code for errors while running and packaging.

Then the operation reported an error:What does this error mean?

That’s right, it means the beginning of nesting dolls! After 8 or 9 sets of children, the operation still reported an error and no children to set.

Bye-bye. Yang!

Create the.eslintrc.js file in the root directory

module.exports = {
  parser: 'vue-eslint-parser',
  parserOptions: {
    parser: '@typescript-eslint/parser',
    ecmaVersion: 2020,
    sourceType: 'module',
    ecmaFeatures: {
      jsx: true,
    },
  },

  extends: [
    'plugin:vue/vue3-recommended',
    'plugin:@typescript-eslint/recommended',
    'prettier/@typescript-eslint',
    'plugin:prettier/recommended',
  ],
  rules: {
    '@typescript-eslint/ban-ts-ignore': 'off',
    '@typescript-eslint/explicit-function-return-type': 'off',
    '@typescript-eslint/no-explicit-any': 'off',
    '@typescript-eslint/no-var-requires': 'off',
    '@typescript-eslint/no-empty-function': 'off',
    'vue/custom-event-name-casing': 'off',
    'no-use-before-define': 'off',
    // 'no-setting-before-define': [
    //   'error',
    //   {
    //     functions: false,
    //     classes: true,
    //   },
    // ],
    '@typescript-eslint/no-use-before-define': 'off',
    // '@typescript-eslint/no-setting-before-define': [
    //   'error',
    //   {
    //     functions: false,
    //     classes: true,
    //   },
    // ],
    '@typescript-eslint/ban-ts-comment': 'off',
    '@typescript-eslint/ban-types': 'off',
    '@typescript-eslint/no-non-null-assertion': 'off',
    '@typescript-eslint/explicit-module-boundary-types': 'off',
    '@typescript-eslint/no-unused-vars': [
      'error',
      {
        argsIgnorePattern: '^h$',
        varsIgnorePattern: '^h$',
      },
    ],
    'no-unused-vars': [
      'error',
      {
        argsIgnorePattern: '^h$',
        varsIgnorePattern: '^h$',
      },
    ],
    'space-before-function-paren': 'off',
  },
};

Copy the code

A template for VUe3 can have multiple root nodes, esLint reports an error

The template root requires exactly one element.eslintvue/no-multiple-template-root
Copy the code

Simply add the following rules to rules in the.eslintrc.js file.

'vue/no-multiple-template-root': 'off'
Copy the code

This is somewhat integrated with eslint, but only red code in vscode, without scanning all files and blocking the project when it is run and packaged. Please let me know if you have an integration plugin that can achieve this effect.

  • pritter

There’s nothing to be said for this

npm add --dev prettier eslint-config-prettier eslint-plugin-prettier
Copy the code

Example Create the file prettier.config.js in the root directory

module.exports = {
  printWidth: 100,
  tabWidth: 2,
  useTabs: false,
  semi: true,
  vueIndentScriptAndStyle: true,
  singleQuote: true,
  quoteProps: 'as-needed',
  bracketSpacing: true,
  trailingComma: 'es5',
  jsxBracketSameLine: false,
  jsxSingleQuote: false,
  arrowParens: 'always',
  insertPragma: false,
  requirePragma: false,
  proseWrap: 'never',
  htmlWhitespaceSensitivity: 'strict',
  endOfLine: 'lf',
  rangeStart: 0,
  overrides: [
    {
      files: '*.md',
      options: {
        tabWidth: 2,
      },
    },
  ],
};

Copy the code

Just follow your coding conventions

  • typescript
npm add --dev typescript
Copy the code

The project root directory creates the configuration file: tsconfig.json

{
    "compilerOptions": {
        "target": "ESnext",
        "module": "ESnext",
        "jsx": "preserve",
        "strict": true,
        "importHelpers": true,
        "moduleResolution": "node",
        "experimentalDecorators": true,
        "esModuleInterop": true,
        "allowSyntheticDefaultImports": true,
        "sourceMap": true,
        "baseUrl": ".",
        "paths": {
            "/@/*": [
                "src/*"
            ]
        }
    },
    "include": [
        "src/**/*.ts",
        "src/**/*.tsx",
        "src/**/*.vue",
    ],
    "exclude": [
        "node_modules",
        "dist",
        "**/*.js"
    ]
}
Copy the code

To tell you the truth, this configuration is a little different from other tutorials. The main reason is that I used.tsx file to write the page, and there were a lot of syntax errors, so I changed some configuration items in tsconfig.json, I will explain these differences in the specific error points later, please continue to read.

The configuration file

  • vite.config.ts

Create the vite. Config. ts file in the root directory

import path from 'path'; import vueJsxPlugin from 'vite-plugin-vue-jsx'; Module. exports = {alias: {'/@/': path.resolve(' SRC '),}, optimizeDeps: {}, hostname: '0.0.0.0', port: 8080, // Whether to automatically open in the browser open: true, // whether to enable HTTPS HTTPS: false, // Server render SSR: false, // Basic public path when serving in production. Base: '/', outDir: 'dist, / / reverse proxy / / proxy: {/ /'/API: {/ / target: 'https://blog.csdn.net/weixin_45292658', // changeOrigin: true, // rewrite: path => path.replace(/^\/api/, '') // } // }, plugins: [ vueJsxPlugin() ] };Copy the code

The profile is vite1.0 version profile

Then, the vite-plugin-vuE-jsx plug-in is originated from The teacher qiu and solved by the teacher Qiu. When the project was just set up, he suddenly told me that the dom element written in the.tsx file could not be bidirectional binding, and the Vue instruction could not be recognized, which made him worried.

Sure enough, I finished building the project and copied a page to see, it does not work. Then asked qiu how to solve the teacher. Then I introduced viet-plugin-vue-jsx and registered it in plugins. Solve!

As long as the thigh embrace good, no problem can not be solved!

Then below is the Vite2.0 configuration file

import { defineConfig } from 'vite'; import path from 'path'; import vue from '@vitejs/plugin-vue'; import vueJsx from '@vitejs/plugin-vue-jsx'; import { buildConfig } from './build/config/buildConfig'; Export the default defineConfig ({server: {port: 7874, proxy: {/ / '/ LSBDB' : 'http://10.192.195.96:8087',}, HMR: { overlay: true, }, open: true, }, build: buildConfig, alias: { '/@': path.resolve(__dirname, '.', 'src'), }, optimizeDeps: { include: [], }, plugins: [vue(), vueJsx()], });Copy the code

Since Vite2.0 is decoupled from vue, we need to install @vitejs/plugin-vue and then add it to plugins. Then @vitejs/plugin-vue-jsx, which was vite-plugin-vue-jsx in Vite1.0, changed its name and became an official component.

The address of the 1.0 and 2.0 update document is to dump the local runtime configuration into a large object of the server. The most baffling thing for me is the alias configuration alias:

Vite1.0: '/ @ /' : path) resolve (' SRC '), vite2.0: '/ @ : path. The resolve (__dirname,'. ', 'SRC'),Copy the code

It actually deleted a /. After I upgraded from 1.0 to 2.0, all the errors were reported in the project, and Baidu could not find the reason. Fortunately, I found the official documentation, which explains this change exactly. So be sure to read the official documents, even if they are all in English.

Then I’ll post my package configuration, buildConfig.ts

Const buildConfig = {// The basic public path when used in production. Note that the path should start and end with/base: '/', // defaults to Vite special value 'modules', another special value is' esNext '- only minimal cross-language conversion is performed (to minimize compatibility), and native dynamic imports are assumed to be supported. Target: 'modules', // Whether dynamically imported polyfill is automatically injected. PolyfillDynamicImport: true, // specify the output directory outDir: 'dist', // specify the directory to nest the generated dynamic freedom under assetsDir: 'assets', // Imported or referenced assets less than this threshold are inlined as base64 urls to avoid additional HTTP requests. Set to 0 to disable inlining completely. AssetsInlineLimit: 4096, // Enable/disable CSS code splitting. When enabled, THE CSS imported in the asynchronous block is inlined into the asynchronous block itself and inserted when the block is loaded. If disabled, all CSS in the entire project will be extracted into a SINGLE CSS file. cssCodeSplit: true, //Generate production source maps. Generate the production source diagram. Sourcemap: false, // When set to true, the build will also generate a manifest.json file that contains a mapping of the unhashed static resource file name to its hash version, which the server framework can then use to render the correct static resource link. manifest: false, }; export { buildConfig };Copy the code

I have configured this part by myself according to the official document, that is to say, some common default configurations have been pulled out. However, as for the package and compression minify, it has not been configured yet, and this part will have to be studied later.

The router and vuex

  • Integrated vue – the router
npm add --dev vue-router
Copy the code

SRC create the router folder and index.ts file under the folder

import { RouteRecordRaw, createRouter, createWebHistory } from 'vue-router';
const routes: RouteRecordRaw[] = [
  {
    path: '/',
    name: 'index',
    component: () => import('/@/views/Index'),
  },
  {
    path: '/login',
    name: 'login',
    component: () => import('/@/views/Login'),
  },
];
const router = createRouter({
  history: createWebHistory(),
  routes,
});
export default router;
Copy the code
  • Integrated vuex
npm add --dev vuex
Copy the code

SRC create the store folder and index.ts file under the folder

import { createStore } from 'vuex'; export default createStore({ state() { return { count: 0, }; }, mutations: { increment(state: any) { state.count++; }, }, actions: { increment(context) { context.commit('increment'); ,}}});Copy the code
  • Modify main.ts and app. TSX

Change main.js to main.ts

import { createApp } from 'vue';
import router from '/@/router';
import store from '/@/store';
import App from '/@/App.tsx';
import '/@/styles/index.scss';
console.log(store);
const app = createApp(App);
app.use(router);
app.use(store);
app.mount('#app');
Copy the code

Change the app.vue file to app. TSX file

import { RouterLink, RouterView } from 'vue-router'; export default { render() { return ( <RouterView></RouterView> ); }};Copy the code

Then change main.js to main.ts in index.html, and you’re done!

Optimize TS type inference

In the SRC directory, create the shims-vue.d.ts, source.d.ts, vue.d.ts files

  • Shims-vue.d.ts: (this is not necessary if the project is all developed via TSX)
declare module '*.vue' {
  import { defineComponent } from 'vue';
  const component: ReturnType<typeof defineComponent>;
  export default component;
}
Copy the code

In qiu’s words, if you don’t use TSX development in VUE3, it’s better to use VUE2. Then post an article here: Why do I recommend using JSX to develop Vue3

  • Source.d.ts: (optimizes compiler prompts to declare static resource files)
declare module '*.png' {
  const src: string;
  export default src;
}
Copy the code
  • Vue. Which s:
import { Store } from 'vuex'; declare module '@vue/runtime-core' { export interface ComponentCustomProperties { $store: Store<any>; }}Copy the code

At this point, a basic project framework is almost complete.

pit

The problem is that when I write render in a.tsx file like I write JSX in react, there is a configuration item in tsconfig.json called JSX

{
    "compilerOptions": {
        "jsx": "preserve"
    }
}
Copy the code

This configuration item I initially gave the value react. Then my page will report an errorCannot find name 'React'. This error has been my whole life from beginning to end! But because it does not affect the operation is not detailed. And then the more I looked, the more I felt. I suspected that there was something wrong with this item configured in tsconfig. Teacher Qiu said you could try another one, but I changed it to preserve. Problem solved.

Afterword.

In the process of building the whole framework, I spent most of my time thinking about how to solve the error report. In the process of resolving problems and reporting errors, I also go through some source code and configuration items. However, the actual meaning of many configuration items is still not clear, which needs to be clarified in the future.

Follow-up plan:

  • Introduce axios or what Teacher Qiu calls fetch to encapsulate asynchronous requests.
  • Postcss post-processor is introduced.
  • Introduce the router asynchronous loading mechanism.
  • Introduce global filters.
  • Encapsulate some globally common components.

I will update it later. If you find this article helpful, please give it a thumbs up. What write bad or incorrect words, hope to help correct. We can also communicate with each other if we have any problems.

Update postCSS, AXIos, and Mock have been updated.

Because vue3 removed the filter, some global handlers were written to be injected into $filter for global calls