“What will be hot and what to learn in 2022? This article is participating in the” Talk about 2022 Technology Trends “essay campaign.

πŸŽ‰ Basic template based on Vite2 + Vue3.2 + TypeScript + Pinia + Mock + sass + vantUI + Viewport adaptation + AXIos encapsulation

You are advised to view demo on a mobile phone

The foregoing

  • Vuecli project address: github.com/ynzy/vue3-h…

  • Vite-vue project address: github.com/ynzy/vite-v…

  • I wrote a template project with Vuecli a year ago when vue3 was just released. The article address is: Build mobile template scaffolding based on vue3 +TypeScript+ VUUE-Cli4.0

  • Last year, vite, a new work by The University of Utah, became a hit. I spent 8 nights writing 2-3 hours each night to reconstruct my template project during my off-hours.

  • I think it’s 2022. Vite’s a hit.

  • Used vite all say really sweet, in the end how sweet. Let’s take a look at the comparison of refactoring startup speed, hot update speed, and packaging speed

Development startup speed comparison

  • vue-cli

    • Waiting for a few seconds

  • vite-vue

    • Almost no waiting

  • Summary: Vite starts up 5 times faster than VUE-CLI!

Develop hot update speed comparison

  • vue-cli

    • You need to recompile the file

  • vite-vue

    • It took almost no time for the code changes to take effect

  • Summary: Vite is effective immediately

Production packaging speed comparison

  • vue-cliΒ 

  • vite-vueΒ 

  • Conclusion: Hardly any difference

conclusion

  • Vite in the development environment, greatly improve the development efficiency, really fragrant law!
  • Now let’s refactor the project.

Project introduction

Node Version Requirements

This example is Node.js v17.2.0

Project installation/startup

  • This project uses PNPM package manager, if not, please install PNPM first
  • Use another package manager please deletepnpm-lock.yaml
npm i -g pnpm // Install PNPM globally
pnpm install // Install dependencies
pnpm dev / / development
pnpm build / / packaging
pnpm preview  // Preview the packaged project locally
Copy the code

directory

  • √ Initialize the project with create-vue
  • √ Configure IP access items
  • √ Configure multiple environment variables
  • √ Configure the alias
  • √ Sass global style
  • Tick the Vue – the router
  • √ Pinia Status management
  • √ Use Mock data
  • √ Configure proxy across domains
  • √ Static resource usage
  • √ Axios encapsulation and interface management
  • √ Vue-request Management interface
  • √ Automatic import
  • √ Load VantUI components as required
  • √ Viewport Adaptation solution
  • √ Fit apple bottom safe distance
  • √ Dynamically set title
  • Square root configuration Jssdk
  • √ Eslint + Prettier Unified development specification
  • √ HusKY + Lint-Staged submitted validations
  • √ Project packaging optimization

βœ… initializes the project with create-vue

  • Documents: github.com/vuejs/creat…
  • To manually build a vite base template from 0 to 1, see vite-vue3-template
createVue



npm init vue@3True.js - The Progressive JavaScript Framework ➀ Project Name:... Vite - vue3 - h5 - template βœ” Add TypeScript? ... Yes stocking Add JSX Support? ... Right ➀ Add Vue RouterforSingle Page Application development? ... Yes βœ” Add Piniaforstate management? ... Yes βœ” Add Cypressfortesting? ... No βœ” Add ESLintforcode quality? ... Yes βœ” Add Prettierforcode formatting? ... YesCopy the code


  • Initializing the project contains
    • Vite
    • Vue3.2
    • Vue-router4
    • TypeScript
    • Jsx
    • Pinia
    • Eslint
    • Prettier
    • @types/node // Identify the nodeJS built-in module

Bring back to the top

βœ… Configure IP address access items

  • “Network: Use –host to Expose” appears after Vite boots
vite v23.7. dev server running at:

  > Local: http://localhost:3000/
  > Network: use `--host` to expose
Copy the code
  • The IP address is not configured, so the server cannot be started from the IP address. You need to configure the server to 0.0.0.0 in viet.config. js
export default defineConfig({
  plugins: [vue()],
  // Add the following content to the file
  server: {
    host: '0.0.0.0'}})Copy the code
  • Displayed after the restart
vite v23.7. dev server running at:

  > Local:    http://localhost:3000/
  > Network:  http:/ / 192.168.199.127:3000 /
Copy the code

Bring back to the top

βœ… Configure multiple environment variables

  • Documents: cn. Vitejs. Dev/guide/env – a…
  • In a production environment, the value of import.meta.env is converted to the corresponding value
  1. Add environment variable files, each file writes configuration, define env environment variables must be preceded by VITE_
  • .env.development
