diff --git a/LICENSE.md b/LICENSE.md index d4415fd..eddb309 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,6 +1,9 @@ MIT License -Copyright (c) 2016-2018 Declan de Wet & Sébastien Chopin +Copyright (c) 2016-2019 +- Declan de Wet +- Sébastien Chopin +- All the amazing contributors (https://github.com/nuxt/vue-meta/graphs/contributors) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 8f3823c..203fcad 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,18 @@

- vue-meta + vue-meta

- Manage page meta info in Vue 2.0 components. SSR + Streaming supported. Inspired by react-helmet. + Manage page metadata in Vue.js components with SSR support

-github release npm version Build Status codecov
-dependencies Status devDependencies Status
-npm downloads Gitter + npm downloads + npm version + Coverage Status + Build Status + dependencies Status + Discord

```html @@ -20,852 +23,87 @@ ``` +```html + + + My Example App - Yay! + ... + +``` +# Introduction +Vue Meta is a [Vue.js](https://vuejs.org) plugin that allows you to manage your app's metadata. It is inspired by and works similar as [`react-helmet`](https://github.com/nfl/react-helmet) for react. However, instead of setting your data as props passed to a proprietary component, you simply export it as part of your component's data using the `metaInfo` property. - - -# Table of Contents +These properties, when set on a deeply nested component, will cleverly overwrite their parent components' `metaInfo`, thereby enabling custom info for each top-level view as well as coupling metadata directly to deeply nested subcomponents for more maintainable code. -- [Description](#description) -- [Installation](#installation) - - [Yarn](#yarn) - - [NPM](#npm) - - [CDN](#cdn) -- [Usage](#usage) - - [Step 1: Preparing the plugin](#step-1-preparing-the-plugin) - - [Options](#options) - - [Step 2: Server Rendering (Optional)](#step-2-server-rendering-optional) - - [Step 2.1: Exposing `$meta` to `bundleRenderer`](#step-21-exposing-meta-to-bundlerenderer) - - [Step 2.2: Populating the document meta info with `inject()`](#step-22-populating-the-document-meta-info-with-inject) - - [Simple Rendering with `renderToString()`](#simple-rendering-with-rendertostring) - - [Streaming Rendering with `renderToStream()`](#streaming-rendering-with-rendertostream) - - [Step 3: Start defining `metaInfo`](#step-3-start-defining-metainfo) - - [Recognized `metaInfo` Properties](#recognized-metainfo-properties) - - [`title` (String)](#title-string) - - [`titleTemplate` (String | Function)](#titletemplate-string--function) - - [`htmlAttrs` (Object)](#htmlattrs-object) - - [`headAttrs` (Object)](#headattrs-object) - - [`bodyAttrs` (Object)](#bodyattrs-object) - - [`base` (Object)](#base-object) - - [`meta` ([Object])](#meta-object) - - [`link` ([Object])](#link-object) - - [`style` ([Object])](#style-object) - - [`script` ([Object])](#script-object) - - [`noscript` ([Object])](#noscript-object) - - [`__dangerouslyDisableSanitizers` ([String])](#__dangerouslydisablesanitizers-string) - - [`__dangerouslyDisableSanitizersByTagID` ({[String]})](#__dangerouslydisablesanitizersbytagid-string) - - [`changed` (Function)](#changed-function) - - [`refreshOnceOnNavigation` (Boolean)](#refreshonceonnavigation-boolean) - - [`afterNavigation` (Function)](#afternavigation-function) - - [How `metaInfo` is Resolved](#how-metainfo-is-resolved) - - [Lists of Tags](#lists-of-tags) -- [Performance](#performance) - - [How to prevent the update on the initial page render](#how-to-prevent-the-update-on-the-initial-page-render) -- [FAQ](#faq) - - [How do I use component props and/or component data in `metaInfo`?](#how-do-i-use-component-props-andor-component-data-in-metainfo) - - [How do I populate `metaInfo` from the result of an asynchronous action?](#how-do-i-populate-metainfo-from-the-result-of-an-asynchronous-action) - - [Why doesn't `vue-meta` support `jsnext:main`?](#why-doesnt-vue-meta-support-jsnextmain) -- [Examples](#examples) +## Documentation - +Please find the documention on https://vue-meta.nuxtjs.org +> :globe_with_meridians: Please help us translate the documentation into your language -# Description -`vue-meta` is a [Vue 2.0](https://vuejs.org) plugin that allows you to manage your app's meta information, much like [`react-helmet`](https://github.com/nfl/react-helmet) does for React. However, instead of setting your data as props passed to a proprietary component, you simply export it as part of your component's data using the `metaInfo` property. +## Examples -These properties, when set on a deeply nested component, will cleverly overwrite their parent components' `metaInfo`, thereby enabling custom info for each top-level view as well as coupling meta info directly to deeply nested subcomponents for more maintainable code. +Looking for more examples what vue-meta can do for you? Have a look at the [examples](https://github.com/nuxt/vue-meta/tree/master/examples) -# Installation +## Installation -### Yarn +##### Yarn ```sh $ yarn add vue-meta ``` -### NPM +##### npm ```sh $ npm install vue-meta --save ``` -### CDN +##### Download / CDN -Use the links below - if you want a previous version, check the instructions at https://unpkg.com. +Use the download links below - if you want a previous version, check the instructions at https://unpkg.com. + +Latest version: https://unpkg.com/vue-meta/lib/vue-meta.min.js + +Latest v1.x version: https://unpkg.com/vue-meta@1/lib/vue-meta.min.js - **Uncompressed:** ```html - + ``` **Minified:** ```html - + ``` - -# Usage +## Quick Usage -## Step 1: Preparing the plugin -> This step is optional if you don't need SSR and `Vue` is available as a global variable. `vue-meta` will install itself in this case. - -In order to use this plugin, you first need to pass it to `Vue.use` - if you're not rendering on the server-side, your JS entry file will suffice. If you are rendering on the server, then place it in a file that runs both on the server and on the client before your root instance is mounted. If you're using [`vue-router`](https://github.com/vuejs/vue-router), then your main `router.js` file is a good place: - -**router.js:** +See the [documentation](https://vue-meta.nuxtjs.org) for more information ```js import Vue from 'vue' -import Router from 'vue-router' -import Meta from 'vue-meta' +import VueMeta from 'vue-meta' -Vue.use(Router) -Vue.use(Meta) - -export default new Router({ - ... +Vue.use(VueMeta, { + // optional pluginOptions + refreshOnceOnNavigation: true }) ``` -#### Options +## Higher level frameworks using vue-meta +If you wish to create your app even more quickly, take a look at the following frameworks which use vue-meta -`vue-meta` allows a few custom options: +- [Nuxt.js](https://github.com/nuxt/nuxt.js) - The Vue.js Meta framework +- [Gridsome](https://github.com/gridsome/gridsome) - The Vue.js JAMstack framework -```js -Vue.use(Meta, { - keyName: 'metaInfo', // the component option name that vue-meta looks for meta info on. - attribute: 'data-vue-meta', // the attribute name vue-meta adds to the tags it observes - ssrAttribute: 'data-vue-meta-server-rendered', // the attribute name that lets vue-meta know that meta info has already been server-rendered - tagIDKeyName: 'vmid' // the property name that vue-meta uses to determine whether to overwrite or append a tag -}) -``` +# License -If you don't care about server-side rendering, you can skip straight to [step 3](#step-3-start-defining-metainfo). Otherwise, continue. :smile: - -## Step 2: Server Rendering (Optional) - -If you have an isomorphic/universal webapp, you'll likely want to render your metadata on the server side as well. Here's how. - -### Step 2.1: Exposing `$meta` to `bundleRenderer` - -You'll need to expose the results of the `$meta` method that `vue-meta` adds to the Vue instance to the bundle render context before you can begin injecting your meta information. You'll need to do this in your server entry file: - -**server-entry.js:** -```js -import app from './app' - -const router = app.$router -const meta = app.$meta() // here - -export default (context) => { - router.push(context.url) - context.meta = meta // and here - return app -} -``` - -### Step 2.2: Populating the document meta info with `inject()` - -All that's left for you to do now before you can begin using `metaInfo` options in your components is to make sure they work on the server by `inject`-ing them so you can call `text()` on each item to render out the necessary info. You have two methods at your disposal: - -#### Simple Rendering with `renderToString()` - -Considerably the easiest method to wrap your head around is if your Vue server markup is rendered out as a string: - -**server.js:** - -```js -app.get('*', (req, res) => { - const context = { url: req.url } - renderer.renderToString(context, (error, html) => { - if (error) return res.send(error.stack) - const bodyOpt = { body: true } - const { - title, htmlAttrs, headAttrs, bodyAttrs, link, style, script, noscript, meta - } = context.meta.inject() - return res.send(` - - - - ${meta.text()} - ${title.text()} - ${link.text()} - ${style.text()} - ${script.text()} - ${noscript.text()} - - - ${html} - - - ${script.text(bodyOpt)} - - - `) - }) -}) -``` - -If you are using a separate template file, edit your head tag with - -```html - - {{{ meta.inject().title.text() }}} - {{{ meta.inject().meta.text() }}} - -``` - -Notice the use of `{{{` to avoid double escaping. Be extremely cautious when you use `{{{` with `__dangerouslyDisableSanitizers`. - -#### Streaming Rendering with `renderToStream()` - -A little more complex, but well worth it, is to instead stream your response. `vue-meta` supports streaming with no effort (on it's part :stuck_out_tongue_winking_eye:) thanks to Vue's clever `bundleRenderer` context injection: - -**server.js** -```js -app.get('*', (req, res) => { - const context = { url: req.url } - const renderStream = renderer.renderToStream(context) - renderStream.once('data', () => { - const bodyOpt = { body: true } - const { - title, htmlAttrs, headAttrs, bodyAttrs, link, style, script, noscript, meta - } = context.meta.inject() - res.write(` - - - - ${meta.text()} - ${title.text()} - ${link.text()} - ${style.text()} - ${script.text()} - ${noscript.text()} - - - `) - }) - renderStream.on('data', (chunk) => { - res.write(chunk) - }) - renderStream.on('end', () => { - res.end(` - - - ${script.text(bodyOpt)} - - - `) - }) - renderStream.on('error', (error) => res.status(500).end(`
${error.stack}
`)) -}) -``` - -## Step 3: Start defining `metaInfo` - -In any of your components, define a `metaInfo` property: - -**App.vue:** -```html - - - -``` - -**Home.vue** -```html - - - -``` - -**About.vue** -```html - - - -``` - -### Recognized `metaInfo` Properties - -#### `title` (String) - -Maps to the inner-text value of the `` element. - -```js -{ - metaInfo: { - title: 'Foo Bar' - } -} -``` - -```html -<title>Foo Bar -``` - -#### `titleTemplate` (String | Function) - -The value of `title` will be injected into the `%s` placeholder in `titleTemplate` before being rendered. The original title will be available on `metaInfo.titleChunk`. - -```js -{ - metaInfo: { - title: 'Foo Bar', - titleTemplate: '%s - Baz' - } -} -``` - -```html -Foo Bar - Baz -``` - -The property can also be a function (from [v1.2.0](https://github.com/nuxt/vue-meta/releases/tag/v1.2.0)): - -```js -titleTemplate: (titleChunk) => { - // If undefined or blank then we don't need the hyphen - return titleChunk ? `${titleChunk} - Site Title` : 'Site Title'; -} -``` - -#### `htmlAttrs` (Object) - -Each **key:value** maps to the equivalent **attribute:value** of the `` element. - -```js -{ - metaInfo: { - htmlAttrs: { - foo: 'bar', - amp: undefined - } - } -} -``` - -```html - -``` - -#### `headAttrs` (Object) - -Each **key:value** maps to the equivalent **attribute:value** of the `` element. - -```js -{ - metaInfo: { - headAttrs: { - foo: 'bar' - } - } -} -``` - -```html - -``` - -#### `bodyAttrs` (Object) - -Each **key:value** maps to the equivalent **attribute:value** of the `` element. - -```js -{ - metaInfo: { - bodyAttrs: { - bar: 'baz' - } - } -} -``` - -```html -Foo Bar -``` - -#### `base` (Object) - -Maps to a newly-created `` element, where object properties map to attributes. - -```js -{ - metaInfo: { - base: { target: '_blank', href: '/' } - } -} -``` - -```html - -``` - -#### `meta` ([Object]) - -Each item in the array maps to a newly-created `` element, where object properties map to attributes. - -```js -{ - metaInfo: { - meta: [ - { charset: 'utf-8' }, - { name: 'viewport', content: 'width=device-width, initial-scale=1' } - ] - } -} -``` - -```html - - -``` - -Since v1.5.0, you can now set up meta templates that work similar to the titleTemplate: - -```js -{ - metaInfo: { - meta: [ - { charset: 'utf-8' }, - { - 'property': 'og:title', - 'content': 'Test title', - 'template': chunk => `${chunk} - My page`, //or as string template: '%s - My page', - 'vmid': 'og:title' - } - ] - } -} -``` - -```html - - -``` - - -#### `link` ([Object]) - -Each item in the array maps to a newly-created `` element, where object properties map to attributes. - -```js -{ - metaInfo: { - link: [ - { rel: 'stylesheet', href: '/css/index.css' }, - { rel: 'favicon', href: 'favicon.ico' } - ] - } -} -``` - -```html - - -``` - -#### `style` ([Object]) - -Each item in the array maps to a newly-created ` -``` - -#### `script` ([Object]) - -Each item in the array maps to a newly-created ` -``` - -:warning: You have to disable sanitizers so the content of `innerHTML` won't be escaped. Please refer to [\__dangerouslyDisableSanitizers](#__dangerouslydisablesanitizers-string) section below for more info on related risks. - -```js -{ - metaInfo: { - script: [ - { innerHTML: '{ "@context": "http://schema.org" }', type: 'application/ld+json' } - ], - __dangerouslyDisableSanitizers: ['script'], - } -} -``` - -```html - -``` - -If your browser doesn't support `defer` or any other reason, you want to put ` -``` - -**PostContainer.vue:** -```html - - - -``` - -## How do I populate `metaInfo` from the result of an asynchronous action? - -`vue-meta` will do this for you automatically when your component state changes. - -Just make sure that you're using the function form of `metaInfo`: - -```js -{ - data () { - return { - title: 'Foo Bar Baz' - } - }, - metaInfo () { - return { - title: this.title - } - } -} -``` - -Check out the [vuex-async](https://github.com/nuxt/vue-meta/tree/master/examples/vuex-async) example for a far more detailed demonstration if you have doubts. - -Credit & Thanks for this feature goes to [Sébastien Chopin](https://github.com/Atinux). - -## Why doesn't `vue-meta` support `jsnext:main`? - -Originally, it did - however, it caused [problems](https://github.com/nuxt/vue-meta/issues/25). Essentially, Vue [does not support](https://github.com/vuejs/vue/issues/2880) `jsnext:main`, and does not introspect for the `default` property that is transpiled from the ES2015 source, thus breaking module resolution. - -Given that `jsnext:main` is a non-standard property that won't stick around for long, and `vue-meta` is bundled into one file with no dynamic module internals as well as the fact that if you're using `vue-meta`, you're 99.9% likely to not be using it conditionally - the decision has been made to drop support for it entirely. - -If this were not the case, you would have to instruct Babel to convert `default` imports to the proper commonjs module syntax via a plugin, which is not ideal since many users in the Vue landscape write their code in TypeScript, not Babel. - -# Examples - -To run the examples locally; clone this repository and run `cd examples && yarn install` in the root directory, then run `yarn start`. Head to http://localhost:3000 or run with `HOST=0.0.0.0 PORT=8080 yarn start` to change host or port - -If you would like to help to develop vue-meta then run `yarn install` both in the root and examples dir and run `yarn dev` +[MIT](./LICENSE.md) diff --git a/docs/.vuepress/config.js b/docs/.vuepress/config.js new file mode 100644 index 0000000..220d480 --- /dev/null +++ b/docs/.vuepress/config.js @@ -0,0 +1,70 @@ +module.exports = { + locales: { + '/': { + lang: 'en-US', + title: 'Vue Meta', + description: 'Metadata manager for Vue.js' + }, + }, + serviceWorker: true, + theme: 'vue', + themeConfig: { + repo: 'nuxt/vue-meta', + docsDir: 'docs', + locales: { + '/': { + label: 'English', + selectText: 'Languages', + editLinkText: 'Edit this page on GitHub', + nav: [{ + text: 'Guide', + link: '/guide/' + }, { + text: 'API', + link: '/api/' + }, { + text: 'Release Notes', + link: 'https://github.com/nuxt/vue-meta/releases' + }], + sidebar: [ + '/installation.md', + '/', + { + title: 'Usage', + collapsable: false, + children: [ + '/guide/', + '/guide/ssr', + '/guide/metainfo', + '/guide/special', + '/guide/caveats', + ] + }, + { + title: 'FAQ', + collapsable: false, + children: [ + '/faq/', + '/faq/performance.md', + '/faq/prevent-initial.md', + '/faq/component-props.md', + '/faq/async-action.md', + ] + }, + /*{ + title: 'Advanced', + collapsable: false, + children: [ + '/guide/advanced/navigation-guards.md', + '/guide/advanced/meta.md', + '/guide/advanced/transitions.md', + '/guide/advanced/data-fetching.md', + '/guide/advanced/scroll-behavior.md', + '/guide/advanced/lazy-loading.md' + ] + }*/ + ] + }, + } + } +} diff --git a/docs/.vuepress/public/logo.png b/docs/.vuepress/public/logo.png new file mode 100644 index 0000000..1b1e1c9 Binary files /dev/null and b/docs/.vuepress/public/logo.png differ diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..70da502 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,14 @@ +vue-meta + +::: tip We need your help +We are working on defining the RFC for Vue Meta v3.0. It will be a ground-breaking release built from the ground up. + +We would like your help with this! Please visit the [Vue Meta v3.0 rfc](https://github.com/nuxt/rfcs/issues/19) and let us know your thoughts. +::: + +# Introduction +`vue-meta` is a [Vue.js](https://vuejs.org) plugin that allows you to manage your app's metadata, much like [`react-helmet`](https://github.com/nfl/react-helmet) does for React. However, instead of setting your data as props passed to a proprietary component, you simply export it as part of your component's data using the `metaInfo` property. + +These properties, when set on a deeply nested component, will cleverly overwrite their parent components' `metaInfo`, thereby enabling custom info for each top-level view as well as coupling metadata directly to deeply nested sub components for more maintainable code. + +[Get started](/guide) or play with the [examples](https://github.com/nuxt/vue-meta/tree/master/examples) diff --git a/docs/api/README.md b/docs/api/README.md new file mode 100644 index 0000000..403213f --- /dev/null +++ b/docs/api/README.md @@ -0,0 +1,470 @@ +--- +sidebar: auto +--- + +# API Reference + +## Plugin options + +### keyName +- type `string` +- default `metaInfo` + +The name of the component option that contains all the information that gets converted to the various meta tags & attributes for the page + +### attribute +- type `string` +- default `data-vue-meta` + +The name of the attribute vue-meta arguments on elements to know which it should manage and which it should ignore. + +### ssrAttribute +- type `string` +- default `data-vue-meta-server-rendered` + +The name of the attribute that is added to the `html` tag to inform `vue-meta` that the server has already generated the meta tags for the initial render + +See [How to prevent update on page load](/faq/prevent-initial) + +### tagIDKeyName +- type `string` +- default `vmid` + +The property that tells `vue-meta` to overwrite (instead of append) an item in a tag list. +For example, if you have two `meta` tag list items that both have `vmid` of 'description', +then vue-meta will overwrite the shallowest one with the deepest one. + +### contentKeyName +- type `string` +- default `content` + +The key name for the content-holding property + +### metaTemplateKeyName +- type `string` +- default `template` + +The key name for possible meta templates + +### refreshOnceOnNavigation +- type `boolean` +- default `false` + +When `true` then `vue-meta` will pause updates once page navigation starts and resumes updates when navigation finishes (resuming also triggers an update). +This could both be a performance improvement as a possible fix for 'flickering' when you are e.g. replacing stylesheets + +## Plugin methods + +The `vue-meta` plugin injects a `$meta()` function in the Vue prototype which provides the following methods + +:::tip Note +`$meta()` is a function so we only need to insert it once in the `Vue.prototype`, but still use `this` to reference the component it was called from +::: + +### $meta().getOptions +- returns [`pluginOptions`](/api/#plugin-options) + +Could be used by third-party libraries who wish to interact with `vue-meta` + +### $meta().refresh +- returns [`metaInfo`](/api/#metaInfo-properties) + +Updates the current metadata with new metadata. +Useful when updating metadata as the result of an asynchronous action that resolves after the initial render takes place. + +### $meta().inject +- returns [`metaInfo`](/api/#metaInfo-properties) + +:::tip SSR only +`inject` is available in the server plugin only and is not available on the client +::: + +It returns a special `metaInfo` object where all keys have an object as value which contains a `text()` method for returning html code + +See [Rendering with renderToString](/guide/ssr.html#simple-rendering-with-rendertostring) for an example + +### $meta().pause +- arguments: + - refresh (type `boolean`, default `false`) +- returns `resume()` + +Pauses global metadata updates until either the returned resume method is called or [resume](/api/#meta-resume) + +### $meta().resume +- arguments: + - refresh (type `boolean`, default `false`) +- returns [`metaInfo`](/api/#metaInfo-properties) (optional) + +Resumes metadata updates after they have been paused. If `refresh` is `true` it immediately initiates a metadata update by calling [refresh](/api/#meta-refresh) + +## metaInfo properties + +::: tip Note +The documentation below uses `metaInfo` as `keyName` in the examples, please note that this is [configurable](/api/#keyname) and could be different in your case +::: + +### title +- type `string` + +Maps to the inner-text value of the `` element. + +```js +{ + metaInfo: { + title: 'Foo Bar' + } +} +``` + +```html +<title>Foo Bar +``` + +### titleTemplate +- type `string | Function` + +The value of `title` will be injected into the `%s` placeholder in `titleTemplate` before being rendered. The original title will be available on `metaInfo.titleChunk`. + +```js +{ + metaInfo: { + title: 'Foo Bar', + titleTemplate: '%s - Baz' + } +} +``` + +```html +Foo Bar - Baz +``` + +The property can also be a function: + +```js +titleTemplate: (titleChunk) => { + // If undefined or blank then we don't need the hyphen + return titleChunk ? `${titleChunk} - Site Title` : 'Site Title'; +} +``` + +### htmlAttrs +### headAttrs +### bodyAttrs +- type `object` + +Each **key:value** maps to the equivalent **attribute:value** of the `` element. + +Since `v2.0` value can also be an `Array` + +```js +{ + metaInfo: { + htmlAttrs: { + lang: 'en', + amp: true + }, + bodyAttrs: { + class: ['dark-mode', 'mobile'] + } + } +} +``` + +```html + +Foo Bar +``` + +### base +- type `object` + +Maps to a newly-created `` element, where object properties map to attributes. + +```js +{ + metaInfo: { + base: { target: '_blank', href: '/' } + } +} +``` + +```html + +``` + +### meta +- type `collection` + +Each item in the array maps to a newly-created `` element, where object properties map to attributes. + +```js +{ + metaInfo: { + meta: [ + { charset: 'utf-8' }, + { name: 'viewport', content: 'width=device-width, initial-scale=1' } + ] + } +} +``` + +```html + + +``` + +#### Content templates + +Since `v1.5.0`, you can now set up meta templates that work similar to the titleTemplate: + +```js +{ + metaInfo: { + meta: [ + { charset: 'utf-8' }, + { + 'property': 'og:title', + 'content': 'Test title', + 'template': chunk => `${chunk} - My page`, //or as string template: '%s - My page', + 'vmid': 'og:title' + } + ] + } +} +``` + +```html + + +``` + +### link +- type `collection` + + +Each item in the array maps to a newly-created `` element, where object properties map to attributes. + +```js +{ + metaInfo: { + link: [ + { rel: 'stylesheet', href: '/css/index.css' }, + { rel: 'favicon', href: 'favicon.ico' } + ] + } +} +``` + +```html + + +``` + +### style +- type `object` + +Each item in the array maps to a newly-created ` +``` + +### script +- type `collection` + +Each item in the array maps to a newly-created ` +``` + +#### Add json or other raw data +::: warning +You have to disable sanitizers so the content of `innerHTML` won't be escaped. Please see [__dangerouslyDisableSanitizersByTagID](/api/#dangerouslydisablesanitizersbytagid) for more info on related risks +::: + +```js +{ + metaInfo: { + script: [{ + vmid: 'ldjson-schema', + innerHTML: '{ "@context": "http://schema.org" }', + type: 'application/ld+json' + }], + __dangerouslyDisableSanitizersByTagID: { + 'ldjson-schema': ['innerHTML'] + }, + } +} +``` + +```html + +``` +#### Special attribute: `body: true` + +If you e.g. wish to force delayed execution of a script or just want the script to be included in the `` of the page, add `body: true`. +Script tags with `body: true` are rendered just before `` + +```js +{ + metaInfo: { + script: [{ + innerHTML: 'console.log("I am in body");', + type: 'text/javascript', + body: true + }] + } +} +``` + +### noscript +- type `collection` + +Each item in the array maps to a newly-created `