填写这份《一分钟调查》,帮我们(开发组)做得更好!去填写Home

RendererRenderer2 的迁移

Renderer to Renderer2 migration

迁移概述

Migration Overview

自 Angular 版本 4 开始,Renderer 类就被标记为已弃用。本节提供了一些指导,帮助你把这个已弃用的 API 迁移到更新的 Renderer2 API,并解释了它对你的应用而言意味着什么。

The Renderer class has been marked as deprecated since Angular version 4. This section provides guidance on migrating from this deprecated API to the newer Renderer2 API and what it means for your app.

为什么要迁移到 Renderer2?

Why should I migrate to Renderer2?

已弃用的 Renderer 类已经在 Angular 的 9 中删除了,所以有必要迁移到支持的 API。建议使用 Renderer2 策略,因为它支持与 Renderer 很相似的一组功能。这个 API 非常庞大(有 19 种方法),但原理图会帮你简化这个过程。

The deprecated Renderer class has been removed in version 9 of Angular, so it's necessary to migrate to a supported API. Using Renderer2 is the recommended strategy because it supports a similar set of functionality to Renderer. The API surface is quite large (with 19 methods), but the schematic should simplify this process for your applications.

完成后我还要采取什么行动吗?

Is there action required on my end?

这个原理图应该可以处理大多数情况,仅仅不支持 Renderer.animate()Renderer.setDebugInfo()

No. The schematic should handle most cases with the exception of Renderer.animate() and Renderer.setDebugInfo(), which already aren't supported.

什么是 __ngRendererX 方法族?为什么要用它们?

What are the __ngRendererX methods? Why are they necessary?

有些方法在 Renderer2 没有完全等价的东西,或者对应于多个表达式。例如,这两种渲染器都有 createElement() 的方法,但它们是不相等的,比如在 Renderer 中调用 renderer.createElement(parentNode, namespaceAndName) 时对应 Renderer2 中的如下代码块 :

Some methods either don't have exact equivalents in Renderer2, or they correspond to more than one expression. For example, both renderers have a createElement() method, but they're not equal because a call such as renderer.createElement(parentNode, namespaceAndName) in the Renderer corresponds to the following block of code in Renderer2:

const [namespace, name] = splitNamespace(namespaceAndName); const el = renderer.createElement(name, namespace); if (parentNode) { renderer.appendChild(parentNode, el); } return el;
      
      const [namespace, name] = splitNamespace(namespaceAndName);
const el = renderer.createElement(name, namespace);
if (parentNode) {
  renderer.appendChild(parentNode, el);
}
return el;
    

迁移必须保证函数和变量类型的返回值不变。此原理图会安全地处理大多数情况,它会在用户文件的底部声明一些辅助函数。这些辅助函数封装了你自己的逻辑,把代码中要被替换的内容放到单个函数调用中。这里是 createElement() 迁移工作原理的一个例子:

Migration has to guarantee that the return values of functions and types of variables stay the same. To handle the majority of cases safely, the schematic declares helper functions at the bottom of the user's file. These helpers encapsulate your own logic and keep the replacements inside your code down to a single function call. Here's an example of how the createElement() migration looks:

之前:

Before:

public createAndAppendElement() { const el = this.renderer.createElement('span'); el.textContent = 'hello world'; return el; }
      
      public createAndAppendElement() {
  const el = this.renderer.createElement('span');
  el.textContent = 'hello world';
  return el;
}
    

之后:

After:

public createAndAppendElement() { const el = __ngRendererCreateElement(this.renderer, this.element, 'span'); el.textContent = 'hello world'; return el; } // Generated code at the bottom of the file __ngRendererCreateElement(renderer: any, parentNode: any, nameAndNamespace: any) { const [namespace, name] = __ngRendererSplitNamespace(namespaceAndName); const el = renderer.createElement(name, namespace); if (parentNode) { renderer.appendChild(parentNode, el); } return el; } __ngRendererSplitNamespace(nameAndNamespace: any) { // returns the split name and namespace }
      
      public createAndAppendElement() {
  const el = __ngRendererCreateElement(this.renderer, this.element, 'span');
  el.textContent = 'hello world';
  return el;
}
// Generated code at the bottom of the file
__ngRendererCreateElement(renderer: any, parentNode: any, nameAndNamespace: any) {
  const [namespace, name] = __ngRendererSplitNamespace(namespaceAndName);
  const el = renderer.createElement(name, namespace);
  if (parentNode) {
    renderer.appendChild(parentNode, el);
  }
  return el;
}
__ngRendererSplitNamespace(nameAndNamespace: any) {
  // returns the split name and namespace
}
    

当实现了这些辅助函数时,此原理图会确保它们在每个文件中都只声明一次,并且它们的名字是唯一的,因此和你的代码中已经存在的函数几乎不可能冲突。该原理图还会把它们的参数类型设为 any, 使其不必插入额外的逻辑就能确保它们的值有正确的类型。

When implementing these helper functions, the schematic ensures that they're only declared once per file and that their names are unique enough that there's a small chance of colliding with pre-existing functions in your code. The schematic also keeps their parameter types as any so that it doesn't have to insert extra logic that ensures that their values have the correct type.

我是库作者。我应该运行这次迁移吗?

I’m a library author. Should I run this migration?

库的作者肯定要做这种迁移来远离 Renderer。否则,这些库将不再适用于用 Angular 9 构建的应用。

Library authors should definitely use this migration to move away from the Renderer. Otherwise, the libraries won't work with applications built with version 9.

方法迁移的完整列表

Full list of method migrations

下表列出了从 RendererRenderer2 要迁移的所有方法。

The following table shows all methods that the migration maps from Renderer to Renderer2.

RendererRenderer2
listen(renderElement, name, callback)listen(renderElement, name, callback)
setElementProperty(renderElement, propertyName, propertyValue)setProperty(renderElement, propertyName, propertyValue)
setText(renderNode, text)setValue(renderNode, text)
listenGlobal(target, name, callback)listen(target, name, callback)
selectRootElement(selectorOrNode, debugInfo?)selectRootElement(selectorOrNode)
createElement(parentElement, name, debugInfo?)appendChild(parentElement, createElement(name))
setElementStyle(el, style, value?)value == null ? removeStyle(el, style) : setStyle(el, style, value)
setElementAttribute(el, name, value?)attributeValue == null ? removeAttribute(el, name) : setAttribute(el, name, value)
createText(parentElement, value, debugInfo?)appendChild(parentElement, createText(value))
createTemplateAnchor(parentElement)appendChild(parentElement, createComment(''))
setElementClass(renderElement, className, isAdd)isAdd ? addClass(renderElement, className) : removeClass(renderElement, className)
projectNodes(parentElement, nodes)for (let i = 0; i < nodes.length; i++) { appendChild(parentElement, nodes[i]); }
attachViewAfter(node, viewRootNodes)const parentElement = parentNode(node); const nextSibling = nextSibling(node); for (let i = 0; i < viewRootNodes.length; i++) { insertBefore(parentElement, viewRootNodes[i], nextSibling);}
detachView(viewRootNodes)for (let i = 0; i < viewRootNodes.length; i++) {const node = viewRootNodes[i]; const parentElement = parentNode(node); removeChild(parentElement, node);}
destroyView(hostElement, viewAllNodes)for (let i = 0; i < viewAllNodes.length; i++) { destroyNode(viewAllNodes[i]); }
setBindingDebugInfo()

该函数在 Renderer2 中无用。

This function is a noop in Renderer2.

createViewRoot(hostElement)

应该替换成到 hostElement 的引用

Should be replaced with a reference to hostElement

invokeElementMethod(renderElement, methodName, args?)(renderElement as any)[methodName].apply(renderElement, args);
animate(element, startingStyles, keyframes, duration, delay, easing, previousPlayers?)

抛出一个错误 (和 Renderer.animate() 一样)

Throws an error (same behavior as Renderer.animate())