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.
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>
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>
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>
)
}
We've mentioned two use cases for shared components:
- As common header components.
- 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} />
}
// 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
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 端。並對處理原始路徑(href)的參數,並回應實際路徑
server.get('/p/:id', (req, res) => {
const actualPage = '/post'
const queryParams = { title: req.params.id }
app.render(req, res, actualPage, queryParams)
})
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
}
}
Console 顯示於 server
- 第一次進入網站
- 直接貼上網址
Console 顯示於 browser
- 若是透過 Link 的方式來渲染,就會出現在瀏覽器中
//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 }
}
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 屬性即可套用到下方的子原件
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>
))
//update script
"scripts": {
"start": "next start -p $PORT"
}
//cli
PORT=8000 npm start
PORT=9000 npm start
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.