三亚人才招聘网站,有设计师做的装修效果图的网站,网络运营有前途吗,阜宁网站制作服务商前端代码评审规范
代码千万行#xff0c;安全第一行#xff1b;代码不规范#xff0c;维护两行泪。阅读规范前#xff0c;请先阅读项目自带的README.md#xff0c;配置好项目代码统一的格式检查。
一、代码规范
1. 文件规范
1.1 文件目录模块划分
1.1.1 文件创建原则…前端代码评审规范
代码千万行安全第一行代码不规范维护两行泪。阅读规范前请先阅读项目自带的README.md配置好项目代码统一的格式检查。
一、代码规范
1. 文件规范
1.1 文件目录模块划分
1.1.1 文件创建原则
安放文件时遵循“以功能关联划分为主、类型关联划分为辅”的原则。关联文件之间如果不是就近当前文件夹内安放必需在文件中注释写明文件功能的关联关系必要时两边文件都需要写明。辅助模块config / util需要放在当前业务模块的最顶层不能放在某个子功能的文件夹下。
1.1.2 文件类型模块
由于各个项目命名方式不同本文只按照类型划分大概分为下列几种模块项目文件众多、文件夹模块划分不明确的情况下可以参考整理。
service 服务端模块src/pages 页面模块nsrc/components 组件模块Mobx / Redux / models(Dva.js) 数据源模块Styles 公共样式表Utils 公共方法文件夹下设多个公共模块。 比如通用数据处理方法、业务数据处理方法、业务通用非数据处理方法、埋点方法、特殊hook方法、第三方服务相关方法等。Config 业务相关公共配置Project-config 工程化配置文件夹Dockerfile deployment.yamlFiles / Public 根目录下静态文件Static 图片/视频/html等静态文件资源
1.1.3 处理参考
定义模糊的文件如何安放 比如一个配置文件既可以放在书写功能的文件夹中又可以放在最外层的Config文件夹中。 如果是工程化配置文件应该高于业务config放置在公共 / 顶层文件夹内。 如果是业务配置此时应遵循功能关联为主的原则此文件是否只被当前功能使用如果只被当前功能使用则就近安放在功能关联文件夹的顶层文件夹内。如果是公用config则可以按照类型划分放在外层config文件夹中并且安放时依旧遵循“功能为主原则”区分是否有类似功能config是否是业务config / 配置config安放在相同功能的文件夹中。 如果不存在此功能的文件则可以新创建一个文件注意命名规范写明文件功能注释并且在Code Review中告知同项目组开发成员大家可以一起讨论是否需要新开文件还是安放在其他文件内。
2. 命名规范
常用的命名方式有小驼峰(camelCase)、大驼峰(PascalCase)、短横线(-连接)、下划线(_链接)。 参考命名一部分参考后端开发的命名规范有助于前后对接时减少转换损耗阿里巴巴Java开发手册
2.1 业务文件夹命名
业务文件泛指页面page文件夹。
文件目录命名小驼峰式
有复数结构时要采用复数命名法assets、components、directives、mixins、utils、views等。不能使用中文拼音命名使用英文单词命名。不允许以非英文字符开头和以非英文字符结尾。尽量不采用缩写单词除非语义非常明确名称可以取长名字重点是语义清晰。
2.2 类命名
类组件采用大驼峰式命名。基础类、抽象组件类采用Base开头命名比如Base.jsxBaseButton.jsxBasePicker.jsx等等。方法类也遵循大驼峰式命名比如通用的class方法将会被其他class方法继承。
2.3 组件命名
组件的定义为继承了ReactComponent类的组件或者Hook组件。
组件命名采用大驼峰式命名。组件命名使用全名词由两个以上的单词组成推荐格式为【引用路径副词主要功能】。有利于文件夹顺序排列展示。
引用路径尽量以当前组件被引用的页面相对路径开头放置在相对路径的components文件夹中。egProduct、OrderList、OrderDetail等等。副词修饰组件功能的词汇。 eg移动端专用组件Mobile、通用业务组件Common、基础架构的全局组件Global主要功能如果是通用组件则以主要功能开头放置在最顶层的components文件夹中功能命名可参考Antd组件最后的名词与功能紧密相关推荐egList 、Table、Modal、Form、Upload、Action、ActionButtons、DataDisplay、DataView、Text、Item等等。如果组件命名时引用路径指向明确为当前文件夹名称可酌情省略引用路径直接命名。实在想不出怎么取英文名可以直接找GPT帮忙取一个。
index命名仅限于当前目录下同名导出的文件但是export default导出的组件禁止取名Index最好以文件夹名称以想要使用的组件名为导出名称便于索引和引用提示。
// 比如Select组件文件夹下导出index.js文件为组件。
~/Select/index.js// ❌
class Index extends Component{}
export Index;// ☑️
class Select extends Component{}
or function Select(props){}export default Select;和父组件紧密耦合的子组件应该以父组件名作为前缀命名。根据第2点规则使用统一路径前缀也可以
比如List页面下存在多个子组件应该以List开头设置子组件名称。
components
├─ List.js
├─ ListItem.js
├─ ListItemDataView.js // ❌
├─ StateAction.js // ❌// ☑️
components
├─ List.js
├─ ListItem.js
├─ ListItemDataView.js
├─ ListItemStateAction.js如果components中存在大量相同前缀的组件可以考虑创建一个总前缀的文件夹将内部组件放置其中或者将相同前缀的文件从公共components中移出到相对路径的components文件夹下。避免前缀过长带来的负担。但是一般只推荐在非常大型 (如有 100 个组件) 的应用下才考虑这么做因为在多级目录间找来找去要比在单个 components 目录下滚动查找要花费更多的精力。
2.4 变量 / 常量 命名
一般业务上在函数作用域中声明的变量/常量命名采用小驼峰式。可以与后端传参格式一致
布尔类型需要一个标识变量含义的前缀比如has, is, can, should等数组/集合等复数形式最好以s或list等能够标识复数形式的后缀结尾标识当前变量是复数形式提高可读性
贯穿页面生存周期始终存在的常量使用全大写方式中间以下划线分割如MAX_COUNT系统变量、常量可使用__两个下划线开头命名。其他前缀规范
只在当前函数作用域中暂时使用的临时变量以命名简要清晰、方便书写为准即可比如以_开头命名等。DOM对象 / JQuery对象 $变量名。全局使用的变量为了特殊说明可以使用G_变量名大驼峰方式命名。或者其他项目中内部约定的命名方式。
2.5 方法命名
方法命名采用小驼峰式以动词宾语(动宾短语)的方式命名如果宾语是组件本身并且功能明确可以采用on动词的方式命名 参考antd的方法参数命名必要时添加注释区别。 动词get、query、handle、show、change、open、search、select等等 宾语List、Modal、Data等等。 on动词onChange、onSelect、onShow等等。
2.6 配置文件命名
配置文件命名以小驼峰方式统一以config.js结尾。配置文件在pages页面文件夹下根据Next项目最佳实践规则应采用.jsx后缀。
2.7 样式文件命名
Less文件命名
跟随引用组件的命名以module.less结尾。同一个less被多个文件同时引用可以不跟随引用组件命名取统一的文件夹名称为名称或者index但是不允许非本文件夹下的文件跨文件引用less。只有在公共样式表中允许以.less结尾。移动端less文件应以mobile.module.less命名和非移动端less文件区分。
class命名
CSS模块以module.less后缀命名的文件内的className以小驼峰方式命名下划线方式根据实际情况酌情使用。全局样式表直接.less后缀命名的文件内的className以短横线方式命名。参考antd
id命名
暂时不规定id的命名规范自定义即可id不能以数字开头命名数字开头命名的id在querySelector方法中会报错 querySelector method uses CSS3 selectors for querying the DOM and CSS3 doesn’t support ID selectors that start with a digit 避免出现相同的id命名尽量以当前文件组件名开头。全局层面id可以g_开头命名
2.8 Cookie / LocalStorage 数据命名
采用下划线方式命名。项目内部设置的cookie / localStorage应该采用xxx(项目自行规定)_开头。便于数据整合。
2.9 静态文件 / 媒体资源命名
媒体资源采用短横线命名如app-add.jpg。
2.10 项目常用缩写
通用缩写列在此处供参考使用。
3. JavaScript书写
本篇在大部分情况下适用小部分情况下可根据业务情况灵活处理。 大部分情况下Eslint自动格式化可以帮我们检索出格式错误和警告不推荐使用的方法使我们无需关注自己的代码格式是否符合项目规则又是否与其他人的格式相同因此JS书写中我们可以更关注一些功能、逻辑上的规范。
3.1 常见eslint中的javascript规范
3.1.1 eslint: indent
项目缩进空格一致本项目以2个空格为缩进。
3.1.2 eslint: no-undef
使用未声明的变量。
3.1.3 eslint: no-var
不使用var声明变量使用const和let避免污染全局作用域。 第三方SDK的var声明可忽略。
3.1.4 eslint: no-console
禁止console代码。
3.1.5 eslint: no-loop-func
不要在非函数代码块if , while 等中声明函数
3.1.6 eslint: eqeqeq
使用 和 ! 而非 和 !条件声明例如 if 会用 ToBoolean 这个抽象方法将表达式转成布尔值并遵循如下规则 Objects 等于 trueUndefined 等于 falseNull 等于 falseBooleans 等于 布尔值Numbers 在 0, -0, 或者 NaN 的情况下等于 false, 其他情况是 trueStrings 为 ‘’ 时等于 false, 否则是 true
3.1.7 eslint: no-const-assign
禁止对const常量重新赋值。
3.1.8 eslint: no-unreachable
禁止在return、throw、continue和break语句后出现不可达代码。
3.1.9 eslint: prefer-spread
建议使用扩展运算符…而不是.apply()来调用可变参数的函数。
3.1.10 eslint: prefer-arrow-callback
建议在回调函数中使用箭头函数而不是匿名函数以避免this指向问题。
3.2 函数方法
参数传递
最好存在默认参数。在函数体内对传入的参数进行校验确保参数的类型和有效性。引用传值的参数在其他地方使用的情况下不应直接改变参数的值。方法参数多于3个以上应该采用对象方式传递而非继续在原有基础上增加实参有利于复用方法。
避免全局变量
禁止在函数体内声明全局变量全局变量应该在引用数据整体的作用域中声明。可以使用模块化的方式来组织代码减少全局污染。
单一职责
函数应该专注于完成一个单一的任务保持函数的简洁和可维护性。
错误处理
在函数内部进行适当的错误处理包括使用try-catch块来捕获异常。详见下方的错误处理模块。
不在循环体中声明函数避免反复的销毁和创建或者其他的变量问题。避免回调地狱
当涉及异步操作时尽量使用Promise、async/await或者其他异步编程方式避免回调地狱保持代码的可读性和维护性。
方法块之间应空行。
3.3 变量创建
尽量使用const / let变量声明位置警惕变量提升带来的使用问题不应在声明之前使用变量。不创建同名变量代码中存在同名变量会导致变量覆盖和意外行为。初始化赋值声明变量时应尽量立即进行初始化避免声明后未赋初值的情况。
在组件、方法内创建变量应该赋予初始值如果没有赋予初始值应该在使用前做非空判断。初始值赋予需要正确提前判断数据类型比如一个string类型的字段不能赋予对象初始值。否则后续使用类型方法时如果初始值类型错误可能会报错。 常见数据类型和对应初始赋值 string:‘’,number:0,object:{},array:[],boolean:false,
3.4 非空处理
对后端回传的字段都需要做非空处理不能直接信任只要是复杂类型的数据object / array使用时都需要加一层非空判断。 不仅是在数据处理中在组件使用数据中也应该如此。
❌
request().then(res{res?.data?.list.forEach((d){d.detailDTO.forEach((t){t.namet.tempName;})})
})☑️ 使用?. 和 if非空判断 并且 如果有需要判断为空后做数据初始化兜底。
request().then((res{}){res?.data?.list?.forEach((d{}){d?.detailDTO?.forEach((t){if(t){t.namet.tempName || ;}})})
})非空处理中替代的初始化参数一定要类型正确因为后续组件使用时可能会使用类型方法。代码书写中也要注意因为逻辑疏漏 / 场景缺失造成的非空判断的情况。 这一部分只能靠自身梳理所以更应该注意。
方法处理的返回值是否被其他地方使用方法中是否返回空 / 非空两种情况使用时需要对两种情况处理。场景缺失造成的非空判断情况更容易疏漏。
❌
function func(item){if(!item) return;return item.test?.slice();
}
const A func(item);
A.forEach(...)☑️ 实际上虽然!item的情况可能永远不会出现但是谁也说不准呀因此返回值A可能存在 为空 的情况。
function func(item){if(!item) return;return item.test?.slice();
}
const A func(item);
A?.forEach(...)3.5 错误处理
错误处理捕获需要统一上报至arms整合分析。 错误描述需清晰具体数据能上报多少上报多少。
同步错误使用try catch处理不强制使用但是如果代码存在以下情况建议try catch。
方法中行数超过50行。方法结果被引用于html render中当render引用的方法报错错误将直接导致页面崩溃。复杂数据处理多项数据处理被复用于多个页面多个组件中。
异步错误使用Promise.catch捕获。
异步错误中的同步内容仍然可以使用try catch处理。链式调用中错误被catch后使用throw可以将错误移交下一个catch处理但是如果不抛出下一个then会正常执行因此如果不在上一个链式调用中捕获和处理错误整个链式处理逻辑都会错误。 因此最好避免长链式调用写法如果存在此种情况 a. 最好在每一段同步代码中间将错误捕获这样不会走到catch。 b. 中间的catch只抛出只有最后的catch才处理问题。
new Promise((){throw Error(test)})
.catch((){console.log(catch)})
.then(()console.log(test))// log: catch test
// 即使在上一个catch中做了处理仍然会进行then只有外层存在catch处理才能Throw Error。代码中常见错误可能出现的原因需注意
数据的非空判断。商品 / 套餐缺货下架等情况是否处理。数据类型判断错误使用不存在的方法。JSON.parse JSON.stringify
3.6 注释规范
常见注释写法根据需要注释的内容自行使用具有可读性即可。
注释中的首行应该写明此组件 / 功能 / 方法的功能作用。次行根据组件向外暴露的props书写可以使用的字段字段类型是否有默认值。最后可以书写备注比如此模块用到的第三方服务的文档、说明、模块中可能需要注意的特殊逻辑等。如果代码的命名清晰结构简单易读易懂可以省略注释保留代码整体阅读的流畅性。
// 行注释/**
* 块注释
* param列举函数所使用的参数。其中将参数类型用大括号括起来并在其后注释参数名及描述。
* return类似于param这里用于描述返回值的并且该方法没有名称。
*//**
* 块注释
*
* 分割重点说明
*
*/
- 组件注释中 为了方便vscode的自动查询组件注释应该写在function / class上而非写在文件顶端。
❌
/**
* 注释
*/
import XXX
fuction Index(){}☑️
import XXX
/**
* 注释
*/
fuction Index(){}4. 图片 / 视频
图片视频懒加载
lazyloaddata-src配合监听器使用。
图片、视频需提前定义好宽高避免页面重排重绘 CLS指标变动。
图片
图片大小
PC端图片大小尽量不超过400kb。移动端图片大小尽量不超过200kb。常用压缩网站 https://squoosh.app/ TinyPNG – Compress WebP, PNG and JPEG images intelligently https://imagecompressor.com/
图片格式
图像颜色丰富而且图片文件不太大的40KB 以下或有半透明效果的优先考虑 PNG24 格式图像颜色丰富而且文件比较大的优先考虑 JPEG 格式。条件允许的优先考虑 WebP 代替 PNG 和 JPEG 格式。
视频
视频大小
常见的前端视频压缩码率为5M性能和表现之间比较平衡 以此基础上参考10s的视频大小大概为6~7M因此页面上的视频大小最好都在10M左右。压缩方式可以自行使用ffmpeg进行压缩。
5. CSS规范
5.1 避免过于深层的嵌套
CSS选择器的性能取决于浏览器的实现方式以及页面中的元素数量和嵌套层次。 选择器的性能通常受以下几个因素影响
选择器的复杂性嵌套层次和选择器的组合越复杂性能越差。页面中的元素数量如果页面中的元素数量较多选择器的性能可能受到影响。浏览器的渲染引擎不同浏览器对选择器的解析和匹配方式有所不同。 因此正确的选择器优化策略是专注于避免过度复杂的选择器减少嵌套层次。
5.2 CSS样式尽量少使用范围广的匹配标签降低CSS查询的成本。
尽量不使用 * 通配符选择器。使用标签选择器前考虑使用 , , ~ 缩小选择范围。 样式系统通过从最右侧的选择器开始从右到左遍历规则的选择器。只要生成的css树仍然是正确的样式系统就会继续向左移动直到它匹配规则或因不匹配而中止。 这就阐明了我们应该将优化工作重点放在哪里在右侧选择器上也称为关键选择器。 下面是一个更昂贵的选择器示例A.class0007 * {}。尽管这个选择器可能看起来更简单但浏览器的匹配成本更高。因为浏览器从右到左移动它首先检查与关键选择器“*”匹配的所有元素。这意味着浏览器必须尝试针对页面中的所有元素匹配此选择器。有兴趣的 开发可以尝试 使用此通用选择器的测试页面的加载时间与先前的后代选择器测试页面之间的差异。 很明显关键选择器匹配许多元素的CSS选择器可能会明显减慢网页。其他一些关键选择器可能为浏览器创建大量工作的CSS选择器示例包括 A.class0007 div {} #id0007 a {} .class0007 [href] {} div:first-child {} 并非所有的CSS选择器都会影响性能关键是关注 右侧 关键选择器匹配范围广泛的CSS选择器。这在Web 2.0应用程序中尤为重要因为DOM元素、CSS规则和页面重绘的数量更高。 ❌
.classA *{}
.classA div span{}☑️
.classA div span{}5.3 避免!important
滥用important不利于维护和可能造成样式错误覆盖的情况如果可以的情况下请使用层级叠加的方式来控制css的优先级。 目前适用场景
需要覆盖第三方输入的情况下使用important如antd、富文本编辑。
5.4 复杂的CSS样式需要书写注释
大部分情况下我们更关注JS代码中的注释但是CSS文件中也存在一些需要书写注释的地方比如公用样式、复杂的CSS组合LESS函数样式等CSS中书写注释有利于划分CSS的层级组合、样式嵌套、维护复用等。
5.5 移动端样式书写
常见移动端自适应方案包括路由选择、百分比、rem、媒体查询。媒体查询中的移动端样式最好整合放到一起而非分散在每个CSS样式中。有利于整体查看移动端样式情况和降低移动端样式维护时的反复翻阅文件。
5.6 注意z-index的层级
项目中的z-index书写不能太随心所欲本文规范中以使用的公共组件antd中规定弹窗的z-index:1002为准如果当前组件层级需要高于页面中的弹窗展示则可以取大于1002的值否则应取值小于1002。
5.7 在模块化CSS文件中书写样式
上文样式文件命名中已经提及模块化CSS和公共CSS文件的区别为了避免全局污染书写样式应该使用.module.less结尾。
5.8 CSS引入url
外部的url引入应该使用双引号扩起来不能直接使用链接避免url中原本存在引号导致引入失败。 backgroundImage:url(xxxx)小心外部引入的CSS资源下载失败产生阻塞页面渲染的风险。
5.9 样式兼容性
浏览器兼容 目前常见浏览器如下使用前应该在 https://caniuse.com/ 中查询兼容性。
ChromeSafarifirefox
样式兼容 此处例举几个出现过的样式问题使用前应注意
position: fixed 不能与 transition 一起使用出现渲染层级问题。IOS11低版本safari不能使用overflow衍生属性overflow-x ,-y会出现overflow的效果。
6. React最佳实践
6.1 多用 Function Component
如果组件是纯展示型的不需要维护 state 和生命周期则优先使用 Function Component。它有如下好处
代码更简洁一看就知道是纯展示型的没有复杂的业务逻辑更好的复用性。只要传入相同结构的 props就能展示相同的界面不需要考虑副作用。更小的打包体积更高的执行效率
// 一个典型的 Function Component 是下面这个样子
function MenuItem({menuId, menuText, onClick, activeId}) {return (divmenuId{menuId}className{${style} ${activeId menuId ? active : }}onClick{onItemClick}{menuText}/div);
};6.2 多用 PureComponent React.memo
如果组件需要维护 state 或使用生命周期方法则优先使用 PureComponent而不是 Component。Component 的默认行为是不论 state 和 props 是否有变化都触发 render。而 PureComponent 会先对 state 和 props 进行浅比较不同的时候才会 render。 参考原因链接https://zhuanlan.zhihu.com/p/94618828?utm_id0 写在使用前 只建议纯组件使用非纯组件使用前需要仔细考虑 当前组件使用到的外部props是否是复杂类型 是复杂类型对象的情况下每次props变化是否变化引用地址 如果是对象变化时没有地址变动它的变化可能会被pureComponents过滤掉导致组件无法render。 除了外部的propsPureComponent还会比较内部的State如果内部的State是引用类型修改时没有改state的地址也不会触发更新。 6.3 警惕外部传入的function成为重渲染刺客
外部传入的props数据中包含function参数的应该考虑function的传入方式否则每次组件变动、上层组件变动都会新创建一个函数而函数新创建又导致子组件重渲染造成一些性能问题。 class组件中常见的几种function绑定this操作
此绑定行为是在渲染时绑定每次父组件更新会重新创建一个新的函数同时Eslint会发出警告。
class Parent extends Components{func(){}render(){return Child func{this.func.bind(this)} /}
}此绑定行为利用了箭头函数的性质但是在每次父组件更新也会重新创建一个新的函数。
class Parent extends Components{func(){}render(){return Child func{()this.func()} /}
}class类中的函数地址不变作为参数不会重创建推荐此种传入方式。
class Parent extends Components{func (){}render(){return Child func{this.func} /}
}函数式组件中每次组件的刷新都会重新生成函数上下文和内部函数 / 变量也因此将函数作为参数传入子组件时很容易导致不必要的重渲染。此时就要考虑使用useCallback将函数包装一下只有在它本身需要变化时才更新函数。
function Parent(){const func useCallback((){},[test])render(){return Child func{func} /}
}6.4 Hooks
注意Hook的依赖项是否及时更新函数上下文的变量信息注意Eslint的hook提示。Hook中使用防抖、节流方法时应使用ahook中的方法避免因为函数重新创建平常使用的防抖、节流方法不生效。使用useMemo、useCallback性能优化。
7. Next最佳实践
7.1 dynamic动态加载组件
import dynamic from next/dynamic
合理代码分割有利于加快请求速度并可以配合懒加载方案使用使资源加载时机得到优化。动态加载时可以指定是否服务端渲染有利于首屏渲染。
7.2 Next/Image优化图片资源
next/image可帮助我们对图片进行压缩尺寸 or 质量且支持图片懒加载等可以支持服务端图片加载、预加载等机制。
需要固定宽高会自动对图片资源进行压缩处理对图片清晰度有要求的情况最好不使用。
7.3 next/link 预加载
import Link from next/link 可以用于页面上的固定文字超链接、导航等。基于 hover 识别用户意图当用户 hover 到 Link 标签时对即将跳转的页面资源进行预加载进一步防止页面卡顿。
8. 兼容性
8.1 兼容性查找网站https://caniuse.com/
在兼容性测试中达到95%以上的兼容性为佳。如果低于95%兼容性需要做不兼容的预案考虑可以接受不兼容的情况再进行使用。
8.2 兼容性考虑的测试环境
一般来说测试常见浏览器为电脑端谷歌、safari移动端谷歌、safari测试设备以已有设备为主。
8.3 一些已经发现的兼容性问题
IOS safari需要额外测试safari中已经被发现的一些兼容性错误
复杂正则在safari中无法识别可能会造成渲染阻塞。低版本safari中overflow扩展属性overflow-yoverflow-x不生效会被识别为整体的overflow属性。
二、安全规范
1. 注入性攻击
向外部暴露的文本填空框输入的字段不能在项目里使用html方式解析避免外部输入脚本攻击被前端解析出来之后造成XSS攻击。需要的情况下对外部输入文本框中的文字进行尖括号转码破坏脚本注入。
2. CSXF攻击
// 懒得写了
三、性能考虑
1. 代码分割
非首屏展示的组件可以考虑做代码分割Next/dynamic代码分割配合组件懒加载可以达到展示页面上才加载的效果。