记一次nuxt重构项目经历

初始化项目

npx create-nuxt-app <项目名>

配置nuxt.config
const webpack = require('webpack');
module.exports = {
  mode: 'universal',
  /*
  ** Headers of the page
  */
  server: {
    port: 8889, // default: 3000
    host: '0.0.0.0', // default: localhost
  },
  // other configs
  head: {
    title: '......',
    meta: [{
      charset: 'utf-8'
    },
    {
      name: 'viewport',
      content: 'width=device-width,initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no'
    },
    {
      hid: "keywords",
      name: "keywords",
      content: "......"
    }
    ],
    link: [{
      rel: 'icon',
      type: 'image/x-icon',
      href: '/favicon.ico'
    }
    ],
  },
  loading: { color: '#000' },
  css: [
    'element-ui/lib/theme-chalk/index.css',
    '@/assets/css/base.css',
    '@/assets/iconfont/iconfont.css'
  ],
  plugins: [
    '@/plugins/element-ui',
    '@/plugins/axios',
    '@plugins/echarts',
    {
      src: '@/plugins/bus',
      ssr: false
    },
  ],
  buildModules: [
  ],
  modules: [
    // Doc: https://axios.nuxtjs.org/usage
    '@nuxtjs/axios',
    'cookie-universal-nuxt',
    ['cookie-universal-nuxt', { alias: 'cookiz' }],
  ],
  axios: {
    proxy: true,
    baseURL:'......',
  },
  proxy: {
    "/Main": {
      target: '......',
    },
    "/main": {
      target: '......',
    },
  },
  router: {
    middleware: ['redirect'],
  },
  build: {
    transpile: [/^element-ui/],
    plugins: [
      new webpack.ProvidePlugin({
        $: 'jquery',
        jQuery: 'jquery',
        'window.jQuery': 'jquery'
      })
    ],
    extend(config, ctx) {
    }
  }
}
 head: {
  title: '百姓店铺',
  meta: [
     { charset: 'utf-8' },
     { name: 'viewport', content: 'width=device-width, initial-scale=1' },
     { name: 'applicable-device', content: 'pc,mobile' },
  ],
  link: [
    { rel: 'icon', type: 'image/x-icon', href: '/logoicon.ico' }//地址栏小图标的引入
     { rel: 'stylesheet', type: 'text/css', href: 'https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css'},
  ],
  script: [
     {src: 'https://code.jquery.com/jquery-3.1.1.min.js'},
     {src: 'https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js'}
  ]
}
css: [ //该配置项用于定义应用的全局(所有页面均需引用的)样式文件、模块或第三方库。
    'element-ui/lib/theme-chalk/index.css',//在创建项目的时候安装了elememt插件,这里自动引入插件的默认样式
    '@/assets/css/reset.css',   //引入assets下的reset.css全局标签重置样式
    '@/assets/main.css'  //引入全局的动画样式
  ]
// axios.js
import qs from 'qs'
import Bus from "../plugins/bus.js";
import { setCookie, getCookie, delCookie } from "~/utils/common.js";


// axios相关配置
export default function ({ $axios, app }) {

    // 请求拦截器
    $axios.interceptors.request.use(
        config => {

            config.headers = {
                'X-Requested-With': 'XMLHttpRequest',
                'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
            }

         
            try {
                config.data["token"] = getCookie("zstoken")
            } catch (error) {
                try {
                    config.data["token"] = app.$cookies.get('zstoken')
                } catch (error) {
                    config.data["token"] = ''
                }
                
            }
            // console.log(getCookie("zstoken"))
            // console.log(app.$cookies.get('zstoken'))
            // console.log(config.data["token"])
            // console.log(config.data)

            config.data = qs.stringify(config.data)

            


            return config
        },
        error => {
            return Promise.reject(error)
        }
    )

    // 响应拦截器
    $axios.interceptors.response.use(
        response => {
            response = response.data

            if (response.code === 201) {

                try {
                    setCookie("zstoken", "", -1);
                    setCookie("phone", "", -1);
                    setCookie("uid", "", -1);
                    Bus.$emit("getTarget", {
                        type: "login",
                        from: "请求201"
                    });
                } catch (error) {
                }
               
            }
            return response
        },
        error => {
            return Promise.reject(error)
        }
    );
}
// bus.js
import Vue from 'vue'
export default new Vue()
// echarts.js
import Vue from 'vue'

//引入基本模板
let echarts = require('echarts/lib/echarts')
 
// 引入折线图等组件
require('echarts/lib/chart/line')

