爆肝5小时,带你看懂如何写出“优秀”的代码

想要写好合格的代码,分为三部分:功能、控制、运维

一、功能

功能,也就是产品经理甩给你的需求。
做好功能主要靠"面向百度编程",基本上我们大部分的需求,网上都有现成的轮子了。

值得一提的是,大部分人只注意第一部分【功能】,功能实现就万事大吉了,但如果你不处理后面的控制和运维,代码胡存在很多隐性的问题和bug。

二、控制

控制,就是在这个业务里,你在交互上做的控制。
常见的交互控制有防抖,节流,按钮禁用,交互优化等等

1.函数防抖(debounce)

在事件被触发n秒后再执行回调,如果在这n秒内又被触发,则重新计时。

看一个🌰(栗子):

//模拟一段ajax请求
function ajax(content) {
  console.log('ajax request ' + content)
}

let inputa = document.getElementById('unDebounce')

inputa.addEventListener('keyup', function (e) {
    ajax(e.target.value)
})

看一下运行结果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jM3ZOoe8-1656650437145)(https://s3.bmp.ovh/imgs/2022/06/21/931c681b00813134.png)]
可以看到,我们只要按下键盘,就会触发这次ajax请求。不仅从资源上来说是很浪费的行为,而且实际应用中,用户也是输出完整的字符后,才会请求。下面我们优化一下:

//模拟一段ajax请求
function ajax(content) {
  console.log('ajax request ' + content)
}

function debounce(fun, delay) {
    return function (args) {
        let that = this
        let _args = args
        clearTimeout(fun.id)
        fun.id = setTimeout(function () {
            fun.call(that, _args)
        }, delay)
    }
}
    
let inputb = document.getElementById('debounce')

let debounceAjax = debounce(ajax, 500)

inputb.addEventListener('keyup', function (e) {
        debounceAjax(e.target.value)
    })

这个🌰就很好的解释了,如果在时间间隔内执行函数,会重新触发计时。biu会在第一次1.5s执行后,每隔1s执行一次,而boom一次也不会执行。因为它的时间间隔是2s,而执行时间是1s,所以每次都会重新触发计时

2.函数节流(throttle)

规定在一个单位时间内,只能触发一次函数。如果这个单位时间内触发多次函数,只有一次生效。

看一个🌰:

function throttle(fun, delay) {
        let last, deferTimer
        return function (args) {
            let that = this
            let _args = arguments
            let now = +new Date()
            if (last && now < last + delay) {
                clearTimeout(deferTimer)
                deferTimer = setTimeout(function () {
                    last = now
                    fun.apply(that, _args)
                }, delay)
            }else {
                last = now
                fun.apply(that,_args)
            }
        }
    }

    let throttleAjax = throttle(ajax, 1000)

    let inputc = document.getElementById('throttle')
    inputc.addEventListener('keyup', function(e) {
        throttleAjax(e.target.value)
    })

看一下运行结果:

可以看到,我们在不断输入时,ajax会按照我们设定的时间,每1s执行一次。

结合应用场景

debounce

search搜索联想,用户在不断输入值时,用防抖来节约请求资源。
window触发resize的时候,不断的调整浏览器窗口大小会不断的触发这个事件,用防抖来让其只触发一次

throttle

鼠标不断点击触发,mousedown(单位时间内只触发一次)
监听滚动事件,比如是否滑到底部自动加载更多,用throttle来判断

3.交互优化

1)点击事件处理

  • 给大部分文段赋值点击事件,如果这段文字用户有复制的需求,要处理选中文本和点击冲突处理。
    比较好的解决方法:封装按下事件(建议封装为vue指令),处理复制和点击冲突。
