# react_learn **Repository Path**: shafish/react_learn ## Basic Information - **Project Name**: react_learn - **Description**: facebook 开源前端框架学习记录 - **Primary Language**: Unknown - **License**: CC-BY-SA-4.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2019-09-20 - **Last Updated**: 2020-12-19 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # react_learn ## Description facebook 开源前端框架学习记录 ## 例子 看react-demos目录 ## 学前个人看法 因为之前一直都是在侧重学java方面后端技术,虽早听闻世上存有三大前端开发框架,但一直没打算学(懒+拖)。后来还看到说可以做后端??什么鬼,感觉是时候学习咧。 以上。 ## react历史 不多说了,自己百度吧 ## html模板 大致结构: ```js
``` 模板需要注意两个事情: 1. 如果需要使用JSX语法,必须由type值为`text/babbel`的script的标签包围; 2. 代码用到的react js库,比如react、readct-dom、browser等等都必须首先加载(放在开头)。 ## ReactDOM.render() 函数用于将模板转为html代码,并插入指定的dom节点内容。相当于render的两个参数。 详细使用: ```js ``` 上面的代码就是将h1标签,整个插入到id为example节点内。运行结果: ![](https://down.shafish.cn/blog_photo/react/react1.png) ## JSX语法 是不是对上面例子里,h1内容可以直接写在js代码里感到奇怪?? 可以不加引号写在js代码中,这就是JSX语法,它允许Html于JavaScript代码混写。 jsx语法有几个个特点: 1. html标签用`<>` -- html规则解析 2. 代码块用`{}` -- js解析 3. js变量中可以直接插入模板中,用`{变量}`获取 例子: ```js var names = ['alice','emily','kate']; ReactDOM.render(
{ names.map(function(name){ return
Hello! {name}!
}) }
, document.getElementById('example') ) ``` ```js var arr = [

Hello world!

,

React is awesome

, ]; ReactDOM.render(
{arr}
, document.getElementById('example') ); ``` ## 组件 react允许将代码封装成一个组件,也就是我们html中用到的标签,然后跟普通html标签一样使用。React.createClass方法就用于生成一个组件。 ```js class HelloMessage extends React.Component { render(){ return

Hello,{this.props.name}

; } } ReactDOM.render( , document.getElementById("example") ); ``` 我们可以发现组件的几大特点: 1. 组件需要用class定义,(组件名/类名??)组件类名必须要大写开头 2. 组件类下必须有reander() 3. 使用时,标签参数`xxxx`,需要用`this.props.xxxx`获取 4. reander函数内,只能包含一个顶层标签,比如上面的`h1`标签,顶层标签不能并列。 以上。 遵守上面几点就可以灵活使用组件类,来自定义我们的标签咧。组件可以添加多个属性,比如上面的`name`,根据上面第三点操作就行;组件的属性也可以接受任意值,字符串、对象、函数等等都可以。 注意: `class`属性强制为`className` `for`属性强制为`htmlFor` 因为class和for是js的保留字。 ps:保留字是语言中定义具有特殊含义的标识符,保留字不能作为标识符使用 ### 高级用法 当组件实例被创建并插入 DOM 中时,其生命周期调用顺序如下: ```js constructor() //构造函数。(构造函数都出来了) static getDerivedStateFromProps() render() componentDidMount() ``` 看看倒数第二节,组件的生命周期 ## this.props.children 上面说了`this.props.xxx`是拿来去属性值的,但是有一个例外,就是`this.props.children`。 它表示了组件的所有子节点: ```js class NoteList extends React.Component{ render() { return (
    { /* 之前用过map,大概用法就是:集合.map(单个元素,处理当个元素函数),函数里用到的一个无序列表li*/ React.Children.map(this.props.children, function (child) { return
  1. {child}
  2. ; }) }
); } } ReactDOM.render( hello world shafish , document.getElementById("example") ); ``` 建议: vscode的语言模式选javascript react,获取百度查查vscode关于react的语法查错,打错好几次了。 上面的代码NoteList组件标签在用的时候,包含了三个子节点,都可以通过`this.props.children`获取: ![](https://down.shafish.cn/blog_photo/react/react2.png) 注意: 有没有想过如果NoteList标签里不设子节点会是什么情况??this.props.children 的值有三种可能:需要注意了 1. 如果当前组件没有子节点,它就是 undefined ; 2. 如果有一个子节点,数据类型是 object ; 3. 如果有多个子节点,数据类型就是 array; 你们注意到`React.Children`了吗,React提供了这个工具方法,不管什么类型,放进map中,迭代就行了。[更多Children用法](https://facebook.github.io/react/docs/top-level-api.html#react.children) ## PropTypes 上面在组件那小节提到-组件的属性可以接受任意值,字符串、对象、函数等等。有时,我们需要一种机制,验证别人使用组件时,提供的参数是否符合要求。 `组件类`的`PropTypes属性`,就是用来验证组件实例属性是否符合要求的!! 记得先导包 ```js ``` ```js var data = 123; class MyTitle extends React.Component { static propTypes = { title: PropTypes.string.isRequired, /*指定title必须为string*/ } render() { return

{this.props.title}

; } } ReactDOM.render( , /*强制转类型,不过js的类型好像用的时候区分不大吧,先mark*/ document.getElementById('example') ); ``` 此时显示是可以显示的,但是控制台就存在验证不通过报错了。 ![](https://down.shafish.cn/blog_photo/react/react3.png) [更多PropTypes用法](http://facebook.github.io/react/docs/reusable-components.html) ## getDefaultProps 跟上面那个proptypes用法差不多,但是这个是用来设置组件属性默认值的。 ```js class MyTitle extends React.Component { getDefaultProps : function () { return { title : 'Hello World' }; render() { return

{this.props.title}

; } } ReactDOM.render( , /*用默认值就好*/ document.getElementById('example') ); ``` ## 获取真实的DOM节点 组件并不是真实的 DOM 节点,而是存在于内存之中的一种数据结构,叫做虚拟 DOM。只有当它插入文档以后,才会变成真实的 DOM 。 根据 React 的设计,所有的 DOM 变动,都先在虚拟 DOM 上发生,然后再将实际发生变动的部分,反映在真实 DOM上,这种算法叫做 DOM diff ,它可以极大提高网页的性能表现。 但是,有时需要从组件获取真实 DOM 的节点,这时就要用到 ref 属性: ```js class MyComponent extends React.Component { constructor(props) { super(props); this.myTextInput = React.createRef(); this.handleClick = this.handleClick.bind(this) } handleClick() { this.myTextInput.current.focus(); } render() { return (
); } } ReactDOM.render( , document.getElementById('example') ); ``` 这个就有点复杂了,一步步来看。 - 先看布局,`render()`里包含一个文本输入和按钮。输入框需要获取用户输入,前面说了组件类是虚拟的dom,为了做到用户输入,文本输入框必须有一个 ref 属性,然后 `this.refs.[refName]` 就会返回这个真实的 DOM 节点。 - `this.refs.[refName]` 属性获取的是真实 DOM。所以必须等到虚拟 DOM 插入文档以后,才能使用这个属性,否则会报错。上面代码中,通过为组件指定 Click 事件的回调函数,确保了只有等到真实 DOM 发生 Click 事件之后,才会读取 this.refs.[refName] 属性。 - React 组件支持很多事件,除了 Click 事件以外,还有 KeyDown 、Copy、Scroll 等 [更多事件用法](http://facebook.github.io/react/docs/events.html#supported-events) ## this.state 组件免不了要与用户互动,React 的一大创新,就是将组件看成是一个状态机,一开始有一个初始状态,然后用户互动,导致状态变化,从而触发重新渲染 UI。 ```js class LikeButton extends React.Component { constructor(props) { super(props) this.state = { liked: false } this.handleClick = this.handleClick.bind(this) } handleClick(event) { this.setState({ liked: !this.state.liked }); } render() { var text = this.state.liked ? 'like' : 'haven\'t liked'; return (

You {text} this. Click to toggle.

); } } ReactDOM.render( , document.getElementById('example') ); ``` 一步步来: - 组件`LikeButton`的`getInitialState`方法用于定义初始状态,也就是一个对象,这个对象可以通过`this.state`属性读取。当用户点击组件,导致状态变化,`this.setState` 方法就修改状态值,每次修改以后,自动调用 `this.render` 方法,再次渲染组件。 - 由于` this.props `和 `this.state` 都用于描述组件的特性,可能会产生混淆。一个简单的区分方法是,`this.props` 表示那些一旦定义,就不再改变的特性,而 `this.state` 是会随着用户互动而产生变化的特性。 ## 表单 用户在表单填入的内容,属于用户跟组件的互动,所以不能用 `this.props` 读取 ```js class Input extends React.Component { constructor(props) { super(props) this.state = { value: 'Hello!' } this.handleChange = this.handleChange.bind(this) } handleChange(event) { this.setState({ value: event.target.value }); } render() { var value = this.state.value; return (

{value}

); } } ReactDOM.render(, document.getElementById('example')); ``` 上面代码中,文本输入框的值,不能用 this.props.value 读取,而要定义一个 onChange 事件的回调函数,通过 event.target.value 读取用户输入的值。textarea 元素、select元素、radio元素都属于这种情况 [更多表达用法](http://facebook.github.io/react/docs/forms.html) ## 组件的生命周期 组件的生命周期: - 1. Mounting:已插入真实 DOM - 2. Updating:正在被重新渲染 - 3. Unmounting:已移出真实 DOM React 为每个状态都提供了两种处理函数,will 函数在进入状态之前调用,did 函数在进入状态之后调用,三种状态共计五种处理函数: ```js componentWillMount() componentDidMount() componentWillUpdate(object nextProps, object nextState) componentDidUpdate(object prevProps, object prevState) componentWillUnmount() ``` React 还提供两种特殊状态的处理函数: ```js componentWillReceiveProps(object nextProps):已加载组件收到新的参数时调用 shouldComponentUpdate(object nextProps, object nextState):组件判断是否重新渲染时调用 ``` 例子: ```js class Hello extends React.Component { constructor(props) { super(props) this.state = { opacity: 1.0 }; } componentDidMount() { this.timer = setInterval(function () { var opacity = this.state.opacity; opacity -= .05; if (opacity < 0.1) { opacity = 1.0; } this.setState({ opacity: opacity }); }.bind(this), 100); } render() { return (
Hello {this.props.name}
); } } ReactDOM.render( , document.getElementById('example') ); ``` 写的时候是懵的,这都是什么?? 我们只看render函数,除了一个style参数其他都是之前熟悉的。继续 - setState函数,我是知道的,作用跟在flutter中用法是一样的,拿来刷新状态。 - setInterval设定了一个100毫秒的定时器,来处理opacity值,emmm? opacity是什么,opacity属性是css3中设置元素不透明级别的一个参数。而且React组件在构造函数里设置的一个初始值,`this.state.opacity` - componentDidMount,组件插入dom之后。每隔0.1s,opacity以0.5递减,低于0.1又是一个循环。 所以最后显示的结果就是hello world在不停地渐变。 注意: 取opacity值的时候,不是`style="{opacity: this.state.opacity}"`,而是`style={{opacity: this.state.opacity}}` 这是因为 React 组件样式是一个对象,所以第一重大括号表示这是 JavaScript 语法,第二重大括号表示样式对象 ## ajax last one 组件的数据来源,通常是通过 Ajax 请求从服务器获取,可以使用 componentDidMount 方法设置 Ajax 请求,等到请求成功,再用 this.setState 方法重新渲染 UI : 上面代码使用 jQuery 完成 Ajax 请求,这是为了便于说明。React 本身没有任何依赖,完全可以不用jQuery,而使用其他库。 我们甚至可以把一个Promise对象传入组件: ```js class UserGist extends React.Component { constructor(props) { super(props) this.state = { username: '', lastGistUrl: '' } } componentDidMount() { $.get(this.props.source, function(result) { var lastGist = result[0]; this.setState({ username: lastGist.owner.login, lastGistUrl: lastGist.html_url }); }.bind(this)); } render() { return (
{this.state.username}'s last gist is here.
); } } ReactDOM.render( , document.getElementById('example') ); ``` 从Github的API抓取数据,然后将Promise对象作为属性,传给RepoList组件: ```js ReactDOM.render( , document.body ); ``` 如果Promise对象正在抓取数据(pending状态),组件显示"正在加载";如果Promise对象报错(rejected状态),组件显示报错信息;如果Promise对象抓取数据成功(fulfilled状态),组件显示获取的数据: ```js class RepoList extends React.Component{ getInitialState: function() { return { loading: true, error: null, data: null}; }, componentDidMount() { this.props.promise.then( value => this.setState({loading: false, data: value}), error => this.setState({loading: false, error: error})); }, render: function() { if (this.state.loading) { return Loading...; } else if (this.state.error !== null) { return Error: {this.state.error.message}; } else { var repos = this.state.data.items; var repoList = repos.map(function (repo) { return (
  • {repo.name} ({repo.stargazers_count} stars)
    {repo.description}
  • ); }); return (

    Most Popular JavaScript Projects in Github

      {repoList}
    ); } } }; ``` 参考: 阮一峰的网络日志 15年出的 [React 入门实例教程](http://www.ruanyifeng.com/blog/2015/03/react.html)