nuxt

474 阅读14分钟

SSR服务器渲染

  1. SSR,即服务器渲染,在服务器端将对Vue页面进行渲染生成html文件,将html页面传递给浏览器。
  2. 使用 vue-server-render 插件进行服务端渲染,并集成了vue-router、vuex的服务端渲染框架

特性

  • 基于 Vue.js
  • 自动代码分层
  • 服务端渲染
  • 强大的路由功能,支持异步数据
  • 静态文件服务
  • ES2015+ 语法支持
  • 打包和压缩 JS 和 CSS
  • HTML 头部标签管理
  • 本地开发支持热加载
  • 集成 ESLint
  • 支持各种样式预处理器: SASS、LESS、 Stylus 等等
  • 支持 HTTP/2 推送

目录结构

|-- .nuxt                   // Nuxt自动生成,临时的用于编辑的文件,build
|-- assets                  // 放置经过webpack打包处理的资源文件,如LESS、SASS、图片、字体、JavaScript
|-- components              // 存放可复用的组件,比如滚动组件,日历组件,分页组件
|-- layouts                 // 布局目录,不可更改,页面都会加载在布局页面中的 <nuxt /> 标签中。如需在普通页面中使用下级路由,则要在页面中添加 <nuxt-child />
|-- middleware              // 存放中间件,可以在页面中调用: middleware: 'middlewareName'
|-- pages                   // 存放写的页面 
|-- plugins                 // 存放在根vue.js应用实例化之前,需要运行的js插件
|-- static                  // 存放静态资源资源,不需经过 webpack 打包的静态资源,如js css 图片
|-- store                   // 组织应用的Vuex 状态管理。
|-- .editorconfig           // 开发工具格式配置
|-- .eslintrc.js            // ESLint的配置文件,用于检查代码格式
|-- .gitignore              // 配置git不上传的文件
|-- nuxt.config.json        // 组织Nuxt.js应用的个性化配置,已覆盖默认配置
|-- package-lock.json       // npm自动生成,用于帮助package的统一性设置的,yarn也有相同的操作
|-- package.json            // npm包管理配置文件

pages目录下
1.一个 vue 文件即为一个页面,index.vue 为根页面。
2.若需要二级页面,则添加文件夹即可。
3.如果页面的名称类似于 _id.vue (以 _ 开头),则为动态路由页面,_ 后为匹配的变量(params)。
4.若变量是必须的,则在文件夹下建立空文件 index.vue。

API

页面(以下API只存在于页面组件)

执行顺序

  layout(context) {
    console.log('layout')
  },
  validate(context) {
    console.log('validate')
    return true
  },
  asyncData(context) {
    console.log('asyncData')
  },
  fetch(context) {
    console.log('fetch')
  },
  key(route) {
    console.log('key')
  },
  head() {
    console.log('head')
  },
  transition(to, from) {
    console.log('transition')
  },
  // 创建之前
  beforeCreate() {
    console.log('beforeCreate')
  },
  // 创建之后
  created() {
    console.log('created')
  },
  // 挂载之后
  mounted() {
    console.log('mounted')
  },
  // 挂载之前
  beforeMount() {
      console.log('beforeMount')
  }, 
  // 更新之前
  beforeUpdate() {
    console.log('beforeUpdate')
  },
  // 更新之后
  updated() {
    console.log('updated')
  },
  // nuxt没有 - 如果页面有keep-alive缓存功能,这个函数会触发
  activated() {
    console.log('activated')
  }, 
  // nuxt没有 - 如果页面有keep-alive缓存功能,这个函数会触发
  deactivated() {
    console.log('deactivated')
  },
  // 销毁之前
  beforeDestroy() {
    console.log('beforeDestroy')
  },
  //销毁完成
  destroyed() {
    console.log('destroyed')
  } 

qwe.png 121.jpg

asyncData

/*
1.在组件渲染前(组件初始化前)异步获取服务器数据、并渲染
2.组件每次加载前被调用,在服务端或路由更新之前被调用
3.return的数据会合并到data
4.不能获取this,为undefined,此时组件还没渲染
5.不能获取window、document等DOM元素,报错windowis not defined,因为运行在服务器,没有DOM元素
6.可以通过context获取app、route、store、params、query等信息
*/
export default {
  data() {
    return { project: 'default' }
  },
  asyncData(context) {
    return { project: 'nuxt' }
  }
}

