目录:

  • 1 组件和组件属性(Component and Props)
  • 1.1 组件定义方式
  • 1.2 组件嵌套使用
  • 1.3 提取组件
  • 2 状态和声明周期(State and LifeCycle)
  • 2.1 状态值
  • 2.2 生命周期
  • 3 事件处理
  • 4 条件渲染(Conditional Rendering)
  • 5 列表和键(Lists and Keys)
  • 6 Form 表单
  • 7 状态值提升(Lifting State Up)
  • 8 组合与继承(Composition and Inheritance)
  • 9 结束示例
  • 10 总结

1 组件和组件属性(Component and Props)

1.1 组件定义方式

React 基于组件开发,而组件可以使UI进行分离,并且高可复用性,从概念上来说,更像是 js 的函数,往组件传入参数(也就是 this.props)然后返回整个组件对象,通过 ReactDOM.render 渲染到网页中。

组件定义方式应该有三种:

  1. 函数式

    定义

    // test.html<div id="root"></div><script type="text/babel">function Welcome( props ) {return (<h1>Hello React.js, {props.name}<br /></h1>);
    }</script>

    渲染

    ReactDOM.render( <Welcome name="Welcome" />, document.getELementById('root') );

    最终会输出结果:‘Hello React.js, Welcome’

    上面函数的参数 props 也就是组件 render 时候内部的一些属性值集合

    这种方式会发现并不需要组件函数中存在 render 方法,这个应该只在 类继承和React.createClass 中才需要。下面看看类继承方式

  2. 类继承

    类继承需要用到 ES6 语法中的 class,通过 React.Component 来创建被继承的组件对象,如下:

    类继承方式定义组件:(这里注意, React.Component 是个组件类,不是构造函数,直接使用 {} 即可)

    class Welcome extends React.Component {render() {return (<h1>Hello React.js, {this.props.name}<br /></h1>);}
    }

    渲染方式都是一样的,通过组件名 Welcome 去渲染即可

    ReactDOM.render( <Welcome name="Welcome" />, document.getELementById('root') );

    最终结果和函数式组件定义一样

  3. React.createClass

    这里使用的是 React 自己的创建组件类的方法去实现,如果对 es6 不太熟悉的话,这种方式更容易接受点

    定义:

    var Welcome = React.createClass({render: function () {return (<h1>Hello React.js, {this.props.name}<br /></h1>);}
    });

    渲染:

    ReactDOM.render( <Welcome name="Welcome" />, document.getELementById('root') );

其实不管使用上面哪种方式,最后生成的组件在 DOM 树中呈现的结果都是一致的,如下:

<h1 data-reactid=".1"><span data-reactid=".1.0">Hello React.js, </span><span data-reactid=".1.1">Welcome</span><br data-reactid=".1.2">
</h1>

还有需要注意的是,不论上面那种方式,Welcome 组件名的首字母一定要大写,否则会报错,并且报错内容都是一样的:‘Uncaught ReferenceError: Welcome is not define’

我们看到上面的 ReactDOM.render 方法里面是直接传的组件形式,事实上 react 也只是变量,比如:

上面的 <Welcome name="Welcome" /> 可以定义成变量,再传入到渲染函数

const element = <Welcome name="Welcome" />;ReactDOM.render( element, document.getElementById( 'root' ) );

结果显示上面方式也是 OK 的,那后面的 id 肯定也是可以通过变量传入的,最后就变成如下:

const element = <Welcome name="Welcome" />;
const id = document.getElementById( 'root' );ReactDOM.render( element, id );

这样是不是就清爽许多了……

从官文上有一段注意点:

Caveat:
Always start component names with a capital letter.
For example,

represents a DOM tag, but represents a component and requires Welcome to be in scope.

这里也说明了为什么组件首字母要大小写原因之一:意思就是说 react 会把消息开头的当作 DOM 标签,而把大写开头的才会当成是个组件名称。

1.2 组件嵌套使用

由于组件一旦定义完成,它就是个以独立的个体形式存在,也就是说它可以被单独渲染到 DOM 树中,同时也可以被其它组件引用,作为其输出,并且支持多次使用

如示例:(不仅被其他组件引用,还能多次重复使用)

// test.html// Welcome 组件上面已经定义好,直接拿来使用即可var Wrap = React.createClass({return (<div><Welcome name="Lizc" /><Welcome name="Fll" /><Welcome name="Liwy" /></div>);
});const element = <Wrap />;
const id      = document.getElementById( 'root' );ReactDOM.render( element, id );

输出结果:

Hello React.js, LizcHello React.js, FllHello React.js, Liwy

1.3 提取组件

官文上有这么一句话:‘Don’t be afraid to split components into smaller components.’
意思就是告诉我们不要害怕将组件分割成更小的组件,也就是说我们可以根据我们的需求尽可能的将内容进行组件化,我们来看下官网的示例程序

// extract-component.html// 样式
<style>.avatar {width:100px;height:100px;}</style>// 脚本
<script type="text/babel">const rootEle = document.getElementById( 'root' );var Comment = React.createClass({render: function () {return (<div className="comment"><div className="user-info"><img className="avatar" src={this.props.author.avatarUrl} alt={this.props.author.name}/></div><div className="user-info-name">作者:{this.props.author.name}</div><div className="comment-text">问候语:{this.props.text}</div><div className="comment-date">日期:{this.props.date}</div></div>);}});var element = <Comment author={{ avatarUrl:'./images/ygr-01.jpg', name: 'lizc' }}text='hello react.js' date={new Date().toString()}/>ReactDOM.render( element, rootEle );</script>

从上面的 Comment 组件看,其实里面包含四个部分(头像,作者,问候语,日期),本着官文给我们的注解,尽可能的组件化,那么这四个部分也就应该成为独立的组件,下面来实现下这四个组件,而数据这块,依然采用父组件中的属性

