Nuxt介绍

Nuxt.js是什么

Nuxt.js 是一个基于 Vue.js 的服务端渲染应用框架,它可以帮我们轻松的实现同构应用。

通过对客户端/服务端基础架构的抽象组织,Nuxt.js 主要关注的是应用的 UI渲染。

我们的目标是创建一个灵活的应用框架,你可以基于它初始化新项目的基础结构代码,或者在已有 Node.js 项目中使用 Nuxt.js。

Nuxt.js 预设了利用 Vue.js 开发服务端渲染的应用所需要的各种配置。

除此之外,我们还提供了一种命令叫: nuxt generate ,为基于 Vue.js 的应用提供生成对应的静态站点的功能。

我们相信这个命令所提供的功能,是向开发集成各种微服务(Microservices)的 Web 应用迈开的新一 步。

作为框架,Nuxt.js 为 客户端/服务端 这种典型的应用架构模式提供了许多有用的特性,例如异步数据 加载、中间件支持、布局支持等非常实用的功能。

特性
  • 基于 Vue.js
    • Vue、Vue Router、Vuex、Vue SSR
  • 自动代码分层
  • 服务端渲染
  • 强大的路由功能,支持异步数据
  • 静态文件服务
  • ES2015+ 语法支持
  • 打包和压缩 JS 和 CSS
  • HTML 头部标签管理
  • 本地开发支持热加载
  • 集成 ESLint
  • 支持各种样式预处理器: SASS、LESS、 Stylus 等等
  • 支持 HTTP/2 推送
创建项目

Nuxt提供两种方式创建项目:

  • 使用create-nuxt-app工具
  • 手动创建

使用手动创建过程

  1. 初始化项目目录并安装nuxt

    # 创建示例项目
    mkdir nuxt-js-demo
    # 进入示例项目目录中
    cd nuxt-js-demo
    # 初始化 package.json 文件
    npm init -y
    # 安装 nuxt
    npm innstall nuxt
    

    在package.json中scripts添加

    "scripts": {
        "dev": "nuxt"
    }
    
  2. 创建页面并启动项目

    在根目录下创建pages目录,并创建index.vue文件

    <template>
      <div>
        <h1>hello nuxt.js</h1>
      </div>
    </template>
    
    <script>
    export default {
      name: 'HomePage'
    }
    
    </script>
    <style>
    </style>
    

    启动项目

    npm run dev

    现在我们的应用运行在 http://localhost:3000

    示例地址

  3. Nuxt中的基础路由

    Nuxt会依据pages目录中的所有.vue文件生成应用的路由配置

    假设 pages 的目录结构如下:

    pages/
    --| user/
    -----| index.vue
    -----| one.vue
    --| index.vue
    

    那么,Nuxt自动生成路由配置如下

    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.js 依据 pages 目录结构自动生成 vue-router 模块的路由配置。

  • 路由导航

    • a标签
    • <nuxt-link>组件
    • 编程时导航
  • 动态路由

    在 Nuxt.js 里面定义带参数的动态路由,需要创建对应的以下划线作为前缀的 Vue 文件 或 目录。 以下目录结构:

    pages/
    --| _slug/
    -----| comments.vue
    -----| index.vue
    --| users/
    -----| _id.vue
    --| index.vue
    

    Nuxt生成对应的路由配置表为:

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

    你会发现名称为 users-id 的路由路径带有 :id? 参数,表示该路由是可选的。如果你想将它设置为必 选的路由,需要在 users/_id 目录内创建一个 index.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.config.js文件

    // nuxt配置模块
    module.exports = {
      router: {
        // http://localhost:3000/app/ 
        base: '/app/',
        // 扩展路由配置
        // routes 一个数组,路由配置表
        // resolve:解析路由组件路径
        extendRoutes (routes, resolve) {
          routes.push({
            // name: 'custom',
            // path: '*',
            // component: resolve(__dirname, 'pages/404.vue')
            path: '/hello',
            name: 'hello',
            component: resolve(__dirname, 'pages/about.vue')
          })
        }
      }
    }
    

    示例地址

视图
image-20210324085229414.png
模板
<!DOCTYPE html>
<html {{ HTML_ATTRS }}>
  <head {{ HEAD_ATTRS }}>
    {{ HEAD }}
  </head>
  <body {{ BODY_ATTRS }}>
    <!-- 渲染内容最终会注入到这里 -->
    <h1>app.html</h1>
    {{ APP }}
  </body>
</html>
布局

根目录下创建layouts目录,default.vue为默认布局组件

  1. 默认布局

    <template>
      <div>
        <h1>layouts/default组件</h1>
        <!-- 页面出口,类似于子路由出口 -->
        <nuxt />
      </div>
    </template>
    
    <script>
    export default {
      name: 'layoutsDefault'
    }
    
    </script>
    <style>
    </style>
    
  2. 自定义布局

    新增foo.vue组件

    <template>
      <div>
        <h1>layouts/foo组件</h1>
        <!-- 页面出口,类似于子路由出口 -->
        <nuxt />
      </div>
    </template>
    
    <script>
    export default {
      name: 'layoutsFoo'
    }
    
    </script>
    <style>
    </style>
    

    在pages中的路由中添加layout: 'foo'

    <template>
      <div>
        <h1>hello nuxt.js</h1>
      </div>
    </template>
    
    <script>
    export default {
      name: 'HomePage',
      layout: 'foo',
      // 默认 layout设置自定义的布局组件 未设置的路由还会走默认的布局组件
      // layout: 'default'
    }
    
    </script>
    <style>
    </style>
    

    示例地址

