【Vue3+Ts+Vite】使用Vite与TS构建Vue3项目

Vue3+Ts+Vite

今天学习一下如何初始化一个 Vue3 + Ts + Vite 的项目

学习地址:开始 {#getting-started} | Vite中文网 (vitejs.cn)

与时俱进 开始用全新的技术

本文包含以下内容:基础框架的搭建,别名配置,vue-router配置,pinia配置,axios配置,ESLint配置。

安装

# Vite 需要 Node.js 版本 >= 12.0.0
npm init vite@latest

# 根据相关问题进行回答
# 需要选择 框架以及使用语言 配置项目名

# 使用附加命令创建指定项目 无需再选择
npm init vite@latest vue-ts-viet-prj --template vue ts
# npm 7+, 需要额外的双横线:
npm init vite@latest my-vue-app -- --template vue ts

# 进入项目目录
cd vite-project
# 安装依赖
npm install
# 运行项目
npm run dev

配置别名

  • 习惯Vue2脚手架中用 @符号指向Src的习惯了 在Vite中配置一下
  • 需要修改 vite.config.ts tsconfig.json
// viet.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { resolve } from 'path'

export default defineConfig({
    plugins: [vue()],
    resolve: {
        alias: {
            // 配置别名指向src目录
            "@": resolve(__dirname, 'src'),
        },
        // 使用别名的文件后缀
        extensions: ['.js', '.json', '.ts']
    }
})
// tsconfig.json
{
    "compilerOptions": {
        "target": "ESNext",
        "useDefineForClassFields": true,
        "module": "ESNext",
        "moduleResolution": "Node",
        "strict": true,
        "jsx": "preserve",
        "resolveJsonModule": true,
        "isolatedModules": true,
        "esModuleInterop": true,
        "lib": ["ESNext", "DOM"],
        "skipLibCheck": true,
        "noEmit": true,
        // 加入以下配置项
        "baseUrl": ".", // 用于设置解析非相对模块名称的基本目录,相对模块不会受到baseUrl的影响
        "paths": { // 用于设置模块名到基于baseUrl的路径映射
            "@/*": ["src/*"]
        }
    },
    "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"],
    "references": [{ "path": "./tsconfig.node.json" }],
}

Vue-Router

# Vue-Router 4+ 版本支持 Vue3
npm install vue-router@4
  • 新建 目录/文件夹 src/router/index.ts
// index.ts
import { createRouter,createWebHashHistory,RouteRecordRaw } from 'vue-router';

// 添加类型校验
const routes: RouteRecordRaw[] = [
    {
        path: "/",
        name: "home",
        component: ()=>import('@/components/HelloWorld.vue')
    },
    {
        path: "/logIn",
        name: "logIn",
        component: ()=>import('@/view/LogIn.vue')
    },
]

// 创建router
const router = createRouter({
    // 配置为Hash模式
    history: createWebHashHistory(),
    // 配置toures
    routes,
    // 路由跳转时返回顶部
    scrollBehavior () {
        return {top: 0}
    }
})

// 设置前置路由守卫
router.beforeEach((to, from, next) => {
    next()
})

// 设置后置路由守卫
router.afterEach((to, from, failure) => {
    
})

export { router }
// main.ts
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
// 引入router
import { router } from './router'

const app = createApp(App);

// 挂载到 Vue 实例
app.use(router)
app.mount("#app");



Pinia

npm install pinia
  • 新建 目录/文件 src/store/index.ts
// index.ts
/**
 *    1. 定义容器并导出
 *    2. 使用容器中的state
 *    3. 修改容器中的state
 *    4. 使用容器中的action
 */
import { defineStore } from "pinia";

/**
 * 1. 定义容器并导出
 * 参数一: 容器ID, 唯一, 将来 Pinia 会把所有的容器挂载到根容器
 * 参数二: 选项对象
 * 返回值: 函数, 调用的时候要空参调用, 返回容器实例
 */
export const mainStore = defineStore('main', {
    /**
     * 类似组件的 data, 用于存储全局的的状态
     * 注意:
     *    1.必须是函数, 为了在服务端渲染的时候避免交叉请求导致的数据交叉污染
     *    2.必须是箭头函数, 为了更好的 TS 类型推导
     */
    state: () => {
        return {
            state: {
                token: true
            }
        }
    },
    /**
     * 类似组件的 computed, 用来封装计算属性, 具有缓存特性
     */
    getters: {

    },
    /**
     * 类似组件的 methods, 封装业务逻辑, 修改state
     * 注意: 里面的函数不能定义成箭头函数(函数体中会用到this)
     */
    actions: {

    }
})
// main.ts
import { createApp } from 'vue'
import App from './App.vue'
import {createPinia} from 'pinia'
// 创建 Pinia 实例
const pinia = createPinia()
// 创建 Vue 实例
const app = createApp(App)
// 挂载到 Vue 根实例
app.use(pinia)
app.mount('#app')

Axios

npm insall axios
  • 新建 目录/文件 src/utils/request.ts src/api/xxx.ts
// request.ts

import axios from 'axios'
// 导入pinia
import { mainStore } from '@/store'
const store = mainStore()

// 创建axios
const $http = axios.create({
    //设置默认请求地址
    baseURL: 'http://localhost:8080',
    //设置请求超时时间
    timeout:5000,
    //设置请求头
    headers: {
        'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8'
    }
})


// 请求拦截器
$http.interceptors.request.use(config => {
    // 验证 token
    const token = store.state.token;
    if (config.headers!= undefined) config.headers.Authorization = token
    return config;
},error => {
    return Promise.reject(error);
})

//响应拦截
$http.interceptors.response.use(res => {

    // 状态码为200正常返回
    if (res.status === 200) {
        return Promise.resolve(res);
    } else {
        return Promise.reject(res);
    }
}, error => {
    return Promise.reject(error);
})

