7

Vite is very hot now, maybe many friends have not used Vite , but I believe most of the friends are already using Vite , because it is too fragrant. There may be many things that Vite is not configured in the process of use. It is not as Vue-cli . So today I will talk about how to configure the development environment. The main points involved are as follows:

  • TypeScript
  • Vuex
  • Vue-Router
  • E2E

    • Cypress
  • Test unit

    • Jest
    • vtu
  • Eslint + Perttite
  • verify git commit message
  • CI
  • alias

Vite initialization project

Before starting, you must first use Vite create a project. If you Vite , you can skip it. According to the Vite official website, you can use npm or yarn to create a project.

Use NPM:

npm init vite@latest

Use Yarn:

yarn create vite

Use PNPM:

pnpx create-vite

After entering the command, follow the prompts. Because the project needs to support TypeScript I chose vue-ts here. Once you've created Vite will help us to create a good project can be found Vite create a good project and actually use Vue-cli created project directory structure in fact is about the same, not much to go into details here.

Integrated Vue-Router

Vue-Router is one of the indispensable tools in most projects. Vue-Router can make it easier to build single-page applications. The functions included are:

  • Nested routing/view table
  • Modular, component-based routing configuration
  • Routing parameters, queries, wildcards
  • View transition effect based on Vue.js transition system
  • Fine-grained navigation control
  • Link with automatically activated CSS class
  • HTML5 history mode or hash mode, automatically downgraded in IE9
  • Custom scroll bar behavior

The above is intercepted from Vue-router official website

Install Vue-Router:

Use NPM:

npm install vue-router@next --save

Use Yarn:

yarn add vue-router@next --save

After the installation is complete, create a folder router/index.ts src directory. After the creation is complete, you need to initialize Vue-Router in Vue-Router We temporarily put the initialization work aside, first need to create the pages folder, and create the page that needs to be displayed.

After the creation is complete, the next step is to complete the initialization of the file in router/index.ts

import { createRouter, createWebHashHistory } from "vue-router";

const router = createRouter({
  history: createWebHashHistory(),
  routes: [
    {
      path: "/home",
      name: "Home",
      alias: "/",
      component: () => import("../pages/Home.vue")
    },
    {
      path: "/about",
      name: "About",
      component: () => import("../pages/About.vue")
    }
  ]
})

export default router;

Next, integrate Vue-Router in the main.ts file:

import { createApp } from 'vue';
import App from './App.vue';

import router from "./router";

const app = createApp(App);
app.use(router);
app.mount('#app');

Test it. Here we modify App.vue to test whether our routing can be used normally.

<template>
  <router-link to="/home">Home</router-link>
  <router-link to="/about">About</router-link>
  <router-view></router-view>
</template>

<script lang="ts">
import { defineComponent } from 'vue'

export default defineComponent({
  name: 'App'
})
</script>

After starting the service, you can see the configured page, indicating that the configured route has taken effect. Good Job , really good~~~

Integrate Vuex

Vuex is Vue . It also plays a great role in the actual application process. When the data flow between multiple components becomes very difficult, it is necessary to centrally manage the state. The state storage of Vuex Responsive. When the Vue component reads the status store store changes, the corresponding component will be updated accordingly.

Install Vuex:

Use NPM:

npm install vuex@next --save

Use Yarn:

yarn add vuex@next --save

After the installation is complete, first add store/index.ts to initialize Vuex . It should be noted that the following example uses the Vuex namespace. It may be relatively common to use namespaces in actual projects to avoid variable pollution during state management.

import { createStore } from "vuex";

const store = createStore({
  modules: {
    home: {
      namespaced: true,
      state: {
        count: 1
      },
      mutations: {
        add(state){
          state.count++;
        }
      }
    }
  }
})

export default store;

Integrated into Vue :

import { createApp } from 'vue';
import App from './App.vue';

import router from "./router";
import store from "./store";

const app = createApp(App);
app.use(router);
app.use(store);
app.mount('#app');

Now Vuex has been integrated into Vue . In order to ensure that the integrated Vuex is effective, you need to test this:

pages/Home.vue

<template>
  <h1>Home</h1>
  <h2>{{count}}</h2>
  <button @click="handleClick">click</button>
</template>

<script lang="ts">
import { defineComponent, computed } from 'vue';
import { useStore } from 'vuex';

export default defineComponent({
  setup () {
    const store = useStore();
    const count = computed(() => store.state.home.count);
    const handleClick = () => {
      store.commit('home/add');
    };
    return {
      handleClick,
      count
    };
  }
})
</script>

When you click the button, you can find that the count also increases with each click, so store can be used normally. Good Job , really good~~~

Integrated unit test

In order to ensure the robustness of the program during the development process, the program needs to be unit tested, so when the project is initialized, it is also necessary to configure the unit test. The tool used in the configuration is jest . If you don't know much about unit testing, please learn from Baidu on your own and get out of the focus of this article, so I won't go into details.

Installation related dependencies:

Use NPM:

npm install jest -D                  # 单元测试工具 ^26.6.3
npm install @types/jest -D           # 单元测试类型文件
npm install babel-jest -D            # 支持es6模块 ^26.6.3
npm install @babel/preset-env -D     # 支持es6模块 ^7.14.7
npm install vue-jest@next -D         # 支持Vue文件 ^5.0.0-alpha.8
npm install ts-jest -D               # 支持Ts      ^26.5.1
npm install @vue/test-utils@next     # Vue官方测试工具 2.0.0-rc.6
npm install @vue/test-utils@next -D  # 2.0.0-rc.6
npm install @babel/preset-typescript # 支持Ts ^7.12.17

Use Yarn:

yarn add jest --dev                  # 单元测试工具 ^26.6.3
yarn add @types/jest --dev           # 单元测试类型文件
yarn add babel-jest --dev            # 支持es6模块 ^26.6.3
yarn add @babel/preset-env --dev     # 支持es6模块 ^7.14.7
yarn add vue-jest@next --dev         # 支持Vue文件 ^5.0.0-alpha.8
yarn add ts-jest --dev               # 支持Ts      ^26.5.1
yarn add @vue/test-utils@next        # Vue官方测试工具 2.0.0-rc.6
yarn add @babel/preset-typescript    # 支持Ts ^7.12.17

After the dependency installation is complete, create a tests src directory. This folder is used to store test-related files. Because we not only have unit tests but also E2E tests, we need to distinguish between the two.

Because the installation and configuration may cause version incompatibility and cause an error, you must pay attention to the version number when you configure it. The version number has been annotated above, so you need to pay attention. After installing the dependencies, the next step is to configure the unit test.

Create the basic configuration of Jest jest.config.js in the root directory:

module.exports = {
  transform: {  //  babel预设
    "^.+\\.vue$": "vue-jest",       //  支持导入Vue文件
    "^.+\\.jsx?$": "babel-jest",    //  支持import语法
    '^.+\\.tsx?$': 'ts-jest'        //  支持ts
  }
};

Modify tsconfig.json

{
  "compilerOptions": {
    "target": "esnext",
    "module": "esnext",
    "moduleResolution": "node",
    "strict": true,
    "jsx": "preserve",
    "sourceMap": true,
    "resolveJsonModule": true,
    "esModuleInterop": true,
    "lib": ["esnext", "dom"],
    "types": ["vite/client", "jest"]    //  指定类型为jest
  },
  "include": [
    "src/**/*.ts", 
    "src/**/*.d.ts",
    "src/**/*.tsx",
    "src/**/*.vue", 
    "tests"     // 指定单元测试路径
  ]
}

Because Node not run TypeScript here need to use babel to TypeScript compile, to configure babel related configuration, created in the root directory babel.config.js :


module.exports = {
  transform: {
    "^.+\\.vue$": "vue-jest",
    "^.+\\.jsx?$": "babel-jest",
    '^.+\\.tsx?$': 'ts-jest'
  },
  testMatch: [  //  匹配单元测试规则
    "**/?(*.)+(test).[jt]s?(x)"
  ]
};

The configuration of the unit test has been basically completed above. The next step is to test whether the unit test can be run normally. Create folders (files) such as tests/unit

