
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 árvoresAgora, 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 6O 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.
i18nA 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çõesNo 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 angularesAngular 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() {
O que é reconhecido como indesejável e o que está mudando fundamentalmenteVamos 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 reativasAnteriormente, 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 AngularTããã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 antigoVejamos 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 IvySe 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 existentesVocê 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 recursosVamos considerar quais novas oportunidades teremos ao trabalhar com esse mecanismo de exibição.
Propriedades particulares em modelosUm 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çãoObserve: 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 AoTAtualmente, 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 aprimoradasAgora, 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!