Vue3 + Vite + JavaScript + Pinia 搭建一套团队用脚手架

2,094 阅读7分钟

为什么需要搭建一套团队用脚手架

  • 团队开发,为了约束成员的代码行为
  • 养成相同的代码习惯
  • 便于团队人员迭代的代码交接
  • 利于后期代码维护

项目结构

image.png

  • api 统一封装网络请求,配置请求拦截和响应拦截
  • assets 存放静态资源
  • common 存放公共资源
  • components 存放非路由组件
  • router 存放路由
  • store 状态管理仓库
  • utils 存放工具类文件
  • views 存放路由组件,也就是页面组件

下面开始从头进行代码风格配置

使用 vite 快速创建脚手架

npm init vite@latest
按提示进行操作,然后打开项目
npm install
npm run dev

配置网络请求

在src下新建api文件夹,index.js文件

npm i axios

image.png

import axios from 'axios';
import { ElMessage } from 'element-plus';

const PORT = 4000;
const baseURL = `http://localhost:${PORT}`;

const service = axios.create({
  baseURL,
  timeout: 2000,
});

// 请求拦截器
service.interceptors.request.use(
  res => {
    return res;
  },
  error => {
    return Promise.reject(error);
  },
);

// 响应拦截器
service.interceptors.response.use(
  res => {
    return res;
  },
  error => {
    if (error.response && error.response.data) {
      const code = error.response.status;
      const msg = error.response.data.message;
      ElMessage.error(`Code: ${code}, Message: ${msg}`);
      // console.error(`[Axios Error]`, error.response);
    } else {
      ElMessage.error(`${error}`);
    }
    return Promise.reject(error);
  },
);

export default service;

配置路由

在src下新建router文件夹,index.js文件

npm install vue-router@4

image.png

import { createRouter, createWebHashHistory } from 'vue-router';
import Home from '@/views/HomeView.vue';

const routes = [
  {
    path: '/',
    name: 'home',
    component: Home,
  },
  {
    path: '/demo',
    name: 'demo',
    component: () => import('@/views/demoView.vue'),
  },
  {
    path: '/todo',
    name: 'todo',
    component: () => import('@/views/todoList.vue'),
  },
];

const router = createRouter({
  history: createWebHashHistory(),
  routes,
});

export default router;

配置状态管理机

在src下新建store文件夹,index.js文件,再分别建立各个对应的仓库

npm install pinia

image.png

// src/store/index.js
import { createPinia } from 'pinia';

const store = createPinia();

export default store;

// src/store/demo.js
import { defineStore } from 'pinia';

const demoStore = defineStore({
  id: 'user',
  state: () => {
    return {
      name: 'demo',
    };
  },
});

export default demoStore;

入口文件引入

在入口文件引入并use仓库和路由还有其他UI库

// src/main.js
import { createApp } from 'vue';
import ElementPlus from 'element-plus';
import App from './App.vue';
import router from './router';
import store from './store';

import 'element-plus/theme-chalk/index.css';

createApp(App).use(router).use(store).use(ElementPlus).mount('#app');

集成 CSS 预编译器 Sass/Less

npm i sass -D
npm i less -D

开始各种配置

一、修改 Vite 配置文件

1.Vite 配置文件 vite.config.ts 位于根目录下,项目启动时会自动读取。

2.简单配置:设置 @ 指向 src 目录、 服务启动端口、打包路径、代理等。

3.关于 Vite 更多配置项及用法,请查看 Vite 官网 vitejs.dev/config/

// vite.config.js

import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
// 如果编辑器提示 path 模块找不到,则可以安装一下 @types/node -> npm i @types/node -D
import { resolve } from 'path';

const pathResolve = dir => {
  return resolve(__dirname, dir);
};
// https://vitejs.dev/config/
export default defineConfig({
  plugins: [vue()],
  resolve: {
    alias: {
      '@': pathResolve('src'),
    },
    extensions: ['.js', '.jsx'],
  },
  base: './', // 设置打包路径
  server: {
    port: 4000, // 设置服务启动端口号
    open: true, // 设置服务启动时是否自动打开浏览器
    cors: true, // 允许跨域

    // 设置代理,根据我们项目实际情况配置
    // proxy: {
    //   '/api': {
    //     target: 'http://XXX.XXX.XXX.XXX:8000',
    //     changeOrigin: true,
    //     secure: false,
    //     rewrite: path => path.replace('/api/', '/'),
    //   },
    // },
  },
});

二、集成 EditorConfig 配置

1.去插件市场下载插件 EditorConfig for VS Code 。

image.png

2.在项目根目录下增加 .editorconfig 文件:

