标签

The tag provides data about the HTML document. data will not be displayed on the page, but will be machine parsable.

data 中文名叫元数据,是用于描述数据的数据。它不会显示在页面上,但是机器却可以识别。 常用于定义页面的说明,关键字,最后修改日期,和其它的元数据。这些元数据将服务于浏览器,搜索引擎和其它网络服务。

标签共有两个属性,分别是 name 属性和 http-equiv 属性:

  • name:主要用于描述网页,比如网页的关键词,网站描述等。与之对应的属性值为 content,content 中的内容是对 name 填入类型的具体描述,便于搜索引擎抓取。比如我们常见的 viewport:
<  name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1">
  • http-equiv:相当于文件的头作用,用于向浏览器传递一些有用的信息,以帮助正确地显示网页内容,与之对应的属性为 content。
<  http-equiv="X-UA-Compatible" content="IE=edge">

以上代码告诉 IE 浏览器,IE8/9 及以后的版本都会以最高版本 IE 来渲染页面。关于 HTML 标签的相关知识,这里就不再继续展开,感兴趣的同学可以阅读 HTML 标签总结与属性使用介绍 这篇文章。

Service 简介

为了让开发者能够方便地操作页面中的 信息,Angular 为我们提供 服务。该服务支持以下的方法:

首先要使用 服务,我们需要从 @angular/platform-browser 库导入 类,然后利用 Angular 依赖注入的机制,通过构造注入的方式注入 服务:

import { Injectable } from '@angular/core';
import {   } from '@angular/platform-browser';

@Injectable({
   providedIn: 'root'
})
export class  Service { 
  constructor(private  :  ) { }
}

addTag()

addTag(tag: Definition, forceCreation: boolean = false): HTML Element | null

该方法用于在页面上添加一个 HTML 标签,它接收两个参数:

  • tag: Definition 类型的对象
  • forceCreation:是否强制创建,默认为 false

tag 参数对应的 Definition 类型定义如下:

export type  Definition = {
  charset?: string; 
  content?: string;
  httpEquiv?: string; 
  id?: string; 
  itemprop?: string;
  name?: string;
  property?: string;
  scheme?: string;
  url?: string;
} &
{
  // TODO(IgorMinar): this type looks wrong
  [prop: string]: string;
};

了解完上述的内容,我们来动手实践一下:

@Injectable({
  providedIn: "root"
})
export class  Service {
  constructor(private  :  ) { }

  addTag() {
    this. .addTag({ name: 'de ion', content: 'Angular   Service' });
    this. .addTag({ name: 'keywords', content: 'Angular, RxJS, Type ' });
  }
}

上述代码我们通过调用 服务的 addTag() 方法,创建了两个 标签。如果需要一次添加多个 标签,我们可以调用 addTags() 方法。

addTags()

addTags(tags: Definition[], forceCreation: boolean = false): HTML Element[]

该方法用于一次性添加多个 HTML 标签,它接收两个参数:

  • tags: Definition 类型的对象数组
  • forceCreation:是否强制创建,默认为 false
addTags() {
  this. .addTags([
    { name: 'de ion', content: 'Angular   Service' },
    { name: 'keywords', content: 'Angular, RxJS, Type ' }
  ]);
}

在创建完 HTML 标签,我们可以通过 getTag() 方法来获取对应的 HTML Element 对象。

getTag()

getTag(attrSelector: string): HTML Element | null

该方法用于获取 attrSelector 属性选择器对应的 HTML Element 对象,它接收一个参数,即属性选择器,比如我们需要获取 keywords 标签:

get Tag(){
  let  El: HTML Element = this. .getTag('name="keywords"');
  console.log(`Get keywords   tag: ${ El}`);
}

当 getTag() 方法匹配不了 attrSelector 属性选择器时,会返回 null 对象。与 setTag() 类似,getTag() 方法也存在一个 getTags() 方法。

getTags()

getTags(attrSelector: string): HTML Element[]

该方法用于获取所有匹配 attrSelector 选择器的所有 HTML Element 对象:

get Tags() {
  let els: HTML Element[] = this. .getTags('name');
  els.forEach(el => {
    console.log(el);
    console.log(el.name);
    console.log(el.content);
  });
}

这时我们已经介绍完了如何创建和查找 HTML Element 对象,有时候在创建完 HTML Element 对象后,我们可能需要修改 HTML Element 对象,此时我们需要使用 updateTag() 方法。

updateTag()

updateTag(tag: Definition, selector?: string): HTML Element | null

该方法用于更新 HTML 标签的信息,它接收两个参数:

  • tag: Definition 类型的对象
  • selector(可选):选择器
update Tags() {
  this. .updateTag({
    name: 'de ion', 
    content: 'Updated: Angular   Service'
  });
  this. .updateTag({ name: 'keywords', content: 'Node.js, Angular' });
}

