Skip to main content

笔记

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生命周期

img

nuxt生命周期是在nuxt项目构建完成后,用户在客户端数据url后的一些列动作,由先到后的执行顺序如下(以下是基于SSR服务端渲染):nuxt start,服务端启动项目后,服务端中加载Nuxt、中间件以及Nuxt插件(Nuxt插件的引入根据nuxt.config.js中定义的先后顺序),下面进入到生命周期:

服务端生命周期

nuxtServerInit

  • 服务端中运行,每个页面都执行

  • 有两个参数,第一个是Vuex的上下文,第二个是NuxtcontextVuex用于预填存储状态;

  • 该钩子在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}){}

  • 在页面渲染之前执行;用于校验动态路由参数是否合法,必须返回布尔值(truefalse),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

将处理这样的请求:

PathFile
/index.vue
/peoplepeople/index.vue
/people/123people/_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 属性

  • 类型: ObjectFunction

使用 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-sasssass-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
    • 数组元素类型: StringObject

如果数组元素类型是 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 属性配置

  • 类型: BooleanObjectString

在页面切换的时候,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

以下表格为进度条定制化时可用到的配置项及其说明。

类型默认值描述
colorString'black'进度条的颜色
failedColorString'red'页面加载失败时的颜色 (当 datafetch 方法返回错误时)。
heightString'2px'进度条的高度 (在进度条元素的 style 属性上体现)。
throttleNumber200在 ms 中,在显示进度条之前等待指定的时间。用于防止条形闪烁。
durationNumber5000进度条的最大显示时长,单位毫秒。Nuxt.js 假设页面在该时长内加载完毕。
continuousBooleanfalse当加载时间超过duration时,保持动画进度条。
cssBooleantrue设置为 false 以删除默认进度条样式(并添加自己的样式)。
rtlBooleanfalse从右到左设置进度条的方向。

举个例子,一个 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%,这将重复直到加载完成。

持续进度条的示例:

continuous loading

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 幅度变慢。