【团队建立】前端编码规范

打印 上一主题 下一主题

主题 2045|帖子 2045|积分 6135




  

概览

前端技术的迭代速率较快,相干的框架和知识点也越来越多,在开发新项目时可供选择的技术栈也越来越多。对于必要多人团队写作的场景下,怎样保证各人共同的编码规范,用一套约定俗成的尺度举行开发至关重要,可以更好的提升本身的开发体验。
接下来,重要解说不同前端领域的编码规范以合格式化代码,本质上底层照旧前端三件套的编程头脑。
焦点:语义化、代码清楚明确、注释美满,代码配置 < 沟通 < 约定 < 文档 < 体系
语雀原文档: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. // 1. 安装 pnpm
  2. npm install -g pnpm
  3. // 2. 设置淘宝镜像源
  4. pnpm config set registry https://registry.npmmirror.com/
  5. // 3. 在项目中使用 pnpm 安装依赖包
  6. pnpm install
复制代码
⭐ESLint 8

ESLint 是一个静态代码分析工具,重要用于查找和修复代码中的潜伏标题、错误、不划一和不推荐的模式。是资助你提高代码质量、制止常见的错误,以及确保团队成员遵循统一的编码约定。
这里必要根据不同的项目框架举行详细的配置,如:VUE React TS,这里网上有很多参考资料举行参考
  1. // 1. 安装 eslint 和 vue 插件
  2. pnpm i eslint@8 eslint-plugin-vue -D
  3. // 2. 安装 prettier 和 相关插件
  4. pnpm i prettier@3 eslint-config-prettier eslint-plugin-prettier -D
  5. // 3. TS 解析器安装
  6. pnpm i @typescript-eslint/parser -D
  7. // 4. TS
  8. pnpm i @typescript-eslint/eslint-plugin -D
复制代码
  1. module.exports = {
  2.   root: true,
  3.   parser: 'vue-eslint-parser',
  4.   // 环境设置
  5.   env: {
  6.     es6: true,
  7.     node: true,
  8.     browser: true
  9.   },
  10.   // 解析选项
  11.   parserOptions: {
  12.     sourceType: 'module',
  13.     ecmaVersion: 'latest',
  14.     // TS 解析器配置
  15.     parser: '@typescript-eslint/parser'
  16.   },
  17.   // 插件
  18.   plugins: ['vue'],
  19.   // 引入的语法校验规则
  20.   extends: [
  21.     'plugin:vue/vue3-recommended',
  22.     'plugin:@typescript-eslint/recommended',
  23.     'prettier',
  24.     'plugin:prettier/recommended' // eslint-plugin-prettier 规则放到最后
  25.   ],
  26.   // 自定义规则
  27.   rules: {
  28.     'no-console': 'off',
  29.     'no-debugger': 'off',
  30.     'no-unused-vars': 'warn',
  31.     'prefer-rest-params': 'warn',
  32.     'vue/html-indent': 'off',
  33.     'vue/html-self-closing': 'off',
  34.     'vue/max-attributes-per-line': 'off',
  35.     'vue/multi-word-component-names': 'off',
  36.     'vue/no-setup-props-destructure': 'warn',
  37.     // TS 冲突规则
  38.     '@typescript-eslint/no-unused-vars': 'off',
  39.     // 强制组件在模板中使用 kebab-case
  40.     'vue/component-name-in-template-casing': ['warn', 'kebab-case'],
  41.     // 自闭和单标签元素
  42.     'vue/html-self-closing': [
  43.       'warn',
  44.       {
  45.         html: {
  46.           void: 'always',
  47.           normal: 'always',
  48.           component: 'always'
  49.         },
  50.         svg: 'always',
  51.         math: 'always'
  52.       }
  53.     ]
  54.   },
  55.   // 配置忽略文件
  56.   ignorePatterns: ['node_modules/', 'dist/', 'public/', 'pnpm-lock.yaml']
  57. }
复制代码
⭐Prettier 3

Prettier是一个代码格式化工具,专注于对代码举行格式化,使其符合划一的风格规范。它会主动调整代码的缩进、换行、引号等,确保代码在不同的编辑器和情况中具有划一的表面。
简单来说,ESLint更注重你的代码是否符合规范,Pretter则是为你提供了按照规范格式化代码的能力。
  1. {
  2.   "tabWidth": 2,
  3.   "semi": false,
  4.   "printWidth": 90,
  5.   "endOfLine": "auto",
  6.   "singleQuote": true,
  7.   "trailingComma": "none"
  8. }
