Mecanismo de renderização Angular 6 e Ivy

imagem Boa tarde, colegas. Estamos pensando em atualizar o livro de Jacob Fine e Anton Moiseev " Angular e TypeScript. Construção de sites para profissionais ". Uma nova edição sai neste outono e inclui material sobre Angular 5 e 6.

Inicialmente, pensamos em publicar material sobre o mecanismo Ivy, que provavelmente é a inovação mais interessante no Angular 6, mas paramos em uma publicação mais geral do Cedric Exbright (o original foi lançado em maio).

No Angular 6, houve muitas inovações sérias; além disso, as mais importantes delas não podem ser citadas como características: este é Ivy, um novo mecanismo de renderização. Como o mecanismo ainda é experimental, falaremos sobre isso no final deste artigo e começaremos com outros novos recursos e mudanças revolucionárias.

Fornecedores instáveis ​​em árvores

Agora, existe uma maneira nova e recomendada de registrar o provedor diretamente no decorador @Injectable() , usando o novo atributo providedIn . Leva 'root' como o valor de qualquer módulo em seu aplicativo. Ao usar 'root' objeto implementado será registrado no aplicativo como um solitário e você não precisará adicioná-lo aos provedores no módulo raiz. Da mesma forma, ao usar o método providedIn: UsersModule objeto implementado é registrado como o provedor UsersModule e não é adicionado aos provedores do módulo.

 @Injectable({ providedIn: 'root' }) export class UserService { } 

Um novo método foi introduzido para remover melhor o código não funcional do aplicativo (trepidação de árvores). Atualmente, a situação é tal que o serviço que está sendo adicionado aos provedores do módulo terminará no conjunto final, mesmo que não seja usado no aplicativo - e permitir isso é um pouco triste. Se você usar carregamento lento, poderá cair em várias armadilhas ao mesmo tempo ou encontrar-se em uma situação em que o serviço será inserido no conjunto errado.

É improvável que essa situação nos aplicativos ocorra com frequência (se você escreve um serviço e depois o usa), mas os módulos de terceiros às vezes oferecem serviços que não precisamos - como resultado, temos um monte de JavaScript inútil.

Portanto, esse recurso será especialmente útil para desenvolvedores de bibliotecas, mas agora é recomendável registrar objetos implementados dessa maneira - isso também se aplica aos desenvolvedores de aplicativos. Agora, a nova CLI ainda usa o andaime providedIn: 'root' por padrão ao trabalhar com serviços.

Na mesma linha, agora você pode declarar um InjectionToken , registrá-lo diretamente com o providedIn e adicionar uma factory aqui:

 export const baseUrl = new InjectionToken<string>('baseUrl', { providedIn: 'root', factory: () => 'http://localhost:8080/' }); 

Observe: isso também simplifica o teste de unidade. Para os fins de tais testes, eles são usados ​​para registrar o serviço com os provedores do módulo de teste. Aqui está o que fizemos antes:

 beforeEach(() => TestBed.configureTestingModule({ providers: [UserService] })); 

Agora, se o UserService usar a providedIn: 'root' :

 beforeEach(() => TestBed.configureTestingModule({})); 

Apenas não se preocupe: todos os serviços registrados com o providedIn não são carregados no teste, mas são instanciados com preguiça, apenas nos casos em que são realmente necessários.

Rxjs 6

O Angular 6 agora usa o RxJS 6 internamente, portanto, você precisa atualizar o aplicativo com isso em mente.

E ... O RxJS 6 está mudando a abordagem de importação!

No RxJS 5, você poderia escrever:

 import { Observable } from 'rxjs/Observable'; import 'rxjs/add/observable/of'; import 'rxjs/add/operator/map'; const squares$: Observable<number> = Observable.of(1, 2) .map(n => n * n); 

No RxJS 5.5, apareceram instruções pipeable:

 import { Observable } from 'rxjs/Observable'; import { of } from 'rxjs/observable/of'; import { map } from 'rxjs/operators'; const squares$: Observable<number> = of(1, 2).pipe( map(n => n * n) ); 

