Vue3 + TypeScript + Vite + Eslint详细配置

前言

在使用Vue3 + TypeScript的过程中,这种写法在适应之后,确实更加舒服,通过Vue3的setup语法糖,我可以将一个功能模块的所有东西,都写到一个片段中,而不是像之前一样,一个功能涉及到的变量、方法、watch、computed等东一块西一块,需要维护的时候,还要上下翻来翻去。
而网上有一些大佬,包括我身边的几位前端朋友,都在批判TypeScript,毕竟习惯了裸奔,突然给你加了一层又一层的衣服,哈哈,难免不适应。我个人刚开始使用TypeScript的时候,也相当不适应,经常编辑器一片红,但是,ts可以帮助我在写代码的时候更加严谨,也就避免的运行项目的时候,出现各种各样的错误。我个人宁可编辑器一篇红,也不愿意在浏览器里错误百出。毕竟,本人水平不高,只有这么点认知。
在这里,还是要说一下ts和js的关系,ts是js的超集,简单粗俗的去理解,ts就是给js加了类型系统,ts提供了js的所有功能,想要很好的使用ts的前提,还是要熟练掌握js的。

搭建项目前的准备工作

大家可以阅读vite中文文档的开始项,vite需要Node.js14.18以上版本,我使用nvm,安装了14.19.1版本。
https://cn.vitejs.dev/ vite中文文档
https://blog.csdn.net/m0_64697285/article/details/127318141 参考这位博主文章,安装nvm工具

构建vite项目

编辑器终端中,使用npm构建vite项目
npm create vite@latest

输入项目名
? Project name: » vue3_demo

选择Vue框架
? Select a framework: » - Use arrow-keys. Return to submit.
    Vanilla
>   Vue
    React
    Preact
    Lit
    Svelte
    Others

选择TypeScript
? Select a variant: » - Use arrow-keys. Return to submit.
    JavaScript
>   TypeScript
    Customize with create-vue ↗
    Nuxt ↗

进入创建的项目  可以直接控制台 cd vue3_demo 也可以编辑器打开文件夹vue3_demo
npm install
npm run dev

配置eslint与prettier

vscode安装插件

  • Prettier - Code formatter(格式化)
  • eslint
  • Stylelint
1.安装插件
npm install -D eslint eslint-plugin-vue
2.进行eslint初始化
npm init @eslint/config

下面是我自己配置时候的选项,根据自己的需要去选择
? How would you like to use ESLint? ... => To check syntax and find problems
? What type of modules does your project use? ... => JavaScript modules (import/export)
? Which framework does your project use? ... => Vue.js
Does your project use TypeScript? => Yes
? Where does your code run? ... => Browser, Node
? What format do you want your config file to be in? ... => JavaScript
? Would you like to install them now? => Yes
? Which package manager do you want to use? ...  => npm

运行完后,根目录下会生成.eslintrc.cjs文件
下面是我自己的配置项,非常简单的配置
module.exports = {
  env: {
    browser: true,
    es2021: true,
    node: true
  },
  extends: [
    'eslint:recommended',
    'plugin:vue/vue3-essential',
    'plugin:@typescript-eslint/recommended',
    'plugin:prettier/recommended'  // 这里是将prettier作为插件被eslint使用,避免两者有冲突,放在extends最下方
  ],
  overrides: [],
  parser: 'vue-eslint-parser',
  parserOptions: {
    ecmaVersion: 'latest',
    sourceType: 'module',
    parser: '@typescript-eslint/parser'
  },
  plugins: ['vue', '@typescript-eslint'],
  rules: {
    'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
    'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
    'max-len': ['error', 100], // 强制一行的最大长度
    'no-shadow': 'off', // 禁止变量声明与外层作用域的变量同名
    'no-unused-vars': 'warn', // 禁止出现未使用过的变量
    eqeqeq: 'off', // 要求使用 === 和 !==
    'prefer-const': 'warn', // 要求使用 const 声明那些声明后不再被修改的变量
    '@typescript-eslint/no-empty-function': 'off', // 不允许空函数
    '@typescript-eslint/no-explicit-any': 'off' // 禁止使用 any 类型
  }
}