复制代码
忽略格式化的文件本质上和 .gitignore 大差不差(可以忽略)
  1. # Logs
  2. logs
  3. *.log
  4. npm-debug.log*
  5. yarn-debug.log*
  6. yarn-error.log*
  7. pnpm-debug.log*
  8. lerna-debug.log*
  9. node_modules
  10. dist
  11. dist-ssr
  12. *.local
  13. # Editor directories and files
  14. .vscode/*
  15. !.vscode/extensions.json
  16. .idea
  17. .DS_Store
  18. *.suo
  19. *.ntvs*
  20. *.njsproj
  21. *.sln
  22. *.sw?
  23. # build report file
  24. report.html
复制代码
⭐Husky 9

https://juejin.cn/post/7355535964612100108
https://juejin.cn/post/7041768022284976165
这个重要是限制提交的 commit 信息规范即可,不能随意提交,对此没要求的可以不做。
  1. // 1. 安装钩子触发工具 husky
  2. pnpm i husky@9 -D
  3. // 2. 执行配置脚本
  4. npx husky init
  5. // 3. 安装 commitlint 规范提交信息
  6. pnpm i @commitlint/cli @commitlint/config-conventional -D
  7. // 4. 新建 commitlint.config.ts 校验文件
  8. // 5. 新建 commit-msg husky 规范提示消息
  9. #!/bin/sh
  10. npx --no-install commitlint -e
  11. // 6. husky 新建 pre-commit
  12. pnpm run lint
  13. // 7. 提交前 eslint 检查代码
  14. 具体参考 package.json 命令配置
复制代码
  1. module.exports = {
  2.   extends: ['@commitlint/config-conventional'],
  3.   rules: {
  4.     'type-enum': [
  5.       2,
  6.       'always',
  7.       [
  8.         'feat', // 新功能(feature)
  9.         'bug', // 此项特别针对bug号,用于向测试反馈bug列表的bug修改情况
  10.         'fix', // 修补bug
  11.         'ui', // 更新 ui
  12.         'docs', // 文档(documentation)
  13.         'style', // 格式(不影响代码运行的变动)
  14.         'perf', // 性能优化
  15.         'release', // 发布
  16.         'deploy', // 部署
  17.         'refactor', // 重构(即不是新增功能,也不是修改bug的代码变动)
  18.         'test', // 增加测试
  19.         'chore', // 构建过程或辅助工具的变动
  20.         'revert', // feat(pencil): add ‘graphiteWidth’ option (撤销之前的commit)
  21.         'merge', // 合并分支, 例如: merge(前端页面): feature-xxxx修改线程地址
  22.         'build' // 打包
  23.       ]
  24.     ],
  25.     // subject 不做大小写限制
  26.     'subject-case': [0]
  27.   }
  28. }
复制代码
⭐vite 5 构建

  1. import { defineConfig } from 'vite'
  2. import vue from '@vitejs/plugin-vue'
  3. import svgLoader from 'vite-svg-loader'
  4. import { fileURLToPath, URL } from 'node:url'
  5. import { visualizer } from 'rollup-plugin-visualizer'
  6. import viteCompression from 'vite-plugin-compression'
  7. // Element-Plus 按需引入
  8. import AutoImport from 'unplugin-auto-import/vite'
  9. import Components from 'unplugin-vue-components/vite'
  10. import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
  11. export default defineConfig({
  12.   base: process.env.VITE_PUBLIC_PATH,
  13.   plugins: [
  14.     vue(),
  15.     svgLoader(),
  16.     viteCompression(),
  17.     // TODO 性能优化原则在出问题时候去做动作,不然效果不明显,没有成就感
  18.     visualizer({
  19.       title: '打包性能分析报告',
  20.       filename: 'report.html'
  21.     }),
  22.     AutoImport({
  23.       dts: 'types/auto-imports.d.ts',
  24.       resolvers: [ElementPlusResolver()]
  25.     }),
  26.     Components({
  27.       dts: 'types/components.d.ts',
  28.       extensions: ['vue'],
  29.       resolvers: [ElementPlusResolver()]
  30.     })
  31.   ],
  32.   // 路径优化
  33.   resolve: {
  34.     alias: {
  35.       '@': fileURLToPath(new URL('./src', import.meta.url)),
  36.       '#': fileURLToPath(new URL('./types', import.meta.url))
  37.     }
  38.   },
  39.   // 开发服务器配置
  40.   server: {
  41.     // 端口号
  42.     port: 5202,
  43.     // 本地跨域代理 https://cn.vitejs.dev/config/server-options.html#server-proxy
  44.     proxy: {
  45.       '/api': {
  46.         target: 'http://www.xxxx.com',
  47.         changeOrigin: true
  48.       },
  49.     },
  50.     // 预热文件以提前转换和缓存结果,降低启动期间的初始页面加载时长并防止转换瀑布
  51.     warmup: {
  52.       clientFiles: ['./index.html', './src/{views,components}/*']
  53.     }
  54.   },
  55.   // TODO 后台项目不用配置,方便排查问题
  56.   esbuild: {
  57.     drop: process.env.MODE === 'development' ? ['console', 'debugger'] : []
  58.   }
  59. })
复制代码
⭐package.json

这里是全局 安装包与命令执行的入口文件,常用的配置如下所示:
  1. "scripts": {
  2.   "format": "prettier --write src/",
  3.   "lint": "eslint . --ext .vue,.js,.ts,.jsx,.tsx,.cjs,.mjs --fix --ignore-path .gitignore",
  4. },
复制代码
注释

https://segmentfault.com/a/1190000044789971
代码注释是软件开发中的重要构成部分,它资助开发者理解代码的功能和目标,同时也是代码维护和团队协作的基础,一个清楚的注释规范可以或许提高代码的可读性和维护性

  • 设计复杂业务逻辑的处置惩罚函数必须说明函数的寄义
  • 模块化封装的代码必要说明该模块的使用说明和功能先容
  • 团队成员的注释风格须保持划一性,以保证注释与代码的同步性
  1. /**
  2. * 斐波那契数列(用于复杂逻辑功能的说明)
  3. * @param {number} n 斐波那契数列的第n项
  4. * @returns {number} 返回第n项的值
  5. */
  6. const fibonacciDP = (n) => {
  7.   let fib = [0, 1]
  8.   for (let i = 2; i <= n; i++) {
  9.     fib[i] = fib[i - 1] + fib[i - 2]
  10.   }
  11.   return fib[n]
  12. }
  13. // 斐波那契传入的数字(描述字段、函数、配置...的简短说明)
  14. const num = 10
  15. console.log(fibonacciDP(10))
