从零开始搭建Nuxt到部署上线

2,181 阅读7分钟

配置Nuxt

安装Nuxt

直接用官网到快速开始就行

npx create-nuxt-app <项目名>

配置stylus

安装包

npm i -S stylus stylus-loader

配置

无需配置,直接在js中引入,然后在js同级的目录中创建styl页面

<template>
    <div></div
</template>
<style scoped lang="stylus" src="./style.styl"></style>

配置自适应vw布局

安装包

npm install postcss-px-to-viewport -D

或者

yarn add postcss-px-to-viewport -D

配置

这一块的配置内容其实和vue中没什么区别,主要还是nuxt中配置项,要通过nuxt插件的形式使用

在nuxt.config.js中找到build

viewportWidth这里是根据设计稿来的,移动端一般的都是6s的375宽度为基础

build: {
    transpile: [/^element-ui/], // 安装的时候已经选择element ui
    postcss: {
      plugins: [
        require('postcss-px-to-viewport')({
          unitToConvert: "px",	// 需要转换的单位,默认为"px"
          viewportWidth: 1920,   // 视窗的宽度,对应pc设计稿的宽度,一般是1920
          // viewportHeight: 1080,// 视窗的高度,对应的是我们设计稿的高度
          unitPrecision: 3,		// 单位转换后保留的精度
          propList: [		// 能转化为vw的属性列表
            "*"
          ],
          viewportUnit: "vw",		// 希望使用的视口单位
          fontViewportUnit: "vw",		// 字体使用的视口单位
          selectorBlackList: [],	// 需要忽略的CSS选择器,不会转为视口单位,使用原有的px等单位。
          minPixelValue: 1,		// 设置最小的转换数值,如果为1的话,只有大于1的值会被转换
          mediaQuery: false,		// 媒体查询里的单位是否需要转换单位
          replace: true,		// 是否直接更换属性值,而不添加备用属性
          exclude: /(\/|\\)(node_modules)(\/|\\)/,		// 忽略某些文件夹下的文件或特定文件,例如 'node_modules' 下的文件
        })
      ]
    }
}

在vue中配置

在vue中配置可以看我的这篇文章

从零开始在vue-cli4配置自适应vw布局

参考资料

Nuxt.js 引入postcss-px-to-viewport

配置vuex方式一 | 普通方式

1-1. store下建立index.js文件

1-2. index.js内构建store内容

import Vue from 'vue'
import Vuex from 'vuex'
import createLogger from 'vuex/dist/logger'

Vue.use(Vuex)

const debug = process.env.NODE_ENV !== 'production'

export const state = () => ({
  barIndex: '1'
})

export const getters = {
  barIndex: state => state.barIndex
}

export const mutations = {
  CHANGE_INDEX (state, value) {
    state.barIndex = value
    window.sessionStorage.setItem('barIndex', value)
  }
}

export const strict = debug

export const plugins = debug ? [createLogger()] : []

// Classic(不建议使用): store/index.js返回创建Vuex.Store实例的方法。
// const store = new Vuex.Store({
//   state: {
//     barIndex: '1'
//   },
//   getters: {
//     barIndex: state => state.barIndex
//   },
//   mutations: {
//     CHANGE_INDEX (state, value) {
//       state.barIndex = value
//       window.sessionStorage.setItem('barIndex', value)
//     }
//   },
//   strict: debug,
//   plugins: debug ? [createLogger()] : []
// })
// export default () => store

配置vuex方式二 | 模块化

2-1. 目录结构

2-2. 状态内容

2-2-1. index

初始化store,指定vuex不同状态对应的文件

import Vue from "vue";
import Vuex from "vuex";
import * as actions from "./actions";
import * as getters from "./getters";
import state from "./state";
import mutations from "./mutations";
import createLogger from "vuex/dist/logger"; // 打印日志

Vue.use(Vuex);

// 设置只有在开发环境的时候才打印日志
const debug = process.env.NODE_ENV !== "production";

export default new Vuex.Store({
  actions,
  getters,
  state,
  mutations,
  strict: debug,
  plugins: debug ? [createLogger()] : []
});

2-2-2. state

官网解释

可以理解为vuex存储的数据地方,是一个数据存储中心

const state = {
  status: "",
};

export default state;

2-2-3. getters

官网解释

可以理解为更方便的调用state里面的属性

export const status = state => state.status;

一般使用辅助函数mapGetters来获取state里面定义的数据,可以通过this.status直接使用

