服务端渲染
服务器知识: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 ⽂件⽣成应⽤的路由配置
导航:
<nuxt-link to="/">⾸⻚</nuxt-link>
嵌套:制造⼀个.vue⽂件和⽂件夹同名
pages/
--| main/
--| main.vue
动态路由:⽂件名或者⽂件夹名称要带_
pages/
--| main/
-----| detail/
--------| _id.vue
⻚⾯
⾃定义布局:
- 创建layouts/main.vue
<template>
<div>
<nav>
<n-link to="/main">index</n-link>
<!-- 别名 -->
<n-link to="/main/admin" no-prefetch>管理</n-link>
<N-link to="/main/cart">gouwu</N-link>
</nav>
<nuxt-child />
</div>
</template>
<script>
export default {
}
</script>
<style lang="scss" scoped>
</style>
**pages/login.vue**
用户登录
2. ⻚⾯中引⼊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}}
**接⼝准备:**
1. 创建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
a
x
i
o
s
.
axios.
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({KaTeX parse error: Expected 'EOF', got '}' at position 6: axios}̲){ // 利用axios模快幫忙方法setToken設置全局請求頭
// 此處省略token截取邏輯
$axios.setToken(document.cookie,‘Bearer’)
}
## 布局
整体布局 默认的
layouts/default.vue
自定义布局
layouts/error.vue
页面不存在
应用发生错误异常
首页/layouts/users.vue