美文网首页
学习笔记(二十一)NuxtJS综合案例

学习笔记(二十一)NuxtJS综合案例

作者: 彪悍de文艺青年 | 来源:发表于2021-03-24 10:53 被阅读0次

    中间跨了个春节,过完年手贱将做了一个礼拜的案例代码给删了,加上最近工作有点忙,学习也变的懈怠了,很久没有更新了
    最近终于把之前的案例部分又重新做了遍,使用Github Actions自动化部署到自己的服务器,以及自动化部署到Vercel都给安排上
    争取把学习进度赶回来吧

    NuxtJS综合案例

    案例介绍

    案例相关资源

    使用NuxtJS来实现案例

    问题与解决方案

    在使用NuxtJS实现整个RealWorld案例的过程中,会碰到各种各样的问题,这里记录一下碰到的各种问题与相应的解决方案

    静态资源加载

    这个案例中使用到的一些CSS、字体文件等静态资源来源于国外的网站,在国内环境不使用"科学上网"方式来访问加载时会非常的缓慢

    解决方案:

    • 尝试使用jsDelivr网站,这个网站为npm、github、wordpress、deno等多个平台的开源资源提供免费的CDN地址

    • 在该网站上搜索相应的静态资源库名称,例如我们案例中使用到的ionicons.min.css,找到相对应的版本,并获取cdn链接

    • https://www.jsdelivr.com/

      image-20210203210747636
    • 对于非开源独立部署的静态资源,无法在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()方法,该方法返回一个配置对象,可以包含titlemeta等属性

    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.jsonpackage-lock.json一起上传到服务器

    部署

    首次部署之前,需要配置nuxt.config.js中的server选项,指定hostport

    module.exports = {
      ...
      server: {
        host: '0.0.0.0',
        port: 8080 // 默认3000
      }
    }
    

    NuxtJS官方提供了各种平台、容器的部署方式指南,包括常用的PM2、NGINX、Github、Google App Engine、Vercel等近20种,相应方式的部署可以参考官方的指南及注意事项进行配置

    自动化部署

    image-20210206124247970

    项目日常开发与运维过程中,项目代码更新、打包、上传、部署这一系列操作需要反复的重复执行,将这些重复的工作通过自动化的方式去执行,可以减少重复工作,也符合DevOps的思想

    常见的CI/CD服务有很多

    • Jenkins
    • Gitlab CI
    • Github Actions
    • Gitee Go
    • Travis CI
    • Circle CI
    • ...

    Github Actions

    以Github Actions为例来实现自动化部署NuxtJS项目

    准备工作

    • 在github上创建项目仓库并将项目代码上传

    • 准备一台用于部署的服务器(一般使用linux服务器)

    配置Github Access Token

    配置Github Actions执行脚本

    • 在项目根目录创建.github/workflows目录
    • 下载main.ymlworkflows目录中
    • 修改配置
    # 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中可以查看到正在执行中的部署过程

    image-20210324101403124

    自动化部署完成后,访问服务器的相应的地址与端口,确认是否可以正常访问页面

    image-20210324101556859

    写在最后

    NuxtJS官方提供了多达几十种部署方案相应的指南,参照这些指南可以方便地将我们的NuxtJS项目以我们自己想要的方式或者平台进行部署,例如部署到Vercel

    我所完成的这整个案例

    相关文章

      网友评论

          本文标题:学习笔记(二十一)NuxtJS综合案例

          本文链接:https://www.haomeiwen.com/subject/oxichltx.html