Project Initialization with pnpm
Some advantages: approximately 2x faster than similar tools, saves disk space...
Installation:
npm install -g pnpm
Creating a project:
pnpm create vue
ESLint & Prettier Configuration
Environment Synchronization:
- Install the ESLint plugin and enable auto-fix on save
- Disable the Prettier plugin and turn off auto-formatting on save
// ESLint plugin + VSCode configuration for auto-formatting and fixing
"editor.codeActionsOnSave": {
"source.fixAll": true
},
"editor.formatOnSave": false,
.eslintrc.cjs Configuration File
- Prettier style configuration: https://prettier.io
- Single quotes
- No semicolons
- Maximum 80 characters per line
- No trailing commas in objects/arrays
- Line endings not restricted (Windows/Mac inconsistency)
- Vue component names should consist of multiple words (ignoring index.vue)
- Props destructuring disabled
rules: {
'prettier/prettier': [
'warn',
{
singleQuote: true, // Single quotes
semi: false, // No semicolons
printWidth: 80, // Maximum 80 characters per line
trailingComma: 'none', // No trailing commas in objects/arrays
endOfLine: 'auto' // Line endings not restricted (Windows/Mac inconsistency)
}
],
'vue/multi-word-component-names': [
'warn',
{
ignores: ['index'] // Vue component names should be multiple words (ignores index.vue)
}
],
'vue/no-setup-props-destructure': ['off'], // Disable props destructuring validation
// Added undefined variable error support, create-vue@3.6.3 has this disabled, adding for next chapter demonstration.
'no-undef': 'error'
}
Husky-Based Code Checking Workflow
Husky is a Git hooks tool that executes specific commands at particular moments in Git operations.
Husky Configuration
- Initialize Git:
git init - Initialize Husky tool configuration
https://typicode.github.io/husky/
The init command simplifies Husky setup in your project. It creates a pre-commit script in .husky/ and updates the prepare script in package.. You can then modify it according to your workflow.
pnpm dlx husky-init && pnpm install
In v7, pnpm dlx is the same as pnpx. It downloads a package and executes it.
When you need to create an application, pnpm create is shorthand for pnpm dlx.
For example, pnpm create react-app my-app will download the create-react-app package and run it to bootstrap a React application. This is equivalent to running pnpm dlx create-react-app my-app.
There's also pnpm exec, which doesn't download packages but only runs a package already in node_modules/.bin.
Modify .husky/pre-commit file
pnpm lint
Issue: By default, it performs a full check, which can be time-consuming and may reveal historical issues.
lint-staged Configuration
Installation:
pnpm i lint-staged -D
Configure package.:
{
// ... omitted ...
"lint-staged": {
"*.{js,ts,vue}": [
"eslint --fix"
]
}
}
{
"scripts": {
// ... omitted ...
"lint-staged": "lint-staged"
}
}
Modify .husky/pre-commit file:
pnpm lint-staged
Project Directory Structure Adjustment
The default generated directory structure doesn't meet our development requirements, so we need to make some custom modifications. The main tasks are:
- Delete default initialization files
- Modify remaining code content
- Add new directory structures as needed
- Copy initialization resource files and install preprocessor plugins
- Delete files
- Modify content
src/router/index.js
import { createRouter, createWebHistory } from 'vue-router'
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: []
})
export default router
src/App.vue
<script setup></script>
<template>
<div>
<router-view></router-view>
</div>
</template>
<style scoped></style>
src/main.js
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'
import router from './router'
const app = createApp(App)
app.use(createPinia())
app.use(router)
app.mount('#app')
- Add required directories: api, utils
- Copy global styles and image files needed for the project to the assets folder, and import global styles in main.js
import '@/assets/main.scss'
Install sass dependency
pnpm add sass -D
VueRouter4 Implementation
Basic Code Analysis
import { createRouter, createWebHistory } from 'vue-router'
// createRouter creates a routing instance, equivalent to new VueRouter()
// 1. History mode: createWebHistory() http://xxx/user
// 2. Hash mode: createWebHashHistory() http://xxx/#/user
// import.meta.env.BASE_URL in Vite is an environment variable:
// Environment variables and modes | Vite Official Chinese Documentation
// https://vitejs.dev/guide/build.html#public-base-path
// If you deploy to a domain path like: http://xxx/my-path/user
// Add configuration to vite.config.ts: base: my-path, the route will then have the my-path prefix
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: []
})
export default router
Introducing Element UI Component Library
Official documentation: https://element-plus.org/zh-CN/
Installation:
$ pnpm add element-plus
Automatic On-Demand Import:
Install plugins:
pnpm add -D unplugin-vue-components unplugin-auto-import
Add the following code to your Vite or Webpack configuration file:
...
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
...
AutoImport({
resolvers: [ElementPlusResolver()]
}),
Components({
resolvers: [ElementPlusResolver()]
})
]
})
Direct Usage:
<template>
<div>
<el-button type="primary">Primary</el-button>
<el-button type="success">Success</el-button>
<el-button type="info">Info</el-button>
<el-button type="warning">Warning</el-button>
<el-button type="danger">Danger</el-button>
...
</div>
</template>
Easter egg: Files in the components directory are automatically registered by default!
Pinia - Building User Store and Persistence
Official documentation: https://prazdevs.github.io/pinia-plugin-persistedstate/zh/
- Install the pinia-plugin-persistedstate plugin
pnpm add pinia-plugin-persistedstate -D
- Use in main.js
import persist from 'pinia-plugin-persistedstate'
...
app.use(createPinia().use(persist))
- Configure stores/user.js
import { defineStore } from 'pinia'
import { ref } from 'vue'
// User module
export const useUserStore = defineStore(
'big-user',
() => {
const token = ref('') // Define token
const setToken = (t) => (token.value = t) // Set token
return { token, setToken }
},
{
persist: true // Persistence
}
)
Pinia - Unified Store Management
Pinia independent maintenance
- Current situation: Initialization code is in main.js, store code is in stores, code is scattered and functions are not unified
- Optimization: Unified maintenance by stores, complete pinia initialization in stores/index.js, and deliver to main.js for use
Store unified export
- Current situation: Using a store requires importing from different paths like
import { useUserStore } from './stores/user.js' - Optimization: Unified export from stores/index.js, unified import path
'./stores', and store maintenance in stores/modules
Data Interaction - Request Tool Design
1. Creating an axios instance
We'll use axios to request backend APIs. Typically, axios is configured (e.g., base URL, etc.).
In general project development, axios is often encapsulated as a separate module for easy use.
Install axios:
pnpm add axios
Create utils/request.js to encapsulate the axios module
Use axios.create to create a custom axios instance
http://www.axios-js.com/zh-cn/docs/#axios-create-config
import axios from 'axios'
const baseURL = 'http://big-event-vue-api-t.itheima.net'
const instance = axios.create({
// TODO 1. Base URL, timeout
})
instance.interceptors.request.use(
(config) => {
// TODO 2. Carry token
return config
},
(err) => Promise.reject(err)
)
instance.interceptors.response.use(
(res) => {
// TODO 3. Handle business failure
// TODO 4. Extract core response data
return res
},
(err) => {
// TODO 5. Handle 401 error
return Promise.reject(err)
}
)
export default instance
2. Complete axios basic configuration
import { useUserStore } from '@/stores/user'
import axios from 'axios'
import router from '@/router'
import { ElMessage } from 'element-plus'
const baseURL = 'http://big-event-vue-api-t.itheima.net'
const instance = axios.create({
baseURL,
timeout: 100000
})
instance.interceptors.request.use(
(config) => {
const userStore = useUserStore()
if (userStore.token) {
config.headers.Authorization = userStore.token
}
return config
},
(err) => Promise.reject(err)
)
instance.interceptors.response.use(
(res) => {
if (res.data.code === 0) {
return res
}
ElMessage({ message: res.data.message || 'Service exception', type: 'error' })
return Promise.reject(res.data)
},
(err) => {
ElMessage({ message: err.response.data.message || 'Service exception', type: 'error' })
console.log(err)
if (err.response?.status === 401) {
router.push('/login')
}
return Promise.reject(err)
}
)
export default instance
export { baseURL }