本次构建的我的项目技术栈: Vite+Vue3+Volar+Pinia+Vue-Router+TS+JSX+Scss+Script setup

  1. Vite: 下一代前端开发与构建工具,极速的热更新开发体验★★★★★
  2. Vue3.2: 超多的黑魔法
  3. Volar: Vue3必备开发神器
  4. Pinia: 新一代的轻量状态管理器
  5. Vue-Router4: Vue3的官网路由
  6. <Script setup> 语法糖,更简洁的语法、更好的开发体验
  7. Vue3 + TS + JSX,一个重度React + TS + JSX用户的Vue我的项目解决方案

    应用vite创立vue3我的项目

# npm 6.xnpm 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# yarnyarn create vite my-vue-app --template vue-ts# pnpmpnpm create vite my-vue-app -- --template vue-ts

配置VSCode开发环境

  1. 禁用 vetur插件
// 通过门路 `Code -> Preferences -> Settings` 中关上vscode配置文件"vetur.validation.template": false
  1. 装置 Volar相干插件
    Vue Language FeaturesTypeScript Vue Plugin
  2. 装置 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    }  ],
  1. 装置 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)为准,所以咱们为我的项目创立适宜我的项目的自定义规定:

  1. 我的项目装置 prettier
npm install --save-dev --save-exact prettier
  1. 在我的项目根门路下创立我的项目的格式化配置文件 .prettierrc.json,配置简略内容,更具体的配置规定阐明
// .prettierrc.json{  "singleQuote": true,  "semi": true,  "printWidth": 200}
  1. 我的项目装置 ESlint
npm install --save-dev eslint eslint-plugin-vue eslint-config-prettier
  1. 在我的项目根目录创立ESlint配置文件 .eslintrc.js,应用vue3举荐的规定
// .eslintrc.jsmodule.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',  },};
  1. package.json增加脚本命令
 "scripts": {    // ...    "lint": "eslint --ext .js,.vue --ignore-path .gitignore --fix src",    "format": "prettier . --write",  },
  1. 我的项目装置 @typescript-eslint/parser
npm install @typescript-eslint/parser
  1. 如果心愿eslint谬误间接在浏览器上显示,能够装置插件 vite-plugin-eslint
npm i --save-dev vite-plugin-eslint
  1. vite.config.ts中配置 vite-plugin-eslint
// vite.congfig.tsimport { defineConfig } from 'vite';import vue from '@vitejs/plugin-vue';import eslintPlugin from 'vite-plugin-eslint';export default defineConfig({  plugins: [vue(), eslintPlugin()],});

其它我的项目工具的配置

  1. 装置 vue-router
npm install vue-router@4
  1. 创立 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})
  1. main.ts中启用 router
import { createApp } from 'vue'import App from './App.vue'import {router} from './router'createApp(App).use(router).mount('#app')
  1. 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>
  1. 装置 sass
npm install sass -Dnpm install node-sass -Dnpm install sass-loader -D
  1. 装置 @types/node
npm install @types/node -D
  1. 配置门路别名
    vite.config.ts增加配置
// vite.congfig.tsimport { 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" }]}
  1. 装置 pinia轻量状态管理器
npm install pinia
  1. 创立 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 }})
  1. 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')
  1. 装置 @vitejs/plugin-vue-jsx
npm install @vitejs/plugin-vue-jsx -D
  1. vite.config.ts中启用 jsx
// vite.congfig.tsimport { 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")    }  }});
  1. 批改 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" }]}
  1. 革新 App.vueApp.tsx,并在 main.ts引入
// App.tsximport { 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.tsimport { createApp } from 'vue'import App from './App'import {router} from './router'import { createPinia } from 'pinia'createApp(App).use(router).use(createPinia()).mount('#app')
  1. 我的项目装置 axios
npm install --save axios vue-axios
  1. 封装 axios 结构一个通用的 request 办法
// src/request/base.tslet BASE_URL = ''const TIME_OUT = 30000if (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.tsimport 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.tsimport 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.tsimport 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>
  1. 配置脚本启动参数命令 --mode 环境变量值

    "dev": "vite --host --mode development",

我的项目打包的相干问题

  1. 打包遇到对node_modules中的类型进行了查看,能够批改npm build 命令, 减少 --skipLibCheck 参数,如下:

       "build": "vue-tsc --noEmit --skipLibCheck && vite build",
  2. 执行打包命令时,vue-tsc 语法查看报错时,请查看 IDEVolar插件版本和 vue-tsc 版本是否兼容,如遇到新语法不兼容时可降级 vue-tscVolar 的版本,我这里呈现的是 vue-tsc 无奈辨认自定义指令,然而 Volar 是反对的(开发过程不报错),所以我将 vue-tsc 降级到了 0.33.9 版本。
  3. 有时候 vue-tsc 报打包谬误,查看 node 版本是否为 14.0.0+ ,应用最新的构建工具倡议把 node 版本升级到比拟新的版本。