1、CSRvsSSR
什么是CSR(Client-Side Render)
客户端渲染,用户发送请求返回页面,此时只是一个模板页面。通过接口请求数据使用模板引擎渲染页面,将渲染的内容进行展示。
什么是SSR(Server-Side Render)
用户请求页面,后端获取数据,在后端将页面进行渲染,将完整的页面结构返回给浏览器
- 客户端渲染可以减轻服务端的压力,实现前后端分离开发,SEO优化较差
- 服务端渲染有较好的SEO优化,但是对服务器性能有较高的要求
2、Nuxt框架
Nuxt就是基于Vue的一个应用框架,采用服务端渲染,让SPA应用也可以拥有SEO
直接安装nuxt
npx create-nuxt-app <project-name>
# 注意:在NPM版本5.2.0默认安装了npx,在命令行窗口输入npm --version可查看当前版本号
通过vue-cli创建nxt+starter-template模板
vue init nuxt-community/starter-template <project-name>
创建nuxt+express模板
它比基础模板多出一个api文件夹
vue init nuxt/express <project-name>
3、生命周期
nuxtServerInit
/**服务器初始化加载时候调用
* /store/index.js
*/
export const actions = {
nuxtServerInit(store,context){
// 初始化东西到store中
}
}
middleware
/**配置中间件
* /nuxt.config.js
*/
module.exports = {
router:{
middleware:'auth'
}
}
/**
* 方法一:在middleware文件夹中定义
* /middleware/auth.js
*/
export default (content)=>{
// 可以接受服务端上下文信息({store,route,redirect,params,query,req,res})
// 可以进行全局守卫任务
}
/**
* 方法二:在布局文件夹layouts中定义
* /layouts/default.vue
*/
export default {
middleware:'auth', // 定义在外部的中间件
middleware(){
// 定义在内部的中间件
}
}
/**
* 方法三:在页面文件夹pages中定义
* /pages/index.vue
*/
export default {
middleware:'auth', // 定义在外部的中间件
middleware(){
// 定义在内部的中间件
}
}
所有的中间件都可以获取上下文信息参数
总结执行顺序:nuxt.config.js文件 ——> 布局 ——> 页面
validata
/**用于校验进入页面的参数,所以配置在pages文件夹中
* /pages/index.vue
*/
<script>
export default {
validate({params,query}){
// 进行参数有效性的校验
return true // 校验通过
}
}
</script>
在布局层(layouts)创建error.vue文件作为错误页面,捕获错误
该页面名称不可自定义,通过props:['error']
接收错误信息
asyncData和fetch
/** 进入页面前的异步请求
* /pages/index.vue
*/
<script>
export default {
asyncData(context){
// asyncData读取数据合并到目标组件
return { a:1 }
},
fetch({$axios,store,error}){
// fetch读取数据通过mutations修改数据状态
store.commit('home/M_UPDATE_HOME',这是要传递额数据)
}
}
</script>
beforeCreate
created
以上两个生命周期同时运行在客户端和服务端
beforeMount
mounted
beforeUpdate
updated
beforeDestroy
destroyed
都是运行在客户端的生命周期,客户端不存在组件缓存,所以没有激活和失活两个钩子
总结:
- 客户端执行的生命周期中可以获取
window
对象和this
- 服务端可以获取上下文环境
4、路由
约定的形式
在pages文件夹中新建不同的页面,在layouts文件夹中使用<nuxt/>
作为路由展示区,类似vue中的<router-view/>
- 该文件夹下的所有vue文件都会被认为是一级路由目标文件,无需配置
- 如果需要二级路由,则需要在pages文件下新建文件夹实现二级路由
声明式跳转
<!-- 直接跳转到某个页面 -->
<nuxt-link to='/'>首页</nuxt-link>
<!--
目的:跳转到携带参数的动态路由 /goods/1?a=1
实现:goods文件夹下新建 _id.vue 文件作为动态路由的文件 id是动态的
注意:参数params的key对应文件名
-->
<nuxt-link :to={name:'goods-id',params:{id:1},query:{a:1}}>
首页
</nuxt-link>
扩展路由配置
/*
* 在/nuxt.config.js文件中
**/
module.exports = {
router:{
extendRoutes(routes,resolve){
// 配置一个index路由
routes.push({
name:'index',
path:'/index',
component:resolve(__dirname,'pages/index.vue')
})
}
}
}
路由守卫
路由守卫可以在中间件和插件中实现
前置守卫
1、全局守卫
- nuxt.config指向middleware
/** 配置中间件
* /nuxt.config.js
*/
module.exports = {
router:{
middleware:'auth'
}
}
/**
* /middleware/auth.js
*/
export default ({store,route,redirect,params,query,req,res})=>{
// 可以接受服务端上下文信息({store,route,redirect,params,query,req,res})
// 配置全局守卫
redirect('/login')
}
- layouts中定义中间件
// 类似vue中的App根组件,进行路由拦截
export default {
middleware:'auth', // 直接指向middleware
middleware({store,route,redirect,params,query,req,res}){
// 或者直接在这里实现业务逻辑
redirect('/login')
}
}
2、组件独享守卫
- 在pages文件夹中通过middleware实现组件独享守卫(前置)
后置beforeRouteLeave钩子
后置守卫
- 在pages页面中通过vue中beforeRouteLeave实现独享后置守卫
beforeEouteLeave((to,from,next)=>{ // 组件独享后置守卫 })
通过插件也可以实现前置守卫和后置守卫
- 在
plugins
文件夹下新建routers.js文件 -
在
nuxt.config.js
文件plugins
配置项中配置该文件// ~表示当前项目,直接从根目录查找 plugins:[ '~/plugins/router' ]
- 在文件中实现路由守卫
/* router.js */ export default ({app,redirect,params,query,store})=>{ app.router.beforeEach((to,from,next)=>{ // 前置守卫,只能通过redirect跳转,而不能使用next()跳转 }) app.router.afterEach((to,from)=>{ // 后置守卫,没有next }) }=
5、数据交互
模块安装
@nuxtjs/axios、@nuxtjs/proxy
模块配置
// nuxt.config.js
modules:[
'@/nuxtjs/axios' // 只需要添加axios
]
模块使用
exoprt default {
async asyncData({axios}){
let res = awaitaxios({url:'请求地址'})
console.log(res)
// 将数据合并到组件内部
return {
title:res.data.title
}
},
async fetch({$axios}){
// 这里可以使用vuex管理数据
}
}
跨域配置
// nuxt.config.js
axios:{
proxy:true, // 开启代理
perfix:'/api' // 请求前缀(可选)
},
proxy:{
'/api':{
target:'代理的地址',
pathRewrite:{
'^api':''
}
}
}
请求拦截
// nuxt.config.js文件
plugins:[
{
src:'~/plugins/axios.js',
ssr:true // 服务端渲染
}
]
-----------------------------------------------------------------
// plugins/axios.js文件
export default function({axios,redirect,route,store}){
// 基本配置axios.defaults.timeout = 1000
// 请求拦截
axios.onRequest(config=>{
config.headers.token=''
return config
})
// 响应拦截axios.onResponse(res=>{
// 响应码判断
return res
})
// 错误处理
$axios.onError(error=>{
return error
})
}
6、自定义Loading
// nuxt.config.js
module.exports = {
// 1、配置实现加载时顶部出现加载条
loading:{
color: '#399',
height: '3px'
}
// 2、自定义loading
loading: '~/components/loading.vue'
}
<!-- loading.vue -->
<template>
<div v-if="loading">loading...</div>
</template>
<script>
export default {
data:()=>({
loading:false
}),
methods:{
start(){
this.loading = true
},
finish(){
this.loading = false
}
}
}
</script>
7、状态管理
vuex的使用
/**
* /store/index.js
*/
// 暴露出一个对象
export const state=()=>({
a:1
})
export const getters = {}
export const mutations = {}
export const actions = {
nuxtServerInit(store,context){
// 初始化东西到store中
}
}
模块化使用
对不同页面的数据进行不同文件的管理,直接创建不同的文件,代码格式相同
访问的时候需要说明模块名称
this.$store.dispatch('user/UPDATE_USER')
vuex的辅助函数
mapState | mapGetters | mapMutations | mapActions
methods:{
...mapMutations('user',['M_UPDATE_USER']),
...mapActions('user',['A_UPDATE_USER'])
},
computed:{
// 获取主模块数据
...mapGetters(['getNav']),
...mapState(['nav'])
// 获取user模块数据
...mapState('user',['userName']),
// 如果数据冲突,可以通过对象形式获取数据,实现名称自定义
...mapState({
homeName:state=>state.userName
})
}
状态持久化
登录状态保持的步骤:
- 登录时候,token存入vuex + 持久化存储
- 刷新后,通过
nuxtServerInit
读取持久化token,同步vuex - axios拦截器读取vuex,将token写入请求头
8、使用elementUI
插件文件夹pluigns下新建element-ui.js文件
import Vue from 'vue'
// 整体引入
import ElementUI from 'element-ui'
Vue.use(ElementUI)
// 按需引入
import {
Button
} from 'element-ui'
Vue.use(Button)
在nuxt.config.js中引入css
module.export = {
css:[
'element-ui/lib/theme-chalk/index.css'
]
}
在nuxt.config.js中配置插件
module.export = {
plugins:[
{
src:'~/plugins/element-ui',
// 不支持服务端渲染的设置为false
ssr:true
}
]
}
9、全局定义
通过插件实现全局的定义,在plugins/mixins.js中编写全局定义
全局方法
import Vue from 'vue'
let show = ()=>{ // 这是一个全局方法 }
// 将方法挂载到Vue原型上,服务端内部的钩子不能使用
Vue.prototype.$show = show
全局过滤器
如果都将全局定义写在一个文件中,数量过于庞大,但是因为nuxt文件目录使用的特殊性,故将文件定义在资源目录assets
中
/**
* /assets/js/filters.js
*/
export function fillZero(n){
return n<10 ? `0${n}` : n
}
--------------------------------------
/* 导入
* /plugins/mixins.js
*/
import * as filters from '../assets/js/filters'
// 遍历结果绑定到Vue过滤器中
Object.keys(filters).forEach(key=>Vue.filter(key,filters[key]))
全局指令
同上,编写指令方法,导入后通过Vue.directive('指令名称',指令方法)使用
全局组件
耗费一点内存,但是不需要逐个导入组件
引入组件,通过Vue.component('组件名称')注册到全局
除此之外还有全局样式等
10、meta信息注入
nuxt.config.js
配置head属性
module.exports = {
head:{
title:'网站标题',
meta:[
{ charset:'utf-8' },
{ name:'viewport',content:'width=device-width,initial-scale=1'},
// 避免子组件中的 meta 标签不能正确覆盖父组件中相同的标签而产生重复的现象
// 建议利用 hid 键为 meta 标签配一个唯一的标识编号
{ hid:'description',name:'description',content:'项目描述信息' }
],
link:[
{rel:'icon',type:'image/x-icon',href:'/favicon.ico'}
]
}
}
对页面单独配置,在页面文件中使用head函数
exports default {
head(){
return {
meta:[]
}
}
}
文章评论