index.test.ts

import { mount } from "@vue/test-utils";
import demo from "./demo";
import Foo from "../../src/components/Foo.vue";

test("1+1=2", () => {
  console.log(demo);
  console.log(mount(Foo));
  expect(1+1).toBe(2);
});

demo.ts

export default {
  a: 1
};

After that is to run the unit test, add the command package.json

{
  "scripts": {
    "test:unit": "jest"
  }
}

Run it to see the output result, then it means that our unit test can run normally, Good Job , really good~~~

Integrated E2E testing

Maybe many friends are not particularly familiar with the E2E E2E is an end-to-end test, which is a black box test. By writing test cases, it automatically simulates user operations to ensure normal communication between components and program flow data transfer as expected.

Now that E2E have a certain understanding of 060f53f2198c87, you need to install E2E test the related modules. The E2E test uses the cypress dependency. The first installation may be slow, so be patient:

Use NPM:

yarn add cypress -D             # e2e测试工具

Use Yarn:

yarn add cypress --dev          # e2e测试工具

After the installation dependencies are completed, cypress needs to be initialized. At this time, we need to add commands to package.json

{
  "scripts": {
    "test:e2e": "cypress open"
  }
}

After the execution is complete, a cypress folder will be created in the root directory. This folder contains many directory-related files. tests . Then add the e2e folder to the cypress folder, and cut all the contents of tests/e2e in.

After completing the above operations? cypress at run time will default to cypress file to find its dependencies, but now the cypress the file has been moved away, then we need to cypress to configure, modify cypress base path and point to tests/e2e file That's it.

cypress.json file in the root directory:

{
  "pluginsFile": "tests/e2e/plugins/index.ts",
  "video": false    //  关掉视频录制默认行为
}

.ts may have discovered that the file cited above ends with 060f53f2198e7d, but under the corresponding directory is the .js file. Why? Because our project is based on TypeScript based projects, so we E2E test should also use TypeScript , this time we should put E2E folder all .js file instead .ts (It should be noted that the integration into specs , and delete All test cases inside).

Change plugin under the index.ts folder:

module.exports = (on, config) => {
  return Object.assign({}, config, {
    fixturesFolder: 'tests/e2e/fixtures',
    integrationFolder: 'tests/e2e/specs',
    screenshotsFolder: 'tests/e2e/screenshots',
    videosFolder: 'tests/e2e/videos',
    supportFile: 'tests/e2e/support/index.ts'
  });
};

The unit test for cypress has been completed, but the test case has been deleted just now. Next, write the test case and create it tests/e2e/specs

first.specs.ts

describe("First ", () => {

  it("before", () => {
    cy.visit("http://localhost:3000/");
    cy.get("button").click();
  });

});

It should be noted that because cypress needs to be tested when the project is started, we need to run the local service. After the test case is realized, it can be run. In the pop-up page, you will see the files of all the use cases. Click the corresponding file and it will be opened in the browser. The left side of the browser shows the corresponding test steps.

After the above configuration, the configuration of E2E has been basically completed, because the two tests are all in tests , will the two tests affect each other?

episode

Now that both the E2E test and the unit test have been completed, run the unit test and the E2E test respectively. After running the unit test, an error will be thrown.

  ● Test suite failed to run

    tests/unit/index.spec.ts:8:15 - error TS2339:
        Property 'toBe' does not exist on type 'Assertion'.

    8   expect(1+1).toBe(2);
                    ~~~~

Test Suites: 1 failed, 1 total
Tests:       0 total
Snapshots:   0 total
Time:        3.234 s
Ran all test suites.
error Command failed with exit code 1.

Obviously, our unit test E2E test, and the operation failed. You can find the corresponding location according to the file prompt. Because the expect expect under the unit test is no longer the original one. Unit test expect too.

Time since the problem emerged on the need to solve the problem ing, hives tested in the above, in order to make unit testing support TypeScript in tsconfig.json add a path test, if we just point to unit test inside the problem is not solved, but If so, cypress can no longer support TypeScript , in fact, we can E2E then create a tsconfig.json this document applies only to E2E .

