vite是尤大在更新vue3以後,再次開發的一個新的構建工具。那什麼構建工具讓尤大在過年都在瘋狂更新呢?它到底有什麼魔力呢?javascript
在vite開發出來以後,就連webpack團隊的核心成員都在感嘆css
Vite(法語意思是 「快」,發音爲 /vit/,相似 veet)是一種全新的前端構建工具。你能夠把它理解爲一個開箱即用的開發服務器 + 打包工具的組合,可是更輕更快。Vite 利用瀏覽器原生的 ES 模塊支持和用編譯到原生的語言開發的工具(如 esbuild)來提供一個快速且現代的開發體驗。html
注意:vite是一個全新的前端構建工具(重點:構建工具),不少小夥伴總在問vite和vue-cli的區別。vue-cli是一個腳手架工具,用於自動生成vue.js+webpack的項目模板。方便咱們快速開發。而vite是一個構建工具,用於咱們對項目進行構建(相似於webpack的功能)。而在將來不排除vue-cli裏面會集成vite前端
構建工具:用來讓咱們再也不作機械重複的事情,解放咱們的雙手的。
在早期開發過程當中。有不少令咱們不爽的地方vue
.......java
因而 前端構建工具應運而生。構建工具能夠幫助咱們作如下工做node
...react
在瞭解vite以前咱們先了解一下其餘的構建工具,這樣咱們才能更好的對比vite的強大之處。
前端的構建工具備不少。好比 Grunt Gulp FIS3 Webpack Rollup Parcel snowpack vite 接下來我會挑幾個構建工具進行簡單的介紹webpack
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 是一個用於現代 JavaScript 應用程序的 靜態模塊打包工具。當 webpack 處理應用程序時,它會在內部構建一個 依賴圖(dependency graph),此依賴圖對應映射到項目所需的每一個模塊,並生成一個或多個 bundle。
接下來咱們看看Webpack的工做原理圖 從上圖咱們能夠看出webpack打包分爲一下步驟
查找入口文件
從webpack的配置文件中查找entry的配置,從而找到入口文件
分析依賴關係
接到入口文件以後,從入口文件出發,分析入口文件中依賴了哪些文件,而且這些依賴的文件中還可能依賴別的文件,就這麼遞歸的找下去。
模塊函數
找到依賴中的全部文件,把這些文件轉化成模塊的函數,爲了方便後面webpack進行調用
打包
打包完畢的文件能夠產出到配置文件的output指定路徑裏,生成一個bundle
啓動服務
node建立本地服務器並啓動靜態頁面
這大概是咱們webpack在開發環境打包的主要流程。
熟悉了咱們常見的打包工具的工做流。接下來咱們看看vite是怎麼工做的。 看看vite爲何被稱爲下一代開發和構建工具。
在目前的工做中,咱們主要利用webpack+vue(webpack+react)進行項目開發。然而,當咱們構建愈來愈大型的應用的時候,打包工具須要處理的javascript代碼量也呈指數級增加。在大型項目中包含幾百甚至幾千個模塊的狀況也愈來愈多。咱們開始遇到性能瓶頸,使用javascript的工具一般須要很長的時間,才能啓動開發服務器
這是我工做中的一個真實項目, 項目不大,可是啓動服務器的時間花了30s。這做爲一個講究效率的程序員確定是沒法忍受的
當項目愈來愈大,項目的hrm熱更新速度愈來愈慢。有時候甚至改動一個字段,頁面幾十秒以後纔會進行熱更新。
其實這和webpack打包的原理有關係,咱們前面其實已經大概瞭解webpack的主要工做流,這裏就不在詳細講解
......
vite的三大特色
固然vite也有必定的問題,
vite的生態還不夠完善。
在生產模式下仍然須要打包。儘管原生 ESM 如今獲得了普遍支持,但因爲嵌套導入會致使額外的網絡往返,在生產環境中發佈未打包的 ESM 仍然效率低下(即便使用 HTTP/2)。爲了在生產環境中得到最佳的加載性能,最好仍是將代碼進行 tree-shaking、懶加載和 chunk 分割(以得到更好的緩存)。
經過上面的瞭解,咱們已經知道了利用vite的不少優點,那麼接下來咱們看看vite是怎麼實現的。
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主要作了如下事情
vite整個過程當中沒有對文件進行打包編譯,至於其餘加載的工做就交給了瀏覽器,因此其運行速度比原始的webpack開發編譯速度快出許多。
傳統打包器是將項目打包以後的資源存入電腦的內存之中,這樣他們只須要在文件更改的時候,將對應的模塊進行失活,可是它仍然須要從新構建並重載頁面。 因此像webpack這類的打包工具支持了動態模塊熱重載(HRM),容許一個模塊替換本身,而對其他頁面沒有影響。可是在實踐中。咱們發現HRM的速度會隨着項目的增大而下降(緣由在 目前打包工具的困境
這一節已經分析過了)
而在vite中HMR 是在原生 ESM 上執行的。當編輯一個文件時,Vite 只須要精確地使已編輯的模塊與其最近的 HMR 邊界之間的鏈失效(大多數時候只須要模塊自己),使 HMR 更新始終快速,不管應用的大小。
爲何說vite纔是真正的按需加載呢?難道webpack不是真正的按需加載嗎?
若是你想知道,那麼你能夠看看去看看webpack的原理,這裏我簡單介紹一下 webpack其實在開始構建打包的時候,仍是對全部的文件進行一次打包構建,只是在webpack遇到 import( * ) 這種語法的時候,會另外生成一個chunk; 只有在合適的時候去加載import中的內容
從上面的分析能夠知道。無論咱們這段import的代碼什麼時候執行,咱們對須要對它進行必定的打包
可是vite不同,只有在你真正的須要加載的時候,瀏覽器纔會發送import請求,去請求文件中的內容,因此才說vite纔是真正的按需加載
咱們已經知道了vite的大概工做原理,那麼咱們接下來實現一個最基礎的vite
webpack編譯後的文件
vite編譯後的文件
main.js
第一步: 從上圖咱們能夠看出vite首先也是找到一個入口文件,而後修改一些依賴模塊的路徑,好比把vue替換成了/@modules/vue.js. 把./App.vue替換成了絕對路徑
第二步: 首先去尋找第一個組件App.vue。將App.vue中的js複製給一個常量,而後在App.vue中的html轉化爲一個render函數,掛載到js的那個常量身上。
首先咱們初始化一個項目
npm init vite-app <project-name>
cd <project-name>
npm i
npm run dev
複製代碼
這樣咱們就能成功啓動一個vite+vue的項目。 接下來咱們不用vite,本身來實現一個咱們本身的vite
新建yj-vite.js文件 從上圖咱們對vite分析能夠得知,實現vite主要有如下幾個步驟
咱們這裏主要使用koa來實現
而後執行
node yj-vite.js
能夠看到,這樣一個靜態資源服務器就已經搭建成功了
若是咱們請求的是首頁,那麼咱們拿到絕對地址,進行拼接,讀取文件,並返回,若是不是,咱們就直接讀取文件。並返回
這時候咱們看到請求其實已經成功,可是控制檯報了一個錯誤
緣由是瀏覽器不能識別 import ..form 'vue' 要把vue變成一個絕對路徑地址
因此咱們須要對上一步的內容進行改造,當以js結尾的文件中包含import 加載第三方模塊的時候,咱們首先將
```
import vue from 'vue'
替換
import vue from '/@modules/vue'
```
複製代碼
這樣咱們就能夠看到main.js中已經成功替換了
那麼接下來看看咱們怎麼處理 /@module/vue
這樣咱們就能正確處理第三方模塊了。而且第三方模塊也已經成功加載了。
但此時瀏覽器中會有一個錯誤 這是由於在vue的源碼中,是有process.env這個環境變量,因此在index.html中咱們能夠加下如下內容,進行簡單處理一下。
<script>
window.process = {
env: {
NODE_ENV: 'dev'
}
}
</script>
複製代碼
這樣咱們就可以成功執行。
可是在咱們main.js中通常都不會這樣寫
createApp({
render: () => h('div','3333333')
}).mount('#app')
複製代碼
而是經過單文件組件的形式去編寫代碼
import App from './App.vue'
createApp(App).mount('#app')
複製代碼
那麼咱們怎麼去識別.vue中文件的內容,而且成功獲取呢
這裏簡單說一下接下來的大概思路
export default
替換成 const __script =
__script
下 這樣咱們就成功手寫了一個vite
其實官方的vite遠遠比這個複雜。在http請求以前,其實vite作了一個預打包,預打包的目的是爲了減小http請求,由於可能一個模塊依賴了成百上千個模塊,過多的請求增大了瀏覽器的負擔。 若是你想了解vite怎麼實現預打包,你能夠看看esbuild
固然vite怎麼實現按需加載, 熱更新 ... 等等功能,你能夠關注我,等待後續更新
vite 下一代前端開發與構建工具(二) 主要內容: vite更多功能的實現,以及如何將vue2項目遷移到vite webpack hrm的實現原理 主要內容: 主要講解webpack中hrm的底層實現 vue2 vue2的原理 主要內容: MVVM的實現 虛擬dom diff算法等等