我最近一直在用 React 玩得很开心。 但在我的旅途中,我一直很难找到好的代码风格指南来保持 JSX 和 JS 的混合干净且可读。 我一直在制定自己的风格指南,我想分享出来。 也许这些对您有用,当然,请随时在下面的评论区分享类似的指南。
props
规则 #1:解构您的我最喜欢的 ES6 功能之一是解构。 它使将对象属性分配给变量感觉不再那么麻烦。 让我们看一个例子。
假设我们有一条狗,我们想将其显示为一个 div,其类名与其品种相同。 在 div 内部是一个句子,说明了狗的颜色,并告诉我们它是一条好狗还是坏狗。
class Dog extends Component {
render () {
return <div className={this.props.breed}>My {this.props.color} dog is {this.props.isGoodBoy ? "good" : "bad"}</div>;
}
}
从技术上讲,它确实做了我们想要的一切,但这似乎是一块很大的代码,实际上只有三个变量和一个 HTML 标签。
我们可以通过将props
的所有属性分配给局部变量来将其分解。
let breed = this.props.breed;
let color = this.props.color;
let isGoodBoy = this.props.isGoodBoy;
使用 ES6,我们可以像这样将其放在一个干净的语句中
let { breed, color, isGoodBoy } = this.props;
为了保持一切清洁,我们也将三元运算符(稍后会详细介绍)放在它自己的变量中,瞧。
class Dog extends Component {
render () {
let { breed, color, isGoodBoy } = this.props;
let identifier = isGoodBoy ? "good" : "bad";
return <div className={breed}>My {color} dog is {identifier}</div>;
}
}
非常易于阅读。
规则 #2:一个标签,一行
现在,我们都有过这样的时刻,我们想把整个函数变成一堆运算符和微小的参数名,以创建一个丑陋的、超级快、不可读的实用程序函数。 但是,当你在 React 中创建一个无状态组件时,你可以相当容易地做到同样的事情,同时保持干净。
class Dog extends Component {
render () {
let { breed, color, goodOrBad } = this.props;
return <div className={breed}>My {color} dog is {goodOrBad}</div>;
}
}
vs.
let Dog = (breed, color, goodOrBad) => <div className={breed}>My {color} dog is {goodOrBad}</div>;
如果你只是创建了一个基本元素并将属性放在 HTML 标签中,那么不必为了获得一个完全独立的类而大费周折。 一行代码就够了。
如果你传递一个对象作为你的属性,你甚至可以利用一些 ES6 展开函数来进行创造性的操作。 使用this.props.content
将自动将字符串放在开始和结束标签之间。
let propertiesList = {
className: "my-favorite-component",
id: "myFav",
content: "Hello world!"
};
let SimpleDiv = props => <div {... props} />;
let jsxVersion = <SimpleDiv props={propertiesList} />;
何时使用展开函数
- 不需要三元运算符
- 只传递 HTML 标签属性和内容
- 可以重复使用
何时不使用展开函数
- 动态属性
- 需要数组或对象属性
- 需要嵌套标签的渲染
规则 #3:3 的规则
如果你有三个或更多属性,那么在实例和渲染函数中都将它们放在单独的行上。
只有一行属性是可以的
class GalleryImage extends Component {
render () {
let { imgSrc, title } = this.props;
return (
<figure>
<img src={imgSrc} alt={title} />
<figcaption>
<p>Title: {title}</p>
</figcaption>
</figure>
);
}
}
但考虑一下
class GalleryImage extends Component {
render () {
let { imgSrc, title, artist, clas, thumbnail, breakpoint } = this.props;
return (
<figure className={clas}>
<picture>
<source media={`(min-width: ${breakpoint})`} srcset={imgSrc} />
<img src={thumbnail} alt={title} />
</picture>
<figcaption>
<p>Title: {title}</p>
<p>Artist: {artist}</p>
</figcaption>
</figure>
);
}
}
或者渲染
<GalleryImage imgSrc="./src/img/vangogh2.jpg" title="Starry Night" artist="Van Gogh" clas="portrait" thumbnail="./src/img/thumb/vangogh2.gif" breakpoint={320} />
它可能成为一个太大的代码块,无法阅读。 将每个属性放到下一行,以获得干净、可读的外观
let { imgSrc,
title,
artist,
clas,
thumbnail,
breakpoint } = this.props;
和
<GalleryImage
imgSrc="./src/img/vangogh2.jpg"
title="Starry Night"
artist="Van Gogh"
clas="landscape"
thumbnail="./src/img/thumb/vangogh2.gif"
breakpoint={320} />
规则 #4:属性太多?
属性管理在任何级别都是很棘手的,但有了 ES6 解构和 React 的基于状态的方法,有很多方法可以清理大量属性的外观。
假设我们正在创建一个地图应用程序,该应用程序有一个已保存地址列表和您的当前位置的 GPS 坐标。
当前用户的位置信息和与收藏地址的接近程度应该在 App 的父组件中,如下所示
class App extends Component {
constructor (props) {
super(props);
this.state = {
userLat: 0,
userLon: 0,
isNearFavoriteAddress: false
};
}
}
因此,当我们创建一个地址并希望它记录您距离地址有多远时,我们至少要从 App 传递两个属性。
在 App 的render ()
中
<Address
... // Information about the address
currentLat={this.state.userLat}
currentLong={this.state.userLon} />
在 Address 组件的渲染函数中
render () {
let { houseNumber,
streetName,
streetDirection,
city,
state,
zip,
lat,
lon,
currentLat,
currentLon } = this.props;
return ( ... );
}
你已经可以看出这变得多么笨拙。 如果我们将两组信息分解成它们自己的对象,它会变得更易于管理。
在我们的 App 的constructor ()
中
this.state = {
userPos: {
lat: 0,
lon: 0
},
isNearFavoriteAddress: false
};
在 App 的render ()
之前
let addressList = [];
addressList.push({
houseNumber: "1234",
streetName: "Street Rd",
streetDirection: "N",
city: "City",
state: "ST",
zip: "12345",
lat: "019782309834",
lon: "023845075757"
});
在 App 的render ()
中
<Address addressInfo={addressList[0]} userPos={this.state.userPos} />
在 Address 组件的渲染函数中
render () {
let { addressInfo, userPos } = this.props;
let { houseNumber,
streetName,
streetDirection,
city,
state,
zip,
lat,
lon } = addressInfo;
return ( ... );
}
干净得多。 React 还有一些很棒的方法来确保对象属性存在并且是特定类型的,使用PropTypes
,这是我们在 JavaScript 中通常没有的,这本身就是一件很棒的面向对象编程的事情。
规则 #5:动态渲染 - 映射数组
在 HTML 中,我们经常一遍又一遍地编写相同的基本代码段,只是有一些关键区别。 这就是 React 最初创建的原因。 你创建了一个具有属性的对象,它返回一个复杂的动态 HTML 块,而无需重复编写每个部分。
JavaScript 已经有一个很好的方法来处理类似信息的列表:数组!
React 使用.map()
函数按顺序排列数组,使用数组中的一个参数作为key
。
render () {
let pokemon = [ "Pikachu", "Squirtle", "Bulbasaur", "Charizard" ];
return (
<ul>
{pokemon.map(name => <li key={name}>{name}</li>)}
</ul>
);
}
你甚至可以使用我们方便的展开函数,通过使用Object.keys()
将整个参数列表放入一个对象中(请记住,我们仍然需要一个key
)。
render () {
let pokemon = {
"Pikachu": {
type: "Electric",
level: 10
},
"Squirtle": {
type: "Water",
level: 10
},
"Bulbasaur": {
type: "Grass",
level: 10
},
"Charizard": {
type: "Fire",
level: 10
}
};
return (
<ul>
{Object.keys(pokemon).map(name => <Pokemon key={name} {... pokemon[name]} />)}
</ul>
);
}
规则 #6:动态渲染 - React 三元运算符
在 React 中,你可以使用运算符来执行条件渲染,就像变量声明一样。 在规则 #1 中,我们查看了这方面,用于说明我们的狗是好是坏。 为一个句子中一个词的差异而创建一整行代码并不完全必要,但当它变成大型代码块时,很难找到这些?
和:
。
class SearchResult extends Component {
render () {
let { results } = this.props;
return (
<section className="search-results">
{results.length > 0 &&
results.map(index => <Result key={index} {... results[index] />)
}
{results.length === 0 &&
<div className="no-results">No results</div>
}
</section>
);
}
}
或者,以真正的三元方式
class SearchResult extends Component {
render () {
let { results } = this.props;
return (
<section className="search-results">
{results.length > 0
? results.map(index => <Result key={index} {... results[index] />)
: <div className="no-results">No results</div>
}
</section>
);
}
}
即使有了我们整洁的结果映射,你也可以看到方括号的嵌套已经相当密集。 现在,想象一下,如果我们的渲染不仅仅只有一行。 它很快就变得不可读。 考虑一下另一种方法
class SearchResult extends Component {
render () {
let { results } = this.props;
let outputJSX;
if (results.length > 0) {
outputJSX = (
<Fragment>
{results.map(index => <Result key={index} {... results[index] />)}
</Fragment>
);
} else {
outputJSX = <div className="no-results">No results</div>;
}
return <section className="search-results">{outputJSX}</section>;
}
}
最终,代码长度大致相同,但有一个关键区别:在第一个例子中,我们快速地在两种不同的语法之间切换,这使得视觉解析变得繁琐和困难,而第二个例子只是简单的 JavaScript,使用一种一致的语言进行值分配,并在另一种语言中进行一行函数返回。
在这种情况下,经验法则是,如果你放在 JSX 对象中的 JavaScript 超过两个单词(例如object.property
),那么它应该在return
调用之前完成。
总结
语法的组合会变得混乱,而这些是我看到我的代码偏离轨道最明显的情况。 以下是所有这些都源于的基本概念,可以应用于此处未涵盖的任何情况
- 使用 ES6 功能。 真的。 这里有很多很棒的功能可以使你的工作更轻松、更快,而且更少手动操作。
- 只在
=
或return
的右侧编写 JSX。 - 有时你需要在 JSX 中使用 JavaScript。 如果你的 JavaScript 不能放在一行上(比如
.map()
函数或三元运算符),那么它应该提前完成。 - 如果你的代码看起来像
(<{`${()}`} />)
,那么你可能已经走得太远了。 从当前语句的最低级别开始,在它之前完成。
我认为这个例子
应该是
第一个例子没有解构 props。
规则 #5 可以更进一步,使用
Object.entries
而不是Object.keys
。 与参数中的解构结合起来,你可以得到一个不错的Object.entries(obj).map(([key, val]) => This value is {val})
太棒了!我之前没见过这个,我得开始用它了。
好文章!我强烈建议使用 prettier 来帮助你实现一些你列出的格式化技巧。
另外:在最后一个例子中,我认为你应该在返回值函数中使用 outputJSX 而不是仅仅使用 output。
干杯!
在解构时,使用“const”而不是“let”。如果你要改变值,你应该使用“let”,但这在这里不是这种情况。
我建议使用
const
而不是let
。这将防止重新分配变量,我认为这有点违背不可变数据概念?如果变量不会被重新分配,它应该只是 const,因为这是它的用途。我不确定你为什么要在解构道具时使用
let
而不是const
。道具不允许被修改,我将使用const
来强化我的变量声明。嗨,Daniel,
我同意 Dwayne 的观点。
在任何关于 React 代码风格的文章中,都值得一提的是 Prettier 已迅速成为 JavaScript 事实上的“风格指南”。
从实际意义上讲,它通过允许你以“狂野西部”风格编码,然后在每次保存时神奇地清理代码来加快开发速度。
看看吧!
请默认使用
const
,只有在必要时才使用let
。不要这样做
这样做
不要这样做
这样做
不要这样做
这样做