发布时间:2023-03-05 文章分类:编程知识 投稿人:李佳 字号: 默认 | | 超大 打印

小仙男·言在前

关于框架:为了解决VUE的SPA单页应用对SEO搜索引擎优化不友好的问题,这几天一直在调研各种SSR框架。比如doc.ssr-fc.com/ 和 fmfe.github.io/genesis-do 都是比较不错,且有自己理念和想法的框架。但是对于公司来说技术规范差异太大,团队学习成本比较高,思来想去,还是基于NUXT.JS自己搭建一套SSR框架慢慢完善吧。
关于本文档:本文档是从官网文档中摘录的一些重点内容,以及加入了自己的一些调整和对官网内容的理解和解释。
关于官网:NUXT中文网 特别适合新手学习,文档及案例十分清楚详尽,可以说有手就行。但是,中文网的更新不及时,有些章节(比如fetch钩子中不能使用this)甚至存在明显错误,所以有一定技术水平的宝子,建议直接查看 NUXT英文官网 。

【一、框架概述】

1、框架介绍

2、开发环境

3、分支要求

3、关于本文档

【二、启动与部署】

# 安装框架以来
$ npm install
# 启动本地开发环境,默认端口号:3000
$ npm run dev
# 编译并在生产环境启动
$ npm run build
$ npm run start
# 将网站打包成静态化页面
$ npm run generate

【三、框架结构】

-- 框架根目录
  -- .nuxt        Nuxt运营和编译自动生成
  -- dist         执行Nuxt静态化时生成
  -- api          全局通用的Api请求函数(非Nuxt提供)
  -- assets       静态资源目录,存放全局css、image等
  -- components   自定义组件目录,此目录下组件无需引入,按需使用即可
  -- layout       布局文件,参考https://www.nuxtjs.cn/guide/views
  -- middleware   中间件,类似于路由守卫
  -- modules      模块,用于设置全局监听等,参考https://www.nuxtjs.cn/guide/modules
  -- pages        页面目录,Nuxt会根据此目录自动生成路由,参考https://www.nuxtjs.cn/guide/routing
  -- plugins      插件目录,自定义各种插件,参考https://www.nuxtjs.cn/guide/plugins
   > global.js    (全局变量与全局方法)
   > plugin.js    (全局引入第三方组件)
   > request.js   (全局请求封装)
   > filter.js    (全局过滤器封装)
   > util.js      (全局工具函数封装)
   > all.client.js(仅在客户端执行插件,暂时替代原app.vue)
  -- static       不需要webpack编译的静态文件,一般存放ico等文件
  -- store        Vue状态树,与原写法有所不同,参考https://www.nuxtjs.cn/guide/vuex-store
  -- utils        工具类包 (非Nuxt提供)
  .editorconfig   
  .gitignore
  env.js          环境变量配置,分dev、test、pro三种环境
  nux.config.js   Nuxt的所有配置项,参考https://www.nuxtjs.cn/api/configuration-build
  package-lock.json
  package.json
  README.md       框架使用文档
  ReleaseNote.md  版本更新说明

【四、生命周期】

-- Nuxt完整声明周期
  【服务端渲染】
    -- 全局
  nuxtServerInit    第一个:nuxt中第一个运行的生命周期
  RouteMiddleware   第二个:中间件,类似于原框架的路由导航守卫
    -- 组件
  validate          是用来校验url参数符不符合
  asyncData         Nuxt专属声明周期,可用于数据请求,只有page可用,子组件内部不可用
  beforeCreate      Vue声明周期,但是服务端会执行(不可用于数据请求,数据请求相关操作会在客户端执行)
  created           Vue声明周期,但是服务端会执行(同上)
  fetch             Nuxt专属声明周期,可用于数据请求, page和子组件都可用 
  【客户端渲染】
    -- 全局
  * `@/plugins/all.client.js` (并非Nuxt声明周期,是只在客户端运行的插件。此框架中用于暂时替代原框架中在App.vue中进行的全局初始化操作。)
    -- 组件
  beforeCreate
  created
  beforeMount
  mounted
  ... (其他Vue后续声明周期)

几点说明:

  1. beforeCreate/created 是Vue的生命周期,但是会在服务端和客户端各执行一次,但这两个钩子,仅供了解,不能用于数据请求。
  2. asyncDatafetch都是Nuxt提供的声明周期,都可用于数据请求。只是写法略有不同(参考后续章节【五、数据请求】)。
  3. @/plugins/all.client.js 并非Nuxt声明周期,是只在客户端运行的插件。但是Nuxt框架去掉了app.vue,此插件的声明周期,近似于原来的app.vue,故暂时用于替代原框架中在App.vue中进行的全局初始化操作(是否恰当暂时不知)。

