200 строк кода для реализации упрощенной версии реакции

внешний интерфейс React.js внешний фреймворк
200 строк кода для реализации упрощенной версии реакции

Сейчас (2018)reactЕго становится все больше и больше 🔥 в сфере front-end разработки, да и сам я часто использую его в проектахreact, но всегда интересноreactОсновополагающий принцип реализации , многие попытки прочитатьreactИсходный код невозможно прочитать, это действительно слишком сложно. Не так давно я увидел в Интернете несколько статей, в которых рассказывалось, как сделать это своими руками.react, основываясь на этих материалах и добавляя некоторые свои идеи, начиная с 0 только200строка кода для реализации简版react, я думаю, все будут правы после прочтенияreactУзнайте больше о внутренней реализации . Но прежде чем мы это сделаем, нам нужно освоить несколькоreactсвязанных важных понятий, таких как组件(类)а также组件实例разница,diffалгоритм и生命周期Подождите, давайте представим их по очереди, и мы их реализуем после того, как ознакомимся с этими понятиями.

1 Основные понятия: Component (компонент), instance (экземпляр компонента), element, jsx, dom

Прежде всего, нам нужно понять несколько запутанных концепций и начать изучатьreactМеня также немного смутила разница между ними.Несколько дней назад я обсуждал проблему с новым одноклассником и обнаружил, что он не видит разницы.组件а также组件实例, поэтому необходимо понимать разницу между этими понятиями и связью, мы реализуем это позже в этой статье简版reactтакже основываются на этих понятиях.

Компонент

ComponentЭто компонент, который мы часто реализуем, который может быть类组件(class component)или函数式组件(functional component),а также类组件Его можно разделить на компоненты общего класса (React.Component) и компоненты чистого класса (React.PureComponent), короче говоря, обе категории относятся к类组件,ТолькоPureComponentна основеshouldComponentUpdateСделаны некоторые оптимизации, которые здесь обсуждаться не будут.函数式组件Он используется для упрощения реализации некоторых простых компонентов, используется для написания функции, а входными параметрами являются свойства компонента.props, участвовать类组件изrenderВозвращаемое значение метода такое же, какreact element(Обратите внимание, что следующее введение уже появилось здесь.elementОй). Далее мы реализуем его тремя способами:WelcomeКомпоненты:

// Component
class Welcome extends React.Component {
    render() {
        return <h1>Hello, {this.props.name}</h1>;
    }
}
// PureComponent
class Welcome extends React.PureComponent {
    render() {
        return <h1>Hello, {this.props.name}</h1>;
    }
}
// functional component
function Welcome(props) {
    return <h1>Hello, {props.name}</h1>;
}
экземпляр (экземпляр компонента)

знакомый面向对象编程конечно знаюа также实例Отношения здесь такие же,组件实例На самом деле组件类В результате конкретизации концепция проста, но вreactЗдесь легко запутаться, почему вы так говорите? потому что всеreactне создает экземпляр组件实例, этот процесс на самом делеreactВнутренне сделано для нас, поэтому мы действительно свяжемся组件实例Возможностей не много. Мы больше подвержены следующимelement, потому что мы обычно пишемjsxНа самом деле этоelementЭто просто представление (подробно описанное ниже). несмотря на то что组件实例Я не использую его много, но я использую его иногда.ref.refможет указывать наdom节点или类组件(class component), но не может использоваться для函数式组件,потому что函数式组件не можем实例化. Вот краткое введениеref, нам просто нужно знатьrefможет указывать на组件实例Вот и все, подробнее можно посмотретьreactофициальная документацияRefs and the DOM.

element

упомянутый ранееelement,Прямо сейчас类组件изrenderметод и函数式组件Возвращаемое значениеelement. тогда вотelementчто это такое? На самом деле очень просто, это чистый объект(plain object), и этот чистый объект содержит два свойства:type:(string|ReactClass)а такжеprops:Object,Уведомлениеelementнет组件实例, а чистый объект.несмотря на то чтоelementнет组件实例, но он связан с экземпляром компонента,elementправда组件实例илиdom节点описание. еслиtypeдаstringтипа, значитdom节点,еслиtypeдаfunctionилиclassтипа, значит组件实例. Например, следующие дваelementописатьdom节点с одним组件实例:

// 描述dom节点
{
  type: 'button',
  props: {
    className: 'button button-blue',
    children: {
      type: 'b',
      props: {
        children: 'OK!'
      }
    }
  }
}
function Button(props){
  // ...
}

// 描述组件实例
{
  type: Button,
  props: {
    color: 'blue',
    children: 'OK!'
  }
}
jsx

Просто разберисьelement,ТакjsxНе трудно понять,jsxЭто просто другой способ написания, который нам удобно создаватьelementПросто подумай, если нетjsxТогда эффективность нашей разработки определенно сильно снизится, а код определенно не будет способствовать сопровождению. Например, давайте посмотрим на следующееjsxпример:

const foo = <div id="foo">Hello!</div>;

На самом деле, грубо говоря, он определяет узел domdiv, а набор атрибутов для этого узла равен{id: 'foo'},childrenдаHello!, именно такой объем информации, поэтому он полностью эквивалентен следующему чистому объектному представлению:

{
  type: 'div',
  props: {
    id: 'foo',
    children: 'Hello!'
  }
}

ТакReactкак будетjsxКак насчет преобразования синтаксиса в простые объекты? На самом деле, используяBabelСкомпилировано и сгенерировано, нам нужно только использоватьjsxдобавить в код编译指示(pragma)Да, вы можете обратиться сюдаКак Babel компилирует jsx. Например, мы будем编译指示установить, чтобы указать наcreateElementфункция:/** @jsx createElement */, то предыдущий абзацjsxКод будет скомпилирован в:

var foo = createElement('div', {id:"foo"}, 'Hello!');

Как можно заметить,jsxПроцесс компиляции на самом деле из<,>это标签式написать в函数调用式Это просто преобразование правописания. С этой предпосылкой нам нужно просто реализовать следующееcreateElementфункция может быть построена безelementНу давай сами потом简版reactТакже используйте эту функцию:

function createElement(type, props, ...children) {
    props = Object.assign({}, props);
    props.children = [].concat(...children)
      .filter(child => child != null && child !== false)
      .map(child => child instanceof Object ? child : createTextElement(child));
    return {type, props};
}
dom

Мы также кратко представим здесь dom.Все, кто занимается исследованиями и разработками, должны быть знакомы с этой концепцией. Мы можем создать такой узел domdiv:

const divDomNode = window.document.createElement('div');

На самом деле, все узлы domHTMLElement类например, мы можем проверить следующее:

window.document.createElement('div') instanceof window.HTMLElement;
// 输出 true

оHTMLElementAPI можно обратиться сюда:Введение в HTML-элемент. следовательно,domузелHTMLElement类экземпляр; аналогично, вreactв,组件实例да组件类экземпляр, в то время какelementда снова组件实例а такжеdomОписание узла, взаимосвязь между этими понятиями теперь должны быть понятны всем. После введения этих основных понятий давайте нарисуем картинку, чтобы описать взаимосвязь между этими понятиями:

component vs instance vs dom vs element

2 Виртуальный дом и алгоритм сравнения

считаю использованнымreactБольшинство студентов знакомы с этими двумя понятиями:虚拟domтак же какdiff算法. здесь虚拟domСобственно, об этом упоминалось ранееelement, почему это虚拟Что касается dom, мы уже представили его ранее.elementТолькоdomузел или组件实例Чистое объектное описаниеdomузел, так оно и есть虚拟дом.reactпредоставил нам声明式Способ написания компонента, когда компонентpropsилиstateКомпоненты автоматически обновляются при внесении изменений. Вся страница может фактически соответствовать одномуdomдерево узлов, каждый компонентpropsилиstateИзменения в первую очередь отражаются в虚拟domдерево, а затем, наконец, реагировать на страницуdomОтрисовка дерева узлов.