# Editor configuration, see http://editorconfig.org

# 表示是最顶层的 EditorConfig 配置文件
root = true

[*] # 表示所有文件适用
charset = utf-8 # 设置文件字符集为 utf-8
indent_style = space # 缩进风格(tab | space)
indent_size = 2 # 缩进大小
end_of_line = lf # 控制换行类型(lf | cr | crlf)
trim_trailing_whitespace = true # 去除行首的任意空白字符
insert_final_newline = true # 始终在文件末尾插入一个新行

[*.md] # 表示仅 md 文件适用以下规则
max_line_length = off
trim_trailing_whitespace = false

三、集成 Prettier 配置

1.安装 Prettier,并下载插件 Prettier - Code formatter

npm i prettier -D

image.png

2.在本项目根目录下创建 .prettierrc.js 文件

module.exports = {
  printWidth: 100, // 超过最大值换行
  semi: true, // 行尾添加分号
  tabWidth: 2, // 缩进字节数
  useTabs: false, // 缩进不使用tab,使用空格
  singleQuote: true, // 使用单引号代替双引号
  arrowParens: 'avoid', // (x) => {} 箭头函数参数只有一个时是否要有小括号。avoid:省略括号 | always
  bracketSpacing: true, // 在对象,括号与无序k-v之间加空格 "{ foo: bar }"
  bracketLine: true, // 标签 > 不单独一行
  tslintIntegration: false, // 不让prettier使用tslint的代码格式进行校验
  vueIndentScriptAndStyle: true,
  quoteProps: 'as-needed',
  jsxBracketSameLine: true,
  insertPragma: false,
  requirePragma: false,
  proseWrap: 'never',
  htmlWhitespaceSensitivity: 'strict',
  endOfLine: 'lf',
  trailingComma: 'all',
}

四、集成 ESLint 配置

1.安装 ESLint,并下载插件ESLint

image.png

2.配置 ESLint ESLint 安装成功后,执行 npx eslint --init,然后按照终端操作提示完成一系列设置来创建配置文件。

  • How would you like to use ESLint? (你想如何使用 ESLint?)我们这里选择 To check syntax, find problems, and enforce code style(检查语法、发现问题并强制执行代码风格)

  • 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?(你的项目是否使用 TypeScript?)我们这里选择 No

  • Where does your code run?(你的代码在哪里运行?)我们这里选择 Browser 和 Node(按空格键进行选择,选完按回车键确定)

  • How would you like to define a style for your project?(你想怎样为你的项目定义风格?)我们这里选择 Use a popular style guide(使用一种流行的风格指南)

  • Which style guide do you want to follow?(你想遵循哪一种风格指南?)我们这里选择 Airbnb: github.com/airbnb/java…

  • What format do you want your config file to be in?(你希望你的配置文件是什么格式?)我们这里选择 JavaScript

  • Would you like to install them now with npm?(你想现在就用 NPM 安装它们吗?)根据上面的选择,ESLint 会自动去查找缺失的依赖,我们这里选择 Yes,使用 NPM 下载安装这些依赖包。

注意:如果自动安装依赖失败,那么需要手动安装npm i @typescript-eslint/eslint-plugin @typescript-eslint/parser eslint-config-airbnb-base eslint-plugin-import eslint-plugin-vue -D

3.ESLint 配置文件 .eslintrc.js

上一步操作完成后, 配置.eslintrc.js 文件:

module.exports = {
  root: true,
  env: {
    browser: true,
    es2021: true,
    node: true,
  },
  extends: ['plugin:vue/essential', 'airbnb-base', 'plugin:prettier/recommended'],
  parser: 'vue-eslint-parser',
  parserOptions: {
    ecmaVersion: 'latest',
    sourceType: 'module',
    ecmaFeatures: {
      globalReturn: false,
      impliedStrict: false,
      jsx: true,
    },
  },
  plugins: ['vue'],
  rules: {
   'no-plusplus': 'off', // 允许使用一元运算符++和--
    'vue/no-multiple-template-root': 'off', // 支持vue3添加多个根节点
    'no-unused-expressions': 'off', // 允许使用&&运算符
    'import/no-extraneous-dependencies': ['error', { devDependencies: true }], // 忽略devDependencies校验
    'import/no-unresolved': 'off', // 禁用绝对路径
    'import/extensions': 'off', // 导入校验
    'no-underscore-dangle': 'off', // 下划线开头命名校验
    'no-debugger': process.env.NODE_ENV === 'production' ? 1 : 0,
    endOfLine: 0,
    'array-bracket-newline': 0, // 在数组开括号后和闭括号前强制换行
    'array-bracket-spacing': 1, // 强制数组方括号中使用一致的空格
    'block-spacing': [2, 'always'], // 禁止或强制在代码块中开括号前和闭括号后有空格
    'comma-dangle': 0, // 要求或禁止末尾逗号
    'comma-spacing': [
      // 强制在逗号前后使用一致的空格
      2,
      {
        before: false,
        after: true,
      },
    ],
    'keyword-spacing': [
      // 强制在关键字前后使用一致的空格
      2,
      {
        before: true,
        after: true,
      },
    ],
    'no-delete-var': 2, // 禁止删除变量
    'no-fallthrough': 2, // 禁止 case 语句落空
    'no-lone-blocks': 2, // 禁用不必要的嵌套块
    'no-multiple-empty-lines': [
      // 禁止出现多行空行
      2,
      {
        max: 1,
      },
    ],
    'no-trailing-spaces': 2, // 禁用行尾空格
    'no-with': 2, // 禁用 with 语句
    quotes: [
      // 强制使用一致的反勾号、双引号或单引号
      2,
      'single',
      {
        avoidEscape: true,
        allowTemplateLiterals: true,
      },
    ],
    'spaced-comment': 2, // 强制在注释中 // 或 /* 使用一致的空格
    'max-len': [
      'error',
      {
        code: 300, // 强制行的最大长度
        comments: 300, // 强制注释的最大长度;默认长度同 code
        ignoreUrls: true, // 忽略含有链接的行
        ignoreStrings: true, // 忽略含有双引号或单引号字符串的行
        ignoreTemplateLiterals: true, // 忽略包含模板字面量的行
      },
    ],
    'no-multi-assign': 2, // 禁止连续赋值
    'no-var': 2, // 要求使用 let 或 const 而不是 var
    'symbol-description': 2,
    'no-self-assign': 2, // 禁止自我赋值
    'no-self-compare': 2, // 禁止自身比较
    'no-param-reassign': 0, // 禁止对 function 的参数进行重新赋值
    'no-implied-eval': 2, // 禁止使用类似 eval() 的方法
    'no-empty-function': 2, // 禁止出现空函数
    'default-case': 2, // 要求 switch 语句中有 default 分支
    'no-unused-vars': 2, // 禁止未使用过的变量
    'vue/comment-directive': 0,
    'vue/multi-word-component-names': 0, // 组件命名随意
    'vue/no-async-in-computed-properties': 2, // 禁止在计算属性处理异步线程
    'vue/no-duplicate-attributes': [
      'error',
      {
        allowCoexistClass: true,
        allowCoexistStyle: true,
      },
    ],
    'vue/require-component-is': 2, // 禁止component内置组件没有is属性
    'vue/require-render-return': 2, // render创建VNode不能void
    'vue/no-deprecated-slot-attribute': 0,
    'vue/no-deprecated-slot-scope-attribute': 0,
    'vue/no-deprecated-v-on-native-modifier': 0,
    'vue/no-v-for-template-key': 0,
    'vue/no-mutating-props': 0,
    'vue/no-template-key': 0,
    'vue/no-v-for-template-key-on-child': 0,
  },
};

4.VSCode 在 settings.json 设置文件中,增加以下代码:

 "editor.codeActionsOnSave": {
    "source.fixAll.eslint": true
 }

五、解决 Prettier 和 ESLint 的冲突

解决两者冲突问题,需要用到 eslint-plugin-prettiereslint-config-prettier

  • eslint-plugin-prettier 将 Prettier 的规则设置到 ESLint 的规则中。
  • eslint-config-prettier 关闭 ESLint 中与 Prettier 中会发生冲突的规则。

最后形成优先级:Prettier 配置规则 > ESLint 配置规则

  • 安装插件
npm i eslint-plugin-prettier eslint-config-prettier -D
  • 在 .eslintrc.js 添加 prettier 插件
module.exports = {
  ...
  extends: [
    'plugin:vue/essential',
    'airbnb-base',
    'plugin:prettier/recommended' // 添加 prettier 插件
  ],
  ...
}

六、忽略语法检测

在根目录下新建.eslintignore文件

# 忽略eslint检测
build/*
node_modules
jsconfig.json

七、配置js规则文件

在根目录下新建jsconfig.json文件

{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@/*": [
        "src/*"
      ],
      "@c/*": [
        "src/components/*"
      ]
    },
    "target": "ES6",
    "module": "commonjs",
    "allowSyntheticDefaultImports": true
  },
  "include": [
    "src/**/*"
  ],
  "exclude": [
    "node_modules",
    "dist"
  ],
  "vueCompilerOptions": {
    "experimentalCompatMode": 2
  }
}