vite 下一代前端开发与构建工具(一)

vite是什么

vite是尤大在更新vue3以后,再次开发的一个新的构建工具。那什么构建工具让尤大在过年都在疯狂更新呢?它到底有什么魔力呢?javascript

WechatIMG19.png 在vite开发出来以后,就连webpack团队的核心成员都在感叹css

WechatIMG18.png

Vite(法语意思是 “快”,发音为 /vit/,相似 veet)是一种全新的前端构建工具。你能够把它理解为一个开箱即用的开发服务器 + 打包工具的组合,可是更轻更快。Vite 利用浏览器原生的 ES 模块支持和用编译到原生的语言开发的工具(如 esbuild)来提供一个快速且现代的开发体验。html

注意:vite是一个全新的前端构建工具(重点:构建工具),不少小伙伴总在问vite和vue-cli的区别。vue-cli是一个脚手架工具,用于自动生成vue.js+webpack的项目模板。方便咱们快速开发。而vite是一个构建工具,用于咱们对项目进行构建(相似于webpack的功能)。而在将来不排除vue-cli里面会集成vite前端

构建工具的前世此生

构建工具的做用

构建工具:用来让咱们再也不作机械重复的事情,解放咱们的双手的。
在早期开发过程当中。有不少令咱们不爽的地方vue

    1. js是弱类型
    1. 手动维护依赖很麻烦
    1. 浏览器的兼容性
    1. 没有热更新

.......java

因而 前端构建工具应运而生。构建工具能够帮助咱们作如下工做node

    1. 代码检查
    1. 代码压缩,混淆
    1. 依赖分析,打包
    1. 语言编译(好比ts转化js,scss转化css)

...react

在了解vite以前咱们先了解一下其余的构建工具,这样咱们才能更好的对比vite的强大之处。
前端的构建工具备不少。好比 Grunt Gulp FIS3 Webpack Rollup Parcel snowpack vite 接下来我会挑几个构建工具进行简单的介绍webpack

常见的打包工具的工做流

Gulp

Gulp.js 是一个自动化构建工具,开发者可使用它在项目开发过程当中自动执行常见任务。Gulp.js 是基于 Node.js 构建的,利用 Node.js 流的威力,你能够快速构建项目git

var gulp = require('gulp');
    var jshint = require('gulp-jshint');
    var concat = require('gulp-concat');
    var rename = require('gulp-rename');
    var uglify = require('gulp-uglify');

    // Lint JS
    gulp.task('lint', function() {
    return gulp.src('src/*.js')
        .pipe(jshint())
        .pipe(jshint.reporter('default'));
    });

    // Concat & Minify JS
    gulp.task('minify', function(){
        return gulp.src('src/*.js')
        .pipe(concat('all.js'))
        .pipe(gulp.dest('dist'))
        .pipe(rename('all.min.js'))
        .pipe(uglify())
        .pipe(gulp.dest('dist'));
    });

    // Watch Our Files
    gulp.task('watch', function() {
        gulp.watch('src/*.js', ['lint', 'minify']);
    });

    // Default
    gulp.task('default', ['lint', 'minify', 'watch']);
复制代码

gulp的执行是从上倒下的执行一个个任务。而后文件内容经过管道。进行传递。若是你想了解更多gulp的知识,能够去gulp官网

Webpack

webpack 是一个用于现代 JavaScript 应用程序的 静态模块打包工具。当 webpack 处理应用程序时,它会在内部构建一个 依赖图(dependency graph),此依赖图对应映射到项目所需的每一个模块,并生成一个或多个 bundle。
接下来咱们看看Webpack的工做原理图 WechatIMG21.png 从上图咱们能够看出webpack打包分为一下步骤

    1. 查找入口文件

      从webpack的配置文件中查找entry的配置,从而找到入口文件

    1. 分析依赖关系

      接到入口文件以后,从入口文件出发,分析入口文件中依赖了哪些文件,而且这些依赖的文件中还可能依赖别的文件,就这么递归的找下去。

    1. 模块函数

      找到依赖中的全部文件,把这些文件转化成模块的函数,为了方便后面webpack进行调用

    1. 打包

      打包完毕的文件能够产出到配置文件的output指定路径里,生成一个bundle

    1. 启动服务

      node建立本地服务器并启动静态页面

这大概是咱们webpack在开发环境打包的主要流程。

熟悉了咱们常见的打包工具的工做流。接下来咱们看看vite是怎么工做的。 看看vite为何被称为下一代开发和构建工具。

vite的原理和优点

目前打包工具的困境