fetch

/*
1.在组件渲染前(组件初始化前)异步获取服务器数据、并渲染
2.组件每次加载前被调用,在服务端或路由更新之前被调用
3.与asyncData不同,return的数据不会合并到data
4.不能获取this,为undefined,此时组件还没渲染
5.不能获取window、document等DOM元素,报错windowis not defined,因为运行在服务器,没有DOM元素
6.可以通过context获取app、route、store、params、query等信息
7.用于在渲染页面前填充应用的状态树(store)数据

asyncData fetch 区别
1.asyncData返回的数据会合并到data
2.fetch用于渲染页面前填充store
*/

/*
 应用1
 异步获取数据填充store
 需要返回一个Promise,Nuxt.js 会等这个 promise 完成后再渲染组件
*/
<template>
  <h1>Stars: {{ $store.state.stars }}</h1>
</template>

<script>
  export default {
    fetch({ store, params }) {
      return axios.get('http://my-api/stars').then(res => {
        store.commit('setStars', res.data)
      })
    }
     // 或
     async fetch({ store, params }) {
      let { data } = await axios.get('http://my-api/stars')
      store.commit('setStars', data)
    }
  }
</script>


/*
 应用2
 在fetch调用并操作store,请使用store.dispatch,但是要确保在内部使用async/await等待操作结束
*/
<script>
  export default {
    async fetch({ store, params }) {
      await store.dispatch('GET_STARS')
    }
  }
</script>

// store/index.js
export const actions = {
  async GET_STARS({ commit }) {
    const { data } = await axios.get('http://my-api/stars')
    commit('SET_STARS', data)
  }
}

head

/*
设置当前页面的头部标签
注意:为了避免子组件中的 meta 标签不能正确覆盖父组件中相同的标签而产生重复的现象,
建议利用 hid 键为 meta 标签配一个唯一的标识编号
*/
export default {
    data() {
      return {
        title: 'Hello World!'
      }
    },
    head() {
      return {
        title: this.title,
        meta: [
          {
            hid: 'description',
            name: 'description',
            content: 'My custom description'
          }
        ]
      }
    }
  }

key

/*
设置内部<router-view>组件的key属性

key属性赋值到<router-view>,这对于在动态页面和不同路径中进行转换很有用。
不同的key会使页面组件重新渲染
*/
export default {
  key(route) {
    return route.fullPath
  }
}

layout

/*
layout指定使用哪一个布局文件(layouts目录下组件)
*/
// 使用 layouts/blog.vue 作为当前页面组件的布局文件
export default {
  layout: 'blog',
  // 或
  layout(context) {
    return 'blog'
  }
}

loading

  • 全局禁用、页面禁用、自定义loading 
// nuxt.config.js
// 全局禁用
module.exports = {
  loading: false
}

// 页面禁用
export default {
  loading: false
}

// nuxt.config.js
// 自定义loading样式
module.exports = {
  loading: {
    color: 'blue',
    height: '5px'
  }
}

// nuxt.config.js
// 自定义loading组件
module.exports = {
  loading: '~components/loading.vue'
}


/*
自定义组件需要实现以下接口方法:

方法	是否必须	描述
start()	是	路由更新(即浏览器地址变化)时调用, 请在该方法内显示组件。
finish()	是	路由更新完毕(即asyncData方法调用完成且页面加载完)时调用,请在该方法内隐藏组件。
fail(error)	否	路由更新失败时调用(如asyncData方法返回异常)。
increase(num)	否	页面加载过程中调用, num 是小于 100 的整数。
*/

// components/loading.vue
<template lang="html">
  <div class="loading-page" v-if="loading">
    <p>Loading...</p>
  </div>
</template>
<script>
export default {
  data: () => ({
    loading: false
  }),
  methods: {
    start () {
      this.loading = true
    },
    finish () {
      this.loading = false
    }
  }
}
</script>

middleware

  • 页面设置中间件
  • 类型: String 或 Array
// pages/secret.vue
export default {
  middleware: 'authenticated'
}

// middleware/authenticated.js
export default function ({ store, redirect }) {
  // If the user is not authenticated
  if (!store.state.authenticated) {
    return redirect('/login')
  }
}

scrollToTop

