nuxt.js + element 环境搭建和SSR部署

3,311 阅读7分钟

# 前言 为什么要用nuxt呢?可能绝大部分原因都是运用nuxt来解决我们spa项目seo不友好问题,nuxt可以在服务端将页面渲染成html,再返回给浏览器,spa项目是返回大量js文件,通过js文件在客户端浏览器动态渲染html。js动态渲染模式百度爬虫是无法爬取到渲染完成后的内容的。所以在做有seo需求的项目时,nuxt可能是一个不错的选择。

优点

  • 有利于SEO。
  • 首屏加载速度快。因为SPA引用需要在首屏获取所有资源,而服务器端渲染直接拿了成品展示出来就行了。
  • 无需占用客户端资源。解析模板工作交给服务器完成,对于客户端资源占用更少,尤其是移动端,也可以更省电。

缺点

  • 所有用户的页面请求都需要在服务端渲染,增加服务端压力。
  • 学习成本相对较高

安装运行

npx在NPM版本5.2.0默认安装了

npx create-nuxt-app <项目名>

具体配置项可以参考 zh.nuxtjs.org/guide/insta…

笔者配置如下:

进入项目文件夹

cd <项目名>

安装依赖

 npm install

运行

 npm run dev

nuxt默认运行在3000端口,尝试访问localhost:3000,启动成功

按需加载安装element.ui

项目中可能会使用到element中的一些组件,新建项目时默认在nuxt集成element的话,打包后会将整个element包全部打包进我们的项目,对首屏加载速度不太友好,我们通过配置实现按需加载element组件,只将需要用到的element组件打包进我们的项目。

1. 本地安装按需加载插件babel-plugin-component(已安装的忽略次步骤)

 npm install babel-plugin-component -D-S

2.配置nuxt.config.js

// nuxt.config.js

build: {
  ...other
  // 按需引入element-ui
  babel: {
    plugins: [
      [ "component", 
        {
          "libraryName": "element-ui",
          "styleLibraryName": "theme-chalk"
        }
      ] 
    ] 
  },
},

3.修改plugins/element-ui.js

在第一步创建nuxt项目时候我选择了 Element做为ui,所以在创建的新项目里有这个文件plugins/element-ui.js,如果在创建项目的时候没有选择element作为ui框架的话只需要手动创建这个element.js,并在nuxt.config.js新增配置 plugins: ['@/plugins/element-ui'],

//element.js

import Vue from "vue";
import {
  Button,
} from "element-ui";

Vue.use(Button);

4.在index.vue文件中使用

<el-button type="" >测试</el-button>

axios引入

在创建项目时默认选择安装了@nuxtjs/axios,这里@nuxtjs/axios的使用方法与axios一致,所以这里不在需要另外安装axios(在package.json中名称为@nuxtjs/axios,实际引入我们在项目中通过import AXIOS from 'axios' 引入即可)

封装 新增文件:@/api/index.js

1.安装@nuxtjs/axios

cnpm install @nuxtjs/axios -S-D

安装封装axios时用到的一个json处理包

cnpm install qs -S-D

安装一个常用函数库(后面封装axios有用到)

cnpm install loadsh -S-D

在assets/js下新增lodash.js

// lodash.js

import _merge from 'lodash/merge'
import _cloneDeep from 'lodash/cloneDeep'
import _maxBy from 'lodash/maxBy'
import _minBy from 'lodash/minBy'
import _throttle from 'lodash/throttle'
import _debounce from 'lodash/debounce'
import _get from 'lodash/get'
import _uniqBy from 'lodash/uniqBy'
import _uniq from 'lodash/uniq'

export const merge = _merge
export const cloneDeep = _cloneDeep
export const maxBy = _maxBy
export const minBy = _minBy
export const throttle = _throttle
export const debounce = _debounce
export const get = _get
export const uniqBy = _uniqBy
export const uniq = _uniq

在assets新增api目录并在此目录下新建两个文件index.js、service.js

//index.js

