手把手教你—搭建Vue3企业级项目规范+基础封装配置

前言

Hi,我是大雄! 大雄最近在研究如何搭建一个简易脚手架。核心需求是输入项目命令,clone准备好的项目模板,拉到本地后,装一下依赖,就可以直接开发了。不用每次都花大量时间,去搭建项目规范和做必要的封装配置。

经过简单寻找后,发现没有符合自己预期的。于是大雄从0到1搭建一个具备完善规范的Vue3开发模板✨,并手把手带大家实现,本文你将会学到以下内容,上图👇,话不多说,我们直接开始!!

diff 复制代码
本文环境:
-   win10
-   包管理工具:pnpm
-   node版本:16.16
-   vite版本:5.0.10
-   vue版本:3.3.11

模板地址:https://github.com/1111-stu/vue3-template

项目规范搭建篇

前置包管理工具pnpm安装(已安装的小伙伴可跳过)

vue3项目推荐使用pnpm来作为包管理工具。若当前没安装过pnpm,执行下面的命令安装一下。

安装:

npm install pnpm -g

基本使用:

下载

csharp 复制代码
pnpm install 包  // 
pnpm i 包
pnpm add 包    // -S  默认写入dependencies
pnpm add -D    // -D devDependencies
pnpm add -g    // 全局安装

移除

csharp 复制代码
pnpm remove 包                            //移除包
pnpm remove 包 --global                   //移除全局包

更新

csharp 复制代码
pnpm up                //更新所有依赖项
pnpm upgrade 包        //更新包
pnpm upgrade 包 --global   //更新全局包

可能遇到的问题:

安装完成后,如果pnpm命令可以在cmd执行,无法在vscode终端运行,参考:blog.csdn.net/weixin_4806...

解决步骤:

  1. vscode右键以管理员身份打开运行(不是管理员,会无权限更改)
  2. vscode终端输入命令get-ExecutionPolicy,若显示结果是Restricted,表示关闭命令功能。
arduino 复制代码
get-ExecutionPolicy

3、输入命令set-ExecutionPolicy,输入参数RemoteSigned即可

arduino 复制代码
set-ExecutionPolicy
RemoteSigned

示例见下图所示👇

Vite自定义模板

自定义选项前置知识了解,这里引用一下大佬的图片@吃炸鸡的前端,见下图所示👇

下面开始使用vite自定义基础模板配置,这里推荐一开始就选择Eslint和Prettier的预设,项目开发中途再加,一堆坑,实属没必要踩。详细配置选择,见下图👇。

我们使用git init来生成一个仓库。然后git add . ,git commit- m"" 来进行初次提交。

项目构建完成后,切换到对应目录,安装依赖,启动项目。

测试EsLint、Prettier的格式化功能

上文,我们基于Vite选择Eslint和Prettier的预设,下面我们测试一下EsLint的格式化效果

运行pnpm lint,发现没有任何变化(Eslint中没有添加很多的规则,导致通过了)。在运行pnpm format发现,发现确实帮我们做了格式化(根据.prettierrc.json文件中的内容格式化了)。

出现上述问题的原因是,基于Vite在选择Eslint和Prettier的预设时,并没有让两者联系起来,即没有把Prettier加入到Eslint中。下文我们就来构建两者的联系。

构建Prettier和Eslint的联系

插件安装

构建Prettier和Eslint的联系,需要安装一下下面的两个插件。

eslint-config-prettier //用于解决和 Prettier 冲突的 ESLint 的配置

eslint-plugin-prettier //启用 eslint-plugin-prettier

arduino 复制代码
npm i eslint-config-prettier eslint-plugin-prettier -D

修改 ESLint 配置,使 Eslint 兼容 Prettier 规则

打开.eslintrc.cjs文件,放在extends配置项的末尾,因为extends中后引入的规则会覆盖前面的规则。

那么就可以在.prettierrc.json 中定义自己的代码风格校验。本地的prettier插件会根据这个文件来格式化,项目配置的prettier也会根据该文件来格式化。且eslint的风格与prettier风格冲突的地方会以prettier为主。

bash 复制代码
plugin:prettier/recommended 
java 复制代码
/* eslint-env node */
require('@rushstack/eslint-patch/modern-module-resolution')

