9

React 发展很快,概念也多,本文目的在于帮助初学者理清 React 核心概念。

React 及 React 生态

React 的核心概念只有 2 点:

  • 声明式渲染(Declarative)

  • 基于组件(Component-Based)

声明式渲染

声明式与命令式

  • 命令式编程:命令“机器”如何去做事情(how),这样不管你想要的是什么(what),它都会按照你的命令实现。

  • 声明式编程:告诉“机器”你想要的是什么(what),让机器想出如何去做(how)。

举例:

// 命令式关注如何做(how)

var numbers = [1,2,3,4,5]

var doubled = []

for(var i = 0; i < numbers.length; i++) {

  var newNumber = numbers[i] * 2
  doubled.push(newNumber)

}
console.log(doubled) //=> [2,4,6,8,10]

遍历整个数组,取出每个元素,乘以二,然后把翻倍后的值放入新数组,每次都要操作这个双倍数组,直到计算完所有元素。

// 声明式关注做什么(what)

var numbers = [1,2,3,4,5]

var doubled = numbers.map(function(n) {

  return n * 2
})
console.log(doubled) //=> [2,4,6,8,10]

map 函数所作的事情是将直接遍历整个数组的过程归纳抽离出来,让我们专注于描述我们想要的是什么(what)。

模板渲染

渲染:模板 => HTML => 页面视图

发生在服务器的叫后端模板渲染,公司用的是velocity

发生在客户端的叫前端模板渲染,常用的有 artTemplate

artTemplate 为例。

  • 模板

<script id="test" type="text/html">
    <div>
        <h2>北京时间: {{ date.toLocaleTimeString() }}.</h2>
    </div>
</script>
  • 数据

  • 渲染

setInterval(function() {
    // 数据
    var data = {
        date: new Date()
    };
    // 渲染(将数据和模板绑定在)
    var html = template('test', data);
    // 渲染
    document.getElementById('container').innerHTML = html;
},100)

React 声明式渲染

和普通模板不同的是,React 模板写在 JS 文件中,而不是 html 的 <script> 标签中。能使用所有 JS 语法,而不只有模板语法,所以更加灵活。

function formatName(user) {
  return user.firstName + ' ' + user.lastName;
}

// 数据
const user = {
  firstName: 'Harper',
  lastName: 'Perez'
};

// 模板
const element = (
  <h1>
    Hello, {formatName(user)}!
  </h1>
);

// 渲染
ReactDOM.render(
  element,
  document.getElementById('root')
);

React 可局部渲染,且只渲染改变了的数据。纯模板只能整体渲染。

高效的局部渲染意味着,开发者 只需要维护可变的数据 state (what) ,让 react 框架帮助我们处理 DOM 操作(what)。

// React.createClass 创建模板容器(类)
class Clock extends Component {
  render() {
      return (
          <div>
              <h2>北京时间: { this.props.date.toLocaleTimeString() }</h2>
          </div>
      );
  }
}

setInterval(function() {
    // ReactDOM.render 渲染指令
    ReactDOM.render(
      // date 数据
      <Clock date={new Date()} />,
      document.getElementById('container')
    );
}, 100);

state 只用于存放可变的数据。

通过 setState 告诉 react 什么数据变了,React 会自动更新数据改变部分的视图

class Clock extends Component {
  // 初始化
  constructor(props) {
    super(props);
    // state 只用于存放可变的状态
    this.state = {date: new Date()};
  }
  // 初始化完成后执行
  componentDidMount() {
    setInterval(() => {
      // setState 在修改 state 参数后会自动调用 render 方法。
      this.setState({
        date: new Date()
      })
    },100)
  }

  render() {
      return <h2>北京时间: { this.state.date.toLocaleTimeString() }</h2>
  }
}

ReactDOM.render(
  <Clock />,
  document.getElementById('js-main')
);

React 通过 diffing 算法计算如何更新视图。而 diffing 算法有个 的假设前提,开发人员会提供给长列表的每个子项一个 ID,帮助算法进行对比。

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

const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
  <NumberList numbers={numbers} />,
  document.getElementById('root')
);

完成的渲染流程

初始化的渲染流程分为 3 步。

第一步,开发者使用 JSX 语法写 React,babel 会将 JSX 编译为浏览器能识别的 React JS 语法。这一步,一般配合 webpack 在本地进行。

第二步,执行 ReactDOM.render 函数,渲染出虚拟DOM。

第三步,react 将虚拟DOM,渲染成真实的DOM。

页面更新的流程同样也是 3 步。

第一步,当页面需要更新时,通过声明式的方法,调用 setState 告诉 react。

第二步,react 自动调用组件的 render 方法,渲染出虚拟 DOM。

第三步,react 会通过 diffing 算法,对比当前虚拟 DOM 和需要更新的虚拟 DOM 有什么区别。然后重新渲染区别部分的真实 DOM。


fitfish
1.6k 声望950 粉丝

前端第七年,写一个 RN 专栏。