// 导出封装的axios
export default $http
// api/user.ts
import request from "@/utils/request"

export function login(data: object) {
    return request({
        url: '/user/login',
        method: 'post',
        data
    })
}

export function getInfo(token: object) {
    return request({
        url: '/user/info',
        method: 'get',
        params: { token }
    })
}

export function logout() {
    return request({
        url: '/user/logout',
        method: 'post'
    })
}

ESLint

参考文章:在 Vue3 + Vite + TS 项目中配置 ESLint,让 VSCode 编辑器自动修复错误 - 知乎 (zhihu.com)

  • 使用ESLint进行代码规范 保存就更改 看起来就舒服多了
# 安装
# 指定一下版本号 不然会有很多不兼容以及奇奇怪怪的问题
npm install [email protected] [email protected] vue-eslint-parser @typescript-eslint/parser @typescript-eslint/eslint-plugin [email protected] eslint-plugin-import -D
  • 添加 .eslintrc.js 配置文件 手动添加即可
  • 写入以下代码
module.exports = {
  root: true,
  globals: {
    defineEmits: 'readonly',
    defineProps: 'readonly',
  },
  extends: [
    'plugin:@typescript-eslint/recommended',
    'plugin:vue/vue3-recommended',
    'airbnb-base',
  ],
  parserOptions: {
    parser: '@typescript-eslint/parser',
    ecmaVersion: 2020,
  },
  rules: {
  'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off', // 禁用 debugger
  'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off', // 禁用 console
  'no-bitwise': 'off', // 禁用按位运算符
  'no-tabs': 'off', // 禁用 tab
  'array-element-newline': ['error', 'consistent'], // 强制数组元素间出现换行
  indent: [
    'error',
    2,
    { MemberExpression: 0, SwitchCase: 1, ignoredNodes: ['TemplateLiteral'] },
  ], // 强制使用一致的缩进
  quotes: ['error', 'single'], // 强制使用一致的反勾号、双引号或单引号
  'comma-dangle': ['error', 'always-multiline'], // 要求或禁止末尾逗号
  'object-curly-spacing': ['error', 'always'], // 强制在大括号中使用一致的空格
  'max-len': ['error', 120], // 强制一行的最大长度
  'no-new': 'off', // 禁止使用 new 以避免产生副作用
  'linebreak-style': 'off', // 强制使用一致的换行风格
  'import/extensions': 'off', // 确保在导入路径中统一使用文件扩展名
  'eol-last': 'off', // 要求或禁止文件末尾存在空行
  'no-shadow': 'off', // 禁止变量声明与外层作用域的变量同名
  'no-unused-vars': 'warn', // 禁止出现未使用过的变量
  'import/no-cycle': 'off', // 禁止一个模块导入一个有依赖路径的模块回到自己身上
  'arrow-parens': 'off', // 要求箭头函数的参数使用圆括号
  semi: ['error', 'never'], // 要求或禁止使用分号代替 ASI
  eqeqeq: 'off', // 要求使用 === 和 !==
  'no-param-reassign': 'off', // 禁止对 function 的参数进行重新赋值
  'import/prefer-default-export': 'off', // 如果模块只输入一个名字,则倾向于默认输出
  'no-use-before-define': 'off', // 禁止在变量定义之前使用它们,则倾向于默认输出
  'no-continue': 'off', // 禁用 continue 语句
  'prefer-destructuring': 'off', // 优先使用数组和对象解构
  'no-plusplus': 'off', // 禁用一元操作符 ++ 和 --
  'prefer-const': 'warn', // 要求使用 const 声明那些声明后不再被修改的变量
  'global-require': 'off', // 要求 require() 出现在顶层模块作用域中
  'no-prototype-builtins': 'off', // 禁止直接调用 Object.prototypes 的内置属性
  'consistent-return': 'off', // 要求 return 语句要么总是指定返回的值,要么不指定
  'one-var-declaration-per-line': 'off', // 要求或禁止在变量声明周围换行
  'one-var': 'off', // 强制函数中的变量要么一起声明要么分开声明
  'import/named': 'off', // 确保命名导入与远程文件中的命名导出相对应
  'object-curly-newline': 'off', // 强制大括号内换行符的一致性
  'default-case': 'off', // 要求 switch 语句中有 default 分支
  'no-trailing-spaces': 'off', // 禁用行尾空格
  'func-names': 'off', // 要求或禁止使用命名的 function 表达式
  radix: 'off', // 强制在 parseInt() 使用基数参数
  'no-unused-expressions': 'off', // 禁止出现未使用过的表达式
  'no-underscore-dangle': 'off', // 禁止标识符中有悬空下划线
  'no-nested-ternary': 'off', // 禁用嵌套的三元表达式
  'no-restricted-syntax': 'off', // 禁用特定的语法
  'no-await-in-loop': 'off', // 禁止在循环中出现 await
  'import/no-extraneous-dependencies': 'off', // 禁止使用外部包
  'import/no-unresolved': 'off', // 确保导入指向一个可以解析的文件/模块
  'template-curly-spacing': ['error', 'always'], // 要求或禁止模板字符串中的嵌入表达式周围空格的使用
  '@typescript-eslint/no-var-requires': 'off', // 除import语句外,禁止使用require语句
  '@typescript-eslint/no-empty-function': 'off', // 不允许空函数
  '@typescript-eslint/no-explicit-any': 'off', // 禁止使用 any 类型
  'guard-for-in': 'off', // 要求 for-in 循环中有一个 if 语句
  'class-methods-use-this': 'off', // 强制类方法使用 this
  'vue/html-indent': ['error', 2], // 在