第一章.Nuxt.js简介
- Nuxt.js 是一个基于 Vue.js 的通用应用框架,通过对客户端/服务端基础架构的抽象组织,Nuxt.js 主要关注的是应用的 UI 渲染。
1.1 需求分析
- 采用vue.js开发的应用系统SEO不友好,需要解决SEO的问题。
- 比如:新闻门户、博客系统有SEO的需求,希望杯搜索引擎收录,百度排名靠前。
1.2 了解搜索引擎优化SEO
- 利用搜索引擎的规则提高网站在有关搜素引擎内的自然排名。目的是让其在行业内占据领先地位,很大程度上是网站经营者的一种商业行为。
第二章.服务端渲染和客户端渲染(SSR)
2.1 什么是客户端渲染、服务端渲染
1.服务端渲染过程:浏览器---(发送请求)--->服务端(server接收请求通过JSP生成html)--->服务端渲染成html---(服务端响应html)--->浏览器
2.客户端渲染过程:浏览器---(Ajax发送http请求)--->后台服务器端接口--->服务端封装JSON---(将JSON数据响应)--->浏览器(浏览器接收到数据,渲染html页面,生成DOM元素,然后生成DOM元素,展示给用户)
2.2 为什么使用服务器端渲染(SSR)
与传统的SPA(单页面应用程序)相比,服务端渲染的优势主要有那些
- 更好的SEO,由于搜索引擎爬虫抓取工具可以直接查看完全渲染的页面
- 更快的内容到达时间(首屏加载更快),因为服务端只需要返回渲染好的HTML,这部分代码量很小,所以用户体验好 使用服务器端渲染(SSR)时还需要有一些权衡之处
- 首先时开发成本比较高,比如某些生命周期钩子函数(如beforeCreate,created)能同时运行在服务端和客户端,因此第三方库要做特殊处理,才能在服务器渲染应用程序中运行。
- 由于服务端渲染要用Node.js做中间间,所以部署项目时,需要处于Node.server运行环境。在高流量环境下,还要做好服务器负载和缓存策略。
2.3 Nuxt.js工作原理?
- 1.浏览器(客户端)发送http请求到Node.js服务端。
- 2.部署在Node.js的应用Nuxt.js接收到浏览器发送的请求,它会去请求后台接口服务端。
- 3.后台接口服务端会响应JSON数据,Nuxt.js获取到响应的数据后进行服务端渲染,渲染成HTML。
- 4.然后Nuxt.js将HTML页面响应给浏览器。
- 5.浏览器直接将Nuxt.js响应的Html页面进行展示。 Nuxt.js使用了vue.js+webpack+babel三大技术架构,集成了Vue.js中以下组件/框架,用于开发完整而强大的Web应用。
第三章.搭建Nuxt环境和创建项目
3.1 安装脚手架工具与创建项目
- NPM方式
- 全局安装create-nuxt-app命令
- 卸载create-nuxt-app命令
npm install -g create-nuxt-app
npm uninstall -g create-nuxt-app
- 创建项目
- 创建完成后进入项目,通过npm run dev运行项目
3.2 目录结构和配置文件
- 目录结构
- .nuxt :系统自动配置的文件
- assets:此目录包含未编译的资源,如LESS、SASS或JavaScript。
- components: 公共的组件
- layouts: 布局文件
- middleware:中间件
- node_modelues:npm依赖的资源包
- pages: .vue文件
- plugins: 插件
- static:存放静态图片
- store: 数据状态管理
- nuxt.config.js : nuxt的配置文件
2.配置文件
- 默认修改端口、ip 在package.json配置
{
"config": {
"nuxt":{
"host": "127.0.0.1",
"port": "8088"
}
}
- 多页引用时用到的.css文件,在nuxt.config.js中进行配置
// 在assets文件夹下创建css文件夹,css文件夹下创建normalize.css文件,在nuxt.config.js中配置引入
export default {
css: ['~assets/normalize.css']
}
第四章 视图
4.1 默认页面布局(模板)
1.当执行npm run dev后,会生成一个默认的html模板页面 路径:.next/views/app.template.html,也可以在项目根目录下新建app.html文件写默认的页面布局样式
<!DOCTYPE html>
<html {{ HTML_ATTRS }}>
<head {{ HEAD_ATTRS }}>
{{ HEAD }}
</head>
<body {{ BODY_ATTRS }}>
{{ APP }}
</body>
</html>
<!-- {{ APP }}渲染的时主体内容,就是nuxt/nuxt-demo1/pages下的页面组件 -->
2.在nuxt.config.js中的配置说明
export default {
mode: 'universal',
/*
** Headers of the page
*/
// 默认的服务启动端口
server:{
port: 3000,
host: '0.0.0.0'
},
head: {
// 对应 HTML-ATTRS属性
htmlAttrs: {
lang: 'en'
},
// 对应 HEAD-ATTRS 属性
headAttrs: {
name: 'hadername'
},
// 对应 BODY-ARRTS 属性
bodyAttrs: {
style: 'background-color: green'
},
// 网站标题的名字(默认取的是 package.json中name字段的名字)
title: process.env.npm_package_name || '',
// title: '博客社区站点_IT技术',
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: [
],
/*
** Plugins to load before mounting the App
*/
plugins: [
],
/*
** Nuxt.js dev-modules
*/
buildModules: [
],
/*
** Nuxt.js modules
*/
modules: [
],
/*
** Build configuration
*/
build: {
/*
** You can extend webpack config here
*/
extend (config, ctx) {
}
}
}
4.2 布局
Nuxt.js允许扩展默认的布局,或在layout目录下创建子当以的布局
- 默认布局,可以通过添加layouts/default.vue文件来扩展应用的默认布局
- 文件路径:layouts/default.vue, 在布局文件中添加组件用于显示页面的主体内容。
<template>
<div>
<div>我的博客导航栏在这里</div>
<nuxt />
</div>
</template>
- 告诉页面使用自定义布局
<template>
<!-- Your template -->
</template>
<script>
export default {
layout: 'blog'
// page component definitions
}
</script>
4.3 错误页面和meta标签设置
1.使用错误页面
- 在layouts文件夹下新建,error.vue
<template>
<div>
<!-- 通过状态码判断要去的页面 -->
<h2 v-if="error.statusCode === 404">找不到要去的页面啦!/(ㄒoㄒ)/~~</h2>
<h2 v-else>服务器500错误了.....</h2>
</div>
</template>
<script>
export default {
// 接收到页面error的信息
porps: ['error']
}
</script>
- meta标签设置
- 在单独要设置meta的.vue文件中设置
<template>
<div>
<h2>news-1 details</h2>
<h2>接收到传递的参数是:{{ $route.params.id }}</h2>
<ul>
<li><nuxt-link :to="{ name: 'news' }">news</nuxt-link></li>
</ul>
</div>
</template>
<script>
export default {
// 验证传递的参数
validate({ params }) {
return /^\d{4}/.test(params.id);
},
data() {
return {
// 使用data用title属性接收路由params传递的title信息
title: this.$route.params.title,
};
},
// 使用head,设置当前页面结构的meta信息
head() {
return {
// 设置当前页面的标题
title: this.title,
// 设置当前页面的meta信息
meta: [
{ charset: "utf-8" },
{ name: "viewport", content: "width=device-width, initial-scale=1" },
{
hid: "aaa", // 当前页面的唯一标识
name: "description",
content: "this is news-1 page",
},
],
};
},
};
</script>
<style></style>
4.3 Nuxt.js为页面提供的特殊配置项
- asyncData: 最重要的一个键,支持异步数据处理,另外该方法的第一个参数为当前页面组件的上下文对象
- layout:指定当前页面使用的布局
- loading:用于设置加载进度条,如果设置为false,页面自动调用
this.$nuxt.$loading.start()和this.$nuxt.$loading.finish()将会阻止。 - transition: 指定页面切换的过度动效。
- scrollToTop: 用于判定渲染页面前是否需要将当前页面滚动至顶部。适用于
嵌套路由的应用场景。 - validate: 校验方法用于校验路由的参数。
- middleware: 指定页面的中间件,中间件会在页面渲染之前被调用。
第五章 插件Plugin
5.1 介绍
- Nuxt.js 允许在运行 Vue.js 应用程序之前执行 js 插件。需要注意的是,在任何 Vue 组件的[生命周期]内, 只有
beforeCreate和created这两个方法会在 客户端和服务端被调用。其他生命周期函数仅在客户端被调用。 - 需要注意的是,在任何Vue组件的生命周期内,只有befereCreate和created这两个方法会在客户端和服务端被调用,其他生命周期函数仅在客户端被调用。
- 可以将自定义插件注入到Vue实例(客户端),context(服务器端)、store(Vuex)新增的属性或方法名使用$作为前缀。
5.2 使用第三方模块
1.案例:在客户端和服务端使用axios做HTTP请求
2.安装我们需要的的包axios
- npm install axios --save
3.使用asyncData函数中进行发送请求
import axios from 'axios'
async asyncData(context) {
// 请求接口,获取数据
let { data } = await axios.get(
`https://www.fastmock.site/mock/d5420c129e4f6d1f432a3448e762b7f4/_test/shop/1`
);
return { title: data.data.title };
},
<!-- 在.vue文件中使用接口获取到的属性 -->
<template>
<div>
<h2>店铺的名称是:{{ title }}</h2>
</div>
</template>
5.2 注入Vue实例
1.将内容注入Vue实例,避免重复引入,在Vue原型上挂载注入一个函数,所有组件内部都可以访问(不包含服务器端)
import Vue from 'vue'
Vue.prototype.$myInjectedFunction = function (string) {
console.log('绑定到Vue实例里面的参数', string);
}
2.在nuxt.config.js中将定义好的插件文件引入
plugins: [
'~/plugins/demo-plugins.js',
],
3.在.vue文件中使用
export default {
mounted(){
this.$myInjectedFunction('test')
}
}
5.3 注入context
1.context注入方式和在其它vue应用程序中注入类似 ·在plugins下新建文件:plugins/ctx-inject.js
export default ({ app }, inject) => {
// 直接在直接在context.app对象上设置函数
app.myInjectedFunction = string =>
console.log('Okay, another function', string)
}
2.在nuxt.config.js中引入定义好的插件文件
export dafult {
plugins:[ '~/plugins/ctx-inject.js' ]
}
3.现在,只要获得context,就可以使用该函数了(如asyncData,fetch中),在pages中新建ctx-inject-example.vue进行使用
<script>
export default {
asyncData(context) {
// 调用插件中定义好的方法
context.app.myInjectedFunction("hello world!!!");
},
};
</script>
5.4 同时注入
1.如果需要同时在context,Vue实例,甚至Vuex中同时注入使用,可以使用inject方法,他是plugin导出函数的第二个参数。将内容注入Vue实例的方式与在Vue应用程序中进行注入类似。系统会自动将$添加到方法名的前面。
2.步骤一、新建combined-inject.js
export default ({ app }, inject) => {
inject("myInjectedFunction3", (str) => {
console.log(str);
});
};
- 步骤二、在nuxt.config.js中引入
export dafult {
plugins:[ '~/plugins/combined-inject.js
}
- 步骤三、在pages文件夹下新建combined-inject.vue文件进行测试
export default {
// 在客户端使用
mounted() {
this.$myInjectedFunction3("客户端传递参数...");
},
// 在服务端使用
asyncData(context) {
context.app.$myInjectedFunction3("服务端传递参数...");
},
};
</script>
`在store/index.js中测试使用
import Vue from "vue";
import Vuex from "vuex";
Vue.use(Vuex);
export const actions = {
// 在store仓库中使用
setSomeValueToWhatever({ commit }) {
this.$myInjectedFunction("1234!!!");
},
};
第六章 异步加载数据asyncData
Nuxt.js扩展了Vue.js,增加了一个叫asyncData的方法,使得我们可以在设置组件的数据之间能异步获取或处理数据。
6.1 asyncData方法介绍
- asyncData方法会在组件限于页面组件每次加载之前被调用。它可以在服务端或路由更新之前被调用。在这个方法被调用的时候,第一个参数设定为当前页面的
上下文对象,可以利用asyncData方法来获取数据,Nuxt.js会将asyncData返回的数据融合组件data方法返回的数据一并返回给当前组件。 - fetch()方法会在渲染页面前调用,作用是填充状态树(store)数据,与asyncData方法类似,不同的是它不会设置组件的数据
- 注意:由于asyncData方法是在组件
初始化前被调用的,所以在方法内是没有办法通过this来引用组件的实例对象。
6.2 使用nuxtjs/axios发送请求
- 先安装nuxtjs/axios包
npm install nuxtjs/axios
- 安装好包后,在nuxt.config.js文件中modules模块中引入
modules:[
'nuxtjs/axios'
]
- 发送请求,首页请求接口
// 加载组件之前服务器端会调用
// 方式一
asyncData({$axios}){
return $axios.get('https://www.fastmock.site/mock/d5420c129e4f6d1f432a3448e762b7f4/_test/shop/1')
.then(response => {
let data = response.data.data
return { data }
})
}
// 加载组件之前服务器端会调用
//方式二
async asyncData({$axios}){
const response = await $axios.$get('https://www.fastmock.site/mock/d5420c129e4f6d1f432a3448e762b7f4/_test/shop/1')
return { data: response.data }
}
- 在index.vue中使用
<template>
<div class="container">
<h2>店铺id {{ data.id }}</h2>
<ul>
<li>店铺图片:{{ data.img }}</li>
<li>店铺title: {{ data.title }} </li>
<li>店铺优惠活动: {{ data.notice }}</li>
</ul>
</div>
</template>
6.3 插件方式解耦在Nuxtjs中调用api
1.创建api插件api/test.js
6.4 数据交互,跨域
1. 读取本地数据资源
- 安装nuxtjs/axios、nuxtjs/proxy包
npm install @nuxtjs/axios @nuxtjs/proxy
- 在nuxt.config.js中的module中添加安装的axios模块
export default {
...
module: [ '@nuxtjs/axios']
}
- 在nuxt中可以获取项目下的静态资源,在static文件夹中新建data文件夹新建在建data.json文件。文件路径 static/data/data.json
{
"title": "this is static data",
"content": "test in the nuxt request data"
}
- 在pages下的.vue文件中测试获取定义的数据(asyncData fetch)
<template>
<div>
<h2>首页</h2>
<h3>标题: {{ info.title }}</h3>
<h3>内容描述: {{ info.content }}</h3>
</div>
</template>
<script>
export default {
// 解析上下文,其中添加进来的模块以$开头 ,且读取到的属性会合并到当前组件内部(data中)
async asyncData({ $axios }){
let res = await $axios({ url: '/data/data.json' })
return {
info: res.data
}
}
}
</script>
2. 读取跨域数据
- 在nuxt.config.js中配置axios选项,配置proxy选项
export default {
...
axios:{
proxy: true, // 开启axios跨域
prefix: '/api',// 基础路径baseUrl
},
proxy: {
'/api/': {
target: 'http://localhost:3001', //代理转发的地址
}
},
}
第七章 资源文件
默认情况下Nuxt使用vue-loader、file-loader、url-loader这几个Webpack加载器来处理文件的加载和引用。对不需要通过Webpack处理的静态资源文件,可以放置在static目录中。
7.1 Webpack构建
- 默认情况下,vue-loader自动使用css-loader和Vue模板编译器来编译处理vue文件中的样式和模板。
- 例如:
<template>
<img src="~/assets/images.png" />
</template>
经过编译后会被转换为:
createElement('img', { attrs: { src: require('~/assets/image.png') } })
= 注意:从Nuxt2.0开始,~/alias将无法在CSS文件中正确解析。在url CSS引用中使用~assets(没有斜杠)
7.3 静态文件
- Nuxt服务器启动的时候,static目录下的文件会映射至应用的分路径
/下,可以在代码中使用根路径/结合资源相对路径来引入静态资源。
<!-- 引用 static 目录下的图片 -->
<img src="/my-image.png" />
<!-- 引用 assets 目录下经过 webpack 构建处理后的图片 -->
<img src="~/assets/my-image-2.png" />
第八章 路由
Nuxt.js依据pages目录结构自动生成vue-router模块的路由配置
8.1 基础路由
- 例如pages中的文件结构是
// pages中的文件结构
pages/
--| user/
--| one.vue
--| index.vue
--| index.vue
- 那么,Nuxt.js自动生成的路由配置信息如下:
router: [
routes: [
{
name: 'index',
path: '/',
component: 'pages/index.vue'
},
{
name: 'user',
path: '/user'
component: 'pages/user/index.vue'
},
{
name: 'user-one',
path: '/user/one',
component: 'pages/user/one.vue'
}
]
]
- Nuxt的路由为约定式路由:
- 展示区:
<nuxt> - 声明式跳转
<nuxt-link :to="{ name:'product-id,params:{id:3},query:{a:111,b:222} }"></nuxt-link> - name: 路由名 目录名-其他目录-文件名
- 展示区:
- 展示区的层级控制
- pages/一级展示/二级展示
- 如果在二级展示下面出现了index.vue 会在一级展示
- 如果在二级展示下面出现了index.vue 空文档 代表 有默认页,不会找寻其他 .vue文件
8.2 动态路由
在Nuxt.js里面定义带参数的动态路由,需要创建对应的以下划线作为前缀的Vue文件或目录
- 文件目录结构
pages/
--| _slug/
--| comments.vue
--| index.vue
--| users/
--| _id.vue
--| index.vue
- Nuxt.js生成对应的路由配置表为
router: [
routes: [
{
name: 'index',
path: '/',
component: 'pages/index.vue'
},
{
name: 'user-id',
path: '/user/:id?'
component: 'pages/users/_id.vue'
},
{
name: 'slug-comments',
path: '/:slug/comments',
component: 'pages/_slug/comments.vue'
},
{
name: 'slug',
path: '/:slug'
component: 'pages/_slug/index.vue'
}
]
]
- generate命令会忽略动态路由: API Configuration generate
8.3 路由参数
1.通过声明式导航传递params参数
<li><nuxt-link :to="{ name:'news', params:{ newsId: 3306 } }">NEWS</nuxt-link></li>
2.使用$route.params.newsId接收到传递的params参数
<h2>接收到传递的参数是:{{ $route.params.newsId }}</h2>
8.4 路由校验
1.使用validate校验
<template>
<div>
<h2>news details</h2>
<h2>接收到传递的参数是:{{$route.params.id}}</h2>
<ul>
<li><nuxt-link :to="{ name: 'news' }">news</nuxt-link></li>
</ul>
</div>
</template>
<script>
export default {
validate({params,query,store}){
return /^\d{4}/.test(params.id)
}
}
</script>
<style>
</style>
8.5 嵌套路由和动态嵌套路由
- 嵌套路由
通过vue-router的子路由创建Nuxt.js的嵌套路由,创建内嵌子路由,需要添加一个Vue文件,同时添加一个与该文件同名的目录用来存放子视图组件
// 比如pages是这样的目录结构
pages/
--| user/
--| index.vue
--| one.vue
--| user.vue
// Nuxt.js自动生成的路由配置如下:
{
path: "/user",
component: 'pages/user.vue',
children: [{
path: "",
component: 'pages/user/index.vue',
name: "user"
}, {
path: "one",
component: 'pages/user/one.vue',
name: "user-one"
}]
}
- 动态嵌套路由
- 这种应用场景比较少见,但是Nuxt.js仍然支持,在动态路由下配置动态子路由
// 比如pages的结构目录如这样
pages/
--| _category
--| _subCategory/
--| _id.vue
--| index.vue
--| _subCategory.vue
--| index.vue
--| _category.vue
--| index.vue
// Nuxt.js自动生成的路由配置如下:
{
path: "/:category",
component: _0aaff0a5,
children: [{
path: "",
component: _72f6503d,
name: "category"
}, {
path: ":subCategory",
component: _1e8bf784,
children: [{
path: "",
component: _53cda07e,
name: "category-subCategory"
}, {
path: ":id",
component: _36a5aa66,
name: "category-subCategory-id"
}]
}]
}
- 未知嵌套深度的攻台嵌套路由
- 如果不知道URL结构的嵌套深度,可以使用
_.vue动态匹配嵌套路径。
- 扩展路由
- 在nuxt.config.js中进行配置
export default {
// 扩展路由
/**
* 参数1:routes可以获取到所有的路由信息
* 参数2:resolve返回磁盘上的内容
*/
router:{
extendRoutes(routes,resolve){
console.log(routes);
routes.push({ //添加路由
name: 'goods', // 路由的别名
path: '/index', // 路由的路径
component: resolve(__dirname,'pages/index.vue')
})
}
},
}
8.6 过渡动效
- 全局过渡动效设置
- Nuxt.js默认使用的过渡效果名称为page
- 如果想让每一个给页面的切换都有淡出的效果,需要创建有所有路由公用的CSS文件。
/* 路径路径:assets/css/annimation.css */
/* 全局使用动画效果 */
/* 进入时激活状态,离开时激活的状态 */
.page-enter-active, .page-leave-active {
transition: opacity 2s ;
}
/* 离开那个页面那个页面opacity为0,进入那个页面那个页面刚开始的opactiy为0 */
.page-enter, .page-leave-active {
opacity: 0;
}
// 在nuxt.config.js中配置
export default {
css: [' ~/assets/css/annimation.css ']
}
- 页面的过渡属性
- 如果想给某个页面自定义过渡特效,只要在该页面组件中配置transition字段即可
/* 路径路径:assets/css/annimation.css */
/* 页面使用动画效果 */
.test-enter-active, .test-leave-active {
transition: all 2s;
font-size: 12px;
}
.test-enter, .test-leave-active {
opacity: 0;
font-size: 40px;
}
- 那个页面需要过渡,加过渡
<script>
export default {
// 在页面中使用过渡,引用的值是 test
transition: 'test'
}
</script>
8.7 路由守卫
- 前置路由
- 依赖中间件middleware,插件
- 全局守卫: ①.nuxt.config.js 指向middleware ②. layouts定义中间件
- 组件独享守卫: middleware
- 插件全局前置守卫
- 后置路由
- 使用vue的beforeRouteLeave钩子
- 插件全局后置守卫
1.middleware全局前置守卫
1.使用middleware中间件实现全局前置守卫
- 在middleware文件夹下定义中间件auth.js
export default ( { store,route,redirect,params,query,req,res } )=>{
// context 服务端上下文
// 全局守卫前置业务
// store 状态树信息
// route 一条目标路由信息
// redirect 强制跳转
// params query 校验参数合理性
console.log(" middleware next.config.js 全局守卫前置业务 ");
redirect('/login') //强制跳转到login
}
- 在nuxt.config.js中引入中间件
export dufault {
router: {
middleware: 'auth'
}
}
- 在布局layouts中定义,也可以实现前置路由,进行路由的校验
- 在layouts文件夹下使用default.vue组件,定义使用全局的路由前置守卫
<script>
export default {
// 在页面层级使用middleware实现前置的路由守卫工作
middlerware ( { store,route,redirect,params,query,req,res } ){
// context 服务端上下文
// 全局守卫前置业务
// store 状态树信息
// route 一条目标路由信息
// redirect 强制跳转
// params query 校验参数合理性
console.log(" middleware layouts 全局守卫前置业务 ");
redirect('/register')
}
}
</script>
2.组件独享全局前置守卫
- 在要使用的组件.vue中使用middleware。eg: pages/user.vue组件
<script>
export default {
middleware ( { store,route,redirect,params,query,req,res } ){
// context 服务端上下文
// 组件独享前置守卫业务
// store 状态树信息
// route 一条目标路由信息
// redirect 强制跳转
// params query 校验参数合理性
console.log(" pages 全局守卫前置业务 ");
redirect('/login') //去登录页
}
}
</script>
3. 使用插件plugins全局前置守卫
- 在plugins文件夹下创建router.js文件
- 注意:只能next(true)/next(false) 不允许next('/login')进行路由的跳转
export default ( { app ,redirect} ) => {
// app vue实例
// redirect 跳转函数
// 全局前置守卫,插件形式
app.router.beforeEach(( to,form,next )=>{
// 注意:只能next(true)/next(false) 不允许next('/login')进行路由的跳转
console.log("使用插件实现全局路由前置",to);
// 判断路由名
if(to.name== 'index' || to.name == 'register'){
next()
}else {
redirect({name: 'register'})
}
})
}
- 在nuxt.config.js中进行引入配置即可完成使用插件plugins做全局前置守卫路由
export default {
...
plugins: [ '~/plugins/router.js' ]
}
4.后置守卫
1.使用插件完成全局的后置守卫
- 在plugins文件夹下创建router.js文件
export default(({app,redirect,params,query})=>{
app.router.afterEach((to,form)=>{
console.log("使用插件实现全局后置路由守卫",to);
})
})
- 使用vue的beforeRouteLeave钩子实现实现全局后置路由守卫
- 在要使用后置路由守卫的.vue中设置 eg: pages/login.vue
<script>
export default {
// 使用vue的beforeRouteLeave实现后置路由守卫
beforeRouteLeave(to,form,next){
let msg = window.confirm('确认要离开吗?')
next(msg)
}
}
</script>
第九章 Nuxt.js 的生命周期
9.1 生命周期流程
服务器端
- nuxtServerInit: 服务器初始化
- middleware: 中间件运行
- nuxt.config.js
- matching layout
- matching page& children
- validate: 校验参数
- Pages & children
- asyncData()& fetch(): 异步处理数据
- Pages & children
- redener: 开始客户端渲染 服务器端和客户端
- beforeCreate && created
- 在这两个生命周期钩子的this可以拿到服务器端上下文和组件自身的上下文 其他
- ** 注意在服务端只能访问服务端的上下文信息context(不要用this获取信息),而在客户端可以访问到window对象,也可以访问到组件自身的this **
9.2 nuxtServerInit方法
- 如果在状态树中定义了
nuxtServerInit方法,Nuxt.js调用它的时候会将页面的上下文对象作为第2个参数传递给它。 - eg: 假设我们服务端的会话状态树里可以通过
req.session.user来访问当前登录的用户。将该登录用户信息传给客户端的状态树,我们只需要更新store/index.js
actions: {
nuxtServerInit ({ commit }, { req }) {
if (req.session.user) {
commit('user', req.session.user)
}
}
}
- 这时
context被赋予nuxtServerInit作为第二个参数,它与asyncData或fetch方法相同。nuxtServerInit方法接收的上下文对象和fetch一样,但是不包括context.direct()和context.error().
9.3 中间件的执行时机
- 中间件允许定义一个自定义函数运行在一个页面或一组页面渲染之前。
- 1.首先匹配:nuxt.config.js中的middleware
- 2.接着匹配布局(
layouts)中的middleware - 3.最后匹配页面中的
middleware
// 在middleware文件夹下新建auth.js中间件
export default = ({ app,store,route,redirect,req,res }) => {
console.log('middleware nuxt.config.js outside...')
}
// 1.nuxt.config.js中的配置
export default {
router:{
middleware: 'auth'
}
}
// 2.layouts中的middleware
// 文件路径:layouts/default.vue
<script>
export default {
middleware(){
console.log('middelware layouts....')
}
}
</script>
// 3.匹配页面中的middleware
// 文件路径 pages/index.vue
<script>
export default {
middleware(){
console.log('page middleware....')
}
}
</script>
- 加载index.vue页面后中间件的执行输出
9.4 asyncData && fetch 客户端钩子
<script>
export default {
// 读数据,返回给当前组件
asyncData(context) {
// 异步业务逻辑,读取服务器数据
console.log('在这里请求服务器端的数据...');
return { a: 2 }
},
// 读数据, 读到数据将数据存储到vuex中
fetch({ store }){
// 异步业务逻辑,读取服务端数据提交给vuex
console.log('fetch...');
}
}
</script>
9.4 beforeCreate && created 钩子(服务端和客户端都会运行)
<script>
export default {
// SSR && CSR
beforeCreate(){
console.log('beforeCreate running...');
},
created(){
console.log('create running...');
}
}
</script>
- 客户端和服务器都会打印信息
第十章 vuex
对于大型项目来说,使用状态树store管理状态state十分必要。这也就是为什么Nuxt.js内核实现了Vuex
10.1 使用状态树
-
Nuxt.js会尝试找到src目录(默认是应用根目录)下的store目录,如果目录该存在,它将做以下事情:
- ①. 引用
vuex模块 - ②. 将vuex模块加到
vendors构建配置中 - ③. 设置
Vue根实例的store配置项
- ①. 引用
-
模块方式:
store目录下的每个.js文件会被转换成为状态树·指定命名的子模块`(当然index是根模块) -
Classic(不建议使用):
store/index.js返回创建Vue.Store实例的方法
- 无论使用哪种模式,
state的值应该始终是function,为了避免返回应用类型,会导致多个实例相互影响。
10.2 普通方式
- Nuxt.js允许拥有一个store目录,其中包含与模块对应的每个文件
- 首先,只需将状态导出为函数,将变量和操作作为store/index.js中的对象导出
export const state = () => ({
counter: 0
})
- 然后,可以拥有
store/todos.js文件:
export const state = () => ({
list: []
})
export const mutations = {
add(state,text){
state.list.push({
text,
done: false
})
},
toggle(){
}
}
- 在pages/todos.vue中,使用todos模块
<template>
<div>
<ul>
<li v-for="todo in todos" :key="todo.text">
<input
type="checkbox"
:checked="todo.done"
@change="toggleTodo(todo)"
/>
<span> {{ todo.text }} </span>
</li>
<li>
<input
type="text"
placeholder="what needs to be done"
@keyup.enter="addTodo"
/>
</li>
</ul>
</div>
</template>
<script>
export default {
computed: {
todos() {
// 通过计算属性的返回值将vuex中todos仓库中的list数据拿出
return this.$store.state.todos.list;
},
},
methods: {
addTodo(e) {
// e.target.value为当前input框中输入的value值
this.$store.commit("todos/add", e.target.value);
// 输入完后将input 置空
e.target.value = "";
},
toggleTodo(todo) {
this.$store.commit("todos/toggle", todo);
},
},
};
</script>
10.3 状态持久化和token校验
- 使用安装插件 cookie-universal-nuxt插件
- 思想:登录时,同步vuex和cookie,强制刷新后,nuxtServerInit钩子,取出cookie,同步vuex,axios拦截器读取vuex
- 同步vuex和cookie,
<template>
<div>
<h2>登录页</h2>
<button @click="btnLogin">登录</button>
</div>
</template>
<script>
export default {
// 使用vue的beforeRouteLeave实现后置路由守卫
beforeRouteLeave(to,form,next){
let msg = window.confirm('确认要离开吗?')
next(msg)
},
methods:{
btnLogin(){
this.$axios({
URL: '',
method: 'post',
data:{
username: 'zs',
age: 18
}
}).then((res)=>{
// 同步vuex 和 cookie
// 安装完插件cookie-universal-nuxt插件后就有一个cookies属性方法可以设置cookie的值
this.$cookies.set('username',res.data)
// 通过向user仓库中的mutations提交数据,修改user仓库中state的属性
this.$store.commit('user/M_UPDATA_USER',res.data)
// 判断路由信息,进行跳转
if( !this.$route.query.path ){
this.$router.replace('/user')
}else {
this.$router.replace(this.$route.query.path)
}
})
}
}
}
</script>
- 强制刷新后,nuxtServerInit钩子,取出cookie,同步vuex, 在store/index.js中设置
// state
export const state = ()=> ({
data:{},
err: 0,
token: ''
})
// action
export const actions = {
// 使用服务端初始化钩子
nuxtServerInit(store,{app: {$cookies}}){
// 初始化token同步到store中
let user = $cookies.get('user') ? $cookies.get('user') : { error: 2, msg: '未登录',token:'' }
store.commit('user/M_UPDATA_USER',user)
}
}
// mutation
export const mutations = {}
// getter
export const getters = {}
- axios拦截器读取vuex 路径:plugins/axios.js
export default function({$axios,redirect,route,store,app:{$cookies}}){
// 基本配置
$axios.default.timeout = 5000
// 请求拦截器
$axios.onRequest(config => {
config.headers.token = store.state.user.token
return config
})
// 响应拦截器
$axios.onResponse(res => {
if(res.data.err === 2 && requestAnimationFrame.fullPath !== '/login'){
redirect('/login?path='+route.fullPath)
}
})
}
11.在nuxt.js中使用sass
11.1 安装插件
1.node-sass 和 sass-loader安装完这两个插件后,就可以直接在页面中使用sass编写样式了
- 在安装这两个插件的时候避免踩坑喔!因为node的版本对node-sass插件的支持版本是不同的。已踩过/(ㄒoㄒ)/~~
- 官网的说法,不同的 node.js 版本需要安装不同的 node-sass 版本,并且 node-sass 已经废弃,推荐使用 Dart Sass 代替,Dart Sass 在 npm 中的包名为
sass。
- 附加以下node.js对应的node-sass版本
- 安装sass-loader的版本也要注意,sass-loader@12版本要求webpack@5.0.0的版本
- 提供参考的版本,我的node版本是14.17.2 安装node-sass@4.14.1版本 sass-loader@10.2.1版本
npm install node-sass@4.14.1 sass-loader@10.2.1 --save
- 在页面上使用sass样式
<style lang="scss" scoped>
$bg: #399;
.box1{
background-color: $bg;
}
</style>
11.2 多个组件使用sass样式定义的变量
- 做法:在assets文件夹新建sass文件夹,定义多个组件使用的.scss文件。assets/sass/global.scss
// 定义css变量
$theme-bg: #393
2.接着,安装 @nuxtjs/style-resources插件
npm install @nuxtjs/style-resources --save
3.在nuxt.config.js modules中添加进去nuxtjs/style-resources插件
export default {
...
modules:[
'@nuxtjs/style-resources'
]
}
4.指定全局sass变量的来源,在nuxt.config.js中添加styleResources
export default {
...
styleResources:{
scss: ['~assets/sass/global.scss']
}
}
5.在组件中测试
<template>
<div>
<div class="box1">
<p>测试nuxt.js中使用局部定义的sass变量</p>
</div>
<div class="box2">
<p>测试nuxt.js中使用全局定义的sass变量</p>
</div>
</div>
</template>
<script>
export default {}
</script>
<style lang="scss" scoped>
$bg: #399;
.box1 {
background-color: $bg;
p {
font-size: 3rem;
}
}
.box2 {
background-color: $theme-bg;
}
</style>
12 在nuxt.js中使用TypeScript
- 注意:nuxt的版本需要在2.10或以上
- 安装配置可以参考nuxt typescript官网:typescript.nuxtjs.org/guide/setup
- 第一步安装**@nuxt/typescript-build**这个插件
npm install --save-dev @nuxt/typescript-build @nuxt/types
- 第二步在nuxt.config.js配置文件中的buildModules中加入@nuxt/typescript-build
export default {
buildModules: ['@nuxt/typescript-build']
}
- 第三步在项目根目录下新建tsconfig.json文件,加入下面的内容
{
"compilerOptions": {
"target": "ES2018",
"module": "ESNext",
"moduleResolution": "Node",
"lib": [
"ESNext",
"ESNext.AsyncIterable",
"DOM"
],
"esModuleInterop": true,
"allowJs": true,
"sourceMap": true,
"strict": true,
"noEmit": true,
"baseUrl": ".",
"paths": {
"~/*": [
"./*"
],
"@/*": [
"./*"
]
},
"types": [
"@types/node",
"@nuxt/types"
]
},
"exclude": [
"node_modules"
]
}
- 在项目根目录下新建vue-shim.d.ts文件,用来声明为vue提供型别
declare module "*.vue" {
import Vue from 'vue'
export default Vue
}
- 基本配置已经完成,已经设定好可在 layouts, components, plugins 和 middlewares 中使用 TypeScript了。