React 核心思想之声明式渲染

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,6,8,10]

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

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

var numbers = [1,5]

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

  return n * 2
})
console.log(doubled) //=> [2,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,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。

相关文章

react 中的高阶组件主要是对于 hooks 之前的类组件来说的,如...
我们上一节了解了组件的更新机制,但是只是停留在表层上,例...
我们上一节了解了 react 的虚拟 dom 的格式,如何把虚拟 dom...
react 本身提供了克隆组件的方法,但是平时开发中可能很少使...
mobx 是一个简单可扩展的状态管理库,中文官网链接。小编在接...
我们在平常的开发中不可避免的会有很多列表渲染逻辑,在 pc ...