由于是vite构建的项目,需要添加插件,让vite识别eslint相关配置
npm install vite-plugin-eslint --save-dev

修改vite.config.ts
import eslint from 'vite-plugin-eslint' // 新增
plugins: [vue(), eslint()], // 新增 eslint()

配置编辑器保存后eslint自动修复代码
vscode => setting.json
{
  "workbench.colorTheme": "Bluloco Light",
  "[json]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
  "[javascript]": {
    "editor.defaultFormatter": "vscode.typescript-language-features"
  },
  "editor.fontSize": 16,
  "[vue]": {
    "editor.defaultFormatter": "Vue.volar"
  },
  "workbench.editor.enablePreview": false,
  "[typescript]": {
    "editor.defaultFormatter": "vscode.typescript-language-features"
  },
  "javascript.format.insertSpaceBeforeFunctionParenthesis": true,
  "typescript.format.insertSpaceBeforeFunctionParenthesis": true,
  "eslint.validate": [
    "vue",
    "html",
    "javascript",
    "graphql",
    "javascriptreact",
    "json",
    "typescript",
    "typescriptreact",
    "js",
    "ts",
    "md"
  ],
  "prettier.singleQuote": true,
  "prettier.semi": false,
  "[jsonc]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
  "workbench.iconTheme": "vscode-icons",
  "editor.fontWeight": "normal",
  "[css]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
  "cursorcode.accessToken": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IkN1VWI0NkVHT3BHaXI4TDh3VkRQRCJ9.eyJpc3MiOiJodHRwczovL2N1cnNvci51cy5hdXRoMC5jb20vIiwic3ViIjoiZ2l0aHVifDEyMjAxMzc0OCIsImF1ZCI6WyJodHRwczovL2N1cnNvci51cy5hdXRoMC5jb20vYXBpL3YyLyIsImh0dHBzOi8vY3Vyc29yLnVzLmF1dGgwLmNvbS91c2VyaW5mbyJdLCJpYXQiOjE2ODI0MTUyNTksImV4cCI6MTY4MjUwMTY1OSwiYXpwIjoiS2JaVVI0MWNZN1c2elJTZHBTVUo3STdtTFlCS09DbUIiLCJzY29wZSI6Im9wZW5pZCBwcm9maWxlIG9mZmxpbmVfYWNjZXNzIn0.vfr0VLYkPSI4lXmGPt2I7oDbqsqLmjpMCiBAxpeauiVb6B_7Xb7s3OOoa6l2mSAMl1gnd7uqdNhtqk78pOxoo9Kt3mTfqTO-KlhJ3ppUG5m9NN6LIDcoxsYa8KeplvYPmgRD9vcz1P22xQjHc--g4bgrG85pjBB92irqpzCBKQkW9JRqi514vRF0oVWRoh7aX6NafnMKqjtqeLch8T4tuEPHmH88hAM5TOKLaTGO3fr4Iu7hqTRHOntxiWMJdr38YTIUEIkjIfrFxX5mHIL2mPUur6y9gyIsBpAoB3BJqWK6-xxJyMYtbJmkReinOdsj4Irt31Vu1ddm18P5Vb3FIQ",
  "volar.format.initialIndent": {
    "html": true
  },
  "prettier.enable": true,
  "editor.codeActionsOnSave": {
    "source.fixAll": true,
    "source.fixAll.eslint": true,
    "source.fixAll.stylelint": true
  },
  "stylelint.enable": true,
  "scss.lint.unknownAtRules": "ignore",
  "stylelint.validate": ["css", "scss", "vue"],
  "editor.tabSize": 2,
  "prettier.useTabs": true,
  "editor.formatOnSave": true, // 开启保存文件自动格式化代码
  "editor.defaultFormatter": "esbenp.prettier-vscode", // 默认的代码格式化工具
  "prettier.requireConfig": true,
  "settingsSync.ignoredExtensions": [] // 需要Prettier的配置文件
}

