想要写好合格的代码,分为三部分:功能、控制、运维
一、功能
功能,也就是产品经理甩给你的需求。
做好功能主要靠"面向百度编程",基本上我们大部分的需求,网上都有现成的轮子了。
值得一提的是,大部分人只注意第一部分【功能】,功能实现就万事大吉了,但如果你不处理后面的控制和运维,代码胡存在很多隐性的问题和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