module.exports = {
  root: true,
  'extends': [
    'plugin:vue/vue3-essential',
    'eslint:recommended',
    '@vue/eslint-config-typescript',
    '@vue/eslint-config-prettier/skip-formatting',
    'plugin:prettier/recommended'
  ],
  parserOptions: {
    ecmaVersion: 'latest'
  }
}
'

这时我们运行pnpm lint,可以看到已经帮我们格式化好了。下面我们只需要加入commitLint即可完成我们的规范搭建。

可能遇到的问题

如遇到以下的报错,分析日志信息,发现eslint-config-prettier出现了两个版本,猜测是其他插件包依赖的eslint-config-prettier版本是8.10.0,当前安装的是9.1.0。导致出现了版本冲突,删除9.1.0的eslint-config-prettier,装一下8.10.0就可以了。

rust 复制代码
ESLint couldn't determine the plugin "prettier" uniquely.

集成lint-staged和husky

lint-staged 是一个专门针对已放入 Git 暂存区的文件进行检查的工具

husky 能提供监听 Git 操作并执行脚本代码的能力

安装 lint-staged和husky

css 复制代码
pnpm i lint-staged husky --save-dev

配置lint-staged

在package.json中添加下面的代码,匹配暂存区所有的js,vue文件,并执行命令。

json 复制代码
  "lint-staged": {
    "*.{js,vue,jsx,tsx}": [
      "pnpm lint",
      "prettier --write",
      "eslint --cache --fix",
      "git add"
    ]
  }

配置husky,实现在git 提交时执行 lint-staged

配置脚本钩子,实现在"pnpm i"后自动执行对应的hooks

在 package.json的"scripts "中配置快捷命令,用来在安装项目依赖时生成 husky 的相关文件,配置项postinstall 或者prepare都可以。

json 复制代码
{
  // ...
  "scripts": {
    // ...
    "postinstall": "husky install"
  },
}

在 package.json 文件中,"postinstall" 是一个特殊的脚本钩子,它在 npm install(或等效的 pnpm i、yarn install)命令执行后自动运行。这个特性是由 npm(以及兼容的包管理器如 pnpm 和 yarn)提供的,旨在在安装包后自动执行某些任务。

当在项目中使用 pnpm i 命令安装依赖时,pnpm 会检查 package.json 文件中的 "scripts" 部分,特别是 "postinstall" 脚本。如果存在 "postinstall" 脚本,pnpm 将在所有包安装完成后自动执行该脚本中定义的命令。

在 package.json 中,"postinstall" 被设置为 "husky install"。这意味着每次执行 pnpm i 安装依赖后,pnpm 会自动执行 husky install 命令。这个命令的作用是安装 Husky,Husky是一个流行的 Git 钩子工具,用于在 Git 操作(如提交、推送等)时自动运行脚本。

"prepare" 和 "postinstall" 是 package.json 文件中定义的两个不同的 npm 生命周期钩子,它们在不同的时间点被触发,适用于不同的用途。下面是它们的主要区别:

  1. 触发时机:
    • "prepare" : 这个钩子在几个关键场景中被触发,包括在本地执行 npm install(没有参数)后、在作为 git 依赖安装到其他项目之前、以及在运行 npm pack 和 npm publish 之前(在打包"npm pack"或发布"npm publish"你的库到 npm 之前,会执行 "prepare" 脚本。这样可以确保在打包或发布前运行必要的构建步骤或检查。)。
    • "postinstall" : 这个钩子仅在 npm install 或等效命令(如 pnpm i 或 yarn install)执行后触发,不论是在本地安装项目依赖时,还是作为依赖安装到其他项目中时。
  1. 用途:
    • "prepare" : 通常用于在发布包之前执行构建脚本或其他准备工作。例如,如果你的包需要编译或转换代码,或者需要在发布前进行某些自动化检查,那么 "prepare" 是合适的选择。
    • "postinstall" : 通常用于在安装项目依赖后执行一些设置或配置工作。比如安装或配置工具,或执行一些只在项目初次安装依赖时需要的操作。
  1. 场景应用:
    • 使用 "prepare" 当你的包需要在发布前进行构建或准备工作,或者当你的包被作为 git 依赖安装时。
    • 使用 "postinstall" 适合在每次安装依赖时都需要执行的操作,如安装 Git 钩子或其他项目配置。