修改tsconfig.json
"compilerOptions": {
	"types": ["vite/client"], // 新增,解决eslint对import.meta.env的报错
}

最后,配置prettier自动修复另外一些代码
npm install -D prettier
npm install  -D eslint-config-prettier // eslint兼容的插件
npm install -D eslint-plugin-prettier // eslint的prettier 

根目录下新建.prettierrc.cjs
module.exports = {
  // 一行最多一百字符
  printWidth: 100,
  // 使用2个空格缩进
  tabWidth: 2,
  // 不适用缩进符而使用空格
  useTabs: false,
  // 不尾随分号
  semi: false,
  // 使用单引号
  singleQuote: true,
  // 多行逗号分隔的语法中,最后一行不加逗号
  trailingComma: 'none',
  // 单个参数的箭头函数不加括号
  arrowParens: 'avoid',
  // 对象大括号内两边是否加空格
  bracketSpacing: true
}

新建production与development,配置开发生产环境变量

与src同级,创建.env.production与.env.development文件
字段须VITE开头,否则不识别,也可以自行配置后,使用其他字段
想用其他字段 参考  https://blog.csdn.net/weixin_45547638/article/details/127277857

// .env.development
# 开发环境
VITE_BASE_URL = http://192.168.50.207:8080/api
VITE_APP_TITLE = 开发环境

// .env.production
# 生产环境
VITE_BASE_URL = http://192.168.50.161:5173/api
VITE_APP_TITLE = 生产环境

修改tsconfig.json

// 在compilerOptions中新增
{
  "compilerOptions": {
    ...
    "baseUrl": ".",     // 未设置baseUrl,不允许使用非相对路径
    "paths": {
      "@": ["src"],
      "@/*": ["src/*"]
    }
},

修改vite.config.ts

// vite.config.ts
import { defineConfig, loadEnv } from 'vite'
import vue from '@vitejs/plugin-vue'
import { resolve } from 'path'
export default (({ mode }) => {

  在启动项目时,控制台打印环境。只是给自己看当前属于什么环境和BASE_URL
  console.log(mode)
  const BASE_URL = loadEnv(mode, process.cwd()).VITE_BASE_URL
  console.log(BASE_URL)
  return defineConfig({
  	// base: './',
  	plugins: [vue()],
  	// 配置别名
  	resolve: {
  	  alias: {
	    "@": resolve(__dirname, './src')
	  }
  	}
  })
})
// 会报错: 找不到名称'__dirname'
path模块是node.js的内置模块,node.js默认不支持ts文件
安装@type/node依赖包    npm install @types/node --save-dev

修改package.json

// package.json 修改scripts中内容,修改后为
"scripts": {
  "start": "vite --host --open",  // npm start启动项目,启动后自动打开浏览器,并在控制台输出BASE_URL
  "build": "vue-tsc && vite build",
  "preview": "vite preview"
},

基础的Vite + Vue3项目初步搭建完成,接下来需要配置项目所需的工具

整合Element Plus 框架,安装Less

// 第一步 npm安装
npm install element-plus --save
// 第二步 参考Element Plus官网,可完整引入或按需导入  我选择按需导入
// 按需导入需要安装以下两个插件
npm install -D unplugin-vue-components unplugin-auto-import
// 修改vite.config.ts
import { defineConfig, loadEnv } from 'vite'
import vue from '@vitejs/plugin-vue'
import { resolve } from 'path'
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
export default (({ mode }) => {
  console.log(mode)
  const BASE_URL = loadEnv(mode, process.cwd()).VITE_BASE_URL
  console.log(BASE_URL)
  return defineConfig({
  	// base: './',
  	plugins: [
  	  vue(),
  	  AutoImport({
        resolvers: [ElementPlusResolver()]
      }),
      Components({
        resolvers: [ElementPlusResolver()]
      }),
  	],
  	// 配置别名
  	resolve: {
  	  alias: {
	    "@": resolve(__dirname, './src')
	  }
  	}
  })
})
// 安装Element Plus Icon
npm install @element-plus/icons-vue
// 修改main.ts
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
const app = createApp(App)
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
  app.component(key, component)
}
app.mount('#app')
// 安装Less
npm i less

