Script examples (#31181)

* Add script component examples

* Update script examples

* Refactor code

* Remove unused files

* Fix linter

* Fix lint error

* Fix prettier
This commit is contained in:
Gonzalo Pozzo 2021-11-16 13:47:12 -03:00 committed by GitHub
parent b79591cdaf
commit d550b13b8d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 338 additions and 0 deletions

View file

@ -7,6 +7,7 @@ description: Next.js helps you optimize loading third-party scripts with the bui
<details>
<summary><b>Examples</b></summary>
<ul>
<li><a href="https://github.com/vercel/next.js/tree/canary/examples/script-component">Script Component</a></li>
<li><a href="https://github.com/vercel/next.js/tree/canary/examples/with-google-tag-manager">Google Tag Manager</a></li>
<li><a href="https://github.com/vercel/next.js/tree/canary/examples/with-google-analytics">Google Analytics</a></li>
<li><a href="https://github.com/vercel/next.js/tree/canary/examples/with-facebook-pixel">Facebook Pixel</a></li>

34
examples/script-component/.gitignore vendored Normal file
View file

@ -0,0 +1,34 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# next.js
/.next/
/out/
# production
/build
# misc
.DS_Store
*.pem
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# local env files
.env.local
.env.development.local
.env.test.local
.env.production.local
# vercel
.vercel

View file

@ -0,0 +1,33 @@
# Script component
This example shows different strategies that can be used for the [`next/script` component](https://nextjs.org/docs/basic-features/script):
- [Loading Polyfills](./pages/polyfill.js)
- [Lazy loading](./pages/lazy.js)
- [Executing code after loading](./pages/onload.js)
- [Inline scripts](./pages/inline.js)
- [Forwarding attributes](./pages/attributes.js)
## Preview
Preview the example live on [StackBlitz](http://stackblitz.com/):
[![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/vercel/next.js/tree/canary/examples/script-component)
## Deploy your own
Deploy the example using [Vercel](https://vercel.com?utm_source=github&utm_medium=readme&utm_campaign=next-example):
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/git/external?repository-url=https://github.com/vercel/next.js/tree/canary/examples/script-component&project-name=script-component&repository-name=script-component)
## How to use
Execute [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app) with [npm](https://docs.npmjs.com/cli/init) or [Yarn](https://yarnpkg.com/lang/en/docs/cli/create/) to bootstrap the example:
```bash
npx create-next-app --example script-component script-component-app
# or
yarn create next-app --example script-component script-component-app
```
Deploy it to the cloud with [Vercel](https://vercel.com/new?utm_source=github&utm_medium=readme&utm_campaign=next-example) ([Documentation](https://nextjs.org/docs/deployment)).

View file

@ -0,0 +1,13 @@
{
"private": true,
"scripts": {
"dev": "next",
"build": "next build",
"start": "next start"
},
"dependencies": {
"next": "latest",
"react": "^17.0.2",
"react-dom": "^17.0.2"
}
}

View file

@ -0,0 +1,20 @@
import Link from 'next/link'
import '../styles/global.css'
const MyApp = ({ Component, pageProps, router }) => {
const pathname = router.pathname
return (
<>
<Component {...pageProps} />
{pathname !== '/' && (
<Link href="/">
<a>See all examples</a>
</Link>
)}
</>
)
}
export default MyApp

View file

@ -0,0 +1,22 @@
import Script from 'next/script'
export default function Inline() {
return (
<>
{/* Attributes are forwarded */}
<Script
src="https://www.google-analytics.com/analytics.js"
id="analytics"
nonce="XUENAJFW"
data-test="analytics"
/>
<main>
<h1>Forwarded attributes</h1>
<h5>
Open devtools and check that attributes has been forwarded correctly.
</h5>
</main>
</>
)
}

View file

@ -0,0 +1,36 @@
import Link from 'next/link'
export default function Index() {
return (
<main>
<h1>Script component examples</h1>
<ul>
<li>
<Link href="/polyfill">
<a>Polyfill</a>
</Link>
</li>
<li>
<Link href="/lazy">
<a>Lazy Loading</a>
</Link>
</li>
<li>
<Link href="/onload">
<a>Executing code after loading</a>
</Link>
</li>
<li>
<Link href="/inline">
<a>Inline scripts</a>
</Link>
</li>
<li>
<Link href="/attributes">
<a>Forwarding attributes</a>
</Link>
</li>
</ul>
</main>
)
}

View file

@ -0,0 +1,29 @@
import Script from 'next/script'
export default function Inline() {
return (
<>
{/* Execute arbitrary code */}
<Script id="show-banner" strategy="lazyOnload">
{`document.getElementById('banner').classList.remove('hidden')`}
</Script>
{/* Or */}
{/* <Script
id="show-banner"
dangerouslySetInnerHTML={{
__html: `document.getElementById('banner').classList.remove('hidden')`
}}
/> */}
<main>
<h1>Inline scripts</h1>
<h5 id="banner" className="hidden">
This is initially hidden but its being shown because the `Script`
component removed the `hidden` class.
</h5>
</main>
</>
)
}

View file

@ -0,0 +1,42 @@
import { useCallback, useEffect, useState } from 'react'
import Script from 'next/script'
export default function Lazyload() {
const [log, setLog] = useState([])
const addLog = useCallback(
(text) => {
setLog((log) => log.concat({ time: new Date(), text }))
},
[setLog]
)
useEffect(() => {
addLog(`Page loaded window.FB is undefined`)
}, [addLog])
return (
<>
{/* We lazy load the FB SDK */}
<Script
src="https://connect.facebook.net/en_US/sdk.js"
strategy="lazyOnload"
onLoad={() =>
addLog(`script loaded correctly, window.FB has been populated`)
}
/>
<main>
<h1>Lazy Loading FB sdk</h1>
<h5>You can check `window.FB` on browser console</h5>
<ul>
{log.map(({ time, text }) => (
<li key={+time}>
{time.toISOString()}: {text}
</li>
))}
</ul>
</main>
</>
)
}

View file

@ -0,0 +1,46 @@
import { useMemo, useState } from 'react'
import Script from 'next/script'
export default function Onload() {
const [stripe, setStripe] = useState(null)
const methods = useMemo(
() =>
stripe
? Object.entries(stripe.stripe).filter(
([_key, value]) => typeof value === 'function'
)
: [],
[stripe]
)
function handleLoad() {
const stripe = window.Stripe('pk_test_1234')
console.log('Stripe loaded: ', stripe)
setStripe({ stripe })
}
return (
<>
{/* We load Stripe sdk */}
<Script
id="stripe-js"
src="https://js.stripe.com/v3/"
onLoad={handleLoad}
/>
<main>
<h1>Executing code after loading</h1>
<div>
<p>Stripe methods: </p>
<ul>
{methods.map(([method]) => (
<li key={method}>{method}</li>
))}
</ul>
</div>
</main>
</>
)
}

View file

@ -0,0 +1,48 @@
import { useEffect, useRef, useState } from 'react'
import Script from 'next/script'
import s from '../styles/polyfill.module.css'
export default function Polyfill() {
const ref = useRef()
const [lastIntersection, setIntersection] = useState(new Date())
useEffect(() => {
const observer = new IntersectionObserver(
(intersections) => {
const isIntersecting = intersections[0]?.isIntersecting
if (isIntersecting) {
setIntersection(new Date())
}
},
{
rootMargin: '45px',
}
)
observer.observe(ref.current)
return () => observer.disconnect()
}, [])
return (
<>
{/* We ensure that intersection observer is available by polyfilling it */}
<Script
src="https://polyfill.io/v3/polyfill.min.js?features=IntersectionObserverEntry%2CIntersectionObserver"
strategy="beforeInteractive"
/>
<main className={s.container}>
<h1>IntersectionObserver Polyfill</h1>
<h5>Scroll down to see when was the last intersection</h5>
<section className={s.section}>
<span ref={ref}>
Last intersection at {lastIntersection.toTimeString()}
</span>
</section>
</main>
</>
)
}

View file

@ -0,0 +1,8 @@
* {
font-family: sans-serif;
box-sizing: border-box;
}
.hidden {
display: none;
}

View file

@ -0,0 +1,6 @@
.section {
height: 400vh;
display: flex;
align-items: center;
justify-content: center;
}