一、创建项目
1. 创建一个项目文件夹,并打开bash
2. 输入指令:npm init vite
注意:如果此时在创建项目过程中,不能通过键盘上下键,选择项目选项。改用:winpty npm.cmd init vite
3. 然后选择项目选项。
4. npm i
安装依赖
5. 最后npm run dev
启动项目即可。
二、基本配置
1. 基本配置
npm install @types/node --save-dev
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import { resolve } from 'path';
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
vue(),
],
server: {
host: '0.0.0.0',
port: 8080,
},
// 导入路径简写
resolve: {
alias: [
{
find: '@',
replacement: resolve(__dirname, 'src'),
},
],
},
});
2. 配置路由
npm install vue-router@4
3. 安装sass/scss
npm install -D sass sass-loader
npm install -D scss scss-loader
4. 自动引入
vue component 文档
auto-import
npm i unplugin-auto-import unplugin-vue-components -D
npm run dev, 执行完成之后, 会生成两个文件 auto-imports.d, components.d
注:如果引入后,启动报错,升级node版本,要求14以上。
window升级node版本:where node
查看node位置,然node官网下载,替换旧node文件即可。
5. 配置Element-plus的按需引入
首先引入依赖 npm i element-plus
在vite.config.js文件中配置
import Components from 'unplugin-vue-components/vite';
import AutoImport from 'unplugin-auto-import/vite';
import {ElementPlusResolver} from 'unplugin-vue-components/resolvers'
plugins: [
vue(),
//自动导入模块,不需要在文件中再手动引入
AutoImport({
imports: ['vue'],
dts: 'src/auto-import.d.ts',
}),
Components({
resolvers:[ElementPlusResolver()]
}),
],
配置后,直接使用组件,会自动导入,不用再手动引入。main.ts文件中,也不用再配置Element-Plus
<template>
<div>
<Home class="chose">首页</Home>
<el-input size="small"></el-input>
</div>
</template>
<script setup lang="ts">
</script>
<style scoped lang="scss">
.chose {
font-size: 16px;
color: aqua;
}
</style>
6. eslint 配置
npm i 插件
新建.eslintrc.js文件。
module.exports = {
root: true,
env: {
browser: true,
node: true
},
parser: 'vue-eslint-parser',
parserOptions: {
parser: '@typescript-eslint/parser',
ecmaVersion: 'latest',
sourceType: 'module',
jsxPragma: 'vue'
},
settings: {
'import/resolver': {
alias: {
map: ['@', './src']
}
}
},
extends: [
// 这里新增vue3支持
'plugin:vue/vue3-recommended',
'plugin:@typescript-eslint/recommended'
// 'prettier',
// 'plugin:prettier/recommended',
],
plugins: ['vue'],
rules: {
// @typescript-eslint
'@typescript-eslint/explicit-function-return-type': 'off', // 需要函数和类方法的显式返回类型
'@typescript-eslint/no-explicit-any': 'off', // 禁止使用该 any 类型
'@typescript-eslint/no-var-requires': 'off', // 不允许使用 require 语句,除了在 import 语句中
'@typescript-eslint/no-empty-function': 'off', // 禁止空函数
'@typescript-eslint/no-empty-interface': 'off', // 禁止空接口
'@typescript-eslint/no-use-before-define': 'warn', // 在定义之前禁止使用变量
'@typescript-eslint/ban-ts-comment': 'off', // 禁止 @ts-<directive> 使用评论或在指令后要求描述
'@typescript-eslint/ban-types': 'off', // 禁止使用特定类型
'@typescript-eslint/no-non-null-assertion': 'off', // '!'不允许使用后缀运算符的非空断言
'@typescript-eslint/explicit-module-boundary-types': 'off', // 需要导出函数和类的公共类方法的显式返回和参数类型
'@typescript-eslint/no-unused-vars': [ // 禁止未使用的变量
'warn',
{
argsIgnorePattern: '^_',
varsIgnorePattern: '^_',
},
],
// vue
// 'vue/custom-event-name-casing': 1, // 为自定义事件名称强制使用特定大小写
'vue/attributes-order': 'off', // 强制执行属性顺序
'vue/one-component-per-file': 'off', // 强制每个组件都应该在自己的文件中
'vue/html-closing-bracket-newline': 'off', // 在标签的右括号之前要求或禁止换行
'vue/multiline-html-element-content-newline': 'off', // 在多行元素的内容之前和之后需要换行符
'vue/singleline-html-element-content-newline': 'off', // 在单行元素的内容之前和之后需要换行符
'vue/multi-word-component-names': 'off', //强制组件名称必须是由多个单词组成
'vue/attribute-hyphenation': 'off', // 对模板中的自定义组件强制执行属性命名样式
'vue/require-default-prop': 'off', // 需要 props 的默认值
// 'vue/html-indent': ['warn', 2], // 在<template>中强制一致缩进
'vue/html-indent': 'off', // 在<template>中强制一致缩进
// 'vue/html-self-closing': 1, // 执行自闭合的风格
'vue/max-attributes-per-line': 'off', // 强制每行属性的最大数量
'vue/no-v-html': 'off',
// // ESLint
'space-before-function-paren': 'off' // 强制在 function的左括号之前使用一致的空格
}
};
vite.config.ts配置
import eslintPlugin from 'vite-plugin-eslint';
plugins: [
eslintPlugin({
emitError: true, //打印错误
// emitError: 'production' === NODE_ENV
},
AutoImport({
eslintrc: {
enabled: true, // Default `false`
filepath: './.eslintrc-auto-import.json', // Default `./.eslintrc-auto-import.json`
globalsPropValue: true, // Default `true`, (true | false | 'readonly' | 'readable' | 'writable' | 'writeable')
},
}),
],
7. 封装axios请求
utils/useAxiosApi.ts文件:
import { useAxios } from '@vueuse/integrations/useAxios';
import axios, { AxiosRequestConfig } from 'axios';
// 创建一个axios实例
const instance = axios.create({
withCredentials: false,
timeout: 30 * 60 * 1000, //设置超时
});
// loading
let loading = ref();
//正在请求的数量
let requestCount = 0;
//显示loading
const showLoading = () => {
if (requestCount === 0 && !loading) {
loading = ElLoading.service({
text: '正在加载中......',
background: 'rgba(0, 0, 0, 0.7)',
spinner: 'el-icon-loading',
}) as any;
}
requestCount++;
};
//隐藏loading
const hideLoading = () => {
requestCount--;
if (requestCount == 0) {
//@ts-ignore
loading.close();
}
};
//请求拦截器
instance.interceptors.request.use(
(config) => {
showLoading();
// const token = store.state.user.token;
// if (token) {
config.headers = {
...config.headers,
Authorization: `unauthorized`,
};
// }
return config;
},
(error) => {
console.log(error);
return Promise.reject(error);
}
);
instance.interceptors.response.use(
(response) => {
hideLoading();
return response.data;
},
(error) => {
if (error.response && error.response.status) {
const status = error.response.status;
let message = ref('' as any);
switch (status) {
case 400:
message = '请求错误';
break;
case 401:
message = '请求错误';
break;
case 404:
message = '请求地址出错';
break;
case 408:
message = '请求超时';
break;
case 500:
message = '服务器内部错误!';
break;
case 501:
message = '服务未实现!';
break;
case 502:
message = '网关错误!';
break;
case 503:
message = '服务不可用!';
break;
case 504:
message = '网关超时!';
break;
case 505:
message = 'HTTP版本不受支持';
break;
default:
message = '请求失败';
}
ElMessage.error(message);
return Promise.reject(error);
}
}
);
export default function useAxiosApi(url: string, config: AxiosRequestConfig) {
return useAxios(url, config, instance);
}
export { axios };
使用:
import request from '../utils/useAxiosApi';
export function integrations() {
return request(`url`, params={});
}
8. 状态管理VUEX
store/index.ts文件:
main.ts文件中声明:
import store from './store';
const app = createApp(App);
app.use(store).mount('#app');
modules/index.ts文件:
// 批量直接引入模块
const modulesFiles = import.meta.globEager('./*.ts');
const modules: any = {};
for (const key in modulesFiles) {
modules[key.replace(/(\.\/|\.ts)/g, '')] = modulesFiles[key].default;
}
export { modules, modulesFiles };
自定义模块:modules/mainStore.ts
import { Module } from 'vuex';
const mainStore: Module<MainStore, unknown> = {
state() {
return {
num: 10,
};
},
getters: {},
mutations: {},
actions: {},
};
export default mainStore;
注:MainStore是自定义去全局接口。项目目录下新建global.ts文件。定义为该接口类型后,属性要与该接口声明的保持一致。详情参考typeScript。
9. 常用工具,自定义拖拽指令。
src/directive/index.ts文件:
export default {
install(app: any){
app.directive('dialogPage', {
mounted(el: any){
const dialogHeaderEl = el.querySelector('.page-title');
const dragDom = el;
dialogHeaderEl.style.cssText += ';cursor:move;'
// 获取原有属性 ie dom元素.currentStyle 火狐谷歌 window.getComputedStyle(dom元素, null);
const sty = (function() {
if ((window.document as any).currentStyle) {
return (dom: any, attr: any) => dom.currentStyle[attr];
} else{
return (dom: any, attr: any) => getComputedStyle(dom)[attr];
}
})()
dialogHeaderEl.onmousedown = (e: any) => {
// 鼠标按下,计算当前元素距离可视区的距离
const disX = e.clientX - dialogHeaderEl.offsetLeft;
const disY = e.clientY - dialogHeaderEl.offsetTop;
const screenWidth = document.body.clientWidth; // body当前宽度
const screenHeight = document.documentElement.clientHeight; // 可见区域高度(应为body高度,可某些环境下无法获取)
const dragDomWidth = dragDom.offsetWidth; // 对话框宽度
const dragDomheight = dragDom.offsetHeight; // 对话框高度
const minDragDomLeft = dragDom.offsetLeft;
const maxDragDomLeft = screenWidth - dragDom.offsetLeft - dragDomWidth;
const minDragDomTop = dragDom.offsetTop;
const maxDragDomTop = screenHeight - dragDom.offsetTop - dragDomheight;
// 获取到的值带px 正则匹配替换
let styL = sty(dragDom, 'left');
let styT = sty(dragDom, 'top');
// 注意在ie中 第一次获取到的值为组件自带50% 移动之后赋值为px
if(styL.includes('%')) {
styL = +document.body.clientWidth * (+styL.replace(/\%/g, '') / 100);
styT = +document.body.clientHeight * (+styT.replace(/\%/g, '') / 100);
}else {
styL = +styL.replace(/\px/g, '');
styT = +styT.replace(/\px/g, '');
}
document.onmousemove = function (e) {
// 通过事件委托,计算移动的距离
let left = e.clientX - disX;
let top = e.clientY - disY;
// 边界处理
if (-(left) > minDragDomLeft) {
left = -(minDragDomLeft);
} else if (left > maxDragDomLeft) {
left = maxDragDomLeft;
}
if (-(top) > minDragDomTop) {
top = -(minDragDomTop);
} else if (top > maxDragDomTop) {
top = maxDragDomTop;
}
// 移动当前元素
dragDom.style.cssText += `;left:${left + styL}px;top:${top + styT}px;`;
};
document.onmouseup = function () {
document.onmousemove = null;
document.onmouseup = null;
};
}
}
})
}
}
在main.ts中挂载指令:
使用: