Vue3 + typescript + Pinia build an enterprise level development scaffold


From my first contactvue3It has been one year since the release. Becausevue3.2Release of version,<script setup&Amp; Amp; Gt; The experimental mark of has been removed, and many companies have started to use itvue3.2Development project. This article will help you how to use it quicklyvue3.xtypeScriptviteSet up an enterprise level development scaffold. Don’t talk too much nonsense, just get started. There is a project address at the end of the article!!!

Preparation before construction

  1. Vscode: a necessary code writing artifact for front-end users
  2. Chrome: a developer friendly browser (I rely on it anyway)
  3. Nodejs&npm: configure the local development environment. After installing node, you will find that NPM will be installed together
  4. Vue.js devtools: Browser debug plug-in
  5. Vue Language Features (Volar): vscade develops the necessary plug-in for vue3, and provides syntax highlighting prompt, which is very easy to use
  6. Vue 3 Snippets: vue3quick input

The difference between vue2 and vue3

Vue3Due to the fact thatTSRewrite, which has a strong performance in the definition and use of type judgment in applications. Multiple key return values of the same object must be typed by defining corresponding interfaces. Otherwise, an error will be reported in eslint.

vue2The bidirectional data binding ofES5One ofAPI Object.definePropert()Data hijacking is implemented in combination with publish subscribe mode.Vue3Used ines6ofProxyAPIFor data brokers.

Vue3Support fragment(Fragments)

Vue2AndVue3The biggest difference:Vue2applyOptions APIandVue3UsedComposition API

Lifecycle hook changes:

Vue2 ~~~~~~~~~~~ vue3
beforeCreate  -> setup()
created       -> setup()
beforeMount   -> onBeforeMount
mounted       -> onMounted
beforeUpdate  -> onBeforeUpdate
updated       -> onUpdated
beforeDestroy -> onBeforeUnmount
destroyed     -> onUnmounted
activated     -> onActivated
deactivated   -> onDeactivated

Introducing vite

Vite: the next generation front-end development and construction tool

  • Extremely fast development server startup
  • ⚡ ¢ lightweight and fast thermal module reload (HMR)
    – rich features
  • Built in optimization
  • Universal plug-in interface

Vite(French for “quick”, pronounced /vit/) is a new front-end building tool that greatly improves the front-end development experience.

It mainly consists of two parts

  • A development server based on nativeESThe module provides a wealth of built-in functions, such as the amazing module hot update (HMR).
    A set of build instructions that useRollupPackage your code, and it is pre configured to output optimized static resources for the production environment.
  • Vite is intended to provide out of the box configuration. At the same time, its plug-in API and JavaScript API bring a high degree of scalability and complete type support.

Using vite to quickly create scaffolding

Compatibility Note: vite requires node JS version > = 12.0.0.

1. step 1: open CMD under the directory where the project file needs to be created and run the following command
# npm 6.x
npm init @vitejs/app vite_vue3_ts --template

#NPM 7+, additional double horizontal lines are required:
npm init @vitejs/app vite_vue3_ts -- --template

# yarn
yarn create @vitejs/app vite_vue3_ts --template
  1. Here I useyarnTo install

    Vue3 + typescript + Pinia build an enterprise level development scaffold

  2. Select Vue enter = >vue-tsenter

Vue3 + typescript + Pinia build an enterprise level development scaffold

  1. Open the project folder, install dependencies, and start the project
#Enter project folder
CD to your project folder
#Installation dependency
yarn dev
Vue3 + typescript + Pinia build an enterprise level development scaffold


Constraint code style

Eslint support

#Eslint installation
yarn add eslint --dev
#Eslint plug-in installation
yarn add eslint-plugin-vue --dev

yarn add @typescript-eslint/eslint-plugin --dev

yarn add eslint-plugin-prettier --dev

# typescript parser
yarn add @typescript-eslint/parser --dev

Note: ifeslintInstallation error:

Vue3 + typescript + Pinia build an enterprise level development scaffold


You can try the following command:

yarn config set ignore-engines true

Execute again after successful operationeslintInstallation command

New under item Eslintrc js

allocationeslintVerification rules:

module.exports = {
  root: true,
  env: {
    browser: true,
    node: true,
    es2021: true,
  parser: 'vue-eslint-parser',
  extends: [
    //Abbreviation for eslint config prettier
  parserOptions: {
    ecmaVersion: 12,
    parser: '@typescript-eslint/parser',
    sourceType: 'module',
    ecmaFeatures: {
      jsx: true,
  //Eslint plugin Vue @typescript eslint/eslint-plugin short for eslint plugin prettier
  plugins: ['vue', '@typescript-eslint', 'prettier'],
  rules: {
    '@typescript-eslint/ban-ts-ignore': 'off',
    '@typescript-eslint/no-unused-vars': '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',
    '@typescript-eslint/no-use-before-define': 'off',
    '@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',
    'no-var': 'error',
    'prettier/prettier': 'error',
    //Disable console
    'no-console': 'warn',
    //Disable debugger
    'no-debugger': 'warn',
    //Prohibit duplicate case labels
    'no-duplicate-case': 'warn',
    //Suppress empty statement blocks
    'no-empty': 'warn',
    //Prohibit unnecessary parentheses
    'no-extra-parens': 'off',
    //Disallow reassignment of function declarations
    'no-func-assign': 'warn',
    //Disable unreachable codes after return, throw, continue, and break statements
    'no-unreachable': 'warn',
    //Force all control statements to use a consistent bracket style
    curly: 'warn',
    //Require default branch in switch statement
    'default-case': 'warn',
    //Force point numbers to be used whenever possible
    'dot-notation': 'warn',
    //Requires = = = and==
    eqeqeq: 'warn',
    //Disable else block after return statement in if statement
    'no-else-return': 'warn',
    //Null functions are prohibited
    'no-empty-function': 'warn',
    //Disable unnecessary nested blocks
    'no-lone-blocks': 'warn',
    //Prohibit multiple spaces
    'no-multi-spaces': 'warn',
    //Multiple declarations of the same variable are prohibited
    'no-redeclare': 'warn',
    //Assignment statements are prohibited in return statements
    'no-return-assign': 'warn',
    //Disable unnecessary return await
    'no-return-await': 'warn',
    //Prohibit self assignment
    'no-self-assign': 'warn',
    //Prohibit self comparison
    'no-self-compare': 'warn',
    //Suppress unnecessary catch clauses
    'no-useless-catch': 'warn',
    //Prohibit redundant return statements
    'no-useless-return': 'warn',
    //Prohibit variable declarations from having the same name as variables in the outer scope
    'no-shadow': 'off',
    //Allow delete variables
    'no-delete-var': 'off',
    //Force consistent spaces in array square brackets
    'array-bracket-spacing': 'warn',
    //Force consistent brace style in code blocks
    'brace-style': 'warn',
    //Enforce camel spelling naming conventions
    camelcase: 'warn',
    //Force consistent indentation
    indent: 'off',
    //Force consistent use of double or single quotes in JSX attributes
    // 'jsx-quotes': 'warn',
    //Force the maximum depth of a nested block 4
    'max-depth': 'warn',
    //Force maximum rows 300
    // "max-lines": ["warn", { "max": 1200 }],
    //Force function maximum lines of code 50
    // 'max-lines-per-function': ['warn', { max: 70 }],
    //Force the maximum number of statements allowed for a function block 20
    'max-statements': ['warn', 100],
    //Force maximum nesting depth of callback function
    'max-nested-callbacks': ['warn', 3],
    //Force the maximum number of parameters allowed in a function definition
    'max-params': ['warn', 3],
    //Force the maximum number of statements allowed per row
    'max-statements-per-line': ['warn', { max: 1 }],
    //Require a newline character for each call in the method chain
    'newline-per-chained-call': ['warn', { ignoreChainWithDepth: 3 }],
    //Disable if as the only statement in else statements
    'no-lonely-if': 'warn',
    //Prohibit mixed indentation of spaces and tabs
    'no-mixed-spaces-and-tabs': 'warn',
    //Multiple blank lines are prohibited
    'no-multiple-empty-lines': 'warn',
    semi: ['warn', 'never'],
    //Force consistent whitespace before blocks
    'space-before-blocks': 'warn',
    //Force consistent spaces before the left parenthesis of function
    // 'space-before-function-paren': ['warn', 'never'],
    //Force consistent spaces within parentheses
    'space-in-parens': 'warn',
    //Require spaces around operators
    'space-infix-ops': 'warn',
    //Force consistent spaces before and after unary operators
    'space-unary-ops': 'warn',
    //Force consistent spaces // or / * in comments
    // "spaced-comment": "warn",
    //Force a space around the colon of the switch
    'switch-colon-spacing': 'warn',
    //Force consistent spaces before and after arrows in arrow functions
    'arrow-spacing': 'warn',
    'no-var': 'warn',
    'prefer-const': 'warn',
    'prefer-rest-params': 'warn',
    'no-useless-escape': 'warn',
    'no-irregular-whitespace': 'warn',
    'no-prototype-builtins': 'warn',
    'no-fallthrough': 'warn',
    'no-extra-boolean-cast': 'warn',
    'no-case-declarations': 'warn',
    'no-async-promise-executor': 'warn',
  globals: {
    defineProps: 'readonly',
    defineEmits: 'readonly',
    defineExpose: 'readonly',
    withDefaults: 'readonly',

New under item Eslintignore

#Eslint ignore the check (add it yourself according to the needs of the project)

Prettier support

#Installing prettier
yarn add prettier --dev

Resolve eslint and prettier conflicts

solveESLintStyle specifications andprettierConflicts with style specifications inprettierThe style specification in eslint will automatically become invalid

#Install the plug-in eslint config prettier
yarn add eslint-config-prettier --dev

New under item Prettier js

allocationprettierFormatting rules:

module.exports = {
  tabWidth: 2,
  jsxSingleQuote: true,
  jsxBracketSameLine: true,
  printWidth: 100,
  singleQuote: true,
  semi: false,
  overrides: [
      files: '*.json',
      options: {
        printWidth: 200,
  arrowParens: 'always',

New under item Prettierignore

#Ignore the format file (add it yourself according to the needs of the project)

Package JSON configuration:

  "script": {
    "lint": "eslint src --fix --ext .ts,.tsx,.vue,.js,.jsx",
    "prettier": "prettier --write ."

After the above configuration is completed, you can run the followingcommandTest the following code to check the formatting effect:

#Eslint check
yarn lint
#Prettier auto format
yarn prettier

Configure husky + lint staged

applyhusky+ lint-stagedCode specification for help team, recommended for husky & lint staged installationmrm, it willpackage.jsonDepend on the code quality tools in the to install and configure husky and lint staged, so please ensure that all code quality tools are installed and configured before that, such asPrettierandESlint

Install MRM first

npm i mrm -D --registry=

huskyIs an add-on for git clientshookTools for. After installation, it will be automatically installed in the warehouse.git/Add corresponding hooks under the directory; such aspre-commitThe hook will execute on yougit commitTrigger of.

So we canpre-commitFor exampleLint checkunit testing Code beautificationEtc. Of course,pre-commitOf course, the speed of the commands executed in the phase should not be too slow. It is not a good experience to wait a long time for each commit.

lint-staged, a file that only filters out git code staging area (bygit addThe tools of; This is very practical, because if we check the code of the whole project, it may take a long time. If it is an old project, it may be troublesome to check and modify the code specification of the previous code, which may lead to great changes in the project.

So thislint-staged, which is a good tool for team projects and open source projects. It is a specification and constraint for the code to be submitted by individuals

Lint mounted

mrminstalllint-stagedWill automaticallyhuskyInstalled together

npx mrm lint-staged

After successful installation, you will findpackage.jsonThe following configurations are added in:

Vue3 + typescript + Pinia build an enterprise level development scaffold


Because we have to combineprettierCode formatting. Modify the following configurations:

"husky": {
    "hooks": {
      "pre-commit": "lint-staged"
  "lint-staged": {
    "*.{js,jsx,vue,ts,tsx}": [
      "yarn lint",
      "prettier --write",
      "git add"

OK, here the code formatting configuration is basically completed!!!

Profile reference alias alias

directly modifyvite.config.tsFile configuration:

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

export default defineConfig({
  plugins: [vue()],
  resolve: {
    alias: {
      '@': path.resolve(__dirname, 'src'),


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

Configure CSS preprocessor SCSS

althoughviteNative supportless/sass/scss/stylus, but you must manually install their preprocessor dependencies


yarn add dart-sass --dev
yarn add sass --dev

Configure global SCSS style file

staysrc/assetsAdd nextstyleFolder for global style files

Newmain.scss, set a color variable for testing:

$test-color: red;

How to globally inject this global style file into the project? allocationViteThat is:

        additionalData:'@import "@/assets/style/mian.scss";'
Used in components

You can directly use global without any importscssDefined variables

  color: $test-color;


#Install routing
yarn add [email protected]

staysrcNew under filerouterFolder =>router.tsDocument, as follows:

import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router'

const routes: RouteRecordRaw[] = [
    path: '/',
    name: 'Login',
    Component: () = > Import ('@/pages/login/login.vue'), // note the file suffix vue

const router = createRouter({
  history: createWebHistory(),

export default router

Modify entry filemian.ts :

import { createApp } from 'vue'
import App from './App.vue'
import router from './router/index'

const app = createApp(App)



The basic configuration of routes here has been completed. You can view more configuration informationvue-routerOfficial documents:

vue-router4.xsupporttypescript, the type of configured route isRouteRecordRaw, heremetaIt can give us more room to play. Here are some references:

  • title:string; Page title, usually required.
  • icon?:string; Icon, usually used with menus.
  • auth?:boolean; Whether login permission is required.
  • ignoreAuth?:boolean; Whether to ignore permissions.
  • roles?:RoleEnum[]; Accessible roles
  • keepAlive?:boolean; Whether to enable page caching
  • hideMenu?:boolean; Some routes we do not want to display in the menu, such as some edit pages.
  • order?:number; Menu sorting.
  • frameUrl?:string; Nested outer chain.

Only some ideas are provided here. There will be some differences in the businesses involved in each project. I won’t explain them in detail here. You can configure them according to your own business needs.

Unified request encapsulation

Used vue2 Students of X should be familiar with Axios. Here we directly use Axios for packaging:

#Installing Axios
yarn add axios
#Install nprogress to request loading
#You can also customize other loading according to project requirements
yarn add nprogress
#Type declaration, or add one containing `declare module'nprogress'
yarn add @types/nprogress --dev

In actual use, it can be modified according to the project. For example, put and delete requests can be added in the restful API, and the restype can also be dynamically modified according to the general return value of the back end

Add a service folder, and add http TS file and API folder:

Vue3 + typescript + Pinia build an enterprise level development scaffold


http.ts: foraxiosencapsulation

import axios, { AxiosRequestConfig } from 'axios'
import NProgress from 'nprogress'

//Set request header and request path
axios.defaults.baseURL = '/api'
axios.defaults.timeout = 10000['Content-Type'] = 'application/json;charset=UTF-8'
  (config): AxiosRequestConfig<any> => {
    const token = window.sessionStorage.getItem('token')
    if (token) {
      config.headers.token = token
    return config
  (error) => {
    return error
//Response interception
axios.interceptors.response.use((res) => {
  if ( === 111) {
    sessionStorage.setItem('token', '')
    //Token expiration
  return res

interface ResType<T> {
  code: number
  data?: T
  msg: string
  err?: string
interface Http {
  get<T>(url: string, params?: unknown): Promise<ResType<T>>
  post<T>(url: string, params?: unknown): Promise<ResType<T>>
  upload<T>(url: string, params: unknown): Promise<ResType<T>>
  download(url: string): void

const http: Http = {
  get(url, params) {
    return new Promise((resolve, reject) => {
        .get(url, { params })
        .then((res) => {
        .catch((err) => {
  post(url, params) {
    return new Promise((resolve, reject) => {
        .post(url, JSON.stringify(params))
        .then((res) => {
        .catch((err) => {
  upload(url, file) {
    return new Promise((resolve, reject) => {
        .post(url, file, {
          headers: { 'Content-Type': 'multipart/form-data' },
        .then((res) => {
        .catch((err) => {
  download(url) {
    const iframe = document.createElement('iframe') = 'none'
    iframe.src = url
    iframe.onload = function () {
export default http

api: unified management of interfaces in the project, divided according to modules

stayapiNew under fileloginThe folder is used to store the request interface of the login module. The login folder is added separatelylogin.ts types.ts :


import http from '@/service/http'
import * as T from './types'

const loginApi: T.ILoginApi = {
        return'/login', params)

export default loginApi


export interface ILoginParams {
    userName: string
    passWord: string | number
export interface ILoginApi {
    login: (params: ILoginParams)=> Promise<any>

At this point, a simple request encapsulation is completed!!!!

In addition to manually encapsulating Axios, a vue3 request library is recommended:VueRequest, very easy to use, let’s take a lookVueRequestWhat are the easier functions to use!!!

  • All data is responsive
  • poll request
  • Auto process error retry
  • Built in request cache
  • Throttling request and anti shake request
  • Automatic re request when focusing on page
  • ⚙️ Powerful paging extensions and loading more extensions
  • Completely written in typescript, with powerful type prompt
  • ⚡ Compatible with vite
  • Lightweight
  • Out of the box

Is it powerful

Official website link:

State management Pinia

Because the typescript support of vuex 4 is sad, the state management abandons vuex and adopts pinia Pinia’s author is a member of Vue’s core team
You da seems to saypiniaMay replacevuex, so please feel free to use.

  • idIt is necessary to connect the used store to devtools.
  • Creation method:new Vuex.Store(...)(vuex3)createStore(...)(vuex4)
  • Compared with vuex3, state is now aFunction return object
  • absencemutations, don’t worry. State changes are still recorded in devtools.
yarn add [email protected]

Main Added in TS

import { createPinia } from "pinia"
#Create a root repository and pass it to the application

staysrcNew under folderstoreFolder, followed by adding in the storemain.ts

establishstore, mian.ts :

import { defineStore } from 'pinia'

export const useMainStore = defineStore({
  id: 'mian',
  state: () =>({
    Name: 'super administrator'

Get the store in the build:


<script setup lang="ts">
import { useMainStore } from "@/store/mian"

const mainStore = useMainStore()


Getters usage introduction

Getters in Pinia have the same functions as getters in vuex and calculation attributes in components
store=> mian.ts

import { defineStore } from 'pinia'

export const useMainStore = defineStore({
  id: 'mian',
  state: () => ({
    Name: 'super administrator',
  // getters
  getters: {
    nameLength: (state) =>,

Used in components:

  <div> user name: {{} <br / > length: {{mainstore.namelength} </div>
  <button @click= "updatename" > Modify name</button>

<script setup lang="ts">
import { useMainStore } from '@/store/mian'

const mainStore = useMainStore()

const updateName = ()=>{
  //$patch modify the data in the store
    Name: 'the name has been modified, and the namelength has also been changed accordingly'


Here andVuexIt’s very different,PiniaOnly one method is provided to define the rules of how to change the state. DiscardmutationsRely only onActionsThis is a major change.

PiniaofferActionsMore flexible:

  • You can use components or otheractioncall
  • From otherstoreofactionCall in
  • Directly instoreCall on instance
  • Support synchronous or asynchronous
  • There are any number of parameters
  • It can contain logic about how to change the state (that is, the function of vuex’s changes)
  • Yes$patchMethod to change state properties directly
import { defineStore } from 'pinia'

export const useMainStore = defineStore({
  id: 'mian',
  state: () => ({
    Name: 'super administrator',
  getters: {
    nameLength: (state) =>,
  actions: {
    async insertPost(data:string){
      //Can be asynchronous
      // await doAjaxRequest(data); = data;

Environment variable configuration

viteTwo modes are provided: with development serverDevelopment mode(Development) andProduction mode(production)

Project root directory new:.env.development :



Project root directory new:.env.production :



Used in components:


Packaging distinguishes development environment from production environment

"build:dev": "vite build --mode development",
"build:pro": "vite build --mode production",

Use the naive UI of the component library (you can also choose according to your own technology stack)

Component library selection, here we selectNaive UIWhy did you choose it? Can I just say what you da da recommended?

#Install component library
yarn add naive-ui
#Install fonts
yarn add vfonts

How to use

import { NButton } from "naive-ui"

Global configuration config provider

Global configuration sets the theme, language of internal components and the class name of DOM where components are unloaded in other locations.

<n-config-provider :locale="zhCN" :theme="theme">
    <!--  Container -- >

Vite common basic configuration

Basic configuration

function agentandpackallocation

server: {
    host: '',
    port: 3000,
    open: true,
    https: false,
    proxy: {}

Removing the console debugger in the production environment

  terserOptions: {
      compress: {
        drop_console: true,
        drop_debugger: true

Production environment generation GZ file

opengzipIt can greatly compress static resources and play a significant role in the speed of page loading.

applyvite-plugin-compressionYesgziporbrotliThis step requires the cooperation of the server. Vite can only help you package.gzDocuments. This plug-in is easy to use. You can even import it without configuring parameters.

yarn add --dev vite-plugin-compression

Add to plugins:

import viteCompression from 'vite-plugin-compression'

//Gzip compression production environment generation GZ file
      verbose: true,
      disable: false,
      threshold: 10240,
      algorithm: 'gzip',
      ext: '.gz',

Final vite config. TS

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import path from 'path'
import viteCompression from 'vite-plugin-compression'

export default defineConfig({
  Base:'./'// Packaging path
  plugins: [
    //Gzip compression production environment generation GZ file
      verbose: true,
      disable: false,
      threshold: 10240,
      algorithm: 'gzip',
      ext: '.gz',
  //Configure alias
  resolve: {
    alias: {
      '@': path.resolve(__dirname, 'src'),
        additionalData:'@import "@/assets/style/mian.scss";'
  //Start service configuration
  server: {
    host: '',
    port: 8000,
    open: true,
    https: false,
    proxy: {}
  //Production environment packaging configuration
  //Remove the console debugger
  build: {
    terserOptions: {
      compress: {
        drop_console: true,
        drop_debugger: true,

Common plug-ins

You can view official documents:

So far, a complete project framework has been preliminarily formed, and business development can be started happily!

Highly recommended hooks Library:VueUse

Interested parties can learn about it

Code cloud address:

Recommended Today

String s = new string (“XXX”); how many objects are created?

introduction I went to the interview on Friday and was asked a dumb question Interviewer: String s = new string (“XXX”); How many objects were created?Me: two?Interviewer: which two?I:… (mute) Before that, I only knew that there were two. As for why there were two, I didn’t know. analysis //In constant pool String str1 = […]