Так虚拟domа такжеdiff算法Что это значит? почему естьdiffАлгоритмы на самом деле предназначены для улучшения渲染Эффективность, представьте, если бы каждый компонентstateилиpropsПосле изменения все соответствующиеdomЕсли узел удаляется и создается заново, эффективность должна быть очень низкой, поэтому вreactЕсть два внутри虚拟domдерево соответственно现状так же как下一个状态,setStateсрабатывает после звонкаdiffвыполнение алгоритма, а хорошееdiffАлгоритм должен использоваться повторно как можно чаще.domузел, избегая накладных расходов на повторное создание. Я использую следующий рисунок для представления虚拟domа такжеdiff算法Отношение:

虚拟dom & diff算法
reactКомпонент генерируется первым после его первоначального отображения на странице.第1帧виртуальный дом, на этот разcurrent指针указывает на этот первый кадр.setStateбудет сгенерировано после вызова第2帧виртуальный дом, на этот разnext指针указать на второй кадр, следующийdiffалгоритм, сравнивая第2帧а также第1帧сходства и различия, чтобы применить обновление к реальномуdomдерево, чтобы завершить обновление страницы.

Здесь еще раз подчеркиваетсяsetStateкак генерировать虚拟dom, потому что это важно и его легко игнорировать. Что только что было введено虚拟domДа, этоelementПросто дерево. ЭтоelementКак появилось дерево? На самом деле этоrenderМетод возвращается, следующая блок-схема усиливает впечатление:

element
фактическиreactофициальная параdiff算法Есть другое имя, всем точно будет вreactУвидел в соответствующей информации, называемойReconciliation, лично мне это слово кажется немного непонятным, но позже я перечитал словарь и обнаружил, что оно связано сdiff算法Значение:
reconcile是什么意思
можно увидетьreconcileимеют消除分歧,核对значит, вreactконтраст в контексте虚拟domСмысл сходства и различия, по сути, состоит в том, чтобы сказатьdiff算法. Здесь подчеркивается, что реализацию мы реализуем позже.reconcileфункция, состоит в том, чтобы реализоватьdiffалгоритм.

3 Жизненный цикл и алгоритм сравнения

生命周期а такжеdiff算法Что это значит? Здесь мы беремcomponentDidMount,componentWillUnmount,ComponentWillUpdateтак же какcomponentDidUpdateВозьмите пример, чтобы проиллюстрировать отношения между ними. мы знаем,setStateбудет вызван после звонкаrenderгенерировать новые虚拟domдерево, а это虚拟domДерево может отличаться от предыдущего кадра следующим образом:

  1. Добавлен компонент;
  2. удалил компонент;
  3. Обновлены некоторые свойства компонента.

Поэтому мы реализуемdiff算法Процесс будет вызывать их в соответствующем временном узле.生命周期функция.

Здесь необходимо остановиться на упомянутом ранее第1帧, мы знаем, что каждыйreactВход в приложение:

ReactDOM.render(
    <h1>Hello, world!</h1>,
    document.getElementById('root')
);

ReactDom.renderтакже будет генерировать虚拟domдерево, но это虚拟domДеревья рождаются из ниоткуда第一帧, нет предыдущего кадра, используемого для сравнения, так что это虚拟domВсе компоненты, соответствующие дереву, будут вызывать только挂载期функции жизненного цикла, такие какcomponentDidMount,componentWillUnmount.

4 Реализация

Освоив понятия, введенные ранее, реализуйте简版reactЭто не сложно. Здесь необходимо объяснить, что часть реализации этого раздела основана на реализации этого блога.Didact: a DIY guide to build your own React. Теперь сначала посмотрим, какие API мы хотим реализовать. В итоге мы используем это следующим образом:

// 声明编译指示
/** @jsx DiyReact.createElement */

// 导入我们下面要实现的API
const DiyReact = importFromBelow();

