Codebase Overview

Esta seção fornecerá uma visão geral da organização do código-base React, suas convenções e implementação.

Se você quer contribuir para o React, esperamos que este guia ajude você a se sentir mais à vontade para fazer mudanças.

Não recomendamos necessariamente nenhuma dessas convenções nos aplicativos React. Muitas delas existem por razões históricas e podem mudar com o tempo.

Dependências Externas

O React quase não tem dependências externas. Geralmente, um require() aponta para um arquivo no própria código-base do React. No entanto, existem algumas exceções relativamente raras.

O repositório fbjs existe porque o React compartilha alguns pequenos utilitários com bibliotecas como Relay, e nós os mantemos em sincronia. Não dependemos de módulos pequenos equivalentes no ecossistema do Node porque queremos que os engenheiros do Facebook possam fazer alterações neles sempre que necessário. Nenhum dos utilitários dentro dos fbjs são considerados APIs públicas, e são destinados apenas para uso por projetos do Facebook, como o React.

Pastas de nível superior

Depois de clonar o repositório do React, você verá algumas pastas no nível superior:

  • packages contém metadados (como package.json) e o código fonte (subdiretório src) para todos os pacotes no repositório React. Se a sua alteração está relacionada ao código, o subdiretório src de cada pacote é onde você passará a maior parte do seu tempo.

  • fixtures contém alguns pequenos aplicativos de teste do React para os contribuidores.

  • build é a saída de compilação do React. Não está no repositório, mas aparecerá no seu clone do React depois de você fizer o build pela primeira vez.

A documentação está hospedada em um repositório separado do React.

Existem algumas outras pastas no nível superior, mas elas são usadas principalmente para as ferramentas e você provavelmente nunca as encontrará ao contribuir.

Testes Colocados

Nós não temos um diretório no nível superior para testes unitários. Em vez disso, nós os colocamos em um diretório chamado __tests__ relativo aos arquivos que eles testam.

Por exemplo, um teste para setInnerHTML.js está localizado em __tests__/setInnerHTML-test.js ao lado dele.

Avisos e Invariantes

O código-base do React usa o módulo warning para exibir avisos:

var warning = require('warning');

warning(
  2 + 2 === 4,
  'O Math não está funcionando hoje.'
);

O aviso é mostrado quando a condição warning é false.

Uma maneira de pensar sobre isso é que a condição deve refletir a situação normal e não a excepcional.

É uma boa ideia evitar spam no console com avisos duplicados:

var warning = require('warning');

var didWarnAboutMath = false;
if (!didWarnAboutMath) {
  warning(
    2 + 2 === 4,
  'O Math não está funcionando hoje.'
  );
  didWarnAboutMath = true;
}

Os alertas só são ativados no desenvolvimento. Na produção, eles são retirados. Se você precisar impedir a execuçāo de algum caminho do código, use o módulo invariant em vez disso:

var invariant = require('invariant');

invariant(
  2 + 2 === 4,
  'Você não vai passar!'
);

O invariant é lançado quando a condição invariant é false.

“Invariant” é apenas uma maneira de dizer “essa condição sempre é verdadeira”. Você pode pensar nisso como fazer uma afirmação.

É importante manter o comportamento de desenvolvimento e produção similares. Então o invariant é lançado tanto no desenvolvimento quanto na produção. As mensagens de erro são substituídas automaticamente por códigos de erro em produção para evitar afetar negativamente o tamanho do byte.

Desenvolvimento e produção

Você pode usar a variável pseudo-global __DEV__ no código-base para proteger blocos de código usados apenas no desenvolvimento.

Ele é embutido durante a etapa de compilação e se transforma em verificações process.env.NODE_ENV! == 'production' nos builds do CommonJS.

Para builds autônomas, ele se torna true na build não-minificada e é completamente eliminado com os blocos if que ele guarda na construção minificada.

if (__DEV__) {
  // Esse código vai executar apenas no desenvolvimento.
}

Flow

Recentemente, começamos a introduzir verificações do Flow no código-base. Os arquivos marcados com a anotação @ flow no comentário do cabeçalho da licença estão sendo verificados com typecheck.

Aceitamos pull requests adicionando anotações do Flow ao código existente. Anotações de fluxo são assim:

ReactRef.detachRefs = function(
  instance: ReactInstance,
  element: ReactElement | string | number | null | false,
): void {
  // ...
}

Quando possível, o novo código deve usar anotações do Flow. Você pode executar o yarn flow localmente para verificar seu código com o Flow.

Injeção Dinâmica

React usa injeção dinâmica em alguns módulos. Embora seja sempre explícito, ainda é lamentável porque dificulta a compreensão do código. A principal razão pela qual existe é porque o React originalmente só suportava o DOM como um destino. O React Native começou como um fork do React. Nós tivemos que adicionar a injeção dinâmica para deixar o React Native anular alguns comportamentos.

