0


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

前端代码审查(Code Review)

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

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

具体实践规范

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

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

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

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

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

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

  1. //bad
  2. cosnt obj ={}
  3. cosnt b = obj.a && obj.a.b
  4. console.log(b)// undefined//good
  5. cosnt obj ={}
  6. cosnt b = obj?.a?.b
  7. console.log(b)// undefined

4、定时器及时清理

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

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

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

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

  1. exportdefault{created(){this.getOrderNo()},methods:{asyncgetOrderNo(){const res =awaitthis.$api.getOrderNo()if(res.success){// 成功处理}}}}

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

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

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

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

9、简化switch case判断

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

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

  1. // badconstcheckGameStatus=()=>{if(status===0||(satuas===1&&isEnd===1)||(isEnd===2)){// 调用}}// goodconstisGaneOver=()=>{return(status===0||(satuas===1&&isEnd===1)||(isEnd===2))}constcheckGameStatus=()=>{if(isGameOver()){// 调用}}

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

  1. // badconstpublishPost=(post)=>{if(isLoggenIn){if(post){if(isPostValid()){doPublishPost(post)}else{thrownewError('文章不合法')}}else{thrownewError('文章不能为空')}}else{thrownewError('用户未登录')}}// goodconstpublishPost=(post)=>{if(!isLoggenIn){thrownewError('用户未登录')}if(!post){thrownewError('文章不能为空')}if(!isPostValid()){thrownewError('文章不合法')}doPublishPost(post)}// badconstcreateElement=(item)=>{if(item.type==='ball'){
  2. cosnt div = document.createElement('div')
  3. div.className ='ball'
  4. div.style.backgroundColor = item.color
  5. return div
  6. }elseif(item.type==='block'){const div = document.createElement('div')
  7. div.className ='block'
  8. div.style.backgroundColor = item.color
  9. return div
  10. }elseif(item.type==='square'){const div = document.createElement('div')
  11. div.className ='square'
  12. div.style.backgroundColor = item.color
  13. return div
  14. }else{thrownewError('未知元素类型')}}// good
  15. cosnt createElement=(item)=>{const validTypes =['ball','block','image']if(!validTypes.includes(item.type)){thrownewError('未知元素类型')}
  16. cosnt div = document.createElement('div')
  17. div.className = item.type
  18. div.style.backgroundColor = item.color
  19. return div
  20. }// badlet commodity ={phone:'手机',computer:'电脑',television:'电视',gameBoy:'游戏机',}functionprice(name){if(name === commodity.phone){
  21. console.log(1999)}elseif(name === commodity.computer){
  22. console.log(9999)}elseif(name === commodity.television){
  23. console.log(2999)}elseif(name === commodity.gameBoy){
  24. console.log(3999)}}price('手机')// 1999// goodconst commodity =newMap([['phone',1999],['computer',9999],['television',2999],['gameBoy',3999],])constprice=(name)=>{return commodity.get(name)}price('phone')// 1999

12、判断非空(使用空值合并操作符——??)

  1. // badif(value !==null&& value !==undefined&& value !==''){....}// goodif((value??'')!==''){...}

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

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

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

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

  1. import request from'@/utils/request'exportfunctionafterSaleApplyRefund(data){returnrequest({url:`/web/refundApplyOrder/applyRefund`,method:'put',
  2. data
  3. })}exportfunctiongetStoreList(params){returnrequest({url:`/webWaterStore/getMarkStoreTree`,method:'get',
  4. params
  5. })}....

建议API目录下新建index.js文件,用于统一导出所有API,在main.js引入并将api挂载到vue的原型上

  1. Vue.prototype.$api = api

;在页面直接使用

  1. this.$api.xxx

调用接口。

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

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

  1. // 自动导入modulesconstfiles: any =import.meta.globEager("./modules/*.ts");letmodules: any ={};// eslint-disable-next-line @typescript-eslint/no-unused-vars
  2. Object.entries(files).forEach(([k, v])=>{
  3. Object.assign(modules, v);});exportdefault{...modules
  4. };
  1. // useApiimport{ ComponentInternalInstance, getCurrentInstance }from"vue";exportdefaultfunctionuseApi(){const{ appContext }=getCurrentInstance()as ComponentInternalInstance;const proxy = appContext.config.globalProperties;return{
  2. proxy
  3. };}
  1. //页面使用<script setup lang="ts">import useApi from"@/hooks/useApi";const{ proxy }=useApi();constgetData=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–全局导入
  1. import Vue from'vue'// 全局自动注册baseComponents下的基础组件const requireComponent = require.context('./baseComponents',true,/\.vue$/)// 找到组件文件夹下以.vue命名的文件,如果文件名为index,那么取组件中的name作为注册的组件名
  2. requireComponent.keys().forEach(filePath=>{const componentConfig =requireComponent(filePath)const fileName =validateFileName(filePath)const componentName = fileName.toLowerCase()==='index'?capitalizeFirstLetter(componentConfig.default.name): fileName
  3. Vue.component(componentName, componentConfig.default || componentConfig)})//首字母大写functioncapitalizeFirstLetter(str){return str && str.charAt(0).toUpperCase()+ str.slice(1)}// 对符合'xx/xx.vue'组件格式的组件取组件名functionvalidateFileName(str){return/^\S+\.vue$/.test(str)&&
  4. str.replace(/^\S+\/(\w+)\.vue$/,(rs, $1)=>capitalizeFirstLetter($1))}
全局注册main.js
  1. import'@/components/index.js'// 全局基础组件注入
页面组件使用
  1. <template><div><t-table></t-table></div></template>

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

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

相关文章

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


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


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


本文转载自: https://blog.csdn.net/cwin8951/article/details/135872213
版权归原作者 wocwin 所有, 如有侵权,请联系我们删除。

“前端代码审查(Code Review)---具体实践规范会持续更新”的评论:

还没有评论