前端代码审查(Code Review)---具体实践规范会持续更新

前端代码审查(Code Review)

针对目录结构、SCSS规范、JS规范、Vue规范

可参照官方给出的风格指南(Code Review)

具体实践规范

1、POST/PUT/DELETE 请求按钮需要添加 loading 状态,防止重复提交。

建议使用 Element UI 提供的button 组件的loading属性,或者自己封装一个 loading 状态的按钮组件。

<el-button type="primary" :loading="loading" @click="handleSubmit"> 提交 </el-button>

2、模板上超过两个的判断条件,写成方法或者computed

<!--bad-->
<template><t-table v-if="satus==1&&orderStatus==2&&isShowTable"/>
</template>
<!--good-->
<template><t-table v-if="isChangeAvailiable"/>
</template>
<script>computed: {isChangeAvailiable() {return (this.satus==1&&this.orderStatus==2&&this.isShowTable);},},
</script>

3、可选链访问数组/对象元素

//bad
cosnt obj = {}
cosnt b = obj.a && obj.a.b
console.log(b)	// undefined
//good
cosnt obj = {}
cosnt b = obj?.a?.b
console.log(b)	// undefined

4、定时器及时清理

mounted () {this.timer = setInterval(() => {...}, 1000)
}
destroyed () {if (this.timer) {clearInterval(this.timer)}
}

5、window/body上的监听事件–需要解绑

mounted() {window.addEventListener('resize', this.fun)
}
beforeDestroy () {window.removeEventListener('resize', this.fun);
}

6、async await 结合使用(调用接口)