// 引入提示框和title组件,图例
require('echarts/lib/component/tooltip')
require('echarts/lib/component/title')
require('echarts/lib/component/legend')
require("echarts/lib/component/grid")
Vue.prototype.$echarts = echarts // 引入组件(将echarts注册为全局)
// element-ui.js
import Vue from 'vue'
import Element from 'element-ui'
import VueI18n from 'vue-i18n'
import enLocale from 'element-ui/lib/locale/lang/en'
import zhLocale from 'element-ui/lib/locale/lang/zh-CN'
Vue.use(VueI18n)

const messages = {
    en: {
      message: 'hello',
      ...enLocale // 或者用 Object.assign({ message: 'hello' }, enLocale)
    },
    zh: {
      message: '你好',
      ...zhLocale // 或者用 Object.assign({ message: '你好' }, zhLocale)
    }
  }

const i18n = new VueI18n({
    locale: 'zh', // set locale
    messages, // set locale messages
})

Vue.use(Element, {
    i18n: (key, value) => i18n.t(key, value)
})
  • modules
    该配置项允许您将Nuxt模块添加到项目中。
    例:我就在这里添加了项目中所用到的nuxt的请求,与获取cookie模块
 modules: [   // nuxt 模块扩展
    // Doc: https://axios.nuxtjs.org/usage
    '@nuxtjs/axios',
    'cookie-universal-nuxt',
    ['cookie-universal-nuxt', { alias: 'cookiz' }],
  ],
  • build
    Nuxt.js 允许你在自动生成的 vendor.bundle.js 文件中添加一些模块,以减少应用 bundle 的体积。如果你的应用依赖第三方模块,这个配置项是十分实用的。
    【webpack的相关配置可以在这配置】
    1、vendor配置 : 添加模块,配置只打包一次,减少应用bundle的体积。比如,如果你使用的不是@nuxtjs/axios,那么一般自己引入的axios这种ajax请求插件,基本每个页面都使用import引入,会导致打包时打包多次,这时需要配置,实现只打包一次。
 build: {
     vendor: ['axios', 'iview']
  }

2、plugins 配置 Webpack 插件
例如我项目中用到了jQuery

 plugins: [
      new webpack.ProvidePlugin({
        $: 'jquery',
        jQuery: 'jquery',
        'window.jQuery': 'jquery'
      })
    ],

具体配置可看官网:
https://zh.nuxtjs.org/guide/configuration

asyncData

你可能想要在服务器端获取并渲染数据。Nuxt.js添加了asyncData方法使得你能够在渲染组件之前异步获取数据
asyncData方法会在组件(限于页面组件)每次加载之前被调用。它可以在服务端或路由更新之前被调用。在这个方法被调用的时候,第一个参数被设定为当前页面的上下文对象,你可以利用 asyncData方法来获取数据并返回给当前组件。
注意: 由于asyncData方法是在组件 初始化 前被调用的,所以在方法内是没有办法通过 this 来引用组件的实例对象。

asyncData (context) {
  
}

上下文对象
context 的可用属性一览:
https://zh.nuxtjs.org/api/context


  async asyncData({ $axios }) {
  	try{
			let res = await $axios.post("/Main/action/Dashboard/Homepage/getNewsList", {
		      type: 3,
		      page: 1,
		      size: 6
		    });
		
		    return {
		      articleData: res.code == 200 ? res.data : [],
		      total: res.code == 200 ? res.count : 0
		    };
	}catch(err){}
    
  }
async asyncData({ $axios, query }) {

		try{
			let [
		      researchNewsList,
		      homepageList,
		      articleData,
		    ] = await Promise.all([
		      $axios.post("/Main/action/Dashboard/Homepage/getNewsList", {
		      }),
		      $axios.post("/Main/action/Dashboard/Homepage/HomepageList", {
		      }),
		      $axios.post("/Main/action/Dashboard/Homepage/get_resource", {
		      }),
		    ]);
		    return {
				researchNewsList:researchNewsList.code == 200 ? researchNewsList.data : [],
		      asActivity:homepageList.data.Activity.code == 200 ? homepageList.data.Activity.data : [],
		      articleData: articleData.code == 200 ? articleData.data : {},
		    };
		
		}catch(err){}
    
  }
head()

