The tag provides metadata about the HTML document. Metadata will not be displayed on the page, but will be machine parsable.
Metadata 中文名叫元数据,是用于描述数据的数据。它不会显示在页面上,但是机器却可以识别。meta 常用于定义页面的说明,关键字,最后修改日期,和其它的元数据。这些元数据将服务于浏览器,搜索引擎和其它网络服务。
meta 标签共有两个属性,分别是 name 属性和 http-equiv 属性:
<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
以上代码告诉 IE 浏览器,IE8/9 及以后的版本都会以最高版本 IE 来渲染页面。关于 HTML meta 标签的相关知识,这里就不再继续展开,感兴趣的同学可以阅读 HTML meta标签总结与属性使用介绍 这篇文章。
为了让开发者能够方便地操作页面中的 Meta 信息,Angular 为我们提供 Meta 服务。该服务支持以下的方法:
首先要使用 Meta 服务,我们需要从 @angular/platform-browser
库导入 Meta 类,然后利用 Angular 依赖注入的机制,通过构造注入的方式注入 Meta 服务:
import { Injectable } from '@angular/core'; import { Meta } from '@angular/platform-browser'; @Injectable({ providedIn: 'root' }) export class MetaService { constructor(private meta: Meta) { } }
addTag(tag: MetaDefinition, forceCreation: boolean = false): HTMLMetaElement | null
该方法用于在页面上添加一个 HTML Meta 标签,它接收两个参数:
tag 参数对应的 MetaDefinition 类型定义如下:
export type MetaDefinition = { 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 MetaService { constructor(private meta: Meta) { } addTag() { this.meta.addTag({ name: 'description', content: 'Angular Meta Service' }); this.meta.addTag({ name: 'keywords', content: 'Angular, RxJS, TypeScript' }); } }
上述代码我们通过调用 meta 服务的 addTag()
方法,创建了两个 Meta 标签。如果需要一次添加多个 meta 标签,我们可以调用 addTags() 方法。
addTags(tags: MetaDefinition[], forceCreation: boolean = false): HTMLMetaElement[]
该方法用于一次性添加多个 HTML Meta 标签,它接收两个参数:
addTags() { this.meta.addTags([ { name: 'description', content: 'Angular Meta Service' }, { name: 'keywords', content: 'Angular, RxJS, TypeScript' } ]); }
在创建完 HTML meta 标签,我们可以通过 getTag() 方法来获取对应的 HTMLMetaElement 对象。
该方法用于获取 attrSelector 属性选择器对应的 HTMLMetaElement 对象,它接收一个参数,即属性选择器,比如我们需要获取 keywords
meta 标签:
getMetaTag(){ let metaEl: HTMLMetaElement = this.meta.getTag('name="keywords"'); console.log(`Get keywords meta tag: ${metaEl}`); }
当 getTag() 方法匹配不了 attrSelector 属性选择器时,会返回 null 对象。与 setTag() 类似,getTag() 方法也存在一个 getTags() 方法。
该方法用于获取所有匹配 attrSelector 选择器的所有 HTMLMetaElement 对象:
getMetaTags() { let els: HTMLMetaElement[] = this.meta.getTags('name'); els.forEach(el => { console.log(el); console.log(el.name); console.log(el.content); }); }
这时我们已经介绍完了如何创建和查找 HTMLMetaElement 对象,有时候在创建完 HTMLMetaElement 对象后,我们可能需要修改 HTMLMetaElement 对象,此时我们需要使用 updateTag() 方法。
updateTag(tag: MetaDefinition, selector?: string): HTMLMetaElement | null
该方法用于更新 HTML Meta 标签的信息,它接收两个参数:
updateMetaTags() { this.meta.updateTag({ name: 'description', content: 'Updated: Angular Meta Service' }); this.meta.updateTag({ name: 'keywords', content: 'Node.js, Angular' }); }
除了更新 HTML Meta 标签之外,我们也可以移除指定的 HTML Meta 标签。
该方法用于移除匹配 attrSelector 属性选择器的 HTML Meta 标签:
removeMetaTags() { this.meta.removeTag('name="description"'); this.meta.removeTag('name="keywords"'); }
最后我们来介绍 removeTagElement() 方法。
该方法用于移除 meta 参数对应的 HTML Meta 标签:
removeTagElement() { let keywords: HTMLMetaElement = this.meta.getTag('name="keywords"'); this.meta.removeTagElement(keywords); }
感兴趣的同学,可以浏览 Stackblitz 线上示例。下面我们来简单分析一下 Meta Service 的源码。
// packages/platform-browser/src/browser/meta.ts @Injectable({providedIn: 'root', useFactory: createMeta, deps: []}) export class Meta { private _dom: DomAdapter; constructor(@Inject(DOCUMENT) private _doc: any) { this._dom = getDOM(); // 获取DOM适配器 } }
通过观察 Injectable 装饰器的 Meta 元信息,我们知道 Meta 服务将被注册在根级注入器中,当首次获取 Meta 服务时,将使用 createMeta() 工厂方法创建对应的实例。
import {Inject, Injectable, inject} from '@angular/core'; export function createMeta() { return new Meta(inject(DOCUMENT));// 注意这里是小写的inject的哦 }
接下来我们从最简单的 addTag() 方法开始分析。
addTag(tag: MetaDefinition, forceCreation: boolean = false): HTMLMetaElement|null { if (!tag) return null; return this._getOrCreateElement(tag, forceCreation); }
这时我们知道其实在 addTag() 方法内部,最终是调用内部的私有方法 _getOrCreateElement()
来执行具体操作。_getOrCreateElement() 方法的具体实现如下:
private _getOrCreateElement(meta: MetaDefinition, forceCreation: boolean = false): HTMLMetaElement { if (!forceCreation) { // 非强制模式 const selector: string = this._parseSelector(meta); // 解析选择器 const elem: HTMLMetaElement = this.getTag(selector) !; // 获取选择器匹配的Meta元素 // 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(meta, elem)) return elem; } // 调用Dom适配器的createElement()方法创建meta元素 const element: HTMLMetaElement = this._dom.createElement('meta') as HTMLMetaElement; this._setMetaElementAttributes(meta, element); // 获取head元素,添加新建的meta元素并返回该元素 const head = this._dom.getElementsByTagName(this._doc, 'head')[0]; this._dom.appendChild(head, element); return element; } // 解析选择器 private _parseSelector(tag: MetaDefinition): string { const attr: string = tag.name ? 'name' : 'property'; return `${attr}="${tag[attr]}"`; } // 设置Meta元素的属性 private _setMetaElementAttributes(tag: MetaDefinition, el: HTMLMetaElement): HTMLMetaElement { Object.keys(tag).forEach((prop: string) => this._dom.setAttribute(el, prop, tag[prop])); return el; }
简单分析完 addTag(),我们再来看一下与它对应的 getTag() 方法。
getTag(attrSelector: string): HTMLMetaElement|null { if (!attrSelector) return null; return this._dom.querySelector(this._doc, `meta[${attrSelector}]`) || null; }
该方法内部的实现也很简单,就是通过 DOM 适配器的 querySelector API 来实现元素匹配。对于前面的示例来说:
let metaEl: HTMLMetaElement = this.meta.getTag('name="keywords"');
内部会转换为:
return this._dom.querySelector(this._doc, "meta[name='keywords')" || null;
新增和查询的方法介绍完,我们来继续分析一下 updateTag() 方法。
updateTag(tag: MetaDefinition, selector?: string): HTMLMetaElement|null { if (!tag) return null; selector = selector || this._parseSelector(tag); // 解析选择器 const meta: HTMLMetaElement = this.getTag(selector) !; // 获取选择器对应的 Meta 元素 if (meta) { // 若已存在,则更新对应的属性 return this._setMetaElementAttributes(tag, meta); } return this._getOrCreateElement(tag, true); // 否则在force模式下,创建 Meta 元素 }
最后再来看一下 removeTag() 方法,顾名思义就是用来移除指定的 Meta 元素。
removeTag(attrSelector: string): void { this.removeTagElement(this.getTag(attrSelector) !); } removeTagElement(meta: HTMLMetaElement): void { if (meta) { this._dom.remove(meta); } }
本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。
我来说两句