如果对npm script命令不太了解的小伙伴可以参考下方链接进行学习

juejin.cn/post/684490...

www.ruanyifeng.com/blog/2016/1...

配置完成后,执行pnpm i,会在项目根目录生成 .husky/_ 目录。

生成pre-commit文件

执行下面的命令, husky会 生成 git 操作的监听钩子脚本。

npx husky add .husky/pre-commit "npx lint-staged"

打开.husky/pre-commit 文件,可以看到以下内容,git commit时,这个脚本作为 Git 钩子运行,使用 lint-staged 来对暂存区中的文件执行代码检查。

bash 复制代码
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

npx lint-staged

到此你就完成了git commit时自动去触发eslint的检测和修复。修改代码后git add . git commit,就可以发现代码检测和修复生效了。

补充

值得注意的是,当前这种方式创建的项目,规则很少,适合自己自定义eslint规则。 因为刚初始化好的eslint规则只加入了一些推荐的eslint。(相对来说,踩坑较少)

如果自己需要自定义规则的话,可以在.prettierrc.json或者.eslintrc.cjs的rules中去添加自己的规则。.prettierrc.json定义的规则>.eslintrc.cjs中定义的规则。

如果你需要一套完善的git 提交规范校验,例如:运行 git commmit -m 'xxx' 时,用来检查 xxx 是否满足要求的提交规范, 下文我们一起来实现吧!!🐳

Git 提交规范建设校验commit格式

前置知识

commitlint是什么?

当我们运行 git commmit -m 'xxx' 时,用来检查 xxx 是否满足固定格式的工具。简单来说,就是制定提交规范

提交的格式,与常见的规范

  • 提交格式 (注意冒号后面有空格)

git commit -m [optional scope]:

type :用于表明我们这次提交的改动类型,是新增了功能?还是修改了测试代码?又或者是更新了文档?

optional scope:一个可选的修改范围。用于标识此次提交主要涉及到代码中哪个模块。

description:一句话描述此次提交的主要内容,做到言简意赅。

  • 常用的 type 类型
安装commitlint
css 复制代码
pnpm i --save-dev @commitlint/config-conventional @commitlint/cli
生成commit-msg文件

文件中可以配置在 ****git commit 时对 commit 信息的校验指令

可手动创建文件再输入文件内容,但是建议使用命令创建,命令如下:

sql 复制代码
npx husky add .husky/commit-msg 'npx --no -- commitlint --edit "$1"'

上面命令执行成功后会在 .husky 目录下生成一个 commit-msg 文件,该文件的内容如下,表示在 git commit 前执行一下 npx --no -- commitlint --edit $1 指令。

当你进行 Git 提交时,这个 commit-msg 文件会被触发,它先初始化 Husky,然后使用commitlint 来检查你的提交信息是否符合预设的规范。

bash 复制代码
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

npx --no -- commitlint --edit $1
项目根目录创建名为commitlint.config.js的文件,配置提交规范
javascript 复制代码
module.exports = {
    extends: ["@commitlint/config-conventional"],
    // 定义规则类型
    rules: {
      // type 类型定义,表示 git 提交的 type 必须在以下类型范围内
      "type-enum": [
        2,
        "always",
        [
          "feat", // 增加新功能
          "fix", // 修复 bug
          "del", // 删除功能
          "update", // 更新功能
          "docs", // 文档相关的改动
          "style", // 不影响代码逻辑的改动,例如修改空格,缩进等
          "build", // 构造工具或者相关依赖的改动
          "refactor", //  代码重构
          "revert", // 撤销,版本回退
          "test", // 添加或修改测试
          "perf", // 提高性能的改动
          "chore", // 修改 src 或者 test 的其余修改,例如构建过程或辅助工具的变动
          "ci", // CI 配置,脚本文件等改动
        ],
      ],
      // subject 大小写不做校验
      "subject-case": [0],
    },
    plugins: [
      {
        rules: {
          "commit-rule": ({ raw }) => {
            return [
              /^[(feat|fix|del|update|docs|style|build|refactor|revert|test|perf|chore)].+/g.test(raw),
              `commit备注信息格式错误,格式为 <[type] 修改内容>,type支持${types.join(",")}`,
            ];
          },
        },
      },
    ],
  };