tsconfig.json

{
  "compilerOptions": {
    "target": "esnext",
    "module": "esnext",
    "moduleResolution": "node",
    "strict": true,
    "jsx": "preserve",
    "sourceMap": true,
    "resolveJsonModule": true,
    "esModuleInterop": true,
    "lib": ["esnext", "dom"],
    "types": ["vite/client", "jest"]
  },
  "include": [
    "src/**/*.ts", 
    "src/**/*.d.ts",
    "src/**/*.tsx",
    "src/**/*.vue", 
    "tests/unit"     // 指定单元测试路径
  ]
}

tests/e2e/tsconfig.json

{
  "compilerOptions": {
    "target": "esnext",
    "module": "esnext",
    "moduleResolution": "node",
    "strict": true,
    "jsx": "preserve",
    "sourceMap": true,
    "resolveJsonModule": true,
    "esModuleInterop": true,
    "lib": ["esnext", "dom"]
  }
}

Later, when you run yarn test:unit , you will find that no error will be reported. So after this change, is e2e affected? Obviously no error occurred when running yarn test:e2e , and it can run normally. Good Job , really good~~~

When running yarn test:e2e , a window will always pop up, but when the project is deployed to CI , there is no way to click. What should I do at this time? In fact, cypress be executed on the terminal. Use the npx cypress run command to run the E2E test on the terminal.

Modify package.json :

{
  "scripts": {
    "test:unit": "jest",
    "test:e2e": "cypress open",
    "test": "yarn test:unit && npx cypress run"
  }
}

Then run yarn test to run the unit test first, and then run the E2E test.

Integrated Git submission verification

When developing the project, it may not be developed by one person, but by multiple people. As a standard automation, Git , there must be some fixed and significant formats to standardize our project developers. , This time you need to use certain tools to constrain.

Installation related dependencies:

Use NPM:

npm install yorkie -D
npm install chalk -D

Use Yarn:

yarn add yorkie --dev
yarn add chalk --dev

After installing dependence on yorkie following configuration needs to be related, in package.json add field:

{
    "gitHooks": {
        "commit-msg": "node scripts/commitMessage.js"
    }
}

In the above configuration, a js file is run, then the js file is a verification of the submitted content.

scripts/commitMessage.js

const chalk = require('chalk')
const msgPath = process.env.GIT_PARAMS
const msg = require('fs').readFileSync(msgPath, 'utf-8').trim()

const commitRE = /^(revert: )?(feat|fix|docs|dx|style|refactor|perf|test|workflow|build|ci|chore|types|wip|release)(\(.+\))?(.{1,10})?: .{1,50}/
const mergeRe = /^(Merge pull request|Merge branch)/

if (!commitRE.test(msg)) {
  if (!mergeRe.test(msg)) {
    console.log(msg)
    console.error(
      `  ${chalk.bgRed.white(' ERROR ')} ${chalk.red(
        `invalid commit message format.`,
      )}\n\n` +
        chalk.red(
          `  Proper commit message format is required for automated changelog generation. Examples:\n\n`,
        ) +
        `    ${chalk.green(`feat(compiler): add 'comments' option`)}\n` +
        `    ${chalk.green(
          `fix(v-model): handle events on blur (close #28)`,
        )}\n\n` +
        chalk.red(
          `  See https://github.com/vuejs/vue-next/blob/master/.github/commit-convention.md for more details.\n`,
        ),
    )
    process.exit(1)
  }
}

Integrated Eslint

Eslint is a very good tool for team development. It can be configured to constrain the developer's code style and writing format Eslint

Installation related dependencies:

Use NPM:

npm install eslint -D
npm install eslint-plugin-vue -D
npm install @vue/eslint-config-typescript -D
npm install @typescript-eslint/parser -D
npm install @typescript-eslint/eslint-plugin -D
npm install typescript -D
npm install prettier -D
npm install eslint-plugin-prettier -D
npm install @vue/eslint-config-prettier -D

Use Yarn:

