diff --git a/content/docs/implementation-notes.md b/content/docs/implementation-notes.md
index b345f7d5d..d9a72aca8 100644
--- a/content/docs/implementation-notes.md
+++ b/content/docs/implementation-notes.md
@@ -1,6 +1,6 @@
---
id: implementation-notes
-title: Implementation Notes
+title: Детали реализации
layout: contributing
permalink: docs/implementation-notes.html
prev: codebase-overview.html
@@ -9,93 +9,93 @@ redirect_from:
- "contributing/implementation-notes.html"
---
-This section is a collection of implementation notes for the [stack reconciler](/docs/codebase-overview.html#stack-reconciler).
+В этой главе собраны примеры из реализации [согласователя Stack](/docs/codebase-overview.html#stack-reconciler).
-It is very technical and assumes a strong understanding of React public API as well as how it's divided into core, renderers, and the reconciler. If you're not very familiar with the React codebase, read [the codebase overview](/docs/codebase-overview.html) first.
+Они очень специфичны и требуют хорошего знания React API, а также ядра, рендереров и согласователя. Если вы не очень хорошо знакомы с архитектурой React, тогда изучите главу [Архитектура кодовой базы](/docs/codebase-overview.html), а затем вернитесь к этой.
-It also assumes an understanding of the [differences between React components, their instances, and elements](/blog/2015/12/18/react-components-elements-and-instances.html).
+Также предполагается, что вы понимаете [разницу между компонентами, их экземплярами и элементами в React](/blog/2015/12/18/react-components-elements-and-instances.html).
-The stack reconciler was used in React 15 and earlier. It is located at [src/renderers/shared/stack/reconciler](https://github.com/facebook/react/tree/15-stable/src/renderers/shared/stack/reconciler).
+Согласователь Stack использовался в React 15 и более ранних версиях. Его код находится в каталоге [src/renderers/shared/stack/reconciler](https://github.com/facebook/react/tree/15-stable/src/renderers/shared/stack/reconciler).
-### Video: Building React from Scratch {#video-building-react-from-scratch}
+### Видео: сборка React с нуля {#video-building-react-from-scratch}
-[Paul O'Shannessy](https://twitter.com/zpao) gave a talk about [building React from scratch](https://www.youtube.com/watch?v=_MAD4Oly9yg) that largely inspired this document.
+[Paul O'Shannessy](https://twitter.com/zpao) рассказал в своём докладе [как собрать React с нуля](https://www.youtube.com/watch?v=_MAD4Oly9yg), используя материал из этой главы.
-Both this document and his talk are simplifications of the real codebase so you might get a better understanding by getting familiar with both of them.
+В его докладе и этой главе описаны упрощённые реализации, поэтому, ознакомившись с ними, вы сможете лучше понять, как работает реальная реализация.
-### Overview {#overview}
+### Введение {#overview}
-The reconciler itself doesn't have a public API. [Renderers](/docs/codebase-overview.html#renderers) like React DOM and React Native use it to efficiently update the user interface according to the React components written by the user.
+Согласователь не имеет открытого API. [Рендереры](/docs/codebase-overview.html#renderers), такие как React DOM и React Native, используются, чтобы эффективно обновлять пользовательские UI-компоненты.
-### Mounting as a Recursive Process {#mounting-as-a-recursive-process}
+### Монтирование как рекурсивный процесс {#mounting-as-a-recursive-process}
-Let's consider the first time you mount a component:
+Давайте рассмотрим, как компонент монтируется в первый раз.
```js
ReactDOM.render(, rootEl);
```
-React DOM will pass `` along to the reconciler. Remember that `` is a React element, that is, a description of *what* to render. You can think about it as a plain object:
+React DOM передаст `` в согласователь. Запомните, что `` -- это React-элемент, т.е. описание того, что нужно отрендерить. Вы можете представлять его как просто объект.
```js
console.log();
// { type: App, props: {} }
```
-The reconciler will check if `App` is a class or a function.
+Согласователь будет проверять, чем является `App`: классом или функцией.
-If `App` is a function, the reconciler will call `App(props)` to get the rendered element.
+Если `App` -- функция, согласователь вызовет `App(props)`, чтобы получить элемент, который нужно отрендерить.
-If `App` is a class, the reconciler will instantiate an `App` with `new App(props)`, call the `componentWillMount()` lifecycle method, and then will call the `render()` method to get the rendered element.
+Если `App` -- класс, согласователь создаст экземпляр `App` с помощью `new App(props)`, вызовет метод жизненного цикла `componentWillMount()`, а затем вызовет `render()`, чтобы получить элемент, который нужно отрендерить.
-Either way, the reconciler will learn the element `App` "rendered to".
+В любом случае, согласователь изучит элемент `App`, чтобы узнать, что нужно отрендерить.
-This process is recursive. `App` may render to a ``, `Greeting` may render to a ``, and so on. The reconciler will "drill down" through user-defined components recursively as it learns what each component renders to.
+Этот процесс рекурсивен. `App` может рендерить ``, `Greeting` -- ``, и т.д. Согласователь будет рекурсивно погружаться в пользовательские компоненты, пока не узнает, что каждый компонент должен рендерить.
-You can imagine this process as a pseudocode:
+Рассмотрим этот процесс с помощью псевдокода:
```js
function isClass(type) {
- // React.Component subclasses have this flag
+ // Подклассы React.Component имеют соответствующий флаг
return (
Boolean(type.prototype) &&
Boolean(type.prototype.isReactComponent)
);
}
-// This function takes a React element (e.g. )
-// and returns a DOM or Native node representing the mounted tree.
+// Функция получает React-элемент (например, )
+// и возвращает узел, являющуюся вершиной DOM- или Native-дерева элементов.
function mount(element) {
var type = element.type;
var props = element.props;
- // We will determine the rendered element
- // by either running the type as function
- // or creating an instance and calling render().
+ // Мы будем вычислять необходимый элемент
+ // либо выполняя type как функцию,
+ // либо с помощью создания экземпляра и вызова метода render().
var renderedElement;
if (isClass(type)) {
- // Component class
+ // Компонент является классом
var publicInstance = new type(props);
- // Set the props
+ // Задать пропсы
publicInstance.props = props;
- // Call the lifecycle if necessary
+ // Если необходимо, вызвать метод жизненного цикла
if (publicInstance.componentWillMount) {
publicInstance.componentWillMount();
}
- // Get the rendered element by calling render()
+ // Получить необходимый элемент с помощью вызова render()
renderedElement = publicInstance.render();
} else {
- // Component function
+ // Компонент является функцией
renderedElement = type(props);
}
- // This process is recursive because a component may
- // return an element with a type of another component.
+ // Этот процесс может быть рекурсивным, потому что компонент может
+ // возвращать другой компонент.
return mount(renderedElement);
- // Note: this implementation is incomplete and recurses infinitely!
- // It only handles elements like or .
- // It doesn't handle elements like
or yet.
+ // Примечание: эта реализация не завершена и выполняется бесконечно!
+ // Обрабатывает такие элементы как и .
+ // Пока не обрабатывает такие элементы как и .
}
var rootEl = document.getElementById('root');
@@ -103,81 +103,81 @@ var node = mount();
rootEl.appendChild(node);
```
->**Note:**
+>**Примечание:**
>
->This really *is* a pseudo-code. It isn't similar to the real implementation. It will also cause a stack overflow because we haven't discussed when to stop the recursion.
+>Пример выше -- псевдокод. Он не является реальной реализацией. А также этот код приводит к переполнению стека, потому что мы не описали, когда нужно остановить рекурсию.
-Let's recap a few key ideas in the example above:
+Рассмотрим основные идеи этого кода:
-* React elements are plain objects representing the component type (e.g. `App`) and the props.
-* User-defined components (e.g. `App`) can be classes or functions but they all "render to" elements.
-* "Mounting" is a recursive process that creates a DOM or Native tree given the top-level React element (e.g. ``).
+* React-элементы -- просто объекты, описывающие тип компонента (например, `App`) и его пропсы.
+* Пользовательские компоненты могут быть как классами, так и функциями, но оба «рендерят» элементы.
+* «Монтирование» -- рекурсивный процесс, который создаёт DOM- или Native-дерево заданного React элемента верхнего уровня (например, ``).
-### Mounting Host Elements {#mounting-host-elements}
+### Монтирование базовых элементов {#mounting-host-elements}
-This process would be useless if we didn't render something to the screen as a result.
+Процесс монтирования может стать бесполезным, если мы не отобразим результат на экран.
-In addition to user-defined ("composite") components, React elements may also represent platform-specific ("host") components. For example, `Button` might return a `` from its render method.
+В дополнение к пользовательским («составным») компонентам, React-элементы также могут быть представлены платформо-специфическими («базовыми») компонентами. Например, `Button` может вернуть `` из метода render().
-If element's `type` property is a string, we are dealing with a host element:
+Если свойство `type` имеет тип string, значит мы имеем дело с базовым элементом:
```js
console.log();
// { type: 'div', props: {} }
```
-There is no user-defined code associated with host elements.
+Для базового элемента не существует пользовательского кода.
-When the reconciler encounters a host element, it lets the renderer take care of mounting it. For example, React DOM would create a DOM node.
+Когда согласователь встречает базовый элемент, ответственность за монтирование возьмёт на себя рендерер. Например, React DOM может создать DOM-узел.
-If the host element has children, the reconciler recursively mounts them following the same algorithm as above. It doesn't matter whether children are host (like `
`), composite (like ``), or both.
+Если элемент имеет потомков, согласователь рекурсивно монтирует их, следуя алгоритму выше. Потомки могут быть базовыми (например, `
`), составными (например, ``) или обоих типов.
-The DOM nodes produced by the child components will be appended to the parent DOM node, and recursively, the complete DOM structure will be assembled.
+DOM-узлы, созданные дочерними компонентами, будут добавлены к родительскому DOM-узлу, и рекурсивно будет собрана полная DOM-структура.
->**Note:**
+>**Примечание:**
>
->The reconciler itself is not tied to the DOM. The exact result of mounting (sometimes called "mount image" in the source code) depends on the renderer, and can be a DOM node (React DOM), a string (React DOM Server), or a number representing a native view (React Native).
+>Согласователь не связан с DOM. Точный результат монтирования (иногда называемый «смонтированный образ») зависит от рендерера и может быть DOM-узлом (React DOM), строкой (React DOM Server) или числом (React Native).
-If we were to extend the code to handle host elements, it would look like this:
+Если мы изменим код, чтобы он обрабатывал базовые элементы, то результат будет выглядеть вот так:
```js
function isClass(type) {
- // React.Component subclasses have this flag
+ // Подклассы React.Component имеют соответствующий флаг
return (
Boolean(type.prototype) &&
Boolean(type.prototype.isReactComponent)
);
}
-// This function only handles elements with a composite type.
-// For example, it handles and , but not a .
+// Эта функция обрабатывает только составные элементы.
+// Например, и , но не
function mountComposite(element) {
var type = element.type;
var props = element.props;
var renderedElement;
if (isClass(type)) {
- // Component class
+ // Компонент является классом
var publicInstance = new type(props);
- // Set the props
+ // Задать пропсы
publicInstance.props = props;
- // Call the lifecycle if necessary
+ // Если необходимо, вызвать метод жизненного цикла
if (publicInstance.componentWillMount) {
publicInstance.componentWillMount();
}
renderedElement = publicInstance.render();
} else if (typeof type === 'function') {
- // Component function
+ // Компонент является функцией
renderedElement = type(props);
}
- // This is recursive but we'll eventually reach the bottom of recursion when
- // the element is host (e.g. ) rather than composite (e.g. ):
+ // Эта функция рекурсивна, но иногда достигает границ рекурсии, когда
+ // встречает базовый элемент (такой, как ), вместо составного (такого, как ):
return mount(renderedElement);
}
-// This function only handles elements with a host type.
-// For example, it handles and but not an .
+// Эта функция обрабатывает только базовые элементы.
+// Например, и , но не .
function mountHost(element) {
var type = element.type;
var props = element.props;
@@ -187,9 +187,9 @@ function mountHost(element) {
}
children = children.filter(Boolean);
- // This block of code shouldn't be in the reconciler.
- // Different renderers might initialize nodes differently.
- // For example, React Native would create iOS or Android views.
+ // Этот блок кода не следует размещать в согласователе.
+ // Каждый рендеререр может инициализировать узлы по-своему.
+ // Например, React Native может создать представление для iOS или Android.
var node = document.createElement(type);
Object.keys(props).forEach(propName => {
if (propName !== 'children') {
@@ -197,29 +197,29 @@ function mountHost(element) {
}
});
- // Mount the children
+ // Монтировать потомков
children.forEach(childElement => {
- // Children may be host (e.g. ) or composite (e.g. ).
- // We will also mount them recursively:
+ // Потомки могут быть как базовыми (), так и составными ().
+ // Их мы тоже будем монтировать рекурсивно:
var childNode = mount(childElement);
- // This line of code is also renderer-specific.
- // It would be different depending on the renderer:
+ // Эта строка кода может отличаться
+ // в зависимости от рендерера
node.appendChild(childNode);
});
- // Return the DOM node as mount result.
- // This is where the recursion ends.
+ // Вернуть DOM ноду в качестве результата.
+ // Здесь рекурия заканчивается.
return node;
}
function mount(element) {
var type = element.type;
if (typeof type === 'function') {
- // User-defined components
+ // Пользовательский компонент
return mountComposite(element);
} else if (typeof type === 'string') {
- // Platform-specific components
+ // Платформо-специфический компонент
return mountHost(element);
}
}
@@ -229,40 +229,41 @@ var node = mount();
rootEl.appendChild(node);
```
-This is working but still far from how the reconciler is really implemented. The key missing ingredient is support for updates.
+Этот код работает, но всё ещё далёк от того, как согласователь реализован на самом деле. Отсутствует ключевая деталь -- поддержка обновлений.
-### Introducing Internal Instances {#introducing-internal-instances}
+### Введение во внутренние экземпляры {#introducing-internal-instances}
-The key feature of React is that you can re-render everything, and it won't recreate the DOM or reset the state:
+Ключевая особенность React -- ререндеринг всего без пересоздания DOM или сброса состояния:
```js
ReactDOM.render(, rootEl);
-// Should reuse the existing DOM:
+// Использовать уже существующий DOM:
ReactDOM.render(, rootEl);
```
-However, our implementation above only knows how to mount the initial tree. It can't perform updates on it because it doesn't store all the necessary information, such as all the `publicInstance`s, or which DOM `node`s correspond to which components.
+Однако, наша реализация знает только как монтировать начальное дерево. Она не может обновлять его, потому что не содержит необходимой информации, например, экземпляры `publicInstance`, или какой DOM-узел (`node`) соответствует компоненту.
-The stack reconciler codebase solves this by making the `mount()` function a method and putting it on a class. There are drawbacks to this approach, and we are going in the opposite direction in the [ongoing rewrite of the reconciler](/docs/codebase-overview.html#fiber-reconciler). Nevertheless this is how it works now.
-Instead of separate `mountHost` and `mountComposite` functions, we will create two classes: `DOMComponent` and `CompositeComponent`.
+Согласователь Stack решает эту проблему, сделав функцию `mount()` методом класса. В этом решении есть недостатки, поэтому мы решили [переписать согласователь](/docs/codebase-overview.html#fiber-reconciler). Однако, опишем, как он сейчас работает.
-Both classes have a constructor accepting the `element`, as well as a `mount()` method returning the mounted node. We will replace a top-level `mount()` function with a factory that instantiates the correct class:
+Вместо разделения на функции `mountHost` и `mountComposite`, мы создадим два класса:
+
+Оба класса имеют конструктор, принимающий `element`, а также имеют метод `mount()`, который возвращает необходимый узел. Заменим вызывающую функцию `mount()` на фабрику, которая будет создавать нужный класс:
```js
function instantiateComponent(element) {
var type = element.type;
if (typeof type === 'function') {
- // User-defined components
+ // Пользовательский компонент
return new CompositeComponent(element);
} else if (typeof type === 'string') {
- // Platform-specific components
+ // Платформо-специфический компонент
return new DOMComponent(element);
}
}
```
-First, let's consider the implementation of `CompositeComponent`:
+Для начала рассмотрим реализацию `CompositeComponent`:
```js
class CompositeComponent {
@@ -273,7 +274,7 @@ class CompositeComponent {
}
getPublicInstance() {
- // For composite components, expose the class instance.
+ // Для составных компонентов сделать экземпляр класса видимым.
return this.publicInstance;
}
@@ -285,45 +286,45 @@ class CompositeComponent {
var publicInstance;
var renderedElement;
if (isClass(type)) {
- // Component class
+ // Компонент является классом
publicInstance = new type(props);
- // Set the props
+ // Задать пропсы
publicInstance.props = props;
- // Call the lifecycle if necessary
+ // Если необходимо, вызвать метод жизненного цикла
if (publicInstance.componentWillMount) {
publicInstance.componentWillMount();
}
renderedElement = publicInstance.render();
} else if (typeof type === 'function') {
- // Component function
+ // Компонент является функцией
publicInstance = null;
renderedElement = type(props);
}
- // Save the public instance
+ // Сохранить внешний экземпляр
this.publicInstance = publicInstance;
- // Instantiate the child internal instance according to the element.
- // It would be a DOMComponent for or ,
- // and a CompositeComponent for or :
+ // Получить внутренний экземпляр в соответствии с элементом.
+ // Это может быть DOMComponent для или ,
+ // и CompositeComponent для или :
var renderedComponent = instantiateComponent(renderedElement);
this.renderedComponent = renderedComponent;
- // Mount the rendered output
+ // Монтировать полученный результат
return renderedComponent.mount();
}
}
```
-This is not much different from our previous `mountComposite()` implementation, but now we can save some information, such as `this.currentElement`, `this.renderedComponent`, and `this.publicInstance`, for use during updates.
+Это не сильно отличается от предыдущей реализации `mountComposite()`, однако теперь мы можем сохранять некоторую информацию, такую как `this.currentElement`, `this.renderedComponent` и `this.publicInstance`, чтобы использовать во время обновлений.
-Note that an instance of `CompositeComponent` is not the same thing as an instance of the user-supplied `element.type`. `CompositeComponent` is an implementation detail of our reconciler, and is never exposed to the user. The user-defined class is the one we read from `element.type`, and `CompositeComponent` creates an instance of it.
+Заметьте, что экземпляр `CompositeComponent`, это не то же самое, что экземпляр `element.type`. `CompositeComponent` -- деталь реализации нашего согласователя, которая не может быть доступна пользователю. Пользовательский класс -- это единственное, что мы читаем из `element.type`, а `CompositeComponent` создаёт его экземпляр.
-To avoid the confusion, we will call instances of `CompositeComponent` and `DOMComponent` "internal instances". They exist so we can associate some long-lived data with them. Only the renderer and the reconciler are aware that they exist.
+Чтобы избежать путаницы, мы будем называть экземпляры `CompositeComponent` и `DOMComponent` "внутренними экземплярами". Они существуют для того, чтобы мы могли сохранять в них некоторые долгоживущие данные. Только рендереры и согласователь знают об их существовании.
-In contrast, we call an instance of the user-defined class a "public instance". The public instance is what you see as `this` in the `render()` and other methods of your custom components.
+В противоположность, мы будем называть экземпляры пользовательских классов "внешними экземплярами". Внешние экземпляры -- это то, что вы видите как `this` внутри `render()` и других методов ваших компонентов.
-The `mountHost()` function, refactored to be a `mount()` method on `DOMComponent` class, also looks familiar:
+Функция `mountHost()`, переименованная в метод `mount()` класса `DOMComponent`, также будет выглядеть знакомо:
```js
class DOMComponent {
@@ -334,7 +335,7 @@ class DOMComponent {
}
getPublicInstance() {
- // For DOM components, only expose the DOM node.
+ // Для DOM-компонентов сделать DOM-узел видимым.
return this.node;
}
@@ -347,36 +348,36 @@ class DOMComponent {
children = [children];
}
- // Create and save the node
+ // Создать и сохранить узел
var node = document.createElement(type);
this.node = node;
- // Set the attributes
+ // Задать атрибуты
Object.keys(props).forEach(propName => {
if (propName !== 'children') {
node.setAttribute(propName, props[propName]);
}
});
- // Create and save the contained children.
- // Each of them can be a DOMComponent or a CompositeComponent,
- // depending on whether the element type is a string or a function.
+ // Создать и сохранить потомков.
+ // Каждый из них может быть либо DOMComponent, либо CompositeComponent,
+ // в зависимости от типа свойства type (строка или функция).
var renderedChildren = children.map(instantiateComponent);
this.renderedChildren = renderedChildren;
- // Collect DOM nodes they return on mount
+ // Собрать DOM-узлы, которые возвращает метод mount.
var childNodes = renderedChildren.map(child => child.mount());
childNodes.forEach(childNode => node.appendChild(childNode));
- // Return the DOM node as mount result
+ // Вернуть DOM-узел в качестве результата
return node;
}
}
```
-The main difference after refactoring from `mountHost()` is that we now keep `this.node` and `this.renderedChildren` associated with the internal DOM component instance. We will also use them for applying non-destructive updates in the future.
+Основное отличие от метода `mountHost()` в том, что теперь мы храним `this.node` и `this.renderedChildren` внутри DOMComponent. Мы будем использовать их в дальнейшем, чтобы не поломать структуру во время обновлений.
-As a result, each internal instance, composite or host, now points to its child internal instances. To help visualize this, if a function `` component renders a `