复制代码
特殊注释标记如下:
  1. // TODO:标记尚未实现或需要进一步工作的代码部分,提醒开发者将来需要完成的功能
  2. // FIXME:指出代码中已知的问题或错误,这些代码可能存在问题或不稳定,需要修复
  3. // HACK:指示代码中的临时解决方案或不够优雅的实现,通常是为了快速解决问题,
  4. // 但需要更好的长期解决方案
  5. // NOTE:用于强调代码中重要的信息或解释,如复杂逻辑的解释或特定实现方式的原因
  6. // OPTIMIZE:提示代码性能可以优化,虽然代码可能没有问题,但存在提升效率的空间
  7. // REVIEW:表示代码需要进一步审查,以确保满足需求或寻找更好的实现方法
  8. // DEPRECATED:表明代码已经过时,不推荐使用,可能在未来会被移除或替换
  9. // 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/
  1. // 获取远程最新的分支代码
  2. // 本地没影响,只是更新远程跟踪分支的列表
  3. git remote update origin --prune
  4. // 代码添加到缓存区
  5. git add .
  6. // 代码添加到 commit
  7. git commit -m "feat: 提交信息"
  8. // 创建分支
  9. git checkout -b xxx
  10. // git reset(移动 head 指针
  11. // 参数 soft 不改变工作区和缓存区、默认 mixed 改变缓存区、hard 改变缓存区和工作区
  12. // 修复错误提交 push,可指定Head,不然默认当前状态
  13. git reset Head^ --hard
  14. git push -f
  15. // git revert(选择一个 commit 重新制作并提交,新加一个 commmit 多了一条记录)
  16. git revert xxx
  17. // 后续合错了分支优先推荐 git reset
  18. // git merge(新增/基准线更改)
  19. // 本质是合并代码,主分支拉取分支后提前的直接转移指针,未提前的需要新增 提交信息结点
  20. // git rebase(变基修改)
  21. // 将目标元素 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 是前端的魂魄所在,也是处置惩罚统统业务逻辑的入口,所以这部分的规范至关重要。
