Vite-vue 3 engineering template out of the box

preface

Due to the near graduation liver design and thesis, stopped more for a period of time, but fortunately, liver finished most of the content, only the proofreading work

Vue3, Vite, TypeScript, Node, etc. A few other things have come out of the development process and are expected to be covered in a series of articles

All right, let’s get down to business…

Experience the template

Template warehouse address online preview

Two steps in place

Local introduction

Method a #
npx degit atqq/vite-vue3-template#main my-project

cd my-project
Copy the code
Method # 2
git clone https://github.com/ATQQ/vite-vue3-template.git

cd vite-vue3-template
Copy the code

Start the

# install dependencies
yarn install
Copy the code
# run
yarn dev
Copy the code

The template is introduced

Included features

  • vite
  • vue3
  • @vue/compiler-sfc
  • TypeScript
  • Vuex4.x
  • Vue-Router4.x
  • Axios
  • Provide/inject
  • polyfill.io
  • Element UI Plus
  • Sass
  • Eslint
  • Jest
  • Tencent CloudBase static page
  • Tencent CloudBase Github Action

Built-in common engineering projects used content, the following text only to some of the features of a simple introduction, part of the sample code to do the folding display

catalogue

.Heavy Exercises ─ __tests__ Heavy Exercises ─ dist# Build results├ ─ ─ the publicPublic static resources├ ─ ─ the SRC# source directory│ ├ ─ ─ apis │ ├ ─ ─ assets │ ├ ─ ─ components │ ├ ─ ─ pages │ ├ ─ ─ the router │ ├ ─ ─ store │ ├ ─ ─ @ types │ ├ ─ ─ utils │ ├ ─ ─ Shims - vue. Which s │ ├ ─ ─ env. Which s │ ├ ─ ─ main. Ts │ └ ─ ─ App. Vue ├ ─ ─ the README. Md ├ ─ ─ index. The HTML# App entry├── ├.jest.config.ts ├── LICENSE ├─ Package.json ├── Flag# Tencent Cloud CloudBase configuration file├ ─ ─ vite. Config. Ts# vite configuration file└ ─ ─ yarn. The lockCopy the code

Vite

牪 I don’t need to tell you how Ben Vite is

Simple vite. Config. ts configuration file
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import path from 'path'

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [
    vue(),
  ],
  build: {
    target: 'modules'./ / the default value
    // sourcemap: true,
  },
  server: {
    port: 8080.proxy: {
      '/api/': {
        target: 'http://localhost:3000'.changeOrigin: true.rewrite: (p) = > p.replace(/^\/api/.' '),},'/api-prod/': {
        target: 'http://localhost:3001'.changeOrigin: true.rewrite: (p) = > p.replace(/^\/api-prod/.' '),}}},resolve: {
    alias: {
      The '@': path.resolve(__dirname, './src'),
      '@components': path.resolve(__dirname, './src/components'),}}})Copy the code

@vue/compiler-sfc

This is a controversial proposal some time ago, but really sweet, further understanding

Vuex

Adopt the sub-service module scheme

The directory structure

SRC /store/ ├─ index.txt TXT TXT TXT TXT TXT TXT TXT TXT TXT TXT TXT TXT TXT TXT TXT TXT TXT TXT TXT TXTCopy the code
module1.ts
import { Module } from 'vuex'

interface State {
  count: number
}

const store: Module<State, unknown> = {
  namespaced: true.state() {
    return {
      count: 0,}},getters: {
    isEven(state) {
      return state.count % 2= = =0}},// Can only synchronize
  mutations: {
    increase(state, num = 1) {
      state.count += num
    },
    decrease(state) {
      state.count -= 1}},// Support asynchrony, you can consider introducing apis
  actions: {
    increase(context, payload) {
      context.commit('increase', payload)
      setTimeout(() = > {
        context.commit('decrease')},1000)}}},export default store

Copy the code
index.ts
import { createStore } from 'vuex'
import module1 from './modules/module1'

// Create a new store instance.
const store = createStore({
  modules: {
    m1: module1,
  },
})

export default store
Copy the code

The main ts introduced in

import store from './store'
app.use(store)
Copy the code

Call from view

import { computed } from 'vue'
import { useStore } from 'vuex'

const store = useStore()

// state
const count = computed(() = > store.state.m1.count)
// getters
const isEven = computed(() = > store.getters['m1/isEven'])
// mutations
const add = () = > store.commit('m1/increase')
// actions
const asyncAdd = () = > store.dispatch('m1/increase')
Copy the code

Vue-Router

The directory structure

SRC /router/ exercises ── ├─ exercises ── ├.src /router/ exercises ── ├.src /router/ Exercises ── ├.txtCopy the code

Interceptors are separated from page routing

Interceptor/index.ts
import { Router } from 'vue-router'

declare module 'vue-router' {
    interface RouteMeta {
        // This is optionalisAdmin? :boolean
        // Whether to log inrequireLogin? :boolean}}function registerRouteGuard(router: Router) {
  /** * global front-guard */
  router.beforeEach((to, from) = > {
    if (to.meta.requireLogin) {
      if (from.path === '/') {
        return from
      }
      return false
    }
    return true
  })

  /** * global parse guard */
  router.beforeResolve(async (to) => {
    if (to.meta.isAdmin) {
      try {
        console.log(to)
      } catch (error) {
        // if (error instanceof NotAllowedError) {
        / / / /... Handle the error and unnavigate
        // return false
        // } else {
        // // Unexpected error, unnavigates and passes the error to the global handler
        // throw error
        // }
        console.error(error)
      }
    }
  })