computed: {
    // 使用对象展开运算符将 getter 混入 computed 对象中
    ...mapGetters([
      'status',
      // ...
    ])
  }

2-2-4. mutation-types

官网解释

可以理解为方便后期维护和多人合作,定义了之后用在mutations中

export const SET_STATUS = "SET_STATUS";

2-2-5. mutations

官网解释

可以理解为,在mutations状态中,去同步操作state里面定义的属性


import * as types from "./mutation-types";

// payload就是通过mapMutations函数传过来的数据number1
const mutations = {
  [types.SET_STATUS](state, payload) {
    state.status = payload;
  }
};

export default mutations;

一般使用辅助函数mapMutations来使用

methods: {
    // 引入
    ...mapMutations({
      set_status: 'SET_STATUS'
    }),
    // 使用
    this.set_status('number1')
}

2-2-5. actions

官网解释

也就是说,actions里面执行的是异步操作,执行完后,再把数据通过mutations同步到state的数据中


import * as types from "./mutation-types";

export const actionsSetdataInfo = ({ commit }, data) => {
// 这里可以调用接口,当数据返回后,再通过commit把数据同步到mutations里面
  commit(types.SET_STATUS, data);
};

配置默认模板

vue里面有一个app.vue,可以写布局

nuxt里面有一个layouts目录的可以自定义布局

我写的是需要在每一个组件的开头导入公共组件nav-bar

我们只需要在defalult.vue,这个页面中导入我们之前写的公共组件就行了

<template>
  <div>
    <nav-bar />
    <nuxt />
  </div>
</template>

<script>
import navBar from '../components/nav_bar'
export default {
  components: {
    navBar
  }
}
</script>

这样就实现了,一个公共导航栏的布局

配置axios代理接口

进入nuxt.config页面

axios: {
    // baseURL: 'https://www.abc.cn',
    prefix: '/api', // 给url加一个前缀'/api'
    credentials: true, // 跨域请求是否使用凭证
    proxy: true // 开启代理
  },
proxy: {
'/api': {
  target: 'https://www.abc.cn/api', // 因为我的接口都是/api开头,所以这里要加上api
  pathRewrite: {
    '^/api': '/', // 将 /api 替换掉成'/',这样最好的请求地址就变成了https://www.abc.cn/api
    changeOrigin: true // 是否跨域
  }
}
},

配置axios拦截器

前提是在用脚手架安装nuxt的时候,必须要选了安装插件axios

1.nuxt.config配置

在plugins配置axios

plugins: [
    '@/plugins/element-ui',
    '@/plugins/axios'
],

2. plugins文件夹内添加axios.js

export default function ({ $axios, redirect }, inject) {
   )
  // 请求回调
  // $axios.onRequest((config) => {
  //   console.log('Making request to ' + config.url)
  // })
  
  // 请求回调
  $axios.interceptors.request.use(
    (config) => {
      // do something before request is sent
      return config
    },
    (error) => {
      // do something with request error
      return Promise.reject(error)
    }
 
  // 返回回调 
  $axios.interceptors.response.use((response) => {
    if (response.data.success === false) {
      console.log('请求失败')
      return
    }
    console.log(response)
    return response
  }, (err) => {
    // if (err && err.response) {
    //   switch (err.response.status) {
    //     case 400: err.message = '请求错误(400)'; break
    //     case 401: return history.push('/login')
    //     case 403: err.message = '拒绝访问(403)'; break
    //     case 404: err.message = '请求出错(404)'; break
    //     case 408: err.message = '请求超时(408)'; break
    //     case 500: err.message = '服务器错误(500)'; break
    //     case 501: err.message = '服务未实现(501)'; break
    //     case 502: err.message = '网络错误(502)'; break
    //     case 503: err.message = '服务不可用(503)'; break
    //     case 504: err.message = '网络超时(504)'; break
    //     case 505: err.message = 'HTTP版本不受支持(505)'; break
    //     default: err.message = `连接出错(${err.response.status})!`
    //   }
    // } else {
    //   err.message = '连接服务器失败!'
    // }
    // message.error(err.message)
    return Promise.reject(err)
  })
  // 错误回调
  $axios.onError((e) => {
    const code = parseInt(e.response && e.response.status)
    console.log('bug', code)
    switch (code) {
      case 400:
        redirect('/error')
        // error({ statusCode: 400, message: 'not found' })
        break
      case 600:
        redirect({
          path: '/error'
        })
        // error({ statusCode: 600, message: '用户未验证' })
        break
      // case 401: return history.push('/login'); break;
      // case 403: err.message = '拒绝访问(403)'; break;
      // case 404: err.message = '请求出错(404)'; break;
      // case 408: err.message = '请求超时(408)'; break;
      // case 500: err.message = '服务器错误(500)'; break;
      // case 501: err.message = '服务未实现(501)'; break;
      // case 502: err.message = '网络错误(502)'; break;
      // case 503: err.message = '服务不可用(503)'; break;
      // case 504: err.message = '网络超时(504)'; break;
      // case 505: err.message = 'HTTP版本不受支持(505)'; break;
      // default: err.message = `连接出错(${err.response.status})!`;
    }
  })
}