在目前的工做中,咱们主要利用webpack+vue(webpack+react)进行项目开发。然而,当咱们构建愈来愈大型的应用的时候,打包工具须要处理的javascript代码量也呈指数级增加。在大型项目中包含几百甚至几千个模块的状况也愈来愈多。咱们开始遇到性能瓶颈,使用javascript的工具一般须要很长的时间,才能启动开发服务器

WechatIMG25.png 这是我工做中的一个真实项目, 项目不大,可是启动服务器的时间花了30s。这做为一个讲究效率的程序员确定是没法忍受的

当项目愈来愈大,项目的hrm热更新速度愈来愈慢。有时候甚至改动一个字段,页面几十秒以后才会进行热更新。

为何会产生这种问题

其实这和webpack打包的原理有关系,咱们前面其实已经大概了解webpack的主要工做流,这里就不在详细讲解

  1. 启动服务器慢,是由于在每次服务器启动以前。webpack须要执行一系列的事情。找模块间的依赖,将各个模块进行合并,生成一个build,存入内存中,最后在启动服务器。因此速度上随着项目的增长,速度会愈来愈慢
  2. webpack的hrm。当你改动一个文件的时候,Webpack 的热更新会以当前修改的文件为入口从新 build 打包,全部涉及到的依赖也都会被从新加载一次。因此速度也随着项目的增长而下降(后面会写一遍文章专门介绍webpack的hrm的实现)

......

vite的优点

vite的三大特色

    1. 快速的冷启动 在开发预览中,它是不进行打包的。
    1. 即时热模块更新(HMR,Hot Module Replacement)
    1. 真正按需进行加载

固然vite也有必定的问题,

  1. vite的生态还不够完善。

  2. 在生产模式下仍然须要打包。尽管原生 ESM 如今获得了普遍支持,但因为嵌套导入会致使额外的网络往返,在生产环境中发布未打包的 ESM 仍然效率低下(即便使用 HTTP/2)。为了在生产环境中得到最佳的加载性能,最好仍是将代码进行 tree-shaking、懒加载和 chunk 分割(以得到更好的缓存)。

vite的原理

经过上面的了解,咱们已经知道了利用vite的不少优点,那么接下来咱们看看vite是怎么实现的。

ES Modules

vite的成功得益于现代浏览器对于基于ECMAScript 标准原生模块系统(ES Modules)实现。 目前主流浏览器(IE11除外)都已经支持。他容许咱们在浏览器使用export、import 的方式导入和导出模块,在 script 标签里设置 type="module"

<script type="module">
  import { createApp } from './main.js‘ createApp() </script> 复制代码

浏览器会识别添加type="module"的 <script> 元素,浏览器会把这段内联 script 或者外链 script 认为是 ECMAScript 模块。而后浏览器会被这里面的import引用发起一个http请求,请求获取文件中的内容。 所以咱们对于第三方的模块,能够不用打包合并,而是经过import 这种方式去发起http 请求,获取代码。这也是vite的主要实现思路。 若是你对ES Modules 不够了解。能够去看看ES Modules的规范

vite的工做流和冷启动

首先看一张图

WechatIMG26.png

  1. 首先是启动一个静态资源服务器
  2. 找到项目的入口,开始加载入口文件
  3. 当声明一个 script 标签类型为 module 时,浏览器就会像服务器发起一个GET
  4. Vite 经过劫持浏览器劫持浏览器劫持浏览器的这些请求,并在后端进行相应的处理,将项目中使用的文件经过简单的分解与整合,而后再返回给浏览器。

从上面的分析可知: vite主要作了如下事情

    1. 启动了一个静态资源服务器
    1. 只须要在浏览器请求源码时进行转换并按需提供源码

vite整个过程当中没有对文件进行打包编译,至于其余加载的工做就交给了浏览器,因此其运行速度比原始的webpack开发编译速度快出许多。

vite的热更新

传统打包器是将项目打包以后的资源存入电脑的内存之中,这样他们只须要在文件更改的时候,将对应的模块进行失活,可是它仍然须要从新构建并重载页面。 因此像webpack这类的打包工具支持了动态模块热重载(HRM),容许一个模块替换本身,而对其他页面没有影响。可是在实践中。咱们发现HRM的速度会随着项目的增大而下降(缘由在 目前打包工具的困境 这一节已经分析过了)

而在vite中HMR 是在原生 ESM 上执行的。当编辑一个文件时,Vite 只须要精确地使已编辑的模块与其最近的 HMR 边界之间的链失效(大多数时候只须要模块自己),使 HMR 更新始终快速,不管应用的大小。