// 业务代码
const randomLikes = () => Math.ceil(Math.random() * 100);
const stories = [
  {name: "React", url: "https://reactjs.org/", likes: randomLikes()},
  {name: "Node", url: "https://nodejs.org/en/", likes: randomLikes()},
  {name: "Webpack", url: "https://webpack.js.org/", likes: randomLikes()}
];

const ItemRender = props => {
  const {name, url} = props;
  return (
    <a href={url}>{name}</a>
  );
};

class App extends DiyReact.Component {
    render() {
        return (
            <div>
                <h1>DiyReact Stories</h1>
                <ul>
                    {this.props.stories.map(story => {
                        return <Story name={story.name} url={story.url} />;
                    })}
                </ul>
            </div>
        );
    }
    
    componentWillMount() {
        console.log('execute componentWillMount');
    }
    
    componentDidMount() {
        console.log('execute componentDidMount');
    }
    
    componentWillUnmount() {
        console.log('execute componentWillUnmount');
    }
}

class Story extends DiyReact.Component {
    constructor(props) {
        super(props);
        this.state = {likes: Math.ceil(Math.random() * 100)};
    }
    like() {
        this.setState({
            likes: this.state.likes + 1
        });
    }
    render() {
        const {name, url} = this.props;
        const {likes} = this.state;
        const likesElement = <span />;
        return (
            <li>
                <button onClick={e => this.like()}>{likes}<b>❤️</b></button>
                <ItemRender {...itemRenderProps} />
            </li>
        );
    }
    
    // shouldcomponentUpdate() {
    //   return true;
    // }
    
    componentWillUpdate() {
        console.log('execute componentWillUpdate');
    }
    
    componentDidUpdate() {
        console.log('execute componentDidUpdate');
    }
}

// 将组件渲染到根dom节点
DiyReact.render(<App stories={stories} />, document.getElementById("root"));

Мы использовали в этом бизнес-кодеrender,createElementтак же какComponentТри API, поэтому следующая задача — реализовать эти три API и обернуть их в функцию.importFromBelowвнутри.

4.1 Реализовать createElement

createElementфункция сjsxтесно связаны, как описано ранееjsxЧасть была введена, по сути, она похожа наhtmlПомеченное обозначение преобразуется в чистый объектelement, конкретная реализация выглядит следующим образом:

function createElement(type, props, ...children) {
    props = Object.assign({}, props);
    props.children = [].concat(...children)
        .filter(child => child != null && child !== false)
        .map(child => child instanceof Object ? child : createTextElement(child));
    return {type, props};
}
4.2 Рендеринг

обратите внимание на этоrenderэквивалентноReactDOM.render,нет组件изrenderметод,组件изrenderметод позадиComponentЧасть реализации.

// rootInstance用来缓存一帧虚拟dom
let rootInstance = null;
function render(element, parentDom) {
    // prevInstance指向前一帧
    const prevInstance = rootInstance;
    // element参数指向新生成的虚拟dom树
    const nextInstance = reconcile(parentDom, prevInstance, element);
    // 调用完reconcile算法(即diff算法)后将rooInstance指向最新一帧
    rootInstance = nextInstance;
}

renderРеализация функции очень проста, всего два кадра虚拟domпомириться, а потомrootInstanceуказать на новый虚拟dom. Если вы посмотрите внимательно, вы найдете новые虚拟domдляelement, который вводится в началеelement,а такжеreconcileПосле虚拟domдаinstance, но этоinstanceнет组件实例, посмотри сзадиinstantiateреализация. во всяком случаеrenderНа самом деле метод называетсяreconcileметод для двух кадров虚拟domПросто сравнение.

4.3 Реализовать экземпляр

затем предыдущийinstanceв конце концовelementКакая разница? фактическиinstanceИнструкция проста в том, чтобы положитьelementПереупаковал слой и поставил соответствующийdomОн тоже входит в комплектацию, в чем не сложно разобраться, ведь мы называемreconcileпровестиdiffПри сравнении нужно применять последние к реальнымdom, поэтому необходимо следоватьdomСвязанная следующая реализацияinstantiateФункция делает именно это. Обратите внимание, что из-заelementвключатьdomтип иComponentтип (поtypeПолевое суждение, если вы не понимаете, вы можете вернуться и посмотреть на первый разделelementсвязанное введение), поэтому с ним нужно обращаться в зависимости от ситуации:

domТипelement.typeдляstringтип, соответствующийinstanceСтруктура{element, dom, childInstances}.

ComponentТипelement.typeдляReactClassтип, соответствующийinstanceСтруктура{dom, element, childInstance, publicInstance}, обратите внимание на здесьpublicInstanceэто было описано ранее组件实例.

function instantiate(element) {
    const {type, props = {}} = element;
    const isDomElement = typeof type === 'string';
    
    if (isDomElement) {
        // 创建dom
        const isTextElement = type === TEXT_ELEMENT;
        const dom = isTextElement ? document.createTextNode('') : document.createElement(type);
        
        // 设置dom的事件、数据属性
        updateDomProperties(dom, [], element.props);
        const children = props.children || [];
        const childInstances = children.map(instantiate);
        const childDoms = childInstances.map(childInstance => childInstance.dom);
        childDoms.forEach(childDom => dom.appendChild(childDom));
        const instance = {element, dom, childInstances};
        return instance;
    } else {
        const instance = {};
        const publicInstance = createPublicInstance(element, instance);
        const childElement = publicInstance.render();
        const childInstance = instantiate(childElement);
        Object.assign(instance, {dom: childInstance.dom, element, childInstance, publicInstance});
        return instance;
    }
}

Обратите внимание, потому чтоdom节点а также组件实例могут иметь дочерние узлы, поэтомуinstantiateВ функции есть логика для рекурсивного создания экземпляров.

4.4 Отличие компонентов класса от функциональных компонентов

Как мы упоминали ранее, компоненты включают в себя类组件(class component)а также函数式组件(functional component). Я часто использую эти два типа компонентов в своей обычной работе.Если компонент используется только для рендеринга, я обычно использую函数式组件, ведь логика кода проста, понятна и понятна. ТакReactКак внутренне различить эти два компонента? Этот вопрос прост и прост, он сложен и сложен. Почему ты так говоришь, потому чтоReactВнутренняя реализация действительно относительно проста, но эта простая реализация определяется после различных соображений. общее яйцо (Dan) Есть статья, где подробно разбираетсяReactКак различить два внутренне, я настоятельно рекомендую всем прочитать это, здесь я буду использовать его напрямую, ссылка на статью здесьHow Does React Tell a Class from a Function?. На самом деле ответ очень прост, мы добиваемся类组件Обязательно нужно наследовать от классаReact.Component, поэтому сначала дайтеReact.ComponentСделайте отметку, а затем оцените при создании экземпляра компонентаelement.typeЕсть ли эта метка в цепочке прототипов .

// 打标记
Component.prototype.isReactComponent = {};

// 区分组件类型
const type = element.type;
const isDomElement = typeof type === 'string';
const isClassElement = !!(type.prototype && type.prototype.isReactComponent);

Здесь мы обновляем предыдущую функцию инстанцированияinstantiateразличать函数式组件а также类组件:

function instantiate(element) {
    const {type, props = {}} = element;
    const isDomElement = typeof type === 'string';
    const isClassElement = !!(type.prototype && type.prototype.isReactComponent);
    if (isDomElement) {
      // 创建dom
      const isTextElement = type === TEXT_ELEMENT;
      const dom = isTextElement ? document.createTextNode('') : document.createElement(type);
      
      // 设置dom的事件、数据属性
      updateDomProperties(dom, [], element.props);
      const children = props.children || [];
      const childInstances = children.map(instantiate);
      const childDoms = childInstances.map(childInstance => childInstance.dom);
      childDoms.forEach(childDom => dom.appendChild(childDom));
      const instance = {element, dom, childInstances};
      return instance;
    } else if (isClassElement) {
      const instance = {};
      const publicInstance = createPublicInstance(element, instance);
      const childElement = publicInstance.render();
      const childInstance = instantiate(childElement);
      Object.assign(instance, {dom: childInstance.dom, element, childInstance, publicInstance});
      return instance;
    } else {
      const childElement = type(element.props);
      const childInstance = instantiate(childElement);
      const instance = {
        dom: childInstance.dom,
        element,
        childInstance,
        fn: type
      };
      return instance;
    }
  }