export function addTouchEvent(item, callback, target) {
    // 鼠标按下事件
    item.onmousedown = (e) => {
        let startX = e.clientX
        document.onmouseup = (evt) => {
            let endX = evt.clientX
            let moveX = endX - startX
            document.onmousemove = null
            document.onmouseup = null
            item.releaseCapture && item.releaseCapture()
            if (Math.abs(moveX) < 10) {
                //
                callback && callback.call(target);
            }
        }
        return true
    }

在组件中使用:

<div class="search-list-item" ref="item">

JS:

import { addTouchEvent } from '@/utils/project/viewTools.js'

export default {
    mounted() {
    // console.log('templaye')
    // console.log(this.data)
    this.initEvent()
    },
    methods: {
    initEvent() {
      let item = this.$refs.item
      addTouchEvent(item, this.handleTemplate, this)
    },
}
  • 可以点的地方必须要加悬浮小手样式。也就是style="cursor:pointer",注意:文字和图标都要支持点击。

  • 如果某种条件不能点击按钮的话,应该禁用或隐藏按钮。

  • 操作按钮没有点击提示,点击直接进行了操作
    特别是一些重要不可逆的操作,比如删除,一定要弹对话框提示是否确认操作。

三、运维

运维指的是,我们在编写代码时,需要考虑一些异常情况如何处理,比如极大值、极小值、空值、接口报错等等。

1.极大值,极小值,空值

1)文本超长处理—极限值处理

标题、内容等文本必须要考虑文本超长的情况,如果文本超出区域宽度,加单行省略样式,而且要加title属性,让鼠标悬浮能展示全文。

① 单行文本溢出显示省略号

样式如下:

.ellipsis-line {
  overflow:hidden;
  text-overflow:ellipsis;
  white-space:nowrap;
}
② 多行文本溢出显示省略号

接下来重点说一说多行文本溢出显示省略号,如下。
实现方法:

.multiple-line {
  display: -webkit-box;
  /*! autoprefixer: off */
  -webkit-box-orient: vertical;
  /* autoprefixer: on */
  -webkit-line-clamp: 3;
  overflow: hidden;
}

注意,下面这行代码:

/*! autoprefixer: off */
    -webkit-box-orient: vertical;
  /* autoprefixer: on */

webpack打包 -webkit-box-orient: vertical;这个属性有时候会不生效从而导致无法多行省略,这个属性按上面加个注释就行了。

2)列表高度可能超长的情况添加滚动条

最大高度根据页面定,代码如下:

.yScrollBar {
  overflow-y: auto;
  max-height: 540px;
}

注意:滚动条要结合ui图,实现完美观感,不要出现多个区域滚动条叠加展示问题!

3)列表内容过少需要设置最小高度

min-height: 750px;

一般ui图都会有区块的最小高度,所以页面内容过少需要设置最小高度,否则会出现:数据只够上部展示,下部出现空白。

4)数据空值处理

1.用了字符串、数组原型的方法的变量要考虑空值的情况
用字符串、数组、对象类型的原型方法时,要特别注意,比如replace方法、forEach等等,一定判断是否为空。
为空的判断主要是 “”,null,undefined三种:

function isEmpty(obj) {
  if (typeof obj === 'undefined' || obj == null || obj === '') {
    return true
  } else {
    return false
  }
}

如果数据可能为空,在template就要做对应的空值处理,例如下图在Vue中的处理:

<div>{{item || ''}}<div/>

2.表单输入校验

参数校验是每个程序员必备的基本素养。你的方法处理,必须先校验参数。比如入参是否允许为空,入参长度是否符合你的预期长度。
常见的表单校验有:必填校验、类型校验、长度校验、是否重复等等。

这里以antd的表单为例,主要通过v-decorator注册validatorRules对象来实现

<a-form :form="form">
<a-form-item label="时效性" >
  <a-input v-decorator="['timelinessList',validatorRules.timelinessList]">
  </a-input>
</a-form-item>
</a-form>

在data中定义validatorRules的规则来校验表单

export default {
  data(){
    return {
        form: this.$form.createForm(this),
         validatorRules: {
          timelinessList: {
            rules: [{ required: true, message: '请输入时效性!' }]
          }
        }
    }
  }
}

3.样式检查

1)不同尺寸屏幕下或鼠标滚轮缩放样式不能乱、考虑125%的缩放情况

解决方案1:监听缩放变化,通过控制body标签的zoom,来抵消缩放的影响

pc端web页面开发时,发现windows系统经常推荐用户使用125%、150%比例的缩放窗口,这样导致web页面被进行缩放,除此之外还有人为的按钮缩放。故此,在页面devicePixelRatio(设备像素比例)变化后,通过计算页面body标签zoom修改其大小,来抵消devicePixelRatio带来的变化。

1.获取系统类型

判断是不是需处理的系统,目前只有windows系统下有此问题

_getSystem() {
		let flag = false;
		var agent = navigator.userAgent.toLowerCase();
		//var isMac = /macintosh|mac os x/i.test(navigator.userAgent);
		//if(isMac) {
		//  return false;
		//}
		//现只针对windows处理,其它系统暂无该情况,如有,继续在此添加
		if(agent.indexOf("windows") >= 0) {
			return true;
		}
	}
2.监听方法兼容写法
_addHandler(element, type, handler) {
		if(element.addEventListener) {
			element.addEventListener(type, handler, false);
		} else if(element.attachEvent) {
			element.attachEvent("on" + type, handler);
		} else {
			element["on" + type] = handler;
		}
	}