可能遇到的问题

commitlint.config.js文件,提交时候ESlint会报错,解决方法是,根目录下新建.eslintignore文件,配置路径,忽略对这个文件的Eslint校验。

添加下面这行代码,为什么是.cjs?见下面的报错解决(如未遇到,改为.js就好)

arduino 复制代码
commitlint.config.cjs

添加.eslintignore后,若git commit如果忽略校验没有奏效。检查package.json文件,

如果lint的配置存在--ignore-path,去掉--ignore-path就可以了。原因是:如果eslint后加了 --ignore-path 后,.eslintignore的配置会失效

json 复制代码
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore"

改成:

json 复制代码
 "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix"

参考解决方法:

www.cnblogs.com/zouzhongxin...

juejin.cn/post/700760...

其他忽略ESlint的校验的方法:blog.csdn.net/a7442358/ar...

如果出现下面的问题,重命名commitlint.config.js为commitlint.config.cjs

提供Commit提示信息,实现交互式commit,简化提交的负担

背景:

如果提交的type比较多,每次都需要翻看一下。那么接入commitizen、cz-customizable,实现交互式提交会是个不错的解决方法。交互效果见下图👇

安装commitizen、cz-customizable

commitizen: commit命令行提示

cz-customizable:自定义的Commitizen的交互信息

csharp 复制代码
pnpm add commitizen cz-customizable -g

详细可参考:github.com/leonardoana...

配置信息

根目录新建一个.cz-config.cjs文件(一开始创建的是.js文件,提示使用cjs代替,于是修改为.cjs文件),在根目录创建的.cz-config.cjs 添加以下代码,自定义commit提示内容。

css 复制代码
module.exports = {
    types: [
      { value: 'feat', name: 'feat:     新功能' },
      { value: 'fix', name: 'fix:      修复bug' },
      { value: 'del', name: 'del:     删除功能'},
      {value: 'update', name: 'update:     更新功能'},
      { value: 'docs', name: 'docs:     文档变更' },
      { value: 'style', name: 'style:    代码格式(不影响代码运行的变动)' },
      {
        value: 'refactor',
        name: 'refactor: 重构(既不是增加feature,也不是修复bug)'
      },
      { value: 'perf', name: 'perf:     性能优化' },
      { value: 'test', name: 'test:     测试用例变更' },
      { value: 'chore', name: 'chore:    构建过程或辅助工具的变动' },
      { value: 'revert', name: 'revert:   回退' },
      { value: 'build', name: 'build:    打包' },
      //ci变动
      { value: 'ci', name: 'ci:      CI 配置或脚本文件的改动' }
    ],
    // override the messages, defaults are as follows
    messages: {
      type: '请选择提交类型:',
      // scope: '请输入文件修改范围(可选):',
      // used if allowCustomScopes is true
      customScope: '请输入修改范围(可选):',
      subject: '请简要描述提交(必填):',
      body: '请输入详细描述(可选,待优化去除,跳过即可):',
      // breaking: 'List any BREAKING CHANGES (optional):\n',
      footer: '请输入要关闭的issue(待优化去除,跳过即可):',
      confirmCommit: '确认使用以上信息提交?(y/n/e/h)'
    },
    allowCustomScopes: true,
    // allowBreakingChanges: ['feat', 'fix'],
    skipQuestions: ['body', 'footer'],
    // limit subject length, commitlint默认是72
    subjectLimit: 72
  }
  

package.json添加以下内容:

json 复制代码
"scripts" : {
  ...
  "commit": "./node_modules/cz-customizable/standalone.js"
}
erlang 复制代码
...
"config": {
    "commitizen": {
      "path": "./node_modules/cz-customizable"
    },
    "cz-customizable": {
      "config": "./.cz-config.cjs"
    }
  }
}

配置完成后,输入git cz,出现下面的内容,说明接入成功了

后面的提交,使用git cz来代替git commit

husky hooks接入

输入git cz后,细心的小伙伴会发现,上文的ESlint、Prettier的校验没有生效,我们希望commitizen和ESlint、Prettier,是一起集成到husky的hooks里面,输入git cz自动执行配置的hooks。达到下图的效果

