中国flash网站模板中心,利用网站文件下载做推广,外贸谷歌seo,重庆网站建设就选承越这篇文章没有对错之分#xff0c;肯定也有不完善的地方#xff0c;结合了自己日常开发和经验。可以让你书写代码更具严谨性,希望看完之后有所帮助。本文字数4000 #xff0c;看完本文大概需半小时。1. 注释(1) 文件顶部的注释#xff0c;包括描述、作者、日期/** * descrip…这篇文章没有对错之分肯定也有不完善的地方结合了自己日常开发和经验。可以让你书写代码更具严谨性,希望看完之后有所帮助。本文字数4000 看完本文大概需半小时。1. 注释(1) 文件顶部的注释包括描述、作者、日期/** * description xxxxxx * author chengfeng * since 19/05/21 */复制代码(2) 模块的注释/** * 拷贝数据 * param {*} data 要拷贝的源数据 * param {boolean} [isDeepfalse] 是否深拷贝默认浅拷贝 * return {*} 返回拷贝后的数据 */复制代码(3) 业务代码注释/*业务代码注释*/复制代码(4) 变量注释interface IState { // 名字 name: string; // 电话 phone: number; // 地址 address: string;}复制代码2. 引用组件顺序先引用外部组件库,,再引用当前组件块级组件, 然后是 common 里的公共函数库最后是 css 样式import * as React from react;import { Dropdown, Menu, Icon } from antd;import Header from ./Header;import toast from common/toast;import ./index.less;复制代码3. 引号使用单引号,或者 es6 的反引号4. 缩进使用两个空格const handleCheck () { onCancel onCancel(); onClose onClose();};复制代码5. 分号除了代码块的以外的每个表达式后必须加分号。6. 括号下列关键字后必须有大括号(即使代码块的内容只有一行)if, else, for, while, do, switch, try, catch, finally, with。// not goodif (condition) doSomething();// goodif (condition) { doSomething();}复制代码7. 空格二元和三元运算符两侧必须有一个空格一元运算符与操作对象之间不允许有空格。// bad x;y ;z x?1:2;// goodx;y;z x ? 1 : 2;复制代码用作代码块起始的左花括号 { 前必须有一个空格。// badif (condition){}while (condition){}function funcName(){}// goodif (condition) {}while (condition) {}function funcName() {}复制代码if / else / for / while / function / switch / do / try / catch / finally 关键字后必须有一个空格。// badif(condition) {}while(condition) {}(function() {})();// goodif (condition) {}while (condition) {}(function () {})();复制代码在对象创建时属性中的 : 之后必须有空格: 之前不允许有空格。// badvar obj { a : 1, b:2, c :3};// goodvar obj { a: 1, b: 2, c: 3};复制代码8. 换行每个独立语句结束后必须换行。在函数声明、函数表达式、函数调用、对象创建、数组创建、for 语句等场景中不允许在 , 或 ; 前换行// badvar obj { a: 1 , b: 2 , c: 3,};function test(){ ...}for (const key in object) { if (object.hasOwnProperty(key)) { const element object[key]; }}// goodvar obj { a: 1, b: 2, c: 3,};function test() { ...}for (const key in object) { if (object.hasOwnProperty(key)) { const element object[key]; }}复制代码下列关键字后else, catch, finally 不需要换行// badif (condition) { ...}else { ...}try { ...}catch (e) { ...}finally { ...}// goodif (condition) { ...} else { ...}try { ...} catch (e) { ...} finally { ...}复制代码9. 数组、对象对象属性名不需要加引号对象以缩进的形式书写不要写在一行数组最后不要有逗号。对象最后要有逗号。// badconst a { b: 1};const a {b: 1};const a { b: 1, c: 2};const arr [1, 2, 3, 4,];// goodconst a { b: 1, c: 2,};const arr [1, 2, 3, 4];复制代码10. 命名类名: 大驼峰式风格字母和数字例如AbcTest。禁止汉字、特殊符号禁止非大驼峰式风格。函数名: 小驼峰式风格字母和数字例如abcTest。禁止汉字、特殊符号禁止非小驼峰式风格例如snake_case等。变量名: 同函数名。常量: 全大写风格大写字母、数字和下划线单词之间以下划线分隔例如ABC_TEST。禁止汉字、特殊符号、小写字母。使用 onXxx 形式作为 props 中用于回调的属性名称。interface IProps { onClose?: () void; onOk?: (item: Record) void;}复制代码组件内的事件函数使用 handle 开头尾,handleCheckBtn。使用 withXxx 形式的词作为高阶组件的名称。接口命名前面带上 I 表示 interfaceinterface IProps {}interface IState {}复制代码11. 类型断言// badfunction getLength(something: string | number): number { return something.length;}// index.ts(2,22): error TS2339: Property length does not exist on type string | number.// Property length does not exist on type number.// bad function getLength(something: string | number): number { if ((something).length) { return (something).length; } else { return something.toString().length; }}// goodfunction getLength(something: string | number): number { if (typeof something string) { return something.length; } else { return something.toString().length; }}复制代码12. interface声明顺序日常用到比较多的是四种只读参数放第一位必选参数第二位可选参数次之不确定参数放最后interface iProps { readonly x: number; readonly y: number; name: string; age: number; height?: number; [propName: string]: any;}复制代码13. ts好用的相关工具泛型Record 用这个来声明对象结构的类型用于定义一个javascript的对象key是字符串value是任意类型const people:Record { name: chengfeng, age: 10}复制代码Partial 作用是将传入的属性变为可选项.interface iPeople { title: string; name: string;}const people: Partial { title: Delete inactive users,};定义的结构可以是接口iPeople的任意key复制代码Readonly 作用是将传入的属性变为变成只读interface iPeople { title: string; name: string;}const people: Readonly { title: todo list, name: chenfeng;};title name属性就是只读的了复制代码Required 的作用是将传入的属性变为必选项interface iPeople { title?: string; name?: string;}const people1: Props { title: ts }; // OKconst people22: Required { title: ts }; // Error: property name missing复制代码查看更多14. ts一些好用的小tipskeyofinterface iPeople { name: string; age: number}type T keyof iPeople // - name | age复制代码intype Keys a | btype Obj { [p in Keys]: any} // - { a: any, b: any }复制代码15. 规范其他不要使用 var 声明变量不会被修改的变量使用 const 声明去除声明但未被引用的代码禁止在代码里使用 debug不允许有空的代码块16. 仅当初始 state 需要从 props 计算得到的时候才将 state 的声明放在构造函数中其它情况下使用静态属性声明 state,并且一般情况下不要将 prop 传给 state// badconstructor (){ this.setState({ people: this.props.people })}// goodstate: IState { people: {},};复制代码17. 渲染默认值添加非空判断可以提高代码的稳健性,例如后端返回的一些值,可能会出现不存在的情况应该要给默认值.// badrender(){ {name}}// goodrender(){ {!!name || --}}复制代码还有一种情况就是本来后端应该返回一个数组给你但是数据库取不到数据可能后端给你返回了null,然后前端null.length。这样就gg了// badconst { list, totalCount } await getPeopleList(keyword, page, pageSize);list 可能是null或者undefinedlist.length将直接导致前端报错this.setState({ status: STATUS.READY, apps: list, total: totalCount, page: page,});// good const { list, totalCount } await getPeopleList(keyword, page, pageSize);this.setState({ status: STATUS.READY, apps: list || [], total: totalCount || 0, page: page,});复制代码18. 不确定的属性最后却疯狂的用...访问不存在的属性例如一些地方不确定这个变量里面到底有什么但自己觉得有就疯狂的...,最明显的就是后端返回了一个对象给你前端拿到之后判断都不判断直接data.dataList.forEach()// badconst data await getPeopleList(keyword, page, pageSize);data.dataList.forEach() // 直接挂了// goodconst data await getPeopleList(keyword, page, pageSize);if (data data.dataList Array.isArray(data.dataList) { data.dataList.forEach() }复制代码19. 数据格式转换把字符串转整型可以使用号let maxPrice form.maxPrice.value;let maxPrice Number(form.maxPrice.value);复制代码转成 boolean 值用!!let mobile !!ua.match(/iPhone|iPad|Android|iPod|Windows Phone/);复制代码20. 判断条件真假js 中以下为假,其他情况为真falsenullundefined0 (空字符串)NaN21. 简单组件可以使用函数代替// badclass Listing extends React.Component { render() { return {this.props.hello}; }}// goodfunction Listing({ hello }) { return {hello};}复制代码22. 对于常用的属性进行缓存// badthis.props.app.openid;this.state.time// goodconst { app } this.props;const { time } this.state;console.log(app.openid)复制代码23. input 输入框使用 trim()// badlet searchContent form.search.value;// goodlet searchContent form.search.value.trim();复制代码24. 使用 location 跳转前需要先转义// badwindow.location.href redirectUrl ?a10b20;// goodwindow.location.href redirectUrl encodeURIComponent(?a10b20);复制代码25. 使用 react-router// badimport { withRouter, RouteComponentProps } from react-router-dom;export interface IProps extends RouteComponentProps {}class App extends React.Component {}export default withRouter(App);// goodimport { withRouter, RouteComponentProps } from react-router-dom;class App extends React.Component, AppStates {}export default withRouter(App);复制代码26. 同时开发数据请求 api 目录 git 冲突目录方案在 api 目录下新建一个目录目录对应一级 tab,这个目录内放置一个 index.js 最后把二级 tab 组件所使用的 api 请求都在这个 index.js 内引入。// 目前|- api |- pageA.ts |- pageB.ts// 建议|- api |- pageA |- index.js |- aaa.js |- bbb.js |- pageB |- index.js |- aaa.js |- bbb.js |- ccc.js复制代码27. 组件嵌套过深组件一般不要超过三层,最多四层,层级过深可能会导致数据传递过深在做一些颗粒度比较细的操作的时候处理起来较为繁琐可以使用 redux 等状态管理工具替代。28. 代码过滤掉你没考虑到的情况例如一个函数你只想操作字符串那你必须在函数开头就只允许参数是字符串function parse (str:string){ if (typeof(str) string ) { }}复制代码29. 业务代码里面的异步请求需要 try catchajax 请求使用 try catch错误提示后端返回,并且做一些失败后的状态操作例如进入列表页我们需要一个 loading 状态然后去请求数据,可是失败之后也需要把 loading 状态去掉,把 loading 隐藏的代码就写在 finally 里面。getStudentList async () { try { this.setState({ loading: true, isEmpty: false }); await getStudentList({}); } catch (e) { // TODO console.log(e) } finally { // 失败之后的一些兜底操作 this.setState({ loading: false, isEmpty: true }); }};复制代码30. setState有三种用法// 对象this.setState({})// 函数一般是用于在setState之前做一些操作this.setState( () { // TODO console.log() return { a:300 } })// 第二个参数一般是用于在setState之后做一些操作this.setState({ a:300}, () { // TODO})复制代码31. setState可能是同步的setState 在react里的合成事件和钩子函数中是“异步”的。setState 在原生事件和 setTimeout 中是同步的。32. 不要在 setState 前面加 awaitsetState 前面也是可以带 await 的会变成同步设置状态,但这是一种巧合不确定未来哪个版本就不支持了为了遵循 react 框架的设计原则我们使用回掉函数的形式。// badfunc async (name, value, status) { await this.setState({ name }); // TODO};// goodfunc (name, value, status) { this.setState( { name }, () { // TODO } );};复制代码33. 阻止事件默认行为在 React 中你不能通过返回 false 来阻止默认行为。必须明确调用 preventDefault 。34. 在 componentWillUnmount 里面去除副作用的函数清除 EventListener中止数据请求清除定时器35. key对于组件中的 key 优化起到最大化重用 dom//badthis.state.dataAry.map((item, index) { return ;});//goodthis.state.dataAry.map(item );复制代码36. for-in 中一定要有 hasOwnProperty 的判断(即禁止直接读取原型对象的属性)//badconst arr [];const key ;for (key in obj) { arr.push(obj[key]);}//goodconst arr [];const key ;for (key in obj) { if (obj.hasOwnProperty(key)) { arr.push(obj[key]); }}复制代码37. 第三方库函数的使用用 try catch 包裹防止第三方库的出现错误导致整个程序崩溃/* * Echart 用于代绘制图表但当其自身发生错误时可能影响到业务代码的执行 */// badconst iniDom document.getElementById(init-container);const echartObj echarts.init(iniDom);this.setState( { echartObj }, () { const { echartObj } this.state; // 更新图表 echartObj.setOption(CHART_CONFIG, true); });// goodtry { const iniDom document.getElementById(init-container); const echartObj echarts.init(iniDom); this.setState( { echartObj }, () { const { echartObj } this.state; // 更新图表 echartObj.setOption(CHART_CONFIG, true); } );} catch (error) { // TODO}复制代码38. 防止 xss 攻击inputtextarea 等标签不要直接把 html 文本直接渲染在页面上,使用 xssb 等过滤之后再输出到标签上;import { html2text } from xss;render(){ 39. 在组件中获取真实 dom使用 16 版本后的 createRef()函数class MyComponent extends React.Component { constructor(props) { super(props); this.inputRef React.createRef(); } render() { return ; } componentDidMount() { this.inputRef.current.focus(); }}复制代码40. 减少魔法数字写代码的时候尽量减少一些未知含义的数字尽量用英文单词。例如type 0的时候做了一些操作让人不知所以然。// badif (type ! 0) { // TODO}// goodconst STATUS: Record { READY: 0, FETCHING: 1, FAILED: 2};if (type STATUS.READY) { // TODO}// bestenum STATUS { // 就绪 READY 0, // 请求中 FETCHING 1, // 请求失败 FAILED 2,}复制代码41. 如果需要优化 react 性能(一般用不到)如果组件的 state 和 props 都是简单类型可以继承 PureComponent 而不是 Componentimport { Component, PureComponent } from react;// badclass Message extends Component { render() { return {this.state.message}; }}// goodclass Message extends PureComponent { render() { return {this.state.message}; }}复制代码重写 shouldComponentUpdate 方法,在 shouldComponentUpdate 里面根据 state,props 是否有改变来判断是否需要重新渲染.如果组件继承了 PureComponent 就没必要再重写 shouldComponentUpdate 方法import { isReactPropsEqual, isReactStateEqual } from fe/common/lib/equal;shouldComponentUpdate(nextProps:IProps, nextState:IState) { if (isReactStateEqual(nextState,this.state) isReactPropsEqual(nextProps,this.props)) { return false; } return true;}复制代码42. Event 事件对象类型很多小伙伴用了很久的ts,都不知道常用 Event 事件对象类型ClipboardEvent 剪贴板事件对象DragEvent 拖拽事件对象ChangeEvent Change 事件对象KeyboardEvent 键盘事件对象MouseEvent 鼠标事件对象TouchEvent 触摸事件对象WheelEvent 滚轮事件对象AnimationEvent 动画事件对象TransitionEvent 过渡事件对象import { MouseEvent } from react;interface IProps { onClick(event: MouseEvent): void;}复制代码43. 使用私有属性取代state状态对于一些不需要控制ui的状态属性我们可以直接绑到this上 即私有属性没有必要弄到this.state上不然会触发渲染机制造成性能浪费 例如请求翻页数据的时候,我们都会有个变量。// badstate: IState { pageNo:1, pageSize:10};// good queryParams:Record { pageNo:1, pageSize:10}复制代码44. 代码细粒度的思考总结四句话。我们在写组件或者函数的的时候工具函数和业务逻辑抽离表单校验和业务抽离、事件函数和业务抽离ajax和业务抽离。 例如有些页面是通过location.href跳转的我们有些业务逻辑等都是放到didmountMount,但是后期改需求可能要用react-router进行跳转可能要改的逻辑就会很多了所以函数抽离出来需求更新就少改一点代码。 如果还不确定如何划分函数的细粒度我有个建议。使用过两次以上的代码要抽离组件或者函数两次的可以不用45. if else 等判断太多了后期难以维护。个人觉得if else 嵌套深看起来也不会太难受难受的是项目迭代久之后自己都忘记曾经写过这些代码而且类型多或者不确定有什么类型是否后期还会加的情况下改起来就非常复杂了而且很容易踩坑和背锅。 用配置取代if嵌套大概就是抽离一个config.ts出来里面放一些配置。例如你的业务代码里面会根据不同url参数代码会执行不同的逻辑./info?typewechatuid123456const qsObj qs(window.location.url)const urlType qsObj.type// bad if (urlType wechat) { doSomeThing()} else if () { doSomeThing()} else if () { doSomeThing()} else if () { doSomeThing()}// good config.tconst urlTypeConfig: Record { wechat: { // key 就是对应的type name: wechat, show: [header, footer, wechat] // 展示什么可能是异步的 pession: [admin], // 权限是什么可能是异步的 }, zhifubao: { // key 就是对应的type name: zhifubao, show: [header, footer, zhifubao] // 展示什么可能是异步的 pession: [admin], // 权限是什么可能是异步的 },}// 业务逻辑const qsObj qs(window.location.url)const urlType qsObj.typeurlTypeConfig.forEach(item { if(urlType item.type) { doSomeThing(item.show) }})复制代码46. 不要使用renderXXX,要使用函数式组件发现团队一些小伙伴为了减少render函数里面的代码量会把一些元素拆分到函数里面。// bad renderHeader () { return ( renderHeader() renderBody() renderFooter() ) }复制代码更好的办法是用函数式组件取代在当前组件里面写方法// good function RenderHeader(props) { return ({ render () { return( 47. a标签安全问题使用a标签打开一个新窗口过程中的安全问题。新页面中可以使用window.opener来控制原始页面。如果新老页面同域那么在新页面中可以任意操作原始页面。如果是不同域新页面中依然可以通过window.opener.location访问到原始页面的location对象在带有target_blank的a标签中加上relnoopener属性。如果使用window.open的方式打开页面将opener对象置为空。var newWindow window.open();newWindow.opener null;复制代码48. void 0 替代undefinedclearSessioin () { req.session.userName undefined; req.session.userName void 0}复制代码49. 前端不要操作cookie在做一些前后端鉴权的时候后端应该开启domain,secure,httponly严格模式禁止前端操作cookie防止csrf攻击。50. 代码检查插件我们可以使用构建工具继承 husky eslint tslint lint-stage prettier来规范代码。eslint-config-prettiereslint-plugin-prettiereslint-plugin-reacttslint-reacttslint-plugin-prettiertslint-config-prettier团队开发工作流参考airbnbimweb代码规范如何无痛降低 if else 面条代码复杂度你真的理解setState吗来源链接https://juejin.im/post/5ce24f8ae51d45106477bd45