整合Pinia,了解Pinia的持久化工具

// npm安装
npm install pinia
// 先在main.ts中引入pinia
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import { createPinia } from 'pinia'
const pinia = createPinia()
const app = createApp(App)
app.use(pinia).mount('#app')
// src下新建store文件夹 根据不同业务需要创建不同的 use_textName_Store.ts 文件
// 安装pinia-plugin-persist 实现数据持久化储存
npm i pinia-plugin-persist --save
// 新建useUserStore.ts
import { defineStore } from "pinia";
export const useUserStore = defineStore('user', {
  state: () => {
    return {
      userName: sessionStorage.getItem("userName"),
      phone: sessionStorage.getItem("phone"),
      token: sessionStorage.getItem("satoken"),
    }
  },
  getters: {},
  actions: {
    setUser (user: any) {
      this.userName = user.userName
      this.phone = user.phone
      sessionStorage.setItem("userName", user.userName)
      sessionStorage.setItem("phone", user.phone)
    },
    setToken (token: any) {
      this.token = token
      sessionStorage.setItem(token.tokenName, token.tokenValue)
    }
  },
  // 开启数据缓存 默认sessionStorage state属性全部储存
  persist: {
    enabled: true,
  }
  // 也可以指定sessionStorage或者localStorage  指定持久化字段
  persist: {
    enabled: true,
    strategies: [
      {
        storage: localStorage,
        paths: ['id'],//指定要持久化的字段
      }
    ]
  }
})
// Pinia和VueX相比,在单个文件中最大的差别,就是去掉了饱受诟病的mutations
// 在.vue文件中使用pinia=>useUserStore
import { useUserStore } from '@/store/userStore';
const userStore = useUserStore()

整合Vue-Router,并做动态路由功能

// npm安装Vue-Router
npm install vue-router@4
// 网上有很多关于vue3动态路由的文档博客,配合ts使用,不过我试用了很多,效果都不尽人意,最后,自己总结了可行的动态路由方式。没有配合后端来完成动态路由,后期研究明白,会在文章中补充的。
// src下新建router文件夹 => 新建index.ts 存放不需要权限的路由及beforeEach
import { createRouter, createWebHistory, RouteRecordRaw } from "vue-router";
const staticRoutes: Array<RouteRecordRaw> = [
  {
    path: '/login',
    name: 'Login',
    meta: {
      hidden: true,
      title: '登录页'
    },
    component: () => import('@/views/login/index.vue')
  },
  {
    path: '/',
    name: 'layout',
    component: () => import('@/layout/index.vue'),
    redirect: '/home',
    meta: {
      hidden: false
    },
    children: [
      {
        path: '/home',
        name: 'home',
        component: () => import('@/views/home/index.vue'),
        meta: {
          title: '首页',
          icon: 'House'
        }
      }
    ]
  },
  {
    path: '/404',
    meta: {
      hidden: true,
      title: '404'
    },
    component: () => import('@/views/error_page/404.vue')
  },
]

const router = createRouter({
  history: createWebHistory(),
  routes: staticRoutes
})
export default router

// router文件夹下 新建trendsRouter.ts  存放动态路由内容
import { RouteRecordRaw } from "vue-router";

const trendsRouter: Array<RouteRecordRaw> = [
  {
    path: '/echarts',
    name: 'echarts',
    component: () => import('@/views/echarts/index.vue'),
    meta: {
      title: '可视化',
      icon: 'House'
    }
  }
]

export default trendsRouter