但是有个问题,如果是服务端访问在axios里面是用不了element 组件的。。。尝试解决中

3. 在layouts增加error.vue

服务端进入页面报错后,会进入layouts默认布局

<template>
  <div class="container">
    <nuxt-link to="/">
      Home page
    </nuxt-link>
  </div>
</template>
<script>
export default {
  props: ['error'],
  layout: 'blog'
}
</script>

4. 在pages文件夹下增加error.vue

客户端端进入页面报错后,会进入pages下的error页面

<template>
  <div class="container">
    <nuxt-link to="/">
      Home page
    </nuxt-link>
  </div>
</template>
<script>
export default {
  props: ['error'],
  layout: 'blog'
}
</script>

5. 在页面的中使用asyncData来获取数据

async asyncData ({ $axios }) {
    const aRes = {}
    const res = await $axios.$post('/yourAPi', aRes)
    console.log(res)
}

参考资料

Nuxt.js实战和配置

Nuxt爬坑

Nuxt生命周期

export default {
  middleware () {}, //服务端
  validate () {}, // 服务端
  asyncData () {}, // 服务端和客户端都会执行
  fetch () {}, // store数据加载
  beforeCreate () {  // 服务端和客户端都会执行},
  created () { // 服务端和客户端都会执行 },
  beforeMount () {}, // 客户端
  mounted () {} // 客户端
}

部署Nuxt到线上

部署到线上问题真的多,头秃

打包项目

npm run build

发布项目

配置node端口

在package.json中配置,和name平级配置一个config属性

配置0.0.0.0或者127.0.0.1都代表服务器本地ip

"name": "project_ssr",
"version": "1.0.0",
"description": "My ultimate Nuxt.js project",
"author": "mikoMa",
"private": true,
"config": {
    "nuxt": {
        "host": "0.0.0.0",
        "port": "3333"
    }
},

发布到服务器

把本地文件的.nuxt,static,package.json,package-lock.json,nuxt.config.js,这几个个文件夹或者文件放到服务器目录文件下

我是放在根目录,自己新建的web_ssr文件夹中

在服务中下包

需要部署node环境,这可以看我另外一篇

在web_ssr文件夹中执行命令

npm install

通过pm2启动node服务

安装pm2,可以看我另外一篇文章

执行监听命令

pm2 start npm --name "project_ssr" -- run start --watch
  • 注意:这里的name对应的是package.json中的项目名称

如图所示

mac系统.nuxt找不到

命令如下,如果发布之后,觉得碍事,要继续隐藏该类型文件,则把最后到true改成false就行

defaults write com.apple.finder AppleShowAllFiles -bool true

配置nginx

nuxt官网有各种的发布方法,因为用的是nginx发布,所以就用nginx官方的改下,不要用vue版本的,一点用都没有

官方文档-nginx代理

server {
    listen          2222;             # 端口号,你自己定就行,不过一定要打开在云平台上配置安全组,同时防火墙要打开
    server_name     localhost;    # 如果是域名,就上域名,我这里是ip地址访问
    # 因为客户端没配置压缩,这里就注释掉了
    #gzip            on;
    #gzip_types      text/plain application/xml text/css application/javascript;
    #gzip_min_length 1000;

    location / {
        expires off;

        proxy_redirect                      off;
        proxy_set_header Host               $host;
        proxy_set_header X-Real-IP          $remote_addr;
        proxy_set_header X-Forwarded-For    $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto  $scheme;
        proxy_read_timeout          1m;
        proxy_connect_timeout       1m;
        proxy_pass                          http://127.0.0.1:3333; #默认是3000,因为服务器上还有一个服务,所以就改了端口 
    }
}

配置后重启nginx

nginx -s reload
nginx -s reopen

访问页面出现500,404,

一般都是配置出了问题,不需要vue里面的防止404,也不需要配置图片缓存