yarn add eslint --dev
yarn add eslint-plugin-vue --dev
yarn add @vue/eslint-config-typescript --dev
yarn add @typescript-eslint/parser --dev
yarn add @typescript-eslint/eslint-plugin --dev
yarn add typescript --dev
yarn add prettier --dev
yarn add eslint-plugin-prettier --dev
yarn add @vue/eslint-config-prettier --dev

After the configuration and installation are complete, you need to eslint .eslintrc in the root directory:

.eslintrc

{
  "root": true,
  "env": {
    "browser": true,
    "node": true,
    "es2021": true
  },
  "extends": [
    "plugin:vue/vue3-recommended",
    "eslint:recommended",
    "@vue/typescript/recommended"
  ],
  "parserOptions": {
    "ecmaVersion": 2021
  }
}

The configuration items have been added, how to run the configured eslint ? Next, you need to add a command package.json

{
    "lint": "eslint --ext src/**/*.{ts,vue} --no-error-on-unmatched-pattern"
}

Then try running yarn lint on it, you can eslint complete check of the format, the question now is what, in the implementation of yarn lint time to check all the documents all the time, this is not what we want, if many of the documents, then the speed will be very slow, so there is no way, only git be of modified files when submitted eslint check it?

Installation related dependencies:

Use NPM:

npm install lint-staged -D

Use Yarn:

yarn add lint-staged --dev

Modify package.json :

{
  "gitHooks": {
    "commit-msg": "node scripts/commitMessage.js",
    "pre-commit": "lint-staged"
  },
  "lint-staged": {
    "*.{ts,vue}": "eslint --fix"
  },
  "scripts": {
    "test:unit": "jest",
    "test:e2e": "cypress open",
    "test": "yarn test:unit && npx cypress run",
    "lint": "npx prettier -w -u . && eslint --ext .ts,.vue src/** --no-error-on-unmatched-pattern",
    "bea": "npx prettier -w -u ."   //  美化代码
  },
}

Configure alias

When using cli , always use @ to import some files. Because Vite does not provide similar configuration, we need to manually configure some related configurations to continue to use the @ symbol to quickly import files.

Modify vite.config.ts :

import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import { join } from "path";

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [vue()],
  resolve: {
    alias: [
      {
        find: '@',
        replacement: '/src',
      },
      { find: 'views', replacement: '/src/views' },
      { find: 'components', replacement: '/src/components' },
    ]
  }
});

Modify tsconfig.json :

{
  "compilerOptions": {
    "target": "esnext",
    "module": "esnext",
    "moduleResolution": "node",
    "strict": true,
    "jsx": "preserve",
    "sourceMap": true,
    "resolveJsonModule": true,
    "esModuleInterop": true,
    "lib": ["esnext", "dom"],
    "types": ["vite/client", "jest"],
    "baseUrl": ".",
    "paths": {
      "@/*": ["src/*"]
    } 
  },
  "include": [
    "src/**/*.ts",
    "src/**/*.d.ts",
    "src/**/*.tsx",
    "src/**/*.vue",
    "tests/unit"
  ]
}

In order to ensure the unit can also be used in the test @ introduced src following documents for jest.config.js modification to the configuration:

Modify jest.config.js :

module.exports = {
  transform: {
    '^.+\\.vue$': 'vue-jest',
    '^.+\\.jsx?$': 'babel-jest',
    '^.+\\.tsx?$': 'ts-jest',
  },
  testMatch: ['**/?(*.)+(test).[jt]s?(x)'],
  moduleNameMapper: {
    "@/(.*)$": "<rootDir>/src/$1" 
  }
};

end

Vite when using 060f53f21b2420 to initialize the project. For example, when configuring unit testing, the version is compatible, and when configuring aliases, you need to pay attention to the coordination between multiple files.

We have done so many unified configurations to make the project more standardized in the development process, to achieve a unified effect, and to make our programs more robust. The revolution has not yet succeeded, comrades still need to work hard.

There may be some problems with some of the insights in the article. You are welcome to point out in the comment area. Let's learn and make progress together. If the article is helpful to you, please click like it~


Aaron
4k 声望6.1k 粉丝

Easy life, happy elimination of bugs.