// 在index.ts中,引入trendsRouter.ts,并补充router.beforeEach
import { createRouter, createWebHistory, RouteRecordRaw } from "vue-router";
import trendsRouter from "./trendsRouter";
const staticRoutes: Array<RouteRecordRaw> = [
  {
    path: '/login',
    name: 'Login',
    meta: {
      hidden: true,
      title: '登录页'
    },
    component: () => import('@/views/login/index.vue')
  },
  {
    path: '/',
    name: 'layout',
    component: () => import('@/layout/index.vue'),
    redirect: '/home',
    meta: {
      hidden: false
    },
    children: [
      {
        path: '/home',
        name: 'home',
        component: () => import('@/views/home/index.vue'),
        meta: {
          title: '首页',
          icon: 'House'
        }
      }
    ]
  },
  {
    path: '/404',
    meta: {
      hidden: true,
      title: '404'
    },
    component: () => import('@/views/error_page/404.vue')
  },
]

const router = createRouter({
  history: createWebHistory(),
  routes: staticRoutes
})
// 创建一个变量,用来判断是否已经动态添加了路由  防止陷入动态路由死循环
let needLoad = true
// 配置白名单
const whiteList = ['/login']
router.beforeEach((to, from, next) => {
  const token = sessionStorage.getItem("satoken")
  if (to.meta.title) {
    document.title = to.meta.title as string
  }
  if (!token) {
    if (whiteList.indexOf(to.path) !== -1) {
      next()
    } else {
      next('/login')
    }
  } else {
    if (to.path === '/login') {
      next('/home')
    } else {
      if (needLoad) {
        // 这里用router.options.routes  是因为在实际应用中,我发现直接router.addRoute动态添加路由后,页面总是为空,后来发现新增的路由并没有添加成功,通过此方法,可以有效保证动态添加路由成功并成功跳转
        const current: any = router.options.routes
        trendsRouter.forEach(v => {
          current[1].children.push(v)
          router.addRoute('layout',v)
        })
        router.addRoute({
          path: '/:catchAll(.*)',
          redirect: '/404'
        },)
        needLoad = false
        // 防止空白页面或者404  动态添加完后,路由重新跳转
        next({ ...to, replace: true })
      } else {
        next()
      }
    }
  }
})
export default router

// 在main.ts中引入router
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import { createPinia } from 'pinia'
import router from './router'
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
const pinia = createPinia()
const app = createApp(App)
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
  app.component(key, component)
}
app.use(pinia).use(router).mount('#app')


// 在页面中使用router
import { useRouter, useRoute } from 'vue-router';
const router = useRouter()
const route = useRoute()


当然,目前仅是动态添加没有问题,页面跳转没有问题,如果sidebar选项在动态添加的路由上,我刷新页面的话,浏览器会有警告,但并不影响页面显示及路由跳转
 [Vue Router warn]: No match found for location with path "/echarts"
 
关于路由配置及动态路由,各位有什么好的意见或者方法,可以评论给我,多谢多谢!!!

整合Axios,封装request及接口整合

// npm安装axios
npm install axios
// 个人习惯将request及接口管理文件夹都放在一起
// src下新建http文件夹 http => 新建request.ts
import axios from 'axios'
// 页面顶部的进度条,使用起来简单
// npm i nprogress   npm i --save-dev @types/nprogress
import Nprogress from 'nprogress'
import 'nprogress/nprogress.css'
import { ElMessage } from 'element-plus'

const request = axios.create({
  baseURL: import.meta.env.VITE_BASE_URL,
  timeout: 5000
})
const NERWORK_ERROR = '服务器错误,请稍后重试!'

request.interceptors.request.use(config => {
  Nprogress.start()
  const token = sessionStorage.getItem("satoken")
  config.headers.satoken = token
  return config
}, error => {
  Nprogress.done()
  return Promise.reject(error)
})

request.interceptors.response.use(res => {
  Nprogress.done()
  if (res.status === 200) {
    return res.data
  } else {
    ElMessage.error(NERWORK_ERROR)
    return Promise.reject(NERWORK_ERROR)
  }
})

