初始化项目
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配置
可以在这个配置项中配置全局的 head ,如定义网站的标题、 meta ,引入第三方的 CSS、JavaScript 文件等:
具体配置:https://zh.nuxtjs.org/api/configuration-head/
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'}
]
}
- loading
个性化定制页面加载 (默认显示加载进度条)可以定制它的样式,禁用或者创建自己的加载组件。
具体配置:https://zh.nuxtjs.org/api/configuration-loading/- css
定义应用的全局样式文件、模块或第三方库,在这个配置项中,引入全局的 CSS 文件,之后每个页面都会被引入。
具体配置:https://zh.nuxtjs.org/api/configuration-css/
css: [ //该配置项用于定义应用的全局(所有页面均需引用的)样式文件、模块或第三方库。
'element-ui/lib/theme-chalk/index.css',//在创建项目的时候安装了elememt插件,这里自动引入插件的默认样式
'@/assets/css/reset.css', //引入assets下的reset.css全局标签重置样式
'@/assets/main.css' //引入全局的动画样式
]
- routes
覆盖 Nuxt.js 默认的 vue-router 配置
具体配置:https://zh.nuxtjs.org/api/configuration-router/- plugins
配置需要在 根vue.js应用 实例化之前运行的 Javascript 插件。
ssr设置为false 只在客户端使用
插件在plugins文件夹下定义,在plugins配置项中配置
具体配置:https://zh.nuxtjs.org/api/configuration-plugins/
下面是我配置的axios.js,bus.js,echarts.js,element-ui.js
// 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'
})
],
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
遇到的问题
使用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 中使用
方法一:使用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' // 路由守卫
],
// 捕获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()方法
使用 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缓存实践
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 安装及使用