<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          當(dāng)我們談?wù)搗ite時(shí),我們?cè)谡務(wù)撌裁矗◤牧闵鲜郑。?/h1>

          共 6551字,需瀏覽 14分鐘

           ·

          2021-06-01 13:27

          作者:supot

          原文:https://juejin.cn/post/6962902504212267021

          前言

          在ES6之前,JavaScript沒有一個(gè)標(biāo)準(zhǔn)的模塊方案,社區(qū)比較流行的是AMD方案和node使用的CommonJS的方案。

          為了能夠在瀏覽器端使用npm上大量的CommonJS規(guī)范的包,需要我們?nèi)?duì)模塊進(jìn)行兼容和處理,這就是前端打包需要解決的一個(gè)問(wèn)題。

          Webpack

          Webpack是現(xiàn)在主流的打包工具,不管是螞蟻的umi還是vue的vue-cli,底層都是基于Webpack來(lái)進(jìn)行二次封裝的。

          Webpack 官方將它定位為一個(gè) module bundler,它通過(guò)定義的入口文件,進(jìn)行依賴收集,構(gòu)建出項(xiàng)目的依賴圖,最后生成并輸出一個(gè)或多個(gè)bundle。

          下圖是官方給出的Webapck對(duì)于模塊的處理流程

          一個(gè)能在瀏覽器中運(yùn)行的模塊系統(tǒng)

          我們希望瀏覽器能夠順利的運(yùn)行第三方的包和業(yè)務(wù)代碼,不管是commonjs、requirejs或者是es6的模塊,所以需要提供一個(gè)新的模塊系統(tǒng),將這些第三方包和項(xiàng)目里的業(yè)務(wù)模塊進(jìn)行統(tǒng)一處理,以便在瀏覽器中能正確的運(yùn)行。

          webpack 用類似于node 的commonjs的模塊方案,將最終打包的代碼運(yùn)行在瀏覽器中。

          (function(modules) { // webpackBootstrap
          // 已經(jīng)加載的模板
          var installedModules = {};

          // 模塊加載函數(shù)
          function __webpack_require__(moduleId) {
          // ...
          }

          // 入口文件
          return __webpack_require__(__webpack_require__.s = "./index.js");
          })
          ({
          "./index.js": (function(module, __webpack_exports__, __webpack_require__) {}),
          "./utils/test.js": (function(module, __webpack_exports__, __webpack_require__) {})
          });
          復(fù)制代碼

          上面就是webpack打包出來(lái)的代碼,其中index.js是項(xiàng)目的入口文件,所以的模塊都通過(guò) __webpack_require__ 進(jìn)行加載,并且會(huì)把加載完成的模塊進(jìn)行緩存。

          var installedModules = {};

          // The require function
          function __webpack_require__(moduleId) {
          // Check if module is in cache
          if(installedModules[moduleId]) {
          return installedModules[moduleId].exports;
          }
          // Create a new module (and put it into the cache)
          var module = installedModules[moduleId] = {
          i: moduleId, // Module ID
          l: false, //
          exports: {}
          };

          // Execute the module function
          modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);

          // Flag the module as loaded
          module.l = true;

          // Return the exports of the module
          return module.exports;
          }

          通過(guò)__webpack_require_的代碼可以看出,webpack實(shí)現(xiàn)了一個(gè)類似CommonJS的模塊方案。

          插件系統(tǒng)

          對(duì)于webpack來(lái)說(shuō),如果僅僅解決不同方案模塊的聚合和運(yùn)行,是遠(yuǎn)遠(yuǎn)不夠的。對(duì)于越來(lái)越復(fù)雜的前端項(xiàng)目,需要提供更加底層的能力給開發(fā)者,去實(shí)現(xiàn)不同業(yè)務(wù)場(chǎng)景的不同需求。

          Webpack 基于 Tapable 實(shí)現(xiàn)了一個(gè)復(fù)雜的插件系統(tǒng),在它的構(gòu)建過(guò)程中,對(duì)外拋出了各個(gè)關(guān)鍵的節(jié)點(diǎn)的hook,開發(fā)者可以通過(guò)訂閱這些勾子,對(duì)webpack中的資源進(jìn)行加工和處理,從而完成定制化的需求

          通過(guò)Plugin和Loader,能夠不斷擴(kuò)展Webpack的功能。

          存在的問(wèn)題

          當(dāng)然,Webpack 也不是十全十美的,在學(xué)習(xí)和試用過(guò)程中,也會(huì)存在大大小小的問(wèn)題

          • 概念和配置項(xiàng)太多,需要配合實(shí)際的項(xiàng)目場(chǎng)景不斷的優(yōu)化配置文件,umi、vue-cli等腳手架就是為了解決無(wú)法開箱即用的問(wèn)題
          • 隨著項(xiàng)目的不斷變大,devServer的啟動(dòng)時(shí)間會(huì)越來(lái)越長(zhǎng),并且hmr的速度也會(huì)受到影響
          • 功能缺失,在webpack5之前,沒有系統(tǒng)級(jí)別的文件緩存系統(tǒng)

          契機(jī)的出現(xiàn)

          契機(jī)一:http/2

          由于http/2的普及,使得很多基于http/1的優(yōu)化工作都變成了反模式,其中最主要的一條就是合并代碼減少網(wǎng)絡(luò)請(qǐng)求。

          因?yàn)樵趆ttp/1的時(shí)代,瀏覽器只能并行的發(fā)起5個(gè)請(qǐng)求,這就造成如果文件太多,會(huì)存在請(qǐng)求阻塞的問(wèn)題,所以在很多項(xiàng)目中,我們都會(huì)去對(duì)代碼進(jìn)行打包生成盡量少的vendor。但是在http/2里,所有的請(qǐng)求都會(huì)在一個(gè)tcp鏈接里面完成,并且資源都是并行加載的,這時(shí)候單個(gè)大文件的加載時(shí)間反而會(huì)比多個(gè)小文件的時(shí)間要多。所以在http/2的網(wǎng)絡(luò)里,我們需要對(duì)項(xiàng)目資源進(jìn)行合理的拆分,充分利用資源的并行請(qǐng)求來(lái)減少資源的加載時(shí)間。

          契機(jī)二:ES Module

          在es6中,加入了JavaScript的模塊。

          只需要在script標(biāo)簽上加上 type=module 的屬性,瀏覽器就會(huì)將內(nèi)聯(lián)代碼或者外部引用的腳本視為ECMAScript模塊。

          相比于 Node 的 CommonJS 模塊,ES Module 有很多的不同:

          • ES module 拋出的是一個(gè)引用,而exports拋出的是一個(gè)值
          • require 可以進(jìn)行動(dòng)態(tài)引用,而ES module需要在作用域的頂層聲明所有的依賴,這就導(dǎo)致Node是可以在運(yùn)行時(shí)去加載模塊的, 而ES module可以在編譯階段就完成所有的依賴分析,并為后面的優(yōu)化做準(zhǔn)備(比如tree shaking)
          • ...

          下面的代碼可以在瀏覽器中直接運(yùn)行

          // test.js
          export default function hello() {
          console.log('hello world');
          }

          // index.html
          <script type="module">
          import hello from './test.js';

          hello(); // hello world
          </scirpt>

          在支持ES module的瀏覽器中,當(dāng)解析到ES module模塊的時(shí)候,瀏覽器就會(huì)自動(dòng)發(fā)起一個(gè)請(qǐng)求去加載對(duì)應(yīng)的模塊資源,不再需要我們?nèi)ヌ幚砟K的引入和加載。

          對(duì)于ES module的支持,主流的現(xiàn)代瀏覽器已經(jīng)有這不錯(cuò)的兼容性,隨著使用者的升級(jí),這個(gè)覆蓋率會(huì)越來(lái)越高。

          Vite

          通過(guò)上面的介紹,我們似乎可以利用 http/2 和 瀏覽器對(duì) ES module 的支持,來(lái)直接加載代碼,而不再需要進(jìn)行代碼的打包。

          Vite 和 snowpack 就是基于這種想法而誕生的前端構(gòu)建工具。

          Vite是什么

          Vite,一個(gè)基于瀏覽器原生 ES imports 的開發(fā)服務(wù)器。利用瀏覽器去解析 imports,在服務(wù)器端按需編譯返回,完全跳過(guò)了打包這個(gè)概念,服務(wù)器隨起隨用。同時(shí)不僅有 Vue 文件支持,還搞定了熱更新,而且熱更新的速度不會(huì)隨著模塊增多而變慢。針對(duì)生產(chǎn)環(huán)境則可以把同一份代碼用 rollup 打。雖然現(xiàn)在還比較粗糙,但這個(gè)方向我覺得是有潛力的,做得好可以徹底解決改一行代碼等半天熱更新的問(wèn)題。

          -- 摘自尤雨溪微博

          Bundleless

          Vite 直接使用ES Module,利用瀏覽器去解析文件中import的依賴,對(duì)于使用到的依賴通過(guò)請(qǐng)求去獲得相應(yīng)的資源文件, 從而不再需要像Webpack那樣,從入口文件出發(fā),收集所有的依賴然后整體打包,最后把打包的bundle文件交給瀏覽器解析運(yùn)行

          上圖是各個(gè)構(gòu)建工具在打包生成最終的代碼所需要的時(shí)間,其中snowpack和vite都是Bundleless的方案,所以在最后生成代碼的時(shí)候不需要任何的構(gòu)建,直接把ES module的代碼拋給瀏覽器即可。

          極速的hmr

          Vite 不會(huì)因?yàn)槟K數(shù)量的膨脹而造成熱更新的速度變慢,因?yàn)?Vite 不像 Webpack 需要構(gòu)建一份全量的依賴圖,并且這份依賴圖有可能在模塊依賴特別復(fù)雜的時(shí)候會(huì)造成熱更新的不準(zhǔn)確

          devServer使用304來(lái)進(jìn)行協(xié)商緩存,對(duì)于已經(jīng)加載過(guò)的模塊,會(huì)增加Cache-Control: max-age=31536000 來(lái)做緩存,減少網(wǎng)絡(luò)請(qǐng)求

          上圖是snowpack和webpack 熱更新所需時(shí)間的對(duì)比

          真正的按需編譯

          Vite 通過(guò)瀏覽器的解析來(lái)進(jìn)行依賴資源的加載,所以在文件沒有被使用之前,所有的依賴文件都不用進(jìn)行處理,真正做到了按需編譯

          Webpack 的spa模式很難進(jìn)行按需編譯,因?yàn)閺娜肟谖募_始,會(huì)做完所有的依賴分析和處理,就算是進(jìn)行了按需加載的模塊,也還是會(huì)被編譯。另一個(gè)可行的方案是mpa,但是就脫離了單頁(yè)應(yīng)用的開發(fā)模式了,并且項(xiàng)目在開發(fā)和發(fā)布的時(shí)候復(fù)雜度都會(huì)增加。

          上面兩張圖片可以直觀的看出Vite在編譯上的優(yōu)勢(shì),項(xiàng)目只需要在路由層面進(jìn)行按需加載,就能夠做到按需編譯。

          這里需要注意的是,如果項(xiàng)目沒有對(duì)路由進(jìn)行按需加載的處理,最后還是會(huì)一次性編譯加載所有的依賴

          極速的編譯速度

          Vite 使用 Esbuild 來(lái)進(jìn)行代碼的編譯,其中jsx和tsx都是通過(guò)Esbuild解析生成AST,再生成最終的es module的代碼,commonjs到ES module也使用它來(lái)進(jìn)行轉(zhuǎn)換

          Esbuild是使用Go語(yǔ)言進(jìn)行開發(fā)的,并且發(fā)布的是進(jìn)行編譯過(guò)的更底層的機(jī)器碼,在執(zhí)行效率上遠(yuǎn)遠(yuǎn)高于JavaScript編寫的打包器,預(yù)期會(huì)快10-100百倍

          在 Vite 1.0 中,使用 rollup 的 @rollup/plugin-commonjs 來(lái)實(shí)現(xiàn)commonjs轉(zhuǎn)換成ES6,在Vite 2.0 中,使用esbuild來(lái)處理模塊轉(zhuǎn)換,大大提高了效率

          上圖是對(duì)10份there.js的包進(jìn)行打包時(shí),各個(gè)構(gòu)建工具所需要的時(shí)間,可以看出esbuild的優(yōu)勢(shì)十分巨大

          依賴預(yù)構(gòu)建

          在 Vite 首次運(yùn)行的時(shí)候,會(huì)從入口文件出發(fā),對(duì)其中的第三方依賴進(jìn)行分析和打包,并把這些chunk進(jìn)行緩存,提高頁(yè)面的加載速度

          很多第三方的依賴都是commonjs和umd的模塊,通過(guò)預(yù)構(gòu)建,用 esbuild 將模塊轉(zhuǎn)換為ESM,這樣這些第三方的包就能在瀏覽器中直接運(yùn)行了

          將多入口的模塊,比如lodash,轉(zhuǎn)換成單文件的模塊,減少網(wǎng)絡(luò)請(qǐng)求,提高頁(yè)面的加載性能

          開箱即用

          除了對(duì)于Vue的第一優(yōu)先級(jí)的支持,Vite 還在內(nèi)部?jī)?nèi)置了大量默認(rèn)的處理,比如支持jsx和tsx,樣式預(yù)處理庫(kù)支持less、sass 和 CSS module

          完整的腳手架工具鏈,可以快速生成Vue、React等項(xiàng)目的模板,提供了大量的配置項(xiàng),作者說(shuō)后期有可能會(huì)替換掉vue-cli

          和vue、vue-cli 一樣優(yōu)秀又友好的文檔,并且有對(duì)應(yīng)的中文文檔

          簡(jiǎn)單的處理流程

          使用的核心依賴

          • Connect & Connect middleware
          • Rollup & Rollup plugin
          • esbuild & esbuild plugin
          • acorn、es-module-lexer ...

          Vite 1.0 的版本中使用 koa 來(lái)作為devServer,并且通過(guò)koa的插件機(jī)制,使用不同的插件來(lái)處理各種格式的文件和各種拓展的功能

          Vite 2.0 中使用Connect替代了koa,并通過(guò)中間件來(lái)進(jìn)行流程控制,因?yàn)閂ite使用Rollup來(lái)進(jìn)行最終的代碼build,所以直接擁抱了Rollup的開源社區(qū), 在內(nèi)部也繼承了Rollup的插件擴(kuò)展方案,通過(guò)Rollup插件來(lái)拓展Vite的功能

          vite流程圖.png

          不足之處

          • Esbuild 現(xiàn)在還不夠穩(wěn)定,無(wú)法用于生產(chǎn)環(huán)境的打包,項(xiàng)目打包的時(shí)候需要使用Rollup,這就造成開發(fā)環(huán)境和生產(chǎn)環(huán)境的代碼不一致,有可能一些bug會(huì)無(wú)法定位
          • 瀏覽器的支持度還有待提升
          • ssr還在試驗(yàn)階段

          慣性思維導(dǎo)致的一些問(wèn)題

          在試用vite跑demo的時(shí)候,遇到了一些問(wèn)題。

          因?yàn)槭褂玫募夹g(shù)棧是react + antd,所以就簡(jiǎn)單的搭了一個(gè)demo,來(lái)看一下實(shí)際的開發(fā)體驗(yàn)。在以往的開發(fā)中經(jīng)驗(yàn)中,對(duì)于有antd的項(xiàng)目,做的第一件事可能就是引入 babel-plugin-import 實(shí)現(xiàn)模塊的按需加載,在這個(gè)demo中也試用了相同的方案實(shí)現(xiàn)按需加載。

          但是在項(xiàng)目本地運(yùn)行以后,發(fā)現(xiàn)在沒有緩存的第一性運(yùn)行時(shí),頁(yè)面加載速度有時(shí)候會(huì)非常慢,通過(guò)network的瀑布圖分析后,是因?yàn)槊看雾?yè)面進(jìn)來(lái)的時(shí)候,都會(huì)加載試用到的antd模塊。后來(lái)分析發(fā)現(xiàn),是 babel-plugin-import 這個(gè)插件造成的。

          babel-plugin-import的作用是做antd引入語(yǔ)法的轉(zhuǎn)換,轉(zhuǎn)換的效果如下

          import { Button } from 'antd';

          ↓ ↓ ↓ ↓ ↓ ↓

          var _button = require('antd/lib/button');
          require('antd/lib/button/style/css');

          插件將antd的bare import的語(yǔ)法轉(zhuǎn)換成絕對(duì)路徑的引入方式,這就導(dǎo)致Vite對(duì)于antd 的預(yù)編譯失效,因?yàn)轭A(yù)編譯只對(duì)bare import的模塊有效,并且如果當(dāng)前路由依賴的antd的模塊比較多,就會(huì)造成頁(yè)面的加載速度比較慢

          最近遇到的另一個(gè)babel-plugin-import造成的問(wèn)題是組件庫(kù)打包的問(wèn)題。因?yàn)榻M件依賴了antd,在打包成umd包的時(shí)候,需要通過(guò)externals將antd、react和react-dom 排除出去,但是在分析最后打包產(chǎn)物的時(shí)候,發(fā)現(xiàn)react和react-dom沒有打包進(jìn)來(lái),但是antd用到的模塊還是被打包進(jìn)來(lái)了。后來(lái)經(jīng)過(guò)排查發(fā)現(xiàn),在打包的babel配置中,使用了babel-plugin-import,因?yàn)閘oader的編譯是在build構(gòu)建之前完成的,所有的antd的bare import全被編譯成了絕對(duì)路徑的引入方式,導(dǎo)致webpack build的時(shí)候externals替換規(guī)則失效,因?yàn)閑xternals也只能對(duì)bare import的模塊進(jìn)行替換。

          所以慣性思維會(huì)對(duì)我們?cè)斐梢欢ǔ潭鹊睦_,需要我們更透徹的理解其中的原理和構(gòu)建過(guò)程,才能避免很多坑。

          總結(jié)

          • 基于 ES Module 的前端構(gòu)建工具,充分利用瀏覽器自身的能力,本質(zhì)上解決本地開發(fā)項(xiàng)目構(gòu)建時(shí)間長(zhǎng)等問(wèn)題
          • 相比于Webpack,更加輕量,封裝的層級(jí)更高
          • 完整的生態(tài),活躍的社區(qū),穩(wěn)定的核心開發(fā)

          最后

          歡迎關(guān)注【前端瓶子君】??ヽ(°▽°)ノ?
          回復(fù)「算法」,加入前端編程源碼算法群,每日一道面試題(工作日),第二天瓶子君都會(huì)很認(rèn)真的解答喲!
          回復(fù)「交流」,吹吹水、聊聊技術(shù)、吐吐槽!
          回復(fù)「閱讀」,每日刷刷高質(zhì)量好文!
          如果這篇文章對(duì)你有幫助,在看」是最大的支持
           》》面試官也在看的算法資料《《
          “在看和轉(zhuǎn)發(fā)”就是最大的支持
          瀏覽 71
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)

          評(píng)論
          圖片
          表情
          推薦
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  色黄网站久久久久久久 | 日本一区二区三视频 | 尻屄视频免费网站 | 豆花成人原创av 高清无码啊a视频 | 豆花视频无码在线看 |