/*
控制页面渲染前是否滚动至页面顶部
默认情况下,从当前页面切换至目标页面时,Nuxt.js 会让目标页面滚动至顶部。
但是在嵌套子路由的场景下,Nuxt.js 会保持当前页面的滚动位置,
除非在子路由的页面组件中将 scrollToTop 设置为 true
*/ 

export default {
  scrollToTop: true
}

transition

链接

validate

/*
1.校验动态路由参数的有效性
2.校验方法返回的值不为 true, Nuxt.js 将自动加载显示 404 错误页面
*/

validate({ params, query }) {
  return true // 如果参数有效
  return false // 参数无效,Nuxt.js 停止渲染当前页面并显示错误页面
}
async validate({ params, query, store }) {
  // await operations
  return true // 如果参数有效
  return false // 将停止Nuxt.js呈现页面并显示错误页面
}

// 返回一个 Promise
validate({ params, query, store }) {
  return new Promise((resolve) => setTimeout(() => resolve()))
}

validate({ params }) {
  // Must be a number
  return /^\d+$/.test(params.id)
}

// 校验 store 的数据 (如果 store 此前在 nuxtServerInit 方法 中被设置了的话 )
validate({ params, store }) {
  // 校验 `params.id` 是否存在
  return store.state.categories.some(category => category.id === params.id)
}

// 验证函数执行期间抛出预期或意外错误
async validate({ params, store }) {
  // 使用自定义消息触发内部服务器500错误
  throw new Error('Under Construction!')
}

watchQuery

  • 参数字符串发生变化,将调用所有组件方法(asyncData, fetch, validate, layout, ...)。 为了提高性能,默认情况下禁用
// 监听所有参数字符串
export default {
  watchQuery: true
}

// 监听特定参数字符串
export default {
  watchQuery: ['page']
}


组件

nuxt

/*
1.布局组件中显示页面
*/

// layouts/default.vue
<template>
  <div>
    <div>页头</div>
    <nuxt />
    <div>页脚</div>
  </div>
</template>


/*
有三种方式可以处理 <router-view /> 内部属性的 key
*/ 

// 1. nuxtChildKey全局统一处理key
// 将设置为<router-view />,可用于在动态页面和不同路由内进行过渡
// 默认: $route.path
<template>
  <div>
    <nuxt :nuxt-child-key="someKey" />
  </div>
</template>

// 2.页面组件中的key选项:string 或 function
export default {
  key(route) {
    return route.fullPath
  }
}

链接

默认模板

注意:{{ HEAD }}读取nuxt.config.js里的信息,{{APP}} 就是pages文件夹下的主体页面。HEAD和APP都需要大写,如果小写会报错的。 默认模板配置完成后,要重启服务器,否则显示不会成功,但是默认布局不用重启服务器

<!DOCTYPE html>
<html lang="en">
<head>
   {{ HEAD }}
</head>
<body>
    {{ APP }}
</body>
</html>

错误页面配置 

layouts 下新建error.vue文件

<template>
  <div class="container">
    <h1 v-if="error.statusCode === 404">页面不存在</h1>
    <h1 v-else>应用发生错误异常</h1>
    <nuxt-link to="/">首 页</nuxt-link>
  </div>
</template>

<script>
export default {
  props: ['error'],
  layout: 'blog' // 你可以为错误页面指定自定义的布局
}
</script>

自定义Loading页面

  • nuxt.config.js
module.exports = {
  loading: '~components/loading.vue'
}
  • components/loading.vue
<template lang="html">
  <div class="loading-page" v-if="loading">
    <p>Loading...</p>
  </div>
</template>

<script>
export default {
  data: () => ({
    loading: false
  }),
  methods: {
    start () {
      this.loading = true
    },
    finish () {
      this.loading = false
    }
  }
}
</script>

校验参数

  • 如果校验失败,则自动跳转到错误页面
<script>
export default {
  validate({ params, query }) {
    return /^d+$/.test(params.id) // must be number
  }
}
</script>

Header、Footer等公共组件放哪

  • vue-cli入口文件是app.vue,在nuxt 是 /layout/default.vue
<template>
  <div id="app">
    <!-- 公共头部组件 -->
    <xxx-header></xxx-header>
    <!-- 路由视图,相当于router-view -->
    <nuxt/>
    <!-- 公共底部组件 -->
    <xxx-footer></xxx-footer>
  </div>
</template>
 

没有keep-alive

  • 由于是服务端渲染,所以不支持组件的keep-alive,所以activated、deactivated生命周期也没了

