• 从零入门 Vite 与 Webpack 对比


    什么是构建工具

    浏览器只认识 .html.css.js,这就意味着当项目中使用了其他类型文件时(比如:.ts.vue 等)需要将其转换成浏览器能识别的文件才能正常运行项目,例如以下这些场景:

    • .ts:需要安装 tsc 工具将 .ts 转化成 .js
    • .react / .vue:需要安装 react-compilervue- compiler,将 .jsx.vue 转化为 render() 函数(也就是 js代码)
    • .tsc:需要经过一系列编译 .tsc - .jsx - .js
    • .less / .scss:需要安装 less-loaderscss- loader 等一系列编译工具
    • 语法降级:babel 将一些先进的 es 语法转化为不同版本浏览器都可以接受的语法
    • 体积优化:需要对代码进行压缩(uglify.js 等)将项目压缩使其体积更小传输性能更高
    • 等等

    构建工具不需要我们去手动一步一步处理这些编译过程,它会集成这些编译处理工具并完成这些流程,让开发人员只需要关注业务代码,代码经过构建工具处理后,就能给出最终的 .js

    构建工具处理哪些工作

    模块化开发支持

    JavaScript 设计之初并没有包含模块的概念,基于越来越多的工程需要,为了使用模块化开发,JavaScript 社区涌现了多种模块标准(如 Node 端的 CommonJS)。直到2015年,发布了 ES6(ECMAScript 6.0),自此 JavaScript 语言才具备了模块这一特性(JavaScript模块),最新的浏览器开始原生支持模块功能了,这是一个好事情,浏览器能够最优化加载模块,使它比使用库更有效率,因为使用库通常需要做一些额外的客户端处理

    先来看一个例子:

    
    DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <title>Documenttitle>
      head>
      <body>
        
        <script type="module">
    		import _ from 'lodash-es'
    		console.log('_', _)
    	script>
      body>
    html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    安装 lodash

    npm init -y
    npm i lodash-es
    
    • 1
    • 2

    浏览器打开 index.html,会有报错提示:

    在这里插入图片描述

    提示需要通过相对路径去引入,因为浏览器是不知道 node_modules 这个路径的,我们在项目开发中可以实现这样的引入是因为项目中的构建工具已经帮我们做了这种路径的判断处理和引入

    问:既然都知道,直接引入库名的最佳实践就表示要去 node_modules 下去找这个库,那么为什么 ESModule 不将这种规范加入呢?
    答:因为 CommonJS 最初只为服务端而设计,例如 Node.js 的实现中就采用了 CommonJS 标准的一部分,它可以不使用相对路径和绝对路径就能找到 node_modules,是因为服务端读取文件就是本地获取,但是如果 ESModule (也就是浏览器)也支持这种模块加载方式的话就表示浏览器需要通过网络请求来加载资源,另外每个 package 包还可能引入了很多额外的包,浏览器加载这些包都是需要网络请求的,这无疑是很大的一种性能消耗。这也就是为什么 ESModule 不敢将这种最佳实践的加载方案加入标准中,而是直接报错告诉你,我就是不支持这种引入方式。

    多种模块化支持

    浏览器是不支持 CommonJS 模块化代码的,所以如果在编写代码时使用 CommonJS 模块化方式引入库,比如:

    const lodash = require('lodash-es')
    console.log('_', lodash)
    
    • 1
    • 2

    浏览器打开 index.html,依然会有错误提示:
    在这里插入图片描述

    提示浏览器不认识 require 这种语法,无法通过这种方式引入库,而经过构建工具的处理,实现了 CommonJS 等多种模块化的支持

    语法处理代码兼容

    在处理例如 .less.scss.jsx.vue 等这些浏览器无法识别的文件时,构建工具通过集成每种文件对应处理工具,可以流程化自动实现语法处理工作,同时针对不同浏览器的语法兼容问题通过 babel 语法降级工具的集成可以实现自动化代码兼容性处理

    提高项目性能

    经过处理后的项目经过打包处理,最终会生成可以被浏览器识别的文件类型,在整个打包过程中,构建工具可以压缩文件、代码分割、优化开发体验(例如:热更新、开发服务器解决跨域问题等)等

    总结:构建工具可以让开发者不用每次编码后还要关心如何让代码在浏览器端运行,而只需要关注业务逻辑即可。

    Vite相比Webpack

    Webpack 就是这样一个构建工具,在浏览器支持 ES 模块之前,JavaScript 并没有提供原生机制让开发者以模块化的方式进行开发,上述构建工具需要处理的工作 Webpack 都可以帮我们完成。

    但是,当我们开始构建越来越大型的应用时,需要处理的 JavaScript 代码量也呈指数级增长,因此开始遇到技术瓶颈 —— 使用 JavaScript 开发的工具通常需要很长时间才能启动开发服务器(项目跑起来),即使使用 HMR,文件修改后的效果也需要几秒钟才能在浏览器中反映出来。如此循环往复,迟钝的反馈会极大地影响开发者的开发效率和幸福感。

    而 Webpack 无法通过改善自身来解决上述问题,为什么?我们从 Webpack 的原理进行说明。

    Webpack 是一种多模块化支持的构建工具,例如:

    const lodash = require('lodash') // commonjs 规范
    import Vue from 'vue' // es6 module
    
    // webpack允许上面的两种写法,webpack通过 AST 抽象语法分析工具,分析出所有的导入导出操作
    // 通过启动的服务器最终将其转化成统一的浏览器可以识别的代码
    const lodash = webpack_require('lodash')
    const Vue = webpack_require('vue')
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    这就意味着 Webpack 需要一开始统一模块化代码(统一成 webpack_require 函数处理的模块化方式),这也就意味着需要将所有依赖文件都读取完。这也就是为什么当项目文件越来越多时,Webpack 需要读取的文件就越多,从而需要解析转化的文件也越多,打包(开发时候打包看不到因为是在内存中)过程就越长,导致启动开发服务器所需的时间也就越长。
    因为需要打包构建的项目并不一定总是跑在浏览器端的项目,而运行在不同端的项目构建工具处理的模块化可能也不同,因此,Webpack 考虑更多的是兼容性

    在这里插入图片描述

    Vite 旨在利用生态系统中的新进展解决上述问题:浏览器开始原生支持 ES 模块。它是基于 ESModule 的,不允许 CommonJS 的代码,不需要去处理不同模块化的统一而遍历所有模块文件,而是按需加载的过程( index.html 通过esmodule 新特性可以直接去请求 main.js,再通过 main.js 中引入的模块 Vite 再通过开发服务器去按需加载这些模块 ),以原生 ES 模块方式提供源码,这实际上时让浏览器接管了部分打包程序的工作,Vite 只需要在浏览器请求源码时进行转换并按需提供源码,因此能快速启动开发服务器。

    在这里插入图片描述

    总结:

    • Vite 关注浏览器端的开发体验,Webpack 更多关注的是模块化兼容性
    • Vite 比 Webpack 快是因为它是先启动服务器的,而且不会一次将所有文件解析完,而 Webpack 在启动服务器之前需要先遍历解析所有文件

    关于更多为何选择 Vite 构建工具的原因,官网也给出了更详情的说明

    从0开始 Vite

    Vite 是一种新型前端构建工具,能够显著提升前端开发体验,主要由两部分组成:

    • 一个开发服务器,它基于 原生 ES 模块 提供了 丰富的内建功能
    • 一个构建指令,它使用 Rollup 打包项目代码,并且是预配置的,可输出用于生产环境的高度优化过的静态资源

    开箱即用

    Vite 是开箱即用(out of box)的工具,不需要任何额外配置的情况下就可以帮你处理构建工作:

    
    DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title>Vitetitle>
    head>
    <body>
      <script src="./main.js" type="module">script>
    body>
    html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    // main.js
    import {count} from './counter.js'
    console.log('count', count)
    
    // counter.js
    export const count = 0
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    在没有使用构建工具的情况下如果想要直接运行 index.html 会因为 type="module" 给出跨域报错,具体原因及解决方法可以在 CommonJS,ES6 Module以及模块打包原理 一文中找到答案,在使用了插件以后打开 index.html 页面显示没有问题,这里不再赘述。

    在一个 Vite 项目中,index.html 在项目最外层而不是在 public 文件夹内,是因为在开发期间 Vite 是一个服务器,这个 index.html 是该 Vite 项目的入口文件。通过解析