其实很简单,在 Husky 的 commit-msg 钩子中调用 commitizen。即在 .husky/commit-msg 文件中:添加下面的代码

arduino 复制代码
npx cz --hook || true

随便找个文件修改一下,"git add ."、"git cz"查看效果,出现下图说明配置成功了。

其他可扩展的规范校验接入

  • 集成Style-lint,提交时校验并格式化css、less、scss代码
  • 接入Eslint-plugin-filenames,实现对文件夹文件名的命名校验
  • 补充Prettier、Eslint自定义规则

项目基础配置

配置全局 scss 样式文件

安装saas

css 复制代码
pnpm i sass -d

新增样式文件

src/assets 下新增 style 文件夹,用于存放全局样式文件,新建 main.scss, 设置一个用于测试的颜色变量

css 复制代码
$primary-color: #007bff;
$warning-color: #ffc107;

如何将这个全局样式文件全局注入 到项目中呢?vite.config.ts添加下方配置

css 复制代码
  css:{
    preprocessorOptions:{
      scss:{
        additionalData:'@import "@/assets/style/main.scss";'
      }
    }
  },

组件内使用

css 复制代码
.title {
    color: $primary-color;
}

配置路径别名

依赖安装

@types/node 是一个 NPM 包,它的作用是为 Node.js 环境中的 JavaScript 提供 TypeScript 类型定义。当在 TypeScript 项目中使用 Node.js 时,这个包非常重要

css 复制代码
pnpm i @types/node -D

vite.config.ts:第一种配置

php 复制代码
// path 模块提供了一些工具函数,用于处理文件与目录的路径
import { resolve } from 'path'
// 使用 defineConfig 工具函数,这样不用 jsdoc 注解也可以获取类型提示
import { defineConfig } from 'vite'

/** 当前执行 node 命令时文件夹的地址(工作目录) */
const root: string = process.cwd()

/** 路径拼接函数,简化代码 */
const pathResolve = (dir: string): string => resolve(root, dir)

export default defineConfig({
  resolve: {
    alias: [
      // 设置 `@` 指向 `src` 目录
      { find: '@', replacement: pathResolve('src') },
      // 设置 `@assets` 指向 `src/assets` 目录
      { find: '@assets', replacement: pathResolve('src/assets') },
      // 设置 `@components` 指向 `src/components` 目录
      { find: '@components', replacement: pathResolve('src/components') },
      // 设置 `@views` 指向 `src/views` 目录
      { find: '@views', replacement: pathResolve('src/views') },
      // 设置 `@utils` 指向 `src/utils` 目录
      { find: '@utils', replacement: pathResolve('src/utils') }
      // 可以根据需要添加更多的别名
    ],
  },
})

vite.config.ts:第二种配置

javascript 复制代码
import { fileURLToPath, URL } from 'node:url'

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [vue()],
  resolve: {
    alias: {
      '@': fileURLToPath(new URL('./src', import.meta.url)),
      '@assets': fileURLToPath(new URL('./src/assets', import.meta.url)),
      '@components': fileURLToPath(new URL('./src/components', import.meta.url)),
      '@views': fileURLToPath(new URL('./src/views', import.meta.url)),
      '@utils': fileURLToPath(new URL('./src/utils', import.meta.url))
    }
  }
})

tsconfig.json声明paths

无论是使用配置一还是配置二,都需要在tsconfig.json 声明paths

perl 复制代码
{
  "compilerOptions": {
    // ... 其他已有的配置

    // 设置基础 URL 为项目根目录
    "baseUrl": ".",

    // 定义别名
    "paths": {
      "@/": ["./src/"],
      "@assets/*": ["./src/assets/*"],
      "@components/*": ["./src/components/*"],
      "@views/*": ["./src/views/*"],
      "@utils/*": ["./src/utils/*"]
    }
  },

  // ... 其他 TypeScript 配置,如 include、exclude 等
}

请求封装

安装axios

css 复制代码
pnpm i axios

实际使用中可以根据项目修改,比如RESTfulapi中可以自行添加put和delete请求,ResType也可以根据后端的通用返回值动态的去修改

简单二次封装

新增 service 文件夹,service 下新增 http.ts 文件、 api 文件夹。api文件夹下做接口做统一管理,按照模块来划分。详见下方文件树👇。