class Http {
  get = function (url: string, params: any) {
    return request({
      url: url,
      method: 'get',
      headers: { 'Content-Type': 'multipart/form-data' },
      params
    })
  }
  post = function (url: string, data: any) {
    return request({
      url: url,
      method: 'post',
      data
    })
  }
}
const http = new Http()
export { http }

// http文件夹 => 新建interface文件夹  用于管理各个业务场景下的接口
// interface文件夹 => 新建user.ts  管理用户的注册登录登出,用户信息等接口
class Apis {
  register = `user/register`
  login = `user/login`
  refresh = `user/hello`
}
const user = new Apis()
export { user }

// http文件夹 => 新建http.ts 整合所有接口文件与request
import { http } from "./request";
import { user } from './interface/user'
const register = (data: any) => {
  return http.post(user.register, data)
}
const login = (data: any) => {
  return http.post(user.login, data)
}
const refresh = (data: any) => {
  return http.post(user.refresh, data)
}
export default {
  register,
  login,
  refresh
}

// 在main.ts中引入http 全局挂载  个人喜欢用$http
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import { createPinia } from 'pinia'
import router from './router'
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
import http from './http/http'

const pinia = createPinia()
const app = createApp(App)
// vue3中 不能再使用vue.prototype.$http
app.config.globalProperties.$http = http;
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
  app.component(key, component)
}
app.use(pinia).use(router).mount('#app')

// 在页面中使用$http,也不能再是简单的通过this.$http来使用,为了避免每个页面都返货的获取上下文,我简单封了一个获取全局的方法
// src => 新建utils文件夹 => 新建getCurrentInstance.ts
import { getCurrentInstance, ComponentInternalInstance } from 'vue';
export default function useCurrentInstance() {
  const currentInstance = getCurrentInstance() as ComponentInternalInstance;
  const proxy = currentInstance.appContext.config.globalProperties;
  // 所有通过app.config.globalProperties挂载的,都在proxy中
  return proxy
}
// 页面中
import useCurrentInstance  from '@/utils/getCurrentInstance';
const { $http } = useCurrentInstance();
// 通过$http.login(data) 来登录。。。

// 配置axios的过程中,可能会有Property does not exist on type 'AxiosResponse'报错
// 如果有报错, src => 新建shims-vue.d.ts
import { AxiosRequestConfig } from "axios";
declare module 'axios' {
  interface AxiosInstance {
    (config: AxiosRequestConfig): Promise<any>
  }
}

整合Mock.js,前后端分离自行模拟数据

// npm安装mock.js
npm install mockjs
// src => 新建mock文件夹 => 新建type.ts
// typeScript写参数接口,这个东西看着J里J气的,哈哈,越来越像Java了
export interface MockParams {
  url: string,
  type: string,
  data?: any,
  params?: any,
  response(option?: any): Record<string, unknown>
}