配置插件

  • 所有插件都写在 /plugins目录,以vue-lazyload 为例
// plugins/lazy-load.js
import Vue from 'vue'
import VueLazyLoad from 'vue-lazyload'

Vue.use(VueLazyLoad, {
  loading: require('~/assets/images/loading.jpg'),
  error: require('~/assets/images/error.jpg')
})


// nuxt.config.js
module.expors = {
  plugins = [
    {
      src: "~/plugins/lazy-load",
      ssr: false
    }
  ]
}


引入element ui

1.安装 npm i element-ui -S

2.在根目录下的plugins下创建element-ui.js文件

 // 全局引入
import Vue from 'vue'
import ElementUI from 'element-ui'

Vue.use(ElementUI)

// 按需引入
import { Button, Loading, MessageBox } from 'element-ui'

Vue.use(Button)
Vue.prototype.$loading = Loading.service
Vue.prototype.$msgbox = MessageBox


3.修改nuxt.config.js文件
 css:[
    {src:'element-ui/lib/theme-chalk/index.css'}
  ],
 
  plugins: [{
    src:'~plugins/element-ui',
    ssr: true // 是能在服务端运行
  }]

axios封装

1.安装 npm install axios --save

2.在plugins文件夹下面创建request.js

import * as axios from 'axios'
import {Message, Notification} from 'element-ui'

let service = axios.create({
  baseURL: 'https://interface.meiriyiwen.com', // 域名信息-测试
  timeout: 10000
})

// 请求拦截 可在请求头中加入token等
service.interceptors.request.use(config => {
  return config
}, error => {
  return Promise.reject(error)
})

// 响应拦截 对响应消息作初步的处理
service.interceptors.response.use(resp => {
  if (resp.data) {
    if (resp.data.code !== '10000') {
      Message({
        type: 'error',
        message: resp.data.message,
        duration: 5000
      })
    }
    return {code: resp.data.code, data: resp.data.data, msg: resp.data.message}
  } else {
    return resp
  }
}, error => {
  if (error.response) {
    switch (error.response.states) {
      case 400: {
        if (error.response && error.response.data && error.response.data.message) {
          Notification.error({
            title: '400错误',
            message: error.response.data.message,
            duration: 5000,
            closable: true
          })
        }
        break
      }
    }
  }
})

export default service

3.创建统一接口文件,在一级目录创建api文件夹,在api文件夹下面创建demo.js

import request from '@/plugins/request'
//测试接口
export function getDemoinfo (params) {
  return request({
    url: '/article/today?dev=1',//测试
    method: 'get',
    params: params
  })
}

4.组件内调用接口
import {getDemoinfo} from '@/api/demo'

export default {
  data() {
    return {
      content: ""
    }
  },
  created() {
    getDemoinfo().then(res => {
      console.log(res.data)
      this.content = res.data.content;

    }).catch(error => {
      console.log("error", error)
    })
  }
}

axios配置全局拦截器,处理跨域

  1. 安装依赖 npm install @nuxtjs/axios @nuxtjs/proxy --save
  2. 使用、处理跨域
// nuxt.config.js
// 注意:组件的fetch和asyncData里只能使用nuxtjs模板里的axios,
// 如果使用自己引入的axios,是无法使用的

module.exports = {
  modules: [ '@nuxtjs/axios' ], // 不需要加入@nuxtjs/proxy
  axios: {
    baseURL: 'https://cnodejs.org/api/v1',
    proxy: true,
    prefix: '/api', // baseURL
    credentials: true,
  },
  proxy: {
    '/api/': {
      target: 'http://127.0.0.1:2001', // 代理地址
      changeOrigin: true,
      pathRewrite: {
        '^/api': ''
      },
    },
  }
}

  1. 组件中使用
<script>
export default {
  asyncData ({ app }) {
    console.log(app.$axios)
  },
  fetch ({ app }) {
    console.log(app.$axios)
  },
  created () {
    console.log(this.$axios)
    console.log(this.$axios.$get)
  }
}
</script>
  1. 并不需要在plugins配置axios,如果要设置全局拦截器,就要新建**/plugins/axios.js**
export default function (app) {
  let axios = app.$axios; 
 // 基本配置
  axios.defaults.timeout = 10000
  axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded'

  // 请求回调
  axios.onRequest(config => {})

  // 返回回调
  axios.onResponse(res => {})

  // 错误回调
  axios.onError(error => {})
}
 
  1. 然后在plugins配置它