service

├── api

│ └── login

│ ├── login.ts

│ └── types.ts

└── http.ts

http.ts代码

typescript 复制代码
//http.ts
import axios from 'axios'

// 设置请求头和请求路径
axios.defaults.baseURL = '/api'
axios.defaults.timeout = 10000
axios.defaults.headers.post['Content-Type'] = 'application/json;charset=UTF-8'
axios.interceptors.request.use(
  (config) => {
    const token = window.sessionStorage.getItem('token')
    if (token) {
      //@ts-ignore
      config.headers.token = token
    }
    return config
  },
  (error) => {
    return error
  }
)
// 响应拦截
axios.interceptors.response.use((res) => {
  if (res.data.code === 111) {
    sessionStorage.setItem('token', '')
    // token过期操作
  }
  return res
})

interface ResType<T> {
  code: number
  data?: T
  msg: string
  err?: string
}
interface Http {
  get<T>(url: string, params?: unknown): Promise<ResType<T>>
  post<T>(url: string, params?: unknown): Promise<ResType<T>>
  upload<T>(url: string, params: unknown): Promise<ResType<T>>
  download(url: string): void
}

const http: Http = {
  get(url, params) {
    return new Promise((resolve, reject) => {
      axios
        .get(url, { params })
        .then((res) => {
          resolve(res.data)
        })
        .catch((err) => {
          reject(err.data)
        })
    })
  },
  post(url, params) {
    return new Promise((resolve, reject) => {
      axios
        .post(url, JSON.stringify(params))
        .then((res) => {
          resolve(res.data)
        })
        .catch((err) => {
          reject(err.data)
        })
    })
  },
  upload(url, file) {
    return new Promise((resolve, reject) => {
      axios
        .post(url, file, {
          headers: { 'Content-Type': 'multipart/form-data' },
        })
        .then((res) => {
          resolve(res.data)
        })
        .catch((err) => {
          reject(err.data)
        })
    })
  },
  download(url) {
    const iframe = document.createElement('iframe')
    iframe.style.display = 'none'
    iframe.src = url
    iframe.onload = function () {
      document.body.removeChild(iframe)
    }
    document.body.appendChild(iframe)
  },
}
export default http

login.ts代码

javascript 复制代码
import http from '@/service/http'
import * as T from './types'

const loginApi: T.ILoginApi = {
    login(params){
        return http.post('/login', params)
    }

}
export default loginApi

types.ts代码

typescript 复制代码
export interface ILoginParams {
    userName: string
    passWord: string | number
}
export interface ILoginApi {
    login: (params: ILoginParams)=> Promise<any>
}

至此,一个简单地请求封装完成了!!!!

现成的三方库

除了手动封装 axios外 ,这里推荐一个 vue3 的请求库: VueRequest👇。官网链接: www.attojs.com/

全局状态管理-Pinia的配置

前置知识

pinia的模式有两种:options APIcomposition API。 推荐使用composition API 模式定义store,符合Vue3 setup 的编程模式,让结构更加扁平化。

下图是在项目初始化的时候,vite就帮我们预设好了,我们基于这个基础上做一些改进。

创建总入口

在src/store目录下创建一个入口index.ts,其中包含一个注册函数registerStore(),其作用是把整个项目的store都提前注册好,最后把所有的store实例挂到appStore透传出去。这样以后,只要我们在项目任何组件要使用pinia时,只要import appStore进来,取对应的store实例就行。

typescript 复制代码
// src/store/index.ts
import { useCounterStore } from "./counter";

export interface IAppStore {
    useCounter: ReturnType<typeof useCounterStore>;
    // 其他store...
}

const appStore:IAppStore = {} as IAppStore;

/**
 * 注册app状态库
*/
export const registerStore = () => {
    appStore.useCounter = useCounterStore();
    // 其他store...
}

export default appStore;

composition API模式下不支持某些内置方法,如$reset(),解决方式是重写一下reset方法。

src/utils/storeTools

typescript 复制代码
// src/utils/storeTools
// Pinia store基础集成方法
import type { IAppStore } from '@/store'

/**
 * 重构$reset()
 * @desc 因为setup模式编程不支持reset方法,这里要手动重构
 * @param appStore
 */
export const initResetFun = (appStore: IAppStore) => {
  // 遍历 appStore 中的所有项。
  Object.values(appStore).forEach((item) => {
    // 创建一个空对象 initState 用于存储初始状态。
    const initState = {} as Record<string, any>

    // 遍历 item 的 $state 对象的所有条目。
    Object.entries(item.$state).forEach((item) => {
      // 将每个状态的初始值存储到 initState 对象中。
      initState[item[0]] = item[1]
    })

    // 为每个 store 项定义一个 reset 方法。
    item.reset = () => {
      // 遍历 $state 对象的所有状态。
      Object.keys(item.$state).forEach((state) => {
        // 将每个状态重置为其初始值。
        item.$state[state] = initState[state]
      })
    }
  })
}

src/store/index.ts

typescript 复制代码
//src/store/index.ts
//导入counter.ts
import { useCounterStore } from "./counter";
import { initResetFun } from '@/utils/storeTools'

export interface IAppStore {
    useCounter: ReturnType<typeof useCounterStore>;
    // 其他store...
}

const appStore:IAppStore = {} as IAppStore;

/**
 * 注册app状态库
*/
export const registerStore = () => {
    appStore.useCounter = useCounterStore();
    // 其他store...

    //重写reset方法
    initResetFun(appStore);
}

export default appStore;

简单修改一下counter Store的内容

javascript 复制代码
//src/store/counter.ts
import { ref, computed } from 'vue'
import { defineStore } from 'pinia'

export const useCounterStore = defineStore('counter', () => {
  const count = ref(0)
  const doubleCount = computed(() => count.value * 2)

  const increment= () => {
    count.value++
  }
  
  const decrement= () => {
    count.value--
  }

  return { count, doubleCount, increment,decrement }
})

总线注册

src/main.ts项目总线注册

javascript 复制代码
import './assets/main.css'

import { createApp } from 'vue'
import { createPinia } from 'pinia'
import { registerStore } from './store'

import App from './App.vue'
import router from './router'

const app = createApp(App)

app.use(createPinia())
app.use(router)

//注册pinia状态管理库
registerStore();

app.mount('#app')

具体业务组件使用

xml 复制代码
//src/components/PiniaSetup.vue
<script setup lang='ts'>
import { storeToRefs } from 'pinia';
import appStore from '@/store/index';

// Pinia composition API 模式
const { count, doubleCount } = storeToRefs(appStore.useCounter)
const { increment,decrement } = appStore.useCounter

</script>
<template>
    <div>
        <h1>Pinia composition 模式!</h1>
        <p>Pinia state:count=<b>{{ count }}</b></p>
        <p>pinia getters:doubleCount=<b>{{ doubleCount }}</b></p>
        <button @click="increment">
            increment
        </button>
        <button @click="decrement">decrement</button>
    </div>
</template>

<style lang='scss' scoped></style>

打包解耦

为了让appStore 实例与项目解耦,在构建时要把appStore 抽取到公共chunk,在vite.config.ts 做以下配置

javascript 复制代码
  build: {
    // ...其他配置
    rollupOptions: {
      output: {
        manualChunks(id) {
          // 将pinia的全局库实例打包进vendor,避免和页面一起打包造成资源重复引入
          if (id.includes(path.resolve(__dirname, 'src/store/index.ts'))) {
            return 'vendor';
          }
        }
      }
    }
  }

其他:

更多Pinia函数式(composition API)用法参考:juejin.cn/post/708903...

Vite.config.ts的全部配置内容

javascript 复制代码
import { fileURLToPath, URL } from 'node:url'
import path from 'node:path'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [vue()],
  css:{
    preprocessorOptions:{
      scss:{
        additionalData:'@import "@/assets/style/main.scss";'
      }
    }
  },
  resolve: {
    alias: {
      '@': fileURLToPath(new URL('./src', import.meta.url)),
      '@assets': fileURLToPath(new URL('./src/assets', import.meta.url)),
      '@components': fileURLToPath(new URL('./src/components', import.meta.url)),
      '@views': fileURLToPath(new URL('./src/views', import.meta.url)),
      '@utils': fileURLToPath(new URL('./src/utils', import.meta.url))
    }
  },
  build: {
    // ...其他配置
    rollupOptions: {
      output: {
        manualChunks(id) {
          // 将pinia的全局库实例打包进vendor,避免和页面一起打包造成资源重复引入
          if (id.includes(path.resolve(__dirname, 'src/store/index.ts'))) {
            return 'vendor';
          }
        }
      }
    }
  }
})

