🔨 Front-end architecture project dependency installation
-
▌ This series of articles is too long to be digested separately:
-
Front end framework: Vite2 + VUe3 +typescript+ AXIos + Vant
-
Front end framework: Vite2 + VUe3 +typescript+ AXIos + Vant
-
Front end framework: Vite2 + VUe3 +typescript+ AXIos + Vant
-
Front end framework: Vite2 + VUe3 +typescript+ AXIos + Vant
1. Introduce router and VUex
😈 ps: pay attention to
Vue3. X supports router and VUex 4.0 or later. Otherwise, exposed apis cannot be found when vue3 is used
Step 1: Install
next.router.vuejs.org/
next.vuex.vuejs.org/
Next.vuex.vuejs.org/zh/guide/ty…
npm i vue-router@next vuex@next -S
Copy the code
✔ Step 2: Open the router directory in SRC and configure the index.ts file
import { createRouter, createWebHashHistory, RouteRecordRaw } from "vue-router"; import HelloWorld from 'comps/HelloWorld.vue' const routes: Array<RouteRecordRaw> = [{path: '/',name:'home',component:HelloWorld}] // createRouter with factory function const router = createRouter({history: CreateWebHashHistory (), // hash routes, // hash routes}); export default router;Copy the code
We can see the difference: create the router using the factory function
☞ PS: If you want to use h5 routing
import { createRouter, createWebHistory , RouteRecordRaw } from "vue-router"; import HelloWorld from 'comps/HelloWorld.vue' const routes: Array<RouteRecordRaw> = [{path :' /',name:'home', Component :' HelloWorld '}] const Router = createRouter({history: createWebHistory (), routes }) export default routerCopy the code
✔ Step 3: Open the store directory in SRC and configure the index.ts file
Let’s use the example from the official website to see if it works: use useStore
// store/index.ts import { InjectionKey } from 'vue' import { createStore, useStore as baseUseStore, Store } from 'vuex' export interface State { count: number } export const key: InjectionKey<Store<State>> = Symbol() export const store = createStore<State>({ state: { count: Export function useStore () {return baseUseStore(key)}Copy the code
Simple change: SRC /store/index.ts
// store/index.ts import { InjectionKey } from 'vue' import { createStore, useStore as baseUseStore, Store } from 'vuex' export interface State { count: number } export const key: InjectionKey<Store<State>> = Symbol() export const store = createStore<State>({ state: { count: 0 }, mutations:{ setCount(state:State,count:number){ state.count = count } }, Getters :{getCount(state: state){return state.count}}}) export function useStore () { return baseUseStore(key) }Copy the code
src\components\Helloword.vue
<script setup lang="ts"> import { computed, ref } from 'vue' import { useStore } from '.. /store'; const store = useStore() const count = ref(0) const showCount = computed(()=>{ return store.getters['getCount'] }) const addBtn = ()=>{ store.commit('setCount',++count.value) } </script> <template> <h1>{{showCount}}</h1> <button </button> </template> <style scoped> </style>Copy the code
Step 4: Introduce in main.ts, as follows:
import { createApp } from 'vue'
import App from './App.vue'
import router from "./router";
import {store,key} from "./store";
createApp(App)
.use(router)
.use(store,key)
.mount('#app')
Copy the code
Ps: At this time, we opened the page and found that the page was blank. I still need to modify it a little bit
App.vue
<script setup lang="ts">
</script>
<template>
<router-view />
</template>
<style>
</style>
Copy the code
2. Introduce the UI library
2.1 introduced vant
➤ In order to configure vant for compatibility with VUe3, install the latest version (Vant 3.x).
npm i vant@next -S
Copy the code
➤ Use [ite-plugin-style-import] to introduce them on demand
The vite-plugin-style-import module needs to be installed first
npm install vite-plugin-style-import -S
Copy the code
➤ Introduce in late.config.ts
import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' import {resolve} from "path" import styleImport from 'vite-plugin-style-import'; // https://vitejs.dev/config/ export default defineConfig({ resolve: { alias: { "@": resolve(__dirname,"src"), "comps": resolve(__dirname,"src/components"), "apis": resolve(__dirname,"src/apis"), "views": resolve(__dirname,"src/views"), "utils": resolve(__dirname,"src/utils"), "routes": resolve(__dirname,"src/routes"), "styles": resolve(__dirname,"src/styles"), }, }, plugins: [vue(), styleImport({ libs: [ { libraryName: "vant", esModule: true, resolveStyle: (name) => `vant/es/${name}/style`, }, ], }), ], server: { host: Network: use --host to expose},})Copy the code
Of course, if we don’t want to install the viet-plugin-style-import module, we can also introduce it globally and not cost much
import { createApp } from 'vue' import App from './App.vue' import router from "./router"; import store from "./store"; import 'vant/lib/index.css'; CreateApp (App).use(router).use(store).use(ant).mount('# App ')Copy the code
🍕 Example: Load components related to VANT3 on demand
➤ Create the plugins folder in SRC and create vant3.ts
import { App } from '@vue/runtime-dom'; // Import all components (not recommended) // import Vant from 'Vant '; // import 'vant/lib/index.css'; Import {Button} from 'vant'; Export default function(app: app <Element>){// complete // app.use(Vant) // Use (Button)Copy the code
➤ Introduce in main. Ts
import { createApp } from 'vue'
import "utils/rem"
import "styles/index.scss"
import App from './App.vue'
import router from "./router";
import {store,key} from "./store";
import vant3 from './plugins/vant3'
createApp(App)
.use(router)
.use(store,key)
.use(vant3)
.mount('#app')
Copy the code
✔ Use in components
<van-button type="success"> </van-button>Copy the code
☞ In addition, introduceelement-plus
We may be developing some backend management projects that may need to use the Element UI library, which will need to use the Vue3 compatible Element-Plus library
The installation element – plus:
npm i element-plus -S
Copy the code
Whether to introduce on demand or globally depends on individual needs. For specific introduction methods, please go to the official website of Element-Plus for detailed introduction
3, mobile terminal adaptation
✔ Step 1: Install postCSS-pxTorem
npm install postcss-pxtorem -D
Copy the code
Step 2: Create postcss.config.js in the root directory
Module. Exports = {" plugins ": {" postcss - pxtorem" : {rootValue: 37.5, / / digital | function) said the root element font size or according to the input parameter is the root element propList font size: ['*'], // Use wildcards * to enable all attributes mediaQuery: true, // Allow conversion of px selectorBlackList in media queries: ['.norem'] // filter out.norem- class, norem conversion}}}Copy the code
Configure the rem.ts equivalence file in the new utils directory in the root directory SRC
// rem matching configuration file const baseSize = 37.5; // Set the rem function function setRem() {// Scale the current page width to 375 width, Can be modified according to their own needs, the general design draft is 750 wide (easy to get the design can be changed after). const scale = document.documentElement.clientWidth / 375; // Set the font size for the root node of the page (" math.min (scale, 2) "means that the maximum zoom ratio is 2, ) can be adjusted according to actual business requirements document. The documentElement. Style. FontSize = baseSize * math.h min (scale, 2) + "px"; } // initialize setRem(); Rem window.onresize = function () {console.log(" I did it "); setRem(); };Copy the code
➤ Introduce in main.ts
import { createApp } from 'vue'
import App from './App.vue'
import "./utils/rem"
import router from "./router";
import store from "./store";
createApp(App)
.use(router)
.use(store)
.mount('#app')
Copy the code
PostCSS
Vite automatically applies PostCSS configuration to *.vue files and imported.css files, we just need to install the necessary plug-ins and add postcss.config.js files.
✔ installation autoprefixer
npm i postcss autoprefixer -D
Copy the code
module.exports = { "plugins": { "autoprefixer": { // overrideBrowserslist: ["Android 4.1", "iOS 7.1"], // Browser compatible configuration // grid: true, // true for IE enable grid layout prefix}, "postCSs-pxtorem ": {rootValue: 37.5, / / digital | function) said the root element font size or according to the input parameter is the root element font size propList: [' * '], / / use a wildcard * mediaQuery enable all attributes: True, // allow px selectorBlackList: ['.norem'] // filter out. Norem - class, norem conversion}}}Copy the code
4. Install SCSS
Configure the CSS preprocessor sass
npm i sass sass-loader -D
Copy the code
5. Install AxiOS
We use Axios to invoke the network request
✔ installation
npm i -s axios @types/qs
Copy the code
Simple AXIos encapsulation
Unified encapsulation of data request services is conducive to solving: unified configuration of request, request, and response processing
Create.env.development in the root directory
VITE_BASE_API=/api
Copy the code
➤ Create the server in the SRC \utils folder and configure type.ts under the server
import type {AxiosRequestConfig} from "axios"; // The default object type export interface IdefaultObject {[key: string]: any; } export interface MYRequestInterceptors {requestInterceptor? : (config: AxiosRequestConfig) => AxiosRequestConfig; requestInterceptorCatch? : (error: any) => any; responseInterceptor? : (res: any) => any; rrequestInterceptorCatch? : (error: any) => any; } export interface MDJRequestConfig extends AxiosRequestConfig { interceptors? : MYRequestInterceptors; showLoading? : boolean; }Copy the code
➤ Create request. Ts under the server
import axios, { AxiosRequestConfig, AxiosResponse } from "axios"; import { IdefaultObject } from "./type"; import { whichType } from ".. /common"; const http = axios.create({ baseURL: (import.meta.env.VITE_BASE_API as string | undefined) || "/api", timeout: 10000, // The timeout is in ms, where the timeout is set to 10s}); Export const defaultErrInfo: IdefaultObject = {status: 1, error_message: 'interfaces attempt failed, code: 1} / / request intercept http.interceptors.request.use( (conflg: AxiosRequestConfig) => {if (whichType(conflg) === "object") {return tokon const token = window.localStorage.getItem("accessToken"); If (token && conflg.headers) {// Customize the token field name x-token conflg.headers[" x-token "] = token; } return conflg; } else { return defaultErrInfo; } }, err => { console.log(err); return err; }); / / response to intercept the HTTP. Interceptors. Response. Use ((conflg: AxiosResponse<any>) => { if (whichType(conflg) === "object") { return conflg; } else { return defaultErrInfo; }}, (err) => {if (err && err.response) {switch (err.response.status) {case 400: console.log(" client request syntax error, server did not understand "); break; Case 401: console.log(" Authentication error "); break; Case 403: console.log(" The server understands the request from the requesting client, but refuses to execute the request "); break; Case 404: console.log(' Request address error :${err.response.config.url} '); break; Case 405: console.log(" request mode disabled "); break; Case 408: console.log(" Request timed out "); break; Case 500: console.log(" Server internal error, unable to complete request "); break; Case 501: console.log(" The server did not support the requested function and could not complete the request "); break; Case 502: console.log(" When a server working as a gateway or proxy tried to execute a request, it received an invalid response from the remote server "); break; Case 503: console.log(" Server temporarily unable to handle client requests due to overload or system maintenance. The length of the delay can be included in the server retry-after header "); break; Case 504: console.log(" Server acting as gateway or proxy did not get requests from remote server in a timely manner "); break; Case 505: console.log(" Server does not support requested HTTP version "); break; Default: console.log(' request error :${err.message} '); } return err.response; } else {console.log(" server connection failed "); return defaultErrInfo; }}); export default http;Copy the code
WhichType method
➤ ➤ In the utils file in common.ts
Returns Boolean true/false */ export function isObject(obj: any): boolean { return obj && typeof obj === "object"; Export let whichType = (data: any) => {let dist: IdefaultObject = {"[object Array]": "array", "[object Object]": "object", "[object Number]": "number", "[object Function]": "function", "[object String]": "string", "[object Null]": "null", "[object Undefined]": "undefined", }; return dist[Object.prototype.toString.call(data)]; };Copy the code
✔ Create index. Ts in the server
import request from "./request"; import qs from 'qs' import { MYRequestConfig,IdefaultObject } from "./type"; Export const headerJSON = {" content-type ": "application/json",}; // Export const headerFormUrlencodedData = {" content-type ": "Application/x-www-form-urlencodedData; charset=UTF-8", }; export const headerFormData = { "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8", }; export const headerFileFormData = { "Content-Type": "multipart/form-data", }; interface MYRequestParams{ url:string params? :any json? :string customHeader? :IdefaultObject data? :any [key:string]:any } const http = { /** * methods: Request * @param URL Request address * @param params Request parameter * @param json Check whether data is sent in JSON format */ get(data:MYRequestParams) {const config:MYRequestConfig = { method: "get", url: data.url, headers: data.customHeader ? data.customHeader : (data.json ? headerJSON : headerFormData) }; if (data.params) config.params = data.params; if(data.data) config.data = data.data; return request(config); }, post(data:MYRequestParams) { const config:MYRequestConfig = { method: "post", url: data.url, headers: data.customHeader ? data.customHeader : (data.json ? headerJSON : headerFormData) }; if (data.params) config.params = data.params; if(data.data) config.data = (data.json || data.customHeader) ? data.data: qs.stringify(data.data) console.log('postpost',config,data); return request(config); }, put(data:MYRequestParams) { const config:MYRequestConfig = { method: "put", url: data.url, headers: data.customHeader ? data.customHeader : (data.json ? headerJSON : headerFormData) }; if (data.params) config.params = data.params; if(data.data) config.data = data.data; return request(config); }, delete(data:MYRequestParams) { const config:MYRequestConfig = { method: "delete", url: data.url, headers: data.customHeader ? data.customHeader : (data.json ? headerJSON : headerFormData) }; if (data.params) config.params = data.params; if(data.data) config.data = data.data; return request(config); }}; // Export default HTTP;Copy the code
Configuring the request broker
Add proxy to server in vite.config.ts
import { defineConfig } from "vite"; import vue from "@vitejs/plugin-vue"; import { resolve } from "path"; import styleImport from "vite-plugin-style-import"; // https://vitejs.dev/config/ export default defineConfig({ resolve: { alias: { "@": resolve(__dirname, "src"), comps: resolve(__dirname, "src/components"), apis: resolve(__dirname, "src/apis"), views: resolve(__dirname, "src/views"), utils: resolve(__dirname, "src/utils"), routes: resolve(__dirname, "src/routes"), styles: resolve(__dirname, "src/styles"), }, }, plugins: [ vue(), styleImport({ libs: [ { libraryName: "vant", esModule: true, resolveStyle: (name) => `vant/es/${name}/style`, }, ], }), ], server: { host: Network: use --host to expose port: 4000, // Proxy: {'^/ API ': {target: 'https://baidu.com', changeOrigin: true, ws: true, rewrite: (pathStr) => pathStr.replace(/^/open/, '') }, }, cors: true, }, });Copy the code
6. Eslint specification
Lint standardizes project code with ESLint, formatting code with Prettier
✔ installation eslint
npm install eslint eslint-plugin-prettier prettier eslint-plugin-vue @typescript-eslint/parser @typescript-eslint/eslint-plugin -D
Copy the code
- ESLint: A tool for identifying and reporting patterns found in ECMAScript/JavaScript code. Please check the official document for details
- Prettier: Support JavaScript, Typescript, Css, Scss, Less, JSX, Angular, Vue, GraphQL, JSON, Markdown, etc. Is currently the most popular formatting tool. Please check the official document for details
- Eslint-plugin-vue: The official ESLint plugin for vue.js. This plugin allows you to examine.vue and ESLint files for template and script, as well as.js files for vue company code.
- Eslint-plugin-prettier: Checks code for errors based on the ESLint configuration. Please check the official document for details
- Eslint-config-prettier: Closes all unnecessary rules that might conflict with [Prettier]. This allows you to use the sharable configuration you like without interfering with the style choice when Prettier is used. Note that this configuration only turns the rule off, so it only makes sense to use it with other configurations. Please check the official document for details
- @typescript-eslint/parser @typescript-eslint/eslint-plugin: These two dependencies make esLint support typescript
➤ Then configure the lint rule,.eslintrc.js
module.exports = { parser: 'vue-eslint-parser', parserOptions: { parser: '@typescript-eslint/parser', ecmaVersion: 2020, sourceType: 'module', jsxPragma: 'React', ecmaFeatures: { jsx: true, }, }, extends: [ 'plugin:vue/vue3-recommended', 'plugin:@typescript-eslint/recommended', // 'prettier/@typescript-eslint', / / eslint - config - prettier rely on more than 8.0.0 later don't need to configure this article 'plugin: prettier/it'], 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-use-before-define': [ // 'error', // { // functions: false, // classes: true, // }, // ], '@typescript-eslint/no-use-before-define': 'off', // '@typescript-eslint/no-use-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', quotes: ['error', 'single'], 'comma-dangle': ['error', 'always-multiline'], 'vue/attributes-order': 'off', 'vue/one-component-per-file': 'off', 'vue/html-closing-bracket-newline': 'off', 'vue/max-attributes-per-line': 'off', 'vue/multiline-html-element-content-newline': 'off', 'vue/singleline-html-element-content-newline': 'off', 'vue/attribute-hyphenation': 'off', 'vue/require-default-prop': 'off', 'vue/script-setup-uses-vars': 'off', 'vue/html-self-closing': [ 'error', { html: { void: 'always', normal: 'never', component: 'always', }, svg: 'always', math: 'always', }, ], 'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off', 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off', }, };Copy the code
Configure prettier. Config. js if necessary to modify the default formatting rule for prettier
Module. exports = {printWidth: 80, // length per line (defaults to 80) tabWidth: 2, // how many space per TAB (defaults to 2) useTabs: SingleQuote: false, // Use single quotation marks (default false) semi: true, // Use semicolons (default true) at the end of statements trailingComma: 'es5', // multiple lines use trailing comma (default none) bracketSpacing: true, // Object literals use Spaces between braces (default true) False, // The > in multi-line JSX is placed at the end of the last line instead of starting another line (default false) arrowParens: "Get", / / if the arrow is only one parameter function parameters with parentheses (default get) / / htmlWhitespaceSensitivity: 'ignore' htmlWhitespaceSensitivity: // ESLint: Parsing error: Unexpected token(prettier/prettier) overrides: [{files: Parsing error: Parsing error (prettier/prettier) '*.html', options: { parser: 'html' }, }, { files: '*.vue', options: { parser: 'vue' }, }, ] };Copy the code
Data mocks
✔ Configure
The latest version of Vite-plugin-Mock is vitE2 compatible
npm i mockjs vite-plugin-mock cross-env -D
Copy the code
➤ Introduce a plug-in, late.config.js
import { defineConfig } from "vite"; import vue from "@vitejs/plugin-vue"; import { resolve } from "path"; import styleImport from "vite-plugin-style-import"; import { viteMockServe } from "vite-plugin-mock"; export default defineConfig({ resolve: { alias: { "@": resolve(__dirname, "src"), comps: resolve(__dirname, "src/components"), apis: resolve(__dirname, "src/apis"), views: resolve(__dirname, "src/views"), utils: resolve(__dirname, "src/utils"), routes: resolve(__dirname, "src/routes"), styles: resolve(__dirname, "src/styles"), }, }, plugins: [ vue(), styleImport({ libs: [ { libraryName: "vant", esModule: true, resolveStyle: Name => 'vant/es/${name}/style',},],}), viteMockServe({mockPath: "mock", // ↓ Parse mock folder supportTs in root directory: // Network: use --host to expose port: 4000, // open: true, // proxy: { // "^/api": { // target: "https://baidu.com", // changeOrigin: true, // ws: true, // rewrite: PathStr = > pathStr. Replace (/ ^ / API /, ""), / /}, / /}, cors: true,}, * * * / services in the production of basic public path. * @default '/' */ base: "./", });Copy the code
➤ Set the environment variable, package.json
"scripts": {
"dev": "cross-env NODE_ENV=development vite",
"build": "vue-tsc --noEmit && vite build",
"preview": "vite preview"
}
Copy the code
✔ Create the mock file in the root directory mock/test.ts
Import {MockMethod} from 'viet-plugin-mock' import {MockMethod} from 'viet-plugin-mock' // Just for example: Export default [{url: "/api/getUsers", method: "get", response: () => { console.log('/api/getUsers----------') return { code: 0, message: "ok", data: ["tom", "jerry"], }; }, }] as MockMethod[];Copy the code
➤ Use this interface in your projects
fetch("/api/getUsers")
.then( response => {
return response.json()
})
.then(data => {
console.log('/api/getUsers',data)
})
Copy the code
You get the following return value:
{
code: 0,
data: {
0: "tom",
1: "jerry",
},
length: 2,
message: "ok"
}
Copy the code
➤ Use Axios in the component
import http from '.. /utils/server' try { let data = await http.get({url:"/getUsers"}) console.log(data,'data') } catch (error) { console.log(error,'error') }Copy the code