E no RxJS 6.0, as importações foram alteradas:

 import { Observable, of } from 'rxjs'; import { map } from 'rxjs/operators'; const squares$: Observable<number> = of(1, 2).pipe( map(n => n * n) ); 

Portanto, um dia você terá que alterar as importações em todo o aplicativo. Escrevo “uma vez” e não “agora”, porque a biblioteca compatível com rxJs foi lançada no RxJS, que permite fazer o download do RxJS para a versão 6.0, mesmo que as versões antigas ainda sejam usadas em todo o aplicativo ou em uma das bibliotecas usadas sintaxe.

A equipe do Angular escreveu um documento inteiro sobre esse assunto e é absolutamente necessário lê-lo antes de migrar para o Angular 6.0.

Observe: aqui está um conjunto de regras tslint muito legal chamado rxjs-tslint . Existem apenas quatro regras e, se você as adicionar ao projeto, o sistema migrará automaticamente todas as suas importações e códigos RxJS, e isso é feito com o tslint --fix ! Afinal, se você ainda não sabe, no tslint existe uma opção de fix que corrige automaticamente todos os erros que encontrar! Ele pode ser usado de maneira ainda mais simples: instale globalmente o rxjs-tslint e execute rxjs-5-to-6-migrate -p src/tsconfig.app.json . Tentei o rxjs-tslint em um de nossos projetos e funcionou muito bem (execute-o pelo menos duas vezes para recolher também todas as importações). Confira o README deste projeto para obter mais detalhes: github.com/ReactiveX/rxjs-tslint .

Se você estiver interessado em aprender mais sobre o RxJS 6.0, recomendo o próximo relatório de Ben Lesch sobre ng-conf.

i18n

A perspectiva mais importante associada ao i18n é a capacidade de criar “i18n em tempo de execução”, sem a necessidade de criar o aplicativo separadamente para cada ponto local. Esse recurso ainda não está disponível (existem apenas protótipos) e o mecanismo Ivy será necessário para sua operação (mais sobre ele abaixo).

Outra mudança relacionada ao i18n já ocorreu e está disponível. O canal de moeda é otimizado da maneira mais eficiente: agora ele arredonda todas as moedas não para 2 dígitos, como antes, mas para o número desejado de dígitos (por exemplo, para 3 no caso do dinar do Bahrein ou 0 para o peso chileno).

Se necessário, esse valor pode ser recuperado programaticamente usando a nova função i18n getNumberOfCurrencyDigits .

Outras funções de formatação convenientes, como formatDate , formatCurrency , formatPercent e formatNumber também apareceram no formatNumber formatPercent .

Convenientemente, se você deseja aplicar as mesmas transformações feitas nos canais, faça-o a partir do código TypeScript.

Animações

No Angular 6.0, as animações já são possíveis sem o polyfill web-animations-js , a menos que você use o AnimationBuilder . Seu aplicativo pode ganhar alguns bytes preciosos! Caso o navegador não suporte a API element.animate , o Angular 6.0 reverterá para o uso de quadros-chave CSS.

Elementos angulares

Angular Elements é um projeto que permite agrupar componentes Angular como componentes da Web e incorporá-los em um aplicativo que não usa Angular. A princípio, esse projeto existia apenas no “Laboratório Angular” (ou seja, ainda é experimental). Com a v6, ela chega à vanguarda e é oficialmente incluída na estrutura. Este é um grande tópico que merece um artigo separado.

ElementRef <T>

Se você deseja obter um link de elemento em seu modelo, pode usar @ViewChild ou @ViewChildren , ou mesmo implementar diretamente ElementRef . A desvantagem nesse caso é a seguinte: no Angular 5.0 ou inferior, o ElementRef especificado obterá o tipo any para a propriedade nativeElement .

