<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)我們在談vite時(shí),我們在談什么

          共 6266字,需瀏覽 13分鐘

           ·

          2021-05-19 13:50

          前言

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

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

          Webpack

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

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

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

          698623cfd2d8f7ce80809b8435206ab6.webp

          一個(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__)?{})
          ??});

          上面就是webpack打包出來的代碼,其中index.js是項(xiàng)目的入口文件,所以的模塊都通過 __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;
          }

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

          插件系統(tǒng)

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

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

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

          be1e1b328271ade26edb5bea0c353c4c.webp

          存在的問題

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

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

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

          契機(jī)一:http/2

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

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

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

          0debaae5ad2a3e2e41fd124d64d77d04.webp

          Vite

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

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

          Vite是什么

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

          -- 摘自尤雨溪微博

          a846c0adea982b9748e0ad3d72d4148d.webp

          Bundleless

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

          3da39f9db209e76f6ecca5fbb02627bf.webp

          上圖是各個(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來進(jìn)行協(xié)商緩存,對于已經(jīng)加載過的模塊,會(huì)增加Cache-Control: max-age=31536000 來做緩存,減少網(wǎng)絡(luò)請求

          20f8084d198fc65cafa4ceff48b00598.webp

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

          真正的按需編譯

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

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

          fe58d8097ca697d0cae36e50041d9178.webp0c35892004d660848559a048a552993f.webp

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

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

          極速的編譯速度

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

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

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

          9c1303f9295f260dab185781b62eb0ee.webp

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

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

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

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

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

          開箱即用

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

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

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

          簡單的處理流程

          使用的核心依賴

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

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

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

          92610dfbc485a41d049b25c2983ee704.webp

          不足之處

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

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

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

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

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

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

          import?{?Button?}?from?'antd';
          ?
          ????↓?↓?↓?↓?↓?↓
          ?
          var?_button?=?require('antd/lib/button');
          require('antd/lib/button/style/css');
          復(fù)制代碼

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

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

          所以慣性思維會(huì)對我們造成一定程度的困擾,需要我們更透徹的理解其中的原理和構(gòu)建過程,才能避免很多坑。

          總結(jié)

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


          更多技術(shù)文章請關(guān)注公眾號:字節(jié)逆旅


          瀏覽 89
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <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>
                  国产在线视频福利 | 中国老熟女~x88AV | 天天日天天干天天射天天操 | 在线播放内射 | 国内精品视频在线 |