Видно, что если это函数式组件, мы не создавали экземпляр компонента, а напрямую вызывали функцию для получения虚拟dom.

4.5 Реализовать согласование (алгоритм сравнения)

Вот в чем дело,reconcileдаreactв основе, видимо, как только что поставилиstateБыстрый рендеринг очень важен, поэтомуreactМы попытаемся повторно использовать существующие узлы вместо того, чтобы каждый раз динамически создавать все связанные узлы. ноreactСила этим не ограничивается,react16Будуreconcileалгоритм из предыдущегоstackАрхитектура была обновлена ​​доfiberАрхитектура, дальнейшая оптимизация производительности.fiberСоответствующее содержание будет представлено в следующем разделе.Для простоты и простоты понимания мы по-прежнему используем аналогичныеstackАлгоритм архитектуры реализован, дляfiberТеперь просто нужно знать его调度Впринципе хватает конечно, будет время потом реализовать версию на базеfiberархитектурный.

Сначала посмотри целикомreconcileПоток обработки алгоритма:

reconcile算法的处理流程
Как видите, мы будем выполнять различную обработку в зависимости от различных ситуаций:

  1. Если это добавленоinstance, то вам нужно создать экземплярinstanceа такжеappendChild;
  2. Если это добавленоinstance, но удалитьinstance, то нужноremoveChild;
  3. Если ни добавить, ни удалитьinstance, то нужно посмотретьinstanceизtypeНезависимо от того, изменяется ли он, если есть изменение, узел нельзя использовать повторно, и его необходимо создать.instance,ПотомreplaceChild;
  4. еслиtypeЕсли изменений нет, можно повторно использовать существующую ноду, в этом случае необходимо судить, нативная ли она.domNode по-прежнему является нашей собственной реализацией.reactNode, эти два случая обрабатываются по-разному.

После понимания большого процесса нам нужно только выполнить его в нужное время.生命周期Функции достаточно, смотрите конкретную реализацию ниже:

function reconcile(parentDom, instance, element) {
    if (instance === null) {
        const newInstance = instantiate(element);
        // componentWillMount
        newInstance.publicInstance
            && newInstance.publicInstance.componentWillMount
            && newInstance.publicInstance.componentWillMount();
        parentDom.appendChild(newInstance.dom);
        // componentDidMount
        newInstance.publicInstance
            && newInstance.publicInstance.componentDidMount
            && newInstance.publicInstance.componentDidMount();
        return newInstance;
    } else if (element === null) {
        // componentWillUnmount
        instance.publicInstance
            && instance.publicInstance.componentWillUnmount
            && instance.publicInstance.componentWillUnmount();
        parentDom.removeChild(instance.dom);
        return null;
    } else if (instance.element.type !== element.type) {
        const newInstance = instantiate(element);
        // componentDidMount
        newInstance.publicInstance
            && newInstance.publicInstance.componentDidMount
            && newInstance.publicInstance.componentDidMount();
        parentDom.replaceChild(newInstance.dom, instance.dom);
        return newInstance;
    } else if (typeof element.type === 'string') {
        updateDomProperties(instance.dom, instance.element.props, element.props);
        instance.childInstances = reconcileChildren(instance, element);
        instance.element = element;
        return instance;
    } else {
        if (instance.publicInstance
            && instance.publicInstance.shouldcomponentUpdate) {
            if (!instance.publicInstance.shouldcomponentUpdate()) {
                return;
            }
        }
        // componentWillUpdate
        instance.publicInstance
            && instance.publicInstance.componentWillUpdate
            && instance.publicInstance.componentWillUpdate();
        instance.publicInstance.props = element.props;
        let newChildElement;
        if (instance.publicInstance) { // 类组件
            instance.publicInstance.props = element.props;
            newChildElement = instance.publicInstance.render();
        } else { // 函数式组件
            newChildElement = instance.fn(element.props);
        }
        const oldChildInstance = instance.childInstance;
        const newChildInstance = reconcile(parentDom, oldChildInstance, newChildElement);
        // componentDidUpdate
        instance.publicInstance
            && instance.publicInstance.componentDidUpdate
            && instance.publicInstance.componentDidUpdate();
        instance.dom = newChildInstance.dom;
        instance.childInstance = newChildInstance;
        instance.element = element;
        return instance;
    }
}

