SSR之next.js--篇一

一、名词解释

       SSR(server-side-render),根据名称咱们都知道叫作服务端渲染,我相信对于工做几年的前端开发者确定很熟悉这个名词,多少也都用过,在这里,我想说的是针对我们刚刚踏入前端圈的朋友们的一点总结和感悟,但愿咱们可以不断的提升对next的认知,相互进步,相互成长!这就是咱们的目标~
  • 服务端渲染就是页面的渲染和生成由服务器来完成,并将渲染好的页面返回客户端
  • 客户端渲染就是页面的生成和数据的渲染过程是在客户端(或浏览器或APP)完成

其中掘金就使用vue的ssr功能作了全栈服务端渲染,切效果良好。前端

那咱们用next.js的优势是什么呢?

二、next.js的特性

  • 默认服务端渲染模式,以文件系统为基础的客户端路由
  • 代码自动分割使得页面加载更快
  • (以页面为基础)的简介的客户端路由
  • 以webpack的热替换为基础的开发环境
  • 使用React的JSX和ES6的module,模块化和维护更方便
  • 能够运行在Express和其余node.js的http服务器上
  • 能够定制化专属的babel和webpack配置
那咱们开始上手吧,hello world!

三、hello world

照作如下步骤吧~复制代码


接着打开package.json添加以下代码,以下:vue

{
  "scripts": {
    "dev": "next"
}}复制代码

到这里位置项目的准备工做已完成,运行如下命令开启项目服务器node

npm run dev复制代码

执行完毕后,在浏览器打开localhost:3000,就会看到如下页面:


这是next.js默认生成的404页面,儿开启服务后访问的之因此是如今的404页面,是由于咱们尚未设置项目主页。

那接下来就是建立页面了~~~啦啦啦~~~我学next我开心~~~

四、建立页面

next.js是从服务器生成页面,再返回给前端展现。

重点内容开始了哦~~~~复制代码

  • next.js默认从pages目录下取页面进行渲染返回给前端展现,并默认取pages/index.js为系统是首页进行展现
  • 注意,pages是默认存放页面的目录,路由的根路也是pages目录。

咱们在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

再来构建下多页面的吧~

使用next.js的目的就是构建非SPA的多页面项目,下面开始建立第二个页面。

在pages目录下建立文件pages/content.jsweb

export default () => (  
  <div>    
      <p>这是content 页面</p> 
   </div>
)复制代码

打开路由localhost:3000/content,看到咱们新建的第二个页面了。ajax


因此如今咱们明白全部的路由都是经过后端服务器来控制的 ,要想实现客户端路由,须要借助next.js的 Link API,因此接下来咱们看下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的页面大致相同, 区别就是页面的渲染主题不一样
  • 前者是nodejs服务器获取到后端数据渲染完页面后再返回给前端展现
  • 后者是前端先获取页面主体架构,再经过ajax的方式请求后端的数据,在前端渲染展现

七、Route Masking

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

这里咱们要用一个Link组件的as属性,并给组件添加一个id属性:(以index为例)

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复制代码


But。。。。咱们刷新下页面,咦怎么404了呢???


结论:

  • 当在 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页面中。

结论以下:
  • 当页面渲染时加载数据,咱们使用了一个异步方法getInitialProps。它能异步获取 JS 普通对象,并绑定在props上
  • 当服务渲染时,getInitialProps将会把数据序列化,就像JSON.stringify。因此确保getInitialProps返回的是一个普通 JS 对象,而不是Date, Map 或 Set类型。
  • 当页面初始化加载时,getInitialProps只会加载在服务端。只有当路由跳转(Link组件跳转或 API 方法跳转)时,客户端才会执行getInitialProps。


结论3在这里给你们作个验证:

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还有不少内容须要咱们不断总结和采坑,未完待续。。。。。。😃😄

相关文章
相关标签/搜索