import AXIOS from 'axios'
import { merge } from '@/assets/js/lodash'
import qs from 'qs'

//后台接口地址
const axios = AXIOS.create({
  baseURL: 'localhost:8080'
})

// 请求时的拦截器
axios.interceptors.request.use(
  (config) => {
    //在这里写发起请求前的拦截
    return config
  },
  (error) => {
    console.warn('axios request', error)
    return Promise.reject(error)
  }
)

axios.interceptors.response.use(
  (response) => {
    //在这里写响应请求拦截
    return response
  },
  (error) => {
    console.warn('axios response', error, error.response)
    return Promise.reject(error)
  }
)

const httpServer = (opts, data) => {
  // http默认配置
  const commonOpts = {}
  const options = merge({}, commonOpts, opts)
  if (options.method === 'get') {
    options.params = data
  } else if (
    options.headers &&
    /application\/json/.test(options.headers['content-type'] || '')
  ) {
    options.data = data
  } else {
    options.data = qs.stringify(data)
  }

  return axios(options)
}

export default httpServer
export { axios }

在service.js中统一定义我们的请求方法

//service.js
import { axios } from './index'

export function getQueryInfoDetail(page, size) {
  const path = '/auth/role/list'
  return axios({
    url: path,
    method: 'get',
    params: {
      page,
      size
    }
  })
}

使用

import { getQueryInfoDetail } from "@/assets/api/service.js";
getQueryInfoDetail(1, 10).then(res => {
    console.log(res);
});

vuex 引入

nuxt默认就安装有vuex模块(diss一下:官网上说需要创建项目配置才会有,我也没见创建项目时候让配置vuex。反正默认就是有(ง •_•)ง )

在store/index.js

//index.js

export const state = () => ({
  counter: 0
})

export const mutations = {
  increment (state) {
    state.counter++
  }
}

这里的语法和vue还是有一点点区别,这里我们只需要将vuex的中state、mutations、action等使用export暴露出去,nuxt会帮我们配置好,这里值得注意下的是,state的值应该始终是function,是为了避免返回引用类型,会导致多个实例相互影响,另外vuex默认index.js为根模块,我们需要建立子模块的话只需要在store目录下新增js文件,例如建立user.js子模块,专门用来管理用户信息。

//user.js

export const state = () => ({
  userInfo: {
    name: '',
    avatar: '',
  },
})

export const mutations = {
  setUserInfo (state, userInfo) {
    state.userInfo = userInfo;
  },
  clearUserInfo(state) {
    state.userInfo = {}
  },
  clearAllUserData(state) {
    state.userInfo = {};   
  },
}

export const getters = {
  userInfo(state) {
    return state.userInfo;
  }
}

在使用字模块的时候需要带上子模块名称(store.commit('user/clearAllUserData')) 具体参考zh.nuxtjs.org/guide/vuex-…

配置filters过滤器

plugins目录下新建filters.js

//filters.js

import Vue from 'vue'
Vue.filter('capitalize', function (value) {
  if (!value) return ''
  value = value.toString()
  return value.charAt(0).toUpperCase() + value.slice(1)
})

nuxt.config.js中新增

plugins: [
    { src: '~/plugins/filters', ssr: false }
],

SSR方式部署到服务器

1.打包项目(会在.nuxt目录下生成dist文件夹)

npm run build

2.将项目.nuxt、static、nuxt.config.js、package.json文件上传至服务器(推荐使用winScp)

3.启动项目(推荐Xshell)

首先进入到我们刚刚上传项目的目录

安装依赖

npm install

启动

npm  run start

控制台出现如下,则启动成功

nuxt默认运行在3000端口,作者服务器上3000端口被占用了,所以改成了在3333端口上。更改端口只需要在pageage.json新增"config": { "nuxt": { "host": "0.0.0.0", "port": "3333" } }

测试访问,我们通过 服务器外网ip:端口号访问项目(服务器记得配置端口号的安区组)

问题分析:

