這篇文章沒有對錯之分,確定也有不完善的地方,結合了本身平常開發和經驗。可讓你書寫代碼更具嚴謹性,但願看完以後有所幫助。本文字數4000+ ,看完本文大概需半小時。javascript
/** * @description xxxxxx * @author chengfeng * @since 19/05/21 */
複製代碼
/** * 拷貝數據 * @param {*} data 要拷貝的源數據 * @param {boolean} [isDeep=false] 是否深拷貝,默認淺拷貝 * @return {*} 返回拷貝後的數據 */
複製代碼
/*業務代碼註釋*/
複製代碼
interface IState {
// 名字
name: string;
// 電話
phone: number;
// 地址
address: string;
}
複製代碼
import * as React from 'react';
import { Dropdown, Menu, Icon } from 'antd';
import Header from './Header';
import toast from 'common/toast';
import './index.less';
複製代碼
const handleCheck = () => {
onCancel && onCancel();
onClose && onClose();
};
複製代碼
下列關鍵字後必須有大括號(即便代碼塊的內容只有一行):if, else, for, while, do, switch, try, catch, finally, with。css
// not good
if (condition) doSomething();
// good
if (condition) {
doSomething();
}
複製代碼
// bad
++ x;
y ++;
z = x?1:2;
// good
++x;
y++;
z = x ? 1 : 2;
複製代碼
// bad
if (condition){
}
while (condition){
}
function funcName(){
}
// good
if (condition) {
}
while (condition) {
}
function funcName() {
}
複製代碼
// bad
if(condition) {
}
while(condition) {
}
(function() {
})();
// good
if (condition) {
}
while (condition) {
}
(function () {
})();
複製代碼
// bad
var obj = {
a : 1,
b:2,
c :3
};
// good
var obj = {
a: 1,
b: 2,
c: 3
};
複製代碼
// bad
var obj = {
a: 1
, b: 2
, c: 3,
};
function test()
{
...
}
for (const key in object)
{
if (object.hasOwnProperty(key)) {
const element = object[key];
}
}
// good
var obj = {
a: 1,
b: 2,
c: 3,
};
function test() {
...
}
for (const key in object) {
if (object.hasOwnProperty(key)) {
const element = object[key];
}
}
複製代碼
// bad
if (condition) {
...
}
else {
...
}
try {
...
}
catch (e) {
...
}
finally {
...
}
// good
if (condition) {
...
} else {
...
}
try {
...
} catch (e) {
...
} finally {
...
}
複製代碼
// bad
const a = {
'b': 1
};
const a = {b: 1};
const a = {
b: 1,
c: 2
};
const arr = [1, 2, 3, 4,];
// good
const a = {
b: 1,
c: 2,
};
const arr = [1, 2, 3, 4];
複製代碼
類名: 大駝峯式風格,字母和數字,例如:AbcTest。禁止漢字、特殊符號,禁止非大駝峯式風格。html
函數名: 小駝峯式風格,字母和數字,例如:abcTest。禁止漢字、特殊符號,禁止非小駝峯式風格,例如snake_case等。前端
變量名: 同函數名。java
常量: 全大寫風格,大寫字母、數字和下劃線,單詞之間如下劃線分隔,例如:ABC_TEST。禁止漢字、特殊符號、小寫字母。react
使用 onXxx 形式做爲 props 中用於回調的屬性名稱。git
interface IProps {
onClose?: () => void;
onOk?: (item: Record<string, any>) => void;
}
複製代碼
interface IProps {}
interface IState {}
複製代碼
// bad
function 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 ((<string>something).length) {
return (<string>something).length;
} else {
return something.toString().length;
}
}
// good
function getLength(something: string | number): number {
if (typeof something === 'string') {
return something.length;
} else {
return something.toString().length;
}
}
複製代碼
平常用到比較多的是四種,只讀參數放第一位,必選參數第二位,可選參數次之,不肯定參數放最後es6
interface iProps {
readonly x: number;
readonly y: number;
name: string;
age: number;
height?: number;
[propName: string]: any;
}
複製代碼
用於定義一個javascript的對象,key是字符串,value是任意類型
const people:Record<string,any> = {
name: 'chengfeng',
age: 10
}
複製代碼
interface iPeople {
title: string;
name: string;
}
const people: Partial<iPeople> = {
title: 'Delete inactive users',
};
定義的結構能夠是接口iPeople的任意key
複製代碼
interface iPeople {
title: string;
name: string;
}
const people: Readonly<Todo> = {
title: 'todo list',
name: chenfeng;
};
title name屬性就是隻讀的了
複製代碼
interface iPeople {
title?: string;
name?: string;
}
const people1: Props = { title: 'ts' }; // OK
const people22: Required<iPeople> = { title: 'ts' }; // Error: property 'name' missing
複製代碼
查看更多github
interface iPeople {
name: string;
age: number
}
type T = keyof iPeople // -> "name" | "age"
複製代碼
type Keys = "a" | "b"
type Obj = {
[p in Keys]: any
} // -> { a: any, b: any }
複製代碼
// bad
constructor (){
this.setState({ people: this.props.people })
}
// good
state: IState = {
people: {},
};
複製代碼
// bad
render(){
{name}
}
// good
render(){
{!!name || '--'}
}
複製代碼
// bad
const { list, totalCount } = await getPeopleList(keyword, page, pageSize);
list 多是null或者undefined
list.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,
});
複製代碼
例如一些地方,不肯定這個變量裏面到底有什麼,但本身以爲有,就瘋狂的...,最明顯的就是後端返回了一個對象給你,前端拿到以後判斷都不判斷直接data.dataList.forEach()web
// bad
const data = await getPeopleList(keyword, page, pageSize);
data.dataList.forEach() // 直接掛了
// good
const data = await getPeopleList(keyword, page, pageSize);
if (data && data.dataList && Array.isArray(data.dataList) {
data.dataList.forEach()
}
複製代碼
let maxPrice = +form.maxPrice.value;
let maxPrice = Number(form.maxPrice.value);
複製代碼
let mobile = !!ua.match(/iPhone|iPad|Android|iPod|Windows Phone/);
複製代碼
js 中如下爲假,其餘狀況爲真
// bad
class Listing extends React.Component {
render() {
return <div>{this.props.hello}</div>;
}
}
// good
function Listing({ hello }) {
return <div>{hello}</div>;
}
複製代碼
// bad
this.props.app.openid;
this.state.time
// good
const { app } = this.props;
const { time } = this.state;
console.log(app.openid)
複製代碼
// bad
let searchContent = form.search.value;
// good
let searchContent = form.search.value.trim();
複製代碼
// bad
window.location.href = redirectUrl + '?a=10&b=20';
// good
window.location.href = redirectUrl + encodeURIComponent('?a=10&b=20');
複製代碼
// bad
import { withRouter, RouteComponentProps } from 'react-router-dom';
export interface IProps extends RouteComponentProps<any> {}
class App extends React.Component<IProps, AppStates> {}
export default withRouter(App);
// good
import { withRouter, RouteComponentProps } from 'react-router-dom';
class App extends React.Component<IProps & RouteComponentProps<{}>, AppStates> {}
export default withRouter(App);
複製代碼
// 目前
|- api
|- pageA.ts
|- pageB.ts
// 建議
|- api
|- pageA
|- index.js
|- aaa.js
|- bbb.js
|- pageB
|- index.js
|- aaa.js
|- bbb.js
|- ccc.js
複製代碼
function parse (str:string){
if (typeof(str) === 'string' ) {
}
}
複製代碼
getStudentList = async () => {
try {
this.setState({
loading: true,
isEmpty: false
});
await getStudentList({});
this.setState({
loading: false,
isEmpty: true
});
} catch (e) {
// TODO
console.log(e)
} finally {
// 失敗以後的一些兜底操做
this.setState({
loading: false,
isEmpty: true
});
}
};
複製代碼
// 對象
this.setState({
})
// 函數,通常是用於在setState以前作一些操做
this.setState(
() => {
// TODO
console.log('')
return {
a:300
}
}
)
// 第二個參數,通常是用於在setState以後作一些操做
this.setState({
a:300
}, () => {
// TODO
})
複製代碼
// bad
func = async (name, value, status) => {
await this.setState({
name
});
// TODO
};
// good
func = (name, value, status) => {
this.setState(
{
name
},
() => {
// TODO
}
);
};
複製代碼
//bad
this.state.dataAry.map((item, index) => {
return <span key={index} />;
});
//good
this.state.dataAry.map(item => <span key={item.id} />);
複製代碼
//bad
const arr = [];
const key = '';
for (key in obj) {
arr.push(obj[key]);
}
//good
const arr = [];
const key = '';
for (key in obj) {
if (obj.hasOwnProperty(key)) {
arr.push(obj[key]);
}
}
複製代碼
/* * Echart 用於代繪製圖表,但當其自身發生錯誤時,可能影響到業務代碼的執行 */
// bad
const iniDom = document.getElementById('init-container');
const echartObj = echarts.init(iniDom);
this.setState(
{
echartObj
},
() => {
const { echartObj } = this.state;
// 更新圖表
echartObj.setOption(CHART_CONFIG, true);
}
);
// good
try {
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
}
複製代碼
import { html2text } from 'xss';
render(){
<div
dangerouslySetInnerHTML={{
__html: html2text(htmlContent)
}}
/>
}
複製代碼
class MyComponent extends React.Component<iProps, iState> {
constructor(props) {
super(props);
this.inputRef = React.createRef();
}
render() {
return <input type="text" ref={this.inputRef} />; } componentDidMount() { this.inputRef.current.focus(); } } 複製代碼
// bad
if (type !== 0) {
// TODO
}
// good
const STATUS: Record<string, any> = {
READY: 0,
FETCHING: 1,
FAILED: 2
};
if (type === STATUS.READY) {
// TODO
}
// best
enum STATUS {
// 就緒
READY = 0,
// 請求中
FETCHING = 1,
// 請求失敗
FAILED = 2,
}
複製代碼
import { Component, PureComponent } from 'react';
// bad
class Message extends Component {
render() {
return <span>{this.state.message}</span>;
}
}
// good
class Message extends PureComponent {
render() {
return <span>{this.state.message}</span>;
}
}
複製代碼
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;
}
複製代碼
不少小夥伴用了好久的ts,都不知道經常使用 Event 事件對象類型:
ClipboardEvent<T = Element> 剪貼板事件對象
DragEvent<T = Element> 拖拽事件對象
ChangeEvent<T = Element> Change 事件對象
KeyboardEvent<T = Element> 鍵盤事件對象
MouseEvent<T = Element> 鼠標事件對象
TouchEvent<T = Element> 觸摸事件對象
WheelEvent<T = Element> 滾輪事件對象
AnimationEvent<T = Element> 動畫事件對象
TransitionEvent<T = Element> 過渡事件對象
import { MouseEvent } from 'react';
interface IProps {
onClick(event: MouseEvent<HTMLDivElement>): void;
}
複製代碼
對於一些不須要控制ui的狀態屬性,咱們能夠直接綁到this上, 即私有屬性,沒有必要弄到this.state上,否則會觸發渲染機制,形成性能浪費 例如請求翻頁數據的時候,咱們都會有個變量。
// bad
state: IState = {
pageNo:1,
pageSize:10
};
// good
queryParams:Record<string,any> = {
pageNo:1,
pageSize:10
}
複製代碼
總結四句話。咱們在寫組件或者函數的的時候,工具函數和業務邏輯抽離,表單校驗和業務抽離、事件函數和業務抽離,ajax和業務抽離。 例若有些頁面是經過location.href跳轉的,咱們有些業務邏輯等都是放到didmountMount,可是後期改需求,可能要用react-router進行跳轉,可能要改的邏輯就會不少了,因此函數抽離出來,需求更新就少改一點代碼。 若是還不肯定如何劃分函數的細粒度,我有個建議。使用過兩次以上的代碼,要抽離組件或者函數,兩次的能夠不用
我的以爲if else 嵌套深看起來也不會太難受,難受的是,項目迭代久以後,本身都忘記曾經寫過這些代碼,並且類型多或者不肯定有什麼類型,是否後期還會加的狀況下,改起來就很是複雜了,並且很容易踩坑和背鍋。 用配置取代if嵌套,大概就是抽離一個config.ts出來,裏面放一些配置。
例如你的業務代碼裏面,會根據不一樣url參數,代碼會執行不一樣的邏輯.
/info?type=wechat&uid=123456&
const 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.t
const urlTypeConfig: Record<string, typeItem> = {
'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.type
urlTypeConfig.forEach(item => {
if(urlType === item.type) {
doSomeThing(item.show)
}
})
複製代碼
發現團隊一些小夥伴爲了減小render函數裏面的代碼量,會把一些元素拆分到函數裏面。
// bad
renderHeader = () => {
return (<div />)
}
renderBody = () => {
return (<div />)
}
renderFooter = () => {
return (<div />)
}
render(){
return(
<div>
renderHeader()
renderBody()
renderFooter()
</div>
)
}
複製代碼
更好的辦法,是用函數式組件取代在當前組件裏面寫方法
// good
function RenderHeader(props) = {
return (<div />)
}
function RenderBody(props) = {
return (<div />)
}
function RenderFooter(props) = {
return (<div />)
}
class Component extends React.Component<iProps, iState>{
render () {
return(
<div>
<RenderHeader />
<RenderBody />
<RenderFooter />
</div>
)
}
}
複製代碼
使用a標籤打開一個新窗口過程當中的安全問題。新頁面中可使用window.opener來控制原始頁面。若是新老頁面同域,那麼在新頁面中能夠任意操做原始頁面。若是是不一樣域,新頁面中依然能夠經過window.opener.location,訪問到原始頁面的location對象
在帶有target="_blank"的a標籤中,加上rel="noopener"屬性。若是使用window.open的方式打開頁面,將opener對象置爲空。
var newWindow = window.open();
newWindow.opener = null;
複製代碼
clearSessioin = () => {
req.session.userName = undefined;
req.session.userName = void 0
}
複製代碼
在作一些先後端鑑權的時候,後端應該開啓domain,secure,httponly嚴格模式,禁止前端操做cookie,防止csrf攻擊。
咱們可使用構建工具繼承 husky eslint tslint lint-stage prettier來規範代碼。