From 9e4bb5a1299360042cb9170b5717be5657b75df3 Mon Sep 17 00:00:00 2001 From: Yichi Zhang Date: Tue, 1 Sep 2020 19:27:57 -0400 Subject: [PATCH 01/92] Add Fast Refresh Demo (#16576) Closes #16538 Basically reverts #16497 and some minor changes. Also adds a link in the docs. This reverts commit ec281df70b4d314d41b7d3bc7d0a8ddd42444d7a. --- docs/basic-features/fast-refresh.md | 7 ++ examples/fast-refresh-demo/.gitignore | 34 +++++++++ examples/fast-refresh-demo/README.md | 23 ++++++ .../fast-refresh-demo/components/Button.js | 5 ++ .../components/Button.module.css | 32 +++++++++ .../components/ClickCount.js | 11 +++ examples/fast-refresh-demo/global.css | 14 ++++ examples/fast-refresh-demo/package.json | 15 ++++ examples/fast-refresh-demo/pages/_app.js | 5 ++ examples/fast-refresh-demo/pages/index.js | 71 +++++++++++++++++++ .../fast-refresh-demo/styles/home.module.css | 11 +++ 11 files changed, 228 insertions(+) create mode 100644 examples/fast-refresh-demo/.gitignore create mode 100644 examples/fast-refresh-demo/README.md create mode 100644 examples/fast-refresh-demo/components/Button.js create mode 100644 examples/fast-refresh-demo/components/Button.module.css create mode 100644 examples/fast-refresh-demo/components/ClickCount.js create mode 100644 examples/fast-refresh-demo/global.css create mode 100644 examples/fast-refresh-demo/package.json create mode 100644 examples/fast-refresh-demo/pages/_app.js create mode 100644 examples/fast-refresh-demo/pages/index.js create mode 100644 examples/fast-refresh-demo/styles/home.module.css diff --git a/docs/basic-features/fast-refresh.md b/docs/basic-features/fast-refresh.md index 5df2d35968a98..450b19e6db4b3 100644 --- a/docs/basic-features/fast-refresh.md +++ b/docs/basic-features/fast-refresh.md @@ -6,6 +6,13 @@ description: # Fast Refresh +
+ Examples + +
+ Fast Refresh is a Next.js feature that gives you instantaneous feedback on edits made to your React components. Fast Refresh is enabled by default in all Next.js applications on **9.4 or newer**. With Next.js Fast Refresh enabled, diff --git a/examples/fast-refresh-demo/.gitignore b/examples/fast-refresh-demo/.gitignore new file mode 100644 index 0000000000000..1437c53f70bc2 --- /dev/null +++ b/examples/fast-refresh-demo/.gitignore @@ -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 diff --git a/examples/fast-refresh-demo/README.md b/examples/fast-refresh-demo/README.md new file mode 100644 index 0000000000000..bde2b03db474b --- /dev/null +++ b/examples/fast-refresh-demo/README.md @@ -0,0 +1,23 @@ +# Fast Refresh Demo + +Next.js ships with [Fast Refresh](https://nextjs.org/docs/basic-features/fast-refresh) which gives you instantaneous feedback on edits made to your React components. + +This demos shows how the state of an auto incrementing value and a classic counter is preserved after edits or if there are errors. + +## Deploy your own + +Deploy the example using [Vercel](https://vercel.com): + +[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/import/project?template=https://github.com/vercel/next.js/tree/canary/examples/fast-refresh-demo) + +## 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 fast-refresh-demo fast-refresh-demo +# or +yarn create next-app --example fast-refresh-demo fast-refresh-demo +``` + +Deploy it to the cloud with [Vercel](https://vercel.com/import?filter=next.js&utm_source=github&utm_medium=readme&utm_campaign=next-example) ([Documentation](https://nextjs.org/docs/deployment)). diff --git a/examples/fast-refresh-demo/components/Button.js b/examples/fast-refresh-demo/components/Button.js new file mode 100644 index 0000000000000..fdb433551f560 --- /dev/null +++ b/examples/fast-refresh-demo/components/Button.js @@ -0,0 +1,5 @@ +import styles from './Button.module.css' + +export default function Button(props) { + return +} diff --git a/examples/fast-refresh-demo/global.css b/examples/fast-refresh-demo/global.css new file mode 100644 index 0000000000000..e203447ed2412 --- /dev/null +++ b/examples/fast-refresh-demo/global.css @@ -0,0 +1,14 @@ +body { + font-family: 'SF Pro Text', 'SF Pro Icons', 'Helvetica Neue', 'Helvetica', + 'Arial', sans-serif; + margin: 0 auto; + font-size: 16px; + line-height: 1.65; + word-break: break-word; + font-kerning: auto; + font-variant: normal; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + text-rendering: optimizeLegibility; + hyphens: auto; +} diff --git a/examples/fast-refresh-demo/package.json b/examples/fast-refresh-demo/package.json new file mode 100644 index 0000000000000..50fb57f61e5d6 --- /dev/null +++ b/examples/fast-refresh-demo/package.json @@ -0,0 +1,15 @@ +{ + "name": "fast-refresh-demo", + "version": "1.0.0", + "scripts": { + "dev": "next", + "build": "next build", + "start": "next start" + }, + "dependencies": { + "next": "latest", + "react": "^16.13.1", + "react-dom": "^16.13.1" + }, + "license": "MIT" +} diff --git a/examples/fast-refresh-demo/pages/_app.js b/examples/fast-refresh-demo/pages/_app.js new file mode 100644 index 0000000000000..d305f97bab7f1 --- /dev/null +++ b/examples/fast-refresh-demo/pages/_app.js @@ -0,0 +1,5 @@ +import '../global.css' + +export default function MyApp({ Component, pageProps }) { + return +} diff --git a/examples/fast-refresh-demo/pages/index.js b/examples/fast-refresh-demo/pages/index.js new file mode 100644 index 0000000000000..588c89a8e612c --- /dev/null +++ b/examples/fast-refresh-demo/pages/index.js @@ -0,0 +1,71 @@ +import { useCallback, useEffect, useState } from 'react' +import Button from '../components/Button' +import ClickCount from '../components/ClickCount' +import styles from '../styles/home.module.css' + +function throwError() { + console.log( + // The function body() is not defined + document.body() + ) +} + +function Home() { + const [count, setCount] = useState(0) + const increment = useCallback(() => { + setCount((v) => v + 1) + }, [setCount]) + + useEffect(() => { + const r = setInterval(() => { + increment() + }, 1000) + + return () => { + clearInterval(r) + } + }, [increment]) + + return ( +
+

Fast Refresh Demo

+

+ Fast Refresh is a Next.js feature that gives you instantaneous feedback + on edits made to your React components, without ever losing component + state. +

+
+
+

+ Auto incrementing value. The counter won't reset after edits or if + there are errors. +

+

Current value: {count}

+
+
+
+

Component with state.

+ +
+
+
+

+ The button below will throw 2 errors. You'll see the error overlay to + let you know about the errors but it won't break the page or reset + your state. +

+ +
+
+
+ ) +} + +export default Home diff --git a/examples/fast-refresh-demo/styles/home.module.css b/examples/fast-refresh-demo/styles/home.module.css new file mode 100644 index 0000000000000..693115201a064 --- /dev/null +++ b/examples/fast-refresh-demo/styles/home.module.css @@ -0,0 +1,11 @@ +.main { + padding: 20px 20px 60px; + max-width: 680px; + margin: 0 auto; +} + +.hr { + border: none; + border-bottom: 1px solid #efefef; + margin: 3em auto; +} From aa568a549ebe10ac6748a8c77010a06c3b81ff93 Mon Sep 17 00:00:00 2001 From: WeichienHung Date: Wed, 2 Sep 2020 07:48:56 +0800 Subject: [PATCH 02/92] force persistor persist again after persistStore bootstrap done (#16085) This PR is to fix "[Examples] Problem with query parameters in with-redux-persist (#15484)" The root cause is persist/rehydrate action will issue twice when query parameter is set. But persistStore initial bootstrap is not ready yet. So i add a bootstrap callback and force persistor to persist again to make overall state correct. I also modify the loading prop to a `
loading
` because it's confuse to set Component in loading prop. Attached the GIF ![demo](https://user-images.githubusercontent.com/1462027/89922530-bec04000-dc31-11ea-9831-12cd9d436d96.gif) Closes #15484 --- examples/with-redux-persist/pages/_app.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/examples/with-redux-persist/pages/_app.js b/examples/with-redux-persist/pages/_app.js index a48f78ae7bd35..99bea7d750cf8 100644 --- a/examples/with-redux-persist/pages/_app.js +++ b/examples/with-redux-persist/pages/_app.js @@ -5,11 +5,13 @@ import { PersistGate } from 'redux-persist/integration/react' export default function App({ Component, pageProps }) { const store = useStore(pageProps.initialReduxState) - const persistor = persistStore(store) + const persistor = persistStore(store, {}, function () { + persistor.persist() + }) return ( - } persistor={persistor}> + loading} persistor={persistor}> From 6874adbbb9e65f968f24b5ef309aa258670c7028 Mon Sep 17 00:00:00 2001 From: Alex Castle Date: Tue, 1 Sep 2020 19:42:20 -0700 Subject: [PATCH 03/92] Make the image post-processor ignore SVG images (#16732) This is a small change to the image post-processor logic. When it's looking for images to preload, it will now ignore SVGs, as these are rarely the relevant images for LCP. --- packages/next/next-server/lib/post-process.ts | 9 ++++++++- test/integration/image-optimization/pages/index.js | 1 + test/integration/image-optimization/pages/stars.js | 1 + test/integration/image-optimization/test/index.test.js | 6 ++++++ 4 files changed, 16 insertions(+), 1 deletion(-) diff --git a/packages/next/next-server/lib/post-process.ts b/packages/next/next-server/lib/post-process.ts index d922a56bd174c..fcbefe997a2d7 100644 --- a/packages/next/next-server/lib/post-process.ts +++ b/packages/next/next-server/lib/post-process.ts @@ -191,8 +191,10 @@ class ImageOptimizerMiddleware implements PostProcessMiddleware { } function isImgEligible(imgElement: HTMLElement): boolean { + let imgSrc = imgElement.getAttribute('src') return ( - imgElement.hasAttribute('src') && + !!imgSrc && + sourceIsSupportedType(imgSrc) && imageIsNotTooSmall(imgElement) && imageIsNotHidden(imgElement) ) @@ -243,6 +245,11 @@ function imageIsNotHidden(imgElement: HTMLElement): boolean { return true } +// Currently only filters out svg images--could be made more specific in the future. +function sourceIsSupportedType(imgSrc: string): boolean { + return !imgSrc.includes('.svg') +} + // Initialization registerPostProcessor( 'Inline-Fonts', diff --git a/test/integration/image-optimization/pages/index.js b/test/integration/image-optimization/pages/index.js index 09dec13363566..abbbb7dbbad2b 100644 --- a/test/integration/image-optimization/pages/index.js +++ b/test/integration/image-optimization/pages/index.js @@ -6,6 +6,7 @@ const Page = () => { +