diff --git a/dynamic-routes-step-1/.gitignore b/dynamic-routes-step-1/.gitignore new file mode 100644 index 0000000..922d92a --- /dev/null +++ b/dynamic-routes-step-1/.gitignore @@ -0,0 +1,25 @@ +# 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 +.env* + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* diff --git a/dynamic-routes-step-1/README.md b/dynamic-routes-step-1/README.md new file mode 100644 index 0000000..02695bc --- /dev/null +++ b/dynamic-routes-step-1/README.md @@ -0,0 +1 @@ +This is a starter template for [Learn Next.js](https://nextjs.org/learn). \ No newline at end of file diff --git a/dynamic-routes-step-1/components/layout.js b/dynamic-routes-step-1/components/layout.js new file mode 100644 index 0000000..cff0cca --- /dev/null +++ b/dynamic-routes-step-1/components/layout.js @@ -0,0 +1,64 @@ +import Head from 'next/head' +import styles from './layout.module.css' +import utilStyles from '../styles/utils.module.css' +import Link from 'next/link' + +const name = '[Your Name]' +export const siteTitle = 'Next.js Sample Website' + +export default function Layout({ children, home }) { + return ( +
+ + + + + +
+ {home ? ( + <> + {name} +

{name}

+ + ) : ( + <> + + + {name} + + +

+ + {name} + +

+ + )} +
+
{children}
+ {!home && ( +
+ + ← Back to home + +
+ )} +
+ ) +} diff --git a/dynamic-routes-step-1/components/layout.module.css b/dynamic-routes-step-1/components/layout.module.css new file mode 100644 index 0000000..d0e3a8f --- /dev/null +++ b/dynamic-routes-step-1/components/layout.module.css @@ -0,0 +1,25 @@ +.container { + max-width: 36rem; + padding: 0 1rem; + margin: 3rem auto 6rem; +} + +.header { + display: flex; + flex-direction: column; + align-items: center; +} + +.headerImage { + width: 6rem; + height: 6rem; +} + +.headerHomeImage { + width: 8rem; + height: 8rem; +} + +.backToHome { + margin: 3rem 0 0; +} diff --git a/dynamic-routes-step-1/lib/posts.js b/dynamic-routes-step-1/lib/posts.js new file mode 100644 index 0000000..edba4e0 --- /dev/null +++ b/dynamic-routes-step-1/lib/posts.js @@ -0,0 +1,60 @@ +import fs from 'fs' +import path from 'path' +import matter from 'gray-matter' + +const postsDirectory = path.join(process.cwd(), 'posts') + +export function getSortedPostsData() { + // Get file names under /posts + const fileNames = fs.readdirSync(postsDirectory) + const allPostsData = fileNames.map(fileName => { + // Remove ".md" from file name to get id + const id = fileName.replace(/\.md$/, '') + + // Read markdown file as string + const fullPath = path.join(postsDirectory, fileName) + const fileContents = fs.readFileSync(fullPath, 'utf8') + + // Use gray-matter to parse the post metadata section + const matterResult = matter(fileContents) + + // Combine the data with the id + return { + id, + ...matterResult.data + } + }) + // Sort posts by date + return allPostsData.sort((a, b) => { + if (a.date < b.date) { + return 1 + } else { + return -1 + } + }) +} + +export function getAllPostIds() { + const fileNames = fs.readdirSync(postsDirectory) + return fileNames.map(fileName => { + return { + params: { + id: fileName.replace(/\.md$/, '') + } + } + }) +} + +export function getPostData(id) { + const fullPath = path.join(postsDirectory, `${id}.md`) + const fileContents = fs.readFileSync(fullPath, 'utf8') + + // Use gray-matter to parse the post metadata section + const matterResult = matter(fileContents) + + // Combine the data with the id + return { + id, + ...matterResult.data + } +} diff --git a/dynamic-routes-step-1/package.json b/dynamic-routes-step-1/package.json new file mode 100644 index 0000000..833fb24 --- /dev/null +++ b/dynamic-routes-step-1/package.json @@ -0,0 +1,16 @@ +{ + "name": "my-app", + "version": "0.1.0", + "private": true, + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start" + }, + "dependencies": { + "gray-matter": "^4.0.2", + "next": "9.3.5", + "react": "16.13.1", + "react-dom": "16.13.1" + } +} diff --git a/dynamic-routes-step-1/pages/_app.js b/dynamic-routes-step-1/pages/_app.js new file mode 100644 index 0000000..7e66efc --- /dev/null +++ b/dynamic-routes-step-1/pages/_app.js @@ -0,0 +1,5 @@ +import '../styles/global.css' + +export default function App({ Component, pageProps }) { + return +} diff --git a/dynamic-routes-step-1/pages/index.js b/dynamic-routes-step-1/pages/index.js new file mode 100644 index 0000000..24ba2c6 --- /dev/null +++ b/dynamic-routes-step-1/pages/index.js @@ -0,0 +1,40 @@ +import Head from 'next/head' +import Layout, { siteTitle } from '../components/layout' +import utilStyles from '../styles/utils.module.css' +import { getSortedPostsData } from '../lib/posts' + +export default function Home({ allPostsData }) { + return ( + + + {siteTitle} + +
+

[Your Self Introduction]

+
+
+

Blog

+
    + {allPostsData.map(({ id, date, title }) => ( +
  • + {title} +
    + {id} +
    + {date} +
  • + ))} +
+
+
+ ) +} + +export async function getStaticProps() { + const allPostsData = getSortedPostsData() + return { + props: { + allPostsData + } + } +} diff --git a/dynamic-routes-step-1/pages/posts/[id].js b/dynamic-routes-step-1/pages/posts/[id].js new file mode 100644 index 0000000..89b7f6f --- /dev/null +++ b/dynamic-routes-step-1/pages/posts/[id].js @@ -0,0 +1,31 @@ +import Layout from '../../components/layout' +import { getAllPostIds, getPostData } from '../../lib/posts' + +export default function Post({ postData }) { + return ( + + {postData.title} +
+ {postData.id} +
+ {postData.date} +
+ ) +} + +export async function getStaticPaths() { + const paths = getAllPostIds() + return { + paths, + fallback: false + } +} + +export async function getStaticProps({ params }) { + const postData = getPostData(params.id) + return { + props: { + postData + } + } +} diff --git a/dynamic-routes-step-1/posts/pre-rendering.md b/dynamic-routes-step-1/posts/pre-rendering.md new file mode 100644 index 0000000..de33130 --- /dev/null +++ b/dynamic-routes-step-1/posts/pre-rendering.md @@ -0,0 +1,11 @@ +--- +title: "Two Forms of Pre-rendering" +date: "2020-01-01" +--- + +Next.js has two forms of pre-rendering: **Static Generation** and **Server-side Rendering**. The difference is in **when** it generates the HTML for a page. + +- **Static Generation** is the pre-rendering method that generates the HTML at **build time**. The pre-rendered HTML is then _reused_ on each request. +- **Server-side Rendering** is the pre-rendering method that generates the HTML on **each request**. + +Importantly, Next.js lets you **choose** which pre-rendering form to use for each page. You can create a "hybrid" Next.js app by using Static Generation for most pages and using Server-side Rendering for others. \ No newline at end of file diff --git a/dynamic-routes-step-1/posts/ssg-ssr.md b/dynamic-routes-step-1/posts/ssg-ssr.md new file mode 100644 index 0000000..fe4a156 --- /dev/null +++ b/dynamic-routes-step-1/posts/ssg-ssr.md @@ -0,0 +1,19 @@ +--- +title: "When to Use Static Generation v.s. Server-side Rendering" +date: "2020-01-02" +--- + +We recommend using **Static Generation** (with and without data) whenever possible because your page can be built once and served by CDN, which makes it much faster than having a server render the page on every request. + +You can use Static Generation for many types of pages, including: + +- Marketing pages +- Blog posts +- E-commerce product listings +- Help and documentation + +You should ask yourself: "Can I pre-render this page **ahead** of a user's request?" If the answer is yes, then you should choose Static Generation. + +On the other hand, Static Generation is **not** a good idea if you cannot pre-render a page ahead of a user's request. Maybe your page shows frequently updated data, and the page content changes on every request. + +In that case, you can use **Server-Side Rendering**. It will be slower, but the pre-rendered page will always be up-to-date. Or you can skip pre-rendering and use client-side JavaScript to populate data. \ No newline at end of file diff --git a/dynamic-routes-step-1/public/favicon.ico b/dynamic-routes-step-1/public/favicon.ico new file mode 100644 index 0000000..4965832 Binary files /dev/null and b/dynamic-routes-step-1/public/favicon.ico differ diff --git a/dynamic-routes-step-1/public/images/profile.jpg b/dynamic-routes-step-1/public/images/profile.jpg new file mode 100644 index 0000000..798aba3 Binary files /dev/null and b/dynamic-routes-step-1/public/images/profile.jpg differ diff --git a/dynamic-routes-step-1/styles/global.css b/dynamic-routes-step-1/styles/global.css new file mode 100644 index 0000000..9e1b0fb --- /dev/null +++ b/dynamic-routes-step-1/styles/global.css @@ -0,0 +1,27 @@ +html, +body { + padding: 0; + margin: 0; + font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, + Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif; + line-height: 1.6; + font-size: 18px; +} + +* { + box-sizing: border-box; +} + +a { + color: #0070f3; + text-decoration: none; +} + +a:hover { + text-decoration: underline; +} + +img { + max-width: 100%; + display: block; +} diff --git a/dynamic-routes-step-1/styles/utils.module.css b/dynamic-routes-step-1/styles/utils.module.css new file mode 100644 index 0000000..37f545e --- /dev/null +++ b/dynamic-routes-step-1/styles/utils.module.css @@ -0,0 +1,52 @@ +.heading2Xl { + font-size: 2.5rem; + line-height: 1.2; + font-weight: 800; + letter-spacing: -0.05rem; + margin: 1rem 0; +} + +.headingXl { + font-size: 2rem; + line-height: 1.3; + font-weight: 800; + letter-spacing: -0.05rem; + margin: 1rem 0; +} + +.headingLg { + font-size: 1.5rem; + line-height: 1.4; + margin: 1rem 0; +} + +.headingMd { + font-size: 1.2rem; + line-height: 1.5; +} + +.borderCircle { + border-radius: 9999px; +} + +.colorInherit { + color: inherit; +} + +.padding1px { + padding-top: 1px; +} + +.list { + list-style: none; + padding: 0; + margin: 0; +} + +.listItem { + margin: 0 0 1.25rem; +} + +.lightText { + color: #666; +}