页面
  • 默认 Meta 标签

    // nuxt.config.js
    export default {
      head: {
        title: 'my website title',
        meta: [
          { charset: 'utf-8' },
          { name: 'viewport', content: 'width=device-width, initial-scale=1' },
          {
            hid: 'description',
            name: 'description',
            content: 'my website description'
          }
        ],
        link: [{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }]
      }
    }
    
  • 个性化特定页面的 Meta 标签

    // 指定页面pages/index.vue
    // 设置个性化meta标签 设置页面title和description对SEO非常有用
    head () {
        return {
            title: `${this.article.title} - RealWorld`,
            meta: [
                { hid: 'description', name: 'description', content: this.article.description }
            ]
        }
    }
    
异步数据

Nuxt.js 扩展了 Vue.js,增加了一个叫* asyncData *的方法,使得我们可以在设置组件的数据之前能 异步获取或处理数据。

// 当你想要动态页面内容有利于 SEO 或者是提升首屏渲染速度的时候,就在 asyncData 中发请求拿数据
// 注意事项:1.只能在页面组件中使用,子组件中可通过props获取父组件异步数据 2.没有this,在组件初始化之前被调用
// 调用时机:1.获取服务端渲染数据(确保异步数据在渲染到客户端之前已经填充渲染完成,提高渲染速度,有利于SEO) 2.客户端路由更新之前也会被调用
async asyncData () {
    // 此行输出在服务端执行
    // 在浏览器也会输出 包裹在Nuxt SSR中
    console.log('async Data')
    const res = await axios({
        method: 'GET',
        url: 'http://localhost:3000/data.json'
    })
    // 返回的对象可以直接在页面组件使用
    // 在vue调试工具中有了posts和title
    // asyncData返回的数据会和data中的混合
    return res.data
},

    // 如果是非异步数据或者普通数据,则正常的初始化到 data 中即可
    data () {
        return {
        }
    }

示例地址

上下文对象

可通过 context 来了解该对象的所有属性和方法。

async asyncData (context) {
    console.log(context)
    const { data } = await axios({
        method: 'GET',
        url: 'http://localhost:3000/data.json'
    })
    const id = Number.parseInt(context.params.id)
    return {
        article: data.posts.find(item => item.id === id)
    }
}

context 变量的可用属性一览:

属性字段 类型 可用 描述
app Vue 根实例 客户端 & 服务端 包含所有插件的 Vue 根实例。例如:在使用 axios 的时候,你想获取 axios 可以直接通过 context.app.axios 来获取
isClient Boolean 客户端 & 服务端 是否来自客户端渲染(废弃。请使用 process.client )
isServer Boolean 客户端 & 服务端 是否来自服务端渲染(废弃。请使用 process.server )
isStatic Boolean 客户端 & 服务端 是否来自 nuxt generate 静态化(预渲染)(废弃。请使用 process.static )
isDev Boolean 客户端 & 服务端 是否是开发 dev 模式,在生产环境的数据缓存中用到
isHMR Boolean 客户端 & 服务端 是否是通过模块热替换 webpack hot module replacement (仅在客户端以 dev 模式)
route Vue Router 路由 客户端 & 服务端 Vue Router 路由实例
store Vuex 数据 客户端 & 服务端 Vuex.Store 实例。只有vuex 数据流存在相关配置时可用
env Object 客户端 & 服务端 nuxt.config.js 中配置的环境变量,见 环境变量 api
params Object 客户端 & 服务端 route.params 的别名
query Object 客户端 & 服务端 route.query 的别名
req http.Request 服务端 Node.js API 的 Request 对象。如果 Nuxt 以中间件形式使用的话,这个对象就根据你所使用的框架而定。nuxt generate 不可用
res http.Response 服务端 Node.js API 的 Response 对象。如果 Nuxt 以中间件形式使用的话,这个对象就根据你所使用的框架而定。nuxt generate 不可用
redirect Function 客户端 & 服务端 用这个方法重定向用户请求到另一个路由。状态码在服务端被使用,默认 302 redirect([status,] path [, query])
error Function 客户端 & 服务端 用这个方法展示错误页:error(params) 。params 参数应该包含 statusCode 和 message 字段
nuxtState Object 客户端 Nuxt 状态,在使用 beforeNuxtRender 之前,用于客户端获取 Nuxt 状态,仅在 universal 模式下可用
beforeNuxtRender(fn) Function 服务端 使用此方法更新 NUXT 在客户端呈现的变量,fn 调用 (可以是异步) { Components, nuxtState } ,参考 示例