3.校正浏览器缩放比例
_correct() {
		let t = this;
		document.getElementsByTagName('body')[0].style.zoom = 1 / window.devicePixelRatio;
	}
4.监听页面缩放
_watch() {
		let t = this;
		t._addHandler(window, 'resize', function() { //注意这个方法是解决全局有两个window.resize
			//重新校正
			t._correct()
		})
	}
5.初始化页面比例
init() {
		let t = this;
		if(t._getSystem()) { //判断设备,目前只在windows系统下校正浏览器缩放比例
			//初始化页面校正浏览器缩放比例
			t._correct();
			//开启监听页面缩放
			t._watch();
		}
	}
6.全部代码
/**
 * @author trsoliu
 * @date  2019-12-05
 * @description 校正windows页面在系统进行缩放后导致页面被放大的问题,通常放大比例是125%、150%
 * **/

class DevicePixelRatio {
	constructor() {
		//this.flag = false;
	}
	//获取系统类型
	_getSystem() {
		let flag = false;
		var agent = navigator.userAgent.toLowerCase();
		//		var isMac = /macintosh|mac os x/i.test(navigator.userAgent);
		//		if(isMac) {
		//			return false;
		//		}
		//现只针对windows处理,其它系统暂无该情况,如有,继续在此添加
		if(agent.indexOf("windows") >= 0) {
			return true;
		}
	}
	//获取页面缩放比例
	//	_getDevicePixelRatio() {
	//		let t = this;
	//	}
	//监听方法兼容写法
	_addHandler(element, type, handler) {
		if(element.addEventListener) {
			element.addEventListener(type, handler, false);
		} else if(element.attachEvent) {
			element.attachEvent("on" + type, handler);
		} else {
			element["on" + type] = handler;
		}
	}
	//校正浏览器缩放比例
	_correct() {
		let t = this;
		//页面devicePixelRatio(设备像素比例)变化后,计算页面body标签zoom修改其大小,来抵消devicePixelRatio带来的变化。
		document.getElementsByTagName('body')[0].style.zoom = 1 / window.devicePixelRatio;
	}
	//监听页面缩放
	_watch() {
		let t = this;
		t._addHandler(window, 'resize', function() { //注意这个方法是解决全局有两个window.resize
			//重新校正
			t._correct()
		})
	}
	//初始化页面比例
	init() {
		let t = this;
		if(t._getSystem()) { //判断设备,目前只在windows系统下校正浏览器缩放比例
			//初始化页面校正浏览器缩放比例
			t._correct();
			//开启监听页面缩放
			t._watch();
		}
	}
}
export default DevicePixelRatio;
7.使用
//vue使用
//在app.vue或者其它全局的文件中引入函数
import DevicePixelRatio from './XX/assets/js/libs/devicePixelRatio.js';
//在vue生命周期created中添加
created() {
    new DevicePixelRatio().init();
}
//其它使用
//全局引入devicePixelRatio.js
//在页面加载之时,调用此方法初始化页面比例
new DevicePixelRatio().init();

总结一下思路,页面在加载之时会检测页面的设备像素比例,然后重置页面比例,之后启动监听页面改变,发现变化就重置页面比例;

解决方案2:禁用缩放

一般的,在head标签加上就行

<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0,user-scalable=no"/>

阻止浏览器强制缩放(safari也好使)

window.onload = function() {
    // 阻止双击放大
    var lastTouchEnd = 0;
    document.addEventListener('touchstart', function(event) {
        if (event.touches.length > 1) {
            event.preventDefault();
        }
    });
    document.addEventListener('touchend', function(event) {
        var now = (new Date()).getTime();
        if (now - lastTouchEnd <= 300) {
            event.preventDefault();
        }
        lastTouchEnd = now;
    }, false);

    // 阻止双指放大
    document.addEventListener('gesturestart', function(event) {
        event.preventDefault();
    });
}

4.浏览器兼容问题

现在的市场上有很多种类的浏览器,不同种类的浏览器的内核也不尽相同,所以不同浏览器对代码的解析会存在差异,这就导致对页面渲染效果不统一的问题。
市场上常见的浏览器内核主要有四种:Webkit内核、Presto内核、Trident内核、Gecko内核。


常见的浏览器兼容性可分为三类:
①HTML兼容
②CSS兼容
③JavaScript兼容

1)HTML兼容

html兼容问题涉及到的主要是不同的浏览器能识别的标签不同,有些高级的标签没办法在低级的浏览器中使用,但是我们的常规布局还是以div+css+input为主,这类问题很少会出现

2)CSS兼容

