From d974aa9c2a8c3789c974e4247d0f4bbdff260c27 Mon Sep 17 00:00:00 2001
From: Yangshun Tay
Date: Wed, 19 Feb 2020 09:22:14 +0800
Subject: [PATCH] feat(v2): add ExecutionEnvironment API (#2296)
* feat(v2): add ExecutionEnvironment API
* remove redundant code
---
.../src/analytics.js | 4 +-
.../docusaurus-plugin-google-gtag/src/gtag.js | 3 +-
packages/docusaurus/src/client/clientEntry.js | 3 +-
.../client/exports/ExecutionEnvironment.js | 25 ++++++++
.../docusaurus/src/client/exports/Link.js | 4 +-
website/docs/docusaurus-core.md | 59 ++++++++++++++-----
6 files changed, 77 insertions(+), 21 deletions(-)
create mode 100644 packages/docusaurus/src/client/exports/ExecutionEnvironment.js
diff --git a/packages/docusaurus-plugin-google-analytics/src/analytics.js b/packages/docusaurus-plugin-google-analytics/src/analytics.js
index 38d9edf6ea..139ed18923 100644
--- a/packages/docusaurus-plugin-google-analytics/src/analytics.js
+++ b/packages/docusaurus-plugin-google-analytics/src/analytics.js
@@ -5,8 +5,10 @@
* LICENSE file in the root directory of this source tree.
*/
+import ExecutionEnvironment from '@docusaurus/ExecutionEnvironment';
+
export default (function() {
- if (typeof window === 'undefined') {
+ if (!ExecutionEnvironment.canUseDOM) {
return null;
}
diff --git a/packages/docusaurus-plugin-google-gtag/src/gtag.js b/packages/docusaurus-plugin-google-gtag/src/gtag.js
index 9b7e8a3ebf..959b9f8a1e 100644
--- a/packages/docusaurus-plugin-google-gtag/src/gtag.js
+++ b/packages/docusaurus-plugin-google-gtag/src/gtag.js
@@ -5,10 +5,11 @@
* LICENSE file in the root directory of this source tree.
*/
+import ExecutionEnvironment from '@docusaurus/ExecutionEnvironment';
import siteConfig from '@generated/docusaurus.config';
export default (function() {
- if (typeof window === 'undefined') {
+ if (!ExecutionEnvironment.canUseDOM) {
return null;
}
diff --git a/packages/docusaurus/src/client/clientEntry.js b/packages/docusaurus/src/client/clientEntry.js
index 28fc5408c6..3265c9ccf9 100644
--- a/packages/docusaurus/src/client/clientEntry.js
+++ b/packages/docusaurus/src/client/clientEntry.js
@@ -10,12 +10,13 @@ import {hydrate, render} from 'react-dom';
import {BrowserRouter} from 'react-router-dom';
import routes from '@generated/routes';
+import ExecutionEnvironment from '@docusaurus/ExecutionEnvironment';
import App from './App';
import preload from './preload';
import docusaurus from './docusaurus';
// Client-side render (e.g: running in browser) to become single-page application (SPA).
-if (typeof window !== 'undefined' && typeof document !== 'undefined') {
+if (ExecutionEnvironment.canUseDOM) {
window.docusaurus = docusaurus;
// For production, attempt to hydrate existing markup for performant first-load experience.
// For development, there is no existing markup so we had to render it.
diff --git a/packages/docusaurus/src/client/exports/ExecutionEnvironment.js b/packages/docusaurus/src/client/exports/ExecutionEnvironment.js
new file mode 100644
index 0000000000..be810f723f
--- /dev/null
+++ b/packages/docusaurus/src/client/exports/ExecutionEnvironment.js
@@ -0,0 +1,25 @@
+/**
+ * Copyright (c) 2017-present, Facebook, Inc.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+const canUseDOM = !!(
+ typeof window !== 'undefined' &&
+ window.document &&
+ window.document.createElement
+);
+
+const ExecutionEnvironment = {
+ canUseDOM,
+
+ canUseEventListeners:
+ canUseDOM && !!(window.addEventListener || window.attachEvent),
+
+ canUseIntersectionObserver: canUseDOM && 'IntersectionObserver' in window,
+
+ canUseViewport: canUseDOM && !!window.screen,
+};
+
+module.exports = ExecutionEnvironment;
diff --git a/packages/docusaurus/src/client/exports/Link.js b/packages/docusaurus/src/client/exports/Link.js
index 9a21bbc0b4..861b807d5d 100644
--- a/packages/docusaurus/src/client/exports/Link.js
+++ b/packages/docusaurus/src/client/exports/Link.js
@@ -8,6 +8,7 @@
import React, {useEffect, useRef} from 'react';
import {NavLink} from 'react-router-dom';
import isInternalUrl from '@docusaurus/isInternalUrl';
+import ExecutionEnvironment from '@docusaurus/ExecutionEnvironment';
function Link(props) {
const {to, href} = props;
@@ -15,8 +16,7 @@ function Link(props) {
const isInternal = isInternalUrl(targetLink);
const preloaded = useRef(false);
- const IOSupported =
- typeof window !== 'undefined' && 'IntersectionObserver' in window;
+ const IOSupported = ExecutionEnvironment.canUseIntersectionObserver;
let io;
const handleIntersection = (el, cb) => {
diff --git a/website/docs/docusaurus-core.md b/website/docs/docusaurus-core.md
index 80bda8f829..1b80fc12d5 100644
--- a/website/docs/docusaurus-core.md
+++ b/website/docs/docusaurus-core.md
@@ -4,9 +4,11 @@ title: Docusaurus Client API
sidebar_label: Client API
---
-Docusaurus provides some API on client that can be helpful when building your site.
+Docusaurus provides some APIs on the clients that can be helpful to you when building your site.
-## `Head`
+## Components
+
+### ``
This reusable React component will manage all of your changes to the document head. It takes plain HTML tags and outputs plain HTML tags and is beginner-friendly. It is a wrapper around [React Helmet](https://github.com/nfl/react-helmet).
@@ -55,7 +57,7 @@ Outputs
```
-## `Link`
+### ``
This component enables linking to internal pages as well as a powerful performance feature called preloading. Preloading is used to prefetch resources so that the resources are fetched by the time the user navigates with this component. We use an `IntersectionObserver` to fetch a low-priority request when the `` is in the viewport and then use an `onMouseOver` event to trigger a high-priority request when it is likely that a user will navigate to the requested resource.
@@ -78,7 +80,7 @@ const Page = () => (
);
```
-### `to`: string
+#### `to`: string
The target location to navigate to. Example: `/docs/introduction`.
@@ -86,7 +88,7 @@ The target location to navigate to. Example: `/docs/introduction`.
```
-### `activeClassName`: string
+#### `activeClassName`: string
The class to give the `` when it is active. The default given class is `active`. This will be joined with the `className` prop.
@@ -96,9 +98,26 @@ The class to give the `` when it is active. The default given class is `ac
```
-## `useDocusaurusContext`
+### ``
-React Hooks to access Docusaurus Context. Context contains `siteConfig` object from [docusaurus.config.js](docusaurus.config.js.md).
+Rendering a `` will navigate to a new location. The new location will override the current location in the history stack, like server-side redirects (HTTP 3xx) do. You can refer to [React Router's Redirect documentation](https://reacttraining.com/react-router/web/api/Redirect) for more info on available props.
+
+Example usage:
+
+```jsx {2,5}
+import React from 'react';
+import {Redirect} from '@docusaurus/router';
+
+function Home() {
+ return ;
+}
+```
+
+## Hooks
+
+### `useDocusaurusContext`
+
+React hook to access Docusaurus Context. Context contains `siteConfig` object from [docusaurus.config.js](docusaurus.config.js.md).
```ts
interface DocusaurusContext {
@@ -121,9 +140,9 @@ const Test = () => {
};
```
-## `useBaseUrl`
+### `useBaseUrl`
-React Hook to automatically append `baseUrl` to a string automatically. This is particularly useful if you don't want to hardcode your baseUrl.
+React hook to automatically append `baseUrl` to a string automatically. This is particularly useful if you don't want to hardcode your config's `baseUrl`. We highly recommend you to use this.
Example usage:
@@ -145,17 +164,25 @@ function Help() {
}
```
-## `Redirect`
+## Modules
-Rendering a `` will navigate to a new location. The new location will override the current location in the history stack, like server-side redirects (HTTP 3xx) do. You can refer to [React Router's Redirect documentation](https://reacttraining.com/react-router/web/api/Redirect) for more info on available props.
+### `ExecutionEnvironment`
-Example usage:
+A module which exposes a few boolean variables to check the current rendering environment. Useful if you want to only run certain code on client/server or need to write server-side rendering compatible code.
-```jsx {2,5}
+```jsx {2}
import React from 'react';
-import {Redirect} from '@docusaurus/router';
+import ExecutionEnvironment from '@docusaurus/ExecutionEnvironment';
-function Home() {
- return ;
+function MyPage() {
+ const location = ExecutionEnvironment.canUseDOM ? window.href.location : null;
+ return
{location}
;
}
```
+
+| Field | Description |
+| --- | --- |
+| `ExecutionEnvironment.canUseDOM` | `true` if on client, `false` if SSR. |
+| `ExecutionEnvironment.canUseEventListeners` | `true` if on client and has `window.addEventListener`. |
+| `ExecutionEnvironment.canUseIntersectionObserver` | `true` if on client and has `IntersectionObserver`. |
+| `ExecutionEnvironment.canUseViewport` | `true` if on client and has `window.screen`. |