Vite + Vue3 <script setup> + TS + Pinia + Element-Plus 项目搭建

使用 Vite 创建项目

使用我最喜爱的 pnpm

pnpx create-vite
pnpm create vite

如果和我一样嫌弃 pnpm pnpx 太长,可以设置 alias

// .zshrc
alias p=pnpm
alias px=pnpx

这样就清爽多了

px create-vite

当然选择我最喜爱的 Vue

Select a framework:
    vanilla
❯   vue
    react
    preact
    svelte

Vue3 <script setup> 语法糖对 TS 的支持棒棒的,特别是 props

Select a variant:
❯   vue-ts

进入项目,安装依赖

p i

完成

VS Code 不要安装 Vuter 插件,如果安装了就卸载,用 Volar

配置 ESLint

生成配置文件

eslint 命令生成配置文件

px eslint --init


How would you like to use ESLint? 
  To check syntax only 
  To check syntax and find problems 
❯ To check syntax, find problems, and enforce code style


What type of modules does your project use?
❯ JavaScript modules (import/export) 
  CommonJS (require/exports) 
  None of these


Which framework does your project use? 
  React 
❯ Vue.js 
  None of these


Does your project use TypeScript? (y/N) Y


Where does your code run? (Press <space> to select, <a> to
 toggle all, <i> to invert selection)
❯◉ Browser
 ◯ Node


How would you like to define a style for your project? (Us
e arrow keys)
❯ Use a popular style guide 
  Answer questions about your style 
  Inspect your JavaScript file(s)


Which style guide do you want to follow? 
❯ Airbnb: https://github.com/airbnb/javascript 
  Standard: https://github.com/standard/standard 
  Google: https://github.com/google/eslint-config-google

什么格式都行

What format do you want your config file to be in? (Use ar
row keys)
❯ JavaScript 
  JSON

最后是否使用 npm 安装依赖,我不用 npm 并且我的项目是 Monorepo ,所以我自己安装依赖

The config that you've selected requires the following dependencies:
eslint-plugin-vue@latest @typescript-eslint/eslint-plugin@latest eslint-config-airbnb-base@latest eslint@^7.32.0 || ^8.2.0 eslint-plugin-import@^2.25.2 @typescript-eslint/parser@latest
? Would you like to install them now with npm? (Y/n) n

注意如果依赖的语义版本号使用的 || 两边有空格,就需要用引号包起来

p add -D eslint-plugin-vue@latest @typescript-eslint/eslint-plugin@latest eslint-config-airbnb-base@latest "eslint@^7.32.0 || ^8.2.0" eslint-plugin-import@^2.25.2 @typescript-eslint/parser@latest

.eslintrc.js 创建完成

.eslintrc.js 配置

使用 Vue3 规则

// .eslintrc.js
module.exports = {
  extends: [
    'plugin:vue/vue3-recommended',
};

允许 vite.config.ts 使用 devDependencies 中的依赖

// .eslintrc.js
module.exports = {
  rules: {
    'import/no-extraneous-dependencies': [
      'error',
        devDependencies: ['vite.config.ts'],
};

Missing file extension

// .eslintrc.js
module.exports = {
  rules: {
    'import/extensions': [
      'error',
      'ignorePackages',
        js: 'never',
        jsx: 'never',
        ts: 'never',
        tsx: 'never',
}

枚举 is already declared in the upper scope

// .eslintrc.js
module.exports = {
  rules: {
    'no-shadow': 'off',
    '@typescript-eslint/no-shadow': ['off'],
}

枚举成员 is defined but never used

// .eslintrc.js
module.exports = {
  rules: {
    'no-unused-vars': 'off',
    '@typescript-eslint/no-unused-vars': 'error',
}

用 ESLint 代替 Prettier

ESLint 也是可以作为格式化工具,不需要用 Prettier

安装 ESLint 插件,启用 ESLint 的 Format 功能,启用保存时格式化代码

添加 lint script

// package.json
  "scripts": {
    "lint": "eslint --ext .ts,.tsx,.vue .",
    "lint:fix": "eslint --ext .ts,.tsx,.vue --fix .",
}

配置别名 @

配置的别名要让 Vite 、TS、ESLint 都认识才行

Vite 配置别名

安装 @types/node

p add @types/node -D


// vite.config.ts
import path from 'path';
export default defineConfig({
  resolve: {
    alias: [
      { find: /^@\//, replacement: `${path.resolve(__dirname, './src')}/` },
});


// tsconfig.node.json
  "compilerOptions": {
    "allowSyntheticDefaultImports": true
}

TS 配置别名

// tsconfig.json
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {"@/*": ["src/*"]},
}

ESLint 配置别名

p add -D eslint-import-resolver-alias


// .eslintrc.js
module.exports = {
  settings: {
    'import/resolver': {
      alias: {
        map: [['@', './src/']],
        extensions: ['.ts', '.tsx'],
};

配置接口代理

前后端分离必备,解决跨域问题

// vite.config.ts
export default defineConfig({
  server: {
    proxy: {
      '/api/indicator-factory/': {
        target: 'http://index-factory-yf-api.jd.com',
        ws: true,
        changeOrigin: true,
        rewrite: (p) => p.replace(/^\/api\/indicator-factory\//, '/api/'),
});

配置 git 提交规范

px husky-init

会在项目根目录生成 .husky 目录, husky 添加到了开发依赖,添加了 prepare 脚本

// package.json
  "scripts": {
    "prepare": "husky install"
  "devDependencies": {
    "husky": "^7.0.0"
}

每次安装依赖都会执行 prepare

提交代码前执行代码检测和测试

默认有 pre-commit 钩子,在 git commit 提交前做一些事情,比如执行代码规范检测、执行测试

// pre-commit
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
pnpm lint
pnpm test

约定式提交

p add -D @commitlint/cli @commitlint/config-conventional

在项目根目录创建 commitlint.config.js 文件

// commitlint.config.js
module.exports = {
  extends: [
    '@commitlint/config-conventional'
}

创建 commit-msg 钩子

px husky add .husky/commit-msg 'npx --no-install commitlint --edit "$1"'

配置 Element Plus

p add element-plus

按需自动导入

p add -D unplugin-vue-components unplugin-auto-import


// vite.config.ts
import AutoImport from 'unplugin-auto-import/vite';
import Components from 'unplugin-vue-components/vite';
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers';
export default defineConfig({
  plugins: [
    AutoImport({
      resolvers: [ElementPlusResolver()],
    Components({
      resolvers: [ElementPlusResolver()],
});

就可以使用了

<template>
  <el-button type="primary">
    Primary
  </el-button>
</template>

给组件添加语法提示

// tsconfig.json
  "compilerOptions": {
    "types": [
        "element-plus/global"
}

ESLint 忽略自动生成的 components.d.ts auto-imports.d.ts

// .eslintrc.js
module.exports = {
  ignorePatterns: ['components.d.ts', 'auto-imports.d.ts'],
};

自定义主题

p add -D sass

自定义主题文件

// src/styles/index.scss
@forward 'element-plus/theme-chalk/src/common/var.scss' with (
  $colors: (
    'primary': (
      'base': #00b575,
);


// vite.config.ts
export default defineConfig({
  plugins: [
    Components({
      resolvers: [
        ElementPlusResolver({
          importStyle: 'sass',
  resolve: {
    alias: [
      { find: /~\//, replacement: `${path.resolve(__dirname, 'src')}/` },
  css: {
    preprocessorOptions: {
      scss: {
        additionalData: '@use "~/styles/index.scss" as *;',
});

国际化 & Pinia全局状态管理

p add vue-i18n pinia


// i18n/LOCALE.ts
export enum LOCALE {
  EN_US = 'en_US',
  ZH_CN = 'zh_CN',


// i18n/index.ts
import { createI18n } from 'vue-i18n';
import { LOCALE } from './LOCALE';
export default createI18n({
  locale: LOCALE.ZH_CN,
  messages: {
    [LOCALE.ZH_CN]: {
      hello: '你好世界',
    [LOCALE.EN_US]: {
      hello: 'hello world',
});


// stores/app.store.ts
import { defineStore } from 'pinia';
import { computed, ref, watchEffect } from 'vue';
import zh from 'element-plus/lib/locale/lang/zh-cn';
import en from 'element-plus/lib/locale/lang/en';
import i18n from '@/i18n';
import { LOCALE } from '@/i18n/LOCALE';
export const useAppStore = defineStore('app', () => {
  // 默认语言
  const locale = ref(LOCALE.ZH_CN);
  // element plus 语言
  const elementLocale = computed(() => (locale.value === LOCALE.EN_US ? en : zh));
  watchEffect(() => {
    i18n.global.locale = locale.value;
  // 更新语言
  const updateLocale = (lang: LOCALE) => {
    locale.value = lang;
  return {
    locale,
    elementLocale,
    updateLocale,
});


// main.ts
import { createApp } from 'vue';
import { createPinia } from 'pinia';
import App from './App.vue';
import i18n from './i18n';
const app = createApp(App);
app.use(i18n);
app.use(createPinia());
app.mount('#app');

使用

<script setup lang="ts">
import { useAppStore } from '@/stores/app.store';
import { LOCALE } from '@/i18n/LOCALE';
const appStore = useAppStore();
</script>
<template>
  <el-config-provider :locale="appStore.elementLocale">
    <el-button
      type="primary"
      @click="appStore.updateLocale(appStore.locale === LOCALE.EN_US ? LOCALE.ZH_CN : LOCALE.EN_US)"