Viteの実装原則
記事コンテンツの出力ソース:大規模なフロントエンドの高額トレーニングキャンプ
1.Viteの紹介
1.バイトコンセプト
- Viteは、最新のブラウザー向けの軽量で高速なWebアプリケーション開発ツールです。
- ECMAScript標準ネイティブモジュールシステム(ESモジュール)に基づいて実装されています
2.プロジェクトの依存関係を明らかにする
- クイック
- @ vue /コンパイラ-sfc
3.基本的な使用法
vite serve
/ vite build
実行時vite serve
にパッケージ化する必要がなく、Webサーバーが直接開かれます。単一ファイルコンポーネントの要求など、ブラウザーがサーバーを要求する場合は、サーバー側で単一ファイルコンポーネントをコンパイルし、コンパイルした結果をブラウザーに返します。これに注意してください。コンパイルはサーバー側で行われ、モジュールの処理はリクエストからサーバー側に処理されます。
そして実行しvue-cli-service serve
ます:
実行するとvue-cli-service serve
、内部で使用されwebpack
ます。まず、すべてのモジュールをパッケージ化します。モジュールの数が多いと、パッケージ化の速度が非常に遅くなります。パッケージ化された結果はメモリに保存され、開発されたWebサーバーが開かれて参照されます。ブラウザはWebサーバーを要求し、メモリ内のパッケージの結果を直接ブラウザに返します。webpackのようなツールは、モジュールが実行されているか使用されているかに関係なく、事前にコンパイルおよびパッケージ化されるすべてのモジュールを使用します。コンパイルしてバンドルにパッケージ化する必要があります。プロジェクトがどんどん大きくなるにつれて、パッケージ化されたバンドルはどんどん大きくなり、パッケージング速度は自然に遅くなります。
Viteは、最新のブラウザでネイティブにサポートされているESModuleのモジュラー機能を使用して、モジュールのパッケージ化を省略します。単一ファイルコンポーネント、スタイルモジュールなど、コンパイルが必要なファイルの場合、Viteは別のインスタントコンパイルモードを使用します。ファイルが特に要求された場合、ファイルはサーバー側でコンパイルされるため、この種のジャストインタイムコンパイルの利点は主にオンデマンドコンパイルに反映され、より高速になります。
4. HMR
- Vite HMR
- 現在変更されているファイルをすぐに変更します
- Webpack HMR
- このファイルの場所のエントリで自動的に再構築され、関連するすべての依存関係が1回リロードされるため、応答速度が遅くなります
5.ビルド
- Viteビルド
- 巻き上げる
- 動的インポート
- ポリフィル
6.梱包するかしないか
- Webpackパッケージを使用する2つの理由:
- ブラウザ環境はモジュール性をサポートしていません(そして現在、ほとんどのブラウザはESMモジュール化をサポートしています)
- 散在するモジュールファイルは、多数のHTTP要求を生成します(HTTP2は長い接続を持つ可能性があります)
7.ESModuleのブラウザサポート
最新のブラウザはESModuleのモジュール性をサポートしています
8.箱から出して
- TypeScript-組み込みのサポート
- less / sass / stylus / postcss-組み込みサポート(個別にインストールする必要があります)
- JSX
- Webアセンブリ
9.バイト機能
- ファストコールドスタート
- モジュールのホットアップデート
- オンデマンドでコンパイル
- 箱から出して
2つの静的Webサーバー
1.Viteコア機能
- 静的Webサーバー
- 単一ファイルコンポーネントのコンパイル:ブラウザによって認識されないモジュールをインターセプトして処理します
- HMR
3つ目は、サードパーティモジュールのパスを変更することです
2つのミドルウェアを作成します。一つのミドルウェアが負荷にサードパーティのモジュールにインポートでパスを変更することで@modules/模块文件名
、その他のミドルウェアはリクエストが来た後、要求パスに存在するかどうかを決定することで、@modules/模块名称
そうであれば、外出先node_modulesは対応するモジュールをロードします
第4に、サードパーティのモジュールをロードします
リクエストが来たら、リクエストパス@modules
がで始まるかどうかを判断します。始まる場合は、node_modulesに移動して、対応するモジュールをロードします。
5、単一ファイルコンポーネントをコンパイルします
2つのリクエストを送信します。最初のリクエストは、単一ファイルコンポーネントをオブジェクトにコンパイルすることです。2番目のリクエストは、単一ファイルコンポーネントのテンプレートをコンパイルし、render関数を返し、render関数をオブジェクトのrenderメソッドにマウントすることです。
最終コード:
#!/usr/bin/env node
const path = require('path')
const {
Readable} = require('stream')
const Koa = require('koa')
const send = require('koa-send')
const compilerSFC = require('@vue/compiler-sfc')
const app = new Koa()
// 将流转化成字符串
const streamToString = stream => new Promise((resolve, reject) => {
const chunks = []
stream.on('data', chunk => chunks.push(chunk))
stream.on('end', () => resolve(Buffer.concat(chunks).toString('utf-8')))
stream.on('error', reject)
})
// 将字符串转化成流
const stringToStream = text => {
const stream = new Readable()
stream.push(text)
stream.push(null)
return stream
}
// 3. 加载第三方模块。判断请求路径中是否以`@modules`开头,如果是的话,去node_modules加载对应的模块
app.use(async (ctx, next) => {
// ctx.path --> /@modules/vue
if (ctx.path.startsWith('/@modules/')) {
const moduleName = ctx.path.substr(10)
const pkgPath = path.join(process.cwd(), 'node_modules', moduleName, 'package.json')
const pkg = require(pkgPath)
ctx.path = path.join('/node_modules', moduleName, pkg.module)
}
await next()
})
// 1. 开启静态文件服务器
app.use(async (ctx, next) => {
await send(ctx, ctx.path, {
root: process.cwd(),
index: 'index.html'
})
await next()
})
// 4. 处理单文件组件
app.use(async (ctx, next) => {
if(ctx.path.endsWith('.vue')) {
const contents = await streamToString(ctx.body)
const {
descriptor } = compilerSFC.parse(contents) // 返回一个对象,成员descriptor、errors
let code
if (!ctx.query.type) {
// 第一次请求,把单文件组件编译成一个对象
code = descriptor.script.content
// console.log('code', code)
code = code.replace(/export\s+default\s+/g, 'const __script = ')
code += `
import { render as __render } from "${
ctx.path}?type=template"
__script.render = __render
export default __script
`
} else if (ctx.query.type === 'template') {
const templateRender = compilerSFC.compileTemplate({
source: descriptor.template.content })
code = templateRender.code
}
ctx.type = 'application/javascript'
ctx.body = stringToStream(code) // 转化成流
}
await next()
})
// 2. 修改第三方模块的路径
app.use(async (ctx, next) => {
if (ctx.type === 'application/javascript') {
const contents = await streamToString(ctx.body)
// import vue from 'vue'
// import App from './App.vue'
ctx.body = contents
.replace(/(from\s+['"])(?![\.\/])/g, '$1/@modules/') // 分组匹配,第一个分组中,from原样匹配form,\s+匹配一至多个空格,['"]匹配单引号或双引号。第二个分组中,?!标识不匹配这个分组的结果,也就是排除点开头或者\开头的情况
.replace(/process\.env\.NODE_ENV/g, '"development"')// 替换process对象
}
})
app.listen(4000)
console.log('Server running @ http://localhost:4000')
を使用する場合は、最初にcliプロジェクトをグローバルにリンクします。npm link
次に、vue3my-vite-cli
プロジェクトで実行プロジェクトを実行します。vue3のイメージおよびスタイルモジュールのインポートコードはコメント化されています。