nuxt入门
阅读原文时间:2023年07月15日阅读:1

之前一直都是做vue-spa单页面,不利于SEO。而便于SEO的SSR(服务器端渲染)多页应用,可以使用nuxt.js这个框架来实现

(0)nuxt安装
npx create-nuxt-app <项目名>

以下是按生成的目录文件进行学习了解
(1)资源目录assets 与 static目录 - 资源文件部分
assets:静态资源文件被 Webpack 做构建编译处理
《1》css文件中引入资源
//注意~后无斜杠
background:url("~assets/banner.svg")
《2》vue文件中:

  static:无需Webpack编译处理  
    资源相对路径来解决路径问题  
    《1》vue文件中  
      <img src="../static/1.png"/>

(2)布局目录layouts / 页面pages - 视图部分
《1》默认布局(layouts/default.vue)

《2》自定义布局
1.创建文件layouts/self.vue

2.页面中使用pages/index
export default {
layout: 'self' //指定自定义模板
}

《3》错误页面
定制化错误页面:.layouts/error.vue

    <template>  
      <div class="container">  
        <h1 v-if="error.statusCode === 404">页面不存在</h1>  
        <h1 v-else>应用发生错误异常</h1>  
        <nuxt-link to="/">首 页</nuxt-link>  
      </div>  
    </template>

    <script>  
      export default {  
        props: \['error'\],  
        layout: 'self' // 你可以为错误页面指定自定义的布局  
      }  
    </script> 

《4》pages页面配置项:
asyncData、fetch、head、layout、loading、transition、scrollToTop、validate、middleware

  此部分查看Api - 页面部分

(3)插件目录plugins(全局配置)- 插件部分
《1》使用插件
a.plugins/vue-notifications.js
import Vue from 'vue'
import VueNotifications from 'vue-notifications'
Vue.use(VueNotifications)

  b.nuxt.config.js内配置  
    module.exports = {  
      plugins: \['~/plugins/vue-notifications'\]  
    }

《2》注入Vue 实例
a.plugins/vue-inject.js:
import Vue from 'vue'
Vue.prototype.$myInjectedFunction = (string) => console.log("This is an example", string)

  b.nuxt.config.js  
    export default {  
      plugins: \['~/plugins/vue-inject.js'\]  
    }

  c.pages页面使用:  
     this.$myInjectedFunction('test')

《4》注入context
a.plugins/ctx-inject.js:
export default ({ app }, inject) => {
// Set the function directly on the context.app object
app.myInjectedFunction = (string) => console.log('Okay, another function', string)
}

  b.nuxt.config.js:  
    export default {  
      plugins: \['~/plugins/ctx-inject.js'\]  
    }

  c.pages页面使用:  
    export default {  
      asyncData(context){  
        context.app.myInjectedFunction('ctx!')  
      }  
    }

《5》同时注入 - inject方法
a.plugins/combined-inject.js
export default ({ app }, inject) => {
inject('myInjectedFunction', (string) => console.log('That was easy!', string))
}

  b.调用以上两种方式都行

《6》只在客户端使用的插件nuxt.config.js
export default {
plugins: [
{ src: '~/plugins/both-sides.js' }, //both client & server
{ src: '~/plugins/client-only.js', mode: 'client' },
{ src: '~/plugins/server-only.js', mode: 'server' }
]
}

(4)路由部分:
依据 pages 目录结构自动生成 vue-router 模块的路由配置
(1)动态路由:
pages/
--| _slug/
-----| comments.vue
-----| index.vue
--| users/
-----| _id.vue
--| index.vue

  router: {  
    routes: \[  
      {  
        name: 'index',  
        path: '/',  
        component: 'pages/index.vue'  
      },  
      {  
        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'  
      }  
    \]  
  }

  路由参数校验:pages/users/\_id.vue  
    validate ({ params }) {  
      // 必须是number类型  
      return /^\\d+$/.test(params.id)  
    }

(2)嵌套路由
注意:
1.添加一个 Vue 文件,同时添加一个与【该文件同名的目录】用来存放子视图组件。
2.别忘了在父组件(.vue文件) 内增加 用于显示子视图内容。
pages/
--| users/
-----| _id.vue
-----| index.vue
--| users.vue

        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'  
                }  
              \]  
            }  
          \]  
        }

(5).中间件目录middleware

  • 用于存放应用的中间件
    注意:
    1.中间件允许您定义一个自定义函数运行在一个页面或一组页面渲染之前(类似vue-router导航守卫)
    2.文件名的名称将成为中间件名称(middleware/auth.js将成为 auth 中间件)
    3.一个中间件接收 context 作为第一个参数 (context - 见APi)
    export default function (context) {
    context.userAgent = process.server ? context.req.headers['user-agent'] : navigator.userAgent
    }
    4.中间件执行流程顺序:nuxt.config.js -> 匹配布局 -> 匹配页面
    5.案例:
    1.middleware/stats.js
    import axios from 'axios'
    export default function ({ route }) {
    return axios.post('http://my-stats-api.com', {
    url: route.fullPath
    })
    }

    2.在nuxt.config.js 、 layouts 或者 pages 中使用中间件  
      (1)nuxt.config.js:在每个路由改变时被调用  
          module.exports = {  
            router: {  
              middleware: 'stats'  
            }  
          }
    
      (2)layouts/default.vue 或者 pages/index.vue  
          export default {  
            middleware: 'stats'  
          }

