概览
前端技术的迭代速率较快,相干的框架和知识点也越来越多,在开发新项目时可供选择的技术栈也越来越多。对于必要多人团队写作的场景下,怎样保证各人共同的编码规范,用一套约定俗成的尺度举行开发至关重要,可以更好的提升本身的开发体验。
接下来,重要解说不同前端领域的编码规范以合格式化代码,本质上底层照旧前端三件套的编程头脑。
焦点:语义化、代码清楚明确、注释美满,代码配置 < 沟通 < 约定 < 文档 < 体系
语雀原文档:https://www.yuque.com/xixifusi-eicch/ma8xgr/skhybs2u0glzs5p5?singleDoc# 《编码规范》
资源
推荐几款代码规范文档库,发起收藏! - 掘金
TGideas文档库
Google Style Guides
- https://guide.aotu.io/index.html(首选)
- https://github.com/airbnb/javascript
- https://github.com/airbnb/javascript/tree/master/react
- https://github.com/ryanmcdermott/clean-code-javascript
- https://github.com/Microsoft/TypeScript/wiki/Coding-guidelines
情况
- VUE 新项目推荐开发情况:Vue 3.4.1 + Node 20.11.1 + Pnpm 9.6 + Vite 5.2 + TypeScript 5.2.2
- Node 新项目推荐开发情况:Node 20.11.1 + TypeScript 5.2.2
目录
我们平常开发新项目标时候,往往会利用第三方工具举行生成基础的项目模板,这些文件目录代表的意思如下:
名称寄义.vscodevscode 调试步伐、样式等当地化配置public公共文件夹,存放图标、全局配置api接口文件夹assets静态资源存放,包含字体图标、图片等components抽离的全局公共组件(页面逻辑过多,可分为一级、二级、三级)config相干的配置文件,一样平常是静态相干的hooks具有响应式数据特性/生命周期的方法抽离layout页面的布局组件,包含侧边栏、头部、底部、内容区,单页面应用较为常见router路由组件script脚本执行文件夹,存放常用的主动化脚本store当前页面的数据存储状态(可选是否持久化)style页面的样式,会包含 重置样式、组件样式、自定义样式 等utils工具函数,包含正则、http请求、工具函数等views项目标视图页面,详细可拆分typesTS 项目专有,包含 主动引入、路由声明等 .d.ts 文件.etc一些组件配置,包含 vite 情况、eslint、prettier 样式、.gitignore 等 上述标红的文件夹会有多个文件,一样平常的开发规范是统一通过 index.ts 暴露收口,在 modules 举行编写不同模块的代码逻辑,下面是针对大型项目一些补充说明:
- views 满足上面的定名规则外,页面入口可接纳 index 举行定名
- 静态资源(图片、svg等)使用 下划线 ‘_’,逻辑文件 JS/TS 使用 小驼峰定名(camelCase)
- 所有业务逻辑文件夹(包含 css 文件名 简单 vue 文件可以接纳大驼峰定名)统一接纳 **短横线 ‘-’ **举行连接,原则上不凌驾 3 个单词
工具
前端工具的美满,使得部分样式都给我们规范好了,极大的提高了个人开发者的便利性,但是这种工具的限制链路不是越多越好,尽量把常用的挑出来举行开发,这里可以参考我之前的文章。
前端项目工程化之代码规范_扁平化 代码开发规范-CSDN博客
⭐pnpm 9
新项目开发统一接纳 pnpm ( node 情况 18+ )安装包,并设置淘宝镜像源。
- // 1. 安装 pnpm
- npm install -g pnpm
- // 2. 设置淘宝镜像源
- pnpm config set registry https://registry.npmmirror.com/
- // 3. 在项目中使用 pnpm 安装依赖包
- pnpm install
复制代码 ⭐ESLint 8
ESLint 是一个静态代码分析工具,重要用于查找和修复代码中的潜伏标题、错误、不划一和不推荐的模式。是资助你提高代码质量、制止常见的错误,以及确保团队成员遵循统一的编码约定。
这里必要根据不同的项目框架举行详细的配置,如:VUE React TS,这里网上有很多参考资料举行参考
- // 1. 安装 eslint 和 vue 插件
- pnpm i eslint@8 eslint-plugin-vue -D
- // 2. 安装 prettier 和 相关插件
- pnpm i prettier@3 eslint-config-prettier eslint-plugin-prettier -D
- // 3. TS 解析器安装
- pnpm i @typescript-eslint/parser -D
- // 4. TS
- pnpm i @typescript-eslint/eslint-plugin -D
复制代码- module.exports = {
- root: true,
- parser: 'vue-eslint-parser',
- // 环境设置
- env: {
- es6: true,
- node: true,
- browser: true
- },
- // 解析选项
- parserOptions: {
- sourceType: 'module',
- ecmaVersion: 'latest',
- // TS 解析器配置
- parser: '@typescript-eslint/parser'
- },
- // 插件
- plugins: ['vue'],
- // 引入的语法校验规则
- extends: [
- 'plugin:vue/vue3-recommended',
- 'plugin:@typescript-eslint/recommended',
- 'prettier',
- 'plugin:prettier/recommended' // eslint-plugin-prettier 规则放到最后
- ],
- // 自定义规则
- rules: {
- 'no-console': 'off',
- 'no-debugger': 'off',
- 'no-unused-vars': 'warn',
- 'prefer-rest-params': 'warn',
- 'vue/html-indent': 'off',
- 'vue/html-self-closing': 'off',
- 'vue/max-attributes-per-line': 'off',
- 'vue/multi-word-component-names': 'off',
- 'vue/no-setup-props-destructure': 'warn',
- // TS 冲突规则
- '@typescript-eslint/no-unused-vars': 'off',
- // 强制组件在模板中使用 kebab-case
- 'vue/component-name-in-template-casing': ['warn', 'kebab-case'],
- // 自闭和单标签元素
- 'vue/html-self-closing': [
- 'warn',
- {
- html: {
- void: 'always',
- normal: 'always',
- component: 'always'
- },
- svg: 'always',
- math: 'always'
- }
- ]
- },
- // 配置忽略文件
- ignorePatterns: ['node_modules/', 'dist/', 'public/', 'pnpm-lock.yaml']
- }
复制代码 ⭐Prettier 3
Prettier是一个代码格式化工具,专注于对代码举行格式化,使其符合划一的风格规范。它会主动调整代码的缩进、换行、引号等,确保代码在不同的编辑器和情况中具有划一的表面。
简单来说,ESLint更注重你的代码是否符合规范,Pretter则是为你提供了按照规范格式化代码的能力。
- {
- "tabWidth": 2,
- "semi": false,
- "printWidth": 90,
- "endOfLine": "auto",
- "singleQuote": true,
- "trailingComma": "none"
- }
复制代码 忽略格式化的文件本质上和 .gitignore 大差不差(可以忽略)
- # Logs
- logs
- *.log
- npm-debug.log*
- yarn-debug.log*
- yarn-error.log*
- pnpm-debug.log*
- lerna-debug.log*
- node_modules
- dist
- dist-ssr
- *.local
- # Editor directories and files
- .vscode/*
- !.vscode/extensions.json
- .idea
- .DS_Store
- *.suo
- *.ntvs*
- *.njsproj
- *.sln
- *.sw?
- # build report file
- report.html
复制代码 ⭐Husky 9
https://juejin.cn/post/7355535964612100108
https://juejin.cn/post/7041768022284976165
这个重要是限制提交的 commit 信息规范即可,不能随意提交,对此没要求的可以不做。
- // 1. 安装钩子触发工具 husky
- pnpm i husky@9 -D
- // 2. 执行配置脚本
- npx husky init
- // 3. 安装 commitlint 规范提交信息
- pnpm i @commitlint/cli @commitlint/config-conventional -D
- // 4. 新建 commitlint.config.ts 校验文件
- // 5. 新建 commit-msg husky 规范提示消息
- #!/bin/sh
- npx --no-install commitlint -e
- // 6. husky 新建 pre-commit
- pnpm run lint
- // 7. 提交前 eslint 检查代码
- 具体参考 package.json 命令配置
复制代码- module.exports = {
- extends: ['@commitlint/config-conventional'],
- rules: {
- 'type-enum': [
- 2,
- 'always',
- [
- 'feat', // 新功能(feature)
- 'bug', // 此项特别针对bug号,用于向测试反馈bug列表的bug修改情况
- 'fix', // 修补bug
- 'ui', // 更新 ui
- 'docs', // 文档(documentation)
- 'style', // 格式(不影响代码运行的变动)
- 'perf', // 性能优化
- 'release', // 发布
- 'deploy', // 部署
- 'refactor', // 重构(即不是新增功能,也不是修改bug的代码变动)
- 'test', // 增加测试
- 'chore', // 构建过程或辅助工具的变动
- 'revert', // feat(pencil): add ‘graphiteWidth’ option (撤销之前的commit)
- 'merge', // 合并分支, 例如: merge(前端页面): feature-xxxx修改线程地址
- 'build' // 打包
- ]
- ],
- // subject 不做大小写限制
- 'subject-case': [0]
- }
- }
复制代码 ⭐vite 5 构建
- import { defineConfig } from 'vite'
- import vue from '@vitejs/plugin-vue'
- import svgLoader from 'vite-svg-loader'
- import { fileURLToPath, URL } from 'node:url'
- import { visualizer } from 'rollup-plugin-visualizer'
- import viteCompression from 'vite-plugin-compression'
- // Element-Plus 按需引入
- import AutoImport from 'unplugin-auto-import/vite'
- import Components from 'unplugin-vue-components/vite'
- import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
- export default defineConfig({
- base: process.env.VITE_PUBLIC_PATH,
- plugins: [
- vue(),
- svgLoader(),
- viteCompression(),
- // TODO 性能优化原则在出问题时候去做动作,不然效果不明显,没有成就感
- visualizer({
- title: '打包性能分析报告',
- filename: 'report.html'
- }),
- AutoImport({
- dts: 'types/auto-imports.d.ts',
- resolvers: [ElementPlusResolver()]
- }),
- Components({
- dts: 'types/components.d.ts',
- extensions: ['vue'],
- resolvers: [ElementPlusResolver()]
- })
- ],
- // 路径优化
- resolve: {
- alias: {
- '@': fileURLToPath(new URL('./src', import.meta.url)),
- '#': fileURLToPath(new URL('./types', import.meta.url))
- }
- },
- // 开发服务器配置
- server: {
- // 端口号
- port: 5202,
- // 本地跨域代理 https://cn.vitejs.dev/config/server-options.html#server-proxy
- proxy: {
- '/api': {
- target: 'http://www.xxxx.com',
- changeOrigin: true
- },
- },
- // 预热文件以提前转换和缓存结果,降低启动期间的初始页面加载时长并防止转换瀑布
- warmup: {
- clientFiles: ['./index.html', './src/{views,components}/*']
- }
- },
- // TODO 后台项目不用配置,方便排查问题
- esbuild: {
- drop: process.env.MODE === 'development' ? ['console', 'debugger'] : []
- }
- })
复制代码 ⭐package.json
这里是全局 安装包与命令执行的入口文件,常用的配置如下所示:
- "scripts": {
- "format": "prettier --write src/",
- "lint": "eslint . --ext .vue,.js,.ts,.jsx,.tsx,.cjs,.mjs --fix --ignore-path .gitignore",
- },
复制代码 注释
https://segmentfault.com/a/1190000044789971
代码注释是软件开发中的重要构成部分,它资助开发者理解代码的功能和目标,同时也是代码维护和团队协作的基础,一个清楚的注释规范可以或许提高代码的可读性和维护性
- 设计复杂业务逻辑的处置惩罚函数必须说明函数的寄义
- 模块化封装的代码必要说明该模块的使用说明和功能先容
- 团队成员的注释风格须保持划一性,以保证注释与代码的同步性
- /**
- * 斐波那契数列(用于复杂逻辑功能的说明)
- * @param {number} n 斐波那契数列的第n项
- * @returns {number} 返回第n项的值
- */
- const fibonacciDP = (n) => {
- let fib = [0, 1]
- for (let i = 2; i <= n; i++) {
- fib[i] = fib[i - 1] + fib[i - 2]
- }
- return fib[n]
- }
- // 斐波那契传入的数字(描述字段、函数、配置...的简短说明)
- const num = 10
- console.log(fibonacciDP(10))
复制代码 特殊注释标记如下:
- // TODO:标记尚未实现或需要进一步工作的代码部分,提醒开发者将来需要完成的功能
- // FIXME:指出代码中已知的问题或错误,这些代码可能存在问题或不稳定,需要修复
- // HACK:指示代码中的临时解决方案或不够优雅的实现,通常是为了快速解决问题,
- // 但需要更好的长期解决方案
- // NOTE:用于强调代码中重要的信息或解释,如复杂逻辑的解释或特定实现方式的原因
- // OPTIMIZE:提示代码性能可以优化,虽然代码可能没有问题,但存在提升效率的空间
- // REVIEW:表示代码需要进一步审查,以确保满足需求或寻找更好的实现方法
- // DEPRECATED:表明代码已经过时,不推荐使用,可能在未来会被移除或替换
- // NOCOMMIT:警告标记,表示代码不应该提交到版本控制系统,通常用于临时改动,如调试代码
复制代码 GIT
固然现在有很多的可视化工具可以或许资助我们举行分支利用,这也很方便,从原有的命令行利用中解放出来,这里也可以大概了解一下相干利用。
常用命令
git rebase详解(图解+最简单示例,一次就懂)-CSDN博客
图解 Git 基本命令 merge 和 rebase - Michael翔 - 博客园
https://segmentfault.com/a/1190000044038056
先容 - 《阮一峰 Git 教程》 - 书栈网 · BookStack
https://pure-admin.github.io/pure-admin-doc/pages/git/
- // 获取远程最新的分支代码
- // 本地没影响,只是更新远程跟踪分支的列表
- git remote update origin --prune
- // 代码添加到缓存区
- git add .
- // 代码添加到 commit
- git commit -m "feat: 提交信息"
- // 创建分支
- git checkout -b xxx
- // git reset(移动 head 指针
- // 参数 soft 不改变工作区和缓存区、默认 mixed 改变缓存区、hard 改变缓存区和工作区
- // 修复错误提交 push,可指定Head,不然默认当前状态
- git reset Head^ --hard
- git push -f
- // git revert(选择一个 commit 重新制作并提交,新加一个 commmit 多了一条记录)
- git revert xxx
- // 后续合错了分支优先推荐 git reset
- // git merge(新增/基准线更改)
- // 本质是合并代码,主分支拉取分支后提前的直接转移指针,未提前的需要新增 提交信息结点
- // git rebase(变基修改)
- // 将目标元素 rebase 到当前分支上,注意拉齐水平线,当前分支代码在最新 commit 节点
复制代码 类型说明
类型描述feat新增开发分支提交 featurefix修复 bugdocs仅仅是修改了文档,比如 readme、changelog 等style仅仅是修改了空格、格式缩进、逗号等等,不改变代码逻辑refactor代码重构,没有新功能或者修复 bugperf优化相干,比如提升性能、体验test测试用例,包括单位测试、集成测试等chore改变构建流程、或者增加依靠库、工具等revert回滚到上一个版本
- vscode 推荐插件:git-commit-lint-vscode
上线流程
- 首先必要在 **Matrix (需求管理平台)**创建当前开发任务的描述信息
- 获取 Matrix Id,拉取最新 master 分支的代码到当地并创建分支,如:dev_1803329addProduct_1
- 开发完成之后拉取最新的 release_qa 分支,并合并代码
- 发布到 qa 测试情况转交测试,没标题 合并到 **release **分支发布预发
- 预发测试没标题,代码合并到 master ,打上 tag ,并推送到远程 origin
- 利用公司的主动化部署平台 Jean 举行部署发布,发布完成注意监控非常
HTML
- DOCTYPE 声明一样平常默认,注意 编码** ‘UTF-8’**,语言 ‘zh-CN’
- 使用语义化标签,空元素使用单个自闭和标签,其余不可省略
- HTML标签名、类名、标签属性和大部分属性值统一用小写
- 层级布局块级元素独立一行,内联元素视场景处置惩罚,内联元素不支持嵌套块级
- 不使用 < 和 > 等特殊元素,防止标签相干层级失效,浏览器解析错误
CSS
CSS 目前发展出了多个分支,包含:
- 原生 css ,也包含不同平台推出的语言(wxss、wxs)
- 预处置惩罚 css,对原生样式举行增强,终极转换为 css (sass、scss、less等)
- 组件框架,开箱即用的第三方组件库(Element、Antd)
- 原子化框架,响应式、高度集成的类库(Tailwind)
在开发大型项目过程中,上述样式多少会用到,对于详细的编码规范来说,总结如下:
- class 名称小写,id 重要表明特殊的 dom 元素,**短斜杠 ‘-’ 分割,**不凌驾3个,子元素前面需跟随父元素
- 自定义 css 变量使用 – 作为首部,在全局 :root 举行声明定义,详细的种别后面出一篇前端设计规范文章
- 原子类 css 注意类名的次序、是否重复、抽离公共样式等,可以借助格式化插件或者第三方库实现
- 组件样式的重写首先根据官方文档举行修改,或者** :deep() 样式穿透 优先级 !important(少用)**修改
module
https://juejin.cn/post/6844904080955932680
前端的底层编码风格分为两种,一种是 CommonJS、一种是 ES6 语法,这两种风格的区别如下:
- 首先推荐统一接纳 **ES6 模块化 **语法作为首选编程范式
- 待补充…
JS
JS 是前端的魂魄所在,也是处置惩罚统统业务逻辑的入口,所以这部分的规范至关重要。
依靠导入
- import React from 'react'
- import { useQuery } from 'react-query'
- import { PropTypes } from 'prop-types'
- import styled from 'styled-components/native'
- import colors from '@/styles/colors'
- import { fetchData } from '@/lib/api'
- import ErrorImg from '@/assets/images/error.png'
- import { RouteData, RouteOLG } from '@/types/route'
- import MapVersionDropdown from '@/components/map-version-dropdown'
- import {
- generateRandomColor,
- generateStreamPoint,
- generateStreamPolyline,
- isInputStreamValid,
- generateStreamLink,
- } from '@/lib/utils'
复制代码
- React 内置模块
- 外部引入的模块
- 本身编写的文件
- 样式文件
- 图片资源文件
- TS编写的类型定义文件
- 引入自定义的接口
- 本身编写的组件
- 一行引入多个模块导致分行的,可以放在末了
这里以 React 的项目工程举例,相干等级如上述说明,总体来说在不脱离大类的条件下,要保证长度阶梯性递增,符合开发职员的视觉规范。
变量定名
- 变量接纳 **小驼峰 **定名,一样平常为名词居多,原则上不凌驾 3个单词
- 属性值为互斥关系的统一接纳 is 开头,如:isOpenDialog
- 全局静态变量接纳 大写单词定名 下划线分割,如:const TY_EBK_CONFIG
- 定名的单词尽量普通易懂,原则上使用英文单词(可缩写),制止汉字拼音(特殊情况除外)
- 方法定名使用动词,描述变乱逻辑,一样平常场景推荐使用 **箭头函数书写 ( ) => { } **,一样平常不受this影响,遵循函数时编程:https://juejin.cn/post/6844903936378273799
- 方法与数据相干的常用 get post 开头,与逻辑处置惩罚相干的常用 handle(最常用) update delete pre generate close 开头,条件处置惩罚/判定的常用 **isValidate judge isSatify condition **
计谋模式
- // 为了解决3层以上的if/else、switch判断,可以使用对象的形式存储
- const { role } = { role: "ADMIN" };
- function AdminUser() {
- console.log("ADMIN");
- }
- function EmployeeUser() {
- console.log("EMPLOYEE");
- }
- function NormalUser() {
- console.log("NormalUser");
- }
- const components = {
- ADMIN: AdminUser,
- EMPLOYEE: EmployeeUser,
- USER: NormalUser,
- };
- const Component = components[role];
- Component();
复制代码 条件判定
- // 函数处理逻辑,可以提前结束,而不是包裹在正确的 {} 内部
- if(!isAuto) return √
- if(isAuto) {
- xxx
- xxx
- xxx
- } x
- // 单条件渲染
- { isAuto || 'default' }
- {streamOLGList.length > 0 && <StreamResultList />}
- // 函数执行判断,简单逻辑优先采用三元运算符
- const { role } = user;
- return role === ADMIN ? <AdminUser /> : <NormalUser />;
复制代码 解构赋值
解构赋值 - JavaScript | MDN
- // 对象的解构赋值
- const obj = { a: 1, b: 2 };
- const { a, b } = obj;
- // 数组的解构赋值
- const x = [1, 2, 3, 4, 5];
- const [y, z] = x;
复制代码 模板字符串
- // 避免使用字符串 + 号 连接,会造成不必要的栈内存消耗
- const userDetails = `${user.name}'s profession is ${user.proffession}`
- return <div> {userDetails} </div>
复制代码 TS
前言
https://juejin.cn/post/6872111128135073806
TS 本质上是为了规范编码,利用强类型举行预编译,提前检查编码的错误。但是由于必要限制字段的类型,占用一部分开发时间,对于功能来说增益不大,会压缩原本的工期,下面是适用的场景:
- 工具类、基础建立的团队内部工具
- 业务交互涉及少、前端展示多的网页
- 团队要求规范严格、成员基础好、开发时间充足
文件声明
- // <reference types="vite/client" />
- // 找不到模块“xxx.vue”或其相应的类型声明
- // vite-env.d.ts 防止引入.vue 文件标红未找到模块
- declare module '*.vue' {
- import { ComponentOptions } from 'vue'
- const componentOptions: ComponentOptions
- export default componentOptions
- }
复制代码 组件引入
Node
【超多代码、超多图解】Node.js一文全解析_node技术分析图示-CSDN博客
VUE
组件闭合
- // 组件内部没有子节点,使用自闭合标签,提高了可读性
-
- <follow-track
- v-model:currentTab="currentTab"
- :shop-id="shopId"
- />
复制代码 Fragments 语法
- // 始终使用 Fragment 而不是 Div。
- // 它可以保持代码整洁,并且也有利于性能,因为在虚拟 DOM 中创建的节点少了一个
- return (
- <>
- <component-a />
- <component-b />
- <component-c />
- </>
- );
复制代码 React
组件定名
- //map-version-dropdown.tsx
- export default function MapVersionDropdown(){}
复制代码
- 文件使用 - 连接,组件对应文件名称使用大驼峰定名
属性定名
- const [isTruckTpra, setIsTruckTpra] = useState<boolean>(true)
- const [streamOLGList, setStreamOLGList] = useState<Stream['streamOLGList']>(
- [],
- )
复制代码
- 响应式属性使用useState定名,使用小驼峰定名,至少2个字符以上
- 数据状态不影响UI变化的,使用一样平常数据即可,不用响应式数据
变乱方法外部定义
- const submitData = () => dispatch(ACTION_TO_SEND_DATA);
- return <button onClick={submitData}>This is a good example</button>;
复制代码
- 复杂的变乱方法必要在语法外部定义
- 简单的如状态变化可以在内部定义,尽量控制在一行以内
微信小步伐
微信小步伐设计指南 | 微信开放文档
【第二周】基础语法学习-CSDN博客
- 小步伐设计突出重点,页面尽量简洁清楚
- 组件在单页面按需引入,尽量不用全局组件
Taro 跨端
工具函数
HTTP
**展示前端 **重要是获取数据和前台页面的交互,所以网络模块的封装至关重要,步骤如下:
- 封装 axios 构建 TyHttp 类
- 生成实例,构造通用请求工具函数
- 新建 api 文件夹、index.ts 作为主入口 modules 放置各模块的请求
- 此中 modules 文件小写定名,内里使用 类 收口,静态方法隔离不同请求
参考代码如下所示:
- import { router } from '@/router/'
- import { ElMessage } from 'element-plus'
- import { RequestMethods, TyHttpRequestConfig } from './types.d'
- import { urlConfigResolver } from '@/utils/microAppConfigResolver'
- import Axios, { type AxiosInstance, type AxiosRequestConfig } from 'axios'
- const defaultConfig: AxiosRequestConfig = {
- // 请求超时时间
- timeout: 30000,
- headers: {
- Accept: 'application/json, text/plain, */*',
- 'Content-Type': 'application/json',
- 'X-Requested-With': 'XMLHttpRequest'
- },
- baseURL: urlConfigResolver()
- }
- class TyHttp {
- constructor() {
- this.httpInterceptorsRequest()
- this.httpInterceptorsResponse()
- }
- /** 默认控制台格式化输出数据 */
- private static formatData(method: string, url: string, data: any): void {
- console.log(`%c${method} %c${url}`, 'color: #00f', 'color: #f00', data)
- }
- /** 初始化配置对象 */
- private static initConfig: TyHttpRequestConfig = {
- beforeRequestCallback(config) {
- // console.log('beforeRequestCallback', config)
- },
- beforeResponseCallback(response) {
- const { code, message } = response.data
- // TyHttp.formatData(response.config.method!, response.config.url!, response.data)
- if (code == 200) {
- return response.data
- }
- if (code == 401) {
- localStorage.clear()
- // 强制重新登录
- if (window.__MICRO_APP_ENVIRONMENT__) {
- const baseRouter = window.microApp.router.getBaseAppRouter()
- baseRouter.push('/login')
- } else {
- router.push('/login')
- }
- }
- // 错误处理
- ElMessage.error({
- type: 'error',
- grouping: true,
- message: message
- })
- }
- }
- /** 保存当前Axios实例对象 */
- private static axiosInstance: AxiosInstance = Axios.create(defaultConfig)
- /** 设置请求拦截器 */
- private httpInterceptorsRequest(): void {
- TyHttp.axiosInstance.interceptors.request.use(
- async (config: TyHttpRequestConfig): Promise<any> => {
- // 支持单个接口传入请求拦截判断
- if (typeof config.beforeRequestCallback === 'function') {
- config.beforeRequestCallback(config)
- return config
- }
- // 支持全局请求拦截判断
- if (TyHttp.initConfig.beforeRequestCallback) {
- TyHttp.initConfig.beforeRequestCallback(config)
- return config
- }
- },
- (error) => {
- return Promise.reject(error)
- }
- )
- }
- /** 设置响应拦截器 */
- private httpInterceptorsResponse(): void {
- TyHttp.axiosInstance.interceptors.response.use(
- (response: any) => {
- // 支持单个接口传入请求拦截判断
- const $config = response.config
- if (typeof $config.beforeResponseCallback === 'function') {
- $config.beforeResponseCallback(response)
- return response.data
- }
- // 支持全局请求拦截判断
- if (TyHttp.initConfig.beforeResponseCallback) {
- return TyHttp.initConfig.beforeResponseCallback(response)
- }
- return response
- },
- (error) => {
- const { status } = error.response
- console.log(error, status)
- if (status == 401) {
- ElMessage.error('登录状态失效,请重新登录')
- localStorage.clear()
- // 强制重新登录
- if (window.__MICRO_APP_ENVIRONMENT__) {
- const baseRouter = window.microApp.router.getBaseAppRouter()
- baseRouter.push('/login')
- } else {
- console.log(router)
- router.push('/login')
- }
- throw new Error('登录状态失效,请重新登录')
- } else {
- ElMessage.error('网络失效')
- return Promise.reject(error)
- }
- }
- )
- }
- /** 通用请求工具函数 */
- public request<T>(
- method: RequestMethods,
- url: string,
- param?: AxiosRequestConfig,
- axiosConfig?: TyHttpRequestConfig
- ): Promise<T> {
- const config = {
- method,
- url,
- ...param,
- ...axiosConfig
- } as TyHttpRequestConfig
- // 单独处理自定义请求/响应回调
- return new Promise((resolve, reject) => {
- TyHttp.axiosInstance
- .request(config)
- .then((response: any) => {
- resolve(response)
- })
- .catch((error) => {
- reject(error)
- })
- })
- }
- /** 单独抽离的post工具函数 */
- public post<T, P>(
- url: string,
- params?: AxiosRequestConfig<T>,
- config?: TyHttpRequestConfig
- ): Promise<P> {
- return this.request<P>('post', url, params, config)
- }
- /** 单独抽离的get工具函数 */
- public get<T, P>(
- url: string,
- params?: AxiosRequestConfig<T>,
- config?: TyHttpRequestConfig
- ): Promise<P> {
- return this.request<P>('get', url, params, config)
- }
- }
- export const http = new TyHttp()
复制代码- import { http } from '@/utils/http'
- class UserAccount {
- // 登录接口
- static async login(data) {
- return http.post('/xxx', { data })
- }
- // 登出接口
- static async logout(data) {
- return http.post('/xxx', { data })
- }
- // 获取信息
- static async getUserInfo() {
- return http.get('/xxx')
- }
- }
- export default UserAccount
复制代码- import UserAccount from './modules/user'
- import GlobalConfig from './modules/config'
- export { UserAccount , GlobalConfig }
复制代码 防抖节流
- // 深拷贝
- const deepClone = (target: any, map = new WeakMap()) => {
- if (typeof target !== 'object' || target === null) return target
- if (map.get(target)) return map.get(target)
- const constructor = target.constructor
- if (/^(Function|RegExp|Date|Map|Set)$/i.test(constructor.name))
- return new constructor(target)
- const cloneTarget = Array.isArray(target) ? [] : {}
- map.set(target, cloneTarget)
- for (const key of Object.keys(target)) {
- // @ts-ignore
- cloneTarget[key] = deepClone(target[key], map)
- }
- return cloneTarget
- }
- // 防抖
- let timer: NodeJS.Timeout | null = null
- function debounce(fn: any, delay: number, immediate: boolean) {
- return function () {
- // @ts-ignore
- const context = this
- const args = arguments
- if (timer) clearTimeout(timer)
- if (immediate) {
- const callNow = !timer
- timer = setTimeout(() => {
- timer = null
- }, delay)
- if (callNow) fn.apply(context, args)
- } else {
- timer = setTimeout(() => {
- fn.apply(context, args)
- }, delay)
- }
- }
- }
- // 节流
- function throttle(fn: any, delay: number) {
- let preTime = 0
- return function () {
- // @ts-ignore
- const context = this
- const args = arguments
- const nowTime = +new Date()
- if (nowTime - preTime >= delay) {
- fn.apply(context, args)
- preTime = nowTime
- }
- }
- }
- export { deepClone, debounce, throttle }
复制代码 正则匹配
- // 去除首尾空格
- const trim = (str) => {
- return str.replace(/(^\s*)|(\s*$)/g, '')
- }
- // 匹配某个字符出现的个数
- const matchCount = (str, char) => {
- // 需要转义的字符
- const specialChars = ['.', '*', '+', '?', '|', '(', ')', '[', ']', '{', '}', '\\']
- if (specialChars.includes(char)) {
- char = '\\' + char
- }
- return (str.match(new RegExp(char, 'g')) || []).length
- }
- // 移除输入字符串 value 中所有非数字和非点号的字符,点号最多只能出现一次
- const removeNonNumeric = (value) => {
- if (value === '') {
- return ''
- }
- // 移除点开头的点号和0开头的数字
- let result = value.replace(/^\./, '').replace(/^0+/, '0')
- result = result.replace(/[^\d.]/g, '').replace(/(\d+\.\d*).*/, '$1')
- // 字符串存在一个点号并且后面没有数字,则不判断
- const array = result.split('.')
- if (array.length > 1 && array[1] === '') {
- return result
- }
- // 字符串最后一个字符是0,不判断
- if (result.endsWith('0')) {
- return result
- }
- return parseFloat(result)
- }
- export { trim, matchCount, removeNonNumeric }
复制代码 日期格式
- import dayjs from 'dayjs'
- // 格式化特定日期
- const formatDate = (date, formatStr = 'YYYY-MM-DD') => {
- return dayjs(date).format(formatStr)
- }
- // 获取当前日期并格式化
- const getCurrentDate = (formatStr = 'YYYY-MM-DD HH:mm') => {
- return dayjs().format(formatStr)
- }
- // 获取是周几
- const weekDays = ['周日', '周一', '周二', '周三', '周四', '周五', '周六']
- const getWeekDay = () => {
- const index = dayjs().day()
- return weekDays[index]
- }
- // 判断凌晨 上午 中午 下午 晚上
- const getDayPeriod = () => {
- const hour = dayjs().hour()
- if (hour >= 0 && hour < 6) {
- return '凌晨'
- } else if (hour >= 6 && hour < 12) {
- return '上午'
- } else if (hour >= 12 && hour < 14) {
- return '中午'
- } else if (hour >= 14 && hour < 18) {
- return '下午'
- } else {
- return '晚上'
- }
- }
- export { formatDate, getCurrentDate, getWeekDay, getDayPeriod }
复制代码 AI 辅助编码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |