从壹开始先后端分离 [ vue + .netcore 补充教程 ] 二八║ Nuxt 基础:面向源码研究Nuxt.js

前言

哈喽你们周五好,又是一个开开心心的周五了,接下来就是三天小团圆啦,这里先祝你们节日快乐咯,但愿都没有加班哈哈,今天公司发了月饼,嗯~时间来不及了,上周应该搞个活动抽中几个粉丝发月饼的,下次吧,这里先预告一下,圣诞节活动,给粉丝送苹果吧哈哈,不过听起来好 low 呀,你们有好的想法能够下边评论或者来群里一块儿交流哟~javascript

说接上文,昨天我们第一次的接触到了一个新的框架 Nuxt《二七║ Nuxt 基础:框架初探》,从概念上,给你们简单说了下这个框架的产生和应用场景,你们学习这一块必定要有必定的 vue 基础,还有就是了解 SSR 服务端渲染的知识和原理,才能作到游刃有余,在以前的一系列文章中,我们已经很详细说明了 vue 的客户端渲染 SPA 的工做原理,今天呢,我们就重点说一下 Nuxt 的运行机制,虽然说 Vue 已经发展了近两年,不过网上的资料仍是比较匮乏,全部的教程也都是一篇搞定,直接就是 如何安装,如何写页面,我是不喜欢这样的,虽然本身懂得的也不是不少,可是仍是想给你们分享下,至少往深层次挖掘一下,这样你们下次使用的时候,内心才有一个普。好啦,立刻开始今天的讲解,今天没有涉及到代码,源码会在下周开始 Code 。html

 

零、今天要完成绿色的部分

 

1、Nuxt 的初衷 —— 将核心专一于UI渲染

来个官方的说法vue

Nuxt.js is a minimalistic framework for server-rendered Vue applications (inspired by Next.js)java

意思就是:node

Nuxt 是服务器呈现的简约应用程序的框架,经过对客户端和服务端基础架构的抽象,Nuxt.js 可让开发者更专一于页面的UI渲染。做用就是在 node.js 上进一步封装,而后省去咱们搭建服务端环境的步骤,只须要遵循这个库的一些规则就能轻松实现 SSR。webpack

说到这里你们可能稍微不是很明白,举个最简单的栗子( 这里就不说那个老生常谈的 SEO 了,这个原本就不存在于咱们的客户端渲染中 ):我们以前在用 vue-cli 脚手架搭建项目的时候,每次添加一个页面,都须要去配置路由 router ,是吧,而后呢,有时候运气很差了,在多层嵌套的路径配置时候,还时不时的不起做用,这些原本不该该属于咱们开发的工做的,Nuxt 就很好的解决了这个问题,Nuxt.js 根据 pages 目录结构去生成 vue-router 配置,也就是说 pages 目录的结构直接影响路由结构,仍是实时监听的方式。web

再举个栗子:以前我们在配置页面的时候,都是经过 index.html 提供一个模板,而后再一个 app.vue 的入口程序,而后将全部的 *.vue 组件经过路由填充进去,这个我们第一次使用的时候,感受仍是很嗨皮的,不过长时间的使用的时候,就会发现其实有些问题,好比咱们若是不想让某些组件路由显示出来,而后只能在配置里处理,经过配置路由是否显示等操做,显然又多了一个步骤,不过在 nuxt 框架中,咱们有了动态路由,只须要在名字前加上一个下划线 _ ,好比 _id.vue ,这样就能轻松搞定,是否是很方便,这个我们之后会讲到,这里暂时就不深刻了。vue-router

 

再好比咱们在以前说到组件的时候,用到了这个图,在入门页面中,将咱们的全部组件按需加载,可是若是咱们的项目有多个布局须要怎么办?好比咱们商城首页,和我的中心首页有时候是不同的,当时若是你必定要保持一个风格也是能够的,可是感受这么配置仍是怪怪的。vue-cli

 

不过在 Nuxt.js 框架中,咱们有了新的变化,layouts对应目录中的layouts文件夹,默认pages下的页面走的都是 layouts/default.vue 布局方式,可是若是本身定义的话,也能够新增新的布局页。其中<nuxt/>能够相似vueslot插槽的概念,(若是对 slot 插槽不是很明白,能够看以前的文章 《二十║Vue基础终篇:组件详解+项目说明》的第四节),pages/**.vue中的内容会插在<nuxt/>内。npm

在我们的第二个项目,基于 Nuxt 的我的博客里,我就新建了一个 blog.vue 的布局页面,定制了一个博客布局:

 

<template>
  <div class="layout-default">
      <cl-header></cl-header>//定制博客页头
      <nuxt class="layout-main"/>//nuxt 插入内容
      <cl-footer></cl-footer>//定制博客页脚
      <div class="layout-bg"></div>
  </div>
</template>

<script type="text/javascript">
import clHeader from "~/components/layout/header.vue";
import clFooter from "~/components/layout/footer.vue";
export default {
    data () {
        return {};
    },
    mounted () {
        this.$store.dispatch("initUser");
    },
    components: {
        clHeader,
        clFooter
    }
};
</script>

这个就是 nuxt 的分层页面布局。

由上边的栗子能够看出来,nuxt 是经过封装,将核心原理隐藏起来,重点实现咱们的UI展现,这么作的目的就是更有经历去作 SSR 服务端渲染了,你们还记得我们在讲 SSR 的时候么,须要很复杂的操做配合才能实现,因此,很好的底层封装,是咱们更好开发 SSR 框架的第一步。

 

2、Nuxt 的工做流程 —— 四步走

仍是老规矩,一言不合就放图系列

简单来讲,当你访问一个基于Nuxt.js构建的页面时,发生了的事情以下:

一、当用户访问应用程序, 若是 store 中定义了 nuxtServerInit action,Nuxt.js 将调用它更新 store。

二、将加载即将访问页面所依赖的任何中间件。Nuxt 首先从 nuxt.config.js 这个文件中,加载全局依赖的中间件,以后检测每一个相应页面对应的布局文件 ,最后,检测布局文件下子组件依赖的中间件。以上是中间件的加载顺序。

三、若是要访问的路由是一个动态路由, 且有一个相应的 validate() 方法路由的validate 方法,讲进行路由校验。

四、Nuxt.js 调用 asyncData() 和 fetch() 方法,在渲染页面以前加载异步数据。asyncData() 方法用于异步获取数据,并将 fetch 回来的数据,在服务端渲染到页面。 用 fetch() 方法取回的将数据在渲染页面以前填入store。

最后一步, 将全部数据渲染到页面。

其中核心配置文件 nuxt.config.js 是比较重要的一个文件,整个系统的配置和插件都须要这个文件来执行,感受就像是咱们 vue-cli 脚手架里的 main.js 入口文件同样,具体的配置以及使用我们在之后的文章中再说明吧。上边我们说到了仍是工做流程,那 nuxt 到底封装了怎么的工做机制,才能很好的执行这个流程呢,重头戏来了

 

3、面向源码简要分析 Nuxt 的工做机制

一、经过 .nuxt 文件来执行咱们的工做流程

 刚刚我们说到了,nuxt.config.js 就像是一个 main.js 入口同样,这个时候细心的你必定会发现,欸?为何 nuxt 框架没有 router 呢,那咱们的路由该如何配置呢,不要着急,nuxt 已经给咱们封装了,咱们无需把注意力放到这上边,这个时候咱们执行 

npm run dev

会发现多了一个文件(由于昨天咱们已经执行过了,如今就不须要执行了)。

 

这个是咱们项目生成的临时文件,咱们项目运行时候配置的文件都是在这里,你们能够看到这里的路由文件,没错,这个就是系统自动给咱们配置的路由文件,根据咱们的 pages 文件夹路径生成的,你们还能够看到,由app.js ,client.js 和 server.js 这两个就是相似咱们的 SSR 中配置的那个 server.js 入口文件,而后还有 middleware.js 中间件文件,其实这个时候咱们大概能懂了,上边咱们说的工做流程,走的就是这个 临时文件.nuxt 文件夹中的内容,可是这个文件夹是如何生成的呢,你们请往下看。

二、.nuxt 是如何产生的

本文主要研究nuxt的运行原理,分析它从接收一条nuxt指令,到完成指令背后所发生的一系列事情,在开始本文以前,请读者务必亲自体验过nuxt.js的使用,而且具有必定的vue.js相关开发经验。

经过查看nuxt.js工程目录下的package.json文件,咱们能够看到下列几条指令:

"scripts": { 
    "dev": "nuxt",           //    开启一个监听3000端口的服务器,同时提供hot-reloading功能
    "build": "nuxt build", //构建整个应用,压缩合并JS和CSS文件(用于生产环境)
    "start": "nuxt start", //    开启一个生产模式的服务器(必须先运行nuxt build命令)
    "generate": "nuxt generate" //构建整个应用,并为每个路由生成一个静态页面(用于静态服务器)
} 

我们还历来没有看过咱们的依赖包哈,今天就来看看,打开咱们的 node_modules 文件夹下的 nuxt工程文件夹 进入到到bin目录,咱们能够看到5个文件:

我们就说一下 dev 是如何工做的,我们先找到一个片断,发现基本是执行了如下几个步骤:

try {
    nuxt = new Nuxt(options)//实例化一个 nuxt 类
    builder = new Builder(nuxt)
    instance = { nuxt, builder }
  } 。。。。

  return (
    Promise.resolve()
      .then(
        () =>
          oldInstance && oldInstance.builder
            ? oldInstance.builder.unwatch()
            : Promise.resolve()
      )
      // Start build 开始执行 build() 方法
      .then(() => builder.build())
      // Close old nuxt after successful build
      .then(
        () =>
          oldInstance && oldInstance.nuxt
            ? oldInstance.nuxt.close()
            : Promise.resolve()
      )
      // Start listening 开启监听服务端口
      .then(() => nuxt.listen(port, host))
      // Pass new nuxt to watch chain
      .then(() => instance)
      // Handle errors
      .catch(err => onError(err, instance))
  )
}

三、那什么是 nuxt() 类,它又是执行了什么样的方法呢?

这个时候,咱们继续看源码,进入到nuxt/lib目录,咱们能够看到以下的文件目录结构:

├── app                       
├── builder                     
├── common                      
└── core                        
     ├── middleware                           
     │   ├── index.js             
     │   ├── meta.js             
     │   ├── module.js          
     │   ├── muxt.js           
     │   └── render.js          
     └── index.js                

 

 你们看这个熟悉不熟悉?!没错,就是和咱们上边项目执行的时候生成的临时文件是相似的,请和上边的 .nuxt 临时文件夹作对比。

 网上有个很好的总结,如图:

 

 

上图中每一步均可以在具体的代码中自行浏览。在用户输入指令并实例化了Nuxt()类之后,实例化出来的nuxt对象就会执行图中打了绿色对勾的几个方法:build(), render(), renderRoute(), renderAndGetWindow()以及generate()方法。

同时,Nuxt()类也提供了一个close()公有方法,用于关闭其所开启的服务器。

四、build() 进行编译,生成 .nuxt 临时文件

而后就是执行的 build() 方法:

简单来讲,build()方法在判断完运行条件后,会先初始化产出目录 .nuxt ,而后经过不一样目录下的文件结构来生成一系列的配置,写入模板文件后输出到.nuxt目录。接下来,则会根据不一样的开发环境来调用不一样的webpack 配置,运行不一样的 webpack 构建方案。

 

五、render.js文件 打包输出渲染

在 nuxt/lib/core 目录下找到 render.js 文件,它包含着咱们即将要分析的三个方法:render(), renderRoute(), renderAndGetWindow()。

 

经过这张图片,咱们能够知道nuxt对于处理“客户端渲染”与“服务端渲染”的逻辑实际上是很是清晰的。

首先,在render()方法在处理完一系列的路径问题后,会调用renderRoute()方法,获取响应所需内容并完成响应。

其中renderRoute()方法会判断当前响应是否应执行服务端渲染。若是是,则调用vue提供的bundleRenderer()方法,把html内容渲染完毕之后再总体输出;若是不是,则直接输出一个<div></div>字符串,交由客户端渲染。

 // Call renderToString from the bundleRenderer and generate the HTML (will update the context as well)
    let APP = await this.bundleRenderer.renderToString(context)

    if (!context.nuxt.serverRendered) {
      APP = '<div id="__nuxt"></div>'
    }

 

最后,经过renderAndGetWindow()来检查输出的html是否存在问题,而后发出通知,代表html可用。

 

 4、结语

 好啦,今天由于时间的问题,暂时就说到这里吧,其实这里还有不少知识点没有说到,那咱们就在以后的项目中,慢慢给你们说吧,请持续关注哟,能够点个赞👍吧,真的是辛苦哈哈哈