CSS兼容主要都是兼容IE(好在IE终于死了,鼓掌鼓掌!),不过如果你的项目必须要兼容IE的话,就看看下面这篇文章吧,总结的很全。
可参考文章:
浏览器的兼容问题及解决方案整理(建议收藏)

3)JS兼容

JS兼容主要靠babel这个库,把JS高版本语法转换为低版本语法。参考文章:
前端工程化(7):你所需要知道的最新的babel兼容性实现方案
精通前端 polyfill ,兼容各浏览器运行E6语法
Babel基础入门

5.优化代码的复用性和可维护性

1)以设计模式为荣,以代码重复为耻

日常工作中,我们要以设计模式为荣。

比如策略模式、工厂模式、模板方法模式、观察者模式、单例模式、责任链模式等等,都是很常用的。在恰当的业务场景,我们还是把设计模式用上吧。设计模式可以让我们的代码更优雅、更具有扩展性。但是不要过度设计哈,不要硬套设计模式。

我们还要以重复代码为耻。重复代码,我相信每个程序员都讨厌的,尤其有时候你的开发工具还会给你提示出来。我们可以抽取公共方法,抽取公用变量、扩展继承类等方式去消除重复代码。

2)以优化代码为荣,以复制粘贴为耻

日常开发中,很多程序员在实现某个功能时,如果看到老代码有类似的功能,他们很喜欢复制粘贴过来。这样很容易产生重复代码,所以我们要以复制粘贴为耻。一般建议加自己的思考,怎么优化这部分代码,怎么抽取公用方法,用什么设计模式等等。

3)在vue中怎么更好的复用逻辑

vue2的混入和extend,extend
参考笔记:
https://note.youdao.com/s/IKkuUpks
https://note.youdao.com/s/NqajYZrk

这里多说一点extend的技巧。
真实项目中我有个场景是,有两个地方要复用一个组件,在ui图的样式上,两个地方一模一样,唯一有区别的就是loadData里面请求的方法不同。一般人的做法复制一套代码,然后手动改loadData,但这样对后期维护不方便,因为一旦改样式或者JS,你就要改两个地方。那么这里就可以用extend继承组件,然后loadData方法。

4)抽取父类

做好类的继承。
案例,比如datasheet类,字段类

5)以定义常量为荣,以魔法数字为耻

大家平时工作中,是不是经常看到魔法数字。魔法数字(Magic Number)是指拥有特殊意义,却又不能明确表现出这种意义的数字。程序里面存在魔法数字,易读性很差,且非常难以维护。

如下:

if(type==1){
   return "保安"
}else if(type==2){
   return "程序员"
}else{
   return "其他"
}

代码中的1、2就表示魔法数字,我们可以用常量取代魔法数,或者定义枚举去代替魔法数字哈。

结语

只能在网上找轮子,实现需求功能的,只能叫"码农"。能够做好业务功能的控制和运维,才算是合格的"程序员"。

参考文章:
https://mp.weixin.qq.com/s/nKIB-j0q2iQGKVBAJ9qdGA
https://juejin.cn/post/6844903669389885453
https://juejin.cn/post/7066818427922415629
https://juejin.cn/post/6844904014958739470

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
当然可以,下面我来解释一下代码的主要部分。 首先,我们定义了两个变量 `sum` 和 `count`,用于统计班级总分和人数。初始值都为0。 ```python sum = 0 count = 0 ``` 然后,我们使用了一个while循环来不断输入每个同学的成绩。循环条件为 `True`,表示循环条件永远为真,因此需要在循环体内部使用 `break` 语句来跳循环。 在循环体内部,我们先使用 `float(input())` 函数来读取用户输入的成绩,并将其转换为浮点数类型。如果用户输入的成绩小于0,说明输入结束,我们使用 `break` 语句跳循环,进入后续的计算和输。 如果用户输入的成绩大于等于0,说明输入的成绩有效,我们将其累加到 `sum` 变量中,并将 `count` 变量加1,表示班级人数加1。 ```python while True: score = float(input()) if score < 0: break sum += score count += 1 ``` 如果 `count` 变量的值为0,说明没有输入任何有效成绩,我们输 "人数为0"。 ```python if count == 0: print("人数为0") ``` 如果 `count` 变量的值大于0,说明已经输入了有效成绩,我们计算平均分并输。平均分的计算公式为 `avg = sum / count`,其中 `sum` 表示班级总分,`count` 表示班级人数。为了保留两位小数,我们使用了字符串格式化函数 `"{:.2f}".format(avg)`。 ```python else: avg = sum / count print("{:.2f}".format(avg)) ``` 希望这样的解释能帮助你理解这段代码。如果还有什么不的地方,可以随时问我。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值