  /** * global rear guard */
  router.afterEach((to, from, failure) = > {
    // Change the title to monitor some basic information
    // sendToAnalytics(to.fullPath)
    if (failure) {
      console.error(failure)
    }
  })
}

export default registerRouteGuard
Copy the code
routes/index.ts
import { RouteRecordRaw } from 'vue-router'
import Home from '.. /.. /pages/home/index.vue'
import About from '.. /.. /pages/about/index.vue'
import Dynamic from '.. /.. /pages/dynamic/index.vue'

const NotFind = () = > import('.. /.. /pages/404/index.vue')
const Index = () = > import('.. /.. /pages/index/index.vue')
const Axios = () = > import('.. /.. /pages/axios/index.vue')
const Element = () = > import('.. /.. /pages/element/index.vue')
const routes: RouteRecordRaw[] = [
  { path: '/:pathMatch(.*)*'.name: 'NotFound'.component: NotFind },
  {
    path: '/'.name: 'index'.component: Index,
    children: [{path: 'home'.component: Home, name: 'home' },
      { path: 'about'.component: About, name: 'about' },
      { path: 'axios'.component: Axios, name: 'axios' },
      { path: 'element'.component: Element, name: 'element' },
      {
        path: 'dynamic/:id'.component: Dynamic,
        meta: {
          requireLogin: false.isAdmin: true,},name: 'dynamic',},],},]export default routes
Copy the code
router/index.ts
import { createRouter, createWebHistory } from 'vue-router'
import registerRouteGuard from './Interceptor'
import routes from './routes'

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

// Register the route guard
registerRouteGuard(router)

export default router
Copy the code

The main ts introduced in

import router from './router'
app.use(router)
Copy the code

Axios

A simple wrapper for Axios

ajax.ts
import axios from 'axios'

const instance = axios.create({
  baseURL: import.meta.env.VITE_APP_AXIOS_BASE_URL,
})

/** * Request to intercept */
instance.interceptors.request.use((config) = > {
  const { method, params } = config
  // Token with authentication
  const headers: any = {
    token: localStorage.getItem('token'),}// Do not cache GET requests
  if (method === 'get') {
    headers['Cache-Control'] = 'no-cache'
  }
  // Delete request parameters are placed in the body
  if (method === 'delete') {
    headers['Content-type'] = 'application/json; '
    Object.assign(config, {
      data: params,
      params: {},})}return ({
    ...config,
    headers,
  })
})

/** * Response interception */
instance.interceptors.response.use((v) = > {
  if(v.data? .code ===401) {
    localStorage.removeItem('token')
    // alert(' about to jump to login page... ', 'Login expired ')
    // setTimeout(redirectHome, 1500)
    return v.data
  }
  if (v.status === 200) {
    return v.data
  }
  // alert(v.tatustext, 'network error ')
  return Promise.reject(v)
})
export default instance
Copy the code

API directory structure

SRC/apis / ├ ─ ─ ajax. Ts ├ ─ ─ index. The ts └ ─ ─ modules └ ─ ─ public. TsCopy the code

Write the interface call method by service module and export it uniformly through apis/index.ts

export { default as publicApi } from './modules/public'
Copy the code

Inject global Axios instances. In Vue2, methods are usually attached to prototypes. In Vue3, provide/ Inject global instances or methods is recommended because CreateApp is used to create instances

main.ts

import Axios from './apis/ajax'

const app = createApp(App)

app.provide('$http', Axios)
Copy the code

View using

import { inject } from 'vue'

const $http = inject<AxiosInstance>('$http')
Copy the code

polyfill.io

Some browsers may not support the new syntax of ES in a consistent way, resulting in compatibility problems. In this case, polyfill(gasket) is needed.

Polyfill. IO is a gasket service that directly introduces gaskets on demand via CDN without affecting package volume

It works by parsing UA information of the client and determining whether spacers are required based on query parameters

Simple to use

<! DOCTYPEhtml>
<html lang="en">

<head>
  <meta charset="UTF-8" />
  <link rel="icon" href="/favicon.ico" />
  <meta name="viewport" content="Width = device - width, initial - scale = 1.0" />
  <title>Vite App</title>
  <script
    src="https://polyfill.alicdn.com/polyfill.min.js?features=es2019%2Ces2018%2Ces2017%2Ces5%2Ces6%2Ces7%2Cdefault"></script>
</head>

<body>
  <div id="app"></div>
  <script type="module" src="/src/main.ts"></script>
</body>

</html>
Copy the code

Query parameters online generation -> URl-Builder

Since the official service is deployed in a non-continent, the latency is high, and since Polyfilling-service is open source, it can be built on its own

Domestic big factories also have some mirrors:

  • Polyfill.alicdn.com/polyfill.mi…
  • Polyfill.meituan.com/polyfill.mi…

element UI Plus

The Vue3 version of the Element UI component library is a bit crappy, but barely usable

In the process of use, the style performance of Dev and Prod environment is found to be different, so the full import method is adopted

utils/elementUI.ts

import { App } from '@vue/runtime-core'

// Full import
import ElementPlus from 'element-plus'
import 'element-plus/lib/theme-chalk/index.css'
import 'dayjs/locale/zh-cn'
import locale from 'element-plus/lib/locale/lang/zh-cn'

export default function mountElementUI(app: App<Element>) {
  app.use(ElementPlus, { locale })
}
Copy the code

main.ts

import mountElementUI from './utils/elementUI'

mountElementUI(app)
Copy the code