React——第一阶段

根据胡子大哈的文章学习,感谢大胡分享
胡子大哈-react.js第一阶段

2017.6.19更新完毕
2017.6.18更新至state


正文开始

毕设搞定,工作搞定,终于。。。其实也不是没有时间,而是心里不静,不想写。

公司要用react,所以,先复习下,开始

一、什么是组件化?

总的来说,各种框架只是为了解决一个问题——开发效率。组件化是一种解决方案。
前端说起来也就三部分——结构,样式,交互。将某一功能的这三部分抽象出来,提升复用性、可维护性、代码效率。

那么问题的关键是——如何抽象

  1. 较广泛的属性——>较特殊的属性
  2. 输入不同——>输出不同

直白点就是类、函数。很明显,对于前端也就是,用js生成想要的结构,并为其添加功能和样式

具体分析

用js生成页面,再直白点就是——一个字符串形式的dom结构,用js解析、加入功能,然后插入到页面。

1.结构
那么首先要有一个render函数,返回这个字符串形式的dom结构。(也就是最开始的输入)

class oneComponent{
  render() {
    this.el = this.createDOM(`字符串dom结构`);
    return this.el;
  }
  createDOM(str) {
    const div = document.createElement('div');
    div.innerHTML = str;
    return div;
  }
}

结构有了,但显然不够,还要有交互

2.交互
一个思想——状态机。
用一些变量来表示状态,不同的状态,对应不同的页面展示。
首先要有一个状态池,页面的展示由多个状态决定,当状态改变时候,自动重新渲染所有dom。

上边的思路明显有一个问题,自动渲染所有dom开销太大,虽然避免了手工操作dom的各种弊端。解决方案是虚拟dom,其技术细节还有待研究。

首先是状态池:

class oneComponent{
  constructor() {
    this.state = {};
  }
  setState (state) {
    this.state = state;
    this.render();
  }
  render () {
    this.el = this.createDOM(str);
    return this.el;
  }
  
  // 功能函数
  createDOM(str) {
    const div = document.createElement('div');
    div.innerHTML = str;
    return div;
  }
}

有了状态池,功能就很好做了。比如一个点击切换内容功能(实际情况的肯定比较复杂了)

class oneComponent{
  constructor() {
    this.state = {
      isShowOK: true;
    };
  }
  setState (state) {
    this.state = state;
    this.render();
  }
  render () {
    this.el = this.createDOM(`${this.state.isShowOK ? 'ok' : '不ok'}`);
    this.el.addEventListener('click', this.clickChangeContent.bind(this), false);
    return this.el;
  }

  // 交互功能
  clickChangeContent () {
    this.setState ({
      isShowOK: !this.state.isShowOK
    })
  }
  
  // 功能函数
  createDOM(str) {
    const div = document.createElement('div');
    div.innerHTML = str;
    return div;
  }
}

3.插入页面
这一部分其实是在使用组件,有两个阶段:

  • 第一次使用
    第一次使用组件显然不是自己的事情,而是父组件的事情。
  • 状态改变
    每次状态改变,组件自身都要做到自我更新。
class oneComponent{
  constructor() {
    this.state = {
      isShowOK: true;
    };
  }
  setState (state) {
    let oldEl = this.el;
    this.state = state;
    this.render();
    if (this.onStateChange) {
      // 留下处理改变的接口
      this.onStateChange(oldEl, this.el);
    }
  }
  render () {
    this.el = this.createDOM(`${this.state.isShowOK ? 'ok' : '不ok'}`);
    this.el.addEventListener('click', this.clickChangeContent.bind(this), false);
    return this.el;
  }

  // 交互功能
  clickChangeContent () {
    this.setState ({
      isShowOK: !this.state.isShowOK
    })
  }
  
  // 以下功能函数

  // 组件初始化
  init() {
    this.render();
  }
  // 生成dom str->dom
  createDOM(str) {
    const div = document.createElement('div');
    div.innerHTML = str;
    return div;
  }
  // 使用子组件 这个过程发生在解析父组件自身dom的过程中。遇到了组件的语法,就调用这个函数,并传入对应组件的实例。
  useChildComponent(childComponent) {
    let el  = childComponent.init();
    功能:将el插入到对应的位置。
  }

  // 自身更新
  onStateChange (oldEl, newEl) {
    功能:对比前后的dom,哪变化了,就更新哪。
  }
}

稍微抽象下

输入输出上抽象,组件大概就是上边这样,当然有的功能太复杂,就用文字直接简要说明了下。
虽然看起来还不错,但是明显,组件本身也是可以再抽象一下基类。
为了更灵活,可以对基类子类加入props为组件进行配置。这样可定制性更高了。

class Component {
  constructor (props = {}) {
    this.props = props;
  }
  setState (state) {
    let oldEl = this.el;
    this.state = state;
    this.renderDOM();
    if (this.onStateChange) {
      // 留下处理改变的接口
      this.onStateChange(oldEl, this.el);
    }
  }

  renderDOM () {
    this.el = this.createDOM(this.render());
    此处在解析dom过程中添加事件。类似下边注释里边的。
    // this.el.addEventListener('click', this.clickChangeContent.bind(this), false);
    return this.el;
  }