依靠导入

  1. import React from 'react'
  2. import { useQuery } from 'react-query'
  3. import { PropTypes } from 'prop-types'
  4. import styled from 'styled-components/native'
  5. import colors from '@/styles/colors'
  6. import { fetchData } from '@/lib/api'
  7. import ErrorImg from '@/assets/images/error.png'
  8. import { RouteData, RouteOLG } from '@/types/route'
  9. import MapVersionDropdown from '@/components/map-version-dropdown'
  10. import {
  11.   generateRandomColor,
  12.   generateStreamPoint,
  13.   generateStreamPolyline,
  14.   isInputStreamValid,
  15.   generateStreamLink,
  16. } 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 **
计谋模式

  1. // 为了解决3层以上的if/else、switch判断,可以使用对象的形式存储
  2. const { role } = { role: "ADMIN" };
  3. function AdminUser() {
  4.   console.log("ADMIN");
  5. }
  6. function EmployeeUser() {
  7.   console.log("EMPLOYEE");
  8. }
  9. function NormalUser() {
  10.   console.log("NormalUser");
  11. }
  12. const components = {
  13.   ADMIN: AdminUser,
  14.   EMPLOYEE: EmployeeUser,
  15.   USER: NormalUser,
  16. };
  17. const Component = components[role];
  18. Component();
复制代码
条件判定

  1. // 函数处理逻辑,可以提前结束,而不是包裹在正确的 {} 内部
  2. if(!isAuto) return √
  3. if(isAuto) {
  4.   xxx
  5.   xxx
  6.   xxx
  7. } x
  8. // 单条件渲染
  9. { isAuto || 'default' }
  10. {streamOLGList.length > 0 && <StreamResultList />}
  11. // 函数执行判断,简单逻辑优先采用三元运算符
  12. const { role } = user;
  13. return role === ADMIN ? <AdminUser /> : <NormalUser />;
复制代码
解构赋值

解构赋值 - JavaScript | MDN
  1. // 对象的解构赋值
  2. const obj = { a: 1, b: 2 };
  3. const { a, b } = obj;
  4. // 数组的解构赋值
  5. const x = [1, 2, 3, 4, 5];
  6. const [y, z] = x;
复制代码
模板字符串

  1. // 避免使用字符串 + 号 连接,会造成不必要的栈内存消耗
  2. const userDetails = `${user.name}'s profession is ${user.proffession}`
  3. return <div> {userDetails} </div>
复制代码
TS

前言

https://juejin.cn/post/6872111128135073806
TS 本质上是为了规范编码,利用强类型举行预编译,提前检查编码的错误。但是由于必要限制字段的类型,占用一部分开发时间,对于功能来说增益不大,会压缩原本的工期,下面是适用的场景:


  • 工具类、基础建立的团队内部工具
  • 业务交互涉及少、前端展示多的网页
  • 团队要求规范严格、成员基础好、开发时间充足
文件声明

  1. // <reference types="vite/client" />
  2. // 找不到模块“xxx.vue”或其相应的类型声明
  3. // vite-env.d.ts 防止引入.vue 文件标红未找到模块
  4. declare module '*.vue' {
  5.   import { ComponentOptions } from 'vue'
  6.   const componentOptions: ComponentOptions
  7.   export default componentOptions
  8. }
复制代码
组件引入

Node

【超多代码、超多图解】Node.js一文全解析_node技术分析图示-CSDN博客
VUE

组件闭合

  1. // 组件内部没有子节点,使用自闭合标签,提高了可读性
  2.    
  3. <follow-track
  4.   v-model:currentTab="currentTab"
  5.   :shop-id="shopId"
  6. />
复制代码
Fragments 语法

  1. // 始终使用 Fragment 而不是 Div。
  2. // 它可以保持代码整洁,并且也有利于性能,因为在虚拟 DOM 中创建的节点少了一个
  3. return (
  4.   <>
  5.   <component-a />
  6.   <component-b />
  7.   <component-c />
  8.   </>
  9. );
复制代码
React

组件定名

  1. //map-version-dropdown.tsx
  2. export default function MapVersionDropdown(){}
