一. JSX防踩坑的几个地方
1.1 JSX代码注释
JSX
中的代码注释是非常有讲究的,这个书上介绍的也非常少。
我第一次写JSX注释,是直接这样写的,当然这样写是完全不对
的。
<Fragment>
//第一次写注释,这个是错误的
<div>
<input value={this.state.inputValue} onChange={this.inputChange.bind(this)} />
<button onClick={this.addList.bind(this)}> 增加服务 </button>
</div>
</Fragment>
那写JSX的注释,可以有下面两种写法:
<Fragment>
{/* 正确注释的写法 */}
<div>
<input value={this.state.inputValue} onChange={this.inputChange.bind(this)} />
<button onClick={this.addList.bind(this)}> 增加服务 </button>
</div>
</Fragment>
你可以把这个理解为,在jsx中写javascript代码。所以外出我们套入了{}
,然后里边就是一个多行的javascript注释。
如果你要使用单行注释//,你需要把代码写成下面这样。
<Fragment>
{
//正确注释的写法
}
<div>
<input value={this.state.inputValue} onChange={this.inputChange.bind(this)} />
<button onClick={this.addList.bind(this)}> 增加服务 </button>
</div>
</Fragment>
也就是你要进行换行,所以个人认为这种方法不太优雅,所以推荐第一种注释方法。
1.2 JSX中的class陷阱
比如要给朴素单纯的界面,加入黄色成分,让我们的文本框又粗又黄。我们先来错误演示。
第一步:先写一个CSS样式文件,在src目录下,新建一个style.css的样式文件。
.input {border:3px solid #ae7000}
第二步:在Xiaojiejie.js里引入,先用import进行引入,能用import引入,都是webpack的功劳。
import './style.css'
第三部:给JSX加入`class,注意下面的代码是错误的。
<input class="input" value={this.state.inputValue} onChange={this.inputChange.bind(this)} />
虽然现在页面是可以正常显示结果的,但是你代开浏览器控制台会发现Warning警告。
index.js:1437 Warning: Invalid DOM property `class`. Did you mean `className`?
in input (at Xiaojiejie.js:19)
in div (at Xiaojiejie.js:18)
in Xiaojiejie (at src/index.js:5)
意思就是要把class
换成className
,它是防止和js中的class类名 冲突,所以要求换掉。这也算是一个小坑吧。
1.3 JSX中的html解析问题
如果想在文本框里输入一个<h1>
标签,并进行渲染。默认是不会生效的,只会把<h1>
标签打印到页面上,这并不是我想要的。如果工作中有这种需求,可以使用dangerouslySetInnerHTML
属性解决。具体代码如下:
<ul>
{
this.state.list.map((item,index)=>{
return (
<li
key={index+item}
onClick={this.deleteItem.bind(this,index)}
dangerouslySetInnerHTML={{__html:item}}
>
</li>
)
})
}
</ul>
上面的代码就可以实现html
格式的输出。
1.4 JSX中for陷阱
JSX中<label>的坑,也算是比较大的一个坑,label是html中的一个辅助标签,也是非常有用的一个标签。
先看下面的代码,我们在文本框前面加入一个<label>。
<div>
<label>加入服务:</label>
<input className="input" value={this.state.inputValue} onChange={this.inputChange.bind(this)} />
<button onClick={this.addList.bind(this)}> 增加服务 </button>
</div>
这时候想点击“加入服务”直接可以激活文本框,方便输入。按照html的原思想,是直接加ID就可以了。代码如下:
<div>
<label for="jspang">加入服务:</label>
<input id="jspang" className="input" value={this.state.inputValue} onChange={this.inputChange.bind(this)} />
<button onClick={this.addList.bind(this)}> 增加服务 </button>
</div>
这时候你浏览效果虽然可以正常,但console里还是有红色警告提示的。大概意思是不能使用for。它容易和javascript里的for循环混淆,会提示你使用htmlFor。
<div>
<label htmlFor="jspang">加入服务:</label>
<input id="jspang" className="input" value={this.state.inputValue} onChange={this.inputChange.bind(this)} />
<button onClick={this.addList.bind(this)}> 增加服务 </button>
</div>
这时候代码就正确了,可以实现点击<label>后,激活<input>标签了。
二. 关于React中的代码快速生成插件 Simple React Snippets
2.1 安装Simple React Snippets
打开VSCode的插件查单,然后在输入框中输入Simple React Snippets
,然后点击进行安装就可以了。
2.2 快速导入文件
直接在vscode中输入imrc,就会快速生成最常用的import代码。
import React, { Component } from 'react';
2.3 快速生成class
在写组件的时候,都需要写一个固定的基本格式,这时候你就可以使用快捷键cc。插件就会快速帮我们生成如下代码:
class extends Component {
state = { }
render() {
return ( );
}
}
export default ;
当然,还有很多快捷键,我就没必要再这里唠叨了,如果你需要理解,打开插件的说明文件看一下就可以了。这个插件建议小伙伴们要熟练掌握。
三. 组件的拆分
3.1 新建服务菜单组件
在src目录下,新建一个文件,这里就叫做XiaojiejieItem.js,然后先把最基础的结构写好。
import React, { Component } from 'react'; //imrc
class XiaojiejieItem extends Component { //cc
render() {
return (
<li>小姐姐</li>
);
}
}
export default XiaojiejieItem;
写好这些代码后,就可以到以前写的Xiaojiejie.js文件中用import
进行引入,代码如下:
import XiaojijieItem from './XiaojiejiItem'
3.2 修改Xiaojiejie组件
已经引入了新写的组件,这时候原来的代码要如何修改才能把新组件加入?
把原来的代码注释掉,当然你也可以删除掉,我这里就注释掉了,注释方法如下:
{/*
<li
key={index+item}
onClick={this.deleteItem.bind(this,index)}
dangerouslySetInnerHTML={{__html:item}}
>
</li>
*/ }
然后替换掉就OK了。
四. 组件间传值
4.1 父组件向子组件传值
我们可以通过使用组件属性的形式来让父组件向子组件传值。比如:我们在<XiaojiejieItem>组件中加入content属性,然后给属性传递{item}内容,这样就完成了父组件向子组件传值。
<XiaojiejieItem content={item} />
现在值已经顺利的传递了过去,这时候可以通过this.props.xxx
的形式进行接受,比如传递过来的值,可以用如下代码进行接收。
import React, { Component } from 'react'; //imrc
class XiaojiejieItem extends Component { //cc
render() {
return (
<div>{this.props.content}</div>
);
}
}
export default XiaojiejieItem;
学到这里你要记住一点:父组件向子组件传递内容,靠属性的形式传递。
4.2 子组件向父组件传递数据
现在要做这样一个功能:点击组件中的菜单项后,删除菜单项。在前已经实现了这个功能,只是现在组件拆分了,就涉及了一个子组件向父组件传递数据的知识需要掌握。
先来绑定点击事件,这时候当然是要在XiaojiejieItem组件中绑定了,代码如下:
class XiaojiejieItem extends React.Component {
render() {
return (
<li onClick={this.handleClick.bind(this)}>{this.props.content}</li>
);
}
handleClick() {
console.log(this.props.index)
}
}
export default XiaojiejieIte
其实这里对于this的绑定有一个优化,就是最好在构造函数中去绑定,而不是像上面的代码那样,具体原因还不知道
有言曰:构造函数中绑定性能会高一些,特别是在高级组件开发中,会有很大的作用
import React from 'react';
class XiaojiejieItem extends React.Component {
constructor(props) {
super(props)
this.handleClick = this.handleClick.bind(this)
}
render() {
return (
<li onClick={this.handleClick}>{this.props.content}</li>
);
}
handleClick() {
console.log(this.props.index)
}
}
export default XiaojiejieItem
现在就要通过操作子组件来删除父组件里的数据了。但是React有明确规定,子组件是不能操作父组件里的数据的,所以需要借助一个父组件的方法,来修改父组件的内容。
其实在以前已经写了一个删除方法deleteItem,现在要做的就是子组件调用这个方法。
//删除单项服务
deleteItem(index){
let list = this.state.list
list.splice(index,1)
this.setState({
list:list
})
}
获取数组索引下标:
那现在问题来了,要删除就要知道索引值,还是需要通过父组件传递给子组件。这里还是通过props
属性的形式进行传递。
<ul>
{
this.state.list.map((item,index)=>{
return (
<XiaojiejieItem
key={index+item}
content={item}
index={index} />
)
})
}
</ul>
然后修改XiaojiejieItem组件,在handleClick方法里,写入下面代码:
handleClick(){
console.log(this.props.index)
}
在获取到索引后,就可以通过在子组件里调用父组件的方法来实现删除某一项了。
子组件调用父组件方法:
如果子组件要调用父组件方法,其实和传递数据差不多,只要在组件调用时,把方法传递给子组件就可以了,记得这里也要进行this的绑定,如果不绑定子组件是没办法找到这个父组件的方法的。
<ul>
{
this.state.list.map((item,index) => {
return (
<XiaojiejieItem
key={index}
content={item}
index={index}
delItem={this.delItem.bind(this)}
>
</XiaojiejieItem>
// <li key={index}
// onClick={this.delItem.bind(this, index)}>
// {item}
// </li>
)
})
}
</ul>
五. 单项数据流和其他
5.1 单项数据流
React的特性中有一个概念叫做“单项数据流”,可能刚刚接触React的小伙伴不太明白这个概念,还是拿出《小姐姐服务菜单》的Demo,来给大家讲解。比如我们在父组件中可以直接把this.state.list传递过来。例如下面代码:
<ul>
{
this.state.list.map((item,index)=>{
return (
<XiaojiejieItem
key={index+item}
content={item}
index={index}
list={this.state.list}
deleteItem={this.deleteItem.bind(this)}
/>
)
})
}
</ul>
其实这样传是没有问题的,问题是你只能使用这个值,而不能修改这个值,如果你修改了,比如我们把代码写成这样:
handleClick(){
//关键代码——---------start
this.props.list=[]
//关键代码-----------end
this.props.deleteItem(this.props.index)
}
就会报下面的错误:
TypeError: Cannot assign to read only property 'list' of object '#<Object>'
意思就是list是只读的,即单项数据流。那如果要改变这里边的值怎么办?我们可以通过向子组件传递父组件的方法。
5.2 和其他框架配合使用
有小伙伴问我,React和jquery能一起使用吗?
答案:是可以的,React其实可以模块化和组件化开发。看/public/index.html文件,代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<title>React App</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<!--关键代码start-->
<div id="root"></div>
<!--关键代码end-->
</body>
</html>
其实React只对这一个<div>,外边的其他DOM并不受任何影响,比如我们在它的下方再写一个<div>,然后查看效果。
<div id="root"></div>
<div style="color:red">今天过的好开心!</div>
你可以在其他的div里加入任何内容,但是这种情况很少,我也不建议这么使用。希望小伙伴们还是统一技术栈。
5.3 函数式编程
在面试React时,经常会问道的一个问题是:函数式编程的好处是什么?
- 函数式编程让我们的代码更清晰,每个功能都是一个函数。
- 函数式编程为我们的代码测试带来了极大的方便,更容易实现前端自动化测试。
- React框架也是函数式编程,所以说优势在大型多人开发的项目中会更加明显,让配合和交流都得心应手。