module.exports = {
  plugins = [
    {
      src: "~/plugins/axios",
      ssr: false
    },
  ]
}

 Meta标签-配置文件

// nuxt.config.js

module.exports = {
  head: {
    title: 'your project title',
    meta: [
      { charset: 'utf-8' },
      { name: 'viewport', content: 'width=device-width, initial-scale=1' }
    ],
    link: [
      { rel: 'stylesheet', href: 'https://fonts.googleapis.com/css?family=Roboto' }
    ]
  }
}

Meta标签-页面组件

<script>
export default {
  head () {
    return {
      meta: 
      [
        {
          name: 'keywords',
          content: '最强王者,今晚吃鸡'
        },
      ]
    }
  }
}
</script>

Meta标签-动态路由填充

  • 举例,详情页面,由于数据是异步获取的,需要把获取数据写在asyncData钩子,待数据获取成功才会渲染该页面组件   
<script>
export default {
  async asyncData ({ app, params }) {
    let data = await app.$axios.get(`/appinfo/${params.id}`);
    return {
      appname: data.appinfo.appname
    }
  },
  head () {
    return {
      title: '首页' + (this.$route.query.tab ? `- ${this.tabsObj[this.$route.query.tab]}` : ''),
      meta: [{
          hid: 'keywords',
          name: 'keywords',
          content: `${this.appname},无限宝石,无限元宝`
        }]
    }
  }
}
</script>
 

 head封装

每个页面都需要写 head 对象,就会显得尤其的繁琐。借助 nuxt 的 plugin 机制,将其封装成一个函数,并注入到每一个页面当中

// plugins/global.js
import Vue from 'vue'

Vue.mixin({
  methods: {
    // 必传 标题,描述。其他的 meta 标签通过 payload 注入,其中,每个 meta 的 hid 需要是唯一的。
    $seo(title, content, payload = []) {
      return {
        title,
        meta: [{
          hid: 'description',
          name: 'description',
          content
        }].concat(payload)
      }
    }
  }
})

// nuxt.config.js 
export default {
  plugins: [
    '~plugins/global.js'
  ]
}

// pages/index.vue  
head() {
    return this.$seo(this.detail.title, this.detail.summary)
}

Vuex 使用

  • nuxt集成了vuex,不需要安装,在/store目录下新建index.js即可使用
// store/index.js

import Vuex from 'vuex'

let store = () => new Vuex.Store({
  state: {
    token: ''
  },
  mutations: {
    setToken (state, token) {
       state.token = token
    }
  }
})

export default store
 

head里面引入js文件

在head标签中,以inline的形式引入flexible.js文件

// nuxt.config.js
head: {
  script: [
    { innerHTML: 'console.log("hello")', type: 'text/javascript', charset: 'utf-8'}
  ]
}

生成 html:
<script data-n-head="true" type="text/javascript" charset="utf-8">
  console.log(&quot;hello&quot;)
</script>

发现 vue-meta 把引号做了转义处理,加入 __dangerouslyDisableSanitizers: ['script'] 后,就不会再对这些字符做转义了,该字段使用需慎重
head: {
  script: [{ innerHTML: require('./assets/js/flexible'), type: 'text/javascript', charset: 'utf-8'}],
  __dangerouslyDisableSanitizers: ['script']
}

数据持久化

  1. vue-cli中,vuex-persistedstate,可以使vuex的状态持久化,页面刷新都不会丢失,原理是localStorage
  2. vue-cookies保存token也可以
  3. nuxt中,上面这两种方法有个问题,于在created钩子中不存在window对象(获取cookie、localStorage都需要window对象),
  4. 当你需要判断是否存在token的时候,必须在mounted进行操作,这说明页面进来的一瞬间无法得知是否已经登录了,导致显示用户名、组件显示于隐藏都慢半拍
  5. nuxt非常友好, 提供了fetch钩子,还有nuxtServerInit,都运行在服务端并且能很快速地操作store

fetch

页面组件设置了fetch方法,会在组件每次加载前被调用(在服务端或切换至目标路由之前),此方法需要跟服务端的人员配合

<script>
export default {
  async fetch ({ app, store, params }) {
    let { data } = app.$axios.get('/token');
    store.commit('setToken', data.token);
  }
}
</script>