【五、数据请求】

1. 数据请求钩子

1.1 钩子相关说明

1.2 asyncData

// ① 使用return返回的对象,将直接初始化到组件`data`中
async asyncData({app, params}) {
    const { code, data } = await app.$get('/policy/findById/'+params.id)
    return {detail: data}
},
// ② return一个Promise,将在Promise执行完成后,将数据初始化到组件`data`中
asyncData({app, params}) {
    return app.$get('/policy/findById/'+params.id).then(res => {
      return {detail: data}
    })
},
// ③ 第二个参数为callback回调函数,可直接传入数据,初始化到组件`data`中
asyncData({app, params}, callback) {
    app.$get('/policy/findById/'+params.id).then(res => {
      callback(null, {detail: data}) 
    })
},

1.3 fetch

// ① 使用return返回一个Promise
fetch() {
    return this.getDetail()
},
// ②  使用await/async
async fetch() {
    await this.getDetail()
},
methods: {
    // ① 使用await编写methods方法
    async getDetail(id){
        const { code, data } = await this.$get('/policy/findById/'+this.$route.params.id)
        this.detail = data
    }
    // ② 使用return Promise编写methods方法
    getDetail(id){
        return this.$get('/policy/findById/'+this.$route.params.id).then(resw => {
          this.detail = res.data
        })
    }
}

2. 数据请求方式

2.1 【框架推荐】 使用vue实例直接调用

// 以this调用为例,如果是在`asyncData`中,需要使用上下文`context.app`调用
// ① get
this.$get('/policy/findById/'+this.$route.params.id)
// ②  post
this.$post('/policy/findAll/',{page:1,size:10,params:{}})
// ③  request
this.$request({
    url: '/policy/findAll/',
    method: 'post',
    data: {page:1,size:10,params:{}}
})

2.2 兼容老框架的api分离式调用

/**
 * @/api/index.js
 */ 
import request from '@/utils/request'
export function getPageList(data) {
    return request.post('/policy/findAll', data)
}
/**
 * @/pages/index.vue
 */ 
import { getPageList } from "@/api/index.js"
export default {
    fetch() {
        return this.getPageList(this.pageDto)
    },
    methods: {
        getPageList(pageDto) {
            return getPageList(pageDto).then(res => {
              this.pageList = res.data.result
            })
        }
    },
}

3. 其他注意事项

【六、其他规范与Q&A】

1. 关于pages

【注意】

// return true表示验证通过,return false表示验证失败 404
validate({ params }) {
    return /^\d+$/.test(params.id)
},

2. 关于plugins

3. 关于layout

export default {
    // 需要调用的视图名称,不写默认调用default.vue
    layout: 'onlyBody',
    data(){
      return {}
    }
}

4. 关于components

<template>
  <div>
    // Header组件
    <Header />
  </div>
</template>

5. 关于store

/**
 * 【注意区别】
 * state mutations action不再是包裹在一个对象中,并在new Vuex()的时候传入。 而是分别作为单独模块使用export导出即可。
 */
export const state = () => ({
    counter: 0
})
export const mutations = {
    increment(state) {
        state.counter++
    }
}

6. 关于middleware

7. 关于modules

const generator: any = function () {
    this.nuxt.hook('generate:done', (context: any) => {
        // TODO samething
    })
}
export default generator

8. 其他Q&A

1)每个页面,必须使用head设置title,必要时还需在详情页设置description。(!!!切记!!!)

export default {
    head() {
        return {
            // title必须设置!!! 列表可以直接写“xxx列表”,详情页等有不同标题的,要用新闻标题、商品标题等作为title前缀。
            title: this.detail.title + '_新闻详情',
            meta: [
                // 详情页,需要设置不同的description。 this.$getDesc 为全局封装的从富文本中截取100字符的description
                { hid: 'description', name: 'description', content: this.$getDesc(this.detail.details) },
            ],
        }
    }
}

2)pages目录中的层级结构,务必按照功能梳理清楚,比如“news(新闻)”的列表、详情都要在一个文件夹中。

(!!!目录结构一旦确定,原则上不可再调整!!!)

3)框架中的其他重要文件之【CSS篇】!!

4)(未完待续…)其他任何框架问题,详询小仙男