No Angular 6.0, você pode digitar ElementRef mais rigorosamente, se desejar:

 @ViewChild('loginInput') loginInput: ElementRef<HTMLInputElement>; ngAfterViewInit() { // nativeElement  `HTMLInputElement` this.loginInput.nativeElement.focus(); } 

O que é reconhecido como indesejável e o que está mudando fundamentalmente

Vamos falar sobre o que você precisa ter em mente ao iniciar a migração!

preserveWhitespaces : padrão false

Na seção “Problemas que podem ocorrer durante a atualização”, observamos que preserveWhitespaces agora é false por padrão. Essa opção apareceu no Angular 4.4 e se você está se perguntando o que esperar ao mesmo tempo - aqui está uma publicação completa sobre esse tópico. Desmancha prazeres: tudo pode fazer, ou pode quebrar completamente seus modelos.

ngModel e formas reativas

Anteriormente, era possível fornecer o mesmo campo de formulário com ngModel e formControl , mas hoje essa prática é considerada indesejável e não será mais suportada no Angular 7.0.

Um pouco de confusão surge aqui, e todo o mecanismo, talvez, não funcionou como o esperado ( ngModel era uma diretiva há pouco tempo familiar a você, mas a entrada / saída da diretiva formControl , que executa quase a mesma tarefa, mas não idêntica).

Então agora, se aplicarmos o código:

 <input [(ngModel)]="user.name" [formControl]="nameCtrl"> 

então recebemos um aviso.

Você pode configurar o aplicativo para exibir um aviso de always ( once ), once (uma vez) ou never (nunca). O padrão é always .

 imports: [ ReactiveFormsModule.withConfig({ warnOnNgModelWithFormControl: 'never' }); ] 

De uma forma ou de outra, preparando a transição para o Angular 7, você precisa adaptar o código para usar formulários orientados a modelos ou reativos.

Projeto Ivy: novo (novo) mecanismo de renderização no Angular

Tããão ... Esta é a quarta versão principal do Angular (2, 4, 5, 6) e o mecanismo de renderização está sendo reescrito pela terceira vez!

Lembre-se: Angular compila seus modelos em código TypeScript equivalente. Então este TypeScript é compilado com o TypeScript que você escreveu em JavaScript, e o resultado está à disposição do usuário. E antes de nós já é a terceira versão desse mecanismo de renderização no Angular (a primeira foi no lançamento inicial do Angular 2.0 e a segunda no Angular 4.0).

Nesta nova versão do mecanismo de renderização, a abordagem para escrever modelos não muda, no entanto, otimiza vários indicadores, em particular:

  • Tempo de construção
  • Tamanho de discagem

Tudo isso ainda é profundamente experimental, e o novo mecanismo de renderização Ivy é ativado por uma caixa de seleção, que você deve colocar nas opções do compilador (no arquivo tsconfig.json ), se quiser experimentá-lo.

 "angularCompilerOptions": { "enableIvy": true } 

Observe que esse mecanismo talvez não seja muito confiável, portanto, não o use ainda na produção. Talvez ele ainda não funcione. Porém, em um futuro próximo, ela será aceita como opção padrão, portanto, tente uma vez, veja se funciona no seu aplicativo e quais os benefícios dele.

Vamos discutir com mais detalhes como o Ivy difere do mecanismo de renderização mais antigo.

Código gerado pelo mecanismo antigo

Vejamos um pequeno exemplo: vamos ter um componente PonyComponent que usa o modelo PonyModel (com os parâmetros de name e color ) e exibe a imagem do pônei (dependendo do terno), bem como o nome do pônei.

É assim:

 @Component({ selector: 'ns-pony', template: `<div> <ns-image [src]="getPonyImageUrl()"></ns-image> <div></div> </div>` }) export class PonyComponent { @Input() ponyModel: PonyModel; getPonyImageUrl() { return `images/${this.ponyModel.color}.png`; } } 

O mecanismo de renderização introduzido no Angular 4 gerou uma classe chamada ngfactory para cada modelo. A classe geralmente continha (código simplificado):

 export function View_PonyComponent_0() { return viewDef(0, [ elementDef(0, 0, null, null, 4, "div"), elementDef(1, 0, null, null, 1, "ns-image", View_ImageComponent_0), directiveDef(2, 49152, null, 0, i2.ImageComponent, { src: [0, "src"] }), elementDef(3, 0, null, null, 1, "div"), elementDef(4, null, ["", ""]) ], function (check, view) { var component = view.component; var currVal_0 = component.getPonyImageUrl(); check(view, 2, 0, currVal_0); }, function (check, view) { var component = view.component; var currVal_1 = component.ponyModel.name; check(view, 4, 0, currVal_1); }); } 

É difícil de ler, mas as principais partes deste código são descritas a seguir:

  • A estrutura do DOM criado, que contém as definições dos elementos ( figure , img , figcaption ), seus atributos e definições dos nós de texto. Cada elemento da estrutura DOM na definição de matriz de visualizações é representado por seu próprio índice.
  • Funções de detecção de alterações; o código contido neles verifica se as expressões usadas no modelo resultam nos mesmos valores de antes. Aqui, o resultado do método getPonyImageUrl é getPonyImageUrl e, se for alterado, o valor de entrada para o componente de imagem é atualizado. O mesmo se aplica ao apelido do pônei: se ele mudar, o nó de texto que contém esse apelido será atualizado.

Código gerado por Ivy

Se trabalharmos com o Angular 6 e o ​​sinalizador enableIvy definido como true , um ngfactory separado não será gerado no mesmo exemplo; As informações serão incorporadas diretamente no campo estático do próprio componente (código simplificado):

 export class PonyComponent { static ngComponentDef = defineComponent({ type: PonyComponent, selector: [['ns-pony']], factory: () => new PonyComponent(), template: (renderFlag, component) { if (renderFlag & RenderFlags.Create) { elementStart(0, 'figure'); elementStart(1, 'ns-image'); elementEnd(); elementStart(2, 'div'); text(3); elementEnd(); elementEnd(); } if (renderFlag & RenderFlags.Update) { property(1, 'src', component.getPonyImageUrl()); text(3, interpolate('', component.ponyModel.name, '')); } }, inputs: { ponyModel: 'ponyModel' }, directives: () => [ImageComponent]; }); // ...   } 

Agora tudo está contido neste campo estático. O atributo template contém o equivalente ao familiar ngfactory , com uma estrutura ligeiramente diferente. A função de template , como antes, será iniciada em qualquer alteração, mas agora tem dois modos:

  • Modo de criação: o componente está sendo criado, contém os nós estáticos do DOM que precisam ser criados
  • O restante da função é executado a cada alteração (se necessário, atualiza a fonte da imagem e o nó de texto).

O que isso muda?

Agora todos os decoradores são criados diretamente em suas classes (o mesmo para @Injectable , @Pipe , @Directive ) e, para gerá-los, você só precisa saber sobre o decorador atual. Esse fenômeno é chamado pela equipe Angular de “princípio da localidade”: para recompilar um componente, você não precisa re-analisar o aplicativo.

O código gerado é um pouco reduzido, mas, mais importante, é possível eliminar várias dependências, acelerando a recompilação se uma das partes do aplicativo for alterada. Além disso, com os coletores modernos, por exemplo, o Webpack, tudo se torna muito mais bonito: o código não funcional é cortado com segurança, as partes da estrutura que você não usa. Por exemplo, se você não possui canais no aplicativo, a estrutura necessária para sua interpretação nem sequer é incluída no conjunto final.

Estamos acostumados a tornar pesado o código angular. Às vezes, não é assustador, mas o Hello World pesando 37 kb após minificação e compactação é demais. Quando Ivy é responsável por gerar o código, o código não funcional é cortado com muito mais eficiência. Agora, o Hello World após a minificação é compactado para 7,3 kb e após a compactação - apenas para 2,7 kb, e essa é uma grande diferença. Aplicativo TodoMVC após compactação - apenas 12,2 kb. Trata-se de dados da equipe Angular, e outras pessoas não conseguiram resolver o problema, pois para que Ivy funcione como descrito aqui, você ainda precisará corrigi-lo manualmente.

Para mais detalhes, confira esta palestra com ng-conf.

Compatibilidade com bibliotecas existentes

Você pode estar interessado em: o que acontecerá com as bibliotecas que já são publicadas no formato antigo se o Ivy for usado em seu projeto? Não se preocupe: o mecanismo criará uma versão compatível com Ivy das dependências do seu projeto, mesmo se elas forem compiladas sem o Ivy. Não vou expor o interior agora, mas todos os detalhes devem ser transparentes.

Novos recursos

Vamos considerar quais novas oportunidades teremos ao trabalhar com esse mecanismo de exibição.

Propriedades particulares em modelos

Um novo mecanismo adiciona um novo recurso ou alteração potencial.
Essa situação está diretamente relacionada ao fato de que a função de modelo está incorporada no campo estático do componente: agora podemos usar as propriedades particulares de nossos componentes nos modelos. Isso era impossível anteriormente, por causa do qual fomos forçados a manter públicos todos os campos e métodos do componente usado no modelo, e eles caíram em uma classe separada ( ngfactory ). Ao acessar uma propriedade privada de outra classe, a compilação do TypeScript falha. Agora está no passado: como a função de modelo está em um campo estático, ela tem acesso às propriedades particulares do componente.

Vi um comentário dos membros da equipe Angular sobre o fato de que não é recomendado o uso de propriedades particulares em modelos, embora isso agora seja possível - já que isso pode ser proibido novamente no futuro ... portanto, provavelmente é mais prudente continuar usando apenas campos públicos em modelos! De qualquer forma, agora é mais fácil escrever testes de unidade, porque o teste pode verificar o estado de um componente sem gerar nem verificar o DOM para isso.

i18n em tempo de execução

Observe: o novo mecanismo de renderização finalmente abre uma oportunidade muito esperada para nós e fornece "i18n em tempo de execução". No momento da redação, ela ainda não estava pronta, mas vimos vários commits ao mesmo tempo, e este é um bom sinal!
O legal é que você não precisa alterar muito seu aplicativo se já estiver trabalhando com o i18n. Mas agora você não precisa recriar o aplicativo para cada localidade que planeja oferecer suporte - basta fazer o upload do JSON com traduções para cada localidade, e a Angular cuidará do resto!

Bibliotecas AoT

Atualmente, uma biblioteca lançada no NPM deve publicar o arquivo metadata.json e não pode publicar o código AoT de seus componentes. Isso é triste, pois os custos associados a essa montagem são repassados ​​para o nosso aplicativo. Com o Ivy, não há necessidade de um arquivo de metadados, e os autores da biblioteca agora poderão publicar seu código AoT diretamente no NPM!

Faixas de pilha aprimoradas

Agora, o código gerado deve fornecer rastreamentos aprimorados de pilha, se você tiver um problema com seus modelos - resultar em um erro puro indicando a linha do modelo em que ocorreu. Você pode até definir pontos de interrupção nos modelos e acompanhar o que realmente está acontecendo no Angular.

NgModule desaparecerá?

Essa ainda é uma perspectiva distante, mas talvez no futuro seja possível ficar sem o NgModules. Os primeiros sinais de tais mudanças são provedores que podem tremer em árvore e é lógico supor que Ivy tenha todos os blocos básicos necessários para aqueles que estão prontos para abandonar gradualmente os NgModules (ou, pelo menos, torná-los menos responsivos). É verdade que tudo isso ainda está no futuro, seremos pacientes.

Não haverá muitos recursos novos nesta versão, mas Ivy é definitivamente interessante para o futuro. Experimente - eu me pergunto como você vai gostar!

Source: https://habr.com/ru/post/pt419389/


All Articles