nuxtServerInit

终极无敌方法

// store/index.js

import Vuex from 'vuex'
let store = () => new Vuex.Store({
  state: {
    token: ''
  },
  mutations: {
    setToken (state, token) {
       state.token = token
    }
  },
  actions: {
    nuxtServerInit({ commit }, { req }) {
      let cookie = req.headers.cookie;
      // 将cookie转成json对象(自己实现该方法)
      let token = cookieparse(cookie).token;
      commit('setToken', token);
    },
  }
})
export default store

全局方法

  • 需要在plugins中配置
let xielikang = function () {

  /**
   * @method 打印信息方法
   * @param {String} msg 信息
   */
  let message = function (msg) {
    msg && console.log(msg)
  }

  let otherfn = function (msg) {}

  return {
    message,
    otherfn
  }
}

Vue.prototype.$kang= xielikang


// 组件调用
<script>
export default {
  created() {
    this.$kang.message('哈哈哈')
  }
}
</script>
 

全局样式

// nuxt.config.js

module.exports = {
  css: ['~/assets/stylesheets/main.css']
}

scss

  1. 安装依赖 npm install node-sass sass-loader --dev
  2. 组件中使用(不需要其他的配置了)
<style lang="scss" scoped>
</style>
  • 如果需要在项目中全局使用某个 scss 文件(如 mixins, vars 等)
  • 需要借助 sass-resources-loader : yarn add sass-resources-loader —dev
  • 还需要在 nuxt.config.js 的 build 配置中调整导出的 loader 配置
export default {
  ...
  build: {
    extend(config, { isDev, isClient }) {
      const sassResourcesLoader = {  
        loader: 'sass-resources-loader',  
        options: {  
          resources: [
            // 填写需要全局注入 scss 的文件。引入后,所有页面均有效。
            'assets/styles/mixins.scss'  
          ]
        }  
      }
      // 修改 scss sass 引用的 loader。
      config.module.rules.forEach((rule) => {  
        if (rule.test.toString() === '/\\.vue$/') {  
          rule.options.loaders.sass.push(sassResourcesLoader)  
          rule.options.loaders.scss.push(sassResourcesLoader)  
        }  
        if (['/\\.sass$/', '/\\.scss$/'].indexOf(rule.test.toString()) !== -1) {  
          rule.use.push(sassResourcesLoader)  
        }  
      })  
    }
  }
  ...
}

 fetch、asyncData、validate使用范围

只能在页面组件使用,也就是pages目录下的组件,而不是components目录下的组件

部署到服务器

前提:nuxt是基于nodejs运行的,因此确保已经安装 Node.js 

  1. 本地 npm run build 会在.nuxt文件夹下生成dist文件
  2. .nuxt, static, package.json, nuxt.config.js,放到服务器目录文件下,如C:\inetpub\nuxt
  3. 安装依赖 npm install -production
  4. 运行 npm start
  5.  http://localhost:8888

此时在服务器的本地可以访问了,但外网是无法进行访问的。 Nginx目的是通过域名访问到nuxt服务(测试域名为 www.test.top)

Nginx

  1. 安装Nginx,windows上推荐压缩包的安装(下载稳定版)
  2. 解压
  3. 双击nginx.exe  启动 http://localhost:80
  4. 修改端口80为81, /conf/nginx.conf中的server配置
#user  nobody;
worker_processes  1;

#error_log  logs/error.log;
#error_log  logs/error.log  notice;
#error_log  logs/error.log  info;

#pid        logs/nginx.pid;
events {
    worker_connections  1024;
}