环境变量配置

前置知识

vite 提供了两种模式:具有开发服务器的开发模式(development)和生产模式(production)

在项目根目录下,你可以创建一个或多个环境文件。这些文件以 .env 开头,后面可以跟上为特定模式(如 development、production)指定的后缀。例如:

  • .env:在所有的环境中加载。
  • .env.local:在所有的环境中加载,但会被 git 忽略。
  • .env.development:只在开发环境中加载。
  • .env.production:只在生产环境中加载。

新建.env.development、.env.production 文件

根目录新建 .env.development 文件,内容如下:

ini 复制代码
NODE_ENV=development

VITE_APP_WEB_URL= 'YOUR WEB URL'

根目录新建 .env.production 文件 ,内容如下:

ini 复制代码
NODE_ENV=production

VITE_APP_WEB_URL= 'YOUR WEB URL'

组件中使用:

arduino 复制代码
console.log(import.meta.env.VITE_APP_WEB_URL)

打包区分开发环境和生产环境

package.json 的scripts中加入下面内容

json 复制代码
    "build:dev": "vite build --mode development",
    "build:pro": "vite build --mode production"

Vite 常用打包优化配置

详见往期文章:基于Vite构建的Vue3+Ts项目打包优化全过程 - 掘金

更多Vite配置细节参考:Vite

Vite常用插件

插件官方文档:vitejs.cn/plugins/

  • @vitejs/plugin-vue:这是 Vue 3 项目中的官方插件,用于支持单文件组件(.vue 文件)。
  • @vitejs/plugin-react:用于支持 React 项目,提供 JSX 转换和相关优化。
  • @vitejs/plugin-legacy: 为打包后的文件提供传统浏览器兼容性支持
  • vite-plugin-svelte:用于集成 Svelte,一个类似于 Vue 和 React 的现代前端框架。
  • vite-plugin-env-compatible:提供对 .env 环境变量文件的支持。
  • vite-plugin-pages:用于自动生成路由,特别适用于 Vue 和 React 项目。
  • vite-plugin-windicss:集成 Windi CSS,一种高效的实用工具优先的 CSS 框架。
  • vite-plugin-compression:用于在构建过程中压缩资源,支持 gzip 或 brotli 压缩。
  • vite-plugin-pwa:添加渐进式网络应用(PWA)支持。
  • vite-plugin-svg-icons:用于优化 SVG 文件的处理和使用。
  • vite-plugin-eslint:集成 ESLint,确保代码质量和风格的一致性。
  • vite-plugin-stylelint:集成 Stylelint,用于 CSS/SCSS/Less 等样式文件的代码质量检查。
  • unplugin-vue-components :组件的按需自动导入
  • vite-plugin-compression :使用 gzip 或者 brotli 来压缩资源
  • .....

参考文章

相关推荐
Fan_web12 分钟前
jQuery——事件委托
开发语言·前端·javascript·css·jquery
安冬的码畜日常13 分钟前
【CSS in Depth 2 精译_044】第七章 响应式设计概述
前端·css·css3·html5·响应式设计·响应式
莹雨潇潇1 小时前
Docker 快速入门(Ubuntu版)
java·前端·docker·容器
Jiaberrr1 小时前
Element UI教程:如何将Radio单选框的圆框改为方框
前端·javascript·vue.js·ui·elementui
Tiffany_Ho2 小时前
【TypeScript】知识点梳理(三)
前端·typescript
安冬的码畜日常3 小时前
【D3.js in Action 3 精译_029】3.5 给 D3 条形图加注图表标签(上)
开发语言·前端·javascript·信息可视化·数据可视化·d3.js
小白学习日记4 小时前
【复习】HTML常用标签<table>
前端·html
丁总学Java4 小时前
微信小程序-npm支持-如何使用npm包
前端·微信小程序·npm·node.js
yanlele4 小时前
前瞻 - 盘点 ES2025 已经定稿的语法规范
前端·javascript·代码规范