Nuxt.js

Nuxt.js是什么

Nuxt.js是一个基于Vue.js生态的第三方开源服务端渲染应用框架,用于构建同构应用。

为什么要使用Nuxt.js

传统的服务端渲染在用户访问网页地址时,服务端读取模板和数据,并将模板和数据组合成最终的渲染结果发送给客户端,所有操作都在服务端进行,服务端的压力较大,前后端耦合度高,不利于开发和维护,开发成本也较高。每次获取新的页面内容都要刷新页面,用户体验也不好。
在这里插入图片描述
而客户端渲染在用户访问网页地址时,首先服务端返回一个空白的HTML页面,然后客户端通过ajax请求数据,数据返回之后再根据数据动态渲染页面。这样就造成客户端的首次渲染会发送多次请求,首屏渲染速度比较慢。客户端渲染访问页面时只会返回空白的HTML页面,需要等到执行js代码之后才会根据请求到的数据渲染页面内容,SEO只会读取根据网页地址返回的HTML字符串内容,因此客户端渲染几乎没有供给SEO提取的内容。
在这里插入图片描述
为了解决客户端渲染首屏渲染以及不利于SEO的缺点,现代化的服务端渲染应运而生。

现代化的服务端渲染也称同构渲染,是一种服务端渲染加客户端渲染的组合渲染方式。首屏渲染利用服务端渲染直接返回首屏渲染的内容,来解决首屏渲染速度慢以及不利于SEO的缺点。然后由客户端渲染接管页面交互。
在这里插入图片描述
同构渲染的实现可以通过Vue、React等js框架提供的官方解决方案来初步搭建,也可以通过第三方解决方案来实现,如React生态的Next.js,Vue生态的Nuxt.js。

Nuxt.js的特点

Nuxt.js集成了完整的Vue生态,包括Vue-Router、Vuex,并集成了现代化的打包功能,可以做到开箱即用,无需做额外的配置,只需要按照Nuxt.js的约定开发即可。

Nuxt.js用于初始化项目,或者在已有的Node服务端项目中,将Nuxt作为一个中间件继承到Node服务中。也可以在现有的Vue项目中使用,只是需要针对Nuxt.js的规则做一些调整。

Nuxt.js项目的创建

  1. 创建项目文件夹,并肩初始化package.json。
  2. 安装nuxt.js
  3. 根据Nuxt.js规则编写代码
  4. 修改package.json,配置启动命令。scripts中添加"dev": “nuxt”,运行yarn dev会自动编译,进入开发模式。

Nuxt.js 基本规则

路由规则

  • Nuxt会根据pages文件夹下的文件自动生成路由,以文件的路径作为path。
    在这里插入图片描述
    上面的目录结构会生成下面的路由
    在这里插入图片描述

  • 路由导航和Vue-Router的路由导航类似,通过nuxt-link组件或this.$router来实现,nuxt-link的使用和router-link的使用一致。

  • 使用动态路由的部分的文件夹或文件需要使用“_”开头,路由参数则通过this.$route.paramsthis.$route.query来访问。
    在这里插入图片描述
    上面的目录结构会生成下面的路由
    在这里插入图片描述

  • 嵌套路由:创建与父路由页面同名的文件夹,存放子路由页面,父路由页面要添加nuxt-child标签作为子路由出口
    在这里插入图片描述
    上面的目录结构会生成下面的路由
    在这里插入图片描述
    父路由页面示例:

    <template>
      <div>
        <h1>User 父路由</h1>
        <nuxt-child />
      </div>
    </template>
    
    <script>
    export default {
      name: 'User'
    }
    </script>
    
    <style>
    
    </style>
    
  • 自定义router配置,创建nuxt的配置文件nuxt.config.js,并在导出对象的router属性中添加router配置选项。在router中配置extendRoutes函数来添加、删除或替换路由。

    export default {
      router: {
        // base: '/pre/', // 路由前缀
        extendRoutes(routes, resolve) {
          // 清空路由
          routes.splice(0)
          // 配置路由规则
          routes.push(...[
            {
              path: '/',
              component: resolve(__dirname, 'pages/layout'),
              children: [
                {
                  name: 'home',
                  path: '',
                  component: resolve(__dirname, 'pages/home')
                },
                {
                  name: 'login',
                  path: '/login',
                  component: resolve(__dirname, 'pages/login')
                },
                {
                  name: 'register',
                  path: '/register',
                  component: resolve(__dirname, 'pages/login')
                },
                {
                  name: 'profile',
                  path: '/profile/:username',
                  component: resolve(__dirname, 'pages/profile')
                },
                {
                  name: 'settings',
                  path: '/settings',
                  component: resolve(__dirname, 'pages/settings')
                },
                {
                  name: 'editor',
                  path: '/editor',
                  component: resolve(__dirname, 'pages/editor')
                },
                {
                  name: 'dynamic-editor',
                  path: '/editor/:slug',
                  component: resolve(__dirname, 'pages/editor')
                },
                {
                  name: 'article',
                  path: '/article/:slug',
                  component: resolve(__dirname, 'pages/article')
                }
              ]
            }
          ])
        }
      },
      // 服务信息配置
      server: {
        host: '0.0.0.0', //服务启动ip,0.0.0.0表示服务器的所有ip地址
        port: '3000'
      },
      // 注册插件
      plugins: [
        '~/plugins/request.js',
        '~/plugins/dayjs.js'
      ]
    }
    

视图规则

  • 视图模板
    定制默认的html模板,服务端渲染的内容,在根目录下创建app.html文件。Nuxt.js会将其视作html模板。可以在模板中引入第三方的库。{{ APP }} 是固定的页面渲染入口,其他选项则可以在配置文件中配置。

    <!DOCTYPE html>
    <html {{ HTML_ATTRS }}>
      <head {{ HEAD_ATTRS }}>
        {{ HEAD }}
        <link href="https://cdn.jsdelivr.net/npm/ionicons@2.0.1/css/ionicons.min.css" rel="stylesheet" type="text/css">
        <link href="//fonts.googleapis.com/css?family=Titillium+Web:700|Source+Serif+Pro:400,700|Merriweather+Sans:400,700|Source+Sans+Pro:400,300,600,700,300italic,400italic,600italic,700italic" rel="stylesheet" type="text/css">
        <!-- Import the custom Bootstrap 4 theme from our hosted CDN -->
        <link rel="stylesheet" href="/main.css">
      </head>
      <body {{ BODY_ATTRS }}>
        {{ APP }}
      </body>
    </html>
    
  • 布局
    与父路由类似,nuxt会自动读取根目录下layouts文件夹内的文件作为布局,文件名作为布局名,默认布局为default.vue。layouts下的文件必须具有nuxt标签作为子页面的出口。

    <template>
      <div>
        <h1>layouts/default.vue</h1>
        <nuxt />
      </div>
    </template>
    
    <script>
    export default {
      name: 'defaultLayout'
    }
    </script>
    
    <style>
    
    </style>
    

    果要使用布局,在具体页面添加layout属性,指定要添加的布局名称,即文件名,如果没有指定,会默认使用default.vue作为布局。
    在这里插入图片描述

异步数据规则

  • Nuxt.js扩展了Vue.js,增加了asyncData方法,使我们可以在设置数据之前异步获取或处理数据,比如动态获取SEO的关键字。

  • asyncData方法会在服务端渲染期间或客户端路由更新之前被调用。

  • asyncData返回的数据会和data中的数据合并,asyncData中的返回的数据会覆盖data中的数据。aysncData中可以执行异步任务,data中不能。

  • asyncData只能在页面组件中定义和调用,即pages中的页面。

  • asyncData中没有this。

  • asyncData适用于需要获取首屏使用动态数据的场景。

    <template>
      <div class="profile-page">
    
        <div class="user-info">
          <div class="container">
            <div class="row">
    
              <div class="col-xs-12 col-md-10 offset-md-1">
                <img :src="profile.image" class="user-img" />
                <h4>{{ profile.username }}</h4>
                <p>
                  {{ profile.bio }}
                </p>
                <nuxt-link
                  v-if="$store.state.user.username === profile.username" 
                  class="btn btn-sm btn-outline-secondary action-btn"
                  :to="{
                    name: 'settings',
                  }"
                >
                  <i class="ion-gear-a"></i> Edit Profile Settings
                </nuxt-link>
                <button
                  v-else
                  class="btn btn-sm btn-outline-secondary action-btn"
                  :disabled="doFollowing"
                  @click="onFollowUser()"
                >
                  <i class="ion-plus-round"></i>
                  &nbsp;
                  {{ profile.following ? 'Unfollow Eric Simons' :  'Follow Eric Simons'}}
                </button>
                
              </div>
    
            </div>
          </div>
        </div>
      </div>
    </template>
    
    <script>
    import { getProfile, followUser, unfollowUser } from '@/api/profile'
    import { getArticles } from '@/api/article'
    import articleList from '@/components/article-list';
    
    export default {
      name: 'profileIndex',
      middleware: 'authenticated',
      components: {
        articleList
      },
      async asyncData({ params }) {
        const { data } = await getProfile(params.username)
        return {
          profile: data.profile
        }
      }
    }
    </script>
    
  • asyncData参数,第一个参数为上下文对象context,包括下列属性:
    在这里插入图片描述

在nuxt 中使用vuex

  1. 新建store文件夹,然后新建index.js,nuxt会默认读取index.js中的内容,也不需要手动创建vuex对象

  2. 在index.js中需要导出state,mutations,actions。其中state应该定义为一个函数,返回数据对象,这是因为服务端渲染期间运行都是同一个实例,为防止数据冲突/污染,state必须是一个函数,这样每次都会返回新的数据对象。

  3. nuxtServerInit是一个特殊的 action 方法,会在服务端渲染期间自动调用。并且只会在服务端渲染期间调用。作用是初始化容器数据,传递给客户端使用。

    export const actions = {
      // vuex 在 nuxt 中特有的 action 方法,该方法会在服务端渲染期间自动调用,并且只会在服务端调用
      // 方法的作用是初始化容器数据,然后传递给客户端使用
      nuxtServerInit({ commit }, { req }) {
        let user = null
        if (req.headers.cookie) {
          // 从 cookie 中获取保存的用户信息,使用cookieParser将cookie字符串转化为对象
          const parsed = cookieparser.parse(req.headers.cookie)
          if (parsed.user) {
            try {
              user = JSON.parse(parsed.user)
            } catch(e) {
            }
          }
        }
        // 将 user 信息保存到 vuex 中
        commit('saveUser', user)
      }
    }
    

nuxt中间件

允许定义一个自定义函数运行在一个页面或一组页面渲染之前

  • 每一个中间件应该放置在middleware/目录下,文件名即为中间件名。
  • 中间件接受一个context作为第一个参数
  • 中间件的执行流程:nuxt.config.js -> 匹配的布局 -> 匹配的页面
  • 使用中间件时需要在组件内通过middleware声明使用那些中间件
    中间件代码示例:
    export default function({store, redirect}) {
      if (!store.state.user) {
        return redirect('/login')
      }
    }
    
    组件内声明使用的中间件
    <script>
    import { getProfile, followUser, unfollowUser } from '@/api/profile'
    import { getArticles } from '@/api/article'
    import articleList from '@/components/article-list';
    
    export default {
      name: 'profileIndex',
      middleware: 'authenticated', // ['authenticated', 'test']
      components: {
        articleList
      },
      async asyncData({ params }) {
        const { data } = await getProfile(params.username)
        return {
          profile: data.profile
        }
      }
    }
    </script>
    

监听路由参数变化

在组件内部通过定义watchQuery,来监听路由参数的变化,然后执行组件方法(asyncData,fetch,validate,layout,…)。

<script>
export default {
  name: 'HomeIndex',
  middleware: 'authenticated',
  data() {
  },
  components: {
    articleList
  },
  /* 监听参数变化,触发 asyncData */
  watchQuery: ['page', 'tag', 'tab'],
  async asyncData({ query }) {
    const page = Number.parseInt(query.page || 1)
    const limit = 10
    const tag = query.tag
    const tab = query.tab || 'global'
    const loadArticles = tab === 'your' ? getFeedArticles : getArticles
    const [articlesRes, tagRes] = await Promise.all([
      loadArticles({
        limit,
        offset: (page - 1) * limit,
        tag,
      }),
      getTags()
    ])
    const { articles, articlesCount } = articlesRes.data
    const { tags } = tagRes.data

    // 为articles的每一项添加 favoriteDisable 属性 
    articles.forEach(item => {
      item.favoriteDisable = false
    })
    return {
      articles,
      articlesCount,
      page,
      limit,
      tags,
      tag,
      tab
    }
  }
}
</script>

nuxt插件

允许在运行Vue应用程序之前执行js插件。
在Vue组件的生命周期内,只有beforeCreate和created会在客户端和服务端被调用。

  • nuxt.js插件一般存放在plugins文件夹下。
  • 插件必须在nuxt.config.js的plugins中注册,插件注册时需要通过~/来表示根目录。
  • 插件文件默认导出一个函数,函数的第一个参数是上下文对象context,其中包括app,route,query,params,store,req,res等属性。

插件示例代码:

import axios from 'axios';

export const request = axios.create({
  baseURL: 'https://conduit.productionready.io'
})

// 通过插件机制获取上下文对象(query,params,req,res,app,store,)
export default ({ store }) => {
  // 添加请求拦截器
  request.interceptors.request.use(function (config) {
    const { user } = store.state
    if (user && user.token) {
      // 在发送请求之前做些什么
      // 注入请求头token
      config.headers = {
        Authorization: `Token ${user.token}`
      }
    }
    return config
  }, function (error) {
    // 对请求错误做些什么
    return Promise.reject(error);
  });
}

在nuxt.config.js中注册插件:

export default {
  router: {
    // base: '/pre/', // 路由前缀
  // 服务信息配置
  server: {
    host: '0.0.0.0', //服务启动ip,0.0.0.0表示服务器的所有ip地址
    port: '3000'
  },
  // 注册插件
  plugins: [
    '~/plugins/request.js',
    '~/plugins/dayjs.js'
  ]
}

SEO优化配置

nuxt.js允许在nuxt.config.js或组件内部定义head属性或方法,来配置页面标题和meta标签。

在nuxt.config.js中配置head属性,将作用于所有组件。
在这里插入图片描述
在组件内部定义head,只在当前组件所在页面生效。
在这里插入图片描述

Nuxt.js项目的打包部署

  1. 首先需要在package.json中配置build和start命令
    在这里插入图片描述

  2. 在nuxt.config.js中添加server配置,此处是配置启动服务的主机和端口,0.0.0.0表示所有网卡的主机
    在这里插入图片描述

  3. 运行yarn build打包nuxt应用,生成.nuxt文件

  4. 将.nuxt、static、package.json、package-lock.js/yarn-lock.js、nuxt.config.js上传到服务器指定文件夹下

  5. 在该文件夹下执行yarn install/npm install命令下载第三方依赖

  6. 运行yarn start 命令启动应用。yarn start会直接从生成的文件中读取文件,不用重新打包生成文件。

  7. 改用pm2管理node服务

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值