http {
    include       mime.types;
    default_type  application/octet-stream;

    #log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
    #                  '$status $body_bytes_sent "$http_referer" '
    #                  '"$http_user_agent" "$http_x_forwarded_for"';

    #access_log  logs/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    #keepalive_timeout  0;
    keepalive_timeout  65;

    #gzip  on;

    upstream nodenuxt {
            server 127.0.0.1:3000; # nuxt 项目监听PC端端口
            keepalive 64;
        }

    server {
        listen       81;
        server_name  www.test.top;

        #charset koi8-r;

        #access_log  logs/host.access.log  main;

        location / {
           proxy_pass http://nodenuxt;
        }

        #error_page  404              /404.html;

        # redirect server error pages to the static page /50x.html
        #
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }

        # proxy the PHP scripts to Apache listening on 127.0.0.1:80
        #
        #location ~ \.php$ {
        #    proxy_pass   http://127.0.0.1;
        #}

        # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
        #
        #location ~ \.php$ {
        #    root           html;
        #    fastcgi_pass   127.0.0.1:9000;
        #    fastcgi_index  index.php;
        #    fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;
        #    include        fastcgi_params;
        #}

        # deny access to .htaccess files, if Apache's document root
        # concurs with nginx's one
        #
        #location ~ /\.ht {
        #    deny  all;
        #}
    }


    # another virtual host using mix of IP-, name-, and port-based configuration
    #
    #server {
    #    listen       8000;
    #    listen       somename:8080;
    #    server_name  somename  alias  another.alias;

    #    location / {
    #        root   html;
    #        index  index.html index.htm;
    #    }
    #}


    # HTTPS server
    #
    #server {
    #    listen       443 ssl;
    #    server_name  localhost;

    #    ssl_certificate      cert.pem;
    #    ssl_certificate_key  cert.key;

    #    ssl_session_cache    shared:SSL:1m;
    #    ssl_session_timeout  5m;

    #    ssl_ciphers  HIGH:!aNULL:!MD5;
    #    ssl_prefer_server_ciphers  on;

    #    location / {
    #        root   html;
    #        index  index.html index.htm;
    #    }
    #}

}

  1. 在nginx根目录下打开cmd命令窗口,启动 start nginx
  2. 浏览器输入www.test.top:81
其他命令
nginx -s reload //重新载入nginx(当配置信息发生修改时)

nginx -s quit //停止ngix 

nginx -h 查看帮助信息

注意:

  • 多次修改/conf/nginx.conf 后重启nginx ,在windows可能出现多个nginx 进程服务,需要结束这些进程,然后重启方可解决
  • 关闭服务器上nuxt运行黑窗口时,服务就断了,我们不能实时盯着他,因此需要PM2进行守护

pm2进程管理

第一种: 它允许您永久保持应用程序活跃,无需停机即可重新加载它们,并不需要传统部署的.nuxt文件夹,该部署方法也跟生产环境一样含热更新

npm install pm2 -g

npm run build
pm2 start ./node_modules/nuxt/bin/nuxt-start

第二种:

  1. 全局安装pm2 npm install -g pm2
  2. pm2启动nuxt项目,cd到目录,启动
pm2 start /node_modules/nuxt/bin/nuxt.js --name 项目名称  (项目目录的node_modules包)

pm2其他命令

pm2 list
pm2 show 0                           #查看进程详细信息,0为PM2进程id 
pm2 stop all                         #停止PM2列表中所有的进程
pm2 stop 0                           #停止PM2列表中进程为0的进程
pm2 reload all                       #重载PM2列表中所有的进程
pm2 reload 0                         #重载PM2列表中进程为0的进程
pm2 delete 0                         #删除PM2列表中进程为0的进程
pm2 delete all                       #删除PM2列表中所有的进程

生命周期1

注意:在任何 Vue 组件的生命周期内, 只有 beforeCreate 和 created 这两个钩子方法会在 客户端和服务端均被调用。其他钩子方法仅在客户端被调用

生命周期.png 输出 生命周期输出.png

生命周期2

121.png 生命周期流程图,红框内的是Nuxt的生命周期(运行在服务端),黄框内同时运行在服务端&&客户端上,绿框内则运行在客户端 (1)红框、黄框内的周期都不存在Window对象

export default {
  asyncData() {
    console.log(window) // 服务端报错
  },
  fetch() {
    console.log(window) // 服务端报错
  },
  created () {
    console.log(window) // undefined
  },
  mounted () {
    console.log(window) // Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, frames: Window, …}
  }
}

cookieparse

/*
 * @Author: likang xie 
 * @Date: 2018-09-14 15:56:14 
 * @Purpose: cookie格式化
 */

let cookieparse = function (cname, req) {
  let name = cname + "="
  let decodedCookie
  if (typeof window === 'undefined') decodedCookie = decodeURIComponent(req.headers.cookie)
  else decodedCookie = decodeURIComponent(document.cookie)
  let ca = decodedCookie.split(';')
  for (let i = 0; i < ca.length; i++) {
    let c = ca[i]
    while (c.charAt(0) == ' ') {
      c = c.substring(1)
    }
    if (c.indexOf(name) == 0) {
      return c.substring(name.length, c.length)
    }
  }
  return ""
}

