中间跨了个春节,过完年手贱将做了一个礼拜的案例代码给删了,加上最近工作有点忙,学习也变的懈怠了,很久没有更新了
最近终于把之前的案例部分又重新做了遍,使用Github Actions自动化部署到自己的服务器,以及自动化部署到Vercel都给安排上
争取把学习进度赶回来吧
NuxtJS综合案例
案例介绍
- 案例名称:RealWorld
- 案例简介:一个开源的学习项目,帮助开发者快速学习新技能,提供了各种前端、后端技术实现该案例的方式
- Github仓库:https://github.com/gothinkster/realworld
- 在线示例:https://demo.realworld.io/#/
案例相关资源
- 页面模板:https://github.com/gothinkster/realworld-starter-kit/blob/master/FRONTEND_INSTRUCTIONS.md
- 接口文档:https://github.com/gothinkster/realworld/tree/master/api
使用NuxtJS来实现案例
问题与解决方案
在使用NuxtJS实现整个RealWorld案例的过程中,会碰到各种各样的问题,这里记录一下碰到的各种问题与相应的解决方案
静态资源加载
这个案例中使用到的一些CSS、字体文件等静态资源来源于国外的网站,在国内环境不使用"科学上网"方式来访问加载时会非常的缓慢
解决方案:
尝试使用jsDelivr网站,这个网站为npm、github、wordpress、deno等多个平台的开源资源提供免费的CDN地址
在该网站上搜索相应的静态资源库名称,例如我们案例中使用到的ionicons.min.css,找到相对应的版本,并获取cdn链接
-
https://www.jsdelivr.com/
对于非开源独立部署的静态资源,无法在jsDelivr上找到相应的CDN地址,可以将相应的资源手动下载下载,直接放到项目目录中进行本地访问
登录状态存储与持久化
我们在使用Vue开发前端项目时,通常使用Vuex来存储状态,并将单一状态树对象存储到
localStorage
中来实现状态的持久化NuxtJS默认集成了Vuex,但NuxtJS应用在进行服务端渲染时,无法使用浏览器环境下的
localStorage
解决方案:
NuxtJS官方提供的一种解决示例是将状态存储到
Cookie
中,以此实现服务端与客户端之间的状态共享与持久化存储浏览器环境的
Cookie
有大小限制,不同浏览器之间存在差异,通常为4K
// store/index.js
// NuxtJS 中 Vuex Store 示例
const cookieparser = process.server ? require('cookieparser') : undefined
// 服务端渲染运行期间都是同一个实例
// 为了防止数据冲突,必须把 state 定义成一个函数,返回数据对象
export const state = () => {
return {
user: null
}
}
export const mutations = {
setUser (state, data) {
state.user = data
}
}
export const actions = {
// nuxtServerInit 是一个特殊的 action 方法
// 这个 action 会在服务端渲染期间自动调用
nuxtServerInit ({ commit }, { req }) {
let user = null
// 判断请求头中携带 Cookie
if (req.headers.cookie) {
// 使用 cookieparser 将 Cookie 字符串解析成 JavaScript 对象
const parsed = cookieparser.parse(req.headers.cookie)
try {
user = JSON.parse(parsed.user)
commit('setUser', user)
} catch (error) {
}
}
}
}
// 使用 Cookie 存储状态数据示例
const Cookie = process.client ? require('js-cookie') : undefined
...
...
this.$store.commit('setUser', data.user)
// 使用 Cookie 存储状态数据实现持久化
Cookie.set('user', data.user)
页面访问鉴权
使用Vue开发前端应用时,对于页面访问的鉴权控制,通常可以使用路由拦截器(全局前置导航守卫beforeEach)来实现
对于NuxtJS开发的应用,当进行服务端渲染时,应当在进入页面处理之前就需要进行鉴权拦截,官方提供了通过路由中间件middleware的方式来实现页面访问鉴权的处理,可以同时支持客户端和服务端渲染的路由拦截
NuxtJS路由中间件
- 中间件是一个自定义的函数,接收
context
作为第一个参数,运行于一组页面渲染之前 - NuxtJS约定中间件定义的JS文件需放置在
middleware
目录下,JS文件的名称将作为中间件的名称 - 在需要使用中间件的页面组件中,通过
middleware
选项指定使用的中间件名称,可以使用数组同时指定多个需要执行的中间件
// middleware/authenticated.js
// 中间件定义示例
/**
* 验证是否登录的中间件
*/
export default function ({ store, redirect }) {
if (!store.state.user) {
return redirect('/login')
}
}
监听Query变化
实现首页数据分页处理的过程中,服务端渲染在
asyncData()
中加载页面数据,通过URL Query中增加page参数来获取当前访问的分页页码,进而处理接口获取相应的分页数据分页的触发可以直接使用
a
标签,但这样会触发页面刷新,重新请求服务端端渲染使用
nuxt-link
组件代替a
标签,可以触发Query的变化,但是并不会触发页面接口数据的重新加载NuxtJS官方提供了watchQuery属性,将监听配置所指定的Query参数字符串,当监听的Query参数字符串发生变化,将重新调用所有组件方法(asyncData/fetch/validate/layout等)
出于性能考虑,该属性默认禁用
使用示例
export default {
// 支持指定多个Query参数
// 如果要监听所有参数则直接设置为 true
watchQuery: ['page']
}
axios拦截器添加Token
对于axios请求,通常会单独定义JS文件来创建实例,并封装处理请求拦截器与响应拦截器,请求拦截器中往往需要向header中添加用户鉴权的toekn,而用户token存储在store中,无法在拦截器中直接访问,NuxtJS提供了
plugins
,自定义plugin是一个函数,接收context
上下文对象作为参数
plugns
需要在nuxt.config.js
配置文件中通过plugins
选项进行注册
// plugins/request.js
/**
* 基于 axios 封装的请求模块
*/
import Axios from "axios"
export const axios = Axios.create({
baseURL: 'https://conduit.productionready.io/',
})
// 通过插件机制获取上下文对象(query、params、req、res、app、store...)
export default ({ store }) => {
// 请求拦截器
axios.interceptors.request.use(function (config) {
const { user } = store.state
if (user && user.token) {
config.headers.Authorization = `Token ${user.token}`
}
return config
}, function (error) {
return Promise.reject(error)
})
// 响应拦截器
}
// nuxt.config.js
module.exports = {
...
plugins: [
'~/plugins/request'
]
}
Markdown支持
文章发布的内容支持markdown语法编辑提交,提交后的内容需要转换成HTML格式进行展示
这里借助第三方库
markdown-it
,将内容转换成HTML字符串,并使用v-html
指令进行展示
import { getArticle } from '@/api/article'
import MarkdownIt from 'markdown-it'
import ArticleMeta from '@/components/ArticleMeta'
export default {
name: 'Article',
components: {
ArticleMeta
},
props: {},
async asyncData ({ params }) {
const { data } = await getArticle(params.slug)
const { article } = data
const md = new MarkdownIt()
article.html = md.render(article.body)
return {
article
}
},
data () {
return {
}
},
computed: {},
created () { },
mounted () { },
methods: {}
}
设置页面Meta优化SEO
发布的不同的文章内容与标签需要进行SEO的优化,需要动态设置页面的Meta
NuxtJS可以通过
nuxt.config.js
来配置固定的Meta内容,使用head: {meta: {}}
选项如果想要针对不同的页面个性化定制Meta,NuxtJS在页面组件中提供了额外的
head()
方法,该方法返回一个配置对象,可以包含title
、meta
等属性
export default {
name: 'Article',
head () {
return {
title: `${this.article.title} - RealWorld`,
meta: [
{ hid: 'description', name: 'description', content: this.article.description }
]
}
},
...
}
发布部署
打包
NuxtJS提供的命令
nuxt
:开发模式编译带热更新nuxt build
:进行生产模式打包,打包生成.nuxt
目录,以及.nuxt/dist
目录(开发模式下不生成dist
目录)nuxt start
:启动生产模式打包的内容nuxt generate
:生成静态HTML资源
这里我们使用
nuxt build
进行生产模式打包,将打包后的.nuxt
目录,以及static
目录、nuxt.config.js
配置文件、package.json
、package-lock.json
一起上传到服务器
部署
首次部署之前,需要配置
nuxt.config.js
中的server
选项,指定host
和port
module.exports = {
...
server: {
host: '0.0.0.0',
port: 8080 // 默认3000
}
}
NuxtJS官方提供了各种平台、容器的部署方式指南,包括常用的PM2、NGINX、Github、Google App Engine、Vercel等近20种,相应方式的部署可以参考官方的指南及注意事项进行配置
自动化部署
项目日常开发与运维过程中,项目代码更新、打包、上传、部署这一系列操作需要反复的重复执行,将这些重复的工作通过自动化的方式去执行,可以减少重复工作,也符合DevOps的思想
常见的CI/CD服务有很多
- Jenkins
- Gitlab CI
- Github Actions
- Gitee Go
- Travis CI
- Circle CI
- ...
Github Actions
以Github Actions为例来实现自动化部署NuxtJS项目
准备工作
在github上创建项目仓库并将项目代码上传
准备一台用于部署的服务器(一般使用linux服务器)
配置Github Access Token
- https://github.com/settings/tokens生成token
- 将生成的token配置到项目的Secrets中
配置Github Actions执行脚本
- 在项目根目录创建
.github/workflows
目录 - 下载
main.yml
到workflows
目录中 - 修改配置
# main.yml 示例
name: Publish And Deploy Demo
on:
push:
tags:
- 'v*'
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
# 下载源码
- name: Checkout
uses: actions/checkout@master
# 打包构建
- name: Build
uses: actions/setup-node@master
- run: npm install
- run: npm run build
- run: tar -zcvf release.tgz .nuxt static nuxt.config.js package.json package-lock.json
# 发布 Release
- name: Create Release
id: create_release
uses: actions/create-release@master
env:
GITHUB_TOKEN: ${{ secrets.TOKEN }}
with:
tag_name: ${{ github.ref }}
release_name: Release ${{ github.ref }}
draft: false
prerelease: false
# 上传构建结果到 Release
- name: Upload Release Asset
id: upload-release-asset
uses: actions/upload-release-asset@master
env:
GITHUB_TOKEN: ${{ secrets.TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./release.tgz
asset_name: release.tgz
asset_content_type: application/x-tgz
# 部署到服务器
- name: Deploy
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.HOST }}
username: ${{ secrets.USERNAME }}
password: ${{ secrets.PASSWORD }}
port: ${{ secrets.PORT }}
script: |
cd /root/realworld-nuxtjs
wget https://github.com/rpyoyo/realworld-nuxtjs/releases/latest/download/release.tgz -O release.tgz
tar zxvf release.tgz
npm install --production
pm2 reload pm2.config.json
发布更新内容到Github并触发自动化部署
- 使用
git tag v1.0.0
命令创建tag标签 -
git push origin v1.0.0
提交tag标签触发自动化部署
在github项目的actions中可以查看到正在执行中的部署过程
自动化部署完成后,访问服务器的相应的地址与端口,确认是否可以正常访问页面
写在最后
NuxtJS官方提供了多达几十种部署方案相应的指南,参照这些指南可以方便地将我们的NuxtJS项目以我们自己想要的方式或者平台进行部署,例如部署到Vercel
我所完成的这整个案例
- Github地址:https://github.com/rpyoyo/realworld-nuxtjs
- Vercel地址:https://realworld-nuxtjs-zeta.vercel.app/