This article has participated in the activity of “New person creation Ceremony”, and started the road of digging gold creation together.

1. Initial project

Build foundation project quickly by using vite command (project name can be set by yourself)

// Note: I am using [email protected] yarn create @vitejs/app my-projectCopy the code

choosevueenter

choosevue-tsCarriage return (the build project chooses to install TypeScript initially to avoid reinstalling TypeScript later)

Then according to the prompt operation installation can generate directory, project in C disk user by default!

Enter the project and install the dependencies

npm install
Copy the code

Run the project

npm run dev
Copy the code

Open the project default addresshttp://localhost:3000To view

At this point, the initial construction of the project is complete.

2. Introduce esLint and Prettier to constrain code style

Install eslint

npm i -D eslint eslint-plugin-vue @typescript-eslint/parser @typescript-eslint/eslint-plugin
Copy the code

The functions of these four dependencies are:

  • eslint: EsLint core code
  • eslint-plugin-vue:Plugin to use Eslint for Vue
  • @typescript-eslint/parser: ESLint parser that parses typescript to check and normalize typescript code
  • @typescript-eslint/eslint-pluginESLint: This is an ESLint plugin that contains various conventions that are defined to check Typescript code

Create.eslintrc.js under the project and configure the ESLint verification rule:

module.exports = { extends: ["plugin:vue/vue3-essential"], parserOptions: { ecmaVersion: 2020, sourceType: "module", }, plugins: ["vue", "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"," console": "Warn ", // disable debugger" no-debugger": "warn", // disallow duplicate case tag "no-duplicate-case":" WARN ", // disallow empty block "no-empty": "Warn ", // disallow unnecessary parentheses "no-extra-parens": "off", // disallow reassigning" no-fun-assign "to function declarations: "Warn ", // Disallow the unreachable code "no-unreachable":" WARN "after return, throw, continue, and break statements, // force all control statements to use the same parenthesis style curly: "Warn ", // requires the switch statement to have a default branch "default-case":" WARN ", // forces the use of the dot "dot-notation": "WARN" as much as possible, // requires the use of === and! == eqeqeq: "warn", // disallow if statement after return statement else block "no-else-return": "warn", // disallow empty function" no-empty-function": "Warn ", // disallow unnecessary nested blocks" no-lone-blocks": "warn", // disallow multiple Spaces "no-multi-spaces":" WARN ", // disallow multiple declarations of the same variable "no-redeclare": "Warn ", // disallow the assignment statement "no-return-assign":" WARN "in return statements, // disable unnecessary return await" no-return-await": "Warn ", // disallow self-assignment "no-self-assign": "warn", // disallow self-comparison "no-self-compare": "warn", // disallow unnecessary catch clause "no-useless-catch": "Warn ", // disallow redundant return statements "no-useless-return":" WARN ", // disallow variable declarations with the same name as variables in the outer scope "no-shadow": "Off ", // allows the delete variable "no-delete-var": "off", // enforces the consistent spacing of array brackets in "array-bracket-spacing": "Warn ", // enforces the use of a consistent brace style in code blocks "brace-style": "warn", // enforces the camel spelling naming convention camelcase:" WARN ", // enforces the use of a consistent indent: "Off ", // forces the consistent use of double or single quotes in JSX attributes // 'jsx-quotes': 'WARN ', // forces the maximum depth of nested blocks to be 4 "max-depth": "Warn ", // enforce Max lines 300 // "max-lines": ["warn", {" Max ": 1200}], // enforce Max lines 50 // 'max-lines-per-function': [' WARN ', {Max: 70}], // Forces the maximum number of statements allowed in the function block 20 "max-statements": [" WARN ", 100], // forces the maximum nesting depth of the callback function "max-nested-callbacks": ["warn", 3], // Enforces the maximum number of arguments allowed in a function definition "max-params": [" WARN ", 3], // enforces the maximum number of statements allowed in each line" max-statements per line": ["warn", {Max: 1}], // Require each call in the method chain to have a newline character "newline-per-chained-call": ["warn", {ignoreChainWithDepth: }], // disallow if as the only statement in else statement "no-lonely-if": "warn", // disallow space and TAB indent "no-mixed-spaces-and-tabs": "Warn ", // disallow multiple blank lines" no-multiple-empty-lines": "warn", // enforces a consistent space "space-before-blocks" before blocks: "Warn ", // enforces a consistent space before the opening parenthesis of function // 'space-before-function-paren': ['warn', 'never'], // enforces a consistent space in parentheses "space-in-parens": "warn", // requires a space around the operator "space-infix-ops": "Warn ", // enforces consistent Spaces before and after unary operators "space-unary-ops":" WARN ", // enforces consistent Spaces in comments // or /* / "spaced-comment": "Warn ", // enforces spacing around the switch colons "switch-colon-spacing": "warn", // enforces consistent spacing around the arrows of 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", }, overrides: [ { files: ["*.vue"], rules: {/ / write here cover the rules of the vue file "no - unused - vars" : [0],},},]};Copy the code

Install the prettier

npm i --save-dev prettier eslint-config-prettier eslint-plugin-prettier
Copy the code

The three dependencies are:

  • prettierThe core code of the: prettier plug-in
  • eslint-config-prettierPrettier: Resolves conflicts between the style specification in ESLint and the style specification in Prettier by using the prettier style specification as the criterion for prettier
  • eslint-plugin-prettierPrettier is used as the ESLint specification

Create.prettier. Js and configure the prettier rule:

Module. exports = {printWidth: 120, // set tabWidth: 2, // set the number of space for each horizontal indency of the tool useTabs: false, semi: VueIndentScriptAndStyle: true, trailingComma: 'None ', // Last object element: bracketSpacing: True, // jsxBracketSameLine: true, // JSX > Whether to start another line arrowParens: 'always', // (x) => {} Whether to have parentheses requirePragma: @prettier insertPragma: false @prettier insertPragma: false @prettier insertPragma: falseCopy the code

Add the checklint and prettier commands to package.json file

"scripts": {
    "checklint": "eslint --ext .ts,.js,.vue src --fix",
    "prettier": "prettier --write ."
},
Copy the code

After the above configuration is complete, you can run the following command to test the code check and perform the formatting:

NPM run prettier NPM run prettierCopy the code

3. Introduce Element Plus

The installation element – plus

npm install element-plus --save
Copy the code

Modify import file mian. Ts:

import { createApp } from 'vue'
import App from './App.vue';
import ElementPlus from 'element-plus' 
import 'element-plus/dist/index.css'

const app = createApp(App)
app.use(ElementPlus)
app.mount('#app')
Copy the code

This completes the introduction of Element Plus. Note that style files need to be introduced separately.

4. Import routes

Install the router4

yarn add vue-router@4
Copy the code

Create a router folder under SRC and create index.ts as follows:

import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router' const routes: RouteRecordRaw[] = [ { path: '/', name: 'Login', component: () => import('@/pages/login/ login.vue '), // note that there is a file extension here. createWebHistory(), routes, }) export default routerCopy the code

Modify import file mian. Ts:

import { createApp } from 'vue'
import App from './App.vue'
import ElementPlus from 'element-plus' 
import 'element-plus/dist/index.css'
import router from './router/index'

const app = createApp(App)
app.use(ElementPlus)
app.use(router)
app.mount('#app')
Copy the code

5. Status management Pinia

Install pinia

yarn add pinia@next
Copy the code

Modify import file mian. Ts:

import { createApp } from 'vue'
import App from './App.vue'
import ElementPlus from 'element-plus' 
import 'element-plus/dist/index.css'
import router from './router/index'
import { createPinia } from "pinia"

const app = createApp(App)
app.use(ElementPlus)
app.use(router)
app.use(createPinia())
app.mount('#app')
Copy the code

Add the store folder to the SRC folder, and then add the main.ts folder to the store folder.

Import {defineStore} from 'pinia' // 'id' is necessary to connect the store used to DevTools. Export const useMainStore = defineStore({id: 'mian', state: () => ({name: 'username'}), getters: {name: (state) => state.name,}, actions: {// Synchronize action updataUser(data: any) {this.name = data; } // Async updataUser2(data: any) {const {data} = await api.login(Account, PWD) return data}})Copy the code

Components used in:

<template> <div>{{mainstore.name}}</div> < button@click = 'handClick()'>{{' click '}}</button> </template> <script setup Lang ="ts"> import {useMainStore} from "@/store/mian" Const name = mainStore.name const name = mainstore. name Const updateName = ()=>{$patch = mainStore.$patch({name: Const handClick=()=>{mainStore.name = 'mainStore.name '} </script>Copy the code

Data persistence

Installing pinia-plugin-persist assists in data persistence (content stored in Pinia disappears when the page refreshes).

npm i pinia-plugin-persist --save
Copy the code

Pinia-plugin-persist is defined in the main.ts file

import { defineStore } from 'pinia' import piniaPluginPersist from 'pinia-plugin-persist' const useMainStore = DefineStore ({id: 'mian', state: () => ({name: 'username'}), getters: {name: (state) => state.name,}, actions: Action updataUser(data: any) {this.name = data; } // Async updataUser2(data: any) {const {data} = await api.login(Account, PWD) return data\}} persist: {// Data cache must be enabled enabled: true, // Data will be stored in sessionStorage by default, and store ID will be used as key. Strategies: [{// change the key value to my_user and select enable. Key: 'my_user', // change the storage location from sessionStorage to localStorage and select enable. LocalStorage, // All states will be cached by default. You can specify fields to persist through paths, and other fields will not be persisted. ['name'] } ] } }) useMainStore.use(useMainStore) export default useMainStoreCopy the code

6. Configure SCSS for the CSS preprocessor

Install the SCSS

    npm install -D sass-loader node-sass
Copy the code

Configure the global SCSS style file

Create a new “style” folder under SRC /assets to store the global style files. Create an index. SCSS folder.

html {
    box-sizing: border-box; 
} 
Copy the code

Modify import file mian. Ts:

import { createApp } from 'vue'
import App from './App.vue'
import ElementPlus from 'element-plus' 
import 'element-plus/dist/index.css'
import router from './router/index'
import { createPinia } from "pinia"
import './styles/index.scss'

const app = createApp(App)
app.use(ElementPlus)
app.use(router)
app.use(createPinia())
app.mount('#app')
Copy the code

7. Unified request encapsulation

Install axios

yarn add axios
Copy the code

Js file, errorcode. ts file, common.ts file, loading.

Http. ts: Used for AXIOS encapsulation

import axios from 'axios' import errorCode from '@/utils/errorCode' import { ElMessage, ElMessageBox } from 'element-plus' import { getToken } from '@/utils/auth' import { tansParams } from "@/utils/common"; import { showLoading, closeLoading } from "@/utils/loading"; axios.defaults.headers['Content-Type'] = 'application/json; Const service = axios.create({// the request in axios is configured with the baseURL option, indicating that the request is the public part of the URL baseURL: Process.env.vue_app_base_api, // timeout: 10000}) / / open loading showLoading () / / request interceptor service. The interceptors. Request. Use (config = > {the if (getToken () &&! IsToken) {config.headers['Authorization'] = 'Bearer '+ getToken() // Get request mapping params parameters if (config.method === 'get' && config.params) { let url = config.url + '? ' + tansParams(config.params); url = url.slice(0, -1); config.params = {}; config.url = url; } if (config.method === 'post' && config.params) { const requestObj = { url: config.url, data: typeof config.data === 'object' ? JSON.stringify(config.data) : config.data, } const sessionObj = cache.session.getJSON('sessionObj') if (sessionObj === undefined || sessionObj === null || sessionObj === '') { cache.session.setJSON('sessionObj', requestObj) } } return config }, Error = > {the console. The log (error) Promise. Reject (error)}) / / response blocker service. Interceptors. Response. Use (res = > {/ / closed loading, The delay processing adopted here is to combine loading request effect. Avoid multiple request loading closed and open setTimeout () = > {closeLoading ()}, 200) / / successful status code is not set, the default state const code = res. Data. The code | | 200; / / for errors information const MSG = res. Data. MSG | | errorCode [code] | | errorCode [' default '] / / binary data is returned directly if (res) request) responseType = = = "blob" | | res. Request. ResponseType = = = 'arraybuffer') {return res. Data} / / judgment status code if (code = = = 200) {return res. The data }else if(code === 401){return promise. reject(MSG)}else{ElMessage({message: MSG, type: 'error' }) return Promise.reject(msg) } }, error => { setTimeout(() => { closeLoading() }, 200) let { message } = error;  If (message == "Network Error") {message = "Network Error"; } else if (message.includes("timeout")) {message = "system interface request timed out"; } else if (message.includes("Request failed with status code")) {message = "system interface" + message.substr(message.length-3) + "exception "; } ElMessage({ message: message, type: 'error', duration: 5 * 1000 }) return Promise.reject(error) } ) export default serviceCopy the code

Errorcode. ts: used to define the returned errorCode

Export default {'400': 'Request header error ', '401':' Authentication failed, system resources cannot be accessed, please log in again ', '403': 'current operation does not have permission ', '404':' Access resource does not exist ', '500': 'Server error, '503':' service unavailable ', 'default': 'System unknown error, please report to administrator'}Copy the code

Common.ts: Used to define common methods

*/ export function tansParams(params) {let result = "" for (const propName of object.keys (params)) {const value = params[propName]; var part = encodeURIComponent(propName) + "="; if (value ! == null && typeof (value) ! == "undefined") { if (typeof value === 'object') { for (const key of Object.keys(value)) { if (value[key] ! == null && typeof (value[key]) ! == 'undefined') { let params = propName + '[' + key + ']'; var subPart = encodeURIComponent(params) + "="; result += subPart + encodeURIComponent(value[key]) + "&"; } } } else { result += part + encodeURIComponent(value) + "&"; } } } return result }Copy the code

Load. ts: used to define interface loading. V-loading is not required for each page

/** * Global loading effect: Merge multiple loading requests to avoid repeating loading requests * When showLoading is called once, the number of times is +1; Loading * When closeLoading is called once, loading * is -1. Loading */ import {ElLoading} from 'element-plus'; Let loadingRequestCount = 0; // Initialize loading let loadingInstance; ++ export showLoading = (target) => {if (loadingRequestCount === 0) {loadingInstance = Elload. service({lock: true, text: 'loading... ', BACKGROUND: 'rgba(0, 0, 0, 0.7)'}); } loadingRequestCount++} // write a function to hide loading, -- export closeLoading = () => {if (loadingRequestCount <= 0) return loadingRequestCount-- if (loadingRequestCount === 0) { loadingInstance.close(); }}Copy the code