// mock文件夹 => 新建api文件夹 => 新建user.ts  模拟用户注册登录等
function paramObj (url: any) {
  const search = url.split('?')[1]
  if (!search) {
    return {}
  }
  return JSON.parse(
    '{"' +
    decodeURIComponent(search)
      .replace(/"/g, '\\"')
      .replace(/&/g, '","')
      .replace(/=/g, '":"') +
    '"}')
}

const userLogin = {
  url: `user/login`,
  type: "post",
  response: (config: any) => {
    const { userName, userPassword } = JSON.parse(config.body)
    if (userName === 'wq19970106' && userPassword === 'Wq19970106..') {
      return {
        code: 20000,
        message: '登录成功',
        data: {
          tokenName: 'satoken',
          tokenValue: 'dsadfhasoidqwehdasd',
          userName: '王琪',
          phone: '15582868787'
        }
      }
    } else {
      return {
        code: 40010,
        message: '账号或密码错误'
      }
    }
  }
}

const userRegister = {
  url: `/user/register`,
  type: 'post',
  response: (config: any) => {
    const { userName, userPassword } = JSON.parse(config.body)
    if (userName !== 'wq19970106' && userPassword) {
      return {
        code: 20000,
        message: '注册成功'
      }
    } else {
      return {
        code: 40010,
        message: '账号已存在'
      }
    }
  }
}

const user = [userLogin, userRegister]
export default user

// mock文件夹 => 新建index.ts
import Mock from 'mockjs'
import { MockParams } from './type'
import user from './api/user'

const mocks = [...user]
export function mockRequest() {
  let i: MockParams
  for(i of mocks){
    Mock.mock(new RegExp(i.url), i.type || 'get', i.response)
  }
}

// 在main.ts中引入使用
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import { createPinia } from 'pinia'
import { mockRequest } from './mock'
import router from './router'
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
import http from './http/http'
const pinia = createPinia()
const app = createApp(App)
app.config.globalProperties.$http = http;
if(process.env.NODE_ENV === 'development'){
  mockRequest()
}
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
  app.component(key, component)
}
app.use(pinia).use(router).mount('#app')


// 在配置mock.js过程中  控制台会有typeScript关于mock的报错
npm install @types/mockjs
// shims-vue.d.ts 添加
declare module 'mockjs'

引入Moment.js与Lodash

// npm安装
npm install moment
npm i --save lodash
// moment是关于时间的工具类,内容全面,功能强大,同样的还有day.js 本人更习惯moment
// main.ts中引入moment,全局挂载
import moment from 'moment'
app.config.globalProperties.$moment = moment;
// 在页面中同样使用封好的getCurrentInstance
import useCurrentInstance  from '@/utils/getCurrentInstance';
const { $http, $moment } = useCurrentInstance();
const now = ref($moment().format("LTS"));
const timer = setInterval(function () {
  now.value = $moment().format("LTS")
}, 1000)
// lodash是一款很实用的js工具类,包含数组、集合、字符串等操作函数,我用的更多的是防抖之类的方法

整合Echarts

// npm安装
npm install echarts
// utils文件夹 => 新建echarts.ts 用于按需导入echarts
import * as echarts from 'echarts/core';
import {
  BarChart,
  // 系列类型的定义后缀都为 SeriesOption
  BarSeriesOption,
  LineChart,
  LineSeriesOption
} from 'echarts/charts';
import {
  TitleComponent,
  // 组件类型的定义后缀都为 ComponentOption
  TitleComponentOption,
  TooltipComponent,
  TooltipComponentOption,
  ToolboxComponent,
  GridComponent,
  GridComponentOption,
  // 数据集组件
  DatasetComponent,
  DatasetComponentOption,
  // 内置数据转换器组件 (filter, sort)
  TransformComponent,
  LegendComponent
} from 'echarts/components';
import { LabelLayout, UniversalTransition } from 'echarts/features';
import { CanvasRenderer } from 'echarts/renderers';

// 通过 ComposeOption 来组合出一个只有必须组件和图表的 Option 类型
type ECOption = echarts.ComposeOption<
  | BarSeriesOption
  | LineSeriesOption
  | TitleComponentOption
  | TooltipComponentOption
  | GridComponentOption
  | DatasetComponentOption
>;

// 注册必须的组件
echarts.use([
  TitleComponent,
  TooltipComponent,
  ToolboxComponent,
  GridComponent,
  DatasetComponent,
  TransformComponent,
  BarChart,
  LineChart,
  LabelLayout,
  UniversalTransition,
  CanvasRenderer,
  LegendComponent
]);

export default echarts

