从0带您打造企业级 Vue 服务器渲染 Nuxt.js (一) 入门

3,260 阅读5分钟

写在开始前

公司团队技术栈一直主要使用 Vue,由于 SPA 模式开发有几大痛点,团队在2017年开始尝试 SSR(服务端渲染)。 记得刚开始 Nuxt.js 还没有到1.0(坑哭了),到目前为止团队已经经历了4个 Nuxt.js 项目,版本也从0.xx 一直到现在的 2.xx,我会把这期间遇到的问题和经验总结给大家,希望在使用 Nuxt.js 少跳些坑。

我能学到些什么

  • Nuxt 入门(普及些基础的知识,项目搭建)
  • Nuxt 性能提升(如何通过内网请求让你的api加快请求速度)
  • Nuxt 常见的问题和解决方案
  • Nuxt 线上部署

正文

SSR 的很多概念性的问题建议移步官方文档本文不做详细介绍。

1.为什么服务端渲染能解决 SEO 和首屏渲染

首先不管是Vue React Angular 等单页面应用渲染是从服务器获取所需Js,在客户端将其解析生成 HTML, 这样会带来几个问题。

  • 首屏先去服务器请求完成 Js 然后再由 Js 生成 HTML 元素,这会导致网站首屏加载缓慢,不利于用户体验。
  • 页面的 HTML 元素都是由后期的 Js 动态生成,搜索引擎无法爬取(谷歌目前已经可以爬取SPA页面,由于我们在天朝,你懂得)

2.为什么采用 Nuxt.js

首先使用 SSR 开发成本对纯前端来说还是比较高的,需要了解 SSR 渲染原理,有基础的 Node 知识。

还有一些生命周期,中间件需要自己去处理,Webpack, 这些搭建完成就会挡住大部分人。

Nuxt.js 提供了基础的 Webpack 和 Node 的封装,不需要我们再去搭建繁琐的 SSR 程序, 只需通过特定的文件结构和 Nuxt.js 提供的 Api 即可快速搭建一个 SSR 程序,详细可以异步官方文档

搭建一个 Nuxt.js 项目

Nuxt.js 2.0 以后提供了脚手架 create-nuxt-app

确保安装了npx(npx在NPM版本5.2.0默认安装了):

$ npx create-nuxt-app <项目名>

或者用yarn :

yarn create nuxt-app <项目名>

完成后脚手架会让你进行一些自定义的选择。

1.在集成的服务器端框架之间进行选择:

Nuxt.js 2.0 以前脚手架默认是不把 Node 服务器暴露出来的,只提供默认渲染服务器,当初我们用 Koa 分离出来还花费了一些时间,现在官方已经吧服务端框架直接在脚手架中让用户自由选择了(感谢)。

如果是初学建议你选择 None (Nuxt 默认服务器)框架不会暴露服务端代码到你目录,这样有个缺点,以后做些自定义的事情时候不是很方便。

我们团队成员基本都会使用 Egg 所以果断选择了 Koa 来创建项目,也建议您选择一个服务端框架进行初始化,后续本文会介绍一些针对 SSR 下内网请求加速,SSR 下使用中间层加速页面接口请求速度。

当运行完时,它将安装所有依赖项,现在启动项目愉快的玩耍吧:

$ npm run dev || yarn dev
// 应用现在运行在 http://localhost:3000 上运行。

Nuxt.js 流程图

下图阐述了 Nuxt.js 应用一个完整的服务器请求到渲染(或用户通过 切换路由渲染页面)的流程:

Nuxt.js 目录结构

里面基本的目录官方都有详细介绍官方文档,可以看见我们用了 Koa 初始化项目后会多一个 Server 目录 这个文件里面的 index.js 就是服务端框架的代码,如果对 Koa 不熟或者 Node 不熟可以先去官方充充电,入门还是比较简单的。

开始配置我们的项目

由于我们主要采用手机页面所以会配置自适应和一些便于开发的配置

配置nuxt.config.js

配置postcss
postcss: [
      require('postcss-import')({}),
      require('postcss-url')({}),
      require('postcss-preset-env')({
        browsers: 'last 2 versions'
      }),
      require('postcss-aspect-ratio-mini')({}),
      require('postcss-px-to-viewport')({
        viewportWidth: 375,
        viewportHeight: 667,
        unitPrecision: 3,
        viewportUnit: 'vw',
        selectorBlackList: ['.ignore', '.hairlines'],
        minPixelValue: 1,
        mediaQuery: false
      }),
      require('postcss-viewport-units')({
        filterRule: rule =>
          rule.selector.indexOf('::after') === -1 &&
          rule.selector.indexOf('::before') === -1 &&
          rule.selector.indexOf(':after') === -1 &&
          rule.selector.indexOf(':before') === -1
      })
    ]

以前我们适配各种手机主要是采用rem方案

现在浏览器对 vw 单位支持的越来越好,下面也给大家介绍一下在 Nuxt.js 中采用 vw

postcss-import:主要功有是解决@import引入路径问题。使用这个插件,可以让你很轻易的使用本地文件、node_modules或者web_modules的文件。这个插件配合postcss-url让你引入文件变得更轻松。

postcss-url:该插件主要用来处理文件,比如图片文件、字体文件等引用路径的处理。

postcss-preset-env:用来自动处理浏览器前缀的一个插件。你也可以像这样来指定last 2 versions 或者 > 5%

postcss-aspect-ratio-mini:主要用来处理元素容器宽高比。在实际使用的时候,具有一个默认的结构

postcss-px-to-viewport:主要把px单位转换为vw、vh、vmin或者vmax这样的视窗单位,也是vw适配方案的核心插件之一。在配置中需要配置相关的几个关键参数:

viewportWidth: 375,// 视窗的宽度,对应的是我们设计稿的宽度,一般是750 (如果我们设置的宽度是300px,那么编译之后的宽度为(300/750*100)=40vw,如果频宽实际为375px,那么该元素的宽度为(375*0.4)= 150px)
viewportHeight: 667,// 视窗的高度
unitPrecision: 3,// 指定`px`转换为视窗单位值的小数位数(很多时候无法整除)
viewportUnit: 'vw',// 指定需要转换成的视窗单位,建议使用vw
selectorBlackList: ['.ignore', '.hairlines'],// 指定不转换为视窗单位的类,可以自定义,可以无限添加,建议定义一至两个通用的类名
minPixelValue: 1,// 小于或等于`1px`不转换为视窗单位,你也可以设置为你想要的值
mediaQuery: false// 允许在媒体查询中转换`px`

postcss-viewport-units:插件主要是给CSS的属性添加 content 的属性,配合 viewport-units-buggyfill 库给 vw 、 vh 、 vmin 和 vmax 做适配的操作。

这是实现 vw 布局必不可少的一个插件,因为少了这个插件,这将是一件痛苦的事情。后面你就清楚。

现在 PostCSS 主要配置以及完成了,根据个人需要做删减。

配置环境变量来实现自动编译不同环境代码

首先去 babel plugins 配置一个插件

// 首先导入 webpack
const webpack = require('webpack')

plugins: [
  new webpack.DefinePlugin({
    // 接收环境变量并注入
    'process.env': JSON.stringify({
      NODE_EVENT: `${process.env.NODE_EVENT}`
    })
  })
],

然后我们去package.json 配置 local qa pre prd 等几个环境

"dev": "cross-env NODE_EVENT=local  NODE_ENV=development nodemon server/index.js --watch server",
"qa": "cross-env NODE_EVENT=qa  nuxt build",
"pre": "cross-env NODE_EVENT=pre  nuxt build",
"prd": "cross-env NODE_EVENT=prd  nuxt build",

接着去新增个config/index.js的配置文件

let starBaseUrl = ''

const NODE_EVENT = process.env.NODE_EVENT

if (NODE_EVENT == 'local') {
  // 本地开发环境
  starBaseUrl = 'http://xx.cn'
} else if (NODE_EVENT == 'qa') {
  // 测试
  starBaseUrl = 'http://xx.cn'

} else if (NODE_EVENT == 'testprod') {
  // 预生产
  starBaseUrl = 'http://xx.cn'
} else if (NODE_EVENT == 'prod') {
  // 生产
  starBaseUrl = 'http://xx.cn'
}

module.exports = {
  starBaseUrl
}

现在我们在生产时候就能编译出不同环境的代码,避免收到修改导致的失误。

尽请期待下一章