使用 head 方法设置当前页面的头部标签。
在 head 方法里可通过 this 关键字来获取组件的数据,你可以利用页面组件的数据来设置个性化的 meta 标签。

  head() {
    return {
      title: `${this.title}='标题'`,
      meta: [
        {
          hid: "keywords",
          name: "keywords",
          content: `${this.words}+'关键词'`
          }`
        },
       	{ hid: 'description', name: 'description', content: `${this.desc}+'页面内容的简介'` }
      ]
    };
  },
配置错误页面
  • layouts文件夹下
// error.vue
<template>
    <div style="background-color: #fff;">
        <div class="err-container">
            <p class="err-img">
                <img src="../assets/404.png" alt="">
            </p>
            <p class="err-msg">对不起!您访问的页面出错了!</p>
            <p class="err-countdown">
                <span>{{ down }}S</span>自动返回首页
            </p>
            <p class="err-home">
                <nuxt-link to="/" class="gohome">返回首页</nuxt-link>
            </p>
        </div>
    </div>
</template>

<script>
export default {
  props: ["error"],
  data() {
    return {
      down: 4,
      timer: null
    };
  },
  mounted() {
    var _this = this;
    this.timer = setInterval(() => {
      if (_this.down > 0) {
        _this.down--;
      } else {
        clearInterval(_this.timer);
        _this.$router.push("/");
      }
    }, 1000);
  }
};
</script>
<style scoped lang='less'>
.err-container {
  width: 1200px;
  height: auto;
  text-align: center;
  margin: 0 auto;
  background-color: #fff;
  .err-img {
    position: relative;
    left: -50px;
    padding-top: 70px;
    padding-bottom: 50px;
  }
  .err-msg {
    font-size: 18px;
    margin-bottom: 20px;
  }
  .err-countdown {
    font-size: 14px;
    line-height: 3;
    span {
      color: #388df7;
    }
  }
  .err-home {
    padding-bottom: 100px;
    a {
      display: inline-block;
      width: 160px;
      height: 40px;
      line-height: 40px;
      font-size: 14px;
      border-radius: 20px;
      background-color: #078dff;
      color: #fff;

      &:hover {
        background-color: #0079e0;
      }
    }
  }
}
</style>
vuex
  • store文件夹下
// index.js
export const state = () => ({
    counter: 0
  })
  export const mutations = {
    increment (state) {
      state.counter++
    }
  }
发布部署

Nuxt.js 提供了两种发布部署应用的方式:服务端渲染应用部署 和 静态应用部署。

  • 静态应用部署
    Nuxt.js 可依据路由配置将应用静态化,使得我们可以将应用部署至任何一个静态站点主机服务商。
    npm run generate
    这个命令会创建一个 dist 文件夹,所有静态化后的资源文件均在其中
    注意: 使用 nuxt generate 静态化应用的时候, 传给 asyncData() 和 fetch() 方法的上下文对象 不会包含 req 和 res 两个属性。
  • 服务端渲染应用部署
    部署 Next.js 服务端渲染的应用不能直接使用 next 命令,而应该先进行编译构建,然后再启动 Next 服务,官方通过以下两个命令来完成:
    next build next start
    项目在本地开发完毕,npm run buid 打包
    在这里插入图片描述> 这几个文件上传到服务器空间
    然后再服务器空间 npm install 安装依赖,npm run start 运行
  • pm2开启进程守护
    进入对应的应用目录,执行以下命令:
    pm2 start npm --name “项目名称” – run start
pm2 简单介绍

pm2是nodejs的一个带有负载均衡功能的应用进程管理器的模块,类似有Supervisor,forever,用来进行进程管理。

  • 安装:
    npm install pm2 -g
  • 启动:
    pm2 start app.js
    pm2 start app.js --name my-api #my-api为PM2进程名称
    pm2 start app.js -i 0 #根据CPU核数启动进程个数
    pm2 start app.js --watch #实时监控app.js的方式启动,当app.js文件有变动时,pm2会自动reload
  • 查看进程:
    pm2 list
    pm2 show 0 或者 # pm2 info 0 #查看进程详细信息,0为PM2进程id
  • 监控:
    pm2 monit
  • 停止:
    pm2 stop all #停止PM2列表中所有的进程
    pm2 stop 0 #停止PM2列表中进程为0的进程
  • 重载:
    pm2 reload all #重载PM2列表中所有的进程
    pm2 reload 0 #重载PM2列表中进程为0的进程
  • 重启:
    pm2 restart all #重启PM2列表中所有的进程
    pm2 restart 0 #重启PM2列表中进程为0的进程
  • 删除PM2进程:
    pm2 delete 0 #删除PM2列表中进程为0的进程
    pm2 delete all #删除PM2列表中所有的进程
  • 日志操作:
    pm2 logs [–raw] #Display all processes logs in streaming
    pm2 flush #Empty all log file
    pm2 reloadLogs #Reload all logs
  • 升级PM2:
    npm install pm2@lastest -g #安装最新的PM2版本
    pm2 updatePM2 #升级pm2
  • 更多命令参数请查看帮助:
    pm2 --help
遇到的问题
  • 1、在asyncData里发送请求,会在node服务端发送请求拿到数据进行拼接渲染,而我的登录状态是基于cookie做的,axios怎么在服务端获取cookie?

使用cookie-universal-nuxt插件

安装:npm i --save cookie-universal-nuxt

在nuxt.config.js中进行配置
{
modules: [
// Simple usage
‘cookie-universal-nuxt’,
// With options
[‘cookie-universal-nuxt’, { alias: ‘cookiz’ }],
]
}

在plugins/axios.js 中使用
在这里插入图片描述

  • 2、如何进行路由拦截?

方法一:使用middleware中间件
在middleware文件夹下创建redirect.js

 export default function ({ route, redirect,app}) {
    if(route.path=='/manage'){
        if(!app.$cookies.get('zstoken')){
            redirect('/')
        }
    }
  }
// nuxt.config.js页面进行配置
 router: {
    middleware: ['redirect'],
  }

方法二: 在plugins文件夹下创建router.js

export default ({ app, store }) => {
 app.router.beforeEach((to, from, next) => {
   // 设置条件
   console.log(to, from)
   next()
 })
}

在nuxt.config.js的plugins里配置

 plugins: [
    '@/plugins/element-ui',
    '@/plugins/router' // 路由守卫
  ],
  • 3、由于项目中发送请求没有进行catch()操作,控制台提示不友好
// 捕获Promise未处理的错误
// process.on('unhandledRejection', (reason, p) => {
//   // console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
//   // application specific logging, throwing an error, or other logic here
// });
参考文章:

UnhandledPromiseRejectionWarning---不能言喻的痛
英文资料
promise-不使用catch出现warning的原因
Node.js 代码阅读笔记系列 — process.nextTick() 的实现
如何预防你的node.js程序崩溃(译)
《ES6标准入门》(六)之Promise对象2——then()和catch()方法

  • 4、项目重构后网站响应速度过慢,需要优化

使用 lru-cache 包进行缓存

  • 接口缓存
import LRU from "lru-cache";
import md5 from "md5";
const CACHED = new LRU({
  max: 100, // 缓存队列长度
  maxAge: 1000 * 60 // 缓存时间
});

  async asyncData({ $axios, redirect }) {
    try {
      if (CACHED.has(md5("viewpointlist"))) {
        // console.log('缓存命中')

        // 缓存命中
        let obj = JSON.parse(CACHED.get(md5("viewpointlist")));

        return {
          consensus: obj.consensus.code == 200 ? obj.consensus.data : [],
          littleNews: obj.littleNews.code == 200 ? obj.littleNews.data : [],
          reportData: obj.reportData.code == 200 ? obj.reportData.data : [],
          hisList: obj.hisList.code == 200 ? obj.hisList.data : [],
          interview: obj.interview.code == 200 ? obj.interview.data : []
        };
      }

      // console.log('没命中,请求')

      let [
        consensus,
        littleNews,
        reportData,
        hisList,
        interview
      ] = await Promise.all([
        $axios.post("/Main/action/Dashboard/Homepage/getNewsList", {
          // 行业资讯
          type: 2,
          ald_type: 2,
          size: 8,
          page: 1
        }),
        $axios.post("/Main/action/Dashboard/Homepage/newsFlash", {
          // 快讯
          page: 1
        }),
        $axios.post("/Main/action/Dashboard/Homepage/getNewsList", {
          // 研究院报告
          type: 2,
          ald_type: 3,
          size: 8,
          page: 1
        }),
        $axios.post("/Main/action/Dashboard/Homepage/getNewsList", {
          // 历史榜单
          type: 2,
          ald_type: 4,
          size: 8,
          page: 1
        }),
        $axios.post("/Main/action/Dashboard/Homepage/getNewsList", {
          // 小访谈
          type: 2,
          ald_type: 5,
          size: 8,
          page: 1
        })
      ]);

		// 设置缓存
      CACHED.set(
        md5("viewpointlist"),
        JSON.stringify({
          consensus,
          littleNews,
          reportData,
          hisList,
          interview
        })
      );

      return {
        consensus: consensus.code == 200 ? consensus.data : [],
        littleNews: littleNews.code == 200 ? littleNews.data : [],
        reportData: reportData.code == 200 ? reportData.data : [],
        hisList: hisList.code == 200 ? hisList.data : [],
        interview: interview.code == 200 ? interview.data : []
      };
    } catch (error) {
      console.log("服务端渲染接口错误");
      return redirect("/404");
    }
  },
  • 页面缓存
    使用中间件
    在这里插入图片描述
    在nuxt.config.js中配置
    在这里插入图片描述
// page-cache.js

const LRU = require('lru-cache')	
let cachePage = new LRU({	
max: 100, // 缓存队列长度	
maxAge: 1000 * 60 * 30 // 缓存1分钟	
})	
export default function(req, res, next){	
 let url = req._parsedOriginalUrl	
 let pathname = url.pathname	
 // 通过路由判断,只有首页才进行缓存

   // console.log(pathname)

 if (['/'].indexOf(pathname) != -1 ) {	
   const existsHtml = cachePage.get('homeData')	
   if (existsHtml) {	
   
       // console.log('页面缓存')
     return res.end(existsHtml.html, 'utf-8')	
   } else {
       // console.log('进行缓存')	
     res.original_end = res.end	
     // 重写res.end	
     res.end = function (data) {	
       if (res.statusCode === 200) {	
        // 设置缓存	
        cachePage.set('homeData', { html: data})	
       }	
       // 最终返回结果	
       res.original_end(data, 'utf-8')	
     }	
   }	
 }	
 next()	
}
  • 组件缓存-但是我没在这个项目中用
参考文章

Vue在 Nuxt.js 中重定向 404 页面的方法
基于Nuxt.js项目的服务端性能优化与错误检测(容错处理)
基于Nuxt.js项目的服务端性能优化与错误检测(容错处理)
基于Nuxt.js项目的服务端性能优化与错误检测(容错处理)
nuxt.js 缓存实践
简述Nuxt.js
Nuxt实现的SSR页面性能优化的进一步探索与实践
nuxt缓存实践

  • 5、上线后项目不兼容ie,控制台报错

在这里插入图片描述
在这里插入图片描述

vendors.app.js这个文件里面报错,这个文件是webpack打包的引用的第三方包的集合

  • 刚开始以为项目没有es6转es5,所以使用了babel进行了转换
// .babelrc

// {
// 	// 此项指明,转码的规则
// 	"presets": [
// 	// env项是借助插件babel-preset-env,下面这个配置说的是babel对es6,es7,es8进行转码,并且设置amd,commonjs这样的模块化文件,不进行转码
// 	["env", { "modules": false }],
// 	// 下面这个是不同阶段出现的es语法,包含不同的转码插件
// 	"stage-2"
// 	],
// 	// 下面这个选项是引用插件来处理代码的转换,transform-runtime用来处理全局函数和优化babel编译
// 	"plugins": ["transform-runtime"],
// 	// 下面指的是在生成的文件中,不产生注释
// 	"comments": false,
// 	// 下面这段是在特定的环境中所执行的转码规则,当环境变量是下面的test就会覆盖上面的设置
// 	"env": {
// 	// test 是提前设置的环境变量,如果没有设置BABEL_ENV则使用NODE_ENV,如果都没有设置默认就是development
// 	"test": {
// 	"presets": ["env", "stage-2"],
// 	// instanbul是一个用来测试转码后代码的工具
// 	"plugins": ["istanbul"]
// 		}
// 	 }
// 	}
{
	"presets": [
	  [
		"@babel/env",
		{
		  "targets": {
			"ie":9,
			"edge": "17",
			"firefox": "60",
			"chrome": "67",
			"safari": "11.1"
		  },
		  "useBuiltIns": "usage"
		}
	  ]
	]
  }

在这里插入图片描述在这里插入图片描述

  • 但是并没有解决问题,查阅资料后,第三方包不兼容使用nuxt自带的babel转化
  • 在这里插入图片描述解决
参考文章:

nuxt项目兼容低版本ie
Nuxt.js项目搭建配置踩坑
nuxtjs与IE11兼容性问题_对象分配
nuxt.js 开发兼容ie浏览器的es6 转码
babel官网
Nuxt.js 构建配置

其他参考文章:

next.js、nuxt.js等服务端渲染框架构建的项目部署到服务器,并用PM2守护程序
PM2 常用命令
pm2 启动 nuxtjs 项目
nuxt.js部署vue应用到服务端过程
NuxtJS服务端部署过程
百度SEO核心技术——主动提交sitemap
做seo优化必须要了解Sitemap提交的秘密
Siege高性能压测工具
siege压力测试工具安装和介绍
siege 安装及使用

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值