Hacking With React读书笔记之React入门

前言

好久没有写博客了,暑假以来,项目啊、小论文啊,种种使得没能坚持下来。。惭愧思密达~最近看了本书Hacking with React,介绍了React前端框架入门、React-Router路由库、JestJS单元测试工具、Webpack前端模块加载工具、ES6、Babel、SuperAgent、Chance、ESLint等相关技术,来构建模块化JavaScript应用。对我来说,简直打开新世界的大门~因为都不会。。悲伤脸~

相关技术介绍


  1. 什么是React?

    React的核心是使用组件来定义界面、是一个View层的前端库。但是由于React设计思想的独特、性能的出众,使得该项目从最早的UI引擎变成一整套前后端通吃的Web App解决方案。
    使用React的时候我们通常还需要一套机制去管理组件与组件之间、组件与模型之间的通信。Facebook官方提出Flux思想管理数据流,Redux是github上涌现的实现FLUX的框架。由于本书属于入门书,并没有介绍这个框架。

    真正学会 React 是一个漫长的过程。你会发现,它不是一个库,也不是一个框架,而是一个庞大的体系。想要发挥它的威力,整个技术栈都要配合它改造。你要学习一整套解决方案,从后端到前端,都是全新的做法。而使用React最合理的选择就是采用它的整个技术栈。

  2. 什么是Babel?

    因为我们要基于ES6开发应用,目前的浏览器暂不完全支持所有新语法特性。而Babel.js是一个转换编译器,能将ES6转换成主流浏览器支持的ES5代码。满足我们使用ES6编写代码的需求。

  3. 什么是Webpack?

    我们希望通过NPM管理自定义组件,需要选用一个兼容CommomJS的工具集。而Webpack是一个模块加载兼打包工具,能够像Nodejs一样处理依赖关系,然后解析出模块之间的依赖,将代码打包。通过配置一个webpack.config.js的文件来管理,将各种资源作为模块来使用。对非JS文件(如:样式文件、图片、字体文件)都非常友好,所以我们选择Webpack。

  4. 什么是JSX?

    JSX是HTML和Javascript混写的语法,当遇到<>,JSX就当作HTML来解析,遇到{}就当作JavaScript来解析。表达式用{}包起来,不要加引号,加引号会被当成字符串,也不可以是一段代码,有三元表达式。React.render方法可以渲染HTML标签–声明变量首字母小写,也可以渲染React标签–声明变量首字母大写。标签的属性class和for,需要写成className和htmlFor。因为两个属性是JavaScript的保留字和关键字。无论你是否使用JSX。

    使用React,不一定非要使用JSX语法,可以使用原生的JS进行开发。但是React作者强烈建议我们使用JSX,因为JSX在定义类似HTML这种树形结构时,十分的简单明了。简明的代码结构更利于开发和维护。 XML有着开闭标签,在构建复杂的树形结构时,比函数调用和对象字面量更易读。

