这篇七个点:1 事件系统 2 表单 3 样式处理 4 组件间通信 5 组件抽象 6 性能优化 7 动画 8 自动化测试
2.1 事件系统
2.1.1 合成事件的绑定方式
为按钮添加点击事件
render(){
return (
<div>
<button onClick={this.handleClick}>测试</button>
</div>
)
}
注意点使用驼峰书写事件属性名(onClick),{}内可以是任意的值。
2.1.2 合成事件的实现机制
- 1 事件委托
React并不是把事件处理函数直接绑定到真实DOM上,而是绑定到最外层的Document上,使用统一的事件监听器。 - 2 自动绑定
在组件中,每个方法的上下文都会指向实例,this会自动绑定当前组件。但是在使用classes或者纯函数时,就需要手动绑定this。
方法一:bind方法(双冒号方法不成功)
class Event extends Component{
constructor(props){
super(props);this.state={
name:"bolala"
};
}
handleClick() {
console.log(this.state.name);
};
render(){
return (
<div>
<button onClick = {::this.handleClick}>测试2</button>
</div>
)
}
}
方法二:构造器声明
这种方式的好处在于仅需要绑定一次。
class Event extends Component{
constructor(props){
super(props);this.state={
name:"bolala"
};
this.handleClick=this.handleClick.bind(this);
}
handleClick() {
console.log(this.state.name);
};
render(){
return (
<div>
<button onClick = {this.handleClick}>测试2</button>
</div>
)
}
}
方法三:箭头函数
使用箭头函数会自动绑定定义次函数作用域的this,因此就不需要再绑定了。
class Event extends Component{
constructor(props){
super(props);this.state={
name:"bolala"
};
}
handleClick=()=>{
console.log(this.state.name);
};
render(){
return (
<div>
<button onClick = {this.handleClick}>测试2</button>
</div>
)
}
}
或者
class Event extends Component{
constructor(props){
super(props);this.state={
name:"bolala"
};
}
handleClick(){
console.log(this.state.name);
}
render(){
return (
<div>
<button onClick = {()=>this.handleClick()}>测试2</button>
</div>
)
}
}
2.1.3 在React中使用原生事件
可以componentDidMount中用refs实现原生事件。也要记得componentWillUnmount中手动移除。
class Event extends Component{
constructor(props){
super(props);this.state={
name:"bolala"
};
}
render(){
return (
<div>
<button
onClick = {()=>this.handleClick()}
ref="button"
>
测试2
</button>
</div>
)
}
componentDidMount(){
this.refs.button.addEventListener('click',()=>{
this.handleClick();
},false)
}
handleClick(){
console.log(this.state.name);
}
componentWillUnmount(){
this.refs.button.removeEventListener('click');
}
}
2.1.3React合成事件与js原生事件的对比
- 合成事件只支持事件冒泡机制。
- 合成事件的事件类型是js原生事件类型的一个子集。
- 合成事件绑定方式很简单:
<button onClick = {()=>this.handleClick()}>测试2 </button>
2.2 表单
2.2.1应用表单组件
- 文本框
class Textbox extends Component {
constructor(props) {
super(props);
this.inputChange=this.inputChange.bind(this);
this.textChange=this.textChange.bind(this);
this.state = {
inputValue:"",
textValue:""
};
}
inputChange(e){
this.setState({inputValue:e.target.value})
}
textChange(e){
this.setState({textValue:e.target.value})
}
render(){
return(
<div>
<input type="text" onChange={this.inputChange} value={this.state.inputValue}/>
<textarea onChange={this.textChange} value={this.state.textValue}/>
</div>
)
}
}
- 单选和复选按钮
class Radio extends Component{
constructor(props){
super(props);
this.state={radioValue:""};
this.handleChange= this.handleChange.bind(this);
}
handleChange(e){
this.setState({
radioValue:e.target.value
})
}
render(){
return (
<div>
<label htmlFor="male">男:</label>
<input type="radio" name="sex" value="male" id="male"
checked={this.state.radioValue==="male"}
onChange={this.handleChange}/>
<label htmlFor="female">女:</label>
<input type="radio" name="sex" value="female" id="female"
checked={this.state.radioValue==="female"}
onChange={this.handleChange}/>
</div>
)
}
}
复选框略
一个选项
class Select extends Component{
constructor(props){
super(props);
this.state={area:""};
this.handleChange= this.handleChange.bind(this);
}
handleChange(e){
this.setState({
area:e.target.value
})
}
render(){
return (
<div>
<select value={this.state.area} onChange={this.handleChange}>
<option value="beijng">北京</option>
<option value="shanghai">上海</option>
<option value="hangzhou">杭州</option>
</select>
</div>
)
}
}
多个选项
class Select extends Component {
constructor(props) {
super(props);
this.state = {area: []};
this.handleChange = this.handleChange.bind(this);
}
handleChange(e) {
var newArea=[];
console.log(e.target.options);
for (let i=0;i<e.target.options.length;i++){
if (e.target.options[i].selected){
newArea.push(e.target.options[i]);
}
}
this.setState({
area: newArea
})
}
render() {
return (
<select multiple={true} onChange={this.handleChange}>
<option value="beijng">北京</option>
<option value="shanghai">上海</option>
<option value="hangzhou">杭州</option>
</select>
)
}
}
2.2.2 受控组件
每当表单的状态发生变化,都会被写入组件的state中,这种组件被称为受控组件,通过这种方式消除了组件的局部状态,让整个状态更加可控。
React受控组件更新state的流程:
- 初始化state设置初始值。
- 表单的值发生变化时,调用onChange事件处理器。
- 通过e.target.value拿到改变后的状态,更新state。
- setState触发重新渲染,完成表单组件的更新。
与原生表单组件相比,确实复杂了很多,这样同意了组件内部状态,我们可以在执行setState前对表单进行清洗和检验。
inputChange(e) {
this.setState({inputValue: e.target.value.toUpperCase()})
}
2.2.3 非受控组件
如果一个表单组件没有value/checked时就可以称为非受控组件,可以使用defaultValue和defaultChecked表示组件的默认状态。
class Textbox extends Component {
constructor(props) {
super(props);
this.button=this.button.bind(this);
this.state = {};
}
button(){
console.log(this.refs.name);
}
render() {
return (
<div>
<input ref="name" type="text" defaultValue="bolala"/>
<button onClick={this.button}>显示</button>
</div>
)
}
}
//始终显示<input type="text" value="bolala">
2.2.4 对比受控组件和非受控组件
非受控组件的状态不会受应用状态的控制应用中也多了局部组件状态,而受控组件的值来自于组件的state。
- 官方不推荐使用非受控组件。
2.2.5 表单组件几个重要属性
1 状态属性:
- value:应用于 text的input,textarea,select
- checked: 应用于 radio,checkbox的input
- selected:option
2 事件属性:
React一般用onChange事件,同时也支持DOM3中定义的所有表单事件。
2.3 样式处理
2.3.1 基本样式设置
class BasicCss extends Component{
constructor(props){
super(props);
this.state = {};
}
render(){
var css={
box:{background:'blue',color:"white"}
};
return (
<div>
<div style={{border:'1px solid red'}}>haha</div>
<h1 style={css.box}>BOLALA</h1>
</div>
)
}
}
使用classnames库
当要使用多个class时,官方推荐使用这个库。
class ClassNames extends Component {
constructor(props){
super(props);
this.state={isPressed:false,isHovered:true};
}
render(){
var classNames = require('classnames');
return(
<div>
<h1 className={classNames({btn:true,bolala:true})}>ha</h1>
<h2 className={classNames({
"btn":true,
"btn-pressed":this.state.isPressed,
"btn-over":!this.state.isPressed&&this.state.isHovered
})}>
bhbh</h2>
</div>
)
}
}
2.3.2 CSS Modules
https://segmentfault.com/q/1010000018891784
https://www.cnblogs.com/suihang/p/10417755.html
http://www.ruanyifeng.com/blog/2016/06/css_modules.html
略。以后如果遇到,看最后一个链接,讲得清楚又简单。
2.4 组件间通信
2.4.1 父组件向子组件通信
在React中,数据流是单向的,通过props从父节点传递到子节点,如果props发生改变,会遍历整个组件树,从而渲染用到这个props的所有组件。
class Child extends Component{
constructor(props){
super(props);
this.state={};
}
render(){
return(
<h1>{this.props.text}</h1>
)
}
}
class Transmit extends Component{
constructor(props){
super(props);
this.state={};
this.handleClick=this.handleClick.bind(this);
}
handleClick(){
this.setState({
text:"传入props"
})
}
render(){
return(
<div>
<Child text={this.state.text||"没有传入值"}/>
<button onClick={this.handleClick}>改变</button>
</div>
)
}
}
2.4.2 子组件向父组件通信
子组件更新父组件,需要父组件传一个回调函数给子组件,然后子组件调用,就可以出发父组件更新了。
class Transmit3 extends Component{
constructor(props){
super(props);
this.state={};
}
handleClick(){
this.setState({
text:"传入参数"
})
}
render(){
return(
<div>
<h1>{this.state.text||"未传参数"}</h1>
<Child handleClick={this.handleClick.bind(this)}/>
</div>
)
}
}
class Child extends Component{
constructor(props){
super(props);
}
render(){
return(
<button onClick={this.props.handleClick}>改变</button>
)
}
}
2.4.3 跨级组件通信
- 两个子组件有一个共同的父组件,可以同构哦父组件来通信,一个子组件可以通过父组件的回调函数来改变props,从而改变另一个子组件的props。
- 当有共同的祖先,不一定是亲兄弟,React提供了一种上下文方式,允许子组件可以直接访问祖先组件的属性和方法。
class Transmit1 extends Component{
constructor(props){
super(props);
this.state={};
}
handleClick(){
this.setState({
text:"传入新值"
})
}
render(){
return(
<div>
<Child1 text={this.state.text||"没有传值"}/>
<Child2 handleClick={this.handleClick.bind(this)}/>
</div>
)
}
}
class Child1 extends Component{
constructor(props){
super(props)
}
render(){
return(
<h1>{this.props.text}</h1>
)
}
}
class Child2 extends Component{
constructor(props){
super(props)
}
render(){
return(
<button onClick={this.props.handleClick}>改变</button>
)
}
}
2.4 4 没有嵌套关系的组件通信
略
2.5 组件间抽象
略
2.6 组件性能优化
2.6.1 纯函数
三大原则:
- 给定相同的输入,总是返回相同的输出。
- 过程没有副作用。
- 没有额外的状态依赖。
2.6.2 PureRender
2.6.3 Immutable
2.6.4 key
class KeyWorld extends Component{
constructor(props){
super(props);
this.state={
list:[{id:1,name:"bolala"},{id:2,name:"bolala2"},{id:3,name:"bolala3"}]
}
}
render(){
return(
<ul>
{this.state.list.map((value,index)=>(
<li key={value.id}>{value.name}</li>
))}
</ul>
)
}
}
原则:key值要独一无二,并且能不用遍历或随机值就不用,除非列表内容也并不是唯一的标识,且没有可以相匹配的属性。
2.6.5 react-addons-perf
是为了量化性能优化的效果的官方提供的插件。
2.7 动画
https://blog.csdn.net/sd19871122/article/details/98624902
- 基于定时器或 requestAnimationFrame(RAF) 的间隔动画;
- 基于 css3 的简单动画;
- React 动画插件 CssTransitionGroup;
- 结合 hook 实现复杂动画;
- 其他第三方动画库。
class Progress extends Component {
constructor(props) {
super(props);
this.state = {percent:10}
}
increase=()=>{
const percent=this.state.percent;
const target=(percent>=90)?100:(percent+10);
const speed=(target-percent)/100;
let start=null;
const animate=(timestamp)=>{
if (!start) start = timestamp;
const progress = timestamp - start;
const currentProgress=parseInt(speed * progress + percent, 10);
this.setState({percent:currentProgress});
if (currentProgress<target){
window.requestAnimationFrame(animate);
}
};
window.requestAnimationFrame(animate);
};
decrease=()=>{
const percent=this.state.percent;
const target=(percent<=10)?0:(percent-10);
const speed=(target-percent)/400;
let start=null;
const animate=(timestamp)=>{
if (!start) start = timestamp;
const progress = timestamp - start;
const currentProgress=parseInt(speed * progress + percent, 10);
this.setState({percent:currentProgress});
if (currentProgress>target){
window.requestAnimationFrame(animate);
}
};
window.requestAnimationFrame(animate);
};
render() {
return (
<div style={{width:"600px"}}>
<div className="progress">
<div className="wrap" style={{background:"#ededed",borderRadius:"10px"}}>
<div className="inner" style={{width:`${this.state.percent}%`,background:"rgba(255, 0, 0, 0.6)",height:'10px',borderRadius:"10px"}}></div>
</div>
</div>
<span>{`${this.state.percent}%`}</span>
<div className="btns">
<button onClick={this.decrease}>-</button>
<button onClick={this.increase}>+</button>
</div>
</div>
)
}
}
import './index.css';
class Css3 extends Component {
constructor(props) {
super(props);
this.state = {show:true};
this.handleClick=this.handleClick.bind(this);
}
handleClick(){
this.setState({
show:!this.state.show
})
}
render() {
return (
<div>
<div className={this.state.show ? "show" : "hide"}>Hello</div>
<button onClick={this.handleClick}>切换</button>
</div>
);
}
}
.show{
animation:show-item 1s ease-in ;
}
.hide{
animation:hide-item 1s ease-in ;
}
@keyframes show-item {
0%{
opacity: 0;
color: red;
}
100%{
opacity: 1;
color: blue;
}
}
@keyframes hide-item {
0%{
opacity: 1;
color: red;
}
100%{
opacity: 0;
color: blue;
}
}
https://www.jianshu.com/p/a38439337058
https://blog.csdn.net/Sophie_U/article/details/80093876?utm_medium=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.nonecase&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.nonecase
略
网友评论