创建Vite-Vue项目

项目初始化

// 我用的npm 
npm init vite@latest
  • 安装如下流程进行安装
Need to install the following packages:
  create-vite@latest
Ok to proceed? (y) y
√ Project name: ... todos-list
√ Select a framework: » vue
√ Select a variant: » vue-ts

Scaffolding project in D:\Learning\todos-list...

Done. Now run:

  cd todos-list
  npm install
  npm run dev

完善项目结构

  • 在scr目录下新增一些文件结构
文件名用途
api存放请求相关文件
layout布局
plugins插件
composables组合式 API 抽离方法
router路由
store仓储
styles公共样式
utils工具函数
views路由页面

代码规范与 ESLint

ESLint下载 并运行

npm install eslint -D
npx eslint --init
  • 根据向导安装eslint相关包
PS D:\Learning\todos-list> npx eslint --init
You can also run this command directly using 'npm init @eslint/config'.
Need to install the following packages:
  @eslint/create-config
Ok to proceed? (y) y
? How would you like to use ESLint? ...
  To check syntax only
  To check syntax and find problems
√ How would you like to use ESLint? · style       
√ What type of modules does your project use? · esm
√ Which framework does your project use? · vue
√ Does your project use TypeScript? · No / Yes
√ Where does your code run? · browser
√ How would you like to define a style for your project? · guide
√ Which style guide do you want to follow? · standard    
√ What format do you want your config file to be in? · JavaScript
Checking peerDependencies of eslint-config-standard@latest       
√ The style guide "standard" requires eslint@^7.12.1. You are currently using eslint@8.11.0.
  Do you want to downgrade? · No / Yes
The config that you've selected requires the following dependencies:

eslint-plugin-vue@latest @typescript-eslint/eslint-plugin@latest eslint-config-standard@latest eslint@^7.12.1 eslint-plugin-import@^2.22.1 eslint-plugin-node@^11.1.0 eslint-plugin-promise@^4.2.1 || ^5.0.0 @typescript-eslint/parser@latest
√ Would you like to install them now with npm? · No / Yes
Installing eslint-plugin-vue@latest, @typescript-eslint/eslint-plugin@latest, eslint-config-standard@latest, eslint@^7.12.1, eslint-plugin-import@^2.22.1, eslint-plugin-node@^11.1.0, eslint-plugin-promise@^4.2.1 || ^5.0.0, @typescript-eslint/parser@latest     

added 124 packages, removed 1 package, and changed 8 packages in 18s
Successfully created .eslintrc.js file in D:\Learning\todos-list

eslint文件配置

module.exports = {
  env: {
    browser: true,
    es2021: true
  },
  extends: [
    // 'plugin:vue/essential',  vue2的规则
    // 在 node_modules/eslint-plugin-vue/lib/conmfigs 文件夹下
    'plugin:vue/vue3-strongly-recommended', // vue3
    'standard'
  ],
  parserOptions: {
    ecmaVersion: 'latest',
    parser: '@typescript-eslint/parser',
    sourceType: 'module'
  },
  plugins: [
    'vue',
    '@typescript-eslint'
  ],
  rules: {
  }
}

命令行配置

"scripts": {
    "lint":"eslint ./src/**/*.{js,jsx,vue,ts,tsx} --fix"
  },

在VSCode 编辑器中显示eslint错误

  • 卸载/禁用 Vuter
  • 安装 ESLint 插件
    • 默认查找项目中的验证规则,并给出提示
  • 安装 Volar 插件

配置

  • 打开设置,扩展,选取eslint, 启用格式化工具选项
  • 在这里插入图片描述
  • 鼠标右键,选择格式化文档的方式,设置默认格式化方式,ESLint
  • 快捷键: Shift + Alt + F

配置git commit hook

下载 lint-staged

npx mrm@2 lint-staged
  • husky 钩子脚本工具
  • lint-staged 验证

配置

  • package.json 文件中修改配置(用于本地git提交的校验)
"lint-staged": {
    "*.{js,jsx,vue,ts,tsx}": [
      "npm run lint",
      "git add"
    ]
  }

开发构建中进行代码规范校验

vite-plugin-eslint 的下载

  • 我这里用的 vite-plugin-eslint,
npm install vite-plugin-eslint --save-dev

