服务端渲染
服务器知识:koa、node.js
SSR原理:
*将同⼀个组件渲染为服务器端的 HTML 字符串,将它们直接发送到浏览器,最后将这些静态标记"激
活"为客户端上完全可交互的应⽤程序。*
应⽤场景:
- 应⽤需要更好的 SEO
- 应⽤需要更快的内容到达时间 (⾸屏渲染时间),特别是对于缓慢的⽹络情况或运⾏缓慢的设备。
ssr的局限:
- 更⼤的服务器端负载
- 较⾼的学习成本
- ⼀些外部扩展库使⽤会受限
nuxt安装
npx create-nuxt-app <项⽬名>
启动
npm run dev
⽬录结构
assets:资源⽬录 assets ⽤于组织未编译的静态资源如 LESS 、 SASS 或 JavaScript 。
components:组件⽬录 components ⽤于组织应⽤的 Vue.js 组件。Nuxt.js 不会扩展增强该⽬录下
Vue.js 组件,即这些组件不会像⻚⾯组件那样有 asyncData ⽅法的特性。
layouts:布局⽬录 layouts ⽤于组织应⽤的布局组件。
middleware: middleware ⽬录⽤于存放应⽤的中间件。
pages:⻚⾯⽬录 pages ⽤于组织应⽤的路由及视图。Nuxt.js 框架读取该⽬录下所有的 .vue ⽂
件并⾃动⽣成对应的路由配置。
plugins:插件⽬录 plugins ⽤于组织那些需要在 根vue.js应⽤ 实例化之前需要运⾏的
Javascript 插件。
static:静态⽂件⽬录 static ⽤于存放应⽤的静态⽂件,此类⽂件不会被 Nuxt.js 调⽤ Webpack 进⾏构建编译处理。 服务器启动的时候,该⽬录下的⽂件会映射⾄应⽤的根路径 / 下。
store: store ⽬录⽤于组织应⽤的 Vuex 状态树 ⽂件。 Nuxt.js 框架集成了 Vuex 状态树 的相关功 能配置,在 store ⽬录下创建⼀个 index.js ⽂件可激活这些配置。
nuxt.confifig.js: nuxt.config.js ⽂件⽤于组织Nuxt.js 应⽤的个性化配置,以便覆盖默认配置。
约定优于配置
约定优于配置
路由:pages⽬录中所有 *.vue ⽂件⽣成应⽤的路由配置
导航:
⾸⻚
嵌套:制造⼀个.vue⽂件和⽂件夹同名
pages/
--| main/
--| main.vue
动态路由:⽂件名或者⽂件夹名称要带_
pages/
--| main/
-----| detail/
--------| _id.vue
⻚⾯
⾃定义布局:
- 创建layouts/main.vue
pages/login.vue
用户登录
登录
- ⻚⾯中引⼊pages/main.vue
export default {
layout: 'main',
};
main/admin.vue
商品管理页
/main/cart.vue
购物车
mian/index.vue
首页
-
{{good.text}}
¥{{good.price}}
mian/detail/_id.vue
商品的详情
${{goodInfo}}
异步数据:
asyncData
它在组件创建之前执⾏,⾥⾯不要⽤this访问组件实例
第⼀个参数是上下⽂
可以在服务端也可以客户端都执⾏
asyncData会和data融合
首页
-
{{good.text}}
¥{{good.price}}
接⼝准备:
- 创建server/api.js
const router = require("koa-router")({prefix:"/api"})
const goods = [
{id:1,text:"web",price:1999},
{
id: 2,
text: "移动",
price: 199
},
];
router.get("/goods",ctx => {
ctx.body = {
ok: 1,
goods
}
});
router.get("/detail",ctx => {
ctx.body = {
ok:1,
data:goods.find(good => good.id == ctx.query.id)
}
});
router.post("/login",ctx => {
const user = ctx.request.body
if(user.username === "jerry" && user.password ==='123'){
// 将token存入cookie
const token = 'a mock token'
ctx.cookies.set('token',token)
ctx.body = {
ok:1,token
}
} else {
ctx.body = {ok: 0}
}
});
module.exports = router
2. 修改服务器配置,server/index.js
const Koa = require('koa')
const consola = require('consola')
const { Nuxt, Builder } = require('nuxt')
// 解析post的数据
const bodyparser = require("koa-bodyparser")
const api = require('./api')
const app = new Koa()
// 设置cookie加密秘钥
app.keys = ["some secret","another secret"]
// Import and Set Nuxt.js options
const config = require('../nuxt.config.js')
config.dev = app.env !== 'production'
async function start () {
// Instantiate nuxt.js
const nuxt = new Nuxt(config)
const {
host = process.env.HOST || '127.0.0.1',
port = process.env.PORT || 3000
} = nuxt.options.server
await nuxt.ready()
// Build in development
if (config.dev) {
const builder = new Builder(nuxt)
await builder.build()
}
// 解析post数据并注册路由
app.use(bodyparser())
app.use(api.routes())
// 页面渲染
app.use((ctx) => {
ctx.status = 200
ctx.respond = false // Bypass Koa's built-in response handling
ctx.req.ctx = ctx // This might be useful later on, e.g. in nuxtServerInit or with nuxt-stash
nuxt.render(ctx.req, ctx.res)
})
app.listen(port, host)
consola.ready({
message: `Server listening on http://${host}:${port}`,
badge: true
})
}
start()
中间件
中间件会在⼀个⻚⾯或⼀组⻚⾯渲染之前运⾏我们定义的函数,常⽤于权限控制、校验等任务。
⾸⻚重定向,创建middleware/index-redirect.js
export default function({route,redirect}){
//中间件可以获得上下文,里面有各种有用信息
//常用的有app,route,redirect,store
if(route.path === '/'){
redirect('/main')
}
}
注册中间件,nuxt.config.js
router: {
middleware: ['index-redirect']
},
插件
Nuxt.js会在运⾏Vue应⽤之前执⾏JS插件,需要引⼊或设置Vue插件、⾃定义模块和第三⽅模块时特
别有⽤。
创建plugins/api-inject.js
// 注入接口 利用插件机制将服务接口注入组件实例,store实例中
export default({$axios},inject) => {
inject("login",user => {
return $axios.$post("/api/login",user)
})
}
注册插件,nuxt.config.js
plugins: [
"@/plugins/api-inject"
],
vuex
⽤户登录及登录状态保存,创建store/user.js
// 应用根目录如果存在Store目录,Nuxt.js将启用vuex状态数,定义各状态树时具名导出state,mutations,getters.actions即可
export const state = () => ({
token:''
})
export const mutations = {
init(state,token){
state.token = token
}
}
export const getters = {
isLogin(state) {
return !!state.token
}
}
export const actions = {
login({commit},u){
return this.$login(u).then(({token})=>{
if(token){
commit("init",token)
}
return !!token
})
}
}
使⽤状态,创建中间件middleware/auth.js
export default function({redirect,store}){
console.log("token:" + store.state.user.token)
//通过vuex中令牌存在与否判断是否登录
if(!store.state.user.token){
redirect("/login")
}
}
注册该中间件,admin.vue
export default {
middleware: 'auth'
}
状态初始化,store/index.js
export const actions = {
nuxtServerInit({commit},{req}){
// 服务端就将vuex状态填充
// 参数1是vuex的上下文
// 参数2是nuxt的上下文
// req.ctx是KOA的上下文 因为在server/index 赋的值
const token = req.ctx.cookies.get('token')
if(token){
console.log("初始化token")
console.log(token)
commit('user/init',token)
}
}
}
nuxt.config.js
module.exports = {
mode: 'universal',
/*
** Headers of the page
*/
head: {
title: process.env.npm_package_name || '',
meta: [
{ charset: 'utf-8' },
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
{ hid: 'description', name: 'description', content: process.env.npm_package_description || '' }
],
link: [
{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }
]
},
/*
** Customize the progress-bar color
*/
loading: { color: '#fff' },
/*
** Global CSS
*/
css: [
'element-ui/lib/theme-chalk/index.css'
],
//路由配置
router: {
// 顺序从前往后执行
middleware: ['index-redirect']
},
/*
** Plugins to load before mounting the App
*/
plugins: [
'@/plugins/element-ui',
'@/plugins/api-inject',
{src:"@/plugins.axios",mode:"client"}//仅客户端执行
],
/*
** Nuxt.js dev-modules
*/
buildModules: [
],
/*
** Nuxt.js modules
*/
modules: ['@nuxtjs/axios'],
// axios: {
// proxy: true
// },
// proxy: {
// "api": "http://localhost:8080"
// },
/*
** Build configuration
*/
build: {
transpile: [/^element-ui/],
/*
** You can extend webpack config here
*/
extend (config, ctx) {
}
}
}
pluygins/axios.js
export default function({$axios}){
// 利用$axios模快幫忙方法setToken設置全局請求頭
// 此處省略token截取邏輯
$axios.setToken(document.cookie,'Bearer')
}
布局
整体布局 默认的
layouts/default.vue
自定义布局
layouts/error.vue
页面不存在
应用发生错误异常
首页
/layouts/users.vue
自定义布局
服务端渲染应⽤部署
npm run build
npm start
静态应⽤部署
准备⼯作: 将接⼝服务器独⽴出来
// api.js
const Koa = require("koa");
const bodyparser = require("koa-bodyparser");
const router = require("koa-router")({ prefix: "/api" });
const app = new Koa();
// 设置cookie加密秘钥
app.keys = ["some secret", "another secret"];
const goods = [
{ id: 1, text: "Web全栈架构师", price: 1000 },
{ id: 2, text: "Python架构师", price: 1000 }
];
// /api/goods
router.get("/goods", ctx => {
ctx.body = {
ok: 1,
goods
}; });
router.get("/detail", ctx => {
ctx.body = {
ok: 1,
data: goods.find(good => good.id == ctx.query.id)
}; });
router.post("/login", ctx => {
const user = ctx.request.body;
if (user.username === "jerry" && user.password === "123") {
// 将token存⼊cookie
const token = 'a mock token';
ctx.cookies.set('token', token);
ctx.body = { ok: 1, token };
} else {
ctx.body = { ok: 0 };
} });
app.use(bodyparser());
app.use(router.routes());
app.listen(8080);
// index.js const Koa = require("koa"); const consola = require("consola"); const { Nuxt, Builder } = require("nuxt"); const app = new Koa(); // Import and Set Nuxt.js options let config = require("../nuxt.config.js"); config.dev = !(app.env === "production"); async function start() {
// Instantiate nuxt.js
const nuxt = new Nuxt(config);
const {
host = process.env.HOST || "127.0.0.1",
port = process.env.PORT || 3000
} = nuxt.options.server;
// Build in development
if (config.dev) {
const builder = new Builder(nuxt);
await builder.build();
} else {
await nuxt.ready();
}
// ⻚⾯渲染
app.use(ctx => {
ctx.status = 200;
ctx.respond = false; // Bypass Koa's built-in response handling
ctx.req.ctx = ctx; // This might be useful later on, e.g. in nuxtServerInit or with nuxt-stash
nuxt.render(ctx.req, ctx.res);
})
app.listen(port, host);
consola.ready({
message: `Server listening on http://${host}:${port}`,
badge: true
})
}
start()
启动接⼝服务器api.js
代理接⼝,nuxt.confifig.js
axios: {
proxy: true }, proxy: {
"/api": "http://localhost:8080" },
⽣成应⽤的静态⽬录和⽂件
配置服务器默认端⼝,package.json
"scripts": {
"generate": "cross-env PORT=80 nuxt generate" }
执⾏⽣成命令
npm run generate
nginx配置
server {
listen 80;
server\_name localhost;
\# 静态⽂件服务
location / {
root C:\\\\Users\\\\yt037\\\\Desktop\\\\kaikeba\\\\projects\\\\nuxttest\\\\dist;
index index.html;
}
#nginx反向代理,实现接⼝转发
location ^~ /api/ proxy\_pass http://localhost:3000; #注意路径后边不要加/
} }