1、項目構建
1.1、使用vite初始化項目
1.1.1、創(chuàng)建項目文件夾
mkdir my-vue3
1.1.2、進入項目文件夾
cd my-vue3
1.1.3、初始化項目
npm init vite@latest
1.1.4、輸入項目名稱
1.1.5、選擇vue
1.1.6、選擇TypeScript
1.1.7、查看當前源(非必要)
npm config get registry
1.1.8、更換為國內鏡像(非必要)
npm config set registry=http://registry.npm.taobao.org/
1.1.9、進入項目
cd vue3-ts-scss
1.1.10、安裝依賴
npm install
1.1.11、運行項目
npm run dev
1.1.12、修改部分報錯信息
找不到模塊“@vitejs/plugin-vue”。你的意思是要將 “moduleResolution” 選項設置為 “node”,還是要將別名添加到 “paths” 選項中?
在根目錄tsconfig.node.json文件中修改
"moduleResolution": "node"
在根目錄tsconfig.json文件中修改
"moduleResolution": "node"
1.2、項目基礎配置
1.2.1、引入 @types/node 包
types/node 是 TypeScript 官方提供的一個類型聲明文件包,它包含了 Node.js 核心模塊的類型聲明。在使用 TypeScript 編寫 Node.js 應用程序時,為了獲得更好的類型檢查和代碼提示,你可以通過安裝 @types/node 包來獲取 Node.js 核心模塊的類型聲明。
npm install @types/node --save-dev
1.2.2、配置別名
在根目錄vite.config.ts文件中添加
import { resolve } from 'path';
export default defineConfig({
resolve: {
//別名配置,引用src路徑下的東西可以通過@如:import Layout from '@/layout/index.vue'
alias: [
{
find: '@',
replacement: resolve(__dirname, 'src'),
},
],
},
});
1.2.3、指定 Vite 開發(fā)服務器監(jiān)聽的主機地址
在根目錄vite.config.ts文件中添加
export default defineConfig({
base: './',
server: {
host: '0.0.0.0',
port: 5000,
open: true,
},
})
1.2.4、配置ts文件采用@方式導入
在根目錄tsconfig.json文件中添加配置
{
"compilerOptions": {
"suppressImplicitAnyIndexErrors": true, //允許字符串用作下標
"ignoreDeprecations": "5.0", //高版本上句報錯,此句解決。如此句報錯可注釋掉
"baseUrl": ".",
"paths": {
"@/*": [
"src/*"
]
}
},
"exclude": ["node_modules"] // // ts排除的文件
}
在根目錄tsconfig.json文件中修改配置
{
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"]
}
1.2.5、使用全局變量
在根目錄中新建.env.development文件
# 頁面標題
VITE_APP_TITLE = 配置VUE3+TS+scss+axios+pinia
# 開發(fā)環(huán)境配置
VITE_APP_ENV = 'development'
# 開發(fā)環(huán)境
VITE_APP_BASE_API = '/dev-api'
# 應用訪問路徑 例如使用前綴 /admin/
VITE_APP_CONTEXT_PATH = '/'
在根目錄中新建.env.production文件
# 頁面標題
VITE_APP_TITLE = 配置VUE3+TS+scss+axios+pinia
# 生產環(huán)境配置
VITE_APP_ENV = 'production'
# 生產環(huán)境
VITE_APP_BASE_API = '/prod-api'
# 是否在打包時開啟壓縮,支持 gzip 和 brotli
VITE_BUILD_COMPRESS = gzip
# 應用訪問路徑 例如使用前綴 /admin/
VITE_APP_CONTEXT_PATH = '/'
1.2.6、配置按需引入文件
安裝babel-plugin-import
npm install babel-plugin-import --save-dev
1.3、添加代碼檢測
添加 ESLint 和 Prettier 可以幫助團隊保持一致的編碼風格,減少代碼中的錯誤和問題,并提高代碼的可讀性和可維護性,從而提高開發(fā)效率和團隊協(xié)作效果。
1.3.1、安裝eslint
npm install --save-dev eslint eslint-plugin-vue
1.3.2、安裝vite-plugin-eslint(eslint結合vite使用)
npm add -D vite-plugin-eslint
1.3.3、安裝eslint-parser
npm add -D @babel/core
npm add -D @babel/eslint-parser
1.3.4、配置vite.config.ts
import eslintPlugin from 'vite-plugin-eslint';
plugins: [
vue(),
eslintPlugin({
include: ['src/**/*.ts', 'src/**/*.vue', 'src/*.ts', 'src/*.vue'],
}),
]
1.3.5、VsCode安裝ESLint插件
在商店中查找ESLint插件,然后安裝它,不需要配置,在項目內如果集成了eslint的npm包,這個插件會根據(jù)配置文件,對代碼檢查問題進行高亮提示(紅色波浪線是錯誤提示,黃色波浪線是警告提示),然后根據(jù)彈出框指示修改就可以了。
1.3.6、安裝Prettier
npm add -D prettier
1.3.7、安裝Prettier兼容eslint的插件
npm add -D eslint-config-prettier
1.3.8、安裝eslint的prettier
npm add -D eslint-plugin-prettier
1.3.9、在根目錄下創(chuàng)建.eslintrc.js文件
/** .eslintrc.js
* 在VSCode中安裝ESLint插件,編寫過程中檢測代碼質量
* ESLint 代碼質量校驗相關配置
* 這里使用prettier作為代碼格式化工具,用ESLint做代碼質檢
* 相關配置使用下面extends擴展先做默認設置
* 在.prettierrc.js文件中配置好后,格式化規(guī)則會以.prettierrc.js作為最終格式,所以不建議在本文件中做代碼格式化相關配置
* 相關prettier配置ESLint會默認加載為代碼質檢 格式化以prettier為主
* 在本配置文件中只做代碼質量約束規(guī)范配置
*/
module.exports = {
root: true,
env: {
browser: true,
node: true,
},
extends: [
'eslint-config-prettier',
'eslint:recommended', // 使用推薦的eslint
'plugin:@typescript-eslint/recommended',
'plugin:vue/vue3-recommended', // 使用插件支持vue3
'plugin:vue/vue3-essential',
//1.繼承.prettierrc.js文件規(guī)則 2.開啟rules的 "prettier/prettier": "error" 3.eslint fix的同時執(zhí)行prettier格式化
'plugin:prettier/recommended',
],
parser: 'vue-eslint-parser',
parserOptions: {
ecmaVersion: 2020,
sourceType: 'module',
parser: '@typescript-eslint/parser',
},
plugins: [],
globals: {
defineProps: 'readonly',
defineEmits: 'readonly',
defineExpose: 'readonly',
withDefaults: 'readonly',
},
rules: {
'no-console': process.env.NODE_ENV === 'production' ? ['error', { allow: ['error', 'warn'] }] : 'off', //生產模式不允許使用log
'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off', //生產默認不允許使用debugger
'@typescript-eslint/no-unused-vars': ['error', { varsIgnorePattern: '.*', args: 'none' }], //變量聲明未使用
'@typescript-eslint/no-explicit-any': 'off', // 允許ts使用any
// '@typescript-eslint/no-var-requires': 'off', // 強制使用 import 且不允許使用 require 設置off關閉檢查
'vue/require-v-for-key': 'error', // 對保留元素檢查 vue3中v-for會自動追加key值,所以不用再強制添加key屬性,所以不檢查key的填寫
'vue/valid-v-for': 'error', // 對于非保留(自定義)元素檢查 vue3中v-for會自動追加key值,所以不用再強制添加key屬性,所以不檢查key的填寫
// // 添加組件命名忽略規(guī)則 vue官方默認規(guī)則是多單詞駝峰來進行組件命名
// 'vue/multi-word-component-names': [
// 'warn',
// {
// ignores: ['index'], //需要忽略的組件名
// },
// ],
},
};
1.3.10、在根目錄下創(chuàng)建.prettierrc.js文件
如果不想格式化某些文件可以再添加一個.prettierignore的文件,用法和.gitignore文件差不多,將不需要格式化的文件夾或文件通過正則匹配或者具名的方式添加進去,這樣就不會格式化對應的文件了。
module.exports = {
// 一行最多多少個字符
printWidth: 200,
// 指定每個縮進級別的空格數(shù)
tabWidth: 2,
// 使用制表符而不是空格縮進行
useTabs: false,
// 在語句末尾是否需要分號
semi: true,
// 是否使用單引號
singleQuote: true,
// 更改引用對象屬性的時間 可選值"<as-needed|consistent|preserve>"
quoteProps: 'as-needed',
// 在JSX中使用單引號而不是雙引號
jsxSingleQuote: false,
// 多行時盡可能打印尾隨逗號。(例如,單行數(shù)組永遠不會出現(xiàn)逗號結尾。) 可選值"<none|es5|all>",默認none
trailingComma: 'es5',
// 在對象文字中的括號之間打印空格
bracketSpacing: true,
// jsx 標簽的反尖括號需要換行
jsxBracketSameLine: false,
// 在單獨的箭頭函數(shù)參數(shù)周圍包括括號 always:(x) => x \ avoid:x => x
arrowParens: 'always',
// 這兩個選項可用于格式化以給定字符偏移量(分別包括和不包括)開始和結束的代碼
rangeStart: 0,
rangeEnd: Infinity,
// 指定要使用的解析器,不需要寫文件開頭的 @prettier
requirePragma: false,
// 不需要自動在文件開頭插入 @prettier
insertPragma: false,
// 使用默認的折行標準 always\never\preserve
proseWrap: 'preserve',
// 指定HTML文件的全局空格敏感度 css\strict\ignore
htmlWhitespaceSensitivity: 'css',
// Vue文件腳本和樣式標簽縮進
vueIndentScriptAndStyle: false,
//在 windows 操作系統(tǒng)中換行符通常是回車 (CR) 加換行分隔符 (LF),也就是回車換行(CRLF),
//然而在 Linux 和 Unix 中只使用簡單的換行分隔符 (LF)。
//對應的控制字符為 "\n" (LF) 和 "\r\n"(CRLF)。auto意為保持現(xiàn)有的行尾
// 換行符使用 lf 結尾是 可選值"<auto|lf|crlf|cr>"
endOfLine: 'auto',
};
1.3.11、配置vscode保存自動格式化文件
打開vscod→文件→首選項→設置,指定文件名,即可根據(jù)文件的配置去做代碼校驗
1.3.12、選擇使用的格式化文檔
右鍵點擊(使用…格式化文檔),選擇Prettier - code - formatter (默認值)
1.3.13、選擇格式化操作選項
需要重啟vscode才能生效
1.4、集成sass
1.4.1、安裝sass
npm install -D sass sass-loader
1.4.2、在src/assets/styles目錄下創(chuàng)建reset.scss文件
*:after,
*:before {
box-sizing: border-box;
outline: none;
}
html,
body,
div,
span,
applet,
object,
iframe,
h1,
h2,
h3,
h4,
h5,
h6,
p,
blockquote,
pre,
a,
abbr,
acronym,
address,
big,
cite,
code,
del,
dfn,
em,
img,
ins,
kbd,
q,
s,
samp,
small,
strike,
strong,
sub,
sup,
tt,
var,
b,
u,
i,
center,
dl,
dt,
dd,
ol,
ul,
li,
fieldset,
form,
label,
legend,
table,
caption,
tbody,
tfoot,
thead,
tr,
th,
td,
article,
aside,
canvas,
details,
embed,
figure,
figcaption,
footer,
header,
hgroup,
menu,
nav,
output,
ruby,
section,
summary,
time,
mark,
audio,
video {
font: inherit;
font-size: 100%;
margin: 0;
padding: 0;
vertical-align: baseline;
border: 0;
}
article,
aside,
details,
figcaption,
figure,
footer,
header,
hgroup,
menu,
nav,
section {
display: block;
}
body {
line-height: 1;
}
ol,
ul {
list-style: none;
}
blockquote,
q {
quotes: none;
&:before,
&:after {
content: '';
content: none;
}
}
sub,
sup {
font-size: 75%;
line-height: 0;
position: relative;
vertical-align: baseline;
}
sup {
top: -.5em;
}
sub {
bottom: -.25em;
}
table {
border-spacing: 0;
border-collapse: collapse;
}
input,
textarea,
button {
font-family: inhert;
font-size: inherit;
color: inherit;
}
select {
text-indent: .01px;
text-overflow: '';
border: 0;
border-radius: 0;
-webkit-appearance: none;
-moz-appearance: none;
}
select::-ms-expand {
display: none;
}
code,
pre {
font-family: monospace, monospace;
font-size: 1em;
}
1.4.3、在src/assets/styles目錄下創(chuàng)建variables.scss文件
// base color
$blue: #324157;
$red: #C03639;
$pink: #E65D6E;
$green: #30B08F;
$tiffany: #4AB7BD;
$yellow: #FEC171;
$panGreen: #30B08F;
1.4.4、在src/assets/styles目錄下創(chuàng)建mixin.scss文件
@mixin clearfix {
&:after {
content: "";
display: table;
clear: both;
}
}
@mixin scrollBar {
&::-webkit-scrollbar-track-piece {
background: #d3dce6;
}
&::-webkit-scrollbar {
width: 6px;
}
&::-webkit-scrollbar-thumb {
background: #99a9bf;
border-radius: 20px;
}
}
@mixin relative {
position: relative;
width: 100%;
height: 100%;
}
@mixin triangle($width, $height, $color, $direction) {
$width: $width/2;
$color-border-style: $height solid $color;
$transparent-border-style: $width solid transparent;
height: 0;
width: 0;
@if $direction==up {
border-bottom: $color-border-style;
border-left: $transparent-border-style;
border-right: $transparent-border-style;
}
@else if $direction==right {
border-left: $color-border-style;
border-top: $transparent-border-style;
border-bottom: $transparent-border-style;
}
@else if $direction==down {
border-top: $color-border-style;
border-left: $transparent-border-style;
border-right: $transparent-border-style;
}
@else if $direction==left {
border-right: $color-border-style;
border-top: $transparent-border-style;
border-bottom: $transparent-border-style;
}
}
1.4.5、在src/assets/styles目錄下創(chuàng)建index.scss文件
@import "./reset.scss";
@import "./variables.scss";
@import "./mixin.scss";
1.4.6、配置全局樣式
在根目錄vite.config.ts文件中新增
export default defineConfig({
css: {
// css預處理器
preprocessorOptions: {
scss: {
charset: false,
// 引入 index.scss 這樣就可以在全局中使用 index.scss中預定義的變量了
// 給導入的路徑最后加上;
additionalData: '@import "@/assets/styles/index.scss";',
},
},
},
})
1.4.7、文件中的使用
<style lang="scss">
.box {
color: $red;
@include box();
}
</style>
1.5、集成element-plus
1.5.1、安裝element-plus
npm install element-plus --save
1.5.2、安裝element-plus icon
npm install @element-plus/icons-vue
1.5.3、Volar 支持
在根目錄tsconfig.json中新增
{
"compilerOptions": {
// ...
"types": ["element-plus/global"]
}
}
1.5.4、按需引入element-plus組件
首先你需要安裝unplugin-vue-components 和 unplugin-auto-import這兩款插件
npm install -D unplugin-vue-components unplugin-auto-import
在根目錄vite.config.ts文件中新增
import AutoImport from 'unplugin-auto-import/vite';
import Components from 'unplugin-vue-components/vite';
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers';
export default defineConfig({
// ...
plugins: [
// ...
AutoImport({
resolvers: [ElementPlusResolver()],
}),
Components({
resolvers: [ElementPlusResolver()],
}),
],
})
1.5.5、國際化配置
在src/App.vue文件中新增
<template>
<el-config-provider :locale="locale">
<App/>
</el-config-provider>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import zhCn from 'element-plus/es/locale/lang/zh-cn';
const locale = ref(zhCn);
</script>
1.5.5、按需加載自定義主題樣式
在src/assets/styles目錄下新建element.scss
@forward 'element-plus/theme-chalk/src/common/var.scss' with (
$colors: (
'primary': (
'base': red,
),
),
);
在src/assets/styles/index.scss文件中添加
@import "./element.scss";
在根目錄vite.config.ts文件中添加
export default defineConfig({
plugins: [
Components({
resolvers: [
ElementPlusResolver({
importStyle: 'sass',
}),
],
}),
],
})
1.6、集成pinia全局狀態(tài)管理
1.6.1、安裝pinia
npm install pinia
1.6.2、全局引入pinia
在src目錄main.ts新增
import { createPinia } from 'pinia';
// 實例化 Pinia
const pinia = createPinia();
app.use(pinia);
1.6.3、創(chuàng)建store文件
在src/store目錄下創(chuàng)建user.ts
import { defineStore } from 'pinia';
import { computed, reactive, ref } from 'vue';
export interface Userinfo {
id: string;
username: string;
email: string;
phone: string;
address: string;
birthDate?: number;
lastLoginTime?: number;
role: number;
}
export const useUserStore = defineStore('user', () => {
//state
const userinfo = reactive<Userinfo>({
id: '',
username: '',
email: '',
phone: '',
address: '',
birthDate: 0,
lastLoginTime: 0,
role: 0,
});
const isLogined = ref<boolean>(false);
//getters
const getUserinfo = computed(() => userinfo);
//action
const login = (): void => {
console.log('login');
};
const logout = (): void => {
console.log('logout');
};
return { userinfo, isLogined, getUserinfo, login, logout };
});
1.6.4、頁面中使用
相關用法和hooks一致
<template>
<div>{{ getUserinfo.username }}</div>
</template>
<script setup lang="ts">
import { useUserStore } from '@/store/user';
const { getUserinfo } = useUserStore();
</script>
1.7、集成頁面路由
1.7.1、安裝vue-router
npm install vue-router @types/vue-router
1.7.2、使用路由
在src/main.ts文件中新增
import router from './router';
app.use(router);
在src/App.vue文件中修改
<template>
<el-config-provider :locale="getLocale">
<router-view></router-view>
</el-config-provider>
</template>
1.7.4、配置路由
在src/router目錄下新建index.ts文件
import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router';
import Layout from '@/layout/index.vue';
const routes: RouteRecordRaw[] = [
{
path: '/redirect',
component: Layout,
children: [
{
path: '/redirect/:path(.*)',
component: () => import('@/views/redirect/index.vue'),
},
],
},
{
path: '/login',
component: () => import('@/views/login.vue'),
},
{
path: '/register',
component: () => import('@/views/register.vue'),
},
{
path: '/:pathMatch(.*)*',
component: () => import('@/views/error/404.vue'),
},
{
path: '/401',
component: () => import('@/views/error/401.vue'),
},
{
path: '',
component: Layout,
redirect: '/home',
children: [
{
path: '/home',
component: () => import('@/views/home.vue'),
name: 'Home',
meta: { title: '首頁', icon: 'dashboard', affix: true },
},
],
},
{
path: '/user',
component: Layout,
redirect: 'noredirect',
children: [],
},
];
const router = createRouter({
history: createWebHistory(),
routes: routes,
});
export default router;
1.8、集成操作Cookie工具
1.8.1、安裝 js-cookie
npm install js-cookie
1.8.2、創(chuàng)建類型聲明文件
在根目錄中創(chuàng)建js-cookie.d.ts文件
declare module 'js-cookie' {
export function set(name: string, value: any, options?: any): void;
export function get(name: string): string | undefined;
export function remove(name: string, options?: any): void;
export function getJSON(name: string): any;
}
1.8.3、使用js-cookie
在src/utils目錄中創(chuàng)建auth.ts文件
import Cookies from 'js-cookie';
Cookies.get('key');
Cookies.set('key', 1);
Cookies.remove('key');
1.9、集成HTTP請求工具
1.9.1、安裝axios
npm install axios
1.9.2、配置axios
借鑒若依框架的請求
在src/plugins目錄下創(chuàng)建cache.ts文件
const sessionCache = {
set(key: string, value: string) {
if (!sessionStorage) {
return;
}
if (key != null && value != null) {
sessionStorage.setItem(key, value);
}
},
get(key: string) {
if (!sessionStorage) {
return null;
}
if (key == null) {
return null;
}
return sessionStorage.getItem(key);
},
setJSON(key: string, jsonValue: any) {
if (jsonValue != null) {
this.set(key, JSON.stringify(jsonValue));
}
},
getJSON(key: string) {
const value = this.get(key);
if (value != null) {
return JSON.parse(value);
}
},
remove(key: string) {
sessionStorage.removeItem(key);
},
};
const localCache = {
set(key: string, value: string) {
if (!localStorage) {
return;
}
if (key != null && value != null) {
localStorage.setItem(key, value);
}
},
get(key: string) {
if (!localStorage) {
return null;
}
if (key == null) {
return null;
}
return localStorage.getItem(key);
},
setJSON(key: string, jsonValue: any) {
if (jsonValue != null) {
this.set(key, JSON.stringify(jsonValue));
}
},
getJSON(key: string) {
const value = this.get(key);
if (value != null) {
return JSON.parse(value);
}
},
remove(key: string) {
localStorage.removeItem(key);
},
};
export default {
/**
* 會話級緩存
*/
session: sessionCache,
/**
* 本地緩存
*/
local: localCache,
};
在src/utils目錄下創(chuàng)建auth.ts文件
import Cookies from 'js-cookie';
const TokenKey: string = 'Admin-Token';
export function getToken() {
return Cookies.get(TokenKey);
}
export function setToken(token) {
return Cookies.set(TokenKey, token);
}
export function removeToken() {
return Cookies.remove(TokenKey);
}
在src/utils目錄下創(chuàng)建index.ts文件
/**
* 參數(shù)處理
* @param {*} params 參數(shù)
*/
export function tansParams(params) {
let result = '';
for (const propName of Object.keys(params)) {
const value = params[propName];
const part = encodeURIComponent(propName) + '=';
if (value !== null && value !== '' && typeof value !== 'undefined') {
if (typeof value === 'object') {
for (const key of Object.keys(value)) {
if (value[key] !== null && value[key] !== '' && typeof value[key] !== 'undefined') {
const params = propName + '[' + key + ']';
const subPart = encodeURIComponent(params) + '=';
result += subPart + encodeURIComponent(value[key]) + '&';
}
}
} else {
result += part + encodeURIComponent(value) + '&';
}
}
}
return result;
}
// 驗證是否為blob格式
export function blobValidate(data) {
return data.type !== 'application/json';
}
在src/plugins目錄下創(chuàng)建errorCode.ts文件
export default {
'401': '認證失敗,無法訪問系統(tǒng)資源',
'403': '當前操作沒有權限',
'404': '訪問資源不存在',
default: '系統(tǒng)未知錯誤,請反饋給管理員',
};
安裝file-saver
npm install file-saver
在src/utils目錄下創(chuàng)建request.ts文件
import axios, { AxiosInstance } from 'axios';
import { App } from 'vue';
import { getToken } from '@/utils/auth';
import { tansParams, blobValidate } from '@/utils/index';
import cache from '@/plugins/cache';
import errorCode from '@/plugins/errorCode';
import { ElNotification, ElMessageBox, ElMessage, ElLoading } from 'element-plus';
import { useUserStore } from '@/store/user';
import { saveAs } from 'file-saver';
let downloadLoadingInstance;
// 是否顯示重新登錄
export const isRelogin = { show: false };
axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8';
// 對應國際化資源文件后綴
axios.defaults.headers['Content-Language'] = 'zh-cn';
// 創(chuàng)建一個 Axios 實例
const axiosInstance: AxiosInstance = axios.create({
baseURL: import.meta.env.VITE_APP_BASE_API, // 替換成你的 API 地址
timeout: 10000, // 設置請求超時時間
});
// request攔截器
axiosInstance.interceptors.request.use(
(config) => {
// 是否需要設置 token
const isToken = (config.headers || {}).isToken === false;
// 是否需要防止數(shù)據(jù)重復提交
const isRepeatSubmit = (config.headers || {}).repeatSubmit === false;
if (getToken() && !isToken) {
config.headers['Authorization'] = 'Bearer ' + getToken(); // 讓每個請求攜帶自定義token 請根據(jù)實際情況自行修改
}
// get請求映射params參數(shù)
if (config.method === 'get' && config.params) {
let url = config.url + '?' + tansParams(config.params);
url = url.slice(0, -1);
config.params = {};
config.url = url;
}
if (!isRepeatSubmit && (config.method === 'post' || config.method === 'put')) {
const requestObj = {
url: config.url,
data: typeof config.data === 'object' ? JSON.stringify(config.data) : config.data,
time: new Date().getTime(),
};
const sessionObj = cache.session.getJSON('sessionObj');
if (sessionObj === undefined || sessionObj === null || sessionObj === '') {
cache.session.setJSON('sessionObj', requestObj);
} else {
const s_url = sessionObj.url; // 請求地址
const s_data = sessionObj.data; // 請求數(shù)據(jù)
const s_time = sessionObj.time; // 請求時間
const interval = 1000; // 間隔時間(ms),小于此時間視為重復提交
if (s_data === requestObj.data && requestObj.time - s_time < interval && s_url === requestObj.url) {
const message = '數(shù)據(jù)正在處理,請勿重復提交';
console.warn(`[${s_url}]: ` + message);
return Promise.reject(new Error(message));
} else {
cache.session.setJSON('sessionObj', requestObj);
}
}
}
return config;
},
(error) => {
console.log(error);
Promise.reject(error);
}
);
// 響應攔截器
axiosInstance.interceptors.response.use(
(res) => {
// 未設置狀態(tài)碼則默認成功狀態(tài)
const code = res.data.code || 200;
// 獲取錯誤信息
const msg = errorCode[code] || res.data.msg || errorCode['default'];
// 二進制數(shù)據(jù)則直接返回
if (res.request.responseType === 'blob' || res.request.responseType === 'arraybuffer') {
return res.data;
}
if (code === 401) {
if (!isRelogin.show) {
isRelogin.show = true;
ElMessageBox.confirm('登錄狀態(tài)已過期,您可以繼續(xù)留在該頁面,或者重新登錄', '系統(tǒng)提示', { confirmButtonText: '重新登錄', cancelButtonText: '取消', type: 'warning' })
.then(() => {
isRelogin.show = false;
useUserStore()
.logout()
.then(() => {
location.href = import.meta.env.VITE_APP_CONTEXT_PATH + 'home';
});
})
.catch(() => {
isRelogin.show = false;
});
}
return Promise.reject('無效的會話,或者會話已過期,請重新登錄。');
} else if (code === 500) {
ElMessage({ message: msg, type: 'error' });
return Promise.reject(new Error(msg));
} else if (code === 601) {
ElMessage({ message: msg, type: 'warning' });
return Promise.reject(new Error(msg));
} else if (code !== 200) {
ElNotification.error({ title: msg });
return Promise.reject('error');
} else {
return Promise.resolve(res.data);
}
},
(error) => {
console.log('err' + error);
let { message } = error;
if (message == 'Network Error') {
message = '后端接口連接異常';
} else if (message.includes('timeout')) {
message = '系統(tǒng)接口請求超時';
} else if (message.includes('Request failed with status code')) {
message = '系統(tǒng)接口' + message.substr(message.length - 3) + '異常';
}
ElMessage({ message: message, type: 'error', duration: 5 * 1000 });
return Promise.reject(error);
}
);
// 通用下載方法
export function download(url, params, filename, config) {
downloadLoadingInstance = ElLoading.service({ text: '正在下載數(shù)據(jù),請稍候', background: 'rgba(0, 0, 0, 0.7)' });
return axiosInstance
.post(url, params, {
transformRequest: [
(params) => {
return tansParams(params);
},
],
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
responseType: 'blob',
...config,
})
.then(async (response) => {
const isBlob = blobValidate(response.data);
if (isBlob) {
const blob = new Blob([response.data]);
saveAs(blob, filename);
} else {
const resText = await response.data.text();
const rspObj = JSON.parse(resText);
const errMsg = errorCode[rspObj.code] || rspObj.msg || errorCode['default'];
ElMessage.error(errMsg);
}
downloadLoadingInstance.close();
})
.catch((r) => {
console.error(r);
ElMessage.error('下載文件出現(xiàn)錯誤,請聯(lián)系管理員!');
downloadLoadingInstance.close();
});
}
// 將 Axios 實例添加到 Vue 實例的原型鏈中
export const setupAxios = (app: App<Element>) => {
app.config.globalProperties.$axios = axiosInstance;
};
export default axiosInstance;
1.9.3、通過代理方式處理前端跨域問題
export default defineConfig({
server: {
proxy: {
'/dev-api': {
target: 'http://127.0.0.1:8080',
changeOrigin: true,
rewrite: (p) => p.replace(/^\/dev-api/, ''),
},
'/prod-api': {
target: 'http://127.0.0.1:8080',
changeOrigin: true,
rewrite: (p) => p.replace(/^\/prod-api/, ''),
},
},
}
})
1.9.4、創(chuàng)建api
在src/api目錄下創(chuàng)建login.ts
import request from '@/utils/request';
// 登錄方法
export function login(username: string, password: string, code: string) {
const data = {
username,
password,
code,
};
return request({
url: '/login',
headers: {
isToken: false,
},
method: 'post',
data: data,
});
}
// 獲取用戶詳細信息
export function getInfo() {
return request({
url: '/getInfo',
method: 'get',
});
}
1.9.5、在頁面中使用
<script lang="ts" setup>
import { onMounted } from 'vue';
import { getInfo } from '@/api/login';
onMounted(() => {
getInfo().then((response) => {
console.log(response);
});
});
</script>
1.10、集成圖表展示工具
1.10.1、安裝ECharts
npm install echarts --save
1.10.2、配置按需引用
在src/utils目錄下創(chuàng)建echarts.ts文件
// 引入 echarts 核心模塊。
import * as echarts from 'echarts/core';
//引入柱狀圖和折線圖組件。
import { BarChart, LineChart } from 'echarts/charts';
// 引入標題、提示框、網格、數(shù)據(jù)集和數(shù)據(jù)轉換器組件。
import {
TitleComponent,
TooltipComponent,
GridComponent,
// 數(shù)據(jù)集組件
DatasetComponent,
// 內置數(shù)據(jù)轉換器組件 (filter, sort)
TransformComponent,
} from 'echarts/components';
//引入標簽布局和通用過渡動畫特性。
import { LabelLayout, UniversalTransition } from 'echarts/features';
// 引入 Canvas 渲染器。
import { CanvasRenderer } from 'echarts/renderers';
import type {
// 系列類型的定義后綴都為 SeriesOption
BarSeriesOption,
LineSeriesOption,
} from 'echarts/charts';
import type {
// 組件類型的定義后綴都為 ComponentOption
TitleComponentOption,
TooltipComponentOption,
GridComponentOption,
DatasetComponentOption,
} from 'echarts/components';
import type { ComposeOption } from 'echarts/core';
// 通過 ComposeOption 來組合出一個只有必須組件和圖表的 Option 類型
export type EChartsOption = ComposeOption<BarSeriesOption | LineSeriesOption | TitleComponentOption | TooltipComponentOption | GridComponentOption | DatasetComponentOption>;
/**
注冊必須的組件,包括標題、提示框、網格、數(shù)據(jù)集、數(shù)據(jù)轉換器,
以及柱狀圖、折線圖、標簽布局、通用過渡動畫和 Canvas 渲染器。
*/
echarts.use([TitleComponent, TooltipComponent, GridComponent, DatasetComponent, TransformComponent, BarChart, LineChart, LabelLayout, UniversalTransition, CanvasRenderer]);
// 導出
export default echarts;
1.10.3、頁面中使用echarts
<template>
<div ref="chart" style="width: 600px; height: 400px"></div>
</template>
<script lang="ts" setup>
import type { EChartsOption } from '@/utils/echarts';
import echarts from '@/utils/echarts';
import { onMounted, getCurrentInstance } from 'vue';
var option: EChartsOption;
option = {
xAxis: {
type: 'category',
data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
},
yAxis: {
type: 'value',
},
series: [
{
data: [150, 230, 224, 218, 135, 147, 260],
type: 'line',
},
],
};
onMounted(() => {
const proxy = getCurrentInstance();
var chartDom = proxy?.refs.chart;
var myChart = echarts.init(<HTMLElement>chartDom);
option && myChart.setOption(option);
});
</script>
1.11、配置大屏自適應
1.11.1、使用插件
在src/utils目錄下創(chuàng)建flexible.ts
(function (win, lib) {
const doc = win.document;
const docEl = doc.documentElement;
let metaEl = doc.querySelector('meta[name="viewport"]');
const flexibleEl = doc.querySelector('meta[name="flexible"]');
let dpr = 0;
let scale = 0;
let tid;
const flexible = lib.flexible || (lib.flexible = {});
if (metaEl) {
console.warn('將根據(jù)已有的meta標簽來設置縮放比例');
const match = metaEl.getAttribute('content')?.match(/initial-scale=([d.]+)/);
if (match) {
scale = parseFloat(match[1]);
dpr = parseInt((1 / scale).toString());
}
} else if (flexibleEl) {
const content = flexibleEl.getAttribute('content');
if (content) {
const initialDpr = content.match(/initial-dpr=([d.]+)/);
const maximumDpr = content.match(/maximum-dpr=([d.]+)/);
if (initialDpr) {
dpr = parseFloat(initialDpr[1]);
scale = parseFloat((1 / dpr).toFixed(2));
}
if (maximumDpr) {
dpr = parseFloat(maximumDpr[1]);
scale = parseFloat((1 / dpr).toFixed(2));
}
}
}
if (!dpr && !scale) {
// var isAndroid = win.navigator.appVersion.match(/android/gi);
const isIPhone = win.navigator.appVersion.match(/iphone/gi);
const devicePixelRatio = win.devicePixelRatio;
if (isIPhone) {
// iOS下,對于2和3的屏,用2倍的方案,其余的用1倍方案
if (devicePixelRatio >= 3 && (!dpr || dpr >= 3)) {
dpr = 3;
} else if (devicePixelRatio >= 2 && (!dpr || dpr >= 2)) {
dpr = 2;
} else {
dpr = 1;
}
} else {
// 其他設備下,仍舊使用1倍的方案
dpr = 1;
}
scale = 1 / dpr;
}
docEl.setAttribute('data-dpr', dpr.toString());
if (!metaEl) {
metaEl = doc.createElement('meta');
metaEl.setAttribute('name', 'viewport');
metaEl.setAttribute('content', 'initial-scale=' + scale + ', maximum-scale=' + scale + ', minimum-scale=' + scale + ', user-scalable=no');
if (docEl.firstElementChild) {
docEl.firstElementChild.appendChild(metaEl);
} else {
const wrap = doc.createElement('div');
wrap.appendChild(metaEl);
doc.write(wrap.innerHTML);
}
}
function refreshRem() {
let width = docEl.getBoundingClientRect().width;
if (width / dpr > 1920) {
// 這個位置劃重點 1920是設計稿的大小 如果你的設計稿是750 那么就需要將1920替換成750
width = (docEl.clientWidth / 1920) * 1920;
}
const rem = width / 10;
docEl.style.fontSize = rem + 'px';
flexible.rem = win.rem = rem;
}
win.addEventListener(
'resize',
function () {
clearTimeout(tid);
tid = setTimeout(refreshRem, 300);
},
false
);
win.addEventListener(
'pageshow',
function (e) {
if (e.persisted) {
clearTimeout(tid);
tid = setTimeout(refreshRem, 300);
}
},
false
);
if (doc.readyState === 'complete') {
doc.body.style.fontSize = 12 * dpr + 'px';
} else {
doc.addEventListener(
'DOMContentLoaded',
function () {
doc.body.style.fontSize = 12 * dpr + 'px';
},
false
);
}
refreshRem();
flexible.dpr = win.dpr = dpr;
flexible.refreshRem = refreshRem;
flexible.rem2px = function (d) {
let val = parseFloat(d) * this.rem;
if (typeof d === 'string' && d.match(/rem$/)) {
val += 'px';
}
return val;
};
flexible.px2rem = function (d) {
let val = parseFloat(d) / this.rem;
if (typeof d === 'string' && d.match(/px$/)) {
val += 'rem';
}
return val;
};
})(window, window['lib'] || (window['lib'] = {}));
1.11.2、安裝vscode插件
搜索cssrem并安裝
配置自動轉換(右鍵插件→擴展設置),如設計稿為1920 * 1080,這該數(shù)值為 1920 / 10 = 192
1.12、集成拖拽插件
1.12.1、安裝Pragmatic drag and drop
npm i @atlaskit/pragmatic-drag-and-drop-react-drop-indicator
1.12.2、頁面中使用
鏈接: Pragmatic drag and drop官網
import {draggable} from '@atlaskit/pragmatic-drag-and-drop/element/adapter';
2、技術選型
2.1、為何選擇vite
2.1.1、快速的開發(fā)啟動速度
Vite 以其快速的開發(fā)啟動速度而聞名。它利用了現(xiàn)代瀏覽器的原生 ES 模塊支持,將開發(fā)服務器和構建工具的性能優(yōu)化到極致,因此在啟動開發(fā)服務器和進行熱模塊更新時表現(xiàn)出色。
2.1.2、基于 ES 模塊的開發(fā)
Vite 使用原生 ES 模塊來開發(fā),這意味著你可以直接在瀏覽器中運行你的代碼,而無需將其轉換為 CommonJS 或其他模塊格式。這簡化了開發(fā)過程,同時也提高了開發(fā)效率。
2.1.3、支持現(xiàn)代的 JavaScript 和 CSS 特性
Vite 內置了對最新 JavaScript 和 CSS 特性的支持,包括模塊化、動態(tài)導入、CSS 變量等。這使得開發(fā)者能夠更輕松地使用現(xiàn)代的前端技術來構建項目。
2.1.4、插件系統(tǒng)
Vite 提供了豐富的插件系統(tǒng),允許開發(fā)者通過插件來擴展和定制構建過程。這使得 Vite 可以與各種工具和框架集成,滿足不同項目的需求。
2.1.5、Vue.js 生態(tài)系統(tǒng)的良好支持
Vite 由 Vue.js 的作者開發(fā),因此與 Vue.js 框架集成得非常好。Vue 3 默認支持 Vite,而且 Vite 也提供了一些針對 Vue.js 的優(yōu)化。
2.1.6、適用于小型項目和原型開發(fā)
由于其快速的啟動速度和簡潔的配置,Vite 特別適用于小型項目和原型開發(fā),可以幫助開發(fā)者更快地搭建起項目框架并進行迭代。
2.2、為何選擇vue3
2.2.1、響應式性能提升
性能比Vue2塊1.5-2倍。性能的提升主要是通過響應式Q系統(tǒng)的提升(vue3使用proxy對象重寫響應式)以及編譯優(yōu)化(優(yōu)化編譯和重寫虛擬dom、優(yōu)化diff算法)來完成。
2.2.2、代碼體積更小
相比Vue2,Vue3按需編譯,整體體積變小了。除了移出一些不常用的API,值得一提的是Tree shanking任何一個函數(shù),如ref、reactive、computed等,僅僅在用到的時候才打包,沒用到的模塊都被去掉,打包的整體體積變小。
2.2.3、支持組合API(Composition Api)
Vue2使用Options Api(選項api),而Vue3使用Composition Api (組合api)和setup語法糖,向下兼容vue2的Options Api(選項api)。
2.2.4、更好的 ts 支持
Vue 新增了 DefineComponent 函數(shù),使組件在 ts 下,更好的利用參數(shù)類型推斷。如:reactive 和 ref 很具有代表性。
2.2.5、更先進的組件
①vue 中可以不需要根節(jié)點,多個元素或標簽可并列存在;②可以把 teleport 中的內容添加到任意的節(jié)點內;③允許程序在等待異步組件渲染一些后備的內容,可以讓我們創(chuàng)建一個平滑的用戶體驗。
2.3、為何選擇TypeScript
2.3.1、靜態(tài)類型檢查
TypeScript 提供了靜態(tài)類型檢查功能,可以在編譯時捕獲潛在的錯誤和類型不匹配問題,有助于提高代碼質量和可維護性。通過類型檢查,可以在開發(fā)過程中更早地發(fā)現(xiàn)錯誤,并減少在運行時出現(xiàn)的問題。
2.3.2、增強的編輯器支持
TypeScript 提供了豐富的類型信息,這使得編輯器能夠提供更強大的代碼提示、自動完成和重構功能。特別是在使用支持 TypeScript 的編輯器(如 Visual Studio Code)時,能夠獲得最佳的開發(fā)體驗。
2.3.3、更好的文檔和可讀性
通過在代碼中添加類型注解,可以提供更清晰和可讀的文檔。類型信息可以幫助開發(fā)人員理解代碼的意圖,并使代碼更易于維護和理解。
2.3.4、更好的團隊協(xié)作
類型信息可以提供更明確的接口定義,有助于不同團隊成員之間更好地協(xié)作。通過類型定義,可以清楚地了解函數(shù)和組件的輸入輸出,從而減少溝通成本和誤解。
2.3.5、漸進式采用
TypeScript 是 JavaScript 的超集,這意味著你可以逐步將現(xiàn)有的 JavaScript 代碼轉換為 TypeScript,而不需要一次性進行全面的重構。這使得在現(xiàn)有項目中引入 TypeScript 變得更加容易和靈活。
2.3.6、生態(tài)系統(tǒng)支持
TypeScript 在社區(qū)和生態(tài)系統(tǒng)方面擁有強大的支持,有大量的第三方庫和工具可以與之兼容。同時,許多流行的框架和庫(如 Vue、React、Angular 等)都提供了對 TypeScript 的良好支持。
2.4、為何選擇pinia
2.4.1、簡潔而強大的狀態(tài)管理
Pinia 是一個基于 Vue 3 的狀態(tài)管理庫,它提供了簡潔而強大的 API,使得狀態(tài)管理變得更加直觀和容易。Pinia 的設計理念是將狀態(tài)管理的復雜性降到最低,同時保持靈活性和可擴展性。
2.4.2、類型安全
Pinia 充分利用了 TypeScript 的類型系統(tǒng),提供了類型安全的狀態(tài)管理解決方案。通過使用 TypeScript,可以在編譯時捕獲潛在的錯誤和類型不匹配問題,從而提高代碼質量和可維護性。
2.4.3、響應式和異步支持
Pinia 內置了對 Vue 3 的響應式系統(tǒng)的支持,可以輕松地管理和響應狀態(tài)的變化。同時,Pinia 還提供了對異步操作的支持,使得在處理異步邏輯時更加方便和簡潔。
2.4.4、輕量級和可擴展性
Pinia 的代碼庫非常輕量,沒有過多的依賴和復雜的概念,這使得它非常適合于構建中小型應用。同時,Pinia 也提供了豐富的插件系統(tǒng)和擴展點,可以根據(jù)需要進行定制和擴展。
2.4.5、與 Vue 生態(tài)系統(tǒng)的集成
作為 Vue 3 的狀態(tài)管理庫,Pinia 與 Vue 生態(tài)系統(tǒng)無縫集成,可以與 Vue Router、Vue DevTools 等其他 Vue 相關工具和庫配合使用,為開發(fā)提供更加便利的體驗。
2.4.6、活躍的社區(qū)和文檔支持
Pinia 擁有一個活躍的社區(qū)和完善的文檔支持,你可以在社區(qū)中獲取到豐富的資源和解決方案,并且能夠獲得及時的技術支持和反饋。
2.5、為何集成eslint和babel
2.5.1、代碼質量保障
Eslint 是一個代碼質量檢查工具,它可以幫助團隊確保代碼的一致性、可讀性和可維護性。通過配置 Eslint 規(guī)則,可以捕獲代碼中的潛在問題和錯誤,從而提高代碼的質量和穩(wěn)定性。
2.5.2、規(guī)范化團隊開發(fā)
Eslint 提供了豐富的規(guī)則和配置選項,可以根據(jù)團隊的開發(fā)風格和偏好進行定制。通過統(tǒng)一的代碼規(guī)范,可以提高團隊成員之間的協(xié)作效率,并降低代碼維護的成本。
2.5.3、提高開發(fā)效率
Eslint 可以集成到代碼編輯器中,提供實時的代碼檢查和提示,幫助開發(fā)人員及時發(fā)現(xiàn)和解決問題。這可以減少代碼審查和調試的時間,提高開發(fā)效率。
2.5.4、支持現(xiàn)代 JavaScript 特性
Babel 是一個 JavaScript 編譯器,可以將新版本的 JavaScript 代碼轉換為向后兼容的代碼,從而確保代碼在不同環(huán)境中的兼容性。通過使用 Babel,可以使用最新的 JavaScript 特性,而無需擔心兼容性問題。
2.5.5、生態(tài)系統(tǒng)支持
Eslint 和 Babel 都擁有龐大的生態(tài)系統(tǒng),有大量的插件和擴展可以滿足不同項目的需求。無論是處理特定的代碼風格還是支持特定的 JavaScript 特性,都可以在生態(tài)系統(tǒng)中找到相應的解決方案。文章來源:http://www.zghlxwxcb.cn/news/detail-856188.html
2.5.6、持續(xù)更新和改進
Eslint 和 Babel 都是活躍的項目,持續(xù)地更新和改進。他們及時跟進最新的 JavaScript 標準和最佳實踐,保持與時俱進,為開發(fā)人員提供更好的工具和體驗。文章來源地址http://www.zghlxwxcb.cn/news/detail-856188.html
到了這里,關于搭建vue3,TypeScript,pinia,scss,element-plus,axios,echarts,vue-router,babylon,eslint,babel,拖拽,rem自適應大屏的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網!