Você pode ver módulos declarando suas dependências dinâmicas como este:

// Injetado dinamicamente
var textComponentClass = null;

// Depende do valor injetado dinamicamente
function createInstanceForText(text) {
  return new textComponentClass(text);
}

var ReactHostComponent = {
  createInstanceForText,

  // Fornece uma oportunidade para injeção dinâmica
  injection: {
    injectTextComponentClass: function(componentClass) {
      textComponentClass = componentClass;
    },
  },
};

module.exports = ReactHostComponent;

O campo injection não é tratado de maneira especial. Mas por convenção, isso significa que este módulo quer ter algumas dependências (presumivelmente específicas da plataforma) injetadas em tempo de execução.

Existem vários pontos de injeção no código-base. No futuro, pretendemos nos livrar do mecanismo de injeção dinâmica e conectar todas as peças estaticamente durante a construção.

Pacotes Múltiplos

React é um monorepo. Seu repositório contém vários pacotes separados para que suas alterações possam ser coordenadas em conjunto e os problemas residam em um só lugar.

React Core

O “core” do React inclui todas as React APIs de nível superior, por exemplo:

  • React.createElement()
  • React.Component
  • React.Children

O core React inclui apenas as APIs necessárias para definir os componentes. Não inclui o algoritmo de reconciliação ou qualquer código específico da plataforma. Ele é usado pelos componentes React DOM e React Native.

O código do React core está localizado em packages/react na árvore de origem. Está disponível no npm como o pacote react. A construção do navegador independente correspondente é chamada react.js,e exporta um global chamado React.

Renderizadores

O React foi originalmente criado para o DOM, mas depois foi adaptado para também suportar plataformas nativas com o React Native. Isso introduziu o conceito de “renderizadores” para as partes internas do React.

Os renderizadores gerenciam como uma árvore no React se transforma nas chamadas de subjacentes da plataforma.

Renderizadores tambéms são encontrados em packages/:

O único outro renderizador oficialmente suportado é o react-art. Costumava estar em um repositorio GitHub separado mas nós os movemos para a árvore de código principal.

Nota:

Tecnicamente o react-native-renderer é uma camada muito fina que ensina o React a interagir com a implementação do React Native. O código específico da plataforma real que gerencia as views nativas reside no repositório do React Native junto com seus componentes.

Reconciliadores

Até mesmo renderizadores muito diferentes, como o React DOM e o React Native, precisam compartilhar muita lógica. Em particular, o algoritmo de reconciliação deve ser o mais semelhante possível para que a renderização declarativa, os componentes personalizados, o state, os lifecycle méthods e os refs funcionem de maneira consistente em todas as plataformas.

Para resolver isso, diferentes renderizadores compartilham algum código entre eles. Nós chamamos essa parte do React de “reconciliador”. Quando uma atualização como setState() está agendado, o reconciliador chama o método render() em componentes na árvore e monta, atualiza ou desmonta.

Os reconciliadores não são empacotados separadamente porque atualmente não possuem uma API pública. Em vez disso, eles são usados exclusivamente por renderizadores como React DOM e React Native.

Reconciliador Stack

O reconciliador “stack” é a implementação que energiza o React 15 e o anterior. Desde então, paramos de usá-lo, mas está documentado em detalhes na próxima seção.

Reconciliador Fiber

O reconciliador de “fiber” é um novo esforço com o objetivo de resolver os problemas inerentes ao reconciliador de pilha e corrigir alguns problemas de longa data. Foi o reconciliador padrão desde o React 16.

Seus principais objetivos são:

  • Capacidade de dividir o trabalho ininterrupto em blocos.
  • Capacidade de priorizar, rebaixar e reutilizar o trabalho em andamento.
  • Capacidade de retroceder entre pais e filhos para dar suporte ao layout no React.
  • Capacidade de retornar vários elementos do método render().
  • Melhor suporte para limites de erro.

Você pode ler mais sobre a arquitetura do React Fiber aqui e aqui. Embora tenha sido fornecido com o React 16, os recursos assíncronos ainda não estão habilitados por padrão.

Seu código-fonte está localizado em packages/react-reconciler.

Sistema de Eventos

O React implementa um sistema de eventos sintéticos que é agnóstico dos renderizadores e funciona com React DOM e React Native.Seu código-fonte está localizado em packages/events.

Esse é um vídeo com mais profundidade no código (66 minutos).

Qual o proximo passo?

Leia a próxima seçãopara aprender sobre a implementação do reconciliador antes do React 16 em mais detalhes. Ainda não documentamos os componentes internos do novo reconciliador.