读书记录


  1. How to install Webpack, Babel and React for development with ES6.

    1
    2
    3
    4
    5
    6
    npm init -y  //生成默认的package.json,省去交互式界面
    npm install --save-dev webpack webpack-dev-serve react-hot-loader
    npm install --save-dev babel-core babel-loader
    npm install --save-dev babel-preset-es2015 babel-preset-react
    npm install --save-dev react react-dom history react-router
    //--save 在package.json的dependencies字段增加或者修改一个安装包版本号名值对,-dev 修改devDependencies开发环境字段,这样就不用再安装了某个包之后手动修改package.json
  2. How to create a basic React component and import it into an application.

  3. How to write simple JSX to render content.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    //创建--一般一个组件就是一个js文件
    import React from 'react';

    class Detail extends React.Component{
    render(){
    return <p>hello react!</p>
    }
    }
    export default Detail;

    //使用
    import React from 'react';
    import ReactDOM from 'react-dom';

    import Detail from './pages/Detail';

    ReactDOM.render(
    <Detail/>,
    document.getElementById('app')
    );
    //在文档的哪个地方渲染哪个组件,两个参数
    //JSX直接<Detail/>,内部通过React.createElement转化到原生的js
  4. How to use props to give a component values.

  5. How to render several elements at once.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
     //render()方法只能return一个元素,所以当不止一个JSX元素被返回时,用一个div包含住。
    import React from 'react';
    improt Chance from 'chance';

    class Detail extends React.Component{
    render(){
    return (<div>
    <p>{this.props.message}</p>
    <p>hello,{chance.first()}!</p>
    <p>You are from {chance.country({full:true})}</p>
    </div>)

    }
    }
    export default Detail;

    //props往往通过组件调用的方式指定,props的拥有着是调用方,初始化props之后,props是不可变的。像属性一样。

    import React from 'react';
    import ReactDOM from 'react-dom';
    import Detail from './pages/Detail';

    ReactDOM.render(
    <Detail message='hello React!'/>,
    document.getElementById('app')
    );

  6. How to handle events such as onClick.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    //React Component 绑定事件监听
    //通过on驼峰式命名的方式来绑定相应事件,而处理函数一般是通过对象属性封装在react component的对象实例上。onClick并不是html属性onclick,html属性大小写不敏感,html4推荐全小写。
    class Detail extends React.Component{
    buttonClicked(){
    this.forceUpdate();
    }

    render(){
    return (<div>
    <p>hello,{chance.first()}!</p>
    <button onClick={this.buttonClicked.bind(this)}>Meet someone new</button>
    </div>)

    }
    }
    //永远记住,return的并不是一个真是的DOM节点
    //forceUpdate 重新render。
    //bind(this)确保函数体内的this指向是正确的,在react中this为组件实例,而函数的回调有可能会改变this的指向,要么用箭头函数=>,要么用bind(this)来改变this的指向。
    //假设这是JQuery的ajax,那么ajax函数内的this,指向是jQuery本身,而不是react,bind(this)就是为了防止这种现象,
  7. How to use state, and how it differs from props.

    forceUpdate

    • 有些变量不在state上,但你又想达到这个变量更新的时候就重新刷新render方法的效果;
    • state里面的某个变量层次太深,更新的时候并没有自动触发render;
    • forceUpdate导致render方法在相应的组件上被调用,并且子组件也会调用自己的render。
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      //state我们认为是属于当前组件的,可变,调用组件的setState方法即可。
      //这里点击按钮,并不会使得state变化,因为constructor只会在object被创建的时候被调用一次,forceUpdate反复re-render,state属性也没有变化
      class Detail extends React.Component{
      constructor(props){
      super(props);
      this.state = {
      name:chance.first(),
      country:chance.country({full:true})
      };
      }

      buttonClicked(){
      this.forceUpdate();
      }

      render(){
      return (<div>
      <p>hello,{this.state.name}!</p>
      <p>You are from {this.state.country}.</p>
      <button onClick={this.buttonClicked.bind(this)}>Meet someone new</button>
      </div>) return <p>hello react!</p>

      }
      }

      //states/props变化,都会自动re-render
      //新老state合并后共存
      buttonClicked(){
      const newState = {
      name:chance.first()
      };
      this.setState(newState);
      }
  8. How to loop over and render data in an array.

    [].map
    原数组映射到新的数组,方便获得数组中特定的属性值们。
    [].map(function(item,index){}),回调函数一定要有返回值
    key={index}来表示唯一性,这个习惯挺重要哒~

    箭头函数
    es6: input.map(item => item + 1);
    es5: input.map(function(item){return item+1});
    箭头函数的特性还没得到广泛支持,Babel将其转化为普通函数就能在js环境中执行了。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    class Detail extends React.Component{
    constructor(props){
    super(props);

    const people = [];
    for(let i=0;i<10;i++){
    people.push({
    name:chance.first(),
    country:chance.country({full:true})
    })
    }
    this.state = {
    people
    };
    }

    render(){
    return (<div>
    {this.state.people.map((person,index) = >(
    <p key={index}>hello,{person.name} from {person.country}!</p>
    ))}
    </div>) return <p>hello react!</p>

    }
    }
  9. How to fetch data from GitHub using SuperAgent and Ajax.

    superAgent
    一个流行的nodejs第三方模块,专注于处理服务器/客户端的HTTP请求,相较于nodejs中内置的http模块来说提供了更加优雅简单的API来进行请求的发送响应处理等操作。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    import ajax from 'superagent';

    class Detail extends React.Component{
    constructor(props){
    super(props);
    this.state = {
    commits:[]
    };
    }

    //只有当end()方法调用之后这个请求才会发出去,在调用end()之前所有的动作其实都是对请求的配置。
    componentWillMount(){
    ajax.get('http://api.github/repos/facebook/react/commits')
    .end((error,response) = >{
    if(!error && response){
    this.setState({commits:response.body});
    }else{
    console.log('There was an errpr fetching from Github',error);
    }
    })
    }
    //const 声明一个只读的常量,一旦声明,值就不能改变
    //map 函数要有返回值,不写return的话,=>之后的内容全体返回
    render(){
    return (<div>
    {this.state.commits.map((commit,index) = >{
    const author = commit.author?commit.author.login:'Anonymous';

    return (<p key={index}>
    <strong>{author}</strong>
    <a href={commit.html_url}>{commit.commit.message}</a>
    </p>)
    })}
    </div>)

    }
    }
  10. How to use string interpolation and computed property names.

  11. How to pass parameters using onClick.

    字符串模板
    es6 允许使用反引号(tab键上面那个)来创建字符串,此种方法创建的字符串里面可以包含有美元符号加花括号包裹的变量,如${varible}.
    计算属性名:this.setState({[type]:response.body})这个[type]。
    两种方式给事件传参

    • <button onClick={this.selectMode.bind(this,'arg1')}>show</button>选这个
    • <button onClick={this.selectMode.bind(this)} data-mode='arg1'>show</button>jest测试时检测不到data-mode属性。
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      fetchFeed(type){
      ajax.get(`http://api.github/repos/facebook/react/${type}`)
      .end((error,response) = >{
      if(!error && response){
      this.setState({[type]:response.body});
      }else{
      console.log(`Error fetching ${type}`,error);
      }
      })
      }
      }
  12. How to create routes using React Router.

  13. How to create links between pages using .
  14. How to render default content using .
  15. How to store your React Router routes separately from your code.
  16. How to make breadcrumbs using and .

    React-router路由库
    是React体系的一个重要部分,官方维护也是唯一可选的路由库。通过管理URL,实现组件的切换和状态的变化。帮助快速实现路由功能,包括URL和component同步映射的关系。
    IndexRoute
    默认子路由,当没有别的子路由匹配时,默认的子路由
    Link VS IndexLink
    制作面包屑导航条

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    //分离的routes文件
    import React from 'react';
    import {
    Route,
    IndexRoute
    } from 'react-router';

    import App from './pages/App';
    import Detail from './pages/Detail';
    import List from './pages/List';
    import User from './pages/User';

    const routes = (
    <Route path='/' component={App}>
    <IndexRoute component={List}/>
    <Route path='detail/:repo' component={Detail}/>
    <Route path='user/:user' component={User}/>
    </Route>
    );

    export default routes;
    //引用routes文件
    import React from 'react';
    import ReactDOM from 'react-dom';
    import { Router } from 'react-router';
    import createHistory from 'history/lib/createHashHistory';

    import routes from './routes';

    ReactDOM.render(
    <Router history={createHistory({queryKey:false})} onUpdate={()=>window.scrollTo(0,0)}>
    {routes}
    </Router>,
    document.getElementById('app')
    )

    //List组件
    import React from 'react';
    import {
    Link,
    IndexLink
    } from 'react-router';

    class List extends React.Component {
    render() {
    return (
    <div>
    <p>You are here: <IndexLink to='/' activeClassName='active'>Home</IndexLink></p>

    <p>Please choose a respository from the list below.</p>
    <ul>
    <li><Link to='/detail/react'>React</Link></li>
    <li><Link to='/detail/react-native'>React-Native</Link></li>
    <li><Link to='/detail/jest'>Jest</Link></li>
    </ul>
    </div>);
    }
    }

    export default List;
  17. How to use Jest to test React component rendering.

  18. How to use Jest to simulate user interface with controls.
  19. How to create asynchronous tests using waitFor() and runs().
  20. How to fake an Ajax call using the require() function.
  21. How to find rendered components using scryRenderedDOMComponentsWithClass().
  22. How to lint your React code using ESLint and Babel.