# must start with VITE_
VITE_ENV = 'development'
VITE_OUTPUT_DIR = 'dev'
Copy the code
  • .env.production
# must start with VITE_
VITE_ENV = 'production'
VITE_OUTPUT_DIR = 'dist'
Copy the code
  • .env.test
# must start with VITE_
VITE_ENV = 'test'
VITE_OUTPUT_DIR = 'test'
Copy the code
  1. Modify scripts command
  • --modeTo identify our environment
"dev": "vite --mode development"."test": "vite --mode test"."prod": "vite --mode production".Copy the code
  1. Access in the project
console.log(import.meta.env)
Copy the code
  1. Typescript smart tips
  • Modify thesrc/env.d.tsFile if one is not created
/// <reference types="vite/client" />

interface ImportMetaEnv extends Readonly<Record<string, string>> {
  readonly VITE_ENV: string; / / environment
  readonly VITE_OUTPUT_DIR: string; // Package directory
}
interface ImportMeta {
  readonly env: ImportMetaEnv;
}
Copy the code

Dynamic import environment configuration

// config/env.development.ts
// Local environment configuration
export default {
  env: 'development'.mock: true.title: 'development'.baseUrl: 'http://localhost:9018'.// Project address
  baseApi: 'https://test.xxx.com/api'.// Local API request address, note: if you use proxy, please set to '/'
  APPID: 'wx9790364d20b47d95'.APPSECRET: 'xxx'.$cdn: 'https://imgs.solui.cn'
}
Copy the code
// config/index.ts
export interface IConfig {
  env: string // Development environmentmock? :string / / the mock data
  title: string / / project titlebaseUrl? :string // Project addressbaseApi? :string // the API requests the addressAPPID? :string // Public appId is usually stored on the server sideAPPSECRET? :string // The public account appScript is stored on the server side
  $cdn: string // CDN public resource path
}
const envMap = {}
const globalModules = import.meta.globEager('./*.ts')
Object.entries(globalModules).forEach(([key, value]) = > {
  // key.match(/\.\/env\.(\S*)\.ts/)
  const name = key.replace(/\.\/env\.(.*)\.ts$/.'$1')
  envMap[name] = value
})

// Introduce different configurations depending on the environment
export const config = envMap[import.meta.env.VITE_ENV].default
console.log('Introduce different configurations depending on the environment', config)
Copy the code

Bring back to the top

βœ… Configure the alias

  • A SRC alias has been configured for project initialization
import { fileURLToPath } from 'url'

resolve: {
    alias: {
      The '@': fileURLToPath(new URL('./src'.import.meta.url))
    }
  },
Copy the code

Bring back to the top

βœ… Sass global style

  • Documents: cn. Vitejs. Dev/guide/featu…
  1. Install dependent usedart-sass, the installation speed is relatively fast, and there is a high probability that the installation will not fail
pnpm i -D sass
Copy the code
  1. Each page’s own style is written in its own.vue filescopedIt adds the concept of a domain to CSS as the name suggests.
<style lang="scss">
  /* global styles */
</style>

<style lang="scss" scoped>
  /* local styles */
</style>
Copy the code

css modules

  • Currently, tests can only be used in TSX, vue-template can be imported and used in JS,<template>Don’t know how to use it yet
  • To define a*.module.scssor*.module.cssfile
  • Used in TSX
import { defineComponent } from 'vue'
import classes from '@/styles/test.module.scss'
export default defineComponent({
  setup() {
    console.log(classes)
    return () = > {
      return <div class={`rootThe ${classes.moduleClass} `} >Test the CSS - modules</div>}}})Copy the code

Vite recognizes sASS global variables

  • Documents: cn. Vitejs. Dev/config / # CSS…
  • Vite.config.js adds the configuration
css: {
    preprocessorOptions: {
      scss: {
        additionalData: ` @import "@/styles/mixin.scss"; @import "@/styles/variables.scss"; `,}}},Copy the code

Bring back to the top

βœ… Vue – router4

  • Documents: next.router.vuejs.org/zh/installa…
  • Composition – API used: next.router.vuejs.org/zh/guide/ad…
  • The initialization project integrates vue-Router. We will only do configuration here
// router/index.ts
import { createRouter, createWebHistory } from 'vue-router'
import { routes } from './router.config'

const router = createRouter({
  history: createWebHistory(import.meta.env.BASE_URL),
  routes
})

