Skip to content

Instantly share code, notes, and snippets.

@Kcin1993
Last active April 15, 2019 06:30
Show Gist options
  • Save Kcin1993/5578562ccd7c170aa13000fd4db4462f to your computer and use it in GitHub Desktop.
Save Kcin1993/5578562ccd7c170aa13000fd4db4462f to your computer and use it in GitHub Desktop.
Next js 學習筆記

官方教程摘要

Handling Errors

By default, Next.js will track errors like these and show it in the browser. This helps you identify errors and fix them quickly.

Once you fix the issue, the page will appear instantly without a full page reload. We do this with the help of Webpack's hot module replacement facility, which is supported in Next.js by default.

Link is Just a Higher Order Component (HOC)

Actually, the title prop on next/link component has no effect. That's because next/link is just a higher order component which only accepts the "href" and some similar props. If you need to add props to it, you need to do it to the underlying component. Do not expect the next/link component to pass those props to it's children.

In this case, the child of the next/link component is the anchor tag.

// OK
<Link href="/about">
  <a title="About Page">About Page</a>
</Link>

//Not ok
<Link href="/about" title="About Page">
  <a>About Page</a>
</Link>

Link Works With Anything

Just like a button, you can place any of your custom React components or even a div within a Link.

The only requirement for components placed inside a Link is they should accept an onClick prop.

<Link href="/about" >
  <button>Go to About Page</button>
</Link>

The Component Directory

We don't need to put our components in a special directory; the directory can be named anything. The only special directory is the pages directory.

You can even create the Component inside the pages directory.

Here we didn't do that because we don't need a direct URL to our Header component.

import Link from 'next/link'
import Header from './comp/Header';

export default function Index() {
  return (
    <div>
      <Header /> 
      <p>Hello Next.js</p>
    </div>
  )
}

Using Components

We've mentioned two use cases for shared components:

  1. As common header components.
  2. As Layouts.

You can use components for styling, page layouts, and any other tasks you like. Additionally, you can import components from NPM modules and use them.

Method 1 - Layout as a Higher Order Component

// components/MyLayout.js
import Header from './Header'

const layoutStyle = {
  margin: 20,
  padding: 20,
  border: '1px solid #DDD'
}

const withLayout = Page => {
  return () => (
    <div style={layoutStyle}>
      <Header />
      <Page />
    </div>
  )
}

export default withLayout

// pages/index.js
import withLayout from '../components/MyLayout'

const Page = () => <p>Hello Next.js</p>

export default withLayout(Page)

Method 2 - Page content as a prop

// components/MyLayout.js
import Header from './Header'

const layoutStyle = {
  margin: 20,
  padding: 20,
  border: '1px solid #DDD'
}

const Layout = props => (
  <div style={layoutStyle}>
    <Header />
    {props.content}
  </div>
)

export default Layout

// pages/index.js

import Layout from '../components/MyLayout.js'

const indexPageContent = <p>Hello Next.js</p>

export default function Index() {
  return <Layout content={indexPageContent} />
}

Passing Data via Query Strings

// Link
const PostLink = props => (
  <li>
    <Link href={`/post?title=${props.title}`}>
      <a>{props.title}</a>
    </Link>
  </li>
)

// Post
import { withRouter } from 'next/router'
import Layout from '../components/MyLayout.js'

const Content = withRouter(props => (
  <div>
    <h1>{props.router.query.title}</h1>
    <p>This is the blog post content.</p>
  </div>
))

const Page = props => (
  <Layout>
    <Content />
  </Layout>
)

export default Page

Clean URLs with Route Masking

As you have witnessed, route masking works pretty nicely with the browser history. All you have to do is just add the “as” prop for the link.

const PostLink = props => (
  <li>
    <Link as={`/p/${props.id}`} href={`/post?title=${props.title}`}>
      <a>{props.title}</a>
    </Link>
  </li>
)

Server Side Support for Clean URLs

需要建立新的 server 端。並對處理原始路徑(href)的參數,並回應實際路徑

Ref here

server.get('/p/:id', (req, res) => {
  const actualPage = '/post'
  const queryParams = { title: req.params.id }
  app.render(req, res, actualPage, queryParams)
})

getInitialProps

That's a static async function you can add into any page in your app. Using that, we can fetch data and send them as props to our page. Ref

Index.getInitialProps = async function() {
  const res = await fetch('https://api.tvmaze.com/search/shows?q=batman')
  const data = await res.json()

  console.log(`Show data fetched. Count: ${data.length}`)

  return {
    shows: data
  }
}

Where the console?

Ref here

Console 顯示於 server

  • 第一次進入網站
  • 直接貼上網址

Console 顯示於 browser

  • 若是透過 Link 的方式來渲染,就會出現在瀏覽器中

Context of getInitialProps

Ref

//server
server.get('/p/:id', (req, res) => {
  const actualPage = '/post'
  const queryParams = { id: req.params.id }
  app.render(req, res, actualPage, queryParams)
})

//client
Post.getInitialProps = async function(context) {
  const { id } = context.query
  const res = await fetch(`https://api.tvmaze.com/shows/${id}`)
  const show = await res.json()

  console.log(`Fetched show: ${show.name}`)

  return { show }
}

Styles should go inside template strings

strings

Styled jsx works as a babel plugin. It will parse all of the CSS and apply it in the build process. (With that our styles get applied without any overhead time)

It also supports having constraints inside styled-jsx. In the future, you will be able to use any dynamic variable inside styled-jsx. That is why CSS needs to go inside of a template string. ({``})

<style jsx>{`
  h1,
  a {
    font-family: 'Arial';
  }

  ul {
    padding: 0;
  }

  li {
    list-style: none;
    margin: 5px 0;
  }

  a {
    text-decoration: none;
    color: blue;
  }

  a:hover {
    opacity: 0.6;
  }
`}</style>

Global Styles

樣式無法套用元件底下的子原件。加上 global 屬性即可套用到下方的子原件

import Layout from '../components/MyLayout.js'
import { withRouter } from 'next/router'
import Markdown from 'react-markdown'

export default withRouter(props => (
  <Layout>
    <h1>{props.router.query.title}</h1>
    <div className="markdown">
      <Markdown
        source={`
This is our blog post.
Yes. We can have a [link](/link).
And we can have a title as well.

### This is a title

And here's the content.
     `}
      />
    </div>
    <style jsx global>{`
      .markdown {
        font-family: 'Arial';
      }

      .markdown a {
        text-decoration: none;
        color: blue;
      }

      .markdown a:hover {
        opacity: 0.1;
      }

      .markdown h3 {
        margin: 0;
        padding: 0;
        text-transform: uppercase;
      }
    `}</style>
  </Layout>
))

Build Once, Run Many Instances

Ref here

//update script
"scripts": {
  "start": "next start -p $PORT"
}

//cli
PORT=8000 npm start
PORT=9000 npm start

Now Port

ZEIT Now will always use 443

Even if you start your app on port 8000, once deployed to now, you can access it with port 443 (the default port for "https" websites).

That's a feature of ▲ZEIT Now. You only need to start your app on any port you like elsewhere. ▲ZEIT Now will map it to port 443 always.

部署

Typescript

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment