亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

詳解使用Next.js構(gòu)建服務端渲染應用

 更新時間:2018年07月10日 09:45:51   作者:MudOnTire  
這篇文章主要介紹了詳解使用Next.js構(gòu)建服務端渲染應用,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧

next.js簡介

最近在學React.js,React官方推薦使用next.js框架作為構(gòu)建服務端渲染的網(wǎng)站,所以今天來研究一下next.js的使用。

next.js作為一款輕量級的應用框架,主要用于構(gòu)建靜態(tài)網(wǎng)站和后端渲染網(wǎng)站。

框架特點

  1. 使用后端渲染
  2. 自動進行代碼分割(code splitting),以獲得更快的網(wǎng)頁加載速度
  3. 簡潔的前端路由實現(xiàn)
  4. 使用webpack進行構(gòu)建,支持模塊熱更新(Hot Module Replacement)
  5. 可與主流Node服務器進行對接(如express)
  6. 可自定義babel和webpack的配置

使用方法

創(chuàng)建項目并初始化

mkdir server-rendered-website
cd server-rendered-website
npm init -y

安裝next.js

使用npm或者yarn安裝,因為是創(chuàng)建React應用,所以同時安裝react和react-dom

npm:

npm install --save react react-dom next

yarn:

yarn add react react-dom next

在項目根目錄下添加文件夾pages(一定要命名為pages,這是next的強制約定,不然會導致找不到頁面),然后在package.json文件里面添加script用于啟動項目:

"scripts": {
 "dev": "next"
}

如下圖

創(chuàng)建視圖

在pages文件夾下創(chuàng)建index.js文件,文件內(nèi)容:

const Index = () => (
 <div>
 <p>Hello next.js</p>
 </div>
)

export default Index

運行

npm run next

在瀏覽器中打開http://localhost:3000/,網(wǎng)頁顯示如下:

這樣就完成了一個最簡單的next網(wǎng)站。

前端路由

next.js前端路由的使用方式非常簡單,我們先增加一個page,叫about,內(nèi)容如下:

const About = () => (
 <div>
  <p>This is About page</p>
 </div>
)

export default About;

當我們在瀏覽器中請求https://localhost:3000/about時,可以看到頁面展示對應內(nèi)容。(==這里需要注意:請求url的path必須和page的文件名大小寫一致才能訪問,如果訪問localhost:3000/About的話是找不到about頁面的。==)

我們可以使用傳統(tǒng)的a標簽在頁面之間進行跳轉(zhuǎn),但每跳轉(zhuǎn)一次,都需要去服務端請求一次。為了增加頁面的訪問速度,推薦使用next.js的前端路由機制進行跳轉(zhuǎn)。

next.js使用next/link實現(xiàn)頁面之間的跳轉(zhuǎn),用法如下:

import Link from 'next/link'

const Index = () => (
 <div>
 <Link href="/about" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" >
  <a>About Page</a>
 </Link>
 <p>Hello next.js</p>
 </div>
)

export default Index

這樣點擊index頁面的AboutPage鏈接就能跳轉(zhuǎn)到about頁面,而點擊瀏覽器的返回按鈕也是通過前端路由進行跳轉(zhuǎn)的。 官方文檔說用前端路由跳轉(zhuǎn)是不會有網(wǎng)絡請求的,實際會有一個對about.js文件的請求,而這個請求來自于頁面內(nèi)動態(tài)插入的script標簽。但是about.js只會請求一次,之后再訪問是不會請求的,畢竟相同的script標簽是不會重復插入的。 但是想比于后端路由還是大大節(jié)省了請求次數(shù)和網(wǎng)絡流量。前端路由和后端路由的請求對比如下:

前端路由:

后端路由:

Link標簽支持任意react組件作為其子元素,不一定要用a標簽,只要該子元素能響應onClick事件,就像下面這樣:

<Link href="/about" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" >
 <div>Go about page</div>
</Link>

Link標簽不支持添加style和className等屬性,如果要給鏈接增加樣式,需要在子元素上添加:

<Link href="/about" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" >
 <a className="about-link" style={{color:'#ff0000'}}>Go about page</a>
</Link>

Layout

所謂的layout就是就是給不同的頁面添加相同的header,footer,navbar等通用的部分,同時又不需要寫重復的代碼。在next.js中可以通過共享某些組件實現(xiàn)layout。