export default router
Copy the code
// router/router.config.ts
import { RouteRecordRaw, createRouter, createWebHistory } from 'vue-router'
import Layout from '@/views/layouts/index.vue'
export const routes: Array<RouteRecordRaw> = [
  {
    path: '/'.name: 'Home'.redirect: '/home'.meta: {
      title: 'home'.keepAlive: false
    },
    component: Layout,
    children: [{path: '/home'.name: 'Home'.component: () = > import('@/views/Home.vue'),
        meta: { title: 'home'.keepAlive: false.showTab: true}}, {path: '/tsx'.name: 'Tsx'.component: () = > import('@/test/demo'),
        meta: { title: 'test TSX'.keepAlive: false.showTab: true}}, {path: '/static'.name: 'Static'.component: () = > import('@/test/testStatic.vue'),
        meta: { title: 'Testing static Resources'.keepAlive: false.showTab: true}}, {path: '/cssModel'.name: 'CssModel'.component: () = > import('@/test/testCssModel'),
        meta: { title: 'test CSS - model'.keepAlive: false.showTab: true}}, {path: '/mockAxios'.name: 'MockAxios'.component: () = > import('@/test/testMockAxios'),
        meta: { title: 'test the mock - axios'.keepAlive: false.showTab: true}}, {path: '/pinia'.name: 'Pinia'.component: () = > import('@/test/testPinia.vue'),
        meta: { title: 'test pinia'.keepAlive: false.showTab: true}}]}]Copy the code

Bring back to the top

βœ… Pinia Status management

  • The initialization project integrates pinia, and we will only do configuration here
  • Documents: pinia.vuejs.org/
  • Reference: juejin.cn/post/704919…
  • Pinia features:
    • Full typescript support;
    • Lightweight enough, compressed volume is only 1.6KB;
    • Remove mutations, only state, getters, actions (one of my favorite features);
    • Actions support synchronous and asynchronous;
    • There is no module nesting, only the concept of stores, stores can be used freely, better code segmentation;
    • There is no need to manually add stores, stores are automatically added once created;

Install dependencies

pnpm i pinia
Copy the code

Create the Store

  • Create a SRC /store directory and create index.ts under it to export store
// src/store/index.ts

import { createPinia } from 'pinia'

const store = createPinia()

export default store
Copy the code

Introduced and used in main.ts

// src/main.ts

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

const app = createApp(App)
app.use(store)
Copy the code

Define the State

  • Create a user.ts under SRC /store
//src/store/user.ts

import { defineStore } from 'pinia'
import { useAppStore } from './app'

export const useUserStore = defineStore({
  id: 'user'.state: () = > {
    return {
      name: 'Joe'.age: 18}},getters: {
    fullName: (state) = > {
      return state.name + 'company'}},actions: {
    updateState(data: any) {
      this.$state = data
      this.updateAppConfig()
    },
    updateAppConfig() {
      const appStore = useAppStore()
      appStore.setData('app-update')}}})Copy the code
//src/store/app.ts
import { defineStore } from 'pinia'

export const useAppStore = defineStore({
  id: 'app'.state: () = > {
    return {
      config: 'app'}},actions: {
    setData(data: any) {
      console.log(data)
      this.config = data
    }
  }
})
Copy the code

Get/update State

<script setup lang="ts"> import { useUserStore } from '@/store/user' import { useAppStore } from '@/store/app' import { storeToRefs } from 'pinia' import { computed } from 'vue' const userStore = useUserStore() const appStore = useAppStore() console.log(appStore.config) console.log(userStore) console.log(userStore.name) const name = computed(() => userStore.name) const { age } = storeToRefs(userStore) const updateUserState = () => { const { name, age } = userStore.$state userStore.updateState({ name: name + 1, age: The age + 1})} < / script > < template > < div > name: {{name}} < / div > < div > age: {{age}} < / div > < div > computing's name: {{userstore. fullName}}</div> <div>app config: </div> <button @click="updateUserState"> </button> </template> <style lang=" SCSS" scoped></style>Copy the code

Data persistence

  • Documents: github.com/prazdevs/pi…
  • Pinia-plugin-persistedstate can assist in data persistence.

  • Data is stored in sessionStorage by default, and the store ID is used as the key.

  • Install dependencies

pnpm i pinia-plugin-persistedstate
Copy the code
  • The plug-in
// src/store/index.ts

import { createPinia } from 'pinia'
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
const store = createPinia()
store.use(piniaPluginPersistedstate)
export default store
Copy the code
  • Simply enable Persist in the corresponding store