配置

  • vite.comfig.ts 文件
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import eslintPlugin from 'vite-plugin-eslint'

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [
    vue(),
    eslintPlugin({
      // 配置选项
      cache: false // 缓存,默认开启
    })
  ]
})

初始化 vueRouter

安装 vue-router

npm i vue-router@4

初始化路由实例

  • 在router文件夹下创建index.ts文件
import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router'

const routes: RouteRecordRaw[] = [{
  path: '/',
  name: 'Home',
  component: () => import('../views/home/IndexHome.vue')
}]

const router = createRouter({
  history: createWebHashHistory(), // 路由模式
  routes
})

export default router

  • 挂载router实例,及配置路由出口
// main.ts
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
createApp(App)
  .use(router)
  .mount('#app')

// App.vue
<template>
  <router-view />
</template>

初始化 Vuex

安装 Vuex

npm install vuex@next --save

配置Store

// index.ts 文件
import { createStore, Store } from 'vuex'
import { InjectionKey } from 'vue'
export interface State {
  count: number,
  foo: string
}

// 定义 injection key
export const key: InjectionKey<Store<State>> = Symbol('')

// 创建store实例
export const store = createStore<State>({
  state () {
    return {
      count: 0,
      foo: 'Hi'
    }
  },
  mutations: {
    increment (state) {
      state.count++
    }
  }
})

  • vuex.d.ts 文件
/* eslint-disable no-unused-vars */
// vuex.d.ts
import { ComponentCustomProperties } from 'vue'
import { Store } from 'vuex'
import { State } from './store/index'
declare module '@vue/runtime-core' {
  // 声明自己的 store state
  // interface State {
  //   count: number
  // }

  // 为 `this.$store` 提供类型声明
  interface ComponentCustomProperties {
    $store: Store<State>
  }
}
  • main.ts
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import { store, key } from './store'
createApp(App)
  .use(router)
  .use(store, key)
  .mount('#app')

  • 在使用以上配置,使用useStore时,每次都需要导入相关配置,较为麻烦
  • 简化 useStore 用法
import { createStore, Store, useStore as baseUseStore } from 'vuex'
import { InjectionKey } from 'vue'
export interface State {
  count: number,
  foo: string
}

// 定义 injection key
export const key: InjectionKey<Store<State>> = Symbol('')

// 创建store实例
export const store = createStore<State>({
  state () {
    return {
      count: 0,
      foo: 'Hi'
    }
  },
  mutations: {
    increment (state) {
      state.count++
    }
  }
})

// 定义自己的useStore组合式函数
export function useStore () {
  return baseUseStore(key)
}

模块路径别名

配置

  • vite.config.ts
import path from 'path'

 resolve: {
    alias: {
      // '@':'绝对路径', 若有需要,自行配置其他
      '@': path.join(__dirname, 'src')
    }
  }
  • tsconfig.json
{
  "compilerOptions": {
 	// 路径配置
    "paths": {
      "@/*": [
        "./src/*"
      ]
    }
  }
}

path引入时的报错解决

  • npm i @types/node 下载node相关的类型声明
  • 模块 ““path”” 只能在使用 “allowSyntheticDefaultImports” 标志时进行默认导入
  • tsconfig.node.josn配置
{
  "compilerOptions": {
    "composite": true,
    "module": "esnext",
    "moduleResolution": "node",
    "allowSyntheticDefaultImports": true
  },
  "include": ["vite.config.ts"]
}

CSS 样式管理

下载相关预处理器

npm i  -D sass

配置

  • 在 styles 文件下搭建样式目录结构
    • index.scss 组织统一导出
    • variables.scss 全局 Sass变量
    • minxin.scss 全局mixin
    • common.scss 全局公共样式
    • transition.scss 全局过渡动画样式
  • index.scss 文件
@import './common.scss';
@import './mixin.scss';
@import './transitiono.scss';
@import './variables.scss';
  • main.ts 文件
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import { store, key } from './store'
// 加载全局样式
import './styles/index.scss'
createApp(App)
  .use(router)
  .use(store, key)
  .mount('#app')

问题及相关优化

  • 如下样式配置,当直接在vue文件中使用,会发现定义的变量无法生效且报错,只有body这类的样式正常使用
// common.scss
body {
  background-color: #000;
}