(6)Store 目录 - Vuex 状态树

    1.store/index.js 跟其他方式没啥差别

    2.模块化使用场景一:
      store/
        --| todos/ (文件夹模块,文件名为对应store属性名)
          -----| state.js
          -----| getters.js
          -----| mutations.js
          -----| actions.js

      页面使用:
        import { mapMutations, mapGetters } from 'vuex'
        …mapMutations({toggle: '模块名/对应方法名'})

        例子:
          …mapMutations({toggle: 'todos/test'})
          this.$store.commit('todos/test', { 参数 })

    3.模块化使用场景二:
      store/
        --| articles/ (文件夹模块)
          -----| comments.js
        --| articles.js (根目录文件模块)

      页面使用:
        import { mapGetters } from 'vuex'
        …mapGetters({
          articles: 'articles/get', //根目录文件模块
          comments: 'articles/comments/get' //文件夹模块
        })

(7)异步数据(asyncData)
1.限于页面组件 - 每次加载之前被调用(beforeCreate之前执行)
2.由于asyncData方法是在组件 初始化 前被调用的,所以在方法内是没有办法通过 this 来引用组件的实例对象。
2.第一个参数被设定为当前页面的上下文对象:即参数为对象context,对象属性:
https://zh.nuxtjs.org/api/context

context:{  
  app: Vue 根实例 //context.app.$axios,  
  params:route.params 的别名,  
  query:route.query 的别名,  
  error:展示错误页,  
  redirect:重定向用户请求到另一个路由,redirect(\[status,\] path \[, query\])  
  env:nuxt.config.js 中配置的环境变量,见 环境变量 api(嵌套路由例子-见demo)  
  store:Vuex 数据,  
  route:Vue Router 路由实例,

  isDev:开发 dev 模式,  
  isHMR:模块热替换,  
  req:  
  res:  
  nuxtState:  
  beforeNuxtRender(fn):  
}

使用回调/动态路由参数/错误处理:  
  使用配置它的文件或文件夹的名称访问动态路径参数:\_id.vue  
  export default {  
    asyncData ({ params }, callback) {  
      axios.get(\`https://my-api/posts/${params.id}\`)  
      .then((res) => {  
        callback(null, { title: res.data.title })  
      }).catch((e) => {  
        //statusCode: 指定服务端返回的请求状态码  
        error({ statusCode: 404, message: 'Post not found' })  
      })  
    }  
  }

获取query参数  
  https://zh.nuxtjs.org/api/pages-watchquery  
  watchQuery: \['page'\]

  如果定义的字符串发生变化,将调用所有组件方法(asyncData, fetch, validate, layout, ...)

《8》过渡动效
1.全局过渡动效设置
Nuxt.js 默认使用的过渡效果名称为 page
例子:
//assets/main.css
.page-enter-active, .page-leave-active { transition: opacity .5s; }
.page-enter, .page-leave-active {
opacity: 0;
}
//nuxt.config.js
module.exports = {
css: [
'assets/main.css'
]
}
//使用
自动全局使用

2.页面过渡动效设置  
  页面样式:  
    .bounce-enter-active { animation: bounce-in .8s; }  
    .bounce-leave-active { animation: bounce-out .5s; }

    @keyframes bounce-in {  
      0% {transform: scale(0)}  
      50% { transform: scale(1.5) }  
      100% { transform: scale(1) }  
    }

    @keyframes bounce-out {  
      0% { transform: scale(1) }  
      50% { transform: scale(1.5) }  
      100% {  transform: scale(0) }  
    }

  页面运用:  
    export default {  
      transition: 'bounce', //动画  
    }  
    或者:  
    export default {  
      transition: {  
        name: 'test',  
        mode: 'out-in'  
      }  
    }

3.transition为函数类型时运用  
  见demo案例  
  <template>  
    <div class="container">  
      <NuxtLink v-if="page > 1" :to="'?page=' + (page - 1)">< Prev</NuxtLink>  
      <a v-else class="disabled">< Prev</a>  
      <span>{{ page }}/{{ totalPages }}</span>  
      <NuxtLink v-if="page < totalPages" :to="'?page=' + (page + 1)">Next ></NuxtLink>  
      <a v-else class="disabled">Next ></a>  
      <ul>  
        <li v-for="user in users" :key="user.id">  
          <img :src="user.avatar" class="avatar" />  
          <span>{{ user.first\_name }} {{ user.last\_name }}</span>  
        </li>  
      </ul>  
    </div>  
  </template>

  <script>  
  export default {  
    // Watch for $route.query.page to call Component methods (asyncData, fetch, validate, layout, etc.)  
    watchQuery: \['page'\],  
    // Key for <NuxtChild> (transitions)  
    key: to => to.fullPath,  
    // Called to know which transition to apply  
    transition (to, from) {  
      if (!from) { return 'slide-left' }  
      return +to.query.page < +from.query.page ? 'slide-right' : 'slide-left'  
    },  
    async asyncData ({ query }) {  
      const page = +query.page || 1  
      const data = await fetch(\`https://reqres.in/api/users?page=${page}\`).then(res => res.json())

      return {  
        page: +data.page,  
        totalPages: data.total\_pages,  
        users: data.data  
      }  
    }  
  }  
  </script>

(9)自定义加载
第一种:自定义加载组件
1.components/loading
loading.vue

  2.nuxt.config.js  
    loading: '~/components/loading.vue'

第二种:加载进度条  
  1.nuxt.config.js  
    loading: { color: 'red' } //修改进度条样式

  2.pages/index 页面中  
    this.$nuxt.$loading.start()  //启动加载条  
    this.$nuxt.$loading.finish() //介乎加载条

(10)其他:
process.static:是否为true来判断应用是否通过nuxt generator生成
process.server:当值为 true 表示是当前执行环境为服务器中
控制插件中的某些脚本库只在服务端使用
案例:process.static ? 'static' : (process.server ? 'server' : 'client')