  // 组件初始化
  init() {
    this.renderDOM();
  }

  // 生成dom str->dom
  createDOM(str) {
    const div = document.createElement('div');
    div.innerHTML = str;
    return div;
  }

  // 使用子组件 这个过程发生在解析父组件自身dom的过程中。遇到了组件的语法,就调用这个函数,并传入对应组件的实例。
  useChildComponent(childComponent) {
    let el  = childComponent.init();
    功能:将el插入到对应的位置。
  }

  // 自身更新
  onStateChange (oldEl, newEl) {
    功能:对比前后的dom,哪变化了,就更新哪。
  }
}

class oneComponent extends Component{
  constructor() {
    super(props);
    this.state = {
      isShowOK: true;
    };
  }

  render () {
    return `${this.state.isShowOK ? 'ok' : '不ok'}`;
  }

  // 交互功能
  clickChangeContent () {
    this.setState ({
      isShowOK: !this.state.isShowOK
    })
  }
}

组件化的概念结束,接下来是基础知识复习

总结一下

组件化设计需要:

  • 像上边这样的组件类
  • 特殊的语法分析器
  • 虚拟dom

二、基础知识

1.杂项

  • 项目生成
    使用create-react-app

  • 组件要引入react、{ Component }react-dom

  • JSX
    将js文件中的类似html的语法结构解析成js对象
    JSX-->(babel+React.js)-->js对象-->(ReactDOM.render)-->DOM-->插入页面

  • 为什么react和react-dom要分离开

    • 构造出的js对象不一定非要渲染成dom,还有可能渲染到canvas上,或者手机app上。
    • 方便更新组件,使用算法操作这个对象,然后整体更新,减少重排,比较快。

2.组件render方法
写react就是写组件,每个组件都必须有一个render方法,返回一个JSX元素
注意

  • 返回中,最外层只要一个元素
  • 在JSX中,class要用className,for要用htmlFor
  • JSX中用{}插入表达式
    • 表达式可以是循环,条件,函数,等等
    • 表达式中的变量来自组件作用域

3.事件监听
在JSX中的原生HTML标签(注意:对组件没用),为对应的事件接口添加回调即可。
比如onClick,onKeyDown
react事件列表

  • event对象

react封装的对象,属性与浏览器自己的event对象基本一致,保证了浏览器兼容性,对外api符合w3c标准。

  • 回调中的this

单纯指定回调,不绑定this的话,是无法在回调函数中通过this拿到组件实例的。
为什么?
很简单,回调函数直接将引用传递出去,其真正执行阶段的作用域是在全局作用域下,也就是说,这是一个在全局环境执行,但是没有明确指出是window对象调用的函数。
es5规定,在严格模式下,不直接用window对象调用函数,其内部this为undefined。react当然用的严格模式。
所以,拿不到组件实例,也拿不到window,最后是一个undined。

解决方法:
method.bind(this, arg1, arg2, ...)

4.state
状态池,就像上一部分说的。
注意:

  • 在组件的构造器中定义
  • state状态自动更新要使用setState()方法,以便于自动触发render
  • setState()参数
  • 对象——表示组件需要更新的状态。
  • 函数
  • setState()的使用
  1. 更新对象放入更新队列中,在本趟结束后更新。所以,前后数据改变存在逻辑关系的话,在同一个函数中,不能只传入对象,解决方法如下。
  2. 使用函数参数解决上边的问题,参数属性为上一个setState的结果。这种情况下,react会把setState一趟更新队列中的状态合并,并一次渲染。
  3. 传入第一个对象参数,并传入第二个当状态改变后的回调函数,封装成一个promise,当成异步来处理。这种情况的话,显然是更新了三次。

5.props
配置组件,组件是比较抽象的,实例化组件就是一个特殊化的过程,通过props来配置。

  • 父组件调用子组件时候,通过标签特性传入。所有的标签特性都会对应到子组件的props字段上。

  • 可以传递任何类型的值。比如函数,对象。

  • props一旦传入进来,就不能被改变。这是为了使得组件的形态/行为可以预测。

  • 只有通过父组件重新渲染,才能改变props。

  • 默认props(defaultProps)

static defaultProps = {
    likedText: '取消',
    unlikedText: '点赞'
  }

6.state与props
总结一下:

  • state组件自己控制自己的状态池,组件外部不可改变。

  • props父组件对子组件的初始化状态池,子组件内部不可改变。

  • 没有state的组件,叫做无状态组件,更利于组建维护。因为有了内部状态,就意味着组件很复杂,复用性降低。

  • react.js鼓励无状态组件。甚至在后来版本引入了函数式组件——不能用state的组件。

const dog = (props) => {
  const say = (event) => alert('dog')
  return (
    
dog
) }

7.渲染列表
渲染列表的一般步骤:
拿到数组-->map遍历-->返回元素-->罗列渲染

  • 注意key值
    key,元素的标识,一般是后台数据的id。

(完)

你可能感兴趣的:(React——第一阶段)