我們先增加一個公共的header組件,放在根目錄的components文件夾下面(頁面級的組件放pages中,公共組件放components中):

import Link from 'next/link';

const linkStyle = {
 marginRight: 15
}

const Header = () => (
 <div>
  <Link href="/" rel="external nofollow" rel="external nofollow" >
   <a style={linkStyle}>Home</a>
  </Link>
  <Link href="/about" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" >
   <a style={linkStyle}>About</a>
  </Link>
 </div>
)

export default Header;

然后在index和about頁面中引入header組件,這樣就實現(xiàn)了公共的layout的header:

import Header from '../components/Header';

const Index = () => (
 <div>
  <Header />
  <p>Hello next.js</p>
 </div>
)

export default Index;

如果要增加footer也可以按照header的方法實現(xiàn)。

除了引入多個header、footer組件,我們可以實現(xiàn)一個整體的Layout組件,避免引入多個組件的麻煩,同樣在components中添加一個Layout.js文件,內(nèi)容如下:

import Header from './Header';

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

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

export default Layout

這樣我們只需要在頁面中引入Layout組件就可以達到布局的目的:

import Layout from '../components/Layout';

const Index = () => (
 <Layout>
  <p>Hello next.js</p>
 </Layout>
)

export default Index;

頁面間傳值

通過url參數(shù)(query string)

next中的頁面間傳值方式和傳統(tǒng)網(wǎng)頁一樣也可以用url參數(shù)實現(xiàn),我們來做一個簡單的博客應用:

首先將index.js的內(nèi)容替換成如下來展示博客列表:

import Link from 'next/link';
import Layout from '../components/Layout';

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

export default () => (
 <Layout>
  <h1>My Blog</h1>
  <ul>
   <PostLink title="Hello next.js" />
   <PostLink title="next.js is awesome" />
   <PostLink title="Deploy apps with Zeit" />
  </ul>
 </Layout>
);

通過在Link的href中添加title參數(shù)就可以實現(xiàn)傳值。

現(xiàn)在我們再添加博客的詳情頁post.js

import { withRouter } from 'next/router';
import Layout from '../components/Layout';

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

export default Post;

上面代碼通過withRouter將next的router作為一個prop注入到component中,實現(xiàn)對url參數(shù)的訪問。

運行后顯示如圖:

列表頁

點擊進入詳情頁:

使用query string可以實現(xiàn)頁面間的傳值,但是會導致頁面的url不太簡潔美觀,尤其當要傳輸?shù)闹刀嗔酥蟆K詎ext.js提供了Route Masking這個特性用于路由的美化。

路由偽裝(Route Masking)

這項特性的官方名字叫Route Masking,沒有找到官方的中文名,所以就根據(jù)字面意思暫且翻譯成路由偽裝。所謂的路由偽裝即讓瀏覽器地址欄顯示的url和頁面實際訪問的url不一樣。實現(xiàn)路由偽裝的方法也很簡單,通過Link組件的as屬性告訴瀏覽器href對應顯示為什么url就可以了,index.js代碼修改如下:

import Link from 'next/link';
import Layout from '../components/Layout';

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

export default () => (
 <Layout>
  <h1>My Blog</h1>
  <ul>
   <PostLink id="hello-nextjs" title="Hello next.js" />
   <PostLink id="learn-nextjs" title="next.js is awesome" />
   <PostLink id="deploy-nextjs" title="Deploy apps with Zeit" />
  </ul>
 </Layout>
);

運行結(jié)果:

瀏覽器的url已經(jīng)被如期修改了,這樣看起來舒服多了。而且路由偽裝對history也很友好,點擊返回再前進還是能夠正常打開詳情頁面。但是如果你刷新詳情頁,確報404的錯誤,如圖:

這是因為刷新頁面會直接向服務器請求這個url,而服務端并沒有該url對應的頁面,所以報錯。為了解決這個問題,需要用到next.js提供的自定義服務接口(custom server API)。

自定義服務接口

自定義服務接口前我們需要創(chuàng)建服務器,安裝Express:

npm install --save express

在項目根目錄下創(chuàng)建server.js 文件,內(nèi)容如下:

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('*', (req, res) => {
   return handle(req, res);
  });
  server.listen(3000, (err) => {
   if (err) throw err;
   console.log('> Ready on http://localhost:3000');
  });
 })
 .catch((ex) => {
  console.error(ex.stack);
  process.exit(1);
 });