复制代码


  • 文件使用 - 连接,组件对应文件名称使用大驼峰定名
属性定名

  1. const [isTruckTpra, setIsTruckTpra] = useState<boolean>(true)
  2. const [streamOLGList, setStreamOLGList] = useState<Stream['streamOLGList']>(
  3.     [],
  4.   )
复制代码


  • 响应式属性使用useState定名,使用小驼峰定名,至少2个字符以上
  • 数据状态不影响UI变化的,使用一样平常数据即可,不用响应式数据
变乱方法外部定义

  1. const submitData = () => dispatch(ACTION_TO_SEND_DATA);
  2. return <button onClick={submitData}>This is a good example</button>;
复制代码


  • 复杂的变乱方法必要在语法外部定义
  • 简单的如状态变化可以在内部定义,尽量控制在一行以内
微信小步伐

微信小步伐设计指南 | 微信开放文档
【第二周】基础语法学习-CSDN博客

  • 小步伐设计突出重点,页面尽量简洁清楚
  • 组件在单页面按需引入,尽量不用全局组件
Taro 跨端

工具函数

HTTP

**展示前端 **重要是获取数据和前台页面的交互,所以网络模块的封装至关重要,步骤如下:

  • 封装 axios 构建 TyHttp
  • 生成实例,构造通用请求工具函数
  • 新建 api 文件夹、index.ts 作为主入口 modules 放置各模块的请求
  • 此中 modules 文件小写定名,内里使用 类 收口,静态方法隔离不同请求