vite的按需加载

为何说vite才是真正的按需加载呢?难道webpack不是真正的按需加载吗?
若是你想知道,那么你能够看看去看看webpack的原理,这里我简单介绍一下 webpack其实在开始构建打包的时候,仍是对全部的文件进行一次打包构建,只是在webpack遇到 import( * ) 这种语法的时候,会另外生成一个chunk; 只有在合适的时候去加载import中的内容
从上面的分析能够知道。无论咱们这段import的代码什么时候执行,咱们对须要对它进行必定的打包

可是vite不同,只有在你真正的须要加载的时候,浏览器才会发送import请求,去请求文件中的内容,因此才说vite才是真正的按需加载

实现一个基础版vite

咱们已经知道了vite的大概工做原理,那么咱们接下来实现一个最基础的vite

vite和webpack编译事后文件的区别

webpack编译后的文件

WechatIMG27.png vite编译后的文件

image.png main.js image.png 第一步: 从上图咱们能够看出vite首先也是找到一个入口文件,而后修改一些依赖模块的路径,好比把vue替换成了/@modules/vue.js. 把./App.vue替换成了绝对路径

WechatIMG29.png 第二步: 首先去寻找第一个组件App.vue。将App.vue中的js复制给一个常量,而后在App.vue中的html转化为一个render函数,挂载到js的那个常量身上。

实现咱们本身的vite

首先咱们初始化一个项目

npm init vite-app <project-name>
 cd  <project-name>  
 npm i 
 npm run dev 
复制代码

这样咱们就能成功启动一个vite+vue的项目。 接下来咱们不用vite,本身来实现一个咱们本身的vite

image.png

新建yj-vite.js文件 从上图咱们对vite分析能够得知,实现vite主要有如下几个步骤

搭建一个服务器

咱们这里主要使用koa来实现

image.png 而后执行 node yj-vite.js

image.png 能够看到,这样一个静态资源服务器就已经搭建成功了

拦截请求,替换模块

image.png 若是咱们请求的是首页,那么咱们拿到绝对地址,进行拼接,读取文件,并返回,若是不是,咱们就直接读取文件。并返回

image.png

这时候咱们看到请求其实已经成功,可是控制台报了一个错误

image.png 缘由是浏览器不能识别 import ..form 'vue' 要把vue变成一个绝对路径地址

更换路径

因此咱们须要对上一步的内容进行改造,当以js结尾的文件中包含import 加载第三方模块的时候,咱们首先将

```
import vue from 'vue'
替换
import vue from '/@modules/vue'
```
复制代码

image.png

这样咱们就能够看到main.js中已经成功替换了

image.png 那么接下来看看咱们怎么处理 /@module/vue

image.png 这样咱们就能正确处理第三方模块了。而且第三方模块也已经成功加载了。
但此时浏览器中会有一个错误 image.png 这是由于在vue的源码中,是有process.env这个环境变量,因此在index.html中咱们能够加下如下内容,进行简单处理一下。

<script>
  window.process = {
    env: {
      NODE_ENV: 'dev'
    }
  }
</script>
复制代码

这样咱们就可以成功执行。

识别vue文件

可是在咱们main.js中通常都不会这样写

createApp({
  render: () => h('div','3333333')
}).mount('#app')
复制代码

而是经过单文件组件的形式去编写代码

import App from './App.vue'
createApp(App).mount('#app')
复制代码

那么咱们怎么去识别.vue中文件的内容,而且成功获取呢
这里简单说一下接下来的大概思路

  1. 匹配.vue结尾的文件,读取其中的js代码 把export default 替换成 const __script =
  2. 将渲染事后template相关的部分挂在到 __script

image.png 这样咱们就成功手写了一个vite

总结

源码地址

github.com/yujun96/myv…

vite

其实官方的vite远远比这个复杂。在http请求以前,其实vite作了一个预打包,预打包的目的是为了减小http请求,由于可能一个模块依赖了成百上千个模块,过多的请求增大了浏览器的负担。 若是你想了解vite怎么实现预打包,你能够看看esbuild

固然vite怎么实现按需加载, 热更新 ... 等等功能,你能够关注我,等待后续更新

后续更新内容

vite 下一代前端开发与构建工具(二) 主要内容: vite更多功能的实现,以及如何将vue2项目迁移到vite webpack hrm的实现原理 主要内容: 主要讲解webpack中hrm的底层实现 vue2 vue2的原理 主要内容: MVVM的实现 虚拟dom diff算法等等

相关文章
相关标签/搜索