前端开发之vue学习笔记分享(前端项目搭建Vue3Vite2)
前端开发之vue学习笔记分享(前端项目搭建Vue3Vite2)--ext: 配置要检查文件的后缀 eslint --ext .js --ext .ts --ext .vue src: 只检查src文件夹下的js/ts/vue文件module.exports = { printWidth: 120 semi: false singleQuote: true trailingComma: "all" bracketSpacing: false jsxBracketSameLine: true arrowParens: "avoid" insertPragma: true tabWidth: 4 useTabs: false };.eslintignoreESLint忽略检查文件node_modules *.md .vscod
环境
node: v16.15.1
npm: 8.12.1
本文项目地址:https://gitee.com/zhengqingya/small-tools
文档更新地址:https://gitee.com/zhengqingya/java-developer-document
- Vue3
- Vite2
- TypeScript
- Vue Router
- Element Plus
- Pinia
创建项目
npm init vite@latest small-tools-web -- --template vue-ts
在这里插入图片描述
二、编码规范配置ESLint:编码规范检查 Prettier:代码格式化工具
1、安装# eslint => ESLint的核心代码
# @typescript-eslint/parser => ESLint的解析器,用于解析ts,从而检查和规范ts代码
# @typescript-eslint/eslint-plugin => 一个ESLint插件,包含了各类定义好的检测ts代码的规范
# eslint-plugin-vue => vue3的代码校验
# prettier => prettier插件的核心代码
# eslint-config-prettier => 解决ESLint中的样式规范和prettier中样式规范的冲突,以prettier的样式规范为准,使ESLint中的样式规范自动失效
# eslint-plugin-prettier => 将prettier作为ESLint规范来使用
cnpm install --save-dev eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin eslint-plugin-vue prettier eslint-config-prettier eslint-plugin-prettier
2、配置.eslintrc.js配置文件
https://eslint.org/docs/latest/user-guide/configuring/configuration-files#configuration-file-formats https://eslint.vuejs.org/rules
module.exports = {
parser: '@typescript-eslint/parser' //定义ESLint的解析器
extends: [
'prettier/@typescript-eslint' // 使@typescript-eslint中的样式规范失效,遵循prettier中的样式规范
'plugin:prettier/recommended' // 使用prettier中的样式规范,且如果使ESLint会检测prettier的格式问题,同样将格式问题以error的形式抛出
]
settings: {
"react": {
"pragma": "React"
"version": "detect"
}
}
parserOptions: {
"ecmaVersion": 2019
"sourceType": 'module'
"ecmaFeatures": {
jsx: true
}
}
env: {
//指定代码的运行环境
browser: true
node: true
}
};
.prettierrc.js配置文件
https://prettier.io/docs/en/configuration.html
module.exports = {
printWidth: 120
semi: false
singleQuote: true
trailingComma: "all"
bracketSpacing: false
jsxBracketSameLine: true
arrowParens: "avoid"
insertPragma: true
tabWidth: 4
useTabs: false
};
.eslintignoreESLint忽略检查文件
node_modules
*.md
.vscode
.idea
dist
/public
.eslintrc.js
src/assets
.prettierignorePrettier忽略格式化文件
/dist/*
/node_modules/**
**/*.svg
/public/*
3、代码检查 & 自动修复 & 自动格式化
package.Json的scripts中添加
--ext: 配置要检查文件的后缀 eslint --ext .js --ext .ts --ext .vue src: 只检查src文件夹下的js/ts/vue文件
"scripts": {
"lint": "eslint --ext .js --ext .ts --ext .vue src"
"lint-fix": "eslint --ext .js --ext .ts --ext .vue src --fix"
"prettier": "prettier --write ."
}
# 代码检查
cnpm run lint
# 自动修复 tips: 部分代码修复可能会影响功能,所以不会自动修复,会给出提示
cnpm run lint-fix
# prettier 自动格式化
cnpm run prettier
4、统一代码风格
需安装插件EditorConfig for VS Code
.editorconfig
# https://editorconfig.org
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
[*.md]
insert_final_newline = false
trim_trailing_whitespace = false
三、Vite配置1、配置路径别名
vite.config.ts
import * as path from 'path';
export default defineConfig({
resolve: {
// 配置路径别名
alias: [
// @代替src
{
find: '@'
replacement: path.resolve('./src')
}
]
}
})
解决:找不到模块“path”或其相应的类型声明
# 安装node声明文件
cnpm i @types/node --save-dev
2、环境变量
.env.dev
# 开发环境
NODE_ENV='dev'
# 为了防止意外地将一些环境变量泄漏到客户端,只有以 VITE_ 为前缀的变量才会暴露给经过 vite 处理的代码。
# ts中通过`import.meta.env.VITE_APP_BASE_API`取值
VITE_APP_PORT = 5173
VITE_APP_BASE_API = '/dev-api'
3、反向代理解决跨域问题 & 配置项目运行端口
vite.config.ts
import { defineConfig loadEnv } from 'vite'
export default defineConfig(({ mode }) => {
// 获取`.env`环境配置文件
const env = loadEnv(mode process.cwd());
return {
server: {
host: 'localhost'
port: Number(env.VITE_APP_PORT)
// 运行时自动打开浏览器
// open: true
proxy: {
[env.VITE_APP_BASE_API]: {
target: 'http://localhost:1218'
changeOrigin: true
rewrite: path =>
path.replace(new RegExp('^' env.VITE_APP_BASE_API) '')
}
}
}
}
})
4、引入scss全局变量
vite.config.ts
export default defineConfig(({ mode }) => {
return {
// 引入scss全局变量
css: {
preprocessorOptions: {
scss: {
additionalData: `@import "@/styles/app-theme.scss";`
}
}
}
}
})
src/styles/app-theme.scss
$dark_main_color: #021b32;
四、TypeScript配置tsconfig.json
通过vite创建项目后的默认配置
{
"compilerOptions": {
"target": "ESNext"
"useDefineForClassFields": true
"module": "ESNext"
"moduleResolution": "Node"
"strict": true
"jsx": "preserve"
"sourceMap": true
"resolveJsonModule": true
"isolatedModules": true
"esModuleInterop": true
"lib": ["ESNext" "DOM"]
"skipLibCheck": true
}
"include": ["src/**/*.ts" "src/**/*.d.ts" "src/**/*.tsx" "src/**/*.vue"]
"references": [{ "path": "./tsconfig.node.json" }]
}
解决:找不到模块“@/router”或其相应的类型声明。
tsconfig.json新增如下配置
"baseUrl": "."
"paths": {
"@/*": [ "src/*" ]
}
解决:template中使用$store报错
vite-env.d.ts
import { Store } from '@/store'
declare module '@vue/runtime-core' {
interface ComponentCustomProperties {
$store: Store;
}
}
五、package.json
package.json
{
"name": "small-tools-web"
"private": true
"version": "1.0.1"
"type": "module"
"scripts": {
// 运行
"dev": "vite --mode dev"
// 构建生成 dist 文件夹
"build:prod": "vue-tsc --noEmit && vite build --mode prod"
// 在本地启动一个静态 Web 服务器,将 dist 文件夹运行在 http://localhost:8080
"preview": "vite preview --port 8080 --mode prod"
// eslint检查
"lint": "eslint --ext .js --ext .ts --ext .vue src"
// eslint自动修复
"lint-fix": "eslint --ext .js --ext .ts --ext .vue src --fix"
// prettier自动格式化代码
"prettier": "prettier --write ."
}
"dependencies": {
"vue": "^3.2.37"
"vue-router": "^4.1.2"
}
"devDependencies": {
"@types/node": "^18.0.5"
"@typescript-eslint/eslint-plugin": "^5.30.6"
"@typescript-eslint/parser": "^5.30.6"
"@vitejs/plugin-vue": "^3.0.0"
"eslint": "^8.20.0"
"eslint-config-prettier": "^8.5.0"
"eslint-plugin-prettier": "^4.2.1"
"eslint-plugin-vue": "^9.2.0"
"prettier": "^2.7.1"
"typescript": "^4.6.4"
"vite": "^3.0.0"
"vue-tsc": "^0.38.4"
}
}
# devDependencies: 里面的插件只用于开发环境,不用于生产环境
# dependencies: 需要发布到生产环境的
# 写入到 dependencies 对象
npm i module_name -S => npm install module_name --save
# 写入到 devDependencies 对象
npm i module_name -D => npm install module_name --save-dev
六、Sass
安装
cnpm install sass --save-dev
七、Vue Router
https://router.vuejs.org/zh
1、安装cnpm install vue-router@4
2、入门配置
src/router/index.ts
import {createRouter createWebHashHistory} from 'vue-router';
// 静态路由
export const routes = [
{
path: '/login'
component: () => import('@/views/login/index.vue')
}
{
path: '/404'
component: () => import('@/views/error-page/404.vue')
}
];
// 创建路由
const router = createRouter({
history: createWebHashHistory()
routes
});
export default router;
src/main.ts
// ****** ↓↓↓ 路由 ↓↓↓ ******
import router from '@/router';
const app = createApp(App);
app.use(router);
app.mount('#app')
src/views/error-page/404.vue
<template>
<h1>404</h1>
</template>
src/App.vue
<template>
<!-- 路由出口 -->
<!-- 路由匹配到的组件将渲染在这里 -->
<router-view/>
</template>
访问http://ip:端口/#/404
八、Element Plushttps://element-plus.gitee.io/zh-CN
1、安装cnpm install element-plus --save
cnpm install @element-plus/icons-vue
2、配置
main.ts
// ****** ↓↓↓ element-plus ↓↓↓ ******
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
// 注册所有图标
for (const [key component] of Object.entries(ElementPlusIconsVue)) {
app.component(key component)
}
const app = createApp(App)
app.use(ElementPlus)
app.mount('#app')
3、Volar 支持
tsconfig.json中通过compilerOptions.types指定全局组件类型
{
"compilerOptions": {
"types": [
"element-plus/global"
]
}
}
4、demo
<template>
<el-button link>按钮</el-button>
<el-icon class="is-loading">
<Loading/>
</el-icon>
<div>
<el-button :icon="Search" circle/>
<el-button type="primary" :icon="Edit" circle/>
<el-button type="success" :icon="Check" circle/>
<el-button type="info" :icon="Message" circle/>
<el-button type="warning" :icon="Star" circle/>
<el-button type="danger" :icon="Delete" circle/>
</div>
</template>
<script lang="ts" setup>
import { Check Delete Edit Message Search Star } from '@element-plus/icons-vue'
</script>
九、自定义样式
main.ts
import '@/styles/index.scss';
src/styles/index.scss
@import './element-plus-theme';
body {
background-color: #021b32;
}
src/styles/element-plus-theme.scss
// ****** ↓↓↓ 覆盖 element-plus 的样式 ↓↓↓ ******
// 按钮
.el-button--text {
// background-color: #8f6732 !important;
margin-left: 3px;
border: none !important;
}
十、Pinia
https://pinia.vuejs.org
1、安装cnpm install pinia --save
2、配置
main.ts
const app = createApp(App)
// pinia
import { createPinia } from 'pinia';
const pinia = createPinia()
app.use(pinia)
// store
import useStore from "@/store";
app.config.globalProperties.$store = useStore();
app.mount('#app')
3、使用
src/store/index.ts
import useAppStore from './modules/app';
const useStore = () => ({
app: useAppStore()
});
export default useStore;
src/store/modules/app.ts
import { AppState } from '@/types/store/app';
import { localStorage } from '@/utils/storage';
import { defineStore } from 'pinia';
const useAppStore = defineStore({
id: 'app'
state: (): AppState => ({
name: localStorage.get('name') || 'Small Tools'
})
actions: {
setName(name: string) {
this.name = name;
localStorage.set('name' name);
}
}
});
export default useAppStore;
src/utils/storage.ts
/**
* window.localStorage => 浏览器永久存储,用于长久保存整个网站的数据,保存的数据没有过期时间,直到手动去删除。
*/
export const localStorage = {
set(key: string val: any) {
window.localStorage.setItem(key JSON.stringify(val));
}
get(key: string) {
const json: any = window.localStorage.getItem(key);
return JSON.parse(json);
}
remove(key: string) {
window.localStorage.removeItem(key);
}
clear() {
window.localStorage.clear();
}
};
/**
* window.sessionStorage => 浏览器本地存储,数据保存在当前会话中,在关闭窗口或标签页之后将会删除这些数据。
*/
export const sessionStorage = {
set(key: string val: any) {
window.sessionStorage.setItem(key JSON.stringify(val));
}
get(key: string) {
const json: any = window.sessionStorage.getItem(key);
return JSON.parse(json);
}
remove(key: string) {
window.sessionStorage.removeItem(key);
}
clear() {
window.sessionStorage.clear();
}
};
页面引用
<template>
<p>store: {{ name }}</p>
<p>store: {{ app.name }}</p>
<p>store: {{ $store.app.name }}</p>
<el-button @click="changeStore('666')">change store</el-button>
</template>
<script lang="ts" setup>
import { storeToRefs } from 'pinia'
import useStore from "@/store";
const { app } = useStore()
// const name = ref(app.name)
// 响应式
const { name: name } = storeToRefs(app)
function changeStore(value: string) {
app.setName(value)
}
</script>
十一、Axios和API封装
http://www.axios-js.com/zh-cn/docs
1、安装cnpm install axios --save
2、axios工具封装
src/utils/request.ts
import axios { AxiosRequestConfig AxiosResponse } from 'axios';
import { ElMessage ElMessageBox } from 'element-plus';
import { localStorage } from '@/utils/storage';
import useStore from '@/store';
// 创建axios实例
const service = axios.create({
baseURL: import.meta.env.VITE_APP_BASE_API
// 请求超时时间:50s
timeout: 50000
headers: { 'Content-Type': 'application/json;charset=utf-8' }
});
// 请求拦截器
service.interceptors.request.use(
(config: AxiosRequestConfig) => {
if (!config.headers) {
throw new Error(
`Expected 'config' and 'config.headers' not to be undefined`
);
}
const { user } = useStore();
if (user.token) {
// 授权认证
config.headers.Authorization = user.token;
}
// 租户ID
config.headers['TENANT_ID'] = '1'
return config;
}
(error) => {
return Promise.reject(error);
}
);
// 响应拦截器
service.interceptors.response.use(
(response: AxiosResponse) => {
const res = response.data;
const { code msg } = res;
if (code === 200) {
return res;
} else {
// token过期
if (code === -1) {
ElMessageBox.confirm("您的登录账号已失效,请重新登录" {
confirmButtonText: "再次登录"
cancelButtonText: "取消"
type: "warning"
}).then(() => {
// 清除浏览器全部缓存
localStorage.clear();
// 跳转登录页
window.location.href = '/';
location.reload();
});
} else {
ElMessage({
message: msg || '系统出错'
type: 'error'
duration: 5 * 1000
});
}
return Promise.reject(new Error(msg || 'Error'));
}
}
(error) => {
const { msg } = error.response.data;
// 未认证
if (error.response.status === 401) {
ElMessageBox.confirm("您的登录账号已失效,请重新登录" {
confirmButtonText: "再次登录"
cancelButtonText: "取消"
type: "warning"
}).then(() => {
// 清除浏览器全部缓存
localStorage.clear();
// 跳转登录页
window.location.href = '/';
location.reload();
});
} else {
ElMessage({
message: "网络异常,请稍后再试!"
type: "error"
duration: 5 * 1000
});
return Promise.reject(new Error(msg || 'Error'));
}
}
);
// 导出实例
export default service;
3、api封装
src/api/index.ts
// 拿到所有api
const modulesFiles = import.meta.globEager('./*/*.*');
const modules: any = {};
for (const key in modulesFiles) {
const moduleName = key.replace(/(.*\/)*([^.] ).*/gi '$2');
const value: any = modulesFiles[key];
if (value.default) {
// 兼容js
modules[moduleName] = value.default;
} else {
// 兼容ts
modules[moduleName] = value;
}
}
// console.log(666 modules);
export default modules;
main.ts
const app = createApp(App);
// 配置全局api
import api from '@/api'
app.config.globalProperties.$api = api;
app.mount('#app')
4、api调用demo
src/api/system/sys_login.ts
import { Captcha } from '@/types/api/system/login';
import request from '@/utils/request';
import { AxiosPromise } from 'axios';
// 获取验证码
export function getCaptcha(): AxiosPromise<Captcha> {
return request({
url: '/captcha?t=' new Date().getTime().toString()
method: 'get'
});
}
src/types/api/system/login.d.ts
// 验证码类型声明
export interface Captcha {
img: string;
uuid: string;
}
src/views/login/index.vue
<template>
<p>Hello...</p>
</template>
<script lang="ts" setup>
import { getCurrentInstance } from 'vue';
// 组件实例
const { proxy }: any = getCurrentInstance();
// 获取验证码
async function handleCaptcha() {
const res = await proxy.$api.sys_login.getCaptcha()
console.log('res:' res);
}
handleCaptcha()
</script>
十二、全局组件1、全局组件注册
src/components/index.ts
const modulesFiles = import.meta.globEager('./*/*.vue');
const modules: any = {};
for (const key in modulesFiles) {
const moduleName = key.replace(/(.*\/)*([^.] ).*/gi '$2');
const value: any = modulesFiles[key];
modules[moduleName] = value.default;
}
// console.log(666 modules);
export default modules;
main.ts
const app = createApp(App);
// 全局组件注册
import myComponent from '@/components/index'
Object.keys(myComponent).forEach((key) => {
app.component(key myComponent[key])
})
app.mount('#app')
2、组件示例
src/components/base/BaseNoData.vue
<template>
<div class="no-data" :class="{ 'no-data-dark': true }">
<slot>暂无数据</slot>
</div>
</template>
<style lang="scss" scoped>
.no-data {
padding: 100px 0;
background: #fff;
font-size: 28px;
color: #999;
text-align: center;
&.no-data-dark {
background: $dark_main_color;
color: #fff;
}
}
</style>
引用
<base-no-data>请先选择数据</base-no-data>
今日分享语句: 愿我可以一直沉迷于学习无法自拔。