参考代码如下所示:
  1. import { router } from '@/router/'
  2. import { ElMessage } from 'element-plus'
  3. import { RequestMethods, TyHttpRequestConfig } from './types.d'
  4. import { urlConfigResolver } from '@/utils/microAppConfigResolver'
  5. import Axios, { type AxiosInstance, type AxiosRequestConfig } from 'axios'
  6. const defaultConfig: AxiosRequestConfig = {
  7.   // 请求超时时间
  8.   timeout: 30000,
  9.   headers: {
  10.     Accept: 'application/json, text/plain, */*',
  11.     'Content-Type': 'application/json',
  12.     'X-Requested-With': 'XMLHttpRequest'
  13.   },
  14.   baseURL: urlConfigResolver()
  15. }
  16. class TyHttp {
  17.   constructor() {
  18.     this.httpInterceptorsRequest()
  19.     this.httpInterceptorsResponse()
  20.   }
  21.   /** 默认控制台格式化输出数据 */
  22.   private static formatData(method: string, url: string, data: any): void {
  23.     console.log(`%c${method} %c${url}`, 'color: #00f', 'color: #f00', data)
  24.   }
  25.   /** 初始化配置对象 */
  26.   private static initConfig: TyHttpRequestConfig = {
  27.     beforeRequestCallback(config) {
  28.       // console.log('beforeRequestCallback', config)
  29.     },
  30.     beforeResponseCallback(response) {
  31.       const { code, message } = response.data
  32.       // TyHttp.formatData(response.config.method!, response.config.url!, response.data)
  33.       if (code == 200) {
  34.         return response.data
  35.       }
  36.       if (code == 401) {
  37.         localStorage.clear()
  38.         // 强制重新登录
  39.         if (window.__MICRO_APP_ENVIRONMENT__) {
  40.           const baseRouter = window.microApp.router.getBaseAppRouter()
  41.           baseRouter.push('/login')
  42.         } else {
  43.           router.push('/login')
  44.         }
  45.       }
  46.       // 错误处理
  47.       ElMessage.error({
  48.         type: 'error',
  49.         grouping: true,
  50.         message: message
  51.       })
  52.     }
  53.   }
  54.   /** 保存当前Axios实例对象 */
  55.   private static axiosInstance: AxiosInstance = Axios.create(defaultConfig)
  56.   /** 设置请求拦截器 */
  57.   private httpInterceptorsRequest(): void {
  58.     TyHttp.axiosInstance.interceptors.request.use(
  59.       async (config: TyHttpRequestConfig): Promise<any> => {
  60.         // 支持单个接口传入请求拦截判断
  61.         if (typeof config.beforeRequestCallback === 'function') {
  62.           config.beforeRequestCallback(config)
  63.           return config
  64.         }
  65.         // 支持全局请求拦截判断
  66.         if (TyHttp.initConfig.beforeRequestCallback) {
  67.           TyHttp.initConfig.beforeRequestCallback(config)
  68.           return config
  69.         }
  70.       },
  71.       (error) => {
  72.         return Promise.reject(error)
  73.       }
  74.     )
  75.   }
  76.   /** 设置响应拦截器 */
  77.   private httpInterceptorsResponse(): void {
  78.     TyHttp.axiosInstance.interceptors.response.use(
  79.       (response: any) => {
  80.         // 支持单个接口传入请求拦截判断
  81.         const $config = response.config
  82.         if (typeof $config.beforeResponseCallback === 'function') {
  83.           $config.beforeResponseCallback(response)
  84.           return response.data
  85.         }
  86.         // 支持全局请求拦截判断
  87.         if (TyHttp.initConfig.beforeResponseCallback) {
  88.           return TyHttp.initConfig.beforeResponseCallback(response)
  89.         }
  90.         return response
  91.       },
  92.       (error) => {
  93.         const { status } = error.response
  94.         console.log(error, status)
  95.         if (status == 401) {
  96.           ElMessage.error('登录状态失效,请重新登录')
  97.           localStorage.clear()
  98.           // 强制重新登录
  99.           if (window.__MICRO_APP_ENVIRONMENT__) {
  100.             const baseRouter = window.microApp.router.getBaseAppRouter()
  101.             baseRouter.push('/login')
  102.           } else {
  103.             console.log(router)
  104.             router.push('/login')
  105.           }
  106.           throw new Error('登录状态失效,请重新登录')
  107.         } else {
  108.           ElMessage.error('网络失效')
  109.           return Promise.reject(error)
  110.         }
  111.       }
  112.     )
  113.   }
  114.   /** 通用请求工具函数 */
  115.   public request<T>(
  116.     method: RequestMethods,
  117.     url: string,
  118.     param?: AxiosRequestConfig,
  119.     axiosConfig?: TyHttpRequestConfig
  120.   ): Promise<T> {
  121.     const config = {
  122.       method,
  123.       url,
  124.       ...param,
  125.       ...axiosConfig
  126.     } as TyHttpRequestConfig
  127.     // 单独处理自定义请求/响应回调
  128.     return new Promise((resolve, reject) => {
  129.       TyHttp.axiosInstance
  130.         .request(config)
  131.         .then((response: any) => {
  132.           resolve(response)
  133.         })
  134.         .catch((error) => {
  135.           reject(error)
  136.         })
  137.     })
  138.   }
  139.   /** 单独抽离的post工具函数 */
  140.   public post<T, P>(
  141.     url: string,
  142.     params?: AxiosRequestConfig<T>,
  143.     config?: TyHttpRequestConfig
  144.   ): Promise<P> {
  145.     return this.request<P>('post', url, params, config)
  146.   }
  147.   /** 单独抽离的get工具函数 */
  148.   public get<T, P>(
  149.     url: string,
  150.     params?: AxiosRequestConfig<T>,
  151.     config?: TyHttpRequestConfig
  152.   ): Promise<P> {
  153.     return this.request<P>('get', url, params, config)
  154.   }
  155. }
  156. export const http = new TyHttp()
复制代码
  1. import { http } from '@/utils/http'
  2. class UserAccount {
  3.   // 登录接口
  4.   static async login(data) {
  5.     return http.post('/xxx', { data })
  6.   }
  7.   // 登出接口
  8.   static async logout(data) {
  9.     return http.post('/xxx', { data })
  10.   }
  11.   // 获取信息
  12.   static async getUserInfo() {
  13.     return http.get('/xxx')
  14.   }
  15. }
  16. export default UserAccount
复制代码
  1. import UserAccount from './modules/user'
  2. import GlobalConfig from './modules/config'
  3. export { UserAccount , GlobalConfig }
