笔记
vue-cli本身问题
单页面无法做到SEO搜索优化:
无法做到多页面
title、描述、关键词是动态的
网站内容是动态加载的
解决方法有两种:
- 预渲染
- 服务端渲染
预渲染
流程演示:
如何做:
多页面预先渲染,使用插件:
prerender-spa-plugin
vue项目中安装
prerender-spa-plugin
vue.config.js进行配置
静态修改title描述关键词:vue-meta-info
- 下载
npm install vue-meta-info -S
- 到页面组件中配置
- 下载
metaInfo: {
title: '小鹿线官网',
meta: [{
name: '关键字,web前端',
content: '描述'
}]
}
解决什么问题:
打包多页面,
解决每个页面单独生成title描述关键词 [vue-meta-info]
接口数据是在html生成之前就放在页面上的,爬虫可以抓取到内容
存在的问题:
- 预渲染无法配置动态路由
- 如果title描述关键词来自于接口的数据,配置到meta-info也是不行的
适合做什么项目:
- 一个项目可能某几个页面需要做seo,比如只做首页SEO,其他页面不需要
服务端渲染
流程演示:
如何做:
- 使用Nuxt.js
适合做什么项目:
- 一个项目可能所有页面都要做seo
解决seo的方案选择
前后端不分离:
传统的后端模板,比如JSP等
压力在后端
好处:安全
前后端分离的:
- spa单页面应用[vue-cli本身处理不了seo]
- 压力客户端
- 预渲染
- 压力客户端
- 问题:
- 在html页面加载之前数据过来渲染后才有html的dom结构,这样的话可能也会存在页面空白的情况
- 一个项目不是所有页面都做seo
- 服务端渲染
- 压力客户端
- 问题:
- 起了2个服务 [一个是后端自己语言的服务,一个是nodejs的服务]
nuxt安装和目录结构
安装过程中的选项:
Programming language: 程序设计语言
Package manager: 包管理器
UI framework:ui框架
Nuxt.js modules: nuxt的js模块
Linting tools:代码校验工具
Testing framework: 测试框架
Rendering modules :渲染模式
Deployment target: 部署目标
Development tools : 开发工具
Version control system : 版本控制工具
目录结构:
- pages : 存放页面,类似于src/views
- components : 存放组件,类似于src/components
- static: 存放静态资源,类似于src/assets
- store: vuex状态树,类似于src/store
- nuxt.config.js : 全局的配置文件,类似于vue.config.js
nuxt生命周期
nuxt
生命周期是在nuxt
项目构建完成后,用户在客户端数据url
后的一些列动作,由先到后的执行顺序如下(以下是基于SSR
服务端渲染):nuxt start
,服务端启动项目后,服务端中加载Nuxt
、中间件以及Nuxt
插件(Nuxt
插件的引入根据nuxt.config.js
中定义的先后顺序),下面进入到生命周期:
服务端生命周期
nuxtServerInit
服务端中运行,每个页面都执行
有两个参数,第一个是
Vuex
的上下文,第二个是Nuxt
的context
,Vuex
用于预填存储状态;该钩子在
store/index.js
中定义类似于vue.js中的main.js。可以在这个生命周期中获取token,并存储。
export const state = {
token:''
}
export const mutations = {
setToken(state,token){
state.token = token;
}
}
export const actions = {
nuxtServerInit(store , context ){
store.commit('setToken','')
console.log( 'nuxtServerInit' );
}
}
Middleware
在服务端运行,在每个页面都执行
middleware({store,route,redirect,params,query,req,res}){}
执行顺序为:
- 全局中间件(在
nuxt.config.js
中定义:router: { middleware: ['auth']}
) - 布局中间件(
layouts
中定义) - 路由中间件(在
page
中定义,通过middleware: 'auth'
);
- 全局中间件(在
类似于vue.js中的导航守卫,可以在这个生命周期中进行用户是否登录判断
中间件写法为:
- 第一种写法:
export default {
middleware:'auth'
}
//新建middleware目录 ==> auth.js- 第二种写法:
//直接写函数
export default {
middleware(){
}
}
validate
在服务端运行,在每个页面都执行,只能在页面中定义;
validate({params,query}){}
在页面渲染之前执行;用于校验动态路由参数是否合法,必须返回布尔值(
true
或false
),true为通过,false则跳到错误页面。
export default {
validate({params,query}){
console.log('validate');
return /^\d+$/.test(query.id);
}
}
asyncData
在服务端运行,在每个页面都执行,只能在页面中定义,不能用于组件
读取数据,返回给页面,这里我们可以做一些异步的数据请求,返回给组件。
传参为当前页面的上下文对象,并且支持给
data
赋值,方法最后return
出来即可;该方法在页面初始化之前执行,因此无法获取
this
fetch
在服务端运行,在每个页面都执行。
Nuxt2.12
之后新增钩子,可以在页面、组件中都能使用在页面上使用
- 同
asyncData
类似,方法用于在渲染页面前填充应用的状态树(store)数据,不同的是它不会设置页面的数据。 - 传参为当前页面的上下文对象,但是无法为
data
赋值,可以拿到Vuex
上下文,用于在页面渲染之前预设store
状态树; - 无法使用
this
。
async fetch({ $axios,store }){
const data = await $axios.get("https://dog.ceo/api/breeds/image/random");
store.commit('setToken',data.data.message)
},- 同
在组件上使用
- 可以设置组件的数据,可以使用
this.data=xxx
- 不能设置函数的传参,不然会失效,可以通
this.$store
获取vuex数据 - 可以使用this
async fetch(/*context*/ ){ //不能设置context参数,不然这个方法会失效
let res = await this.$axios.get('http://testapi.xuexiluxian.cn/api/slider/getSliders');
let items = res.data.data.list;
this.$store.commit('setToken',items[0].createTime)
this.items = items;
}- 可以设置组件的数据,可以使用
为了让获取过程可以异步,你需要返回一个 Promise,Nuxt.js 会等这个 promise 完成后再渲染组件。
<template>
<h1>Stars: {{ $store.state.stars }}</h1>
</template>
<script>
export default {
fetch({ store, params }) {
return axios.get('http://my-api/stars').then(res => {
store.commit('setStars', res.data)
})
}
}
</script>
fetch
会在组件每次加载前被调用(在服务端或切换至目标路由之前)执行,因此
服务端和客户端共有生命周期
这两种方法都会执行两次。
beforeCreate
beforeCreate
:Vue
实例注册完成之后执行;运行环境包括服务端和客户端,用法同vue
差不多;
created
created
:Vue
示例创建完成之后执行;运行环境包括服务端和客户端,用法同vue
差不多;
客户端生命周期
- beforeMount: 此时,DOM节点可用,fetch()方法也已完成,即pending=false;该钩子只在客户端运行,用法同 vue;
- mounted: 用法同 vue
- beforeUpadate: 用法同 vue
- updated: 用法同 vue
- beforeDestroy: 用法同 vue
- destroyed: 用法同 vue
asyncData与fetch区别
相同点
- 都是在页面渲染之前,用来拉取异步数据
不同点
- asyncData仅在页面使用,fetch可以在页面与组件上使用
- asyncData比fetch先执行,但两个异步是同时进行
- fetch在页面上使用是设置vuex数据,不可以设置页面数据;在组件上即可以设置vuex数据,又可以设置组件数据。asyncData则即可以设置vuex数据,又可以设置页面数据
nuxt路由
自动生成
文档链接: https://www.nuxtjs.cn/guide/routing
Nuxt.js 依据
pages
目录结构自动生成 vue-router 模块的路由配置。自动生成的路由配置在.nuxt文件夹中的router.js
要在页面之间使用路由,我们建议使用
<nuxt-link>
标签。
基础路由
假设 pages
的目录结构如下:
pages/
--| user/
-----| index.vue
-----| one.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.js 里面定义带参数的动态路由,需要创建对应的以下划线作为前缀的 Vue 文件 或 目录。
以下目录结构:
pages/
--| _slug/
-----| comments.vue
-----| index.vue
--| users/
-----| _id.vue
Nuxt.js 生成对应的路由配置表为:
router: {
routes: [
{
name: 'users-id',
path: '/users/:id?',
component: 'pages/users/_id.vue'
},
{
name: 'slug',
path: '/:slug',
component: 'pages/_slug/index.vue'
},
{
name: 'slug-comments',
path: '/:slug/comments',
component: 'pages/_slug/comments.vue'
}
]
}
你会发现名称为 users-id
的路由路径带有 :id?
参数,表示该路由是可选的。如果你想将它设置为必选的路由,需要在 users/_id
目录内创建一个 index.vue
文件。
嵌套路由
你可以通过 vue-router 的子路由创建 Nuxt.js 应用的嵌套路由。
创建内嵌子路由,你需要添加一个 Vue 文件,同时添加一个与该文件同名的目录用来存放子视图组件。
别忘了在父组件(
.vue
文件) 内增加<nuxt-child/>
用于显示子视图内容。
假设文件结构如:
pages/
--| users/
-----| _id.vue
-----| index.vue
--| users.vue
Nuxt.js 自动生成的路由配置如下:
router: {
routes: [
{
path: '/users',
component: 'pages/users.vue',
children: [
{
path: '',
component: 'pages/users/index.vue',
name: 'users'
},
{
path: ':id',
component: 'pages/users/_id.vue',
name: 'users-id'
}
]
}
]
}
动态嵌套路由
这个应用场景比较少见,但是 Nuxt.js 仍然支持:在动态路由下配置动态子路由。
假设文件结构如下:
pages/
--| _category/
-----| _subCategory/
--------| _id.vue
--------| index.vue
-----| _subCategory.vue
-----| index.vue
--| _category.vue
--| index.vue
Nuxt.js 自动生成的路由配置如下:
router: {
routes: [
{
path: '/',
component: 'pages/index.vue',
name: 'index'
},
{
path: '/:category',
component: 'pages/_category.vue',
children: [
{
path: '',
component: 'pages/_category/index.vue',
name: 'category'
},
{
path: ':subCategory',
component: 'pages/_category/_subCategory.vue',
children: [
{
path: '',
component: 'pages/_category/_subCategory/index.vue',
name: 'category-subCategory'
},
{
path: ':id',
component: 'pages/_category/_subCategory/_id.vue',
name: 'category-subCategory-id'
}
]
}
]
}
]
}
未知嵌套深度的动态嵌套路由
如果您不知道 URL 结构的深度,您可以使用_.vue
动态匹配嵌套路径。这将处理与更具体请求不匹配的情况。
文件结构:
pages/
--| people/
-----| _id.vue
-----| index.vue
--| _.vue
--| index.vue
将处理这样的请求:
Path | File |
---|---|
/ | index.vue |
/people | people/index.vue |
/people/123 | people/_id.vue |
/about | _.vue |
/about/careers | _.vue |
/about/careers/chicago | _.vue |
Note: 处理 404 页面,现在符合_.vue
页面的逻辑。
手动配置路由
使用
@nuxtjs/router
插件在nuxt.config.js的modules模块进行配置
modules: [
'@nuxtjs/router'
],在根目录下使用router.js文件配置路由
nuxtjs/router返回的内容和vue有所不同
export function createRouter() {
return new VueRouter({
mode: 'history',
routes
})
}
导航守卫
导航守卫有两种,一种是使用vue-router的路由守卫定义(在router.js中配置),一种是使用nuxt的中间件和插件配置
router.js配置
- vue-cli中怎么用,nuxt中就怎么用,几乎一样,比如
beforeEach
等 - 但是vue-router中的路由守卫会在服务端和客户执行,执行两次
- 由于会在服务端执行,所以不能在路由守卫中使用客户端的API,比如localstorage。
nuxtjs配置
使用中间件 : middleware
使用插件 : plugins (全局)
nuxt.config.js进行配置
plugins: [ '~/plugins/router.js' ]
新建plugins/router.js
export default ({app})=>{
app.router.beforeEach((to,from,next)=>{
console.log( to );
next();
})
}
服务端不能使用localStorage和cookie的解决方案
参考链接:https://www.npmjs.com/package/cookie-universal-nuxt
安装下载
npm i --save cookie-universal-nuxt
nuxt.config.js进行配置
modules: [ 'cookie-universal-nuxt']
nuxt配置之head
全局配置
在nuxt.config.js中配置head
局部配置
Nuxt.js 使用了
vue-meta
更新应用的头部标签(Head)
和html 属性
。
- 类型:
Object
或Function
使用 head
方法设置当前页面的头部标签。
在 head
方法里可通过 this
关键字来获取组件的数据,你可以利用页面组件的数据来设置个性化的 meta
标签。
<template>
<h1>{{ title }}</h1>
</template>
<script>
export default {
data() {
return {
title: 'Hello World!'
}
},
head() {
return {
title: this.title,
meta: [
{
hid: 'description',
name: 'description',
content: 'My custom description'
}
]
}
}
}
</script>
注意:为了避免子组件中的 meta 标签不能正确覆盖父组件中相同的标签而产生重复的现象,建议利用 hid
键为 meta 标签配一个唯一的标识编号。请阅读关于 vue-meta
的更多信息。
nuxt配置之Css
在 Nuxtjs 里配置全局的 CSS 文件、模块、库。 (每个页面都会被引入)
如果要使用 sass
就必须要安装 node-sass
和sass-loader
。
npm install --save-dev node-sass sass-loader
在 nuxt.conf.js
中,添加要使用的 CSS 资源:
- Type: Array
- Items: string
module.exports = {
css: [
// 直接加载一个 Node.js 模块。(在这里它是一个 Sass 文件)
'bulma',
// 项目里要用的 CSS 文件
'@/assets/css/main.css',
// 项目里要使用的 SCSS 文件
'@/assets/css/main.scss'
]
}
Nuxt.js 会自动识别被导入文件的扩展名,之后,webpack 会使用相应的预处理器进行处理。前提是,你安装了对应预处理器
nuxt配置之plugins
类型:
Array
- 数组元素类型:
String
或Object
- 数组元素类型:
如果数组元素类型是 Object
, 其具有以下属性:
- src:
String
(文件的路径) - ssr:
Boolean
(默认为true
) 如果值为 false,该文件只会在客户端被打包引入。
plugins
属性使得你可以轻易地为 Nuxt.js 配置使用 Vue.js 插件。
例如 (nuxt.config.js
):
module.exports = {
plugins: ['~plugins/vue-notifications']
}
然后, 我们需要创建 plugins/vue-notifications.js
文件:
import Vue from 'vue'
import VueNotifications from 'vue-notifications'
Vue.use(VueNotifications)
plugins
属性配置的所有插件会在 Nuxt.js 应用初始化之前被加载导入。
每次你需要使用 Vue.use()
时,你需要在 plugins/
目录下创建相应的插件文件,并在 nuxt.config.js
中的 plugins
配置项中配置插件的路径。
想了解更多关于使用插件的信息,请移步 插件使用指引。
nuxt配置之modules
- 类型:
Array
modules 是 Nuxt.js 扩展,可以扩展它的核心功能并添加无限的集成。了解更多
例如 (nuxt.config.js
):
export default {
modules: [
// Using package name
'@nuxtjs/axios',
// Relative to your project srcDir
'~/modules/awesome.js',
// Providing options
['@nuxtjs/google-analytics', { ua: 'X1234567' }],
// Inline definition
function () {}
]
}
模块开发通常会提供额外需要的步骤和使用细节。
Nuxt.js 尝试使用节点需求路径(在node_modules
中)解析modules
数组中的每个项目,如果使用~
别名,则将从项目srcDir
中解析。模块按顺序执行,因此顺序很重要。
模块应该导出一个函数来增强nuxt 构建 / 运行,并可选择返回一个promise
,直到它们的工作完成。请注意,它们在运行时是必需的,因此如果依赖于现代ES6
功能,应该已经进行了转换。
有关它们如何工作或是否有兴趣开发自己的模块的更多详细信息,请参阅模块指南。此外,我们还提供了一个官方Modules部分,列出了由 Nuxt 社区制作的数十个生产模块。
nuxt配置代理
安装
@nuxtjs/axios @nuxtjs/proxy
nuxt.config.js进行配置
modules: [
'@nuxtjs/axios',
'@nuxtjs/proxy'
],
axios:{
//是否可以跨域
proxy:true,
//retry:{ retries:3 },
//baseUr: process.env._ENV == 'production'? 'xxx' ? 'xxx'
},
proxy:{
'/api':{
target:'http://localhost:4000',
pathRewrite:{
'^/api':'',
}
}
},
nuxt配置之loading
loading 属性配置
- 类型:
Boolean
或Object
或String
在页面切换的时候,Nuxt.js 使用内置的加载组件显示加载进度条。你可以定制它的样式,禁用或者创建自己的加载组件。
在你的组件中你可以使用this.$nuxt.$loading.start()
来启动加载条。使用this.$nuxt.$loading.finish()
来使加载条结束。
export default {
mounted() {
this.$nextTick(() => {
this.$nuxt.$loading.start()
setTimeout(() => this.$nuxt.$loading.finish(), 500)
})
}
}
如果要在mounted
方法中启动它,请确保使用this.$nextTick
来调用它,因为$loading
可能无法立即使用。
禁用加载进度条
- 类型:
Boolean
页面切换的时候如果不想显示加载进度条,可以在 nuxt.config.js
里面增加 loading: false
的配置:
module.exports = {
loading: false
}
个性化加载进度条
- 类型:
Object
以下表格为进度条定制化时可用到的配置项及其说明。
键 | 类型 | 默认值 | 描述 |
---|---|---|---|
color | String | 'black' | 进度条的颜色 |
failedColor | String | 'red' | 页面加载失败时的颜色 (当 data 或 fetch 方法返回错误时)。 |
height | String | '2px' | 进度条的高度 (在进度条元素的 style 属性上体现)。 |
throttle | Number | 200 | 在 ms 中,在显示进度条之前等待指定的时间。用于防止条形闪烁。 |
duration | Number | 5000 | 进度条的最大显示时长,单位毫秒。Nuxt.js 假设页面在该时长内加载完毕。 |
continuous | Boolean | false | 当加载时间超过duration 时,保持动画进度条。 |
css | Boolean | true | 设置为 false 以删除默认进度条样式(并添加自己的样式)。 |
rtl | Boolean | false | 从右到左设置进度条的方向。 |
举个例子,一个 5 像素高的蓝色进度条,可以在 nuxt.config.js
中配置如下:
module.exports = {
loading: {
color: 'blue',
height: '5px'
}
}
自定义加载组件
- 类型:
String
你可以新建一个加载组件替代 Nuxt.js 默认的。使用自己的加载组件,需要在 loading
配置项里指定组件的路径,Nuxt.js 会自动调用该组件。
自定义组件需要实现以下接口方法:
方法 | 是否必须 | 描述 |
---|---|---|
start() | 是 | 路由更新(即浏览器地址变化)时调用, 请在该方法内显示组件。 |
finish() | 是 | 路由更新完毕(即asyncData 方法调用完成且页面加载完)时调用,请在该方法内隐藏组件。 |
fail(error) | 否 | 路由更新失败时调用(如asyncData 方法返回异常)。 |
increase(num) | 否 | 页面加载过程中调用, num 是小于 100 的整数。 |
我们可以在 components
目录下创建自定义的加载组件,如 components/loading.vue
:
<template lang="html">
<div class="loading-page" v-if="loading">
<p>Loading...</p>
</div>
</template>
<script>
export default {
data: () => ({
loading: false
}),
methods: {
start() {
this.loading = true
},
finish() {
this.loading = false
}
}
}
</script>
<style scoped>
.loading-page {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(255, 255, 255, 0.8);
text-align: center;
padding-top: 200px;
font-size: 30px;
font-family: sans-serif;
}
</style>
然后, 更新 nuxt.config.js
告诉 Nuxt.js 使用自定义加载组件:
module.exports = {
loading: '~/components/loading.vue'
}
进度条时长说明
Loading 组件不可能事先知道多长时间。加载新页面将需要。因此,无法将进度条准确地设置为 100%的加载时间。
Nuxt 的加载组件通过让你设置 duration
来部分解决这个问题,这应该设置为 guestimate 加载过程需要多长时间。 除非您使用自定义加载组件,否则进度条将始终在 duration
时间内从 0%移至 100%(无论实际进度如何)。 当加载时间超过 duration
时,进度条将保持 100%直到加载完成。
您可以通过将continuous
设置为 true 来更改默认行为,然后在达到 100%后,进度条将在duration
时间内再次收缩回 0%。当达到 0%后仍未完成加载时,它将再次从 0%开始增长到 100%,这将重复直到加载完成。
持续进度条的示例:
nuxt使用Vuex
直接使用nuxt
// /store/index.js
export const state = () => ({
nickname: '张三'
})
export const mutations = {
changeName( state , name ){
state.nickname = name;
}
}
使用原有的vuex时,需要返回一个函数
// /store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
import user from './modules/user'
const store = () => new Vuex.Store({
modules:{
user
}
})
export default store;
axios二次封装
使用axios插件
- 配置 : nuxt.config.js中的
plugins: ['~/plugins/axios',],
- 新建文件:根目录下新建plugins/axios.js
- axios.js(参考链接:https://axios.nuxtjs.org/helpers)
export default ( {$axios} )=>{
$axios.onRequest(config=>{
return config;
})
$axios.onResponse(response=>{
return response.data;
})
}
API目录解耦合
- 配置 : nuxt.config.js中的
plugins: ['~/plugins/axios','~/api/xxx'],
- xxx.js中写入
export default ({$axios},inject)=>{
inject('getFirstCategorys',()=>$axios({
url:'/api/course/category/getFirstCategorys',
method:'GET',
}))
}
- 页面或者组件请求
async asyncData( app ){
let res = await app.$getFirstCategorys();
return {
list:res.data.list
}
}
引入当前第三方UI组件
安装下载:npm i element-ui -S
配置:nuxt.config.js中的plugins为:
plugins: ['~/plugins/element']
在根目录的plugins/element.js新建文件
import Vue from 'vue';
import ElementUI from 'element-ui';
Vue.use(ElementUI);配置css : nuxt.config.js中的css为:
css: ['element-ui/lib/theme-chalk/index.css']
项目上线
执行:
npm run build
将打包好的
.nuxt ==>隐藏文件
static
nuxt.config.sj
package.json
把以上文件丢到服务器某个文件夹中,并且在服务器安装node环境
在服务器上执行 npm install
在服务器运行项目 npm run start
打开的也是localhost:3000
我们需要使用nginx做代理 ==> www.xuexiluxian.cn
公司项目小记
- 通过
process.server
判断当前是否服务端运行 - 如何实现服务端调整接口使用内网IP,而客户端请求接口使用外网域名。
遇到的问题
less-loader 报错 TypeError: this.getOptions is not a function
因为less-loader
版本太高问题,webpack和less-loader版本兼容问题导致的,需要降低版本。
npm uninstall less-loader less
npm install less@3.9.0 less-loader@5.0.0 -s
nuxt项目中使用less全局样式、全局变量的配置
1. 配置全局less
- 使用less先安装less less-loader,nuxt会自动识别导入的后缀名,在nuxt.config.js中配置
- 不能导入less变量
css: [
/**
* 配置全局 css
*/
'element-ui/lib/theme-chalk/index.css',
'@assets/styles/global.less',
],
2. 配置全局 scss 变量
- 为页面注入变量可以使用@nuxtjs/style-resources 来实现
- 安装yarn add @nuxtjs/style-resources -D
- 配置nuxt.config.js
modules: [
// Doc: https://axios.nuxtjs.org/usage
'@nuxtjs/axios',
'@nuxtjs/style-resources'
],
styleResources:{
less:[
'./assets/styles/variable.less'
]
},
- styleResources 配置的资源路径不能使用 ~ 和 @,要使用绝对或者相对路径
- 不要导入实际样式。仅使用此模块导入变量、mixin、函数(等等),因为它们在实际构建中不存在。导入实际样式会将它们包含在每个组件中,并且还会使您的构建/HMR 幅度变慢。