1.1、虚构DOM

常见问题:react virtual dom是什么?说一下diff算法?

拿到一个问题,个别答复都是是什么?为什么?怎么办?那就依照这个思路来吧!

what
用 JavaScript 对象示意 DOM 信息和构造,当状态变更的时候,从新渲染这个 JavaScript 的对象构造。这个 JavaScript 对象称为virtual dom;
why
DOM操作很慢,轻微的操作都可能导致页面从新排版,十分耗性能。绝对于DOM对象,js对象解决起来更快,而且更简略。通过diff算法比照新旧vdom之间的差别,能够批量的、最小化的执行dom操作,从而进步性能。
where
React中用JSX语法形容视图,通过babel-loader转译后它们变为React.createElement(...)模式,该函数将生成vdom来形容实在dom。未来如果状态变动,vdom将作出相应变动,再通过diff算法比照新老vdom区别从而做出最终dom操作。

这里说到了JSX,那就顺带大抵说一下:

什么是JSX
语法糖, React 应用 JSX 来代替惯例的 JavaScript。JSX 是一个看起来很像 XML 的 JavaScript 语法扩大。
React实战视频解说:进入学习
为什么须要JSX
开发效率:应用 JSX 编写模板简略疾速。执行效率:JSX编译为 JavaScript 代码后进行了优化,执行更快。类型平安:在编译过程中就能发现错误。
React 16原理
babel-loader会预编译JSX为React.createElement(...)
React 17原理
React 17中的 JSX 转换不会将 JSX 转换为 React.createElement,而是主动从 React 的 package 中引入新的入口函数并调用。另外此次降级不会扭转 JSX 语法,旧的 JSX 转换也将持续工作。
与vue的异同

react中虚构dom+jsx的设计一开始就有,vue则是演进过程中才呈现的,2.0版本后呈现。

jsx原本就是js扩大,本义过程简略间接的多;vue把template编译为render函数的过程须要简单的编译器转换字符串-ast-js函数字符串

1.2、render、Component根底外围api

render

ReactDOM.render(element, container[, callback]);

当首次调用的时候,容器节点里的所有DOM 元素都会被替换,后续的调用则会应用React的DOM的差分算法(DOM diffing algorithm)进行高效的更新。

如果提供了可选的回调函数,该回调将在组件被渲染或更新之后被执行。

节点类型

1、文本节点2、html 标签节点3、函数组件4、类组件...
函数组件
// 大些字母结尾function Welcome(props) {    return <h1>Hello, {props.name}</h1>}
类组件

React 的组件能够定义为class 或函数的模式,如需定义class 组件,须要继承React.Component 或 React.PureComponent:

class Welcome extends React.Component {    render() {        return <h1>Hello, {this.props.name}</h1>    }}

1.3、手写简版myreact

实现原生标签节点、文本节点、函数组件和类组件的首次渲染

先用 Create React App 创立一个 React 我的项目,装置依赖并运行;

接着在 src/index.js 里边加上 这段代码查看一下版本号,保障本人的是17版本

console.log("version", React.version);

正是因为 React17 中,React会主动替换JSX为js对象,所以咱们次要须要正文掉 src/index.js 中:

// import React from "react";// import ReactDOM from "react-dom";

接着在src 下创立一个myreact文件夹,在里边创立一个 react-dome.js

// vnode 虚构dom对象// node 实在dom节点// ! 首次渲染function render(vnode, container) {  // react17 能够主动转虚构dom  console.log("vnode", vnode);  // vnode->node  const node = createNode(vnode);  // node->container  container.appendChild(node);}// 创立节点function createNode(vnode) {  let node;  const {type} = vnode;  // todo 依据组件类型的不同创立不同的node节点  if (typeof type === "string") { // 原生标签节点    node = updateHostComponent(vnode);  } else if (typeof type == "function") { // 函数组件 再次辨别一下类组件和函数组件    node = type.prototype.isReactComponent        ? updateClassComponent(vnode)      : updateFunctionComponent(vnode);  } else { // 文本节点    node = updateTextComponent(vnode);  }  return node;}// 原生标签节点function updateHostComponent(vnode) {  const {type, props} = vnode;  const node = document.createElement(type);  console.log('document.createElement', node)  // 更新节点局部  updateNode(node, props); // 属性  reconcileChildren(node, props.children); // 遍历children  return node;}// 更新属性function updateNode(node, nextVal) {  Object.keys(nextVal)    .filter((k) => k !== "children") // 过滤一下 children    .forEach((k) => (node[k] = nextVal[k])); // 生成属性}// 文本节点function updateTextComponent(vnode) {  const node = document.createTextNode(vnode);  return node;}// 函数组件function updateFunctionComponent(vnode) {  const {type, props} = vnode;  // type 是一个 function  const vvnode = type(props);  // vvnode->node  const node = createNode(vvnode);  return node;}// 类组件function updateClassComponent(vnode) {  const {type, props} = vnode;  // 类组件须要 new   const instance = new type(props);  console.log('instance', instance);  const vvnode = instance.render();  console.log('vvnode', vvnode);  // vvnode->node  const node = createNode(vvnode);  return node;}// 遍历childrenfunction reconcileChildren(parentNode, children) {  // 和源码一点写法区别,然而也是为了判断是否是数组  const newChildren = Array.isArray(children) ? children : [children];  for (let i = 0; i < newChildren.length; i++) {    let child = newChildren[i];    // vnode    // vnode->node, node插入到parentNode    render(child, parentNode);  }}export default { render };

接着,还要在创立一个 src/myreact/Component.js 文件:

// 类组件必须继承自 Component 或者 PureComponentfunction Component(props) {  // 须要绑定一下this  this.props = props;}// 做了一个 类组件的标记Component.prototype.isReactComponent = {};export default Component;

奥,不能忘了还要改变一下 src/index.js 文件内容:

// import React from 'react';// import ReactDOM from 'react-dom';import ReactDOM from './myreact/react-dom';import Component from "./myreact/Component";import './index.css';// import App from './App';import reportWebVitals from './reportWebVitals';class ClassComponent extends Component {  render() {    return (      <div>        <p>类组件-{this.props.name}</p>      </div>    );  }}export default ClassComponent;function FunctionComponent(props) {  return (    <div>      <p>函数组件-{props.name}</p>    </div>  );}const jsx = (  <div className="myjsx">    <h1>111111</h1>    <h2>222222</h2>    <h3>111111</h3>    <a href="https://www.baidu.com/">百度</a>    <FunctionComponent name="我是函数组件" />    <ClassComponent name="我是类组件" />  </div>)// 原生标签// 文本节点// 函数组件// 类组件ReactDOM.render(  jsx,  document.getElementById('root'));// console.log("version", React.version); // version 17.0.1// If you want to start measuring performance in your app, pass a function// to log results (for example: reportWebVitals(console.log))// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitalsreportWebVitals();

整体代码就是这样,具体过程就不在这里粗疏阐明了,大家好好品一下代码,有疑难的能够分割我。

小结

1、React17 中,React会主动替换JSX为js对象。2、js对象即vdom,它可能残缺形容dom构造。3、ReactDOM.render(vdom, container)能够将vdom转换为dom并追加到container中。4、实际上,转换过程须要通过一个diff过程。