The global environment
- V14.15.4 Node. Js version
- Vue CLI version @vue/ CLI 4.5.13
- TypeScript Version 4.2.3
Project creation
- Execute the command
vue create project_name
Copy the code
2. Select the initial configuration itemSelect the vue3.x version
Overall project structure
Application // Application // Application // Application // Application // Application // Application // Application // Application // Application // .eslintrc.js ├─ ├─ download.config.js ├─ Package-Lock. json ├─ Public │ ├─ FavIcon ├─ SRC │ ├─ API │ ├─ ├─ Login. Currently only encapsulates the logged out │ │ └ ─ the manage. The ts / / packaging request method │ ├ ─ App. Vue │ ├ ─ assets │ │ ├ ─ SCSS / / global CSS style │ | │ └ ─ index. The vue │ │ └ ─ logo. The PNG │ ├ ─ components │ │ └ ─ NoFind. Vue/definition / 404 page │ ├ ─ main. Ts / / entrance │ ├ ─ the router configuration / / routing │ │ └ ─ but ts │ ├ ─ │ ├─ ├─ ├.sci-vue. │ ├─ sci-vue. │ ├─ sci-vue. │ ├─ sci-vue Storage. Ts / / packaging sessionStorage and localStorage, cookie store delete │ └ ─ views │ ├ ─ Home/page/content │ │ └ ─ index. The vue │ ├ ─ Layout / / project │ ├─ ├─ ├─ ├─ vue │ ├─ vue │ ├─ vue │ ├─ vue │ ├─ vue │ ├─ vueCopy the code
UI library installation and introduction
Install YARN add ant-design-vue The official website of ant-design-vue provides two import modes: on-demand load or global import. In this case, global import is used. After installation, add the following code to the main.ts file
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
// 引入ant-design-vue
import Antd from 'ant-design-vue'
import "ant-design-vue/dist/antd.css";
createApp(App).use(store).use(Antd).use(router).mount('#app')
Copy the code
Axios requests encapsulation
The installation
yarn add axios
Copy the code
Request to intercept
Note: The storage of tokens and vuEX storage tokens are described in the corresponding section. The request.ts file is just used to create the utils folder under the project SRC folder and create the request.ts file as follows:
import axios,{AxiosResponse, AxiosRequestConfig} from "axios"; Import {notification} from 'ant-design-vue' import store from '@/store' // obtain token import {localCache} from '@/utils/storage' import {logout} from "@/ API /login"; // Initialize const instance = axios.create({baseURL: process.env.vue_app_API_base_URL, timeout: 120 * 1000, withCredentials: true}) / / request error const err = (error: {message: string | string []. response: { data: any; status: number; }; }) => {if (error.message.includes('timeout')) {notification.error({message: 'system prompt ', description:' request timeout', duration: 4}); } if (error.response) { const data = error.response.data; const token = localCache.getCache('token') if (error.response.status == 403) { notification.error({ message: 'System warning ', description:' Failed to request resource ', duration: 4}); } if (error.response.status === 401 && ! (data.result && data.result.islogin)) {notification.error({message: 'system prompt ', description:' no access permission ', duration: 4}); If (token) {logout(); logout(); } } } return Promise.reject(error); }; / / request the instance. The interceptors. Request. Use ((config: AxiosRequestConfig) => {// Obtain system token const token:string = store.state.token; If (token) {config.headers[' x-access-token '] = token // Let each request carry a user-defined token. Modify it according to the actual situation} // Set the default parameter of the GET interface to carry a timestamp (config.method == 'get') { config.params = { _t: new Date().getTime(), ... config.params } } return config; }, err) / / interception successful request the instance. The interceptors. Response. Use ((response: AxiosResponse) = > {const config: AxiosRequestConfig = response.config || ''; const code = Number(response.data.code); / / code according to the client agreement interface before and after successful state changes the if (code = = 200 | | code = = 0) {if (config) {the console. The log (' request is successful ')} return response. The data; } else { const errorCode = [402, 403, 500]; If (errorcode.includes (response.data.code)) {notification.error({message: 'system prompt ', description:' no permission ', duration: 4}); setTimeout(() => { window.location.reload(); }, 500) } } },err) export default instance;Copy the code
VUE_APP_API_BASE_URL is used to initialize baseURL from process.env.vue_app_API_base_URL. Where. Env all environments are loaded,.env. test test environment is loaded,.env.development development environment is loaded,.env. production environment is loaded. The files are created in the root directory of the project at the same level as vue.config.js. For details about configuration items, see Vue CLI mode and Environment Variables
encapsulation
Create a folder API folder under the project SRC folder and create a manage.ts file with the following contents:
Import axios from '@/utils/request' /** * @desc Post request * @param URL request path * @param parameter Request parameter ** / export function postAction(url: string, parameter: any) { return axios({ url: url, method: 'post', data: Parameter})} / HTTP request * * * * @ desc @ param url request path * @ param parameter request parameters * @ param method = {post | put} * * / export function httpAction(url: string, parameter: any, method: any) { return axios({ url: url, method: method, data: /** * @desc put request * @param URL request path * @param parameter Request parameter ** / export function putAction(url: string, parameter: any) { return axios({ url: url, method: 'put', data: /** * @desc get request * @param url request * @param parameter Request ** / export function getAction(url: string, parameter: any) { return axios({ url: url, method: 'get', params: } /** * @desc delete request * @param url request * @param parameter request ** / export function deleteAction(url: string, parameter: any) { return axios({ url: url, method: 'delete', params: parameter }) }Copy the code
Vue.config.js configures the proxy
- Created in the project root directory
vue.config.js
Configure the server request broker
module.exports = {
devServer: {
port: 3082,
proxy: {
"/pr-api": {
target: "http://localhost:3085",
ws: false,
changeOrigin: true,
},
},
},
lintOnSave: undefined,
};
Copy the code
Unified management of project request apis
- In the project
src
Create folder under folderapi
Folder and createajaxUrl.config.ts
File for unified management of the project request API. The sample is as follows
const Login = {
login: '/sys/login'
}
export {
Login
}
Copy the code
Log in and log out
Exit the login public method
Create the folder API folder under the project SRC folder and create the login.ts file to manage logging out.
import { createVNode } from "vue"; import { ExclamationCircleOutlined } from '@ant-design/icons-vue'; // import { useRouter } from "vue-router"; import { postAction } from "@/api/manage"; import {cookies, localCache} from '@/utils/storage' import { Modal, Message} from 'ant-design-vue' /* exit */ export function logout() {// const router = useRouter(); Modal.confirm({title: 'Log out? 'icon: createVNode (ExclamationCircleOutlined), okText:' confirmed ', cancelText: 'cancel' onOk () {postAction ("/sys/logout ", {}), then ((res: Any) => {if (res.success) {// Clear all data cookies stored in the browser. RemoveCookie ('vuex') localCache.clearCache() // A message indicating successful exit is displayed message.success(res.message); // router.push({name: "Login"}); // Refresh the entire browser setTimeout(() => {window.location.reload(); }}}, 100)); }, onCancel() {message.info(' cancel logout '); }, class: 'test', }); }Copy the code
Cache storage read encapsulation
Note: For jS-cookie installation see section 3 of the Vuex-PersistedState usage steps. Create the utils folder under the project SRC folder and create the storage.ts file as follows:
Import Cookies from 'js-cookie' /* * localStorage encapsulation */ const localCache = {// set setCache(key: string, value: Any) {window. LocalStorage. SetItem (key, JSON stringify (value))}, / / get getCache (key: string) { const value = window.localStorage.getItem(key) if (value) { return JSON.parse(value) } }, // Delete a certain deleteCache(key: String) {window. LocalStorage. RemoveItem (key)}, / / to empty all clearCache () {window. LocalStorage. The clear ()}, } /** * sessionStorage */ const sessionStorage = {// set(key: string, value: Any) {window. The sessionStorage. SetItem (key, JSON stringify (value))}, / / remove the data get < T > (key: string) { const value = window.sessionStorage.getItem(key) if (value && value ! = "undefined" && value ! Parse (value) {return JSON. Parse (value)} return null}, string) { window.sessionStorage.removeItem(key) } } const cookies = { getCookie(key:string) { return Cookies.get(key) }, setCookie(key: string, value:any) { Cookies.set(key, value) }, removeCookie(key:string) { Cookies.remove(key); return } } export { sessionStorage, localCache, cookies }Copy the code
The login
Note: Routing and VUEX storage tokens are described in the corresponding section.
<template> < A-form layout="horizontal" :model="formState"> < A-form-item label=" account "> < A-input V-model :value="formState. Username "placeholder=" /> </a-form-item> < A-form-item label=" password "> < A-input V-model :value="formState. Password "type="password" placeholder=" placeholder" /> </a-form-item> <a-button type="primary" @click="onSubmit"> </a-button> </a-form> </template> <script lang="ts"> import {defineComponent, Reactive, UnwrapRef, toRaw } from "vue"; import { useStore } from "vuex"; import { useRouter } from "vue-router"; import { message } from "ant-design-vue"; Import {postAction} from "@/ API /manage"; Import {Login} from "@/ API/ajaxurl.config "; import {Login} from "@/ API/ajaxurl.config "; FormState {username: string; password: string | number; } export default defineComponent({ setup() { const formState: UnwrapRef<FormState> = reactive({ username: "", password: ""}); // Vuex const store = useStore(); // Vue Router const router = useRouter(); // const onSubmit = () => {let params = toRaw(formState); postAction(Login.login, params).then((res: Any) => {if (res.success) {// Set the token data to Vuex store.mit ("setToken", res.result.token); // Set user information to Vuex store.mit ("setUserInfo", res.result.userinfo); Router.push ({name: "Layout"}); } else { message.error(res.message); }}); }; return { formState, onSubmit, }; }}); </script>Copy the code
exit
<template> <div> <a-button type="primary" @click="outLogin"> </a-button> </div> </template> <script lang="ts"> import { defineComponent } from "vue"; import { logout } from "@/api/login"; Export default defineComponent({setup() {// logout const outLogin = () => {logout(); }; return { outLogin, }; }}); </script>Copy the code
Layout Layout
Layout Layout
- Modify the
App.vue
file
<template> <! </template> <style lang=" SCSS "></style>Copy the code
- Route jump sum
layout
Layout content rendering
<template> <a-layout> <! <a-layout-header class="header"> <div class="logo" /> < A-menu theme="dark" mode="horizontal" v-model:selectedKeys="selectedKeys1" :style="{ lineHeight: '64px' }" > <a-menu-item key="1">nav 1</a-menu-item> </a-menu> </a-layout-header> <a-layout> <! <a-layout-sider width="200" style="background: #fff"> <a-menu mode="inline" v-model:selectedKeys="selectedKeys2" v-model:openKeys="openKeys" :style="{ height: '100%', borderRight: 0}" > <a-sub-menu key="sub1"> <template #title> <span> < user-menu-item </span> </template key="/home"> <router-link :to="{ path: 'home' }"> <span>home</span> </router-link> </a-menu-item> <a-menu-item key="2">option2</a-menu-item> </a-sub-menu> </a-menu> </a-layout-sider> <! <a-layout style="padding: 0 24px 24px"> "> < p style=" margin-bottom: 25px; margin-bottom: 25px; 16px 0"> <a-breadcrumb-item>Home</a-breadcrumb-item> </a-breadcrumb> <a-layout-content :style="{ background: '#fff', padding: '24px', margin: 0, minHeight: '280px', }" > <! <router-view /> </a-layout-content> </a-layout> </a-layout> </a-layout> </template> <script lang="ts"> import { UserOutlined } from "@ant-design/icons-vue"; import { defineComponent, ref } from "vue"; export default defineComponent({ components: { UserOutlined, }, setup() { return { selectedKeys1: ref<string[]>(["2"]), selectedKeys2: ref<string[]>(["1"]), collapsed: ref<boolean>(false), openKeys: ref<string[]>(["sub1"]), }; }}); </script> <style> #components-layout-demo-top-side-2 .logo { float: left; width: 120px; height: 31px; margin: 16px 24px 16px 0; Background: Rgba (255, 255, 255, 0.3); } .ant-row-rtl #components-layout-demo-top-side-2 .logo { float: right; margin: 16px 0 16px 24px; } .site-layout-background { background: #fff; } </style>Copy the code
VueX shares data
Because Vuex needs to store global tokens and user information, and because Vuex runs in memory and stores data in memory, when a user refreshes a page, Memory data is reinitialized and stored persistently through sessionStorage/localStorage/cookie. Here we use two plug-ins to persist Vuex data. Vuex data persistence enabled by Vuex-PersistedState and JS-cookie is removed. Note: Ts projects using JS-cookie need to install “jS-cookie “: “^3.0.1”, and “@types/ Js-cookie “: “^2.2.7”, two dependency packages
Vuex-persistedstate Procedure
1. Install the plug-in
yarn add vuex-persistedstate -S
Copy the code
2. Use plug-ins
Export default createStore({getters, plugins: [createPersistedState()]})Copy the code
3. Install the js – cookies
Yarn add [email protected] @ types/[email protected]Copy the code
The specific use
import { createStore } from 'vuex' import createPersistedState from 'vuex-persistedstate' import Cookies from 'js-cookie' import {localCache} from '@/utils/storage' export interface UserState {count: number token: string, userInfo: Any,} // Vuex-PersistedState provides a reducer function that allows you to customize storage keys or use paths. Paths are recommended. ModuleName. XXXX, PersistData const PERSIST_PATHS = ['token', Return Vuex export default createStore({state:<UserState> {count: 0, token: '', userInfo: {}}, mutations: Count ++}, // setToken setToken(state, token) {state.token = token; SetCache ('token', token)}, // Set user information setUserInfo(state, userInfo) {state.userinfo = userInfo; Localcache. setCache('userInfo', userInfo)}}, actions: {}, modules: {}, getters: {}, Vuex data persistence plugins: [createPersistedState({/* storage default storage to localStorage storage to sessionStorage configuration storage: window.sessionStorage */ // reducer: Var var var var var var var var var var var var var var var var var var var var Paths: PERSIST_PATHS, storage: {getItem: (key) => Cookies.get(key), // Please see https://github.com/js-cookie/js-cookie#json, on how to handle JSON. setItem: (key, value) => Cookies.set(key, value, { expires: 3, secure: true }), removeItem: (key) => Cookies.remove(key), }, })] })Copy the code
VueRouter use
Nested routines are laid out by Layout
import { createRouter, createWebHistory, RouteRecordRaw} from 'vue-router' const Login = () => import('@/views/Login/index.vue') const routes: Array<RouteRecordRaw> = [ { path: '/', name: 'Login', component: Login, }, { path: '/layout', name: 'Layout', Component: () => import("@/views/Layout/index.vue"), // '/home', name: 'Home', component: () => import('@/views/Home/index.vue') }, { path: '/about', name: 'About', component: () => import('@/views/About.vue') } ] }, {/ / match all path used vue2 * vue3 / : pathMatch (. *) * or / : pathMatch (. *) or / : catchAll (. *) path: '/ : pathMatch (. *) *', name: "404", Component: ()=> import("@/ Components/nofind.vue ")}] // Initialize router const router = createRouter({history: createWebHistory(process.env.BASE_URL), routes }) export default routerCopy the code
Routing to intercept
import { createRouter, createWebHistory, RouteRecordRaw} from 'vue-router' import {localCache} from '@/utils/storage' // route idle load const Login = () => import('@/views/Login/index.vue') const routes: Array<RouteRecordRaw> = [] // initialize router const router = createRouter({history: CreateWebHistory (process.env.base_URL), routes}) // Set router guard route. beforeEach((to, from, next) => {// to indicates the path to be accessed, If (to.path == '/') {// The current access is the login page, Next ()} else {// Get the user's local token, jump to the login page if the token does not exist const token = localCache.getCache('token') if (! Token) {next('/')} else {// Next ()}}}) export default routerCopy the code
The overall code
import { createRouter, createWebHistory, RouteRecordRaw} from 'vue-router' import {localCache} from '@/utils/storage' // route idle load const Login = () => import('@/views/Login/index.vue') const routes: Array<RouteRecordRaw> = [ { path: '/', name: 'Login', component: Login, }, { path: '/layout', name: 'Layout', component: Children: [{path: '/home', name: 'home', component: {path: '/home', name:' home', component: {path: '/home', name: 'home', component: () => import('@/views/Home/index.vue') }, { path: '/about', name: 'About', component: () => import('@/views/About.vue') } ] }, {/ / match all path used vue2 * vue3 / : pathMatch (. *) * or / : pathMatch (. *) or / : catchAll (. *) path: '/ : pathMatch (. *) *', name: "404", Component: ()=> import("@/ Components/nofind.vue ")}] // Initialize router const router = createRouter({history: CreateWebHistory (process.env.base_URL), routes}) // Set router guard route. beforeEach((to, from, next) => {// to indicates the path to be accessed, If (to.path == '/') {// The current access is the login page, Next ()} else {// Get the user's local token, jump to the login page if the token does not exist const token = localCache.getCache('token') if (! Token) {next('/')} else {// Next ()}}}) export default routerCopy the code
Configure global Styles
Combined with SCSS and vue.config.js for global style configuration, SCSS preprocessing has been installed in the initial configuration items of project creation. Create the SCSS folder under assets in the SRC folder and create the index. SCSS file. Configure SCSS by referring to project vue.config Configuration
Project vue.config configuration
Configure it in vue.config.js
module.exports = { configureWebpack: (config) => {// Production environment cancel console.log if (process.env.node_env === "production") { config.optimization.minimizer[0].options.terserOptions.compress.drop_console = true; } else {// development environment}}, // Configure SCSS CSS: {loaderOptions: {SCSS: {prependData: '@import "@/assets/ SCSS /index.scss"; DevServer: {port: 3082, proxy: {"/pr-api": {target: "http://localhost:3085", ws: false, changeOrigin: true, }, }, }, lintOnSave: undefined, };Copy the code