export const useUserStore = defineStore({
  id: 'user'.state: () = > {
    return {
      name: 'Joe'}},// Enable data caching
  persist: {
    key: 'user'.storage: sessionStorage, // Data storage location, localStorage by default
    paths: ['name'].// Array of dot notation paths for partial persistent state, indicating that no state will be persisted (default and keep the entire state)
    overwrite: true}})Copy the code

Bring back to the top

βœ… Use Mock data

  • Documents: github.com/vbenjs/vite…
  • Mock data is currently tested and is valid in both XHR and FETCH in the development environment. In the production environment, it can only be invoked using the XHR type request library, and fetch does not take effect

1. Install dependencies

pnpm i -D vite-plugin-mock mockjs @types/mockjs
Copy the code

2. Production environment-related packaging

// mock/_createProductionServer.ts
import { createProdMockServer } from 'vite-plugin-mock/es/createProdMockServer'

const modules = import.meta.globEager('./**/*.ts')

const mockModules: any[] = []
Object.keys(modules).forEach((key) = > {
  if (key.includes('/ _')) {
    return} mockModules.push(... modules[key].default) })/** * Used in a production environment. Need to manually import all modules */
export function setupProdMockServer() {
  createProdMockServer(mockModules)
}
Copy the code
// mock/_util.ts
// Interface data format used to return a unified format

import { Recordable } from 'vite-plugin-mock'

export function resultSuccess<T = Recordable> (result: T, { message = 'ok' } = {}) {
  return {
    code: 0,
    result,
    message,
    type: 'success'}}export function resultPageSuccess<T = any> (
  page: number,
  pageSize: number,
  list: T[],
  { message = 'ok' } = {}
) {
  const pageData = pagination(page, pageSize, list)

  return {
    ...resultSuccess({
      items: pageData,
      total: list.length
    }),
    message
  }
}

export function resultError(message = 'Request failed', { code = -1, result = null } = {}) {
  return {
    code,
    result,
    message,
    type: 'error'}}export function pagination<T = any> (pageNo: number, pageSize: number, array: T[]) :T[] {
  const offset = (pageNo - 1) * Number(pageSize)
  const ret =
    offset + Number(pageSize) >= array.length
      ? array.slice(offset, array.length)
      : array.slice(offset, offset + Number(pageSize))
  return ret
}

export interface requestParams {
  method: string
  body: anyheaders? : { authorization? :string }
  query: any
}

/ * * *@description This function is used to obtain tokens from the request data. Please modify * */ according to the actual situation of the project
export function getRequestToken({ headers }: requestParams) :string | undefined {
  returnheaders? .authorization }Copy the code
// mock/sys/user
import { MockMethod } from 'vite-plugin-mock'
import { resultError, resultSuccess, getRequestToken, requestParams } from '.. /_util'