问题1: xshell退出当前状态或者关闭时,nuxt服务也被关闭了

解决它: 使用pm2 守护nuxt服务

安装pm2

npm install pm2 –g

pm2 启动nuxt (项目名就是package.json里的name字段)

pm2 start npm --name '项目名' -- run start 

再次访问 服务器外网ip:端口号 又能正常访问了, 此时我们退出Xshell时,项目还是能正常的访问。

关闭nuxt方法: 使用pm2 list 查看项目的id, 然后通过pm2 stop id停止,或者通过pm2 delete id删除

pm2基本命令

  • pm2 list
  • pm2 start id
  • pm2 stop id
  • pm2 delete id
  • pm2 log

问题2:分环境部署(不需要分环境部署的可忽略)

Nuxt 中有process.env.NODE_ENV来区分环境,但默认情况下,这个变量的值要么是 production,要么是 development,分别表示生产环境和开发环境,我们需要的环境可能不止这两种,我们还需要区分测试环境等等,我们先区分打包后的项目中的process.env.NODE_ENV区分,也就是说在我们源码中能通过process.env.NODE_ENV区分 开发环境和测试环境、生成环境

安装cross-env

npm install cross-env –S-D

在package.json中scripts中添加测试和正式的打包命令

"build:test": "cross-env NODE_ENV=test nuxt build",
"build:pro": "cross-env NODE_ENV=production nuxt build",

nuxt.config.js 增加如下配置

env: {
    NODE_ENV: process.env.NODE_ENV
},

在项目中使用,区分开发、测试、生成环境分别对应后台接口地址,项目中我们可以通过process.env.NODE_ENV全局变量访问打包命令中定义的变量NODE_ENV的值

举例区分环境使用不同的后台接口地址

项目根目录下新建config.js

config.js需要在跟.nuxt、static、nuxt.config.js、package.json一起上传到服务器

//config.js
if (process.env.NODE_ENV == "development") {
  env = "dev"; //本地开发环境
} else if (process.env.NODE_ENV == "test") {
  env = "test"; //test环境
} else if (process.env.NODE_ENV == "production") {
  env = "production"; //正式环境
}
//接口地址
const baseURLs = {
  dev: "https://api.test",   //测试环境后台接口地址
  test: "https://api.test",  //测试环境后台接口地址
  production: "https://api"  //正式环境后台接口地址
};
const config = {
  baseURL: baseURLs[env],
};
module.exports = config;

修改上文引入axios模块中的assets/api/index.js,替换aixos的baseUrl为config.js的动态设置的baseurl

//index.js

import {baseURL} from '@/config'

import AXIOS from 'axios'
import { merge } from '@/assets/js/lodash'
import qs from 'qs'


//后台接口地址
const axios = AXIOS.create({
  baseURL: baseURL
})

// 请求时的拦截器
axios.interceptors.request.use(
  (config) => {
    //在这里写发起请求前的拦截
    return config
  },
  (error) => {
    console.warn('axios request', error)
    return Promise.reject(error)
  }
)

axios.interceptors.response.use(
  (response) => {
    //在这里写响应请求拦截
    return response
  },
  (error) => {
    console.warn('axios response', error, error.response)
    return Promise.reject(error)
  }
)

const httpServer = (opts, data) => {
  // http默认配置
  const commonOpts = {}
  const options = merge({}, commonOpts, opts)
  if (options.method === 'get') {
    options.params = data
  } else if (
    options.headers &&
    /application\/json/.test(options.headers['content-type'] || '')
  ) {
    options.data = data
  } else {
    options.data = qs.stringify(data)
  }

  return axios(options)
}

export default httpServer
export { axios }

之后我们通过npm run build:test构建后上传的项目,启动后axios的baseUrl就是测试环境的接口地址。

最后

项目地址源码地址: github.com/chenyuhuan1…

谢谢你的观看,欢迎各位的指正,最后麻烦留下你宝贵的一赞┗( ▔, ▔ )┛,如有疑问请在下方留言。