当前位置: 首页 > news >正文

Next.js之基础概念(二)

本篇教程基于上一篇的基础,主要讲解服务端渲染,样式以及部署相关的一些知识,若你没有看过上一篇的内容,或者你看过又忘了,建议重新去看一遍。

顺便说一句,Next.js 3.0的版本在前几天已经正式对外发布,本篇教程仍然基于2.x的版本,若你使用3.0的版本,代码上可能有不一致的地方,需要你留意一下。

为了快速开始,咱们直接使用官方的例子进行讲解,先把代码拷贝下来:

git clone https://github.com/arunoda/learnnextjs-demo.git
cd learnnextjs-demo
git checkout clean-urls-ssr

项目拷贝下来后我们进入目录,安装一下依赖并启动:

cd learnnextjs-demo
npm install
npm run dev

打开页面,即可看到如下效果:

图片描述

服务端渲染

官方的例子是根据TVMaze提供的api来展示电视节目,因此我们需要安装isomorphic-unfetch用于获取远程数据:

npm install  isomorphic-unfetch -S

然后将以下代码替换到pages/index.js中:

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

const Index = (props) => (
  <Layout>
    <h1>Batman TV Shows</h1>
    <ul>
      {props.shows.map(({show}) => (
        <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=batman')
  const data = await res.json()

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

  return {
    shows: data
  }
}

export default Index

上面的代码中,写在Index.getInitialProps中的内容,既可以跑在server端,也可以跑在浏览器端,当我们刷新页面时,可以看到server段的控制台输出了:

Show data fetched. Count: 10

但在浏览器控制台中却没有看到输出,那什么时候这段代码会跑在浏览器端呢?那就是当你通过客户端路由进来的时候,这段代码才会执行,我们来更改一下代码,

server.js中,找到包含/p/:id的地方,并替换如下:

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

同时,更改pages/post.js:

import Layout from '../components/MyLayout.js'
import fetch from 'isomorphic-unfetch'

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()

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

  return { show }
}

export default Post

这时候当我们是从首页的链接点进去post页面时,会由post页面从浏览器端发出请求获取数据,要是在这个页面直接刷新页面,则会由服务端获取数据,这就是Next.js实现的ssr,是不是感觉很简单。

另外,如果有些代码只希望在服务端执行,而不希望浏览器端执行,在可以根据context里面有没有包含req这个字段来判断,代码如下:

   Post.getInitialProps = async function (context) {
     if(context.req) {
         // 只会在服务端执行
     }
   
     return { show }
   } 

上面的代码其实是有问题的,想想看,我希望在服务端执行的代码,自然不希望webpack把它打包到客户端,否则会增大打包后的脚本,用户体验也不好,解决方案可以参考这里,这里就不展开了。

样式

在react中写样式,一般可以归为2类,一类是基于css文件的传统方式(包括sass,postcss等),另一类则是css in js。

基于传统的方式写css,在Next.js中会有些问题,特别是ssr的时候,因此,官网给出的解决方案是使用css in js,在Next.js中,已经预装了一个css in js的框架叫styled-jsx。

我们回到我们的代码中,更改pages/index.js,代码如下:

import Layout from '../components/MyLayout.js'
import Link from 'next/link'

function getPosts () {
  return [
    { id: 'hello-nextjs', title: 'Hello Next.js'},
    { id: 'learn-nextjs', title: 'Learn Next.js is awesome'},
    { id: 'deploy-nextjs', title: 'Deploy apps with ZEIT'},
  ]
}

export default () => (
  <Layout>
    <h1>My Blog</h1>
    <ul>
      {getPosts().map((post) => (
        <li key={post.id}>
          <Link as={`/p/${post.id}`} href={`/post?title=${post.title}`}>
            <a>{post.title}</a>
          </Link>
        </li>
      ))}
    </ul>
    <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>
  </Layout>
)

在标签<style jsx>中,我们写我们的css,css必须包含在 {``}中,否则会报错。

由于css in js有作用域的隔离,也就是说css只会应用于当前组件,不会应用于其子组件。因此,以下代码,样式只会作用于h1和ul标签,不会作用于li标签:

import Layout from '../components/MyLayout.js'
import Link from 'next/link'

function getPosts() {
  return [
    { id: 'hello-nextjs', title: 'Hello Next.js' },
    { id: 'learn-nextjs', title: 'Learn Next.js is awesome' },
    { id: 'deploy-nextjs', title: 'Deploy apps with ZEIT' },
  ]
}

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

export default () => (
  <Layout>
    <h1>My Blog</h1>
    <ul>
      {getPosts().map((post) => (
        <PostLink key={post.id} post={post} />
      ))}
    </ul>
    <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>
  </Layout>
)

所以,你需要把样式写在子组件中:

const PostLink = ({ post }) => (
  <li>
    <Link as={`/p/${post.id}`} href={`/post?title=${post.title}`}>
      <a>{post.title}</a>
    </Link>
    <style jsx>{`
      li {
        list-style: none;
        margin: 5px 0;
      }

      a {
        text-decoration: none;
        color: blue;
        font-family: "Arial";
      }

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

或者,使用全局选择器(global selectors),只需在style标签中加一个global关键字,如下:

<style jsx global>{`
      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>

部署Next.js应用

部署Next.js也是一件非常简单的事情,我们更改一下我们的package.json文件,在scripts字段中添加build和start:

"scripts": {
    "dev": "node server.js",
    "build": "next build",
    "start": "next start"
}

然后执行:

npm run build
npm run start

npm run build命令会打包适用于生产环境的代码,npm run start则会启动我们的应用,默认端口为3000。

若想启动两个应用实例,只需要自定义端口即可,代码如下:

"scripts": {
  "start": "next start -p $PORT"
}
npm run build

PORT=8000 npm start
PORT=9000 npm start

如上,会在8000及9000端口各自启动一个实例。

至此,Next.js的基础概念已经介绍完了,更高级的用法,可以参考官方的例子:Next.js。

相关文章:

  • 日志详解
  • High-Level Streams DSL(翻译)
  • seo优化中不容忽视的页面优化
  • predis连接redis sentinel和redis cluster
  • 4.NIO的非阻塞式网络通信
  • 远程通信的几种选择(RPC,Webservice,RMI,JMS的区别)
  • 从Code Review 谈如何做技术(zz)酷 壳
  • 大白话讲Zookeeper能做什么?(一):命名服务与配置管理
  • 大数据拼精准 可否触动电商个性营销神经
  • 二进制安装MySQL5.5.57
  • Ubuntu 安装和使用 Supervisor(进程管理)
  • 本地虚拟化开发环境 vagrant+virtualbox
  • centos下为php添加gd/curl/zip扩展
  • Simple2D-20(重构)
  • 中国好同事!帮程序猿跟姑娘表白,他们组了一支乐队
  • [译] 理解数组在 PHP 内部的实现(给PHP开发者的PHP源码-第四部分)
  • “Material Design”设计规范在 ComponentOne For WinForm 的全新尝试!
  • 【vuex入门系列02】mutation接收单个参数和多个参数
  • 【编码】-360实习笔试编程题(二)-2016.03.29
  • 【附node操作实例】redis简明入门系列—字符串类型
  • 【许晓笛】 EOS 智能合约案例解析(3)
  • Android Studio:GIT提交项目到远程仓库
  • ES6简单总结(搭配简单的讲解和小案例)
  • java8-模拟hadoop
  • JavaScript 是如何工作的:WebRTC 和对等网络的机制!
  • javascript从右向左截取指定位数字符的3种方法
  • JavaScript中的对象个人分享
  • Quartz初级教程
  • 大整数乘法-表格法
  • 解析带emoji和链接的聊天系统消息
  • 马上搞懂 GeoJSON
  • 每天一个设计模式之命令模式
  • 那些年我们用过的显示性能指标
  • 前端 CSS : 5# 纯 CSS 实现24小时超市
  • 前端之Sass/Scss实战笔记
  • 区块链共识机制优缺点对比都是什么
  • 如何用Ubuntu和Xen来设置Kubernetes?
  • 问:在指定的JSON数据中(最外层是数组)根据指定条件拿到匹配到的结果
  • 由插件封装引出的一丢丢思考
  • 转载:[译] 内容加速黑科技趣谈
  • ​ ​Redis(五)主从复制:主从模式介绍、配置、拓扑(一主一从结构、一主多从结构、树形主从结构)、原理(复制过程、​​​​​​​数据同步psync)、总结
  • !! 2.对十份论文和报告中的关于OpenCV和Android NDK开发的总结
  • # 深度解析 Socket 与 WebSocket:原理、区别与应用
  • #define 用法
  • (1) caustics\
  • (react踩过的坑)Antd Select(设置了labelInValue)在FormItem中initialValue的问题
  • (TOJ2804)Even? Odd?
  • (第61天)多租户架构(CDB/PDB)
  • (附源码)计算机毕业设计SSM保险客户管理系统
  • (三)uboot源码分析
  • (十六)串口UART
  • (图)IntelliTrace Tools 跟踪云端程序
  • (原創) 如何讓IE7按第二次Ctrl + Tab時,回到原來的索引標籤? (Web) (IE) (OS) (Windows)...
  • (转)Windows2003安全设置/维护
  • (轉)JSON.stringify 语法实例讲解