本次构建的我的项目技术栈: Vite
+Vue3
+Volar
+Pinia
+Vue-Router
+TS
+JSX
+Scss
+Script setup
- Vite: 下一代前端开发与构建工具, 极速的热更新开发体验★★★★★
- Vue3.2: 超多的黑魔法
- Volar: Vue3 必备开发神器
- Pinia: 新一代的轻量状态管理器
- Vue-Router4: Vue3 的官网路由
<Script setup>
语法糖,更简洁的语法、更好的开发体验-
Vue3 + TS + JSX
,一个重度React + TS + JSX
用户的Vue
我的项目解决方案应用 vite 创立 vue3 我的项目
# npm 6.x
npm create vite@latest my-vue-app --template vue-ts
# npm 7+, extra double-dash is needed:
npm create vite@latest my-vue-app -- --template vue-ts
# yarn
yarn create vite my-vue-app --template vue-ts
# pnpm
pnpm create vite my-vue-app -- --template vue-ts
配置 VSCode 开发环境
- 禁用
vetur
插件
// 通过门路 `Code -> Preferences -> Settings` 中关上 vscode 配置文件
"vetur.validation.template": false
- 装置
Volar
相干插件Vue Language Features
和TypeScript Vue Plugin
- 装置
eslint
插件, 并对 VSCode 进行配置
"editor.formatOnSave": true,
"eslint.autoFixOnSave": true,
"eslint.validate": [
"javascript",
"javascriptreact",
{
"language": "html",
"autoFix": true
},
{
"language": "vue",
"autoFix": true
},
{
"language": "typescript",
"autoFix": true
},
{
"language": "typescriptreact",
"autoFix": true
}
],
- 装置
prettier
插件, 配置通用的prettier
规定
/* prettier 的配置 */
"prettier.printWidth": 300, // 超过最大值换行
"prettier.tabWidth": 2, // 缩进字节数
"prettier.useTabs": false, // 缩进不应用 tab,应用空格
"prettier.semi": true, // 句尾增加分号
"prettier.trailingComma": "none", // #在对象或数组最初一个元素前面不加逗号
"prettier.singleQuote": true, // 应用单引号代替双引号
工具配置
vscode 里的 ESlint 插件和 Prettier 插件会优先以我的项目的配置文件(如.prettierrc.json)为准, 所以咱们为我的项目创立适宜我的项目的自定义规定:
- 我的项目装置
prettier
npm install --save-dev --save-exact prettier
- 在我的项目根门路下创立我的项目的格式化配置文件
.prettierrc.json
, 配置简略内容, 更具体的配置规定阐明
// .prettierrc.json
{
"singleQuote": true,
"semi": true,
"printWidth": 200
}
- 我的项目装置
ESlint
npm install --save-dev eslint eslint-plugin-vue eslint-config-prettier
- 在我的项目根目录创立 ESlint 配置文件
.eslintrc.js
, 应用 vue3 举荐的规定
// .eslintrc.js
module.exports = {
env: {
node: true,
'vue/setup-compiler-macros': true // 新增的配置
},
extends: ['eslint:recommended', 'plugin:vue/vue3-recommended', 'prettier'],
rules: {
'vue/require-default-prop': 'off', // 敞开默认值
"@typescript-eslint/no-explicit-any": "off", // 敞开禁用 any
},
parser: 'vue-eslint-parser',
parserOptions: {
parser: '@typescript-eslint/parser',
ecmaVersion: 2020,
sourceType: 'module',
},
};
- 在
package.json
增加脚本命令
"scripts": {
// ...
"lint": "eslint --ext .js,.vue --ignore-path .gitignore --fix src",
"format": "prettier . --write",
},
- 我的项目装置
@typescript-eslint/parser
npm install @typescript-eslint/parser
- 如果心愿 eslint 谬误间接在浏览器上显示,能够装置插件
vite-plugin-eslint
npm i --save-dev vite-plugin-eslint
- 在
vite.config.ts
中配置vite-plugin-eslint
// vite.congfig.ts
import {defineConfig} from 'vite';
import vue from '@vitejs/plugin-vue';
import eslintPlugin from 'vite-plugin-eslint';
export default defineConfig({plugins: [vue(), eslintPlugin()],
});
其它我的项目工具的配置
- 装置
vue-router
npm install vue-router@4
- 创立
router/index.ts
门路配置文件
import {createRouter,createWebHashHistory} from "vue-router";
import Home from '../views/Home/index.vue'
import Login from '../views/Login/index.vue'
const routes = [{ path: "/", redirect: "/home"},
{
path: "/home",
name: "home",
component: Home
},
{
path: "/login",
name: "login",
component: Login
}
]
export const router = createRouter({history: createWebHashHistory(),
routes: routes
})
- 在
main.ts
中启用router
import {createApp} from 'vue'
import App from './App.vue'
import {router} from './router'
createApp(App).use(router).mount('#app')
- 在
App.vue
中应用
<script setup lang="ts">
</script>
<template>
<router-view />
</template>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
- 装置
sass
npm install sass -D
npm install node-sass -D
npm install sass-loader -D
- 装置
@types/node
npm install @types/node -D
- 配置门路别名
在vite.config.ts
增加配置
// vite.congfig.ts
import {defineConfig} from 'vite';
import vue from '@vitejs/plugin-vue';
import eslintPlugin from 'vite-plugin-eslint';
import {join} from "path";
export default defineConfig({plugins: [vue(), eslintPlugin()],
resolve: {
alias: {"@": join(__dirname, "src"),
"~": join(__dirname, "node_modules")
}
}
});
在 tsconfig.json
在配置
{
"compilerOptions": {
"target": "esnext",
"useDefineForClassFields": true,
"module": "esnext",
"moduleResolution": "node",
"strict": true,
"jsx": "preserve",
"sourceMap": true,
"resolveJsonModule": true,
"isolatedModules": true,
"esModuleInterop": true,
"lib": ["esnext", "dom"],
"baseUrl": ".",
"paths": {"@/*": ["src/*"]
}
},
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"],
"references": [{"path": "./tsconfig.node.json"}]
}
- 装置
pinia
轻量状态管理器
npm install pinia
- 创立
stores/user.ts
全局状态 store
import {defineStore} from 'pinia'
import {ref} from 'vue'
export const useUserStore = defineStore('counter', () => {const count = ref(0)
function increment() {count.value++}
return {count, increment}
})
- vue 启用
pinia
, 批改main.ts
代码
import {createApp} from 'vue'
import App from './App.vue'
import {router} from './router'
import {createPinia} from 'pinia'
createApp(App).use(router).use(createPinia()).mount('#app')
- 装置
@vitejs/plugin-vue-jsx
npm install @vitejs/plugin-vue-jsx -D
- 在
vite.config.ts
中启用jsx
// vite.congfig.ts
import {defineConfig} from 'vite';
import vue from '@vitejs/plugin-vue';
import eslintPlugin from 'vite-plugin-eslint';
import {join} from "path";
import vueJsx from "@vitejs/plugin-vue-jsx";
export default defineConfig({plugins: [vue(), eslintPlugin(), vueJsx()],
resolve: {
alias: {"@": join(__dirname, "src"),
"~": join(__dirname, "node_modules")
}
}
});
- 批改
tsconfig.json
规定,包含src
下所有的.tsx
文件
{
"compilerOptions": {
"target": "esnext",
"useDefineForClassFields": true,
"module": "esnext",
"moduleResolution": "node",
"strict": true,
"jsx": "preserve",
"sourceMap": true,
"resolveJsonModule": true,
"isolatedModules": true,
"esModuleInterop": true,
"lib": ["esnext", "dom"],
"baseUrl": ".",
"paths": {"@/*": ["src/*"]
}
},
"include": ["src/**/*.ts", "src/*.tsx", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"],
"references": [{"path": "./tsconfig.node.json"}]
}
- 革新
App.vue
为App.tsx
, 并在main.ts
引入
// App.tsx
import {defineComponent} from 'vue';
import {RouterLink, RouterView} from 'vue-router';
import './style/app.scss'
export default defineComponent({
name: 'App',
setup() {return () => (
<>
<div id="nav">
<RouterLink to="/home">Home</RouterLink> |
<RouterLink to="/login">Login</RouterLink>
</div>
<RouterView/>
</>
);
}
});
// main.ts
import {createApp} from 'vue'
import App from './App'
import {router} from './router'
import {createPinia} from 'pinia'
createApp(App).use(router).use(createPinia()).mount('#app')
- 我的项目装置
axios
npm install --save axios vue-axios
- 封装
axios
结构一个通用的request
办法
// src/request/base.ts
let BASE_URL = ''
const TIME_OUT = 30000
if (process.env.NODE_ENV === 'dev') {BASE_URL = '开发环境 IP'} else if (process.env.NODE_ENV === 'prod') {BASE_URL = '生产环境 IP'} else {BASE_URL = '其它环境 IP'}
export {BASE_URL, TIME_OUT}
// src/request/type.ts
import type {AxiosRequestConfig, AxiosResponse} from 'axios'
export interface LJRequestInterceptors<T = AxiosResponse> {requestInterceptor?: (config: AxiosRequestConfig) => AxiosRequestConfig
requestInterceptorCatch?: (error: any) => any
responseInterceptor?: (res: T) => T
responseInterceptorCatch?: (error: any) => any
}
export interface LJRequestConfig<T = AxiosResponse> extends AxiosRequestConfig {
showLoading?: boolean
interceptors?: LJRequestInterceptors<T>
}
// src/request/request.ts
import axios from 'axios'
import type {AxiosInstance} from 'axios'
import {LJRequestInterceptors, LJRequestConfig} from './type'
class LJRequest {
instance: AxiosInstance
interceptors?: LJRequestInterceptors
constructor(config: LJRequestConfig) {
// 创立 axios 实例
this.instance = axios.create(config)
// 保留根本信息
this.interceptors = config.interceptors
// 应用拦截器
// 从 config 钟取出的拦截器是对应的实例的拦截器
this.instance.interceptors.request.use(this.interceptors?.requestInterceptor, this.interceptors?.requestInterceptorCatch)
this.instance.interceptors.response.use(this.interceptors?.responseInterceptor, this.interceptors?.requestInterceptorCatch)
// 所有的实例都有的拦截器
this.instance.interceptors.request.use((config) => {console.log('所有的实例都有的拦截器: 申请拦挡胜利')
console.log(config)
return config
}, (err) => {console.log('所有的实例都有的拦截器: 申请拦挡失败')
return err
})
this.instance.interceptors.response.use((res) => {console.log('所有的实例都有的拦截器: 响应拦挡胜利')
return res.data
},
(err) => {console.log('所有的实例都有的拦截器: 响应拦挡失败')
// 例子: 判断不同 httpErrorCode 显示不同错误信息
if (err.response.status === 404) {console.log('404 谬误~')
}
return err
}
)
}
request<T>(config: LJRequestConfig<T>): Promise<T> {return new Promise((resolve, reject) => {
// 单个申请对申请 config 的解决
if (config.interceptors?.requestInterceptor) {config = config.interceptors.requestInterceptor(config)
}
this.instance
.request<any, T>(config)
.then((res) => {
// 单个申请对数据的解决
if (config.interceptors?.responseInterceptor) {res = config.interceptors.responseInterceptor(res)
}
console.log(res)
// 将后果返回进来
resolve(res)
})
.catch((err) => {reject(err)
})
})
}
get<T>(config: LJRequestConfig<T>): Promise<T> {return this.request<T>({ ...config, method: 'GET'})
}
post<T>(config: LJRequestConfig<T>): Promise<T> {return this.request<T>({ ...config, method: 'POST'})
}
delete<T>(config: LJRequestConfig<T>): Promise<T> {return this.request<T>({ ...config, method: 'DELETE'})
}
patch<T>(config: LJRequestConfig<T>): Promise<T> {return this.request<T>({ ...config, method: 'PATCH'})
}
}
export default LJRequest
// src/request/index.ts
import LJRequest from './request'
import {BASE_URL, TIME_OUT} from './base'
const ljRequest = new LJRequest({
baseURL: BASE_URL,
timeout: TIME_OUT,
interceptors: {requestInterceptor: (config) => {
let _config = {...config,}
_config.headers = {
...config.headers,
token: sessionStorage.getItem("token") as any,
app: 'visionary'
}
console.log('申请胜利拦挡')
return _config
},
requestInterceptorCatch: (err) => {console.log('申请失败拦挡')
return err
},
responseInterceptor: (config) => {console.log('响应胜利拦挡')
return config
},
responseInterceptorCatch: (err) => {console.log('响应失败拦挡')
return err
}
}
})
export default ljRequest
// src/views/index.vue
<script setup lang="ts">
import ljRequest from "@/request/index"
ljRequest.get({
url: '接口地址',
showLoading: true,
})
.then((res) => {console.log(res)
}).catch((err) => {console.log(err)
})
</script>
-
配置脚本启动参数命令
--mode 环境变量值
"dev": "vite --host --mode development",
我的项目打包的相干问题
-
打包遇到对 node_modules 中的类型进行了查看,能够批改 npm
build
命令, 减少--skipLibCheck
参数,如下:"build": "vue-tsc --noEmit --skipLibCheck && vite build",
- 执行打包命令时,
vue-tsc
语法查看报错时,请查看IDE
的Volar
插件版本和vue-tsc
版本是否兼容,如遇到新语法不兼容时可降级vue-tsc
或Volar
的版本,我这里呈现的是vue-tsc
无奈辨认自定义指令, 然而Volar
是反对的(开发过程不报错), 所以我将vue-tsc
降级到了0.33.9
版本。 - 有时候
vue-tsc
报打包谬误,查看node
版本是否为14.0.0+
, 应用最新的构建工具倡议把node
版本升级到比拟新的版本。