然后將package.json里面的dev script改為:

"scripts": {
 "dev": "node server.js"
}

運行npm run dev后項目和之前一樣可以運行,接下來我們需要添加路由將被偽裝過的url和真實的url匹配起來,在server.js中添加:

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

這樣我們就把被偽裝過的url和真實的url映射起來,并且query參數(shù)也進行了映射。重啟項目之后就可以刷新詳情頁而不會報錯了。但是有一個小問題,前端路由打開的頁面和后端路由打開的頁面title不一樣,這是因為后端路由傳過去的是id,而前端路由頁面顯示的是title。這個問題在實際項目中可以避免,因為在實際項目中我們一般會通過id獲取到title,然后再展示。作為Demo我們偷個小懶,直接將id作為后端路由頁面的title。

之前我們的展示數(shù)據(jù)都是靜態(tài)的,接下來我們實現(xiàn)從遠程服務獲取數(shù)據(jù)并展示。

遠程數(shù)據(jù)獲取

next.js提供了一個標準的獲取遠程數(shù)據(jù)的接口:getInitialProps,通過getInitialProps我們可以獲取到遠程數(shù)據(jù)并賦值給頁面的props。getInitialProps即可以用在服務端也可以用在前端。接下來我們寫個小Demo展示它的用法。我們打算從TVMaze API 獲取到一些電視節(jié)目的信息并展示到我的網(wǎng)站上。首先,我們安裝isomorphic-unfetch,它是基于fetch實現(xiàn)的一個網(wǎng)絡請求庫:

npm install --save isomorphic-unfetch

然后我們修改index.js如下:

import Link from 'next/link';
import Layout from '../components/Layout';
import fetch from 'isomorphic-unfetch';

const Index = (props) => (
 <Layout>
  <h1>Marvel TV Shows</h1>
  <ul>
   {props.shows.map(({ show }) => {
    return (
     <li key={show.id}>
      <Link as={`/p/${show.id}`} href={`/post?id=${show.id}`}>
       <a>{show.name}</a>
      </Link>
     </li>
    );
   })}
  </ul>
 </Layout>
);

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

export default Index;

以上代碼的邏輯應該很清晰了,我們在getInitialProps中獲取到電視節(jié)目的數(shù)據(jù)并返回,這樣在Index的props就可以獲取到節(jié)目數(shù)據(jù),再遍歷渲染成節(jié)目列表。

運行項目之后,頁面完美展示:

接下來我們來實現(xiàn)詳情頁,首先我們把/p/:id的路由修改為:

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

我們通過將id作為參數(shù)去獲取電視節(jié)目的詳細內(nèi)容,接下來修改post.js的內(nèi)容為:

import fetch from 'isomorphic-unfetch';
import Layout from '../components/Layout';

const Post = (props) => (
 <Layout>
  <h1>{props.show.name}</h1>
  <p>{props.show.summary.replace(/<[/]?p>/g, '')}</p>
  <img src={props.show.image.medium} />
 </Layout>
);

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

export default Post;

重啟項目(修改了server.js的內(nèi)容需要重啟),從列表頁進入詳情頁,已經(jīng)成功的獲取到電視節(jié)目的詳情并展示出來:

增加樣式

到目前為止,咱們做的網(wǎng)頁都太平淡了,所以接下來咱們給網(wǎng)站增加一些樣式,讓它變得漂亮。

對于React應用,有多種方式可以增加樣式。主要分為兩種:

  1. 使用傳統(tǒng)CSS文件(包括SASS,PostCSS等)
  2. 在JS文件中插入CSS

使用傳統(tǒng)CSS文件在實際使用中會用到挺多的問題,所以next.js推薦使用第二種方式。next.js內(nèi)部默認使用styled-jsx框架向js文件中插入CSS。這種方式引入的樣式在不同組件之間不會相互影響,甚至父子組件之間都不會相互影響。

styled-jsx

接下來,我們看一下如何使用styled-jsx。將index.js的內(nèi)容替換如下:

import Link from 'next/link';
import Layout from '../components/Layout';
import fetch from 'isomorphic-unfetch';