export default cookieparse

context 变量的可用属性

**  属性字段**类型可用 描述
isClientBoolean客户端 & 服务端是否来自客户端渲染
isServerBoolean客户端 & 服务端是否来自服务端渲染
isDevBoolean客户端 & 服务端是否是开发(dev) 模式,在生产环境的数据缓存中用到
routevue-router客户端 & 服务端vue-router 路由实例
storevuex客户端 & 服务端Vuex.Store 实例。只有vuex 数据流存在相关配置时可用
envObject客户端 & 服务端nuxt.config.js 中配置的环境变量, 见 环境变量 api
paramsObject客户端 & 服务端route.params 的别名
queryObject客户端 & 服务端route.query 的别名
reqhttp.Request服务端Node.js API 的 Request 对象。如果 nuxt 以中间件形式使用的话,这个对象就根据你所使用的框架而定。nuxt generate 不可用
reshttp.Response服务端Node.js API 的 Response 对象。如果 nuxt 以中间件形式使用的话,这个对象就根据你所使用的框架而定。nuxt generate 不可用
redirectFunction客户端 & 服务端户请求到另一个路由。状态码在服务端被使用,默认 302。redirect([status,] path [, query])
errorFunction客户端 & 服务端用这个方法展示错误页:error(params)params 参数应该包含 statusCode 和 message字段。

Nuxt 特有函数

asyncData(context)

  1. asyncData在渲染组件之前异步获取数据,在服务端中执行的,所以,请求数据时,不存在跨域问题。
  2. 返回的数据与 data() 返回的数据进行合并。由于是在组件 初始化前 被调用的,所以 this 为 undefined
  3. ** 没有 documentwindow对象**

fetch(context)

  1. fetch 在渲染页面前填充应用的状态树(store)数据, 与 asyncData 方法类似,不同的是它不会设置组件的数据。
  2. 为了让获取过程可以异步,你需要返回一个 Promise,Nuxt.js 会等这个 promise 完成后再渲染组件。
  3. fetch 会在组件每次加载前被调用(在服务端或切换至目标路由之前)。

head

  1. Nuxt.js 使用了 vue-meta 更新应用的头部标签(Head) 和 html 属性。
  2. 用于更新 头部信息。如 title,descripe 等。
  3. head 方法里可通过 this 关键字来获取组件的数据。

layout

指定该页面使用哪个布局文件。默认值为 default

middleware

需要执行的中间件,如鉴权的 auth等。

transition

指定页面切换时的动画效果。支持传入 String, Object, Function

validate

Nuxt.js 可以让你在动态路由对应的页面组件中配置一个校验方法用于校验动态路由参数的有效性。 返回 true 说明路由有效,则进入路由页面。返回不是 true 则显示 404 页面

nuxtServerInit

nuxt与vue区别

  1. 路由 nuxt 按照 pages 文件夹的目录结构自动生成路由 http://localhost:3000/user/reg 相当于 去访问 pages文件夹 下的 user文件夹下的 reg.vue vue需在 src/router/index.js 手动配置路由
  2. 入口页面 nuxt 页面入口为 layouts/default.vue  vue页面入口为 src/App.vue
  3. nuxt 类似 router-view , nuxt-link 类似 router-link
  4. webpack配置  nuxt内置webpack,允许根据服务端需求,在 nuxt.config.js 中的build属性自定义构建webpack的配置,覆盖默认配置  vue关于webpack的配置存放在build文件夹下
  5. asyncData 里面发送ajax 这个东西跟生命周期这些都是平级的 要理解asyncData方法执行时,其实是在服务端完成的,这个数据是在服务端渲染好了的

**

nuxt-link和 a 区别

  1. nuxt-link 走的是 vue-router 的路由,即网页已为单页面,并且浏览器不会重定向。
  2. a 标签走的是 window.location.href,每一次点击 a 标签后的页面,都会进行一次服务端渲染,和普通的 PHP 混合开发没有太大的区别。
  3. 因为列表页数据类型有多种,该页面可能会被复用,所以当路由对象发生变化时,需要重新获取数据,这时可以监听路由的变化以做出响应:
watch: {
  '$route': function() {
    console.log('$route has changed.')
    this.getData()
  }
}