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算法等等

相關文章
相關標籤/搜索