复制代码
防抖节流

  1. // 深拷贝
  2. const deepClone = (target: any, map = new WeakMap()) => {
  3.   if (typeof target !== 'object' || target === null) return target
  4.   if (map.get(target)) return map.get(target)
  5.   const constructor = target.constructor
  6.   if (/^(Function|RegExp|Date|Map|Set)$/i.test(constructor.name))
  7.     return new constructor(target)
  8.   const cloneTarget = Array.isArray(target) ? [] : {}
  9.   map.set(target, cloneTarget)
  10.   for (const key of Object.keys(target)) {
  11.     // @ts-ignore
  12.     cloneTarget[key] = deepClone(target[key], map)
  13.   }
  14.   return cloneTarget
  15. }
  16. // 防抖
  17. let timer: NodeJS.Timeout | null = null
  18. function debounce(fn: any, delay: number, immediate: boolean) {
  19.   return function () {
  20.     // @ts-ignore
  21.     const context = this
  22.     const args = arguments
  23.     if (timer) clearTimeout(timer)
  24.     if (immediate) {
  25.       const callNow = !timer
  26.       timer = setTimeout(() => {
  27.         timer = null
  28.       }, delay)
  29.       if (callNow) fn.apply(context, args)
  30.     } else {
  31.       timer = setTimeout(() => {
  32.         fn.apply(context, args)
  33.       }, delay)
  34.     }
  35.   }
  36. }
  37. // 节流
  38. function throttle(fn: any, delay: number) {
  39.   let preTime = 0
  40.   return function () {
  41.     // @ts-ignore
  42.     const context = this
  43.     const args = arguments
  44.     const nowTime = +new Date()
  45.     if (nowTime - preTime >= delay) {
  46.       fn.apply(context, args)
  47.       preTime = nowTime
  48.     }
  49.   }
  50. }
  51. export { deepClone, debounce, throttle }
复制代码
正则匹配

  1. // 去除首尾空格
  2. const trim = (str) => {
  3.   return str.replace(/(^\s*)|(\s*$)/g, '')
  4. }
  5. // 匹配某个字符出现的个数
  6. const matchCount = (str, char) => {
  7.   // 需要转义的字符
  8.   const specialChars = ['.', '*', '+', '?', '|', '(', ')', '[', ']', '{', '}', '\\']
  9.   if (specialChars.includes(char)) {
  10.     char = '\\' + char
  11.   }
  12.   return (str.match(new RegExp(char, 'g')) || []).length
  13. }
  14. // 移除输入字符串 value 中所有非数字和非点号的字符,点号最多只能出现一次
  15. const removeNonNumeric = (value) => {
  16.   if (value === '') {
  17.     return ''
  18.   }
  19.   // 移除点开头的点号和0开头的数字
  20.   let result = value.replace(/^\./, '').replace(/^0+/, '0')
  21.   result = result.replace(/[^\d.]/g, '').replace(/(\d+\.\d*).*/, '$1')
  22.   // 字符串存在一个点号并且后面没有数字,则不判断
  23.   const array = result.split('.')
  24.   if (array.length > 1 && array[1] === '') {
  25.     return result
  26.   }
  27.   // 字符串最后一个字符是0,不判断
  28.   if (result.endsWith('0')) {
  29.     return result
  30.   }
  31.   return parseFloat(result)
  32. }
  33. export { trim, matchCount, removeNonNumeric }
复制代码
日期格式

  1. import dayjs from 'dayjs'
  2. // 格式化特定日期
  3. const formatDate = (date, formatStr = 'YYYY-MM-DD') => {
  4.   return dayjs(date).format(formatStr)
  5. }
  6. // 获取当前日期并格式化
  7. const getCurrentDate = (formatStr = 'YYYY-MM-DD HH:mm') => {
  8.   return dayjs().format(formatStr)
  9. }
  10. // 获取是周几
  11. const weekDays = ['周日', '周一', '周二', '周三', '周四', '周五', '周六']
  12. const getWeekDay = () => {
  13.   const index = dayjs().day()
  14.   return weekDays[index]
  15. }
  16. // 判断凌晨 上午 中午 下午 晚上
  17. const getDayPeriod = () => {
  18.   const hour = dayjs().hour()
  19.   if (hour >= 0 && hour < 6) {
  20.     return '凌晨'
  21.   } else if (hour >= 6 && hour < 12) {
  22.     return '上午'
  23.   } else if (hour >= 12 && hour < 14) {
  24.     return '中午'
  25.   } else if (hour >= 14 && hour < 18) {
  26.     return '下午'
  27.   } else {
  28.     return '晚上'
  29.   }
  30. }
  31. export { formatDate, getCurrentDate, getWeekDay, getDayPeriod }
复制代码
AI 辅助编码


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

杀鸡焉用牛刀

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表