export default {created() {this.getOrderNo()},methods:{async getOrderNo() {const res = await this.$api.getOrderNo()if(res.success){// 成功处理}}}
}

7、使用try…catch…时–错误代码需要提示

try {// 成功处理
} catch (error) {// 处理异常的代码this.$message.error(error.message)
}

8、函数有很多参数,需要封装成一个对象

// bad--->这个方式参数就必须按顺序传递
const getUserInfo =(name,age,sex,mobile,hobby)=> {// 函数逻辑
}
// good
const getUserInfo =(userInfo)=> {// 函数逻辑const {name,age,sex,mobile,hobby} = userInfo
}

9、简化switch case判断

// bad
const counter =(state=0,action)=>{switch (action.type) {case 'ADD':return state + 1case 'MINUS':return state - 1default:return state}
}
// good
const counter =(state=0,action)=>{const step={'ADD':1,'MINUS':-1}return state + (step[action.type] ?? 0)
}

10、判断条件过多需要提取出来

// bad
const checkGameStatus =()=>{if(status===0||(satuas===1&&isEnd===1)||(isEnd===2)){// 调用}
}
// good
const isGaneOver =()=>{return (status===0||(satuas===1&&isEnd===1)||(isEnd===2))
}
const checkGameStatus =()=>{if(isGameOver()){// 调用}
}

11、if 判断嵌套—>错误前置

// bad
const publishPost =(post)=>{if(isLoggenIn){if(post){if(isPostValid()){doPublishPost(post)}else{throw new Error('文章不合法')}}else{throw new Error('文章不能为空')}}else{throw new Error('用户未登录')}
}
// good
const publishPost =(post)=>{if(!isLoggenIn){throw new Error('用户未登录')}if(!post){throw new Error('文章不能为空')}if(!isPostValid()){throw new Error('文章不合法')}doPublishPost(post)
}// bad
const createElement =(item)=>{if(item.type==='ball'){cosnt div = document.createElement('div')div.className = 'ball'div.style.backgroundColor = item.colorreturn div}else if(item.type==='block'){const div = document.createElement('div')div.className = 'block'div.style.backgroundColor = item.colorreturn div}else if(item.type==='square'){const div = document.createElement('div')div.className = 'square'div.style.backgroundColor = item.colorreturn div}else{throw new Error('未知元素类型')}
}// good
cosnt createElement =(item)=>{const validTypes = ['ball', 'block', 'image']if(!validTypes.includes(item.type)){throw new Error('未知元素类型')}cosnt div = document.createElement('div')div.className = item.typediv.style.backgroundColor = item.colorreturn div
}
// bad
let commodity = {phone: '手机',computer: '电脑',television: '电视',gameBoy: '游戏机',
}function price(name) {if (name === commodity.phone) {console.log(1999)} else if (name === commodity.computer) {console.log(9999)} else if (name === commodity.television) {console.log(2999)} else if (name === commodity.gameBoy) {console.log(3999)}
}
price('手机') // 1999
// good
const commodity = new Map([['phone', 1999],['computer', 9999],['television', 2999],['gameBoy', 3999],
])const price = (name) => {return commodity.get(name)
}
price('phone') // 1999

补充常规的—>目录结构规范:

项目根目录下创建 src 目录,src 目录下创建 api 目录、assets 目录、components 目录、directive 目录、router 目录、store 目录、utils 目录、views 目录。

1、api 目录存放所有页面API。

建议将每个页面的API封装成一个单独的js文件,文件名与页面名称相同(防止增删查改接口命名重复),并且都放在api下的modules目录下。

import request from '@/utils/request'
export function afterSaleApplyRefund(data) {return request({url: `/web/refundApplyOrder/applyRefund`,method: 'put',data})
}
export function getStoreList(params) {return request({url: `/webWaterStore/getMarkStoreTree`,method: 'get',params})
}
....

建议API目录下新建index.js文件,用于统一导出所有API,在main.js引入并将api挂载到vue的原型上Vue.prototype.$api = api;在页面直接使用this.$api.xxx调用接口。

WebPack自动加载配置API(使用require.context)
// 自动加载api
const commonApiObj = {}
const finalObj = {}
const modulesApi = require.context('./modules', true, /\.js$/)
modulesApi.keys().forEach(key => {const newKey = key.replace(/(\.\/|\.js)/g, '')commonApiObj[newKey] = require(`./modules/${newKey}`)
})
Object.values(commonApiObj).map(x => Object.assign(finalObj, x))
// console.log('所有业务接口--', finalObj)
export default {...finalObj
}
Vite自动加载配置API(使用import.meta.globEager)

(注册全局api方法 )instance.config.globalProperties.$api = api;

// 自动导入modules
const files: any = import.meta.globEager("./modules/*.ts");
let modules: any = {};
// eslint-disable-next-line @typescript-eslint/no-unused-vars
Object.entries(files).forEach(([k, v]) => {Object.assign(modules, v);
});
export default {...modules
};
// useApi
import { ComponentInternalInstance, getCurrentInstance } from "vue";
export default function useApi() {const { appContext } = getCurrentInstance() as ComponentInternalInstance;const proxy = appContext.config.globalProperties;return {proxy};
}
//页面使用
<script setup lang="ts">
import useApi from "@/hooks/useApi";
const { proxy } = useApi();
const getData = async () => {const res = await proxy.$api.xxx(接口名);if (res.success) {...}
}
</script>

2、assets 目录存放静态资源,如图片、字体、公共scss等。

3、components 目录存放公共组件(store也可以如下方式自动导入)。

建议将公共组件拆分为基础组件(baseComponents)和业务组件(pageComponents),基础组件存放一些通用的组件,如按钮、输入框、表格等,业务组件存放与具体业务相关的组件,如用户管理组件、权限管理组件等。

基础组件命名方式大驼峰,如:TTable;业务组件命名方式是小驼峰,如:importExcel。

组件文件夹下必须包含index.vue文件,index.vue文件中必须包含组件的name属性,name属性值必须与组件文件夹名一致。

基础组件复用性高,通常情况都是全局注册

components 目录下的index.js–全局导入
import Vue from 'vue'
// 全局自动注册baseComponents下的基础组件
const requireComponent = require.context('./baseComponents', true, /\.vue$/)
// 找到组件文件夹下以.vue命名的文件,如果文件名为index,那么取组件中的name作为注册的组件名
requireComponent.keys().forEach(filePath => {const componentConfig = requireComponent(filePath)const fileName = validateFileName(filePath)const componentName = fileName.toLowerCase() === 'index'? capitalizeFirstLetter(componentConfig.default.name): fileNameVue.component(componentName, componentConfig.default || componentConfig)
})
//首字母大写
function capitalizeFirstLetter (str) {return str && str.charAt(0).toUpperCase() + str.slice(1)
}
// 对符合'xx/xx.vue'组件格式的组件取组件名
function validateFileName (str) {return /^\S+\.vue$/.test(str) &&str.replace(/^\S+\/(\w+)\.vue$/, (rs, $1) => capitalizeFirstLetter($1))
}
全局注册main.js
import '@/components/index.js' // 全局基础组件注入
页面组件使用
<template><div><t-table></t-table></div>
</template>

4、utils 目录存放公共方法,如全局loading,axios封装,正则校验等。

axios封装(request.js)
import axios from 'axios'
import { Notification, MessageBox, Message } from 'element-ui'
import store from '@/store'
import { getToken } from '@/utils/auth'export default function (config) {// 创建axios实例const service = axios.create({// baseURL: process.env.VUE_APP_BASE_API,baseURL: process.env.VUE_APP_BASE_API ,// 超时 btimeout: 50000})// request拦截器service.interceptors.request.use(config => {getToken() && (config.headers['Authorization'] = getToken())localStorage.getItem('store_id') && (config.headers['Store-Id'] = localStorage.getItem('store_id'))config.headers['Content-Type'] = config.headers['Content-Type'] || 'application/json'// 8080if (config.type == 'file') {config.headers['content-type'] = 'application/multipart/form-data'} else if (config.type == 'form') {config.headers['Content-type'] = 'application/x-www-form-urlencoded'}if (config.method.toLowerCase() === 'get') {config.data = true}return config},error => {console.log(error)Promise.reject(error)})// 响应拦截器service.interceptors.response.use(res => {const code = res.data.codeif (code === 401) {MessageBox.confirm('登录状态已过期,您可以继续留在该页面,或者重新登录','系统提示', {confirmButtonText: '重新登录',cancelButtonText: '取消',type: 'warning'}).then(() => {store.dispatch('FedLogOut').then(() => {if (!window.__POWERED_BY_QIANKUN__) {// 为了重新实例化vue-router对象 避免buglocation.reload()} else {window.location.href = '/'}})})} else if (code !== 200) {Notification.error({title: res.data.msg})return Promise.reject('error')} else {return res.data}},error => {console.log('err' + error)Message({message: error.message,type: 'error',duration: 5 * 1000})return Promise.reject(error)})return service(config)
}

相关文章

基于ElementUi再次封装基础组件文档


基于ant-design-vue再次封装基础组件文档


vue3+ts基于Element-plus再次封装基础组件文档

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

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

相关文章

jQuery遍历(其他遍历)

1、.add&#xff08;&#xff09;: 添加元素到匹配的元素集合 例一&#xff1a; <script> $(document).ready(function(){ $("div").add("p").css("background-color","red") }) </script…

前端canvas项目实战——简历制作网站(二)——右侧属性栏(颜色)

目录 前言一、效果展示二、实现步骤1. 实现一个自定义的选色板2. 创建属性工厂&#xff0c;为每个对象定制属性3. 为canvas对象注册监听器&#xff0c;点击不同对象时更新属性列表 三、Show u the code后记 前言 上一篇博文中&#xff0c;我们实现了左侧工具栏&#xff0c;通过…

kubernetes实战教程:kubernetes简介

kubernetes实战教程:kubernetes简介 简介 官网:https://kubernetes.io/zh-cn/ 一款生产级别的容器编排系统,也成为k8s,用于自动部署,扩展和管理容器化应用的开源系统.Google在2014年开源了kubernetes(后续就用k8s来代替了)这个项目.k8s建立在Google大规模运行生产工作负载十几…

解决vue 2.6通过花生壳ddsn(frp内网穿透)实时开发报错Invalid Host header和websocket

请先核对自己的vue版本&#xff0c;我的是2.6.14&#xff0c;其他版本未测试 起因 这两天在维护一个基于高德显示多个目标&#xff08;门店&#xff09;位置的项目&#xff0c;由于高德要求定位必须使用https服务&#xff0c;遂在本地无法获取到定位坐标信息&#xff0c;于是…

当一个json存在id 和_id 的时候,使用JSONObject.parseObject进行序列号操作,映射错误

FastJson默认会将Java对象中的下划线风格的字段名转换为驼峰风格的属性名。如果你想保留原始的下划线风格的字段名&#xff0c;可以在对应的字段上添加JSONField注解&#xff0c;并设置其name属性为下划线风格的字段名。 {"hasExtraBed":0,"hasWindow":2,&…

OpenCV笔记之图像处理中遮罩和掩模的关系

OpenCV笔记之图像处理中遮罩和掩模的关系 code review 文章目录 OpenCV笔记之图像处理中遮罩和掩模的关系1.遮罩详解遮罩的创建遮罩的应用遮罩的主要应用遮罩的类型如何创建遮罩遮罩在图像处理中的应用方式 2.遮罩和掩模的关系 1.遮罩详解 在图像处理中&#xff0c;遮罩&#…

字符串和格式化输入/输出

本文参考C Primer Plus进行C语言学习 文章目录 strlen()函数sizeof使用数据类型 1.strlen()函数 之前提到的sizeof运算符它以字节为单位给出对象的大小。strlen()函数给出字符串中的字符长度。 #include<stdio.h> #include<string.h> #define PRAISE "You ar…

IS-IS:05 ISIS开销值和协议优先级

IS-IS 协议为路由器的每个 IS-IS 接口定义并维护了一个 level-1 开销值和一个 level-2开销值。开销值可以在接口上或者全局上手动配置&#xff0c;也可以使用 auto-cost自动计算确定。 修改接口cost&#xff1a; int g0/0/0 isis cost 50修改全局cost&#xff1a; isis cir…

Github 无法正常访问?一招解决

查询IP网址: https://ip.chinaz.com/ 主页如下&#xff1a; 分别查询以下三个网址的IP&#xff1a; github.com github.global.ssl.fastly.net assets-cdn.github.com 修改 hosts 文件&#xff1a; 将 /etc/hosts 复制到 home 下 sudo cp /etc/hosts ./ gedit hosts 在底下…

实现成本最优的一体化管理,新一代数据平台的建设方式丨爱分析调研

导读 1.当前&#xff0c;企业在大数据和数据中台建设上取得成果&#xff0c;但数据开发管理仍具挑战性&#xff08;成本、效率、复杂度&#xff09;。 2.随数据平台领域成熟&#xff0c;厂商应结合自身需求&#xff0c;重新思考“基于开源自建数据平台”的重资产模式与“购买云…

【C语言】(3)字符

字符串 1. 字符串简介 在C语言中&#xff0c;字符串是由字符数组构成的序列&#xff0c;以空字符&#xff08;\0&#xff09;结尾。这个空字符不可见&#xff0c;用于标记字符串的结束。C语言中没有专门的字符串类型&#xff0c;通常使用字符数组表示字符串。 2. 声明和初始…

ARP攻击防范

概念 ARP(Address Resolution Protocol)安全是针对ARP攻击的一种安全特性,它通过一系列对ARP表项学习和ARP报文处理的限制、检查等措施来保证网络设备的安全性。ARP安全特性不仅能够防范针对ARP协议的攻击,还可以防范网段扫描攻击等基于ARP协议的攻击。 ARP泛洪攻击 1)A…

swift 进阶知识点

本文的知识点会比较散&#xff0c;是基础语法之外的一些进阶内容&#xff0c;如果有写的不妥的地方&#xff0c;欢迎评论区指正&#xff5e; Optional 可选值是通过枚举实现的&#xff1a; enum Optional<Wrapped> {case nonecase some(Wrapped)对于Optional<Wrapp…

React中实现虚拟加载滚动

前言&#xff1a;当一个页面中需要接受接口返回的全部数据进行页面渲染时间&#xff0c;如果数据量比较庞大&#xff0c;前端在渲染dom的过程中需要花费时间&#xff0c;造成页面经常出现卡顿现象。 需求&#xff1a;通过虚拟加载&#xff0c;优化页面渲染速度 缺点&#xff1a…

Facebook 广告帐户:多账号运营如何防止封号?

Facebook目前是全球最受欢迎的社交媒体平台之一&#xff0c;拥有超过27亿活跃用户。因此&#xff0c;它已成为个人和企业向全球受众宣传其产品和服务的重要平台。 然而&#xff0c;Facebook 制定了广告商必须遵守的严格政策和准则&#xff0c;以确保其广告的质量和相关性&…

一文搞懂Jenkins持续集成解决的是什么问题

1、持续集成的定义 大师 Martin Fowler 是这样定义持续集成的: 持续集成是一种软件开发实战, 即团队开发成员经常集成他们的工作. 通常, 每个成员每天至少集成一次, 也就意味着每天可能发生多次集成. 持续集成并不能消除Bug, 而是让它们非常容易发现和改正. 根据对项目实战的…

redis面试题合集-基础

前言 又来到每日的复习时刻&#xff0c;昨天我们学习了mysql相关基础知识&#xff0c;还有分布式数据库介绍&#xff08;后续总结时再持续更新&#xff09;。今日继续学习缓存杀器&#xff1a;redis redis基础面试题合集 什么是Redis&#xff1f; Redis是一个开源的、内存中…

【C++11并发】mutex 笔记

简介 在多线程中往往需要访问临界资源&#xff0c;C11为我们提供了mutex等相关类来保护临界资源&#xff0c;保证某一时刻只有一个线程可以访问临界资源。主要包括各种mutex&#xff0c;他们的命名大都是xx_mutex。以及RAII风格的wrapper类&#xff0c;RAII就是一般在构造的时…

docker 修改默认存储位置

✨✨✨✨✨✨✨ &#x1f380;前言&#x1f381;查看前面docker储存位置&#x1f381;移动文件位置&#x1f381;修改配置文件docker.service&#x1f381;修改daemon.json&#x1f381;加载配置并重启 &#x1f380;前言 最近服务出现系统盘满了,发现其中docker存储占用很大一…

Keycloak - docker 运行 前端集成

Keycloak - docker 运行 & 前端集成 这里的记录主要是跟我们的项目相关的一些本地运行/测试&#xff0c;云端用的 keycloak 版本不一样&#xff0c;不过本地我能找到的最简单的配置是这样的 docker 配置 & 运行 keycloak keycloak 有官方(Red Hat Inc.)的镜像&#…