共计 16859 个字符,预计需要花费 43 分钟才能阅读完成。
前端周刊发表每周前端技术相干的大事件、文章教程、一些框架的版本更新、以及代码和工具。每周定期发表,欢送大家关注、转载。
<span style=”color:red;”> 欢送关注公众号 前端每周看</span>
工具
vscode
vscode 能够说是前端最风行的编辑器,其有丰盛的插件零碎。不同开发人员对编辑器设置不同,比方缩进是用空格还是 tab, 缩进几个等等。如果多人开发同一个我的项目,必然会引起文件抵触,所以一个团队最好能对立编辑器。
参考:https://editorconfig.org,在我的项目根目录新建.editconfig 文件
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
[*.md]
insert_final_newline = false
trim_trailing_whitespace = false
prettier
代码格式化工具,vscode 有很多格式化插件,像 formate、vetur 等,咱们抉择 prettier 作为团队格式化工具。
1、装置 prettier
yarn add prettier --save-dev
在我的项目根目录新建.prettierrc.js
module.exports = {
// 强制应用单引号
singleQuote: true,
// 字符串应用单引号
singleQuote: true,
// 大括号内的首尾须要空格
bracketSpacing: true,
// 开端不须要逗号
trailingComma: 'none',
// 箭头函数参数括号
arrowParens: 'avoid',
// 在 jsx 中把 '>' 是否独自放一行
jsxBracketSameLine: true,
// 应用默认的折行规范
proseWrap: 'preserve',
// 依据显示款式决定 html 要不要折行
htmlWhitespaceSensitivity: 'css',
// 换行符应用 crlf/lf/auto
endOfLine: 'auto'
};
2、配置 vscode 保留主动格式化,
第一步,关上 vscode 设置,搜寻 format,勾选 OnPaste、OnSave,如下图
第二步,搜寻,defaultformatter,设置默认格式化工具,抉择 Prettier
3、能够在我的项目 package.json 里配置 format 脚本,
"format": "prettier --write --parser typescript \"(src|test)/**/*.ts\""
eslint
eslint 作为代码检测工具,反对 ts、tsx
1、装置 eslint
yarn add eslint --save-dev
2、装置 ts 解析器以及 ts 规定补充
yarn add @typescript-eslint/parser --save-dev
yarn add @typescript-eslint/eslint-plugin --save-dev
eslint 默认应用 Espree 进行解析,无奈辨认 ts 的一些语法,所以须要装置一个 ts 的解析器 @typescript-eslint/parser,用它来代替默认的解析器 @typescript-eslint/eslint-plugin 作为 eslint 默认规定的补充,提供了一些额定的实用于 ts 语法的规定。
3、反对 tsx
yarn add eslint-plugin-react --save-dev
因为是 react 我的项目,所以还须要插件 eslint-plugin-react 来反对 .tsx
4、在我的项目根目录创立 .eslintrc.js
当运行 ESLint 的时候查看一个文件的时候,它会首先尝试读取该文件的目录下的配置文件,而后再一级一级往上查找,将所找到的配置合并起来,作为以后被查看文件的配置。
module.exports = {
parser: '@typescript-eslint/parser',
plugins: [
'react',
'react-hooks',
'@typescript-eslint/eslint-plugin',
'prettier'
],
settings: {
react: {version: 'detect'}
},
rules: {
'prettier/prettier': 'error',
'no-debugger': 'error',
// 勾销函数参数须要从新赋值给另一个变量能力应用
'no-param-reassign': [0],
// 勾销 {a, b, c} 多个变量须要换行
'object-curly-newline': [0],
// 禁用 var,用 let 和 const 代替
'no-var': 2,
// 开启强制单引号
quotes: [2, 'single'],
// 强制全等(=== 和 !==)
eqeqeq: 2,
// 语句强制分号结尾
semi: [2, 'always'],
// 禁止呈现未应用的变量
'@typescript-eslint/no-unused-vars': [2],
// 箭头函数参数括号,一个参数时可省略括号
'arrow-parens': [2, 'as-needed'],
// 箭头函数,箭头前后空格
'arrow-spacing': [2, { before: true, after: true}],
// 禁止对象最初一项逗号
'comma-dangle': [2, 'never'],
// 单行代码 / 字符串最大长度
'max-len': [2, { code: 120}],
// jsx 缩进 2 个空格
'react/jsx-indent': [2, 2],
// 文件开端强制换行
'eol-last': 2,
// react 配置
// 强制组件办法程序
'react/sort-comp': [2],
// 完结标签,组件省略闭合标签,html 不省略闭合标签
'react/self-closing-comp': [2, { component: true, html: false}],
// 查看 Hook 的规定,不容许在 if for 外面应用
'react-hooks/rules-of-hooks': [2],
// 查看 effect 的依赖
'react-hooks/exhaustive-deps': [2]
}
};
git-commit-message
验证 git 提交规定,创立 verify-commit-msg.js 文件
const chalk = require('chalk')
const msgPath = process.env.GIT_PARAMS
const msg = require('fs').readFileSync(msgPath, 'utf-8').trim()
const commitRE =
/^(revert:)?(wip|release|feat|fix|polish|docs|style|refactor|perf|test|workflow|ci|chore|types|build)(\(.+\))?: .{1,50}/
if (!commitRE.test(msg)) {console.log()
console.error(` ${chalk.bgRed.white('ERROR')} ${chalk.red(`invalid commit message format.`)}\n\n` +
chalk.red(` Proper commit message format is required for automated changelog generation. Examples:\n\n`) +
` ${chalk.green(`feat(compiler): add 'comments' option`)}\n` +
` ${chalk.green(`fix(v-model): handle events on blur (close #28)`
)}\n\n` +
chalk.red(` See .github/COMMIT_CONVENTION.md for more details.\n`)
)
process.exit(1)
}
代码提交规定
feat: 新性能
fix: 修复
docs: 文档变更
style: 代码格局(不影响代码运行的变动)
refactor: 重构(既不是减少 feature,也不是修复 bug)
perf: 性能优化
test: 减少测试
chore: 构建过程或辅助工具的变动
revert: 回退
build: 打包
代码
整个团队是用 umi 封装的脚手架,所有我的项目都是 React.js+Mobx+TypeScript,上面列出了根本标准。
React.js
命名
- React 组件文件名应用 PascalCase 命名规定,并且以.tsx 后缀名。例如:AnotherComponent.tsx
- 如果 React 组件是一个单文件,以组件名作为文件名;如果是将 React 组件放在一个目录里,以组件名作为目录名,并且组件所在文件以 index.jsx 命名
src
|-- components
| |-- BadNamedComponent
| |-- BadNamedComponent.jsx
| |-- BadNamedComponent.css
| |-- GoodNamedComponent
| |-- ChildComponent.jsx
| |-- ChildComponent.css
| |-- index.jsx
| |-- index.css
| |-- AnotherComponent.jsx
| |-- AnotherComponent.csssha
// ❌
import BadNamedComponent from '@/components/BadNamedComponent/BadNamedComponent';
// ❌
import GoodNamedComponent from '@/components/GoodNamedComponent/index';
// ✅
import GoodNamedComponent from '@/components/GoodNamedComponent';
// ✅
import AnotherComponent from '@/components/AnotherComponent';
+ React 组件应用 PascalCase 形式命名,React 组件实例应用 camelCase 形式命名
// ❌
import someComponent from './SomeComponent';
// ✅
import SomeComponent from './SomeComponent';
// ❌
const AnotherComponent = <AnotherComponent />;
// ✅
const anotherComponent = <AnotherComponent />;
- 不举荐 应用高阶组件。如果必须应用,以 with 前缀命名高阶组件
// ❌
export default function wrapForm(WrappedComponent) {return function FormWrapper(props) {return <WrappedComponent {...props} {...somePropsFromWrapper} />;
}
}
// ✅
export default function withForm(WrappedComponent) {return function WithForm(props) {return <WrappedComponent {...props} {...somePropsFromWrapper} />;
}
}
- 高阶组件须要增加 displayName 属性不便调试,displayName 属性的格局为:装璜器函数名称加上圆括号 () 包裹的 WrappedComponent 的 displayName 或者 name 属性,如下所示
// ❌
export default function withForm(WrappedComponent) {function WithForm(props) {return <WrappedComponent {...props} {...somePropsFromWrapper} />;
}
return WithForm;
}
// ✅
export default function withForm(WrappedComponent) {function WithForm(props) {return <WrappedComponent {...props} {...somePropsFromWrapper} />;
}
const wrappedComponentName = WrappedComponent.displayName
|| WrappedComponent.name
|| 'Component';
WithForm.displayName = `withForm(${wrappedComponentName})`;
return WithForm;
}
- props 应用 camelCase 形式命名
// ❌
<SomeComponent
SomeProp="value1"
other_prop="value2"
/>
// ✅
<SomeComponent
someProp="value1"
otherProp="value2"
/>
- 不要应用下划线作为变量名的前缀
function SomeComponent() {
// ❌
const \_handleSubmit = useCallback((params) => {submitWith(params);
}, []);
// ✅
const handleSubmit = useCallback((params) => {submitWith(params);
}, []);
return (<Form onSubmit={_handleSubmit} onSubmit2={handleSubmit} />
);
}
括号
- 当 JSX 标签跨多行时,必须应用圆括号 () 包裹
// ❌
function ParentComponent() {
return <div>
<ChildComponent />
</div>;
}
// ✅
function ParentComponent() {
return (
<div>
<ChildComponent />
</div>
);
}
标签
- 没有 children 时必须应用自闭合标签
// ❌
<SomeComponent prop="value"></SomeComponent>
// ✅
<SomeComponent prop="value" />
2.7.5 对齐
+ 多行属性的折行和对齐形式
// ❌
<SomeComponent superLongParam="bar"
anotherSuperLongParam="baz" />
// ✅
<SomeComponent
superLongParam="bar"
anotherSuperLongParam="baz"
/>
// ✅
<ParentComponent
superLongParam="bar"
anotherSuperLongParam="baz"
> <ChildComponent />
> </ParentComponent>
- 条件渲染语句对齐形式
// ❌
{
someCondition
? <ComponentA />
: <ComponentB />
}
// ✅
{someCondition ? (<ComponentA />) : (<ComponentB />)}
引号
- JSX 上的字符串字面量属性应用双引号,其它中央全副应用单引号
function SomeComponent() {
// ❌
const wrongString = "double quotes is wrong";
// ✅
const rightString = 'single quotes is right';
}
// ❌
<SomeComponent someProp='value1' />
// ✅
<SomeComponent someProp="value1" />
// ❌
<SomeComponent style={{fontSize: "12px"}} />
// ✅
<SomeComponent style={{fontSize: '12px'}} />
空格
- 自闭合标签在标签闭合处留一个空格,变量属性的花括号 {} 和变量之间不能呈现空格,非折行对象属性与花括号之间留一个空格
// ❌
<SomeComponent/>
// ❌
<SomeComponent />
// ✅
<SomeComponent />
// ❌
<SomeComponent someProp={someValue} />
// ✅
<SomeComponent someProp={someValue} />
// ❌
<SomeComponent someObjectProp={{prop: value}} />
// ✅
<SomeComponent someObjectProp={{prop: value}} />
款式
- 不举荐 应用内联 style 款式,倡议对立写在独自的 css 文件里或者应用相似 @material-ui/styles 的款式治理计划。如果有必要应用内联款式,倡议将 styles 定义为变量对立治理,在 JSX 中通过变量引入应用
// ❌
<SomeComponent style={{
marginTop: '10px',
fontSize: '12px',
color: '#f00',
}} />
// ✅
const styles = {
someComponent: {
marginTop: '10px',
fontSize: '12px',
color: '#f00',
},
};
<SomeComponent style={styles.someComponent} />
- 款式应用 styled-components 插件包裹
import styled from 'styled-components';
const Wrapper = styled.div`
width: 100%;
`;
const FC: React.FC = () => {return <Wrapper></Wrapper>;};
export default FC;
props
- 值为 true 的 prop 属性只写属性名
// ❌
<SomeComponent visible={true} />
// ✅
<SomeComponent visible />
- 在应用
<img>
标签时,如果不是装饰性 (Decorative Image) 图片必须有 alt 属性,如果是装饰性图片则应该设置 alt=”” 或者 role=”presentation” 属性
// ❌
<img src="logo.png" />
// ✅
<img src="logo.png" alt="wedoctor" />
// ✅
<img src="some-presentational-image.png" alt="" />
// ✅
<img src="some-presentational-image.png" role="presentation" />
- 不要应用数组下标 index 作为 key,从数组项上摘取一个惟一标识该项的属性作为 key,如果没有,先手动给数组里的每一项增加一个惟一标识
// ❌
{someList.map((item, index) => (<Item key={index} {...item} />
))}
// ✅
{someList.map(item => (<Item key={item.id} {...item} />
))}
- 为非必传 (non-required) prop 配置默认属性 defaultProps
// ❌
function SomeComponent({requiredProp, nonRequiredProp}) {
return (<div>{requiredProp}{nonRequiredProp}</div>
);
}
SomeComponent.propTypes = {
requiredProp: PropTypes.number.isRequired,
nonRequiredProp: PropTypes.string,
};
// ✅
function SomeComponent({requiredProp, nonRequiredProp}) {
return (<div>{requiredProp}{nonRequiredProp}</div>
);
}
SomeComponent.propTypes = {
requiredProp: PropTypes.number.isRequired,
nonRequiredProp: PropTypes.string,
};
SomeComponent.defaultProps = {nonRequiredProp: '',};
- 谨慎应用开展语法 (Spread syntax) 给子组件传 props。两头过渡组件 (比方 HOC) 能够间接应用 {…this.props} 给外部子组件传 props,其它类型组件必须摘出和外部子组件相干的 props 再应用开展语法 {…relevantProps}
// ❌
function SomeRegularComponent(props) {
return (<ChildComponent {...props} />
);
}
// ✅
function HOC(WrappedComponent) {return function WrapperComponent(props) {
const propFromWrapper = 'value';
return (<WrappedComponent {...props} propFromWrapper={propFromWrapper} />
);
};
}
// ✅
function SomeRegularComponent(props) {const { irrelevantProp1, irrelevantProp2, ...relevantProps} = props;
return (<ChildComponent {...relevantProps} />
);
}
- props 的书写程序倡议为:字符串字面量 prop > 非字符串的字面量 prop > 变量 prop > 事件处理函数 Event handlers
// ✅
<ChildComponent
literalStringProp="some string prop"
literalNumberProp={1}
literalBooleanProp={false}
variableProp={someVariable}
onChange={handleChange}
/>
Hooks
文档:https://ahooks.js.org/zh-CN/hooks/use-request/index
- 只容许在组件函数的最外层调用 Hooks 函数,而不能在循环、if 判断以及嵌套函数内调用
function ParentComponent() {
// ❌
if (someCondition) {useEffect(() => {doSomeSideEffects();
}, []);
}
// ✅
useEffect(() => {if (someCondition) {doSomeSideEffects();
}
}, []);
return (<ChildComponent onChange={handleChange} />
);
}
- 只容许在 React 函数组件和自定义 Hooks 函数外部调用 Hooks 函数
// ❌
function someRegularFunction() {const [state, setState] = useState(1);
}
// ✅
function ParentComponent() {const [state, setState] = useState(1);
return (<ChildComponent someProp={state} />
);
}
// ✅
function useSomeCustomHooks() {const [state, setState] = useState(1);
return state;
}
- 组件外部定义的函数类型 props 必须应用 useCallback 包裹
// ❌
function ParentComponent() {const handleChange = () => {// handle change};
return (<ChildComponent onChange={handleChange} />
);
}
// ✅
function ParentComponent() {const handleChange = useCallback(() => {// handle change}, []);
return (<ChildComponent onChange={handleChange} />
);
}
- 所有组件必须应用 React.memo 包裹
function ChildComponent() {
return (
<div>
<span>child component</span>
</div>
);
}
// ❌
export default ChildComponent;
// ✅
export default React.memo(ChildComponent);
- 不要在 JSX 中呈现 Hooks 函数
// ❌
function ParentComponent() {
return (
<ChildComponent
onChange={useCallback(() => {// handle change}, [])}
someMemoProp={useMemo(() => (computeWith(dep)
), [dep])}
/>
);
}
// ✅
function ParentComponent() {const handleChange = useCallback(() => {// handle change}, []);
const someMemoProp = useMemo(() => (computeWith(dep)
), [dep]);
return (<ChildComponent onChange={handleChange} someMemoProp={someMemoProp} />
);
}
注:前期需要调整过程中,Hooks 函数所在的 JSX 块可能会呈现 if 之类的条件渲染逻辑,此时就须要将该 Hooks 函数迁徙到组件函数的最外层很不不便,为了前期保护起见,应该对立在组件函数的最外层调用 Hooks 函数
- 大计算量的计算属性举荐应用 useMemo 包裹
function ParentComponent() {
// ❌
const someComplexComputedValue1 = () => doSomeComplexComputeWith(...deps);
// ✅
const someComplexComputedValue2 = useMemo(() => (doSomeComplexComputeWith(...deps)
), [...deps]);
return (
<ChildComponent
someComplexComputedValue1={someComplexComputedValue1}
someComplexComputedValue2={someComplexComputedValue2}
/>
);
}
- 传递给子组件的援用类型的计算属性倡议应用 useMemo 包裹
function ParentComponent({someProp}) {const [state, setState] = useState(1);
// ❌
const someComputedProp1 = doSomeComputeWith(state, someProp);
// ✅
const someComputedProp2 = useMemo(() => (doSomeComputeWith(state, someProp)
), [state, someProp]);
return (
<ChildComponent
someComputedProp1={someComputedProp1}
someComputedProp2={someComputedProp2}
/>
);
}
- useMemo 只能作为性能优化伎俩,而不能作为回调函数执行与否的根据
// ❌
export default function usePrevious(value) {const previousValueRef = useRef(value);
return useMemo(() => {
const previousValue = previousValueRef.current;
previousValueRef.current = value;
return previousValue;
}, [value]);
}
// ✅
function usePrevious(value) {const previousValueRef = useRef(value);
const currentValueRef = useRef(value);
useEffect(() => {
previousValueRef.current = currentValueRef.current;
currentValueRef.current = value;
}, [value]);
return currentValueRef.current === value
? previousValueRef.current
: currentValueRef.current;
}
参考:https://zh-hans.reactjs.org/d…
- 应用 useRef 缓存数据,以移除 useCallback 的 deps 数组里不必要的依赖项,缩小因 handler 变动引起的子组件从新渲染
function ParentComponent() {const [state, setState] = useState(1);
// ❌
const handleSubmit1 = useCallback(() => {submitWith(state);
}, [state]);
// ✅
const stateRef = useRef(state);
stateRef.current = state;
const handleSubmit2 = useCallback(() => (submitWith(stateRef.current);
), []);
return (
<ChildComponent
onSubmit1={handleSubmit1}
onSubmit2={handleSubmit2}
/>
);
}
- 对于须要监听参数组件应用 observe,不须要监听的应用 React.FC
// 须要监听参数变动
export const component = observer((props: any) => {})
// 不须要监听参数变动
const FC: React.FC = () => {return <Wrapper></Wrapper>;};
export default FC;
Hooks 调用地位和程序倡议:useSelector useContext useState useReducer useDispatch 对立在代码最顶层顺次调用,其次是 useCallback useMemo,而后是 useLayoutEffect useEffect,useRef 的地位能够根据被应用到的地位灵便搁置,useImperativeHandle 个别和 useRef 一起应用,倡议追随在与其相干的 useRef 之后。其它一些局部变量按须要灵便搁置
Mobx
version > 6
import {makeAutoObservable} from 'mobx';
class Store {constructor() {makeAutoObservable(this);
}
fontSize = 80;
updateFontSize(fontSize) {this.fontSize = fontSize;}
}
export default new Store();
TypeScript
环境
根本遵循 JavaScript Style Guide 与 ES-Next Style Guide
1、工程配置
TypeScript 文件应用 .ts 扩展名。含 JSX 语法的 TypeScript 文件应用 .tsx 扩展名。
tsconfig.json 配置文件应开启 strict、noImplicitReturns、noUnusedLocals 选项。
tsconfig.json 配置文件应开启 allowSyntheticDefaultImports 选项。
示例:
// ✅
import React, {PureComponent} from 'react';
// ❌
import \* as React from 'react';
应用 VS Code 编写 TypeScript。
2、文件
在文件结尾处,保留一个空行。
3、命名
接口 应用 Pascal 命名法。
接口名 不应用 I 作为前缀。
示例:
// ✅
interface ButtonProps {// ...}
// ❌
interface IButtonProps {// ...}
类型别名 应用 Pascal 命名法。
示例:
// ✅
interface HeaderStateProps {// ...}
interface HeaderDispatchProps {// ...}
type HeaderProps = HeaderStateProps & HeaderDispatchProps;
语言个性
1、变量
应用 const 申明 枚举。
示例:
// ✅
const enum Directions {
UP,
DOWM,
LEFT,
RIGHT,
}
// ❌
enum Directions {
UP,
DOWN,
LEFT,
RIGHT,
}
2、类型
不应显式申明能够主动推导的类型。
示例:
// ✅
let shouldUpdate = false;
// ❌
let shouldUpdate: boolean = false;
应用 string / number / boolean 申明根本类型,不应用 String / Number / Boolean。
示例:
// ✅
let str: string;
// ❌
let str: String;
不应用 Object / Function 申明类型。
数组元素为简略类型(非匿名且不含泛型)时,应用 T[] 申明类型,否则应应用 Array<T>。
数组元素为不可变数据时,应用 ReadonlyArray<T> 申明类型。
示例:
// ✅
let files: string[];
let tokens: Array<string | number>;
let buffer: Buffer[];
let responses: Array<Promise<number>>;
// ❌
let files: Array<string>;
let tokens: (string | number)[];
let buffer: Array<Buffer>;
let responses: Promise<number>[];
不应用 ! 申明对象属性非空。
示例:
// ✅
if (foo.bar && foo.bar.baz) {// ...}
// ❌
if (foo!.bar!.baz) {// ...}
不应用 any 申明类型。
示例:
// ✅
const identity = <T>(x: T) => x;
// ❌
const identity = (x: any) => x;
应用 as 进行类型申明转换,不应用 <>。
示例:
// ✅
const root = document.getElementById('root') as HTMLDivElement;
// ❌
const root = <HTMLDivElement>document.getElementById('root');
接口不应为空。
接口中同一函数重载的类型申明需相邻。
示例:
// ✅
interface AnyInterface {foo();
foo(x: string);
bar();
bar(x: number);
}
// ❌
interface AnyInterface {foo();
bar();
foo(x: string);
bar(x: number);
}
3、条件
应用 === 或 !== 判断相等性,不应用 == 或 !=。
示例:
// ✅
if (foo !== null && foo !== undefined) {// ...}
// ❌
if (foo != null) {// ...}
4、循环
应用 Object.keys / Object.values / Object.entries / Object.getOwnPropertyNames 遍历对象,不应用 for .. in。
示例:
// ✅
Object.keys(obj).forEach(key => /_ ... _/);
// ❌
for (const key in obj) {if (obj.hasOwnProperty(key)) {// ...}
}
索引仅用于获取数组以后被迭代的项时,应用 for .. of 遍历数组,不应用 for。
示例:
// ✅
for (const item of items) {// ...}
// ❌
for (let i = 0; i < items.length; i++) {const item = items[i];
// ...
}
5、数组
应用 … 进行数组浅拷贝,不应用 Array.from / Array.prototype.slice。
示例:
// ✅
const copies = [...items];
// ❌
const copies = items.slice();
// worst
let copies = [];
for (let i = 0; i < items.length; i++) {copies.push(items[i]);
}
应用 … 将类数组对象转化为数组,不应用 Array.from / Array.prototype.slice。
示例:
// ✅
const elements = [...document.querySelectorAll('.foo')];
// ❌
const element = Array.from(document.querySelectorAll('.foo'));
// worst
const element = Array.prototype.slice.call(document.querySelectorAll('.foo'));
6、对象
应用 … 进行对象浅拷贝,不应用 Object.assign。
示例:
// ✅
this.setState(state => ({...state, clicked: true}));
// ❌
this.setState(state => Object.assign({}, state, {clicked: true}));
7、函数
防止 return undefined,应间接 return。
示例:
// ✅
function foo(bar: boolean) {if (!bar) {return;}
}
// ❌
function foo(bar: boolean) {if (!bar) {return undefined;}
}
8、类
每个文件中最多申明一个类。
类成员的可拜访性为 public 时,不应显式申明。
构造函数可疏忽时,应疏忽。
类成员之间应用空行隔开。
示例:
// ✅
class Button extends PureComponent<ButtonProps, ButtonState> {
readonly state: ButtonState = {clicked: false,};
render() {// ...}
}
// ❌
class Button extends PureComponent<ButtonProps, ButtonState> {
public state: ButtonState = {clicked: false,};
constructor(props: ButtonProps) {super(props);
}
public render() {// ...}
}
构造函数初始化实例属性时,应尽量应用参数属性。
构造函数的参数中,作为属性的参数应排列于其余参数前。
示例:
// ✅
class AppComponent {constructor(private readonly heroService: HeroService) {}}
// ❌
class AppComponent {
private readonly heroService: HeroService;
constructor(heroService: HeroService) {this.heroService = heroService;}
}
9、模块
应用 ECMAScript 2015 规范的模块零碎。
除类型申明文件外,不应用 module / namespace 关键字。
不应用 /// <reference path= >。
示例:
// ✅
import foo from 'foo';
// ❌
import foo = require('foo');
对于同一个模块门路,仅 import 一次。
示例:
// ✅
import React, {PureComponent} from 'react';
// ❌
import React from 'react';
import {PureComponent} from 'react';
对于应用 webpack 等构建工具的我的项目,在模块中引入其余资源(如款式、图片等)时,为资源编写类型申明文件,或应用适合的 loader 生成类型申明文件。
示例:
// ✅
// Button.scss.d.ts
export clicked: string;
// logo.png.d.ts
declare const logo: string;
export default logo;
// Button.tsx
import styles from './Button.scss';
import logo from './logo.png';
// ❌
const styles = require<any>('./Button.scss');
const logo = require<string>('./logo.png');