// 修改main.ts
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import { createPinia } from 'pinia'
import { mockRequest } from './mock'
import router from './router'
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
import moment from 'moment'
import http from './http/http'
import echarts from './utils/echarts'
// echarts.resize()会有浏览器警告,npm i default-passive-events -S
import 'default-passive-events'
const pinia = createPinia()
const app = createApp(App)
app.config.globalProperties.$moment = moment;
app.config.globalProperties.$http = http;
app.config.globalProperties.$echarts = echarts
if(process.env.NODE_ENV === 'development'){
  mockRequest()
}
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
  app.component(key, component)
}
app.use(pinia).use(router).mount('#app')
// 在需要的页面中使用echarts
<template>
  <div class="echarts">
    <div id="main" ref="mainDom"></div>
  </div>
</template>
<script lang='ts' setup>
import { ref, nextTick, watch } from 'vue';
import useCurrentInstance from '@/utils/getCurrentInstance'
import { useUserStore } from '@/store/userStore';
const userStore = useUserStore()
const { $echarts } = useCurrentInstance()
const mainDom = ref(null)
let myEcharts: any
const initEchart = () => {
  myEcharts = $echarts.init(mainDom.value)
  let options = {
    title: {
      text: '数据报表',
      textStyle: {
        color: '#567788',
        fontStyle: 'oblique',
        fontWeight: 'lighter'
      }
    },
    tooltip: {
      trigger: 'axis',
      axisPointer: {
        type: 'cross',
        label: {
          backgroundColor: '#6a7985'
        }
      }
    },
    //类型分类
    legend: {
      data: ['用户1', '用户2', '用户3', '用户4', '用户5']
    },
    toolbox: {
      feature: {
        saveAsImage: {}
      }
    },
    //坐标
    grid: {
      left: '3%',
      right: '4%',
      bottom: '3%',
      containLabel: true//如果坐标图上xy轴显示的有标示,一定要为true,否则展示不全,内容会被隔挡
    },
    //X轴的命名
    xAxis: [
      {
        type: 'category',
        boundaryGap: false,
        data: ['周一', '周二', '周三', '周四', '周五', '周六', '周日']
      }
    ],
    yAxis: [
      {
        type: 'value'
      }
    ],
    series: [
      //bar--柱状图
      //line -- 线图
      {
        name: '用户1',
        type: 'line',
        stack: 'Total',
        areaStyle: {},
        emphasis: {
          focus: 'series'
        },
        data: [120, 132, 101, 134, 90, 230, 210]
      },
      {
        name: '用户2',
        type: 'line',
        stack: 'Total',
        areaStyle: {},
        emphasis: {
          focus: 'series'
        },
        data: [240, 182, 291, 234, 290, 330, 310]
      },
      {
        name: '用户3',
        type: 'line',
        stack: 'Total',
        areaStyle: {},
        emphasis: {
          focus: 'series'
        },
        data: [120, 232, 2101, 154, 190, 330, 410]
      },
      {
        name: '用户4',
        type: 'line',
        stack: 'Total',
        areaStyle: {},
        emphasis: {
          focus: 'series'
        },
        data: [360, 332, 301, 334, 390, 330, 320]
      },
      {
        name: '用户5',
        type: 'line',
        stack: 'Total',
        label: {
          show: true,
          position: 'top'
        },
        areaStyle: {},
        emphasis: {
          focus: 'series'
        },
        data: [820, 932, 901, 934, 1290, 1330, 1320]
      }
    ]
  }
  myEcharts.setOption(options)
  window.addEventListener('resize', () => {
    myEcharts.resize()
  })
}
nextTick(()=> {
  initEchart()
})
watch(userStore, () => {
  myEcharts.dispose()
  setTimeout(() => {
    initEchart()
  },300);
})


</script>
<style lang='less' scoped>
.echarts {
  width: 100%;
  height: 100%;
  position: relative;

  #main {
    width: 45%;
    height: 45%;
    position: absolute;
    top: 10px;
    left: 10px;
    border: 1px solid black;
  }
}
</style>

结束,求大佬指点!

至此,vite构建的最基本的vue3项目就搭建完了。有任何不妥的地方,各位大佬不吝指点一下。多谢*500!

你可能感兴趣的:(typescript,javascript,vue.js)