SSR(server-side-render),根據名稱咱們都知道叫作服務端渲染,我相信對於工做幾年的前端開發者確定很熟悉這個名詞,多少也都用過,在這裏,我想說的是針對我們剛剛踏入前端圈的朋友們的一點總結和感悟,但願咱們可以不斷的提升對next的認知,相互進步,相互成長!這就是咱們的目標~
其中掘金就使用vue的ssr功能作了全棧服務端渲染,切效果良好。前端
那咱們用next.js的優勢是什麼呢?
那咱們開始上手吧,hello world!
照作如下步驟吧~複製代碼
接着打開package.json添加以下代碼,以下:vue
{
"scripts": {
"dev": "next"
}}複製代碼
到這裏位置項目的準備工做已完成,運行如下命令開啓項目服務器node
npm run dev複製代碼
那接下來就是建立頁面了~~~啦啦啦~~~我學next我開心~~~
next.js是從服務器生成頁面,再返回給前端展現。
重點內容開始了哦~~~~複製代碼
咱們在pages/index.js中建立一個React函數式組件:react
const Index = () => (
<div>
<p>小英 study next.js</p>
</div>
)
export default Index複製代碼
next.js默認使用webpack構建項目,webpack的熱部署功能同樣能提高開發效率。建立完pages/index.js後,再訪問http://localhost:3000便可看到設置好的頁面。webpack
很好,不在是咱們不喜歡的404了,看到內容了啊~~ios
再來構建下多頁面的吧~
在pages目錄下建立文件pages/content.jsweb
export default () => (
<div>
<p>這是content 頁面</p>
</div>
)複製代碼
打開路由localhost:3000/content,看到咱們新建的第二個頁面了。ajax
因此如今咱們明白全部的路由都是經過後端服務器來控制的 ,要想實現客戶端路由,須要藉助next.js的 Link API,因此接下來咱們看下Link API吧~~
從next/link中能夠引用到Link組件。在pages/index.js文件中引用Link,修改以下:express
import Link from "next/link"
const Index = () => (
<div>
<Link href="/content">
<a>去往content頁面</a>
</Link>
<p>小英 study next.js</p>
</div>)
export default Index複製代碼
咱們使用的Link組件,其實能夠當作a標籤使用,最終渲染的時候也是a標籤,頁面渲染以下:npm
點擊超連接,便可進入到content頁面。下圖是實際渲染的結果:
結論:link組件是經過location.history的瀏覽器API保存歷史路由,因此,能夠經過瀏覽器左上角的前進後退按鈕來切換歷史路由。而在開發過程當中,咱們不須要單獨寫客戶端路由的配置。
next.js是以多頁面爲中心,只要將頁面文件放在pages目錄下,就能夠經過瀏覽器上以文件名爲路由名來訪問到,相反,只要不想讓用戶經過頁面直接訪問的組件,都不放在pages目錄下,開發者想放在哪一個目錄自定義便可。
接下來,咱們建一個components目錄,裏面建一個公共的Header組件,用於頭部導航,經過導航能夠再頁面間切換。
import Link from 'next/link'
const linkStyle = { marginRight: 15}
const Header = () => (
<div>
<Link href="/">
<a style={linkStyle}>首頁</a>
</Link>
<Link href="/content">
<a style={linkStyle}>內容</a>
</Link>
</div>)
export default Header複製代碼
好了,定義完組件,咱們在pages/index.js裏面引用看一下:
import Header from '../components/Header.js'
const Index = () => (
<div>
<Header />
<p>小英 study next.js</p>
</div>)
export default Index複製代碼
在 pages/content.js 中一樣引入 Header 組件,在瀏覽器上經過點擊導航切換頁面。
import Header from '../components/Header.js'
export default () => (
<div>
<Header/>
<p>這是content 頁面</p>
</div>)複製代碼
這樣就能夠再index和content兩個頁面來回切換了
進一步咱們在封裝下Header組件,Layout組件,包含咱們的Header和頁面content的組件;
/**Layout**組件/
import Header from './Header'
const layoutStyle = { margin: 0, padding: 0, border: '1px solid #DDD'}
const Layout = (props) => (
<div style={layoutStyle}>
<Header/>
{props.children}
</div>)
export default Layout複製代碼
/**在index中使用Layout組件**/
import Layout from '../components/Layout.js'
import Link from 'next/link'
const linkArr = [
'我是連接1',
'我是連接2',
'我是連接3',
'我是連接4',
'我是連接5',
'我是連接6',]
const LinkContent = (props) => (
<li>
<Link href={`tolink?title=${props.title}`}>
<a>{props.title}</a>
</Link>
</li>)
const Index = () => (
<Layout>
<h1>這是個人地盤</h1>
<ul>
{
linkArr.map(item => {
return <LinkContent title={item} />
})
}
</ul>
</Layout>)
export default Index複製代碼
頁面渲染以下:(如下至關因而列表頁,點擊列表中的連接至關於到詳情頁,如下就造成了動態頁面之間的跳轉)
/**詳情頁**/
import Layout from '../components/Layout.js'export default (props) => ( <Layout> <h1>{props.url.query.title}</h1> <p>這是不一樣title對應的詳情頁</p> </Layout>)複製代碼
總結:使用next.js建立動態頁面,與使用React和Vue建立一個spa的頁面大致相同, 區別就是頁面的渲染主題不一樣。
next.js提供一個獨特的特性:路由遮蓋(Route Masking)。它可使得在瀏覽器上顯示的是路由A,而在app內部實際顯示的是路由B。這個特性可使咱們的路由簡潔,以上邊爲例,地址欄顯示的是 http://localhost:3000/tolink?title=%E6%88%91%E6%98%AF%E9%93%BE%E6%8E%A56,這個地址含有個title參數,看着很不整潔嗎,接下來咱們就要next改造路由,目標是改爲 http://localhost:3000/p/0
import Layout from '../components/Layout.js'
import Link from 'next/link'
const linkArr = [ '我是連接1', '我是連接2', '我是連接3', '我是連接4', '我是連接5', '我是連接6',]
const LinkContent = (props) => (
<li>
<Link
as={`p/${props.id}`}
href={`tolink?title=${props.title}`}>
<a>{props.title}</a>
</Link>
</li>)
const Index = () => (
<Layout>
<h1>這是個人地盤</h1>
<ul>
{
linkArr.map((item,index) => {
return <LinkContent id={index} title={item} />
})
}
</ul>
</Layout>)
export default Index複製代碼
結論:
- 當在 Link 組件上使用 as 屬性時,瀏覽器上顯示的是 as 屬性的值,走的是客戶端路由,而服務器真正映射的是 href 屬性的值,走的是服務端路由。
- 顯示 404頁面,是由於路由遮蓋默認只在客戶端路由中有效,要想在服務端也支持路由遮蓋,須要在服務端單獨設置路由解析的方法。
下面以express爲例建立後端服務器講解如何設置服務器來支持路由遮蓋。
先來安裝一個express
npm install --save express複製代碼
在項目目錄下建立server.js文件
const express = require('express')
const next = require('next')
const dev = process.env.NODE_ENV !== 'production'
const app = next({ dev })
const handle = app.getRequestHandler()
app.prepare().then(() => {
const server = express()
server.get('/p/:id', (req, res) => {
const actualPage = '/tolink'
const queryParam = { title: req.param.id }
app.render(req, res, actualPage, queryParam)
})
server.get('*', (req, res) => {
return handle(req, res)
})
server.listen(3000, (err) => {
if(err) throw err
console.log('> ready on http://localhost:3000')
})}).catch(ex => {
console.log(ex.stack)
process.exit(1)
})
複製代碼
/**index.js**/
import Layout from '../components/Layout.js'
import { withRouter } from "next/router"
class ToLink extends React.Component {
// 沒有該函數是不能實現服務端路由遮蓋的
static getInitialProps ({ query: { title } }) {
return { title }
}
render() {
let { title } = this.props
return (
<Layout>
<h1>{title}</h1>
<p>這是不一樣title對應的詳情頁</p>
</Layout>
)
}
}
export default withRouter(ToLink)複製代碼
這時在tolink頁面刷新的時候就可以正常顯示頁面了,這裏強調一點,我在參考一些文章的時候,沒有提到加 getInitialProps這個方法,就說頁面刷新就能正常顯示是不正確的,本人屢次試驗,頁面自己的內容是能夠正常顯示的,可是根據路由獲取的title參數是一直拿不到的,加了這個參數就會先去獲取參數,這樣頁面才能完整顯示~~~如下提供截圖證實:
因此,接下來咱們就說說這個靜態的方法。
next.js在react的基礎上爲組件新增了一個特性:getInitialProps, 同於獲取並處理組件的屬性,返回組件的默認屬性。咱們能夠在該方法中請求數據,獲取頁面須要的數據並渲染返回給前端頁面。( 上邊的服務端路由遮蓋也是用了這個方法,原理就是獲取組件的默認屬性)
引入一個支持在客戶端和服務端發送fetch請求的插件isomorphic-unfrtch, 固然咱們也能夠用axios等其餘工具。
npm install --save isomorphic-unfetch複製代碼
而後修改pages/index.js:
import Layout from '../components/Layout.js'
import { withRouter } from "next/router"
import fetch from 'isomorphic-unfetch'
import Link from 'next/link'
const LinkContent = (props) => {
console.log(props)
return (
<li>
<Link
as={`/p/${props.id}`}
href={{pathname: '/tolink', query: {title: props.title}}}>
<a>{props.title}</a>
</Link>
</li>
)
}
class Index extends React.Component {
static async getInitialProps(){
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
}
}
render() {
const { shows } = this.props
return(
<Layout>
<h1>這是個人地盤</h1>
<ul>
{
shows.map(( { show } ) => {
return <LinkContent key={show.id} id={show.id} title={show.name} />
})
}
</ul>
</Layout>)
}
}
export default withRouter(Index)複製代碼
在getInitialProps中咱們使用async await關鍵字異步獲取咱們須要的參數shows,這樣數據就是動態獲取的,從而實現動態渲染。注意:getInitialProps不能使用在子組件中。只能使用在pages頁面中。
結論以下:
servert.js, 這是我在server.js中添加的一個測試id,只有在服務端渲染的時候,咱們的tolink頁面纔會獲取的這個id
pages/tolink.js,咱們在該頁面的 getInitialProps方法中打印一下id
這個結果是我在刷新tolink頁面,或者初始化加載的時候,在終端(服務端)打印的結果,有「測試id」這幾個字;
繼續往下看:
這是我經過首頁的列表Link的方式點擊進來這個頁面,在瀏覽器控制檯打印的結果,很明顯這是客戶端走的這個方法,是沒有」測試id「四個字的,而是undefined,因此這也就驗證告終論3的正確性~~~~,(其實我剛開始也是不懂這個意思,如今明白了,但願正在看的你也能明白哦~~)
Next.js 項目的部署,須要一個 Node.js的服務器,能夠選擇 Express, Koa 或其餘 Nodejs 的Web服務器。本文中以 Express 爲例來部署 Next 項目。
爲了區分部署環境,咱們須要在 package.json 中修改 script 屬性以下:
"scripts": {
"build": "next build",
"start": "NODE_ENV=production node server.js -",
"dev": "NODE_ENV=dev node server.js"
}複製代碼
其中,build 命令是用於打包項目,start 命令是用於生產環境部署,dev 命令是用於本地開發。
執行以下命令便可將 Next項目 部署到服務器:
npm run build
npm run start
複製代碼
執行完命令後,可在 http://host:3000 訪問。其中,host 是指服務器的IP地址。
總結:next.js部署生成環境,必須用build命令打包構建,而後再用start命令部署。
next.js還有不少內容須要咱們不斷總結和採坑,未完待續。。。。。。😃😄