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