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

news/2024/11/17 4:45:24/文章来源:https://www.cnblogs.com/alwn/p/18382158

前言

如何搭建一个简易脚手架。核心需求是输入项目命令,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 来压缩资源
  • .....
 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/787862.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

WPF 模板

一、数据模板继承了ItemConrol的控件对象(如ListView、ListBox、DataGrid、TabControl等等),都可以使用数据模板DataTemplate。 数据模板的作用在于决定每个Item中的数据的展示形式。 普通控件通过Template属性来定义模板,而子项容器控件则通过ItemTemplate属性来定义子项模…

算法与数据结构——队列

队列 队列(queue)是一种遵循先入先出规则的线性数据结构。队列模拟了排队现象,即新来的人不断加入队列尾部,而队列头部的人逐个离开。 如图所示,我们将队列头部称为“队首”,尾部称为“队尾”,将把元素加入队列尾部的操作称为“入队”,删除队首元素的操作称为“出队”。…

AD采集卡:FMC210-1路1Gsps AD、1路2.5Gsps DA的FMC子卡 信号采集卡

FMC210-1路1Gsps AD、1路2.5Gsps DA的FMC子卡 一、板卡概述FMC-1AD2DA是我司自主研发的一款1路1G AD采集、1路2.5G DA回放的FMC子卡。板卡采用标准FMC子卡架构,可方便的与其他FMC板卡实现高速互联,可广泛用于高频模拟信号采集、雷达系统测试等场合。    二、 功能介绍 2.1 …

BAdam A Memory Efficient Full Parameter Optimization Method for Large Language Models

目录概BAdam代码Luo Q., Yu H. and Li X. BAdam: A memory efficient full parameter optimization method for large language models. arXiv preprint, 2024.概 本文介绍了一种 Block corrdinate descent (BCD) 的训练方式. BAdam当模型本身很大的时候, 训练它会成为一个很大…

Origin2024图表中如何直接移除异常点?

平时我们在使用Origin绘图后,可能会发现有一两个「异常点」,这个时候,我们可能会返回工作表,将异常的数据去除,但可能不知道是哪个数据,因为图和数据有时候不太好对应起来; 本期给大家分享做好图之后直接选择移除异常点功能,并且数据表中的数据也会相应的去除,是一个很…

vue3uniapps使用富文本mp-html插件

1. 实现效果 具体需求:顶部是搜索栏,包括搜索结果个数,目前跳到第几个,包含上一个、下一个按钮。富文本区域关键词高亮黄色,当前关键词为高亮橙色。如图2. 版本号 用到vue3 和 uniapp , mp-html 插件版本是v2.5.0, 插件地址:https://ext.dcloud.net.cn/plugin?id=805 用…

西安电子科技大学2021级计算机科学与技术专业重要成绩排名参考

从个人经历分享,所在计算机科学与技术专业的大学期间的重要成绩排名参考【非官方】,仅供选择方向参考!Hello World本文来自博客园,作者:LZHMS,转载请注明原文链接:https://www.cnblogs.com/LZHMS/p/18382071

OKR 如何激励团队有目标地工作

最近,关于90后和Z一代对为目标驱动的公司工作的渴望已经写了很多。这很可能成为围绕工作场所将如何演变的决定性叙述之一,尤其是在人才争夺战持续的情况下。 “带着目的工作 “需要的不仅仅是一个鼓舞人心的公司使命,它是每个领导者必须积极培养团队的东西。在这里,我将介绍…

查壳工具之Exeinfo PE

简介 Exeinfo PE是一款免费、专业的程序查壳软件,可以查看exe、dll程序的编译信息,开发语言,是否加壳,壳的种类以及入口地址等信息。 Exeinfo PE下载地址:https://github.com/ExeinfoASL/ASL GitHub地址打开后,直接选择Code--Download ZIP下载压缩包即可。 Exeinfo PE使用…

JavaScript 的事件循环、宏任务、微任务

JavaScrtipt 执行顺序 首先,必须要明确,在JavaScript中,所有任务都在主线程上执行。任务执行过程分为同步任务和异步任务两个阶段。异步任务的处理经历两个主要阶段:Event Table(事件表)和 Event Queue(事件队列)。Event Table存储了宏任务的相关信息,包括事件监听和相…

One-for-All:上交大提出视觉推理的符号化与逻辑推理分离的新范式 | ECCV 2024

通过对多样化基准的严格评估,论文展示了现有特定方法在实现跨领域推理以及其偏向于数据偏差拟合方面的缺陷。从两阶段的视角重新审视视觉推理:(1)符号化和(2)基于符号或其表示的逻辑推理,发现推理阶段比符号化更擅长泛化。因此,更高效的做法是通过为不同数据领域使用分…

投标流程

电脑不能安装WPS 国家电网新一代电子商务平台 (sgcc.com.cn) https://sign.utcsoft.com/utcbpc/service/download.html?q=ecp20 下载安装“供应商投标工具”