除了更新 HTML 标签之外,我们也可以移除指定的 HTML 标签。

removeTag()

removeTag(attrSelector: string): void

该方法用于移除匹配 attrSelector 属性选择器的 HTML 标签:

remove Tags() {
  this. .removeTag('name="de ion"');
  this. .removeTag('name="keywords"');
}

最后我们来介绍 removeTagElement() 方法。

removeTagElement()

removeTagElement( : HTML Element): void

该方法用于移除 参数对应的 HTML 标签:

removeTagElement() {
  let keywords: HTML Element = this. .getTag('name="keywords"');
  this. .removeTagElement(keywords);
}

感兴趣的同学,可以浏览 Stackblitz 线上示例。下面我们来简单分析一下 Service 的源码。

Service 源码简析

Service 类及构造函数

// packages/platform-browser/src/browser/ .ts
@Injectable({providedIn: 'root', useFactory: create , deps: []})
export class   {
  private _dom: DomAdapter;
  constructor(@Inject(DOCUMENT) private _doc: any) { 
      this._dom = getDOM(); // 获取DOM适配器
  }
}

通过观察 Injectable 装饰器的 元信息,我们知道 服务将被注册在根级注入器中,当首次获取 服务时,将使用 create () 工厂方法创建对应的实例。

import {Inject, Injectable, inject} from '@angular/core';

export function create () {
  return new  (inject(DOCUMENT));// 注意这里是小写的inject的哦
}

接下来我们从最简单的 addTag() 方法开始分析。

addTag()

addTag(tag:  Definition, forceCreation: boolean = false): HTML Element|null {
  if (!tag) return null;
  return this._getOrCreateElement(tag, forceCreation);
}

这时我们知道其实在 addTag() 方法内部,最终是调用内部的私有方法 _getOrCreateElement() 来执行具体操作。_getOrCreateElement() 方法的具体实现如下:

private _getOrCreateElement( :  Definition, forceCreation: boolean = false):
      HTML Element {
    if (!forceCreation) { // 非强制模式
      const selector: string = this._parseSelector( ); // 解析选择器
      const elem: HTML Element = this.getTag(selector) !; // 获取选择器匹配的 元素
      // It's allowed to have multiple elements with the same name so it's not enough to
      // just check that element with the same name already present on the page. 
      // We also need to check if element has tag attributes
      if (elem && this._containsAttributes( , elem)) return elem;
    }
    // 调用Dom适配器的createElement()方法创建 元素  
    const element: HTML Element = this._dom.createElement(' ') as HTML Element;
    this._set ElementAttributes( , element);
    // 获取head元素,添加新建的 元素并返回该元素      
    const head = this._dom.getElementsByTagName(this._doc, 'head')[0];
    this._dom.appendChild(head, element);
    return element;
}

// 解析选择器
private _parseSelector(tag:  Definition): string {
   const attr: string = tag.name ? 'name' : 'property';
   return `${attr}="${tag[attr]}"`;
}

// 设置 元素的属性
private _set ElementAttributes(tag:  Definition, el: HTML Element): 
   HTML Element {
     .keys(tag).forEach((prop: string) => 
      this._dom.setAttribute(el, prop, tag[prop]));
    return el;
}

简单分析完 addTag(),我们再来看一下与它对应的 getTag() 方法。

getTag()

getTag(attrSelector: string): HTML Element|null {
  if (!attrSelector) return null; 
  return this._dom.querySelector(this._doc, ` [${attrSelector}]`) || null;
}

该方法内部的实现也很简单,就是通过 DOM 适配器的 querySelector API 来实现元素匹配。对于前面的示例来说:

let  El: HTML Element = this. .getTag('name="keywords"');

内部会转换为:

return this._dom.querySelector(this._doc, " [name='keywords')" || null;

新增和查询的方法介绍完,我们来继续分析一下 updateTag() 方法。

updateTag()

updateTag(tag:  Definition, selector?: string): HTML Element|null {
    if (!tag) return null;
    selector = selector || this._parseSelector(tag); // 解析选择器
    const  : HTML Element = this.getTag(selector) !; // 获取选择器对应的   元素
    if ( ) { // 若已存在,则更新对应的属性
      return this._set ElementAttributes(tag,  );
    }
    return this._getOrCreateElement(tag, true); // 否则在force模式下,创建   元素
}

最后再来看一下 removeTag() 方法,顾名思义就是用来移除指定的 元素。

removeTag()

removeTag(attrSelector: string): void { 
    this.removeTagElement(this.getTag(attrSelector) !);
}

removeTagElement( : HTML Element): void {
    if ( ) {
      this._dom.remove( );
    }
}

参考资源

收藏 打印