Vue3发布这么久了,还不会用Vite构建项目?

本次构建的项目技术栈: 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.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开发环境

  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.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',
  },
};
  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.ts
import { 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 -D
npm install node-sass -D
npm install sass-loader -D
  1. 安装 @types/node
npm install @types/node -D
  1. 配置路径别名
    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" }]
}
  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.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")
    }
  }
});
  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.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')
  1. 项目安装 axios
npm install --save axios vue-axios
  1. 封装 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>
  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 版本升级到比较新的版本。
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值