function reconcileChildren(instance, element) {
    const {dom, childInstances} = instance;
    const newChildElements = element.props.children || [];
    const count = Math.max(childInstances.length, newChildElements.length);
    const newChildInstances = [];
    for (let i = 0; i < count; i++) {
        newChildInstances[i] = reconcile(dom, childInstances[i], newChildElements[i]);
    }
    return newChildInstances.filter(instance => instance !== null);
}

прочитай этоreconcileПосле алгоритма некоторым людям должно быть любопытно, почему этот алгоритм называетсяstackАлгоритм кратко описан здесь. Как видно из предыдущей реализации, каждый раз, когда компонентstateобновление вызоветreconcileреализация, при этомreconcileВыполнение также является рекурсивным процессом, и он не останавливается до тех пор, пока все узлы не будут выполнены рекурсивно в начале, поэтому он называетсяstackалгоритм. Поскольку это рекурсивный процесс,diffКак только алгоритм запущен, он должен быть выполнен, поэтому поток может быть заблокирован, а поскольку js является однопоточным, это может повлиять на ввод пользователя или частоту кадров рендеринга пользовательского интерфейса, уменьшая взаимодействие с пользователем. ноreact16обновлен доfiberАрхитектура, эта проблема решена.

4.6 Общий код

Объединение всего этого кода из предыдущей реализации завершено.简版react, меньше, чем200Линейный код, так просто~! Посмотреть полный кодDiyReact.

5 Волоконная архитектура

react16улучшенныйreconcileАрхитектура алгоритмов, отstackобновитесь доfiberАрхитектура, о которой мы упоминали ранееstackНедостаток архитектуры в том, что она реализована рекурсивно, после запуска ее нельзя поставить на паузу, и она может быть выполнена только за один раз. пользовательский опыт.

а такжеfiberАрхитектура другая. Нижний слой основан наrequestIdleCallbackпланироватьdiffВыполнение алгоритма, наrequestIdleCallbackДля введения, пожалуйста, обратитесь к моей предыдущей статье о js事件循环статьяцикл событий javascript (сторона браузера, сторона узла).requestIdlecallbackКак следует из названия, функция состоит в том, чтобы использовать свободное время для выполнения задач. Обратите внимание здесь空闲时间Это относительно задач с более высоким приоритетом (таких как пользовательский ввод, рендеринг пользовательского интерфейса).

Вот краткое введениеfiberПроисхождение названия, потому что мне сначала было любопытно, почему оно так называетсяfiber.fiberФактически纤程Значение не новое слово, можно посмотреть объяснение ВикипедииFiber (computer science). На самом деле, я хочу выразить更加精细粒度的调度смысл, потому что на основе этого алгоритмаreactМожно поставить на паузу в любой моментdiffВыполнение алгоритма, а потом есть время простоя на его выполнение, что более精细Поэтому алгоритм планирования называетсяfiberАрхитектура. Эта статья правильнаяfiberСначала я кратко представлю их, а затем, когда будет время, резюмирую их по отдельности.

6 ссылок

В основном обратитесь к следующей информации:

  1. React Components, Elements, and Instances
  2. Refs and the DOM
  3. Введение в HTML-элемент
  4. Didact: a DIY guide to build your own React
  5. How Does React Tell a Class from a Function?
  6. Lin Clark - A Cartoon Intro to Fiber - React Conf 2017
  7. Давайте влюбимся в React Fiber