const Index = (props) => (
 <Layout>
  <h1>Marvel TV Shows</h1>
  <ul>
   {props.shows.map(({ show }) => {
    return (
     <li key={show.id}>
      <Link as={`/p/${show.id}`} href={`/post?id=${show.id}`}>
       <a className="show-link">{show.name}</a>
      </Link>
     </li>
    );
   })}
  </ul>
  <style jsx>
  {`
   *{
    margin:0;
    padding:0;
   }
   h1,a{
    font-family:'Arial';
   }
   h1{
    margin-top:20px;
    background-color:#EF141F;
    color:#fff;
    font-size:50px;
    line-height:66px;
    text-transform: uppercase;
    text-align:center;
   } 
   ul{
    margin-top:20px;
    padding:20px;
    background-color:#000;
   }
   li{
    list-style:none;
    margin:5px 0;
   }
   a{
    text-decoration:none;
    color:#B4B5B4;
    font-size:24px;
   }
   a:hover{
    opacity:0.6;
   }
  `}
  </style>
 </Layout>
);

Index.getInitialProps = async function () {
 const res = await fetch('https://api.tvmaze.com/search/shows?q=marvel');
 const data = await res.json();
 console.log(`Show data fetched. Count: ${data.length}`);
 return {
  shows: data
 }
}

export default Index;

運行項目,首頁變成:

增加了一點樣式之后比之前好看了一點點。我們發(fā)現(xiàn)導航欄的樣式并沒有變。因為Header是一個獨立的的component,component之間的樣式不會相互影響。如果需要為導航增加樣式,需要修改Header.js:

import Link from 'next/link';

const Header = () => (
 <div>
  <Link href="/" rel="external nofollow" rel="external nofollow" >
   <a>Home</a>
  </Link>
  <Link href="/about" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" >
   <a>About</a>
  </Link>
  <style jsx>
   {`
    a{
     color:#EF141F;
     font-size:26px;
     line-height:40px;
     text-decoration:none;
     padding:0 10px;
     text-transform:uppercase;
    }
    a:hover{
     opacity:0.8;
    }
   `}
  </style>
 </div>
)

export default Header;

效果如下:

全局樣式

當我們需要添加一些全局的樣式,比如rest.css或者鼠標懸浮在a標簽上時出現(xiàn)下劃線,這時候我們只需要在style-jsx標簽上增加global關鍵詞就行了,我們修改Layout.js如下:

import Header from './Header';

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

const Layout = (props) => (
 <div style={layoutStyle}>
  <Header />
  {props.children}
  <style jsx global>
   {`
    a:hover{
     text-decoration:underline;
    }
   `}
  </style>
 </div>
)

export default Layout

這樣鼠標懸浮在所有的a標簽上時會出現(xiàn)下劃線。

部署next.js應用

Build

部署之前我們首先需要能為生產(chǎn)環(huán)境build項目,在package.json中添加script:

"build": "next build"

接下來我們需要能啟動項目來serve我們build的內(nèi)容,在package.json中添加script:

"start": "next start"

然后依次執(zhí)行:

npm run build
npm run start

build完成的內(nèi)容會生成到.next文件夾內(nèi),npm run start之后,我們訪問的實際上就是.next文件夾的內(nèi)容。

運行多個實例

如果我們需要進行橫向擴展(Horizontal Scale)以提高網(wǎng)站的訪問速度,我們需要運行多個網(wǎng)站的實例。首先,我們修改package.json的start script:

"start": "next start -p $PORT"

如果是windows系統(tǒng):

"start": "next start -p %PORT%"

然后運行build: npm run build,然后打開兩個命令行并定位到項目根目錄,分別運行:

PORT=8000 npm start
PORT=9000 npm start

運行完成后打開localhost:8000和localhost:9000都可以正常訪問:

通過以上方法雖然能夠打包并部署,但是有個問題,我們的自定義服務server.js并沒有運行,導致在詳情頁刷新的時候依然會出現(xiàn)404的錯誤,所以我們需要把自定義服務加入app的邏輯中。

部署并使用自定義服務

我們將start script修改為:

"start": "NODE_ENV=production node server.js"

這樣我們就解決了自定義服務的部署。重啟項目后刷新詳情頁也能夠正常訪問了。

到此為止,我們已經(jīng)了解了next.js的大部分使用方法,如果有疑問可以查看next.js官方文檔,也可以給我留言討論。

本文Demo源碼:Github

源碼next.js官網(wǎng):https://nextjs.org/

next.js官方教程:https://nextjs.org/learn

next.js Github:https://github.com/zeit/next.js

以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。

相關文章

最新評論