这里只是个示范,更多的组件化可以根据自己的需求来进行分割

  1. 头像组件

    // extract-component.html// 父组件
    var Comment = React.createClass({render: function () {return (// 父组件 Comment 中引用子组件:头像组件(AvatarComp)<div className="comment"><AvatarComp author={this.props.author} />// ... 其他</div>);}});// 子组件
    var AvatarComp = React.createClass({render: function () {return (<div className="user-info"><img className="avatar" src={this.props.author.avatarUrl} alt={this.props.author.name}/></div>);  }
    });

    最终运行结果和之前一致,说明头像组件化成功,另外可以发现,我们的 AvatarComp 组件是定义在父组件 Comment 之后的,但是依然可以运行。

  2. 作者组件

    作者组件:

    // 父组件中引用作者组件
    <AuthorComp name={this.props.author.name} />var AuthorComp = React.createClass({render: function () {return (<div className="user-info-name">作者:{this.props.name}</div>);}
    });
    
  3. 问候语组件

    var WelcomeComp = React.createClass({render: function () {return (<div className="comment-text">问候语:{this.props.text}</div>);}
    });// 引用
    <WelcomeComp text={this.props.text} />
  4. 日期组件

    var DateComp = React.createClass({render: function () {return (<div className="comment-date">日期:{this.props.date}</div>);}
    });// 引用
    <DateComp date={new Date().toString()} />

最终完整代码:虽然在代码量上貌似多了点,可是这样依赖每个功能都成为了单独的一个组件,可以在任何地方使用它,而父组件中直接引用子组件,也让父组件更容易维护,修改,并且更加直观,总之好处多多。

<script type="text/babel">const rootEle = document.getElementById( 'root' );var Comment = React.createClass({render: function () {return (<div className="comment"><AvatarComp author={this.props.author} /><AuthorComp name={this.props.author.name} /><WelcomeComp text={this.props.text} /><DateComp date={new Date().toString()} /></div>);}});var AvatarComp = React.createClass({render: function () {return (<div className="user-info"><img className="avatar" src={this.props.author.avatarUrl} alt={this.props.author.name}/></div>);  }});var AuthorComp = React.createClass({render: function () {return (<div className="user-info-name">作者:{this.props.name}</div>);}});var WelcomeComp = React.createClass({render: function () {return (<div className="comment-text">问候语:{this.props.text}</div>);}});var DateComp = React.createClass({render: function () {return (<div className="comment-date">日期:{this.props.date}</div>);}});var element = <Comment author={{ avatarUrl:'./images/ygr-01.jpg', name: 'lizc' }} text='hello react.js' date={new Date().toString()}/>ReactDOM.render( element, rootEle );
</script>

2 状态和声明周期(State and LifeCycle)

2.1 状态值

React 的数据传递都是单向传递的,而对于需要变化的值,则是通过 state 对象来控制,比如下面的时钟例子,就是通过每秒更新 state 状态的值去刷新显示结果

这有个时钟计时的组件示例:

// lifecycle-state.html<script type="text/babel">var rootElement = document.getElementById( 'root' );function tick() {const element = (<div><h1>Hello world!</h1><h2>It is {new Date().toLocaleTimeString()}.</h2></div>);ReactDOM.render( element, rootElement );}setInterval(tick, 1000);</script>

结果:

Hello world!It is 下午2:10:32.

ES6 类方式

// lifecycle-state.htmlclass Tick extends React.Component {constructor(props) {super(props);this.state = {date: new Date()};}render() {return (<div><h1>Hello world!</h1><h2>It is {this.state.date.toLocaleTimeString()}.</h2></div>);}
}ReactDOM.render( <Tick />, rootElement );

2.2 生命周期

声明周期函数

  1. componentWillMount
  2. componentDidMount
  3. componentWillUpdate
  4. componentDidUpdate
  5. componentWillUnmount

下面是通过类的方式实现了时钟组件,里面使用到了声明周期中的 componentDidMountcomponentWillUnmount 加载完成和卸载完成两个周期函数

class Tick extends React.Component {constructor(props) {super(props);this.state = {date: new Date()};console.log( 'constructor got' );}// 组件渲染到DOM结束后触发componentDidMount() {this.timerID = setInterval(() => this.tick(),1000);}// 组件被卸载时触发componentWillUnmount() {clearInterval( this.timerID );  }// 时钟tick() {this.setState({date: new Date()});}render() {return (<div><h1>Hello world!</h1><h2>It is {this.state.date.toLocaleTimeString()}.</h2></div>);}
}

关于类的使用时候,针对 state 有两点需要注意:

  1. this.state 只能在构造器中去声明
  2. 如果想通过修改 this.state 来控制 UI 的显示,只能通过 this.setState 方法,而不能直接给 this.state.attr 进行赋值,直接赋值是无法达到刷新 UI 的效果的

下面是通过 React.createClass 方式实现的代码,稍微有点不同,状态对象的初始化,需要用到 getInitialState 函数


var TickComp = React.createClass({getInitialState() {return {date: new Date()};},// 组件渲染到DOM结束后触发componentDidMount: function () {this.timerID = setInterval(() => this.tick(),1000);},// 组件被卸载时触发componentWillUnmount: function () {clearInterval( this.timerID );  },// 时钟tick: function () {this.setState({date: new Date()});},render: function () {return (<div><h1>Hello world!</h1><h2>It is {this.state.date.toLocaleTimeString()}.</h2></div>);}
});ReactDOM.render( <TickComp />, rootElement );

this.state 这个保存组件状态的对象,是每个组件私有的对象,在组件外是无法直接使用的,但是可以通过和 this.props 结合使用,比如:将本组件的 this.state 通过属性来传递给子组件,然后子组件就可以通过自己的 this.props 来访问这些状态值

比如:上面的时钟,将显示部分组件化

class DisplayDateComp extends React.Component {constructor(props) {super(props);this.state = {};}render() {return (<h2>It is {this.props.date.toLocaleTimeString()}.</h2>);}
}// 在父组件中,把状态中的日期传递给 DisplayDateComp 同样能达到预期效果
<DisplayDateComp date={this.state.date} />

时钟组件一点生成,便独立存在,就算重复使用也不会互相影响,来试试创建多个时钟看看

class App extends React.Component {constructor( props ) {super( props );}render() {return (<div><TickComp /><TickComp /><TickComp /></div>);}
}

控制台输出:每个组件有自己的计时器

timerID is 1
timerID is 2
timerID is 3

3 事件处理

React 事件和 DOM 事件不同点:

  1. 时间名称,React 采用驼峰式命名(如:onChange),DOM 使用的都是小写(如:onkeydown, onkeypress, onclick
  2. React 在传递事件处理函数时不能使用字符串形式(比如:onclick="buttonClickHandler()"),只能通过 JSX 语法的函数形式传递(比如:{this.buttonClickHandler});
  3. 在 DOM 事件处理中,我们可以通过给处理函数添加 return false;去组织默认行为,但是 React 中只能通过调用 preventDefault 才行。

如果使用的是 ES6 类语法创建组件,在添加事件处理的时候需要注意一点,即 this 作用域问题,每个事件处理程序都要在构造函数里面执行一次绑定 this 操作。

类方式


class ButtonToggle extends React.Component {constructor(props) {super(props);this.state = {isToggleOn: true};this.handleClick = this.handleClick.bind( this );}handleClick() {console.log( this );this.setState(prevState => ({ isToggleOn: !prevState.isToggleOn }));}render() {return (<button onClick={this.handleClick}>{this.state.isToggleOn ? 'ON' : 'OFF'}</button>);}
}

ButtonToggle 组件中,如果不在构造器中添加

this.handleClick = this.handleClick.bind( this );

这一句, 通过打印可知在点击的时候 handleClick 里面的 this 打印出来的是 ‘undefined’,会导致报错。

官文中提供了两种解决方法:

  1. 事件函数声明时用:箭头函数定义

    比如:

    handleClick = () => { console.log( this ); }

    这中写法会报错 ‘Uncaught SyntaxError: embedded: Unexpected token (20:14)’,是上面的写法有什么问题?

    官文注释中有这么一句 // Warning: this is *experimental* syntax. 不知道是不是目前还不支持在类里面这么使用。

    然后尝试了下 把它移到构造器里面去,运行正常

    constructor(props) {super(props);this.state = {isToggleOn: true};this.handleClick = (e) => {console.log( this );this.setState(prevState=> ({ isToggleOn: !prevState.isToggleOn }));}// this.handleClick = (e) => console.log( this )// this.handleClick = this.handleClick.bind( this );
    }
    

    让其成为该类的成员,说明成员的声明必须要放到构造函数里面去。

  2. 在绑定事件时使用箭头函数

    onClick={ (e) => this.handleClick(e) }

    这种方式如果是在把事件处理函数传递给子组件的时候,会被执行两次;

    针对这个尝试了下面的 例子

    
    class ButtonToggle extends React.Component {constructor(props) {super(props);this.state = {isToggleOn: true};// this.handleClick = this.handleClick.bind( this );}componentDidUpdate(prevProps, prevState) {console.log( 'father update' );}handleClick(e) {console.log( this );this.setState(prevState => ({ isToggleOn: !prevState.isToggleOn }));}// 这里如果直接传递 clickHandler={ this.handleClick },会报错// 'Uncaught TypeError: Cannot read property 'setState' of undefined'// 这个报错和上面是一样的,这里的 this 已经发生了改变render() {return (<Button clickHandler={ (e) => this.handleClick(e) } isToggleOn={this.state.isToggleOn} />);}
    }class Button extends React.Component {constructor(props) {super(props);}componentDidUpdate(prevProps, prevState) {console.log( 'son update' );}render() {return (<button onClick={ this.props.clickHandler  }>{this.props.isToggleOn ? 'ON' : 'OFF'}</button>);}
    }ReactDOM.render(<ButtonToggle />,rootElement
    );

    <Button clickHandler={ (e) => this.handleClick(e) } isToggleOn={this.state.isToggleOn} />

    上面通过箭头函数传递下去的时候,this 对象从控制台输出看是指向 ButtonToggle 的。

    这是控制台,点击按钮时候的输出:

    
    // 这个输出就是 this
    ButtonToggle {props: Object, context: Object, refs: Object, updater: Object, state: Object…}// Button 组件更新完成
    son update// ButtonToggle 组件更新完成
    father update

    用上面的方式去实现,貌似并没有官文中说的被重复渲染,有点不太明白,疑问?????

4 条件渲染(Conditional Rendering)

条件渲染其实就是利用 ifif ... else? ... : 来根据状态值控制组件是否需要被渲染

前面就不介绍了,直接看个这节综合点的示例(官文中最后一个示例)

点击按钮控制警告信息是否显示


// event-handle.html<script type="text/babel">var rootElement = document.getElementById( 'root' );function WarningComp( props ) {// 根据属性值来决定是否渲染该组件if ( !props.warn ) { return null; }return (<div className="warning">Warning!</div>);
}class Page extends React.Component {constructor(props) {super(props);this.state = {isShowWarning: true};this.clickHandle = this.clickHandle.bind( this );}clickHandle( e ) {// 取得上一次状态值,返回其取反值this.setState(prevState => ({ isShowWarning: !prevState.isShowWarning }));}render() {return (<div><WarningComp warn={this.state.isShowWarning} /><button onClick={ this.clickHandle }>{this.state.isShowWarning ? 'Hide' : 'Show'}    </button></div>);}
}ReactDOM.render(<Page />,rootElement
);</script>

上例中通过添加条件 if ( !props.warn ) 来决定是否需要渲染 Warning 组件。

另外,组件直接返回 null 并不会影响组件的声明周期,其实可以这么理解 Warning 组件并非不存在而是该组件中并没有任何需要被渲染到 DOM 树中的内容,因此不需要执行渲染部分,但是该组件还是存在的,既然组件存在那就会存在器声明周期,可能通过在组件内部添加声明周期函数来测试

把上面Warning 组件稍做修改下

// condition-render.htmlclass WarningComp extends React.Component {constructor(props) {super(props);}componentWillMount() {console.log( 'Warning component will mount' );}componentDidMount() {console.log( 'Warning component did mount' );}componentWillUpdate(nextProps, nextState) {console.log( 'Warning component will update' );}componentDidUpdate(prevProps, prevState) {console.log( 'Warning component did update' );}componentWillUnmount() {console.log( 'Warning component will unmount' );}render() {if ( !this.props.warn ) { return null; }return (<div className="warning">Warning!</div> );}
}

默认显示警告:

打开页面时首先输出,这里组件并不是 return null

Warning component will mount
Warning component did mount

接着按 Hide,发现输出的是 update

Warning component will update
Warning component did update

再按 Show,同样输出的是 update 过程

Warning component will update
Warning component did update

从上面输出说明,首先加载,加载过后,只有更新因为组件一开始就已经被加载了,后面只是更改了 warn 状态值,决定是否需要把需要渲染的 DOM 元素渲染到 DOM 树中

默认不显示警告,return null;

// 页面加载完成时控制台输出
Warning component will mount
Warning component did mount// 点击 Show 输出
Warning component will update
Warning component did update// 点击 Hide 输出
Warning component will update
Warning component did update

从上面的测试结果说明一点:组件只要一旦创建它就会存在,并且具备组件该有的生命周期,和 render 里面是否有具体的 DOM 元素返回无关,直接返回 null 也同样会执行生命周期函数。

5 列表和键(Lists and Keys)

https://facebook.github.io/react/docs/lists-and-keys.html

列表简单示例:

const numbers = [1, 2, 3, 4, 5, 6];function NumberListComp( props ) {const numbers   = props.numbers;const listItems = numbers.map((number) => <li>{number}</li>);  return (<ul>{listItems}</ul>);
}

上面组件渲染结果:

<ul data-reactid=".0"><li data-reactid=".0.0">1</li><li data-reactid=".0.1">2</li><li data-reactid=".0.2">3</li><li data-reactid=".0.3">4</li><li data-reactid=".0.4">5</li><li data-reactid=".0.5">6</li>
</ul>

从上面可知在没有添加 key 的情况下生成的 DOM 元素及属性

下面将针对 <li> 元素和 li 组件之上添加 key 做实验,给组件添加 key 是为了让 react 更好的发现哪个元素被修改,添加或删除了。添加的结果会直接表现在 data-reactid 之上,来验证下结果:

直接在 <li> 元素之上添加 key:

code:

function NumberListComp( props ) {const numbers   = props.numbers;const listItems = numbers.map(// 这里直接添加 key 属性(number) => <li key={number}>{number}</li>);  return (<ul>{listItems}</ul>);
}

DOM 结果:

<ul data-reactid=".0"><li data-reactid=".0.$1">1</li><li data-reactid=".0.$2">2</li><li data-reactid=".0.$3">3</li><li data-reactid=".0.$4">4</li><li data-reactid=".0.$5">5</li><li data-reactid=".0.$6">6</li>
</ul>

从上面结果可知,添加在 <li> 元素上的 key 属性,并没有直接以 key=number 形式出现在实际元素属性列表中,而是改变了 data-reactid 属性的值,并且可以发现

如果不添加 key 时候,data-reactid 的值就是 .0. + id 形式,id 从 0 开始依次递增

如果添加了 key 时候,data-reactid 会在 .0. 后面附加一个 $ 符号再加上我们自己定义的 id 也就是后面的 1 ~ 6;

但是直接将 key 属性添加到 <li> 元素上并不是最好的解决办法,如果将 <li> 作为一个单独的组件来处理,key 应该位于组件之上去添加,其实这个也好理解,<li> 本身是 DOM 元素,既然这样就应该让其保持其原有的特征和属性,更何况组件是需要被复用的,当直接把 key 限定死到 <li> 之上,那当被复用到其他地方的时候 key 值的定义会变得容易混淆,而直接添加到组件之上就简单了,哪里用到组件,赋予该组件具体的属性即可,而不用关系组件里面的具体实现。

下面是将 <li> 单独作为组件分离出来,将 key 添加到组件引用时的属性之上的示例

function ListItem( props ) {return (<li>{props.value}</li>);
}function List( props ) {const numbers = props.numbers;const listItems = numbers.map( (number) => <ListItem key={number.toString()} value={number} /> );return (<ul>{listItems}</ul>    );
}

DOM 结果:

<ul data-reactid=".0"><li data-reactid=".0.$1">1</li><li data-reactid=".0.$2">2</li><li data-reactid=".0.$3">3</li><li data-reactid=".0.$4">4</li><li data-reactid=".0.$5">5</li><li data-reactid=".0.$6">6</li>
</ul>

从结果上看和直接添加到 <li> 之上是一样的,但从组件概念上,在引用组件时指定更实用,让组件更通用点,毕竟组件的属性只有在引用时才需要指定。

6 Form 表单

HTML 表单元素和 React 中的表单元素有一点点区别,这是因为表单里面的元素往往都有自己的一些内部状态,或者说有自己的默认行为,比如下面的表单:

<form action="#"><label>Name:<input type="text" name="name" /></label><input type="submit" value="Submit" />
</form>

上面的示例中在点击 submit 之后会有默认的表单提交动作,这行为在 react 中也是可行的,但更好的做法是放弃默认行为,而是利用函数去绑定事件,收集到表单中的数据然后进一步处理之后再进行提交。

控制组件

表单中有写输入类型的元素的状态值改变都是基于用户输入的,而在 React 当中是通过数据驱动 UI 刷新,而状态值被保存在 Reactthis.state 对象当中,这个对象是基于组件的,也就是说是组件内部对象,组件外是无法直接访问的,当状态值发生改变时或者调用 this.setState 设置状态值之后,React 内部会执行刷新 UI ,从而做到数据驱动UI更新。

下面来使用 React 状态值来改造上面的示例,主要有两个修改

  1. 添加状态值
  2. 绑定事件并阻止默认行为

类方式:(如果使用 React.createClass,需要用到 getInitialState 来设置状态值对象)

<script type="text/babel" >const rootEle = document.getElementById( 'root' );class NameForm extends React.Component {constructor(props) {super(props);this.state = {nameValue: 'lizc'};// 绑定 this 作用域this.handleSubmit   = this.handleSubmit.bind( this );this.handleName     = this.handleName.bind( this ); }handleSubmit( event ) {alert( 'submit name: ' + this.state.nameValue );// 阻止表单默认提交行为event.preventDefault();}handleName( event ) {this.setState({nameValue: event.target.value});}render() {return (<form onSubmit={this.handleSubmit}><label>Name:<input type="text" value={this.state.nameValue} onChange={this.handleName} /></label><input type="submit" value="Submit" /></form>);}}ReactDOM.render(<NameForm />,rootEle);</script>
  • 文本输入框(texttextarea

    从上面 React 代码看,输入框的输入事件被绑定到了 handlerName ,而这里面只是修改了 this.state 点击提交,会在 handleSubmit 里面收集到表单数据,并阻止默认行为。

    value={this.state.nameValue} 这句可知,输入框的输入内容已经被绑定到了状态值中的 nameValue,因此在通过 setState 去改变其值的时候输入框内容也会发生变化,下面添加另一个输入框试试

    直接复制一个输入框即可:

    <label>Name:<input type="text" value={this.state.nameValue} onChange={this.handleName} /><input type="text" value={this.state.nameValue} onChange={this.handleName} />
    </label>

    通过修改任何一个输入框内容另一个输入框内容也会随之发生变化,这是因为两个输入框的 value 都被绑定到了状态值 nameValue ,而此时输入框的变化事件为:handleName,其中又是设置了 nameValue,根据状态值驱动 UI 变化,因此两个输入框内容会同步更新。

    如图:

    状态值驱动UI更新

    文本区 textareatext 元素

  • 选项(select

    在 HTML 元素的表单选项元素中,其状态值的改变是通过 selected 属性值来设置的,哪个被选中哪一项会被赋予 selected 属性,或者 selected=true,而在 react 中只需要在其顶部元素 <select> 标签中添加状态值即可实现切换。

    针对示例做修改:

    1. 添加选择元素
    2. 给选择元素添加 value 属性和 onChange 事件
    3. handleSubmit 中添加数据集合,并打印出来

    代码:

    // 修改点 3handleSubmit( event ) {let datas = {name: this.state.nameValue,color: this.state.selectedColor};alert( JSON.stringify( datas ) );event.preventDefault();
    }// 修改点 2
    handleSelect( event ) {this.setState({selectedColor: event.target.value});
    }this.state = {nameValue: 'lizc',selectedColor: 'blue'  // 添加选择项状态值
    };// 修改点 1
    render() {return (<form onSubmit={this.handleSubmit}><label>Name:<input type="text" value={this.state.nameValue} onChange={this.handleName} /></label><br /><label>Select Your Color: <select value={this.state.selectedColor} onChange={this.handleSelect}><option value="red">red</option><option value="orange">orange</option><option value="yellow">yellow</option><option value="green">green</option><option value="blue">blue</option></select></label><br /><input type="submit" value="Submit" /></form>);
    }
    

    结果:

    添加选择项

    上面示例说明了列表选项中,只需要给 <select><option> 标签添加 value 属性,然后将 <select>value 绑定状态值 this.state.selectedColor ,即可。

    另外,官方提供了个绑定表单事件的统一接口的实现思路,其思路就是通过给元素添加 name 属性,而这个 name 属性值与 this.state 中的表单元素对应的状态值要同名,然后在事件处理函数中获取到目标事件的 target.name 以及 target.value 值来统一更新表单中多个元素的值;

    代码:

    // form-name.htmlclass NameForm extends React.Component {constructor(props) {super(props);this.state = {isGoing: true,numberOfGuests: 3};this.handleSubmit   = this.handleSubmit.bind( this );this.handleInputChange = this.handleInputChange.bind( this );}handleSubmit( event ) {let datas = {isGoing: this.state.isGoing,number: this.state.numberOfGuests};alert( JSON.stringify( datas ) );event.preventDefault();}handleInputChange( event ) {const target  = event.target;const value   = target.type === 'checkbox' ? target.checked: target.value;const name    = target.name;// 这个元素状态改变的事件处理里面// 通过获取 name 和 value 属性来设置状态值// 这里有个前提:name 属性名必须和 this.state 里面对应元素的// 状态值字符串对应this.setState({[name] : value});// 这里设置对象属性时候用到了 [],这属于 ES6 的新语法// 表示属性值是由 [] 中的表达式计算结果而来,比如:// [ 'name' + i++ ]  => 如果是在循环中,属性名会以 'name0' -> 'namen'// 格式被赋值,而最后对象的属性名即计算后的字符串:('name0', ..., 'namen')}render() {return (<form onSubmit={this.handleSubmit}><label>Name:<input type="checkbox" name="isGoing" value={this.state.isGoing} onChange={this.handleInputChange} /></label><br /><label>Number of guests: <input type="text" name="numberOfGuests" checked={this.state.numberOfGuests} onChange={this.handleInputChange} /></label><br /><input type="submit" value="Submit" /></form>);}
    }
    

    handleInputChange 中使用了一个 ES6 新语法,上面有简单描述,详情可查看:计算属性名。

    上面的实现方式,是使多个表单元素绑定到同一个事件处理函数中,可避免重复定义事件处理函数,但需要注意的一点就是,用这种方式去实现,需要搭配 name 属性来使用。

最后,表单元素的事件处理,还可以通过 ref 对象和 defaultValue 来实现,这种方式更简单,后面在具体进行介绍。

7 状态值提升(Lifting State Up)

从文档中看,这节有点像是共享状态值的意思,其实也就是类似上面两个 input 中,任一个元素的更新会让另一个元素也随之更新,从示例中只是多添加了一层不同的计算方式。

这个是官网上温度转换的例子:


<script type="text/babel">const root = document.getElementById( 'root' );const scaleNames = {'f': 'Fahrenheit','c': 'Celsius'};function toCelsius( fahrenheit ) {return (fahrenheit - 32) * 5 / 9;}function toFahrenheit( celsius ) {return ( celsius * 9 / 5) + 32;}function tryConvert(temperature, convert) {const input = parseFloat(temperature);if ( Number.isNaN(input) ) {return '';}const output = convert(input);const rounded = Math.round( output * 1000 ) / 1000;return rounded.toString();}function BoilingVerdict( props ) {if ( props.celsius >= 100 ) {return <p>The water would boil.</p>;} return <p>The water would not boil.</p>;}class Calculator extends React.Component {constructor(props) {super(props);this.state = {temperature: '',scale: 'c'};this.handleCelsiusChange = this.handleCelsiusChange.bind(this);this.handleFahrenheitChange = this.handleFahrenheitChange.bind(this);}handleCelsiusChange( temperature ) {this.setState({temperature: temperature,scale: 'c'});}handleFahrenheitChange( temperature ) {this.setState({temperature: temperature,scale: 'f'});}render() {const scale = this.state.scale;const temperature = this.state.temperature;const celsius = scale === 'f' ? tryConvert( temperature, toFahrenheit ): temperature;const fahrenheit = scale === 'c'? tryConvert( temperature, toCelsius ): temperature;return (<div><TemperatureInput scale='c' temperature={celsius} onTemperatureChange={this.handleCelsiusChange} /><TemperatureInput scale='f' temperature={fahrenheit} onTemperatureChange={this.handleFahrenheitChange} /><BoilingVerdict celsius={parseFloat(celsius)} /></div>);}}class TemperatureInput extends React.Component {constructor(props) {super(props);this.handleChange = this.handleChange.bind(this);}handleChange( event ) {this.props.onTemperatureChange( event.target.value );}render() {const temperature = this.props.temperature;const scale = this.props.scale;return (<fieldset><legend>Enter temperature in : {scaleNames[scale]} </legend><inputvalue={temperature} onChange={this.handleChange}/></fieldset>);}}ReactDOM.render(  <Calculator />,root);
</script>

下面看下大小写转换的两个输入框,进行对比下(根据官网上是温度转换的原理实现的)

<script type="text/babel">const root = document.getElementById( 'root' );// 这个是用来说明的文字对象const alphaCases = {'u': 'upper','l': 'lower'}/* 字符输入框组件这个组件的数据都来源于引用时传递进来的属性 this.props其状态改变事件,绑定到外部传入的的事件函数,用来控制当前输入框类型和单词*/class AlphaInput extends React.Component {constructor(props) {super(props);this.inputChange = this.inputChange.bind(this);}inputChange( event ) {this.props.onInputChange( event.target.value );}render() {return (<label>{alphaCases[this.props.alphaCase]}case: <input type="text" value={this.props.value} onChange={this.inputChange} /></label>);}}class Input extends React.Component {constructor(props) {super(props);this.state = {word: '',alphaCase: 'l'   // 标识着当前焦点框是大写还是小写,很重要的属性};this.lowerCaseChange = this.lowerCaseChange.bind(this);this.upperCaseChange = this.upperCaseChange.bind(this);}// 小写输入框的输入事件,这里转成小写再设置,避免在其内输入大写的情况lowerCaseChange( lower ) {this.setState({word: lower.toLowerCase(),alphaCase: 'l'});}upperCaseChange( upper ) {this.setState({word: upper.toUpperCase(),alphaCase: 'u'});}render() {// 记录发生变化后的单词const word = this.state.word;// 记录当前焦点框const alphaCase = this.state.alphaCase;console.log( alphaCase )// 小写框的内容,如果当前焦点不在小写输入框,说明该框内容需要被动发生变化// 即:alphaCase 为 'u' 时候,将单词小写化设置到小写框内const lower = alphaCase === 'u' ? word.toLowerCase() : word;// 大写框内容同上,这个时候的 word 其实是小写框内输入的单词,经过转换成// 大写到大写框内const upper = alphaCase === 'l' ? word.toUpperCase() : word;console.log( lower )console.log( upper )// 最后将得到的 lower 和 upper 小写和大写框内的内容更新到 UI 中/* 这里有几个关键变量1. word,这个变量,即当前焦点框中变化的内容;2. alphaCase,该值标识着当前焦点在哪个输入框,这个值由输入框的输入事件触发修改,onInputChange -> onChange3. 另外需要注意的是输入框组件的输入事件其实是绑定到了,组件属性中的 onInputChange,这样一来就可以将 input 组件内容的变化来更新当前组件的状态值,从而刷新UI。*/return (<div><AlphaInput alphaCase='l' value={lower} onInputChange={this.lowerCaseChange} /><br /><AlphaInput alphaCase='u' value={upper} onInputChange={this.upperCaseChange} /></div>);}}ReactDOM.render( <Input />, root );</script>

效果如下:

状态绑定

其实最终能同时更新,最基本的原来就是共享了同一个状态值 word ,然后在渲染之前对该值做了对应的转换,变成各自所需要的值,刷新到 UI。

8 组合与继承(Composition and Inheritance)

React 有一个功能强大的组合模型,并且推荐组件与组件之间通过使用组合来复用代码。这节将介绍使用继承时所遇到的问题,以及如何使用组合来避免这些问题。

[NOTHING]

9 结束示例

在基础篇完成最后,有个用 react 组件化完成的小示例

效果图

产品价格表

如图中显示,将价格表分割成几个部分

产品表组件分割图

从图中分割出各组件如下:

  1. 顶级组件,容器:ProductFilterTable
  2. 搜索框(包含输入框,选项,文字):ProductSearchBar
    • 输入框组件:SearchInput
    • 库存选项组件:StockCheckBox
  3. 产品列表(或价格表)组件:ProductPriceTable
    • 列表标题
    • 产品类表:ProductLists
      • 产品分类标题:ProductRowTitle
      • 产品行:ProductRow

分节目录:

  • 组件定义
  • 共享状态
  • 容器组件修改
  • 搜索框组件修改
  • 输入框组件修改
  • 库存复选框组件修改
  • 产品价格表
  • 分类表
  • 分类标题和行
  • 产品过滤器
  • 最终效果
  • 完整代码

下面根据上面的划分,来进行代码实现

  • 组件定义

    将上面的所有组件都先定义好,如下

    // 1. 容器组件
    class ProductFilterTable extends React.Component {render() {return (<div className="pd-container"><ProductSearchBar />    // 包含一个搜索框组件<ProductPriceTable />   // 包含一个价格表组件</div>);}
    }// 2. 搜索框组件
    class ProductSearchBar extends React.Component {render() {return (<div className="pd-search"><SearchInput />         // 包含一个搜索输入框<StockCheckBox />       // 包含一个多选项组件</div>);}
    }// 2.1 搜索输入框
    class SearchInput extends React.Component {render() {return (<input className="pd-search-input" type="search" />);}
    }// 2.2 多选项组件
    class StockCheckBox extends React.Component {render() {return (<form className="pd-checkbox"><input type="checkbox" /><label>Only show products in stock.</label></form>);}
    }// 3 产品价格表
    class ProductPriceTable extends React.Component {render() {return (<div className="pd-price-tbl"><table><thead><tr><th>Name</th><th>Price</th></tr>        // 表标题</thead><ProductLists category="Sporting Goods" />  // 运动产品表<ProductLists category="Electronics" />     // 电子产品表</table></div>);}
    }// 3.1 产品分类表
    class ProductLists extends React.Component {render() {return (<tbody><ProductRowTitle title={this.props.category} />{list}  // 产品行,根据数据生成的多行</tbody>);}
    }// 3.1.1 产品分类标题
    class ProductRowTitle extends React.Component {render() {return(<tr><td colSpan="2" className="pd-title">{this.props.title}</td></tr>);}
    }// 3.1.2 产品行
    class ProductRow extends React.Component {render() {return (<tr><td>{this.props.name}</td><td>{this.props.price}</td></tr>);}
    }
    
  • 共享状态

    因为涉及到,输入框内容变化需要动态刷新产品表,那么这里就需要让输入框的变化和产品表刷新发生联系,在 React 都是通过状态来驱动 UI 刷新。可是输入框和产品列表都是独立的组件,不可能直接通过一个状态值来传递信息,而其共同点都在容器之下,所以需要在顶级容器中定一个能让输入框和产品表发生联系的状态,如图:

    状态共享

  • 容器组件修改:

    class ProductFilterTable extends React.Component {constructor(props) {super(props);this.state = {searchText: '',stockChecked: false};this.searchTextChange = this.searchTextChange.bind(this);this.stockCheckboxChange = this.stockCheckboxChange.bind(this);}// 1. 添加共享状态的设置,这个设置由输入框组件的输入事件触发searchTextChange( searchText ) {this.setState({searchText: searchText})}// 2. 多选框的事件处理,由搜索框中的  StockCheckBox 组件选中事件触发stockCheckboxChange( checked ) {this.setState({stockChecked: checked});}render() {return (<div className="pd-container">// 3. 这里将输入框和多选框事件处理函数往下传递给搜索框组件<ProductSearchBar onSearchTextChange={this.searchTextChange}onStockCheckboxChange={this.stockCheckboxChange}stockChecked={this.state.stockChecked} searchText={this.state.searchText} />// 4. 这里将共享状态值传递给产品表,通知其更新产品列表<ProductPriceTable stockChecked={this.state.stockChecked} searchText={this.state.searchText} /></div>);}
    }
    

    容器组件的修改主要如代码中 4 个地方,绑定输入框和多选框的事件,让输入框和多选框事件能够触发容器组件中的共享状态值发生改变(searchTextstockChecked),然后把这两个值传递给价格表组件,价格表组件会根据状态值的改变而刷新UI显示,从而筛选出查找的产品信息。

  • 搜索框组件修改

    class ProductSearchBar extends React.Component {render() {return (<div className="pd-search">// 1. 输入框输入事件处理函数传递给输入框组件<SearchInput onSearchTextChange={this.props.onSearchTextChange} searchText={this.props.searchText} />// 2. 多选框事件处理函数传递给多选框组件<StockCheckBox stockChecked={this.props.stockChecked} onStockCheckboxChange={this.props.onStockCheckboxChange}/></div>);}
    }

    搜索框的修改主要就是透传来自容器组件 ProductFilterTable 的输入框和多选框事件处理函数。

  • 输入框组件修改

    class SearchInput extends React.Component {constructor(props) {super(props);this.searchInputChange = this.searchInputChange.bind(this);}searchInputChange( e ) {this.props.onSearchTextChange( e.target.value );}render() {return (<input className="pd-search-input" type="search" value={this.props.searchText} onChange={this.searchInputChange}/>);}
    }

    输入框组件的修改,主要涉及两点

    第一:将输入框的输入事件处理函数 searchInputChange 绑定到容器组件中定义的输入框事件处理函数 this.props.onSearchTextChange,以便修改共享状态值 searchText
    第二:将输入框的 value 值指定为 this.props.searchText 共享状态值,这样输入框的内容就完全与容器组件中的 searchText 状态绑定了。

  • 库存复选框组件修改

    
    class StockCheckBox extends React.Component {constructor(props) {super(props);this.stockChange = this.stockChange.bind(this);}// 1. 添加复选框选中事件,并绑定到容器组件中的事件处理,从而更新共享状态值:stockCheckedstockChange( e ) {this.props.onStockCheckboxChange( e.target.checked );}render() {return (<form className="pd-checkbox">// 2. 给 input 元素添加事件处理,并将 checked 属性与共享状态值 stockChecked 进行绑定<input type="checkbox" style={{verticalAlign: "middle"}} checked={this.props.stockChecked}onChange={this.stockChange}/><label style={{verticalAlign: "middle"}}>Only show products in stock.</label></form>);}
    }

    对于库存复选框的修改类似搜索输入框,主要也是事件添加和状态值绑定。

    PS: 复选框和说明文字中间居中,只要针对两个元素都设置 verticalAlign: 'middle' 即可对齐。

  • 产品价格表

    class ProductPriceTable extends React.Component {render() {return (<div className="pd-price-tbl"><table><thead><tr><th>Name</th><th>Price</th></tr></thead>// 修改点:将 this.props.searchText 和 this.props.stockChecked// 分别透传给分类表组件<ProductLists category="Sporting Goods"searchText={this.props.searchText}stockChecked={this.props.stockChecked}/><ProductLists category="Electronics"searchText={this.props.searchText}stockChecked={this.props.stockChecked}/></table></div>);}
    }

    这个价格表组件也只是个容器,其中涉及修改也只是将一些属性透传给产品分类表组件(ProductLists

    从上面价格表实现看,其中还可以进行优化,因为产品的种类实际肯定不止两种,如果直接使用 ProductLists 写死,不太现实,可以直接在 ProductPriceTable 组件中对数据进行操作,把所有分类获取到,然后统一获取列表数据

    修改如下:

    class ProductPriceTable extends React.Component {render() {const searchText    = this.props.searchText;const stockChecked  = this.props.stockChecked;const categories    = [];// 1. 将数据中的分类全部过滤出来for ( let i = 0, len = products.length; i < len; i++ ) {let category = products[i].category;if ( categories.indexOf( category ) === -1 ) {categories.push( category );}}// 2. 然后根据所有分类决定创建多少个分类表let productListComps = categories.map(function (category, index) {return (<ProductLists key={category + index}category={category}searchText={searchText}stockChecked={stockChecked}/>);});return (<div className="pd-price-tbl"><table><thead><tr><th>Name</th><th>Price</th></tr></thead>{productListComps}</table></div>);}
    }
    

    经过上面的优化,不管数据中有多少个分类,都不需要手动去添加分类表组件。

  • 分类表

    
    class ProductLists extends React.Component {render() {/*分类表的修改,主要就是根据类别名称从数据中获取到全部产品信息,然后组成产品列表*/const searchText = this.props.searchText;// 这里将所有满足条件的产品全部过滤出来了// 条件有二: 1. 搜索框内容; 2. 库存复选框const sportGoods = getCategoryList( this.props.category, searchText, this.props.stockChecked );const prefix = this.props.category;// 再没有任何搜索产品时的友好提示if ( sportGoods.length <= 0 ) { return (<tbody><ProductRowTitle title={this.props.category} /><tr><td colSpan="2">no results</td></tr></tbody>); }// 得到过滤后的所有产品列表const list = sportGoods.map(function (product, index) {return (<ProductRow key={prefix + index} name={product.name} price={product.price} />);});return (<tbody><ProductRowTitle title={this.props.category} />{list}</tbody>);}
    }

    分类表的职责就是从数据中根据分类名查找出所有的产品,然后渲染到列表中。

  • 分类标题和行

    产品行:

    class ProductRow extends React.Component {render() {return (<tr><td>{this.props.name}</td><td>{this.props.price}</td></tr>);}
    }
    

    标题:

    class ProductRowTitle extends React.Component {render() {return(<tr><td colSpan="2" className="pd-title">{this.props.title}</td></tr>);}
    }

    这两个其实就很简单调了,拿到标题,产品名称和价格显示出来即可。

  • 产品过滤器

    
    function getCategoryList( category_name, product_name, stocked ) {if ( !category_name ) { return null; }const isPrice = /^\$/.test(product_name);let filterProducts = products;// 过滤掉无库存的产品if ( typeof stocked !== "undefined" && stocked ) {filterProducts = filterProducts.filter(function (product) {return product.stocked === true;})}// 过滤出要查找的分类产品const productsFilterByCategory = filterProducts.filter(function (product) {return product.category === category_name;});if ( !product_name ) { return productsFilterByCategory; }else { return productsFilterByCategory.filter(function  (product) {/* 如果是以 '$' 开头,表示按价格查找将库存中产品名和要查找的产品关键词都转成小写再进行比较以忽略大小写查找*/if ( isPrice ) { return product.price.indexOf( product_name ) >= 0; }let productNameLower = product.name.toLowerCase();let findNameLower = product_name.toLowerCase();return productNameLower.indexOf( findNameLower ) >= 0; });}
    }
    

    getCategoryList 这个函数主要负责数据的操作,通过传入的参数,来决定是否需要进行过滤,第二个参数 product_name 就是搜索框中输入的关键字,第三个乃库存复选框。

    该函数可实现以下功能:

    1. 根据输入的关键字过滤出包含关键字的产品名的产品列表;
    2. 如果搜索框输入内容以 $ 美元符号开头,可以根据价格进行过滤;
    3. 库存复选框的状态可以决定是否显示已库存的产品;
  • 最终效果

产品价格表

  • 完整代码

完整代码地址:Product Search go✈

10 总结

其实以前零零散散的接触过一些 react 的东西,但大多都是逛论坛网站时候接触到的,所以下定决心自己来通过官方文档来系统性的去学习下。

这篇比较长,本来想分章节来记录,可是想想如果分章节,中间容易出现学习断层,加上有些知识点也并不是很深入,毕竟这篇还是属于基础篇,所以索性将基础的东西集中在一起来学习了,花了几天时间今天结束基础篇,通过最后的示例对于 react 的基础知识多少有了点掌握,接下来将会学习并记录 react 高级部分(也就是官文中的 ‘ADVANCED GUIDES’ 部分),进行进一步的深入学习。

React-基础教程完整版相关推荐

  1. python3基础教程pdf下载-廖雪峰Python3基础教程完整版电子书免费下载

    Python 教程 这是小白的 Python 新手教程,具有如下特点: 中文,免费,零起点,完整示例,基于最新的 Python 3 版本. Python 是一种计算机程序设计语言.你可能已经听说过很多 ...

  2. 你真的了解Java系统启动流程吗?java基础教程完整版

    摘要 Apache Kafka是一个分布式消息发布订阅系统.它最初由LinkedIn公司基于独特的设计实现为一个分布式的提交日志系统( a distributed commit log),之后成为Ap ...

  3. React-高级教程完整版

    React 高级教程完整版 这标题可能有点不太贴切或符合内容,从官方上来区分这部分内容确实属于高级部分,只是由于个人原因,在后面的一些章节并没有记录在列. 也为了承接上一篇,因此勉强将标题定位:&qu ...

  4. JavaScript(基础、高级)笔记汇总表【尚硅谷JavaScript全套教程完整版】

    目   录 前言 JavaScript(基础+高级)配套资料下载 JavaScript 基础 学习地址 学习笔记 day 05(P001-P006)[2016.11.22] day 06(P007-P ...

  5. 尚硅谷最新版JavaScript基础全套教程完整版(p79-p90)

    尚硅谷最新版JavaScript基础全套教程完整版(140集实战教学,JS从入门到精通) 一.函数的方法 1.call()和 apply()方法 -这两个方法都是函数对象方法,需要通过函数对象来调用 ...

  6. python入门教程完整版(懂中文就能学会)-Python入门教程完整版(懂中文就能学会)...

    不过小编的内心是强大的,网友虐我千百遍,我待网友如初恋,因为今天又给大家带来了干货,Python入门教程完整版,完整版啊!完整版! 言归正传,小编该给大家介绍一下这套教程了,希望每个小伙伴都沉迷学习, ...

  7. android+客户端+教程,Android新浪客户端开发教程完整版.pdf

    Android新浪客户端开发教程完整版 Android 新浪客户端开发教程新浪客户端开发教程 (完整版(完整版)) 新浪客户端开发教程新浪客户端开发教程 ((完整版完整版)) android开发我的新 ...

  8. 阿哈c语言教程pdf,C++教程-完整版.pdf

    C教程-完整版 c++基础教程Beta 版 原作: Juan Soulie 翻译: Jing Xu (aqua) 英文原版 本教程根据Juan Soulie 的英文版c++教程翻译并改编. 本版为最新 ...

  9. python新手教程全套_Python入门教程完整版(懂中文就能学会)

    前几天给大家分享视频<python基础教程>受到了广泛的关注,有人不知道怎么领取,居然称小编为"骗子". 不过小编的内心是强大的,网友虐我千百遍,我待网友如初恋,因为今 ...

  10. Python入门教程完整版

    今天本宝宝给大家带来了干货,Python入门教程完整版,完整版啊!完整版! 言归正传,我来给大家介绍一下这套教程,希望每个小伙伴都沉迷学习,无法自拔! 本套教程学习时间15天 1-3天内容:为Linu ...

最新文章

  1. 获取Java系统相关信息
  2. mysql 5.7优化不求人_《MySQL 5.7优化不求人》直播精彩互动
  3. confirm修改按钮文字_踏入MG动画设计的门,才知道文字动画这么重要……
  4. 挑战Unity、UE4,曾戈祭出次世代VR引擎
  5. 排列组合算法之二: 01转换法_java改变后的c++改进版
  6. 深度学习分类pytorch_立即学习AI:02 —使用PyTorch进行分类问题简介
  7. TLV3501超高速电压比较器模块
  8. adb shell 小米手机_【ADB命令实战】免ROOT停用小米手机系统应用
  9. 计算机数字键盘如何用,电脑小数字键盘不能使用的原因和解决方法
  10. 2023蓝牙耳机性价比推荐:高品质蓝牙耳机盘点分享
  11. java计算机毕业设计计算机专业在线学习评估软件-演示录像-源程序+mysql+系统+lw文档+远程调试
  12. python实现图像的白平衡,破坏图像的白平衡(冷、暖)和调节图像的亮度
  13. 安装linux提示没有系统盘,我有RedHat安装光盘,但没有安装软盘, 应怎么办?linux安装...
  14. FCRP-D---帆软官网模拟题,tomcat模块
  15. 数据化管理:洞悉零售及电子商务运营 | 学习笔记 (全)
  16. 批量替换文本中的多组字符串
  17. Redis Geo 实践
  18. Adams中改变模型位置的方法
  19. 芜湖光华学校优选云盒子教育云盘,自建专属数据中心
  20. NVIDIA 7th SkyHackathon(五)图像数据集的处理

热门文章

  1. 亚马逊卖杯子类目需要办理FDA认证介绍
  2. AutoJS4.1.0实战教程 ---百度极速版
  3. 天涯明月刀手游服务器维修,《天涯明月刀》12月30日服务器例行维护介绍
  4. 2019CSP-S广州3日游
  5. DIY你的QQ登陆器
  6. Linux初级篇——vi和vim的区别
  7. 今年北京迎入汛以来最强降雨雨下得有多大
  8. 【报错记录】RuntimeError: don‘t know how to restore data location of torch.FloatStorage (tagged with CPU)
  9. c语言经典100例c33 打印简单心形
  10. 2022.4.19百度笔试记录