From 8f441899efeaee31382b39c861b6ad1586f054bf Mon Sep 17 00:00:00 2001 From: Pim Date: Wed, 24 Jul 2019 16:06:50 +0200 Subject: [PATCH] docs: update docs with new features (#416) --- docs/.vuepress/config.js | 2 + docs/api/README.md | 179 ++++++++++++++++++++++++++++---- docs/faq/chaining.md | 10 ++ docs/faq/prevent-initial.md | 12 ++- docs/guide/caveats.md | 35 +++++++ docs/guide/multiple-apps.md | 37 +++++++ docs/guide/ssr.md | 15 ++- test/fixtures/app.template.html | 2 +- 8 files changed, 264 insertions(+), 28 deletions(-) create mode 100644 docs/faq/chaining.md create mode 100644 docs/guide/multiple-apps.md diff --git a/docs/.vuepress/config.js b/docs/.vuepress/config.js index 447d9dd..97768a4 100644 --- a/docs/.vuepress/config.js +++ b/docs/.vuepress/config.js @@ -46,6 +46,7 @@ module.exports = { '/guide/metainfo', '/guide/special', '/guide/caveats', + '/guide/multiple-apps', ] }, { @@ -57,6 +58,7 @@ module.exports = { '/faq/prevent-initial.md', '/faq/component-props.md', '/faq/async-action.md', + '/faq/chaining.md', ] } ] diff --git a/docs/api/README.md b/docs/api/README.md index ecd995d..ff9ccbf 100644 --- a/docs/api/README.md +++ b/docs/api/README.md @@ -30,7 +30,7 @@ See [How to prevent update on page load](/faq/prevent-initial) - type `string` - default `ssr` -The app id for a server side rendered app. You shouldnt have to change this normallygit s +The app id for a server side rendered app. You shouldnt have to change this normally ### tagIDKeyName - type `string` @@ -89,6 +89,10 @@ It returns a special `metaInfo` object where all keys have an object as value wh See [Rendering with renderToString](/guide/ssr.html#simple-rendering-with-rendertostring) for an example +#### Passing arguments to `text()` + +In some cases you can pass an argument to the text method. E.g. to [automatically add the ssrAttribute on ssr](/faq/prevent-initial.html) or [render properties in the body](/api/#ssr-support) + ### $meta().pause - arguments: - refresh (type `boolean`, default `false`) @@ -229,10 +233,12 @@ Since `v1.5.0`, you can now set up meta templates that work similar to the title 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' + property: 'og:title', + content: 'Test title', + // following template options are identical + // template: '%s - My page', + template: chunk => `${chunk} - My page`, + vmid: 'og:title' } ] } @@ -304,7 +310,37 @@ Each item in the array maps to a newly-created ` ``` -#### Add json or other raw data +#### Add JSON data (since v2.1) + +If you wish to use a JSON variable within a script tag (e.g. for JSON-LD), you can directly pass your variable by using the `json` property. + +::: tip +When passing an array or object to the `json` property the keys and values of the variable will still be sanitized to prevent XSS +::: + +```js +{ + metaInfo: { + script: [{ + type: 'application/ld+json' + json: { + '@context': 'http://schema.org', + unsafe: '

hello

' + } + }] + } +} +``` + +```html + +``` + + +#### Add 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 ::: @@ -327,22 +363,6 @@ You have to disable sanitizers so the content of `innerHTML` won't be escaped. P ```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` @@ -474,3 +494,118 @@ The callback receives the following arguments: } ``` + +## Special metaInfo attributes + +These attributes define specific features when used in a metaInfo property + +### once + +When adding a metaInfo property that should be added once without reactivity (thus will never be updated) you can add `once: true` to the property. + +```js +{ + metaInfo: { + link: [{ + once: true, + rel: 'stylesheet' + href: 'style.css' + }] + } +} +``` + +### skip _(since v2.1)_ + +When a metaInfo property has a `skip` attribute with truthy value it will not be rendered. This attribute helps with e.g. chaining scripts (see [callback attribute](#callback-since-v2-1)) + +```js +{ + metaInfo: { + script: [{ + skip: true, + innerHTML: 'console.log("you wont see me")' + }] + } +} +``` + +### body +### pbody _(since v2.1)_ +tags +::: warning +VueMeta supports the body and pbody attributes on all metaInfo properties, but its up to you or your framework to support these attributes during SSR + +Using these body attributes without SSR support could result in hydration errors / re-rendering or missing tags. +::: + +You can filter tags to be included in the `` instead of the `` to e.g. force delayed execution of a script. + +Use `pbody: true` if you wish to prepend the tag to the body (so its rendered just after ``) or use `body: true` to append the tag to the body (the tag is rendered just before ``). + +```js +{ + metaInfo: { + script: [{ + innerHTML: 'console.log("I am in body");', + type: 'text/javascript', + body: true + }] + } +} +``` + +#### SSR Support + +When rendering your template on SSR make sure to pass an object as first argument to the text method of the metaInfo property with either a value `body: true` or `pbody: true` +```html + + + ${script.text()} + + + ${script.text({ pbody: true })} + +
+ + ${script.text({ body: true })} + +``` + +### callback _(since v2.1)_ + +:::tip vmid required on SSR +When using SSR it is required to define a [`vmid`](/api/#tagidkeyname) property for the metaInfo property + +The vmid is needed to resolve the corresponding callback for that element on hydration +::: + +The callback attribute should specificy a function which is called once the corresponding tag has been loaded (i.e. the onload event is triggered). Use this to chain javascript if one depends on the other. + +```js +{ + metaInfo() { + return { + script: [ + { + vmid: 'extscript', + src: '/my-external-script.js', + callback: () => (this.externalLoaded = true) + }, + { + skip: !this.externalLoaded, + innerHTML: ` + /* this is only added once external script has been loaded */ + /* and e.g. window.$externalVar exists */ + ` + } + ] + } + }, + data() { + return { + externalLoaded: false + } + } +} + diff --git a/docs/faq/chaining.md b/docs/faq/chaining.md new file mode 100644 index 0000000..2f142ba --- /dev/null +++ b/docs/faq/chaining.md @@ -0,0 +1,10 @@ +# How to chain metaInfo which are depending on each other + +Since v2.1 you can add a [callback attribute](/api/#callback-since-v2-1) to your metaInfo proerties which you can use to chain loading. + +If you e.g. need to load an external script before you can implement functionality based on that external script, you can use the [callback](/api/#callback-since-v2-1) and [skip](/api/#skip-since-v2-1) attributes to implement chaining of those scripts. See the API entry of the callback attribute for an [example](/api/#callback-since-v2-1) + + +::: tip +The callback / onload event is supported on `link`, `style` and `script` metaInfo properties +::: diff --git a/docs/faq/prevent-initial.md b/docs/faq/prevent-initial.md index d39e48b..e05c2ec 100644 --- a/docs/faq/prevent-initial.md +++ b/docs/faq/prevent-initial.md @@ -1,9 +1,15 @@ # How to prevent update on page load -Add the `data-vue-meta-server-rendered` attribute to the `` tag on the server-side: +In your template call the text method of `htmlAttrs` with `true` as first argument: +``` + +... +``` -```html - +Or manually add the [`data-vue-meta-server-rendered`](/api/#ssrattribute) attribute to the `` tag on the server-side: + +``` +> ... ``` diff --git a/docs/guide/caveats.md b/docs/guide/caveats.md index 3b6d797..a538b00 100644 --- a/docs/guide/caveats.md +++ b/docs/guide/caveats.md @@ -2,6 +2,8 @@ ## Reactive variables in template functions +_Corresponding issue_: [#322](https://github.com/nuxt/vue-meta/issues/322) + Both [title](/api/#titletemplate) as [meta](/api/#content-templates) support using template function. Due to how Vue determines reactivity it is not possible to use reactive variables directly in template functions @@ -37,3 +39,36 @@ You need to assign the reactive variable to a local variable first for this to w } } ``` + +## Duplicated tags after hydration with SSR + +_Corresponding issue_: [#404](https://github.com/nuxt/vue-meta/issues/404) + +:::tip +Please read [Multiple Vue apps support](/guide/multiple-apps.html#ssr) as a prerequisite +::: + +To optimize performance, VueMeta will only initialize for a Vue app when it finds a metaInfo property on any of the loaded components. That means if you render all your components by passing the component instance directly to the render function, Vue will only know of these components once the app gets mounted (see snippet below). And this means VueMeta is unable to find any metaInfo when it looks if its need to initialize in the `beforeCreate` hook and the appId will not be changed to the [ssrAppId](/api#ssrappid) + +```js +/* this is an example of when metaInfo will only become available once the + * app is mounted and VueMeta will not correctly initialize the SSR app + */ +const myComponent = { + metaInfo: { + title: 'title' + } +} + +export default App { + name: 'myApp', + render(h) { + return h(myComponent) + } +}; +``` + +This will result in all the metaInfo properties of your ssr app to be rendered twice, once with [ssrAppId](/api#ssrappid) and once with appId `1`. + +To prevent this, either make sure there is any metaInfo configured (on any component) when the `beforeCreate` hook runs. Alternative (but not recommended) you could set [ssrAppId](/api#ssrappid) to `1` as well. + diff --git a/docs/guide/multiple-apps.md b/docs/guide/multiple-apps.md new file mode 100644 index 0000000..e10dcb9 --- /dev/null +++ b/docs/guide/multiple-apps.md @@ -0,0 +1,37 @@ +# Multiple Vue apps support + +VueMeta includes basic support for using multiple Vue-apps which each adds their own metaInfo properties to the same html page. + +To keep track of which tag has been added by which Vue app, VueMeta stores an unique `appId` in the [data-vue-meta](/api/#attribute) attribute. + +::: danger +Currently VueMeta only supports adding tags for multiple apps. + +Adding [html](/api/#htmlattrs), [head](/api/#headattrs) or [body](/api/#bodyattrs) attributes is **not** supported. These will always be set to the values of the last app which triggered a metaInfo update. + +Therefore it is recommended you should only set those attributes from one of your Vue apps +::: + +## Client + +On the client side the `appId` starts at `1` and is incremented by one for each subsequent Vue app + +## SSR + +For an SSR app which is served by a Node.js server, the appId would change for each request if we would increment it as on the client. Therefore we use the special [ssrAppId](/api#ssrappid) so the `appId` is constant for every request. + +On hydration VueMeta will check if the Vue app was server-rendered and if so set it's appId to [ssrAppId](/api#ssrappid) as well. + +## `vmid` support + +::: warning +Support for cross-app vmid's might be insufficient for real world applications. +::: + +It is possible to use cross app [vmid](/api/#tagidkeyname)'s with two important caveats: + +- _The value of the last updated app is used_
+Cross app vmid's only work on the client. This is implemented through the use of querySelectors, not by merging metaInfo objects. This means the last app which was refreshed determines the value that is used + +- _There is no fallback to use the vmid of a previous app_
+Given app1 and app2 both with a metaInfo property with vmid1, then when app2 removes its value for vmid1 there is no way to retrieve app1's value for vmid1 which means the element is removed diff --git a/docs/guide/ssr.md b/docs/guide/ssr.md index c2f8451..ebb6d09 100644 --- a/docs/guide/ssr.md +++ b/docs/guide/ssr.md @@ -37,7 +37,7 @@ app.get('*', (req, res) => { } = context.meta.inject() return res.send(` - + ${meta.text()} ${title.text()} @@ -47,10 +47,22 @@ app.get('*', (req, res) => { ${noscript.text()} + + ${style.text({ pbody: true })} + ${script.text({ pbody: true })} + ${noscript.text({ pbody: true })} + + ${html} + + + + + ${style.text({ body: true })} ${script.text({ body: true })} + ${noscript.text({ body: true })} `) @@ -71,7 +83,6 @@ Notice the use of `{{{` to avoid double escaping. Be extremely cautious when you ## Inject metadata into page stream - 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** diff --git a/test/fixtures/app.template.html b/test/fixtures/app.template.html index 3b371f0..258e86d 100644 --- a/test/fixtures/app.template.html +++ b/test/fixtures/app.template.html @@ -1,5 +1,5 @@ - + {{ meta.text() }} {{ title.text() }}