// variables.scss
$color: red;

  • 我们需要引入定义的样式变量才能正常使用,但是每个单文件组件都引入是麻烦的。
<template>
  <h1> demo </h1>
</template>

<script lang="ts" setup>
</script>

<style lang="scss" scoped>
@import '@/styles/variables.scss';
h1 {
  color: $color;
}
</style>

// vite.config.ts
css: {
    preprocessorOptions: {
      scss: {
        // 注入样式变量(根据自己需求注入其他)
        additionalData: '@import "@/styles/variables.scss;"'
      }
    }
  }
  • 就可以直接使用变量
<style lang="scss" scoped>
  h1 {
    color: $color;
  }
</style>

基于axios封装请求模块

下载axios

npm i axios

封装request请求模块

import axios from 'axios'

// 创建实例
const request = axios.create({
  baseURL: 'http:localhost:3030'
})

// 请求拦截器
request.interceptors.request.use(function (config) {
  // 统一设置用户身份 token(根据项目需求配置)
  return config
}, function (error) {
  return Promise.reject(error)
})

// 响应拦截器
request.interceptors.response.use(function (response) {
  // 统一处理接口响应错误,token过期无效、服务端异常等(根据项目需求配置)
  return response
}, function (error) {
  return Promise.reject(error)
})
export default request

基本使用

import request from './request'

interface ReposeeData<T = any> {
  status: number
  name: string
  age: number
  obj: T
}

export const getUserInfo = () => {
  return request.get<ReposeeData<{
    hi: string
    slide: string
  }>>('/user/info')
}

封装泛型请求方法

  • 原生的axios是不支持泛型的
  • 我们对request进行一层包装
import axios, { AxiosRequestConfig } from 'axios'

// 创建实例
const request = axios.create({
  baseURL: 'http:localhost:3030'
})

// 请求拦截器
request.interceptors.request.use(function (config) {
  // 统一设置用户身份 token
  return config
}, function (error) {
  return Promise.reject(error)
})

// 响应拦截器
request.interceptors.response.use(function (response) {
  // 统一处理接口响应错误,token过期无效、服务端异常等
  return response
}, function (error) {
  return Promise.reject(error)
})
// 包装request,实现泛型
export default <T = any> (config: AxiosRequestConfig) => {
  return request(config).then(res => {
    return res.data as T
  })
}

  • 调用
import request from './request'
// 返回数据类型声明
interface ReposeeData<T = any> {
  status: number
  name: string
  age: number
  obj: T
}

export const getLogin = () => {
  return request<ReposeeData>({
    method: 'GET',
    url: '/login/info'
  })
}

环境变量和模式

  • 在vite中支持环境变量的自定义

  • 创建 .env.development 文件

# 开发模式下加载的环境变量
VITE_API_BASEURL = 'https://abc.com/development/api'
  • 创建 .env.production 文件
# 生成模式下加载的环境变量
VITE_API_BASEURL = 'https://abc.com/production/api'
  • env.d.ts 文件中声明自定义环境变量类型
/// <reference types="vite/client" />

declare module '*.vue' {
  import type { DefineComponent } from 'vue'
  // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types
  const component: DefineComponent<{}, {}, any>
  export default component
}

interface ImportMetaEnv extends Readonly<Record<string, string>> {
  readonly VITE_API_BASEURL: string
  // 更多环境变量...
}

// eslint-disable-next-line no-unused-vars
interface ImportMeta {
  readonly env: ImportMetaEnv
}

跨域问题

CORS(跨域资源共享)

  • 服务端配置CORS
  • 后端配置一些http协议

服务器代理模式

server: {
    proxy: {
      // 字符串简写写法
      '/foo': 'http://localhost:4567',
      // 选项写法
      '/api': {
        target: 'http://jsonplaceholder.typicode.com',
        changeOrigin: true,
        rewrite: (path) => path.replace(/^\/api/, '')
      },
      // 正则表达式写法
      '^/fallback/.*': {
        target: 'http://jsonplaceholder.typicode.com',
        changeOrigin: true,
        rewrite: (path) => path.replace(/^\/fallback/, '')
      },
      // 使用 proxy 实例
      '/apis': {
        target: 'http://jsonplaceholder.typicode.com',
        changeOrigin: true,
        configure: (proxy, options) => {
          // proxy 是 'http-proxy' 的实例
        }
      }
    }
  }
Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