export default[{url: '/basic-api/getUserInfo'.method: 'get'.response: (request: requestParams) = > {
      console.log('---- requested getUserInfo-- ')

      return resultSuccess({
        name: 'chapter three'.age: 40.sex: 'male'}}})as MockMethod[]
Copy the code

3. Modify the configuration of vite.config.ts

export default ({ mode, command }: ConfigEnv): UserConfigExport= > {
  const isBuild = command === 'build'
  return defineConfig({
    plugins: [
      viteMockServe({
        ignore: / / ^ _.// Re matches ignored files
        mockPath: 'mock'.// Set the mock. Ts file storage folder
        localEnabled: true.// Set whether to enable the local xxx.ts file, do not open it in production. Setting it to false disables the mock feature
        prodEnabled: true.// Set whether the mock function is enabled in the production environment
        watchFiles: true.// Sets whether to monitor changes in files in the folder corresponding to mockPath
        // Code injection
        injectCode: ` import { setupProdMockServer } from '.. /mock/_createProductionServer'; setupProdMockServer(); `})]})}Copy the code

Bring back to the top

βœ… Configure proxy across domains

server: {
  host: '0.0.0.0'.proxy: {
    // Short for string
    '/foo': 'http://localhost:4567'.// This is an option
    '/api': {
      target: 'http://jsonplaceholder.typicode.com'.changeOrigin: true.rewrite: (path) = > path.replace(/^\/api/.' ')},// Regular expression
    '^/fallback/.*': {
      target: 'http://jsonplaceholder.typicode.com'.changeOrigin: true.rewrite: (path) = > path.replace(/^\/fallback/.' ')}// Use a proxy instance
    // "/api": {
    // target: "http://jsonplaceholder.typicode.com",
    // changeOrigin: true,
    // configure: (proxy, options) => {
    // // proxy is an instance of 'http-proxy'
    / /},
    // },}},Copy the code

Bring back to the top

βœ… Axios encapsulation and interface management

Utils /request.js encapsulates AXIOS and developers need to make changes based on the background interface.

  • service.interceptors.request.useRequest headers can be set, such as Settingstoken
  • config.hideloadingIt is set in the interface parameters in the API folder, as described below
  • service.interceptors.response.useIn the interface can return data processing, such as 401 delete local information, login again
/ * * *@description [axios requests encapsulation] */
import store from '@/store'
import axios, { AxiosResponse, AxiosRequestConfig } from 'axios'
// import {Message, Modal} from 'view-design' // UI component library
import { Dialog, Toast } from 'vant'
import router from '@/router'
// Introduce different API addresses according to the environment
import config from '@/config'

const service = axios.create({
  baseURL: config.baseApi + '/api'.// url = base url + request url
  timeout: 5000.withCredentials: false // send cookies when cross-domain requests
  // headers: {
  // // clear cors
  // 'Cache-Control': 'no-cache',
  // Pragma: 'no-cache'
  // }
})

// Request interceptors
service.interceptors.request.use(
  (config: AxiosRequestConfig) = > {
    // Load the animation
    if (config.loading) {
      Toast.loading({
        message: 'Loading... '.forbidClick: true})}// Add request headers here, such as token
    // if (store.state.token) {
    // config.headers['Authorization'] = `Bearer ${store.state.token}`
    // }
    return config
  },
  (error: any) = > {
    Promise.reject(error)
  }
)

// Response interceptors
service.interceptors.response.use(
  async (response: AxiosResponse) => {
    // await new Promise(resovle => setTimeout(resovle, 3000))
    Toast.clear()
    const res = response.data
    if(res.code ! = =0) {
      / / token expired
      if (res.code === 401) {
        // Warning window
        return
      }
      if (res.code == 403) {
        Dialog.alert({
          title: 'warning'.message: res.msg
        }).then(() = > {})
        return
      }
      // If the background returns an error value, the corresponding error object is returned here, and the following error is received
      return Promise.reject(new Error(res.msg || 'Error'))}else {
      // Note the return value
      return response.data
    }
  },
  (error: any) = > {
    Toast.clear()
    if (error && error.response) {
      switch (error.response.status) {
        case 400:
          error.message = 'Request error (400)'
          break
        case 401:
          error.message = 'Not authorized, please log in to (401)'
          break
        case 403:
          error.message = 'Access denied (403)'
          break
        case 404:
          error.message = 'Error requesting address:${error.response.config.url}`
          break
        case 405:
          error.message = 'Requested method not allowed (405)'
          break
        case 408:
          error.message = 'Request timed out (408)'
          break
        case 500:
          error.message = 'Server internal error (500)'
          break
        case 501:
          error.message = 'Service Not realized (501)'
          break
        case 502:
          error.message = 'Network Error (502)'
          break
        case 503:
          error.message = 'Service unavailable (503)'
          break
        case 504:
          error.message = 'Network Timeout (504)'
          break
        case 505:
          error.message = 'HTTP version not supported (505)'
          break
        default:
          error.message = 'Connection error:${error.message}`}}else {
      if (error.message == 'Network Error') {
        error.message == 'Network exception, please check and try again! '
      }
      error.message = 'Failed to connect to server, please contact administrator'
    }
    Toast(error.message)
    // store.auth.clearAuth()
    store.dispatch('clearAuth')
    return Promise.reject(error)
  }
)

export default service
Copy the code

Interface management

Unify the management interface in the SRC/API folder

  • You can set up multiple module docking interfaces, such ashome.tsHere is the interface of the home pageauthController.ts
  • urlInterface address, which will be concatenated when requestedconfigUnder thebaseApi
  • methodRequest method
  • dataRequest parametersqs.stringify(params)Is the data serialization operation
  • loadingThe defaultfalse, is set totrueSome interfaces in the loading UI interaction need to be perceived by the user
import request from '@/utils/request'
export interface IResponseType<P = {}> {
  code: number
  msg: string
  data: P
}
interface IUserInfo {
  id: string
  avator: string
}
interface IError {
  code: string
}
export const fetchUserInfo = () = > {
  return request<IResponseType<IUserInfo>>({
    url: '/user/info'.method: 'get'.loading: true})}Copy the code

How to call

Because the awaitWrap type derivation is cumbersome, try catch is used to catch errors, both interface errors and business logic errors

onMounted(async() = > {try {
    let res = await fetchUserInfo()
    console.log(res)
  } catch (error) {
    console.log(error)
  }
})
Copy the code

Bring back to the top

βœ… vue-request Management interface

  • Documents: cn.attojs.org/
  • Vue-request makes it easier to manage interfaces

1. Install dependencies

pnpm i vue-request
Copy the code

2. Use AXIos to obtain data and vue-Request for management

// axios 
export const fetchUserInfo = () = > {
  return request<IResponseType<IUserInfo>>({
    url: '/user/info'.method: 'get'.loading: true})}// vue-request
const { data: res, run } = useRequest(fetchUserInfo)
// If the request is not completed, data is undefined. Use run to wait for the request to complete
await run()
console.log(res.value? .data)Copy the code

3. Use vue-request to make periodic requests

// axios
export const getTimingData = () = > {
  return request({
    url: '/getTimingData'.method: 'GET'})}// vue-request
const { data: resultData, run } = useRequest(getTimingData, {
    pollingInterval: 5000.onSuccess: (data) = > {
      console.log('onSuccess', data)
    }
  })
Copy the code

βœ… unplugin-xxx Automatic import

  • Reference: juejin.cn/post/701244…
  • Custom components automatically introduce unplugin-vue-components
  • Plugins such as vue3 automatically introduce unplugin-auto-import/vite
  • Message, notification, etc. import styles automatically import viet-plugin-style-import
  • Eslint plug-in vue – global – API

unplugin-vue-components

  • Automatic import of popular library components and custom components
  1. Install dependencies
pnpm i -D unplugin-vue-components
Copy the code
  1. Modify the vite. Config. Ts
Components({
  // Specify the location of the component, SRC /components by default
  dirs: ['src/components'].// UI library parser
  // resolvers: [ElementPlusResolver()],
  extensions: ['vue'.'tsx'].// Configuration file generation location
  dts: 'src/components.d.ts'.// Search for subdirectories
  deep: true.// Allow subdirectories to be used as namespace prefixes for components.
  directoryAsNamespace: false
  // include:[]
}),
Copy the code

unplugin-auto-import

  • Automatically import vuE3 apis
  1. Install dependencies
pnpm i -D unplugin-auto-import
Copy the code
  1. Configuration vite. Config. Ts
AutoImport({
  include: [
    /\.[tj]sx? $/.// .ts, .tsx, .js, .jsx
    /\.vue$/./\.vue\? vue/.// .vue
    /\.md$/ // .md].imports: ['vue'.'vue-router'.'@vueuse/core'].SRC /auto-import.d.ts' SRC /auto-import.d.ts'
  dts: 'src/auto-import.d.ts'.// eslint globals Docs - https://eslint.org/docs/user-guide/configuring/language-options#specifying-globals
  // Generate a global declaration file for esLint
  eslintrc: {
    enabled: true.// Default `false`
    filepath: './.eslintrc-auto-import.json'.// Default `./.eslintrc-auto-import.json`
    globalsPropValue: true // Default `true`, (true | false | 'readonly' | 'readable' | 'writable' | 'writeable')}})Copy the code
  1. Configuration eslintrc
// .eslintrc.js
module.exports = { 
  / *... * /
  extends: [
    // ...
    './.eslintrc-auto-import.json',]}Copy the code

vue-global-api

  • Use unplugin-auto-import/vite to automatically introduce hooks if not already introduced. The extends in eslintrc.js introduces vue- global-API. This plugin is supported by vue3hooks. Check for the rest. If you can’t find it, manually configure globals
  1. Install dependencies
pnpm i -D vue-global-api
Copy the code
  1. Configuration eslintrc
// .eslintrc.js
module.exports = {
  extends: [
    'vue-global-api']};Copy the code

βœ… VantUI components are loaded on demand

  • Documents: vant – contrib. Gitee. IO/vant/v3 / # / z…

1. Install dependencies

pnpm add vant@3
pnpm add vite-plugin-style-import -D
Copy the code

2. Import configurations as required

  • vite.config.ts
import vue from '@vitejs/plugin-vue'
import styleImport, { VantResolve } from 'vite-plugin-style-import'

export default {
  plugins: [
    vue(),
    styleImport({
      resolves: [VantResolve()]
    })
  ]
}
Copy the code
  • plugins/vant.ts
import { App as VM } from 'vue'
import { Button, Cell, CellGroup, Icon, Tabbar, TabbarItem, Image as VanImage } from 'vant'

const plugins = [Button, Icon, Cell, CellGroup, Tabbar, TabbarItem, VanImage]

export const vantPlugins = {
  install: function (vm: VM) {
    plugins.forEach((item) = > {
      vm.component(item.name, item)
    })
  }
}
Copy the code
  • main.ts
// Global import Import UI library vant on demand
import { vantPlugins } from './plugins/vant'
app.use(vantPlugins)
Copy the code

In 3.

  • If you use this method, you do not need to register the aboveplugins/vant.ts δΊ†
<script setup>
  import { Button } from 'vant';
</script>

<template>
  <Button />
</template>
Copy the code

4. Vant components can be used directly in JSX and TSX without component registration.

  • If you use this method, you do not need to register the aboveplugins/vant.ts δΊ†
import { Button } from 'vant'

export default {
  render() {
    return <Button />}}Copy the code

Bring back to the top

βœ… Viewport adaptation solution

  • seelib-flexibleLib-flexible is a transition option that can be abandoned because viewport units are compatible with many browsers. We recommend that you start using viewPort instead
  • Reference documents: blog.csdn.net/weixin_4642…
  • Vant official document says how to match, first according to the official document to match
  • Postcss-px-to-viewport Documentation: github.com/evrone/post…

1. Install dependencies

pnpm i -D postcss-px-to-viewport autoprefixer
Copy the code

2. Add. Postcssrc. Js

module.exports = {
  plugins: {
    -webkit-, -moz-, etc
    autoprefixer: {
      overrideBrowserslist: ['the Android 4.1'.'iOS 7.1'.'Chrome > 31'.'ff > 31'.'ie >= 8']},'postcss-px-to-viewport': {
      unitToConvert: 'px'.// The unit to convert
      viewportWidth: 375.// Width of UI design draft
      unitPrecision: 6.// The precision of the conversion, i.e. the number of decimal places
      propList: [The '*'].// Specify the unit of the CSS property to be converted. * indicates that all CSS properties are converted
      viewportUnit: 'vw'.// Specify the window unit to convert to, default vw
      fontViewportUnit: 'vw'.// Specify the window unit to convert the font to, default vw
      selectorBlackList: ['wrap'].// Specify the class name that is not converted to window units,
      minPixelValue: 1.// The default value is 1, and the conversion is not performed if the value is less than or equal to 1px
      mediaQuery: true.// Whether the media query is also converted in the CSS code, the default is false
      replace: true.// Whether to replace the attribute value directly after conversion
      exclude: [/node_modules/].// Sets the file to be ignored and the re to match the directory name
      landscape: false // Whether to handle landscape}}}Copy the code

Bring back to the top

βœ… for Apple bottom safe distance

  • The meta of index.html specifies viewport-fit=cover

  • The bottom safe distance parameter comes with vant

<! Add meta tag to head tag and set viewport-fit=cover --><meta
  name="viewport"
  content="Width =device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, viewport-fit=cover"
/><! -- Enable the top safety zone adaptation --><van-nav-bar safe-area-inset-top /><! -- Enable bottom safety zone adaptation --><van-number-keyboard safe-area-inset-bottom />
Copy the code

If you don’t use an adaptation in Vant, you can write it yourself. I wrote a generic style in SCSS

.fixIphonex {
  padding-bottom: $safe-bottom ! important;
  &::after {
    content: ' ';
    position: fixed;
    bottom: 0 ! important;
    left: 0;
    height: calc(#{$safe-bottom} + 1px);
    width: 100%;
    background: #ffffff; }}Copy the code

Bring back to the top

βœ… Dynamically sets the title

// utils/index.ts
import { config } from '@/config'

/** * Dynamically sets the browser title *@param title* /
export const setDocumentTitle = (title? : string) = > {
  document.title = title || config.title
}
Copy the code

The router/index. Ts is used

router.beforeEach((to, from, next) = > {
  setDocumentTitle(to.meta.title as string)
  next()
})
Copy the code

Bring back to the top

βœ… configuration Jssdk

  1. Installation:
yarn add weixin-js-sdk
Copy the code

The type declaration is written in model/ weixin-js-sdK.d. ts

Since Apple Browser only recognizes the route entered for the first time, you need to configure the URL used first

  • router.ts

The JSSDK configuration here is for demonstration only. Normal service logic needs to be written with the back-end

Copy the code
import { defineStore } from 'pinia'

export interface ILinkState {
	initLink: string
}

export const useAuthStore = defineStore({
	id: 'auth'.// id must be unique
	state: () = >
		({
			initLink: ' '
		} as ILinkState),
	actions: {
		setInitLink(data: any) {
			this.$state.initLink = data
		},
		setIsAuth(data) {
			this.$state.isAuth = data
		},
		setCode(code) {
			this.$state.code = code
		}
	},
	// Enable data caching
	persist: {
		key: 'auth'.storage: window.localStorage,
		// paths: ['name'],
		overwrite: true}}Copy the code

Since the window does not have an entryUrl variable, you need to declare it in a declaration file

// typings/index.d.ts
declare interface Window {
  entryUrl: any
}
Copy the code

Create the hooks function

hooks/useWxJsSdk.ts

UseWxJsSdk needs to be called once for each page that uses JSSDK, and then the other wrapped functions are used

Call:

Copy the code

Bring back to the top

βœ… Eslint + Prettier Unified development specification

  • The initialization project integrates ESLint + Prettier, so we just do configuration here
  • .eslintrc.js
/* eslint-env node */
require('@rushstack/eslint-patch/modern-module-resolution')

module.exports = {
  root: true.extends: [
    'plugin:vue/vue3-essential'.'eslint:recommended'.'@vue/eslint-config-typescript/recommended'.'@vue/eslint-config-prettier'].env: {
    'vue/setup-compiler-macros': true
  },
  rules: {
    'prettier/prettier': 'warn'.'@typescript-eslint/no-explicit-any': 'off'.'@typescript-eslint/no-unused-vars': 'off'.'vue/multi-word-component-names': 'off'}}Copy the code
  • .prettier.js
module.exports = {
  // Customize the formatting requirements
  overrides: [{files: '.prettierrc'.options: {
        parser: 'json'}}].printWidth: 100.// A line of up to 100 characters
  tabWidth: 2.// Use 4 Spaces for indentation
  semi: false.// A semicolon is required at the end of the line
  singleQuote: true.// Use single quotes instead of double quotes
  useTabs: false.// Use tabs instead of space indentations
  quoteProps: 'as-needed'.// Add quotes around object attributes only when needed
  jsxSingleQuote: false.// Use single quotes instead of double quotes in JSX
  trailingComma: 'none'.// Do not need a comma at the end
  bracketSpacing: true.// Spaces are required at the beginning and end of braces
  bracketSameLine: false.// Backangle brackets for multi-line HTML (HTML, JSX, Vue, Angular) elements require line breaks
  arrowParens: 'always'.// The arrow function, which has only one argument, also needs the parentheses avoid
  rangeStart: 0.// Each file format ranges from beginning to end
  rangeEnd: Infinity.// The range in which each file is formatted is the entire content of the file
  requirePragma: false.// There is no need to write @prettier at the beginning of the file
  insertPragma: false.// There is no need to automatically insert @prettier at the beginning of a file
  proseWrap: 'preserve'.// Use the default line folding standard always
  htmlWhitespaceSensitivity: 'css'.// Depending on the display style, HTML should be folded or not
  vueIndentScriptAndStyle: false.// (default) For.vue files, do not indent 
  endOfLine: 'lf'.// The newline character uses LF in Linux and macOS as well as git repositories
  embeddedLanguageFormatting: 'auto' // (default) allows automatic formatting of embedded code blocks
}
Copy the code

Bring back to the top

βœ… husky + Lint-staged validation submission

1. Install dependencies

pnpm i -D husky lint-staged
Copy the code

2. Add script commands

npm set-script prepare "husky install"  // Add the "prepare": "husky install" command to package.json/scripts. This command is available only on Linux /uinx operating systems
npm run prepare  // Initialize husky, pass the git hooks to husky to execute, and create the.husky folder in the root directory
npx husky add .husky/pre-commit "npx lint-staged" // pre-commit Execute NPX Lint-staged directives
Copy the code

3. Create. Lintstagedrc. Json

{
  "**/*.{js,ts,tsx,jsx,vue,scss,css}": [
    "prettier --write \"src/**/*.ts\" \"src/**/*.vue\""."eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix"]}Copy the code

Bring back to the top

βœ… project packaging optimization

  • The project packaging optimization mainly extracts the configuration from viet.config. ts into a folder dedicated to packaging configuration
  • Build folder directory
- build - vite vite environment related configuration - | - plugin plug-in configuration - | -- - | | - autocomponents automatic import components - | -- - | - | autoImport automatic import API - Compress compressed package - | - | - mock mock services - | - | - styleImport style automatic import - | | - index plug-in configuration entry - | - build. Ts - | - build configuration Proxy. ts Proxy configuration - utils tool functionCopy the code