示例地址

生命周期
image-20210324093101030.png
中间件

使用中间件对用户进行身份验证,并允许他们在验证通过后访问页面

// middle/authenticated.js
/**
 * 每一个中间件需要放置在middleware/目录,文件名就是中间件名称authenticated中间件
 * param:context上下文对象
 * 中间件执行流程:
 * 1.nuxt.config.js
 * 2.匹配布局layouts
 * 3.匹配页面pages
 * 验证是否登录的中间件
 */
export default function ({ store, redirect }) {
  // If the user is not authenticated
  if (!store.state.user) {
    return redirect('/login')
  }
}
// notAuthenticated.js
export default function ({ store, redirect }) {
  // If the user is authenticated redirect to home page
  // 如果已登录 跳转到首页
  if (store.state.user) {
    return redirect('/')
  }
}

使用,在对应layouts或pages中添加 middleware: 'authenticated'

插件

Nuxt.js 允许在运行 Vue.js 应用程序之前执行 js 插件,需要使用自己的库或第三方模块时特别有用。

例如:封装一个axios请求插件

首先新增文件plugins/request.js

/**
 * 基于axios封装的请求模块
 */
import axios from 'axios'

// 创建请求对象
export const request = axios.create({
  baseURL: 'https://conduit.productionready.io/',
  timeout: 60000
})

// 通过插件机制获取到上下文对象
// export default (context)
export default ({ store }) => {
  // console.log(context)
  // 请求拦截器
  // 任何请求都要经过请求拦截器
  // 可以在拦截器中做公共业务处理 如设置token
  request.interceptors.request.use(function (config) {
    // Do something before request is sent
    // 请求就会经过这里
    /**
     * 需要拿到vuex中的user对象
     * import store from '@/store'
     * 因为store都是通过export按需导出
     * 需要按需加载import { state } from '@/store'
     * 此时拿到的state是一个函数 需要调用一下此函数
     * 这样拿到的数据永远是null
     * 不同于客户端渲染 所以需要放入到plugins中
     */
    const { user } = store.state
  
    if (user && user.token) {
      config.headers.Authorization = `Token ${user.token}`
    }
  
    // 返回 config 请求配置对象
    return config
  }, function (error) {
    // 如果请求失败(此时请求还没有发出去)就会进入这里
    // Do something with request error
    return Promise.reject(error)
  })
}

然后, 在 nuxt.config.js 内配置 plugins 如下:

module.exports = {
  plugins: ['~/plugins/request']
}

也可以将全局过滤器封装为插件使用

模块

模块是 Nuxt.js 扩展,可以扩展其核心功能并添加无限的集成。

详细介绍

Vuex状态管理

Nuxt.js 会尝试找到 src 目录(默认是应用根目录)下的 store目录,如果该目录存在,它将做以下的事情:

  1. 引用 vuex 模块
  2. 将 vuex模块 加到 vendors 构建配置中去
  3. 设置 Vue根实例的store配置项

nuxtServerInit 方法

export const actions = {
  // nuxtServerInit 是一个nuxt提供的特殊的 action 方法
  // 这个 action 会在服务端渲染期间自动调用,且仅在服务端中运行
  // 作用:初始化容器数据,以及需要传递数据给客户端使用的数据
  // commit提交mutations的方法 req服务端渲染期间的请求对象
  nuxtServerInit ({ commit }, { req }) {
    if (req.session.user) {
      commit('user', req.session.user)
    }
  }
}

详细介绍

Nuxt 渲染流程

下图阐述了 Nuxt.js 应用一个完整的服务器请求到渲染(或用户通过 <nuxt-link> 切换路由渲染页面)的流程:

image-20210324094518708.png

  1. 调用nuxtServerInit方法,服务端操作store,实质上是一个Action

  2. middleware中间件运行,可以使用在nuxt.config.js、layouts、pages中,会在页面初始化之前被调用

  3. validate()校验参数,可以让你在动态路由对应的页面组件中配置一个校验方法用于校验动态路由参数的有效性。比如对路由参数验证失败,如果校验方法返回的值不为 true, Nuxt.js 将自动加载显示 404 错误页面

  4. asyncData方法会在组件(限于页面组件)每次加载之前被调用,是运行在page页面加载前的一个方法,这个方法只能在页面组件中使用,这个方法的作用很多,比如:ajax数据请求、操作state数据、页面重定向等等

    asyncData运行在服务器实现render之前,因此就实现了服务端渲染

  5. fetch方法与asyncData调用时机相同,不同之处在于asyncData既可以充应用的状态树(store)数据,也可以设置组件的数据,而fetch 方法用于在渲染页面前填充应用的状态树(store)数据, 不会设置组件的数据。

  6. 渲染页面

  7. 渲染完成之后开始执行vue的生命周期

`

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,240评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,328评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,182评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,121评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,135评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,093评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,013评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,854评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,295评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,513评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,678评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,398评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,989评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,636评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,801评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,657评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,558评论 2 352

推荐阅读更多精彩内容