博客 Nuxt.js 移植重构与服务端渲染入门实现

Posted ·1043 Views·6053 Words

背景

前段时间做了一个 COVID-19 的疫情数据每日邮箱推送平台(https://ncov.ouorz.com),本着给自己用的小工具顺便练手的精神,尝试接触了一些新的关于 JavaScript 的技术栈。包括 Vue-Cli、Node.js、MongoDB 等,好不一番爽快的开发体验。顺便一提,这个疫情数据订阅平台我拿着去填写在了 Github Trending 爆火的 wuhan2020 项目(https://github.com/wuhan2020/wuhan2020)举办的「黑客马拉松」报名表里(https://www.bagevent.com/event/6365361),并且混进了其举办的技术大佬和产品经理的闪电演讲活动(当然我只是想听听这些技术大牛的经验和分享)。但是气氛异常神奇,大概就是大型在线秀技现场,我也是第一次感受到这些在职程序员对于「自己的职业满腔的热情和近乎偏执的认可」... 希望今后不要活成他们那样 🙂

说回博客。有了订阅平台的经验,便想尝试着在其他项目应用实践,最合适折腾的当然就是这个破破烂烂(至少在 WordPress 主题阶段)的博客了。Tony 主题群里最近多了一些很活跃的用户,经常会收到一些帮助和需求实现的请求,感谢他们的支持和积极反馈但是最近网课自学的我不敢浪啊,所以需求完善的效率不会有之前高,一些比较费时间或者支持「百度搜索」的反馈就应付应付了... 并且我也突然意识到其实并不是完全会和之前在年终总结中写到的关于开源免费主题免责免压力的想法一样,单单作为一个伪「开源作者」就理应积极响应反馈,秉持持续改善和贡献的开源精神。但是确实保持纯粹很难,因为不是所有问题都有机会去解决,但是又不想让某一部分的用户得不到支持(我太难了) 总之,矛盾着矛盾着还是先把这个需求做了,也就是「前后端分离」,不依赖 WordPress 的网站环境,静态页面访问。

 

Vue-Cli 移植

本博客最初是以 WordPress 主题为载体呈现的,为了实现 Vue.js 的应用就不得不在 PHP 里写 JavaScript 了,Tony 主题的代码经过了一定程度的调整和美化,但是其前身保留在了之前的本博客。所以移植起来还是蛮麻烦的,但是当时那几天效率特别高(可能是换了个键盘吧哈哈哈哈→https://www.ouorz.com/post/639),两天时间就移植完成了呢...

之前的 PHP 版本(暂且这么叫)在一些交互设计方面实现方法可以说非常简单粗暴,在重构版中自然需要更优雅一点的实现。

加载进度条

之前的加载进度条虽然也是采用 nprogress.js(https://www.npmjs.com/package/nprogress) 但是只是在页面加载完成后从 0% 直接滑动到 100%,只是觉得没了进度条不舒服的强迫症心理才做出了这种粗制滥造的效果(私密骂谁!)... 因为博客中存在多个页面和一些特定的动态路由结构(根据地址栏结构变化对应路由),比如文章页(/post/{文章 ID})、页面页(/page/{页面 ID}),为了更好的体验,使用了 Vue-Router 来实现(无感的)页面路由切换、参数读取等操作,在 Vue-Cli 项目的初始化(或者叫创建原型??)中有提供快捷引入选项。

在浏览器端,目前对于页面加载进度实现进度条大概最理想的方法就是在切换页面时加载一条自增长度的进度条,缓慢增加长度直到下个页面切换加载完毕直接滑动到末端并且淡出。Vue-Router 中可配置在路由切换之前与之后的回调函数,实现方法是在 main.js 或是 router/index.js 中加入配置:

// 引入依赖
import NProgress from "nprogress"
import 'nprogress/nprogress.css'

// 路由加载之前
router.beforeEach((to, from, next) => {
  NProgress.start(); // 展示进度条
  next();
})

 // 路由加载完成
router.afterEach(() => {
  NProgress.done();
})

↑ Vue-Cli 4.x 中 Vue-Router 插件自动引入,实例化对象名是 router

 

无限加载

其次是无限加载的实现,之前的实现方式是监听滑动到距离底部一定距离则执行加载下一页的函数,这种方法经常会在分辨率和页面大小不同的不同手机端上出现无法加载的问题。本次重构便挑选使用了一个其他开发者做的依赖来实现 —— Vue-mugen-scroll(https://www.npmjs.com/package/vue-mugen-scroll),使用方法也很简单快捷,在 .vue 单文件中可以写 CSS、JavaScript 和 HTML,需要注意的是 <template> 标签只能包含一个子元素,需要一个 Div 将所有内容括起来:

<!-- handler:需要执行的函数,should-handle:是否进行占位内容展示和无限滚动功能 -->
<mugen-scroll :handler="new_page" :should-handle="loading_first">
  <li class="article-list-item reveal index-post-list bottom" v-if="!loading_end">
    <div class="skeleton">
      <div class="skeleton-head"></div>
      <div class="skeleton-body">
        <div class="skeleton-title"></div>
        <div class="skeleton-content"></div>
      </div>
    </div>
  </li>
</mugen-scroll>

↑ 引入后使用组件 MugenScroll

// 引入依赖
import MugenScroll from "vue-mugen-scroll";

// 引入组件
export default {
  components: {
    MugenScroll
  }
}

↑ 引入 Vue-mugen-scroll

 

CDN 引入

文件中引入的所以依赖都是整个引入的(也就是非只引入需要使用的模块),于是安装正常普通的打包后的页面性能十分堪忧,单 JS 文件体积能上到 3 MB。所以需要做一些必要优化,比如文件拆分、CDN 静态资源引入、Gzip 等,更多可以参照 https://juejin.im/post/5c85af5de51d451a5a520021

需要注意的是在使用 CDN 引入资源文件时需要在 Vue-Cli 的配置文件中进行名称对应的配置,不同的命名对应起来比较麻烦因为并没有在这些依赖的官网看到关于此类应用的操作方法(比如 jQuery)。于是只好辛辛苦苦地去很多其他开发者分享的项目中找对应的配置命名。于是本项目用到的 CDN 对应配置如下,加入 vue.config.js 中:

chainWebpack: config => {
    config.externals({
      vue: 'Vue',
      'vue-router':'VueRouter',
      'axios':'axios',
      'jquery': 'jQuery',
      'vue-i18n': 'VueI18n'
  })
}

↑ vue-cli 支持修改 webpack 配置

 

其他功能

其他换用依赖库实现的功能包括:

  1. 英文/中文切换
  2. MarkDown 支持
  3. BootStrap UI 框架
  4. Highlight.js 代码高亮

分别采用了以下的依赖:

  1. Vue-i18n(https://www.npmjs.com/package/vue-i18n)
  2. MarkDown-it-vue(https://www.npmjs.com/package/markdown-it-vue)
  3. BootStrap-Vue(https://www.npmjs.com/package/bootstrap-vue)
  4. Highlight.js(https://www.npmjs.com/package/highlight.js)

部署时本来准备按照之前的订阅平台的做法,使用 Vue-Cli 内置生产环境服务器(也就是默认在 localhost:3000) 结合 Nginx 反向代理来实现访问的,但是偶然发现 Vue-Cli 打包的静态站点文件是可以直接被访问的 🙂 直接丢上去就好,真棒...

更多细节代码和实现欢迎前往 Github 一条龙→https://github.com/HelipengTony/antony

 

Nuxt.js 移植

做完 Vue-Cli 移植后继续意犹未尽(得寸进尺)...

众所周知,前端项目中加载动态内容需要先行获取服务端传来的数据后才能进行渲染展示,这就导致了页面内容加载会在首屏之后。机器憨憨的搜索引擎爬虫只会读取首屏之后的页面内容,如果是 PHP 生成的动态页面内容会直接得到展示(应该也算是服务端渲染吧)。不依赖于 Node.js 服务端渲染的纯 Vue.js 前端实现的旧版博客自然对 SEO 非常不友好了,之前的想法是能用自己能看就行自行车不重要,但是顶不住群里用户的需求,加之新技术栈不学白不学,那就开始吧...

 

路由配置

其实从 Vue-Cli 到 Nuxt.js 要改的地方不多,在 Nuxt.js 中原生无配置支持 SSR 服务端渲染及路由特性,异常方便。只需要将页面 .vue 文件放入 pages 目录下即可。打包文件时会自动遍历 pages 目录生成路由配置,实现动态路由只需建立目录层级并将参数名按:_{parameter} 作为文件名即可。具体可参照 Antony-Nuxt 目录结构。

 

服务端渲染

服务端渲染中就不可以渲染一些前端视图依赖的组件了,包括:回到顶部、国际化语言切换、加载进度条、cookies 读取等。解决方案是将这些依赖写成插件并在 nuxt.config.js 配置引入,比如加载进度条的实现。首先建立 plugins/route.js 文件,按照官方文档获取路由实例并进行配置:

import NProgress from "nprogress"
import 'nprogress/nprogress.css'
NProgress.configure({
  easing: 'ease-in-out',
  speed: 500,
  showSpinner: false,
  trickleSpeed: 200,
  minimum: 0.2
})

export default ({
  app
}) => {
  app.router.beforeEach((to, from, next) => {
    NProgress.start();
    next();
  })
  app.router.afterEach(() => {
    NProgress.done();
  })
}

↑ 配置方式和 Vue-Cli 中无异

再将该文件添加入 nuxt.config.js 插件列表中并设为非服务端渲染即可:

module.exports = {
    plugins: [
        {
          src: '@/plugins/route',
          ssr: false
        }
    ]
}

↑ 其余功能实现操作类似

回到顶部是采用的 vue-go-top-button(https://www.npmjs.com/package/vue-go-top-button) 依赖实现的,调用方法是引入组件,这时候就需要声明一个全局组件来解决非服务端渲染的问题了... 首先建立 .vue 文件引入该组件,并创建一个插件用来声明其为全局组件:

<template>
  <div>
    <go-top-button
      :animate="true"
      :speed="70"
      :acceleration="8"
      :scrollDistance="10"
      class="goup-container footer-goup"
    >
      <i class="ri-arrow-up-s-fill footer-goup-icon"></i>
    </go-top-button>
  </div>
</template>

<script>
import GoTopButton from 'vue-go-top-button'
import 'vue-go-top-button/dist/lib/vue-go-top-button.min.css'

export default {
  components: { GoTopButton }
}
</script>

↑ componets/gotop.vue 代码

import Vue from 'vue'
import GoTopButton from "~/components/gotop";

Vue.component('goToTop', GoTopButton)

↑ plugins/gotop.js 代码

 

Nginx 部署

Nuxt.js 服务端渲染依赖于 Node.js 环境,所以需要服务常驻并监听特定端口,通过 443 或者 80 (https/http) 访问则需要配置已有 HTTP 服务器比如 Nginx 的反向代理。{vhost}.conf 配置文件实例如下:

upstream blognuxt {
  server 127.0.0.1:3000; // Nuxt 服务端口
  keepalive 64;
}

server
    {
        listen 443 ssl http2;
        server_name www.ouorz.com ouorz.com;
        index index.html index.htm;
        location / {
            proxy_http_version 1.1;
            proxy_set_header X-Nginx-Proxy true;
            proxy_cache_bypass $http_upgrade;
            proxy_pass http://blognuxt/;
            proxy_redirect off;
        }
}

上传项目目录中 .nuxt(隐藏文件可能无法移动/上传,可改名或者压缩上传)、static、server 文件夹和 package.json,在服务器配置好 Node.js 环境并安装依赖:

yarn install / npm install

启动 Nuxt.js 服务,并使用 PM2(https://www.npmjs.com/package/pm2) 实现后台 Node 程序常驻:

yarn global add pm2 / npm install -g pm2
pm2 start npm --name "antony" -- run start

Nuxt.js 服务之后将不再在浏览器控制台输出错误,出现意料之外的情况可以使用如下语句查看错误信息和运行状态:

pm2 list // 列出 pm2 常驻程序列表
pm2 logs antony // 查看 antony 项目控制台日志

更多细节代码和实现欢迎前往 Github 一条龙→https://github.com/HelipengTony/antony-nuxt

 

后记

那就成现在博客这样啦,体验提升应该还是非常可观的。之后可能会做一键部署、可配置结构等,但那也是咕咕咕很久之后的事了吧 🙂

当然了,Tony 主题继续积极征求意见和反馈并且持续更新→https://github.com/HelipengTony/tony

Comment

Leave a comment to join the discussion