• <fieldset id="8imwq"><menu id="8imwq"></menu></fieldset>
  • <bdo id="8imwq"><input id="8imwq"></input></bdo>
    最新文章專題視頻專題問答1問答10問答100問答1000問答2000關(guān)鍵字專題1關(guān)鍵字專題50關(guān)鍵字專題500關(guān)鍵字專題1500TAG最新視頻文章推薦1 推薦3 推薦5 推薦7 推薦9 推薦11 推薦13 推薦15 推薦17 推薦19 推薦21 推薦23 推薦25 推薦27 推薦29 推薦31 推薦33 推薦35 推薦37視頻文章20視頻文章30視頻文章40視頻文章50視頻文章60 視頻文章70視頻文章80視頻文章90視頻文章100視頻文章120視頻文章140 視頻2關(guān)鍵字專題關(guān)鍵字專題tag2tag3文章專題文章專題2文章索引1文章索引2文章索引3文章索引4文章索引5123456789101112131415文章專題3
    問答文章1 問答文章501 問答文章1001 問答文章1501 問答文章2001 問答文章2501 問答文章3001 問答文章3501 問答文章4001 問答文章4501 問答文章5001 問答文章5501 問答文章6001 問答文章6501 問答文章7001 問答文章7501 問答文章8001 問答文章8501 問答文章9001 問答文章9501
    當(dāng)前位置: 首頁(yè) - 科技 - 知識(shí)百科 - 正文

    AngularRenderer使用案例分享

    來源:懂視網(wǎng) 責(zé)編:小采 時(shí)間:2020-11-27 19:42:45
    文檔

    AngularRenderer使用案例分享

    AngularRenderer使用案例分享:這次給大家?guī)鞟ngular Renderer使用案例分享,Angular Renderer使用的注意事項(xiàng)有哪些,下面就是實(shí)戰(zhàn)案例,一起來看一下。Angular 其中的一個(gè)設(shè)計(jì)目標(biāo)是使瀏覽器與 DOM 獨(dú)立。DOM 是復(fù)雜的,因此使組件與它分離,會(huì)讓我們的應(yīng)用程序,更容易測(cè)試與重構(gòu)。另外
    推薦度:
    導(dǎo)讀AngularRenderer使用案例分享:這次給大家?guī)鞟ngular Renderer使用案例分享,Angular Renderer使用的注意事項(xiàng)有哪些,下面就是實(shí)戰(zhàn)案例,一起來看一下。Angular 其中的一個(gè)設(shè)計(jì)目標(biāo)是使瀏覽器與 DOM 獨(dú)立。DOM 是復(fù)雜的,因此使組件與它分離,會(huì)讓我們的應(yīng)用程序,更容易測(cè)試與重構(gòu)。另外
    這次給大家?guī)鞟ngular Renderer使用案例分享,Angular Renderer使用的注意事項(xiàng)有哪些,下面就是實(shí)戰(zhàn)案例,一起來看一下。

    Angular 其中的一個(gè)設(shè)計(jì)目標(biāo)是使瀏覽器與 DOM 獨(dú)立。DOM 是復(fù)雜的,因此使組件與它分離,會(huì)讓我們的應(yīng)用程序,更容易測(cè)試與重構(gòu)。另外的好處是,由于這種解耦,使得我們的應(yīng)用能夠運(yùn)行在其它平臺(tái) (比如:Node.js、WebWorkers、NativeScript 等)。

    為了能夠支持跨平臺(tái),Angular 通過抽象層封裝了不同平臺(tái)的差異。比如定義了抽象類 Renderer、Renderer2 、抽象類 RootRenderer 等。此外還定義了以下引用類型:ElementRef、TemplateRef、ViewRef 、ComponentRef 和 ViewContainerRef 等。

    本文的主要內(nèi)容是分析 Angular 中 Renderer (渲染器),不過在進(jìn)行具體分析前,我們先來介紹一下平臺(tái)的概念。

    平臺(tái)

    什么是平臺(tái)

    平臺(tái)是應(yīng)用程序運(yùn)行的環(huán)境。它是一組服務(wù),可以用來訪問你的應(yīng)用程序和 Angular 框架本身的內(nèi)置功能。由于Angular 主要是一個(gè) UI 框架,平臺(tái)提供的最重要的功能之一就是頁(yè)面渲染。

    平臺(tái)和引導(dǎo)應(yīng)用程序

    在我們開始構(gòu)建一個(gè)自定義渲染器之前,我們來看一下如何設(shè)置平臺(tái),以及引導(dǎo)應(yīng)用程序。

    import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
    import {BrowserModule} from '@angular/platform-browser';
    @NgModule({
     imports: [BrowserModule],
     bootstrap: [AppCmp]
    })
    class AppModule {}
    platformBrowserDynamic().bootstrapModule(AppModule);

    如你所見,引導(dǎo)過程由兩部分組成:創(chuàng)建平臺(tái)和引導(dǎo)模塊。在這個(gè)例子中,我們導(dǎo)入 BrowserModule 模塊,它是瀏覽器平臺(tái)的一部分。應(yīng)用中只能有一個(gè)激活的平臺(tái),但是我們可以利用它來引導(dǎo)多個(gè)模塊,如下所示:

    const platformRef: PlatformRef = platformBrowserDynamic();
    platformRef.bootstrapModule(AppModule1);
    platformRef.bootstrapModule(AppModule2);

    由于應(yīng)用中只能有一個(gè)激活的平臺(tái),單例的服務(wù)必須在該平臺(tái)中注冊(cè)。比如,瀏覽器只有一個(gè)地址欄,對(duì)應(yīng)的服務(wù)對(duì)象就是單例。此外如何讓我們自定義的 UI 界面,能夠在瀏覽器中顯示出來呢,這就需要使用 Angular 為我們提供的渲染器。

    渲染器

    什么是渲染器

    渲染器是 Angular 為我們提供的一種內(nèi)置服務(wù),用于執(zhí)行 UI 渲染操作。在瀏覽器中,渲染是將模型映射到視圖的過程。模型的值可以是 JavaScript 中的原始數(shù)據(jù)類型、對(duì)象、數(shù)組或其它的數(shù)據(jù)對(duì)象。然而視圖可以是頁(yè)面中的段落、表單、按鈕等其他元素,這些頁(yè)面元素內(nèi)部使用 DOM (Document Object Model) 來表示。

    Angular Renderer

    RootRenderer

    export abstract class RootRenderer {
     abstract renderComponent(componentType: RenderComponentType): Renderer;
    }

    Renderer

    /**
     * @deprecated Use the `Renderer2` instead.
     */
    export abstract class Renderer {
     abstract createElement(parentElement: any, name: string, 
     debugInfo?: RenderDebugInfo): any;
     abstract createText(parentElement: any, value: string, 
     debugInfo?: RenderDebugInfo): any;
     abstract listen(renderElement: any, name: string, callback: Function): Function;
     abstract listenGlobal(target: string, name: string, callback: Function): Function;
     abstract setElementProperty(renderElement: any, propertyName: string, propertyValue: 
     any): void;
     abstract setElementAttribute(renderElement: any, attributeName: string, 
     attributeValue: string): void;
     // ...
    }

    Renderer2

    export abstract class Renderer2 {
     abstract createElement(name: string, namespace?: string|null): any;
     abstract createComment(value: string): any;
     abstract createText(value: string): any;
     abstract setAttribute(el: any, name: string, value: string,
     namespace?: string|null): void;
     abstract removeAttribute(el: any, name: string, namespace?: string|null): void;
     abstract addClass(el: any, name: string): void;
     abstract removeClass(el: any, name: string): void;
     abstract setStyle(el: any, style: string, value: any, 
     flags?: RendererStyleFlags2): void;
     abstract removeStyle(el: any, style: string, flags?: RendererStyleFlags2): void;
     abstract setProperty(el: any, name: string, value: any): void;
     abstract setValue(node: any, value: string): void;
     abstract listen(
     target: 'window'|'document'|'body'|any, eventName: string,
     callback: (event: any) => boolean | void): () => void;
    }

    需要注意的是在 Angular 4.x+ 版本,我們使用 Renderer2 替代 Renderer。通過觀察 Renderer 相關(guān)的抽象類 (Renderer、Renderer2),我們發(fā)現(xiàn)抽象類中定義了很多抽象方法,用來創(chuàng)建元素、文本、設(shè)置屬性、添加樣式和設(shè)置事件監(jiān)聽等。

    渲染器如何工作

    在實(shí)例化一個(gè)組件時(shí),Angular 會(huì)調(diào)用 renderComponent() 方法并將其獲取的渲染器與該組件實(shí)例相關(guān)聯(lián)。Angular 將會(huì)在渲染組件時(shí)通過渲染器執(zhí)行對(duì)應(yīng)相關(guān)的操作,比如,創(chuàng)建元素、設(shè)置屬性、添加樣式和訂閱事件等。

    使用 Renderer

    @Component({
     selector: 'exe-cmp',
     template: `
     <h3>Exe Component</h3>
     `
    })
    export class ExeComponent {
     constructor(private renderer: Renderer2, elRef: ElementRef) {
     this.renderer.setProperty(elRef.nativeElement, 'author', 'semlinker');
     }
    }

    以上代碼中,我們利用構(gòu)造注入的方式,注入 Renderer2 和 ElementRef 實(shí)例。有些讀者可能會(huì)問,注入的實(shí)例對(duì)象是怎么生成的。這里我們只是稍微介紹一下相關(guān)知識(shí),并不會(huì)詳細(xì)展開。具體代碼如下:

    TokenKey

    // packages/core/src/view/util.ts
    const _tokenKeyCache = new Map<any, string>();
    export function tokenKey(token: any): string {
     let key = _tokenKeyCache.get(token);
     if (!key) {
     key = stringify(token) + '_' + _tokenKeyCache.size;
     _tokenKeyCache.set(token, key);
     }
     return key;
    }
    // packages/core/src/view/provider.ts
    const RendererV1TokenKey = tokenKey(RendererV1);
    const Renderer2TokenKey = tokenKey(Renderer2);
    const ElementRefTokenKey = tokenKey(ElementRef);
    const ViewContainerRefTokenKey = tokenKey(ViewContainerRef);
    const TemplateRefTokenKey = tokenKey(TemplateRef);
    const ChangeDetectorRefTokenKey = tokenKey(ChangeDetectorRef);
    const InjectorRefTokenKey = tokenKey(Injector);

    resolveDep()

    export function resolveDep(
     view: ViewData, elDef: NodeDef, 
     allowPrivateServices: boolean, depDef: DepDef,
     notFoundValue: any = Injector.THROW_IF_NOT_FOUND): any {
     const tokenKey = depDef.tokenKey;
     // ...
     while (view) {
     if (elDef) {
     switch (tokenKey) {
     case RendererV1TokenKey: { // tokenKey(RendererV1)
     const compView = findCompView(view, elDef, allowPrivateServices);
     return createRendererV1(compView);
     }
     case Renderer2TokenKey: { // tokenKey(Renderer2)
     const compView = findCompView(view, elDef, allowPrivateServices);
     return compView.renderer;
     }
     case ElementRefTokenKey: // tokenKey(ElementRef)
     return new ElementRef(asElementData(view, elDef.index).renderElement);
     // ... 此外還包括:ViewContainerRefTokenKey、TemplateRefTokenKey、
     // ChangeDetectorRefTokenKey 等
     }
     }
     }
     // ...
    }

    通過以上代碼,我們發(fā)現(xiàn)當(dāng)我們?cè)诮M件類的構(gòu)造函數(shù)中聲明相應(yīng)的依賴對(duì)象時(shí),如 Renderer2 和 ElementRef,Angular 內(nèi)部會(huì)調(diào)用 resolveDep() 方法,實(shí)例化 Token 對(duì)應(yīng)依賴對(duì)象。

    在大多數(shù)情況下,我們開發(fā)的 Angular 應(yīng)用程序是運(yùn)行在瀏覽器平臺(tái),接下來我們來了解一下該平臺(tái)下的默認(rèn)渲染器 - DefaultDomRenderer2。

    DefaultDomRenderer2

    在瀏覽器平臺(tái)下,我們可以通過調(diào)用 DomRendererFactory2 工廠,根據(jù)不同的視圖封裝方案,創(chuàng)建對(duì)應(yīng)渲染器。

    DomRendererFactory2

    // packages/platform-browser/src/dom/dom_renderer.ts
    @Injectable()
    export class DomRendererFactory2 implements RendererFactory2 {
     private rendererByCompId = new Map<string, Renderer2>();
     private defaultRenderer: Renderer2;
     constructor(
     private eventManager: EventManager, 
     private sharedStylesHost: DomSharedStylesHost) {
     // 創(chuàng)建默認(rèn)的DOM渲染器
     this.defaultRenderer = new DefaultDomRenderer2(eventManager);
     };
     createRenderer(element: any, type: RendererType2|null): Renderer2 {
     if (!element || !type) {
     return this.defaultRenderer;
     }
     // 根據(jù)不同的視圖封裝方案,創(chuàng)建不同的渲染器
     switch (type.encapsulation) {
     // 無 Shadow DOM,但是通過 Angular 提供的樣式包裝機(jī)制來封裝組件,
     // 使得組件的樣式不受外部影響,這是 Angular 的默認(rèn)設(shè)置。
     case ViewEncapsulation.Emulated: {
     let renderer = this.rendererByCompId.get(type.id);
     if (!renderer) {
     renderer =
     new EmulatedEncapsulationDomRenderer2(this.eventManager, 
     this.sharedStylesHost, type);
     this.rendererByCompId.set(type.id, renderer);
     }
     (<EmulatedEncapsulationDomRenderer2>renderer).applyToHost(element);
     return renderer;
     }
     // 使用原生的 Shadow DOM 特性 
     case ViewEncapsulation.Native:
     return new ShadowDomRenderer(this.eventManager, 
     this.sharedStylesHost, element, type);
     // 無 Shadow DOM,并且也無樣式包裝
     default: {
     // ...
     return this.defaultRenderer;
     }
     }
     }
    }

    上面代碼中的 EmulatedEncapsulationDomRenderer2ShadowDomRenderer 類都繼承于 DefaultDomRenderer2 類,接下來我們?cè)賮砜匆幌?DefaultDomRenderer2 類的內(nèi)部實(shí)現(xiàn):

    class DefaultDomRenderer2 implements Renderer2 { 
     constructor(private eventManager: EventManager) {}
     // 省略 Renderer2 抽象類中定義的其它方法
     createElement(name: string, namespace?: string): any {
     if (namespace) {
     return document.createElementNS(NAMESPACE_URIS[namespace], name);
     }
     return document.createElement(name);
     }
     createComment(value: string): any { return document.createComment(value); }
     createText(value: string): any { return document.createTextNode(value); }
     addClass(el: any, name: string): void { el.classList.add(name); }
     setStyle(el: any, style: string, value: any, flags: RendererStyleFlags2): void {
     if (flags & RendererStyleFlags2.DashCase) {
     el.style.setProperty(
     style, value, !!(flags & RendererStyleFlags2.Important) ? 'important' : '');
     } else {
     el.style[style] = value;
     }
     }
     listen(
     target: 'window'|'document'|'body'|any, 
     event: string, 
     callback: (event: any) => boolean):
     () => void {
     checkNoSyntheticProp(event, 'listener');
     if (typeof target === 'string') {
     return <() => void>this.eventManager.addGlobalEventListener(
     target, event, decoratePreventDefault(callback));
     }
     return <() => void>this.eventManager.addEventListener(
     target, event, decoratePreventDefault(callback)) as() => void;
     }
    }

    介紹完 DomRendererFactory2DefaultDomRenderer2 類,最后我們來看一下 Angular 內(nèi)部如何利用它們。

    DomRendererFactory2 內(nèi)部應(yīng)用

    BrowserModule

    // packages/platform-browser/src/browser.ts
    @NgModule({
     providers: [
     // 配置 DomRendererFactory2 和 RendererFactory2 provider
     DomRendererFactory2,
     {provide: RendererFactory2, useExisting: DomRendererFactory2},
     // ...
     ],
     exports: [CommonModule, ApplicationModule]
    })
    export class BrowserModule {
     constructor(@Optional() @SkipSelf() parentModule: BrowserModule) {
     // 用于判斷應(yīng)用中是否已經(jīng)導(dǎo)入BrowserModule模塊
     if (parentModule) {
     throw new Error(
     `BrowserModule has already been loaded. If you need access to common 
     directives such as NgIf and NgFor from a lazy loaded module, 
     import CommonModule instead.`);
     }
     }
    }

    createComponentView()

    // packages/core/src/view/view.ts
    export function createComponentView(
     parentView: ViewData, 
     nodeDef: NodeDef, 
     viewDef: ViewDefinition, 
     hostElement: any): ViewData {
     const rendererType = nodeDef.element !.componentRendererType; // 步驟一
     let compRenderer: Renderer2;
     if (!rendererType) { // 步驟二
     compRenderer = parentView.root.renderer;
     } else {
     compRenderer = parentView.root.rendererFactory
     .createRenderer(hostElement, rendererType);
     }
     
     return createView(
     parentView.root, compRenderer, parentView, 
     nodeDef.element !.componentProvider, viewDef);
    }

    步驟一

    當(dāng) Angular 在創(chuàng)建組件視圖時(shí),會(huì)根據(jù) nodeDef.element 對(duì)象的 componentRendererType 屬性值,來創(chuàng)建組件的渲染器。接下來我們先來看一下 NodeDefElementDefRendererType2 接口定義:

    // packages/core/src/view/types.ts
    // 視圖中節(jié)點(diǎn)的定義
    export interface NodeDef {
     bindingIndex: number;
     bindings: BindingDef[];
     bindingFlags: BindingFlags;
     outputs: OutputDef[];
     element: ElementDef|null; // nodeDef.element
     provider: ProviderDef|null;
     // ...
    }
    // 元素的定義
    export interface ElementDef {
     name: string|null;
     attrs: [string, string, string][]|null;
     template: ViewDefinition|null;
     componentProvider: NodeDef|null;
     // 設(shè)置組件渲染器的類型
     componentRendererType: RendererType2|null; // nodeDef.element.componentRendererType
     componentView: ViewDefinitionFactory|null;
     handleEvent: ElementHandleEventFn|null;
     // ...
    }
    // packages/core/src/render/api.ts
    // RendererType2 接口定義
    export interface RendererType2 {
     id: string;
     encapsulation: ViewEncapsulation; // Emulated、Native、None
     styles: (string|any[])[];
     data: {[kind: string]: any};
    }

    步驟二

    獲取 componentRendererType 的屬性值后,如果該值為 null 的話,則直接使用 parentView.root 屬性值對(duì)應(yīng)的 renderer 對(duì)象。若該值不為空,則調(diào)用 parentView.root 對(duì)象的 rendererFactory() 方法創(chuàng)建 renderer 對(duì)象。

    通過上面分析,我們發(fā)現(xiàn)不管走哪條分支,我們都需要使用 parentView.root 對(duì)象,然而該對(duì)象是什么特殊對(duì)象?我們發(fā)現(xiàn) parentView 的數(shù)據(jù)類型是 ViewData ,該數(shù)據(jù)接口定義如下:

    // packages/core/src/view/types.ts
    export interface ViewData {
     def: ViewDefinition;
     root: RootData;
     renderer: Renderer2;
     nodes: {[key: number]: NodeData};
     state: ViewState;
     oldValues: any[];
     disposables: DisposableFn[]|null;
     // ...
    }

    通過 ViewData 的接口定義,我們終于發(fā)現(xiàn)了 parentView.root 的屬性類型,即 RootData

    // packages/core/src/view/types.ts
    export interface RootData {
     injector: Injector;
     ngModule: NgModuleRef<any>;
     projectableNodes: any[][];
     selectorOrNode: any;
     renderer: Renderer2;
     rendererFactory: RendererFactory2;
     errorHandler: ErrorHandler;
     sanitizer: Sanitizer;
    }

    那好,現(xiàn)在問題來了:

    1. 什么時(shí)候創(chuàng)建 RootData 對(duì)象?

    2. 怎么創(chuàng)建 RootData 對(duì)象?

    什么時(shí)候創(chuàng)建 RootData 對(duì)象?

    當(dāng)創(chuàng)建根視圖的時(shí)候會(huì)創(chuàng)建 RootData,在開發(fā)環(huán)境會(huì)調(diào)用 debugCreateRootView() 方法創(chuàng)建 RootView,而在生產(chǎn)環(huán)境會(huì)調(diào)用 createProdRootView() 方法創(chuàng)建 RootView。簡(jiǎn)單起見,我們只分析 createProdRootView() 方法:

    function createProdRootView(
     elInjector: Injector, 
     projectableNodes: any[][], 
     rootSelectorOrNode: string | any,
     def: ViewDefinition, 
     ngModule: NgModuleRef<any>, 
     context?: any): ViewData {
     /** RendererFactory2 Provider 配置
     * DomRendererFactory2,
     * {provide: RendererFactory2, useExisting: DomRendererFactory2},
     */
     const rendererFactory: RendererFactory2 = ngModule.injector.get(RendererFactory2);
     
     return createRootView(
     createRootData(elInjector, ngModule, rendererFactory,
     projectableNodes, rootSelectorOrNode),
     def, context);
    }
    // 創(chuàng)建根視圖
    export function createRootView(root: RootData, def: ViewDefinition, 
     context?: any): ViewData {
     // 創(chuàng)建ViewData對(duì)象
     const view = createView(root, root.renderer, null, null, def);
     initView(view, context, context);
     createViewNodes(view);
     return view;
    }

    上面代碼中,當(dāng)創(chuàng)建 RootView 的時(shí)候,會(huì)調(diào)用 createRootData() 方法創(chuàng)建 RootData 對(duì)象。最后一步就是分析 createRootData() 方法。

    怎么創(chuàng)建 RootData 對(duì)象?

    通過上面分析,我們知道通過 createRootData() 方法,來創(chuàng)建 RootData 對(duì)象。createRootData() 方法具體實(shí)現(xiàn)如下:

    function createRootData(
     elInjector: Injector, 
     ngModule: NgModuleRef<any>, 
     rendererFactory: RendererFactory2,
     projectableNodes: any[][], 
     rootSelectorOrNode: any): RootData {
     const sanitizer = ngModule.injector.get(Sanitizer);
     const errorHandler = ngModule.injector.get(ErrorHandler);
     // 創(chuàng)建RootRenderer
     const renderer = rendererFactory.createRenderer(null, null); 
     return {
     ngModule,
     injector: elInjector,
     projectableNodes,
     selectorOrNode: rootSelectorOrNode, 
     sanitizer, 
     rendererFactory, 
     renderer,
     errorHandler
     };
    }

    此時(shí)瀏覽器平臺(tái)下, Renderer 渲染器的相關(guān)基礎(chǔ)知識(shí)已介紹完畢。接下來,我們做一個(gè)簡(jiǎn)單總結(jié):

    1. Angular 應(yīng)用程序啟動(dòng)時(shí)會(huì)創(chuàng)建 RootView (生產(chǎn)環(huán)境下通過調(diào)用 createProdRootView() 方法)

    2. 創(chuàng)建 RootView 的過程中,會(huì)創(chuàng)建 RootData 對(duì)象,該對(duì)象可以通過 ViewData 的 root 屬性訪問到。基于 RootData 對(duì)象,我們可以通過 renderer 訪問到默認(rèn)的渲染器,即 DefaultDomRenderer2 實(shí)例,此外也可以通過 rendererFactory 訪問到 RendererFactory2 實(shí)例。

    3. 在創(chuàng)建組件視圖 (ViewData) 時(shí),會(huì)根據(jù) componentRendererType 的屬性值,來設(shè)置組件關(guān)聯(lián)的 renderer 渲染器。

    4. 當(dāng)渲染組件視圖的時(shí)候,Angular 會(huì)利用該組件關(guān)聯(lián)的 renderer 提供的 API,創(chuàng)建該視圖中的節(jié)點(diǎn)或執(zhí)行視圖的相關(guān)操作,比如創(chuàng)建元素 (createElement)、創(chuàng)建文本 (createText)、設(shè)置樣式 (setStyle) 和 設(shè)置事件監(jiān)聽 (listen) 等。

    相信看了本文案例你已經(jīng)掌握了方法,更多精彩請(qǐng)關(guān)注Gxl網(wǎng)其它相關(guān)文章!

    推薦閱讀:

    使用JS判斷字符串中包含內(nèi)容方法總結(jié)

    JS+HTML5實(shí)綁定鼠標(biāo)事件的粒子動(dòng)畫

    聲明:本網(wǎng)頁(yè)內(nèi)容旨在傳播知識(shí),若有侵權(quán)等問題請(qǐng)及時(shí)與本網(wǎng)聯(lián)系,我們將在第一時(shí)間刪除處理。TEL:177 7030 7066 E-MAIL:11247931@qq.com

    文檔

    AngularRenderer使用案例分享

    AngularRenderer使用案例分享:這次給大家?guī)鞟ngular Renderer使用案例分享,Angular Renderer使用的注意事項(xiàng)有哪些,下面就是實(shí)戰(zhàn)案例,一起來看一下。Angular 其中的一個(gè)設(shè)計(jì)目標(biāo)是使瀏覽器與 DOM 獨(dú)立。DOM 是復(fù)雜的,因此使組件與它分離,會(huì)讓我們的應(yīng)用程序,更容易測(cè)試與重構(gòu)。另外
    推薦度:
    標(biāo)簽: 分享 使用 實(shí)例
    • 熱門焦點(diǎn)

    最新推薦

    猜你喜歡

    熱門推薦

    專題
    Top
    主站蜘蛛池模板: 精品a在线观看| 亚洲精品国偷自产在线| 全国精品一区二区在线观看| 91国内外精品自在线播放| 亚洲精品动漫免费二区| 成人精品一区二区久久| 国产成人精品久久免费动漫 | 精品亚洲国产成AV人片传媒| 日韩精品无码免费视频| 国产精品午夜一级毛片密呀| 精品一区二区久久久久久久网站| 国内精品久久久久久99蜜桃| 无码人妻精品中文字幕免费| 亚洲精品网站在线观看不卡无广告 | 精品国产乱码久久久久久浪潮| 久久成人影院精品777| 99在线精品一区二区三区| 日韩精品无码久久久久久| 亚洲日韩一页精品发布| 亚洲精品成人片在线观看| 久久亚洲国产精品123区| 国产精品无码免费专区午夜| 欧美日激情日韩精品| 久久91精品国产91久久户| 99在线精品视频| 国产精品视频一区国模私拍| 91视频精品全国免费观看| 99麻豆久久久国产精品免费| 国产精品专区第二| 久久精品国产久精国产| 98香蕉草草视频在线精品看 | 久久国产精品久久| 91精品国产91热久久久久福利 | 精品视频无码一区二区三区| 亚洲精品~无码抽插| 无码精品黑人一区二区三区| 久久久久久亚洲Av无码精品专口 | 亚洲国产精品无码久久98| 亚洲av永久无码精品网站| 无码精品国产VA在线观看DVD| 亚洲精品夜夜夜妓女网|