前言
Vite
经过一段时间的发展,目前的生态现已非常丰厚了。它不仅用于 Vue
,React
、Svelte
、Solid
、Marko
、Astro
、Shopify Hydrogen
,以及 Storybook
、Laravel
、Rails
等项目都现已接入了Vite
,而且也趋于稳定,所以就着手把项目的 Webpack
替换为 Vite
。
切换为 Vite
Vite
生态现在很丰厚了,底子上插件按名称搜索一下,照着文档就能够把 webpack
替换到 Vite
。由于每个项目的装备都不相同,所以也没有什么统一的操作步骤,下面列一些典型替换的比方。
进口
index.html
的位置需求放到项目的最外层,而不是 public
文件夹内。相同 entry
的进口文件也需求从 pages
里换到 index.html
里。由 <script type="module" src="https://juejin.im/post/7160670274521104397/...">
引进。
module .exports = defineConfig ({
pages : {
index : {
entry : 'src/main.ts' ,
template : 'index.html' ,
chunks : ['chunk-vendors' , 'chunk-common' , 'index' ]
}
}
})
<!DOCTYPE html >
<html lang ="en" >
<head >
<meta charset ="utf-8" />
<meta http-equiv ="X-UA-Compatible" content ="IE=edge" />
<meta name ="viewport" content ="width=device-width,initial-scale=1.0" />
<link rel ="icon" href ="/assets/favicon.ico" />
</head >
<body >
<div id ="app" > </div >
<script type ="module" src ="/src/main.ts" > </script >
</body >
</html >
文件loader
这儿挑几个比方(下面比方 webpack 版本都为 webpack5)。
yaml
由本来的 yaml-loader
替换为 rollup-plugin-yamlx
rules : [
{
test : /.ya?ml$/ ,
use : 'yaml-loader'
}
]
import PluginYamlX from 'rollup-plugin-yamlx'
plugins : [
...other,
PluginYamlX ()
]
svg-sprite
由本来的 svg-sprite-loader
替换为 vite-plugin-svg-icons
const resolve = (...dirs ) => require ('path' ).resolve (__dirname, ...dirs)
chainWebpack (config ) {
const svgRule = config.module .rule ('svg' )
svgRule.exclude .add (resolve ('base/assets/icons' )).end ()
config.module
.rule ('icons' )
.test (/.svg$/ )
.include .add (resolve ('base/assets/icons' ))
.end ()
.use ('svg-sprite-loader' )
.loader ('svg-sprite-loader' )
.options ({
symbolId : 'ys-svg-[name]'
})
.end ()
}
import { createSvgIconsPlugin } from 'vite-plugin-svg-icons'
import { resolve } from 'path'
const pathResolve = (dir : string ): string => {
return resolve (__dirname, '.' , dir)
}
plugins : [
...other,
createSvgIconsPlugin ({
iconDirs : [pathResolve ('base/assets/icons/svg' )],
symbolId : 'ys-svg-[name]'
}),
]
留意文件加载方法的共同性,比方本来的 svg-loader
直接 import
引证的是路径地址,而 vite-svg-loader
默许是 Vue
组件。
所以 Vite
需求把默许方法改成和 webpack lodaer
共同。
plugins : [
svgLoader ({ defaultImport : 'url' })
]
每个替换的插件都要看一下文档,也许某个装备便是你需求的功用。
大局常量
比方开发的版本信息,开发环境变量等等。
new webpack.DefinePlugin ({
APP _VERSION : process.env .VUE_APP_VERSION ,
ENV_TEST : process.env .VUE_ENV_TEST
})
import { defineConfig, loadEnv } from 'vite'
const { VITE_SENV_TEST , VITE_APP_VERSION } = loadEnv (mode, process.cwd ())
export default ({ mode }: { mode : string }) => {
return defineConfig ({
define: {
APP_VERSION : VITE_APP_VERSION ,
ENV_TEST :VITE_SENV_TEST
}
})
})
这儿留意,Vite 和 webpack 默许露出的环境变量前缀不相同。
主动加载模块
比方 lodash
plugins : [
new webpack.ProvidePlugin ({
_ : 'lodash'
}),
]
import inject from '@rollup/plugin-inject'
plugins : [
inject ({
_ : 'lodash' ,
exclude : ['**/*.css' , '**/*.yaml' ],
include : ['**/*.ts' , '**/*.js' , '**/*.vue' , '**/*.tsx' , '**/*.jsx' ]
}),
]
底子上所有在用的插件都能够找到对应替换的,乃至像 monaco
,qiankun
,sentry
运用量相对没那么大的都有。
这儿仅仅举例兼容旧代码,lodash 最好仍是写个工具替换成 es-loadsh。
webpack require context
在 webpack 中咱们能够经过require.context
办法动态 解析模块。比较常用的一个做法便是指定某个目录,经过正则匹配等方法加载某些模块,这样在后续增加新的模块后,能够起到动态主动导入 的效果。
比方 layout,router
的主动注册都能够这样用。
const modules = require .context ('base/assets/icons/svg' , false , /.svg$/ )
Vite 支撑运用特别的import.meta.glob
函数从文件系统导入多个模块:
const modules = import .meta .glob ('base/assets/icons/svg/*.svg' )
externals
externals : {
config : 'config' ,
}
import { viteExternalsPlugin } from 'vite-plugin-externals'
plugins : [
viteExternalsPlugin ({
config : 'config'
})
]
ESM 模块
由于 Vite
运用了 ESM 模块方法,所以 commonJs模块
都需求替换成 ESM模块
。
const path = require ('path' )
import path from 'path'
也正是由于这个原因,所以才会又换回了 webpack,这个下面再讲。
主动化转化
社区也有一些主动化从Wepback
转为Vite
的工具,比方vue-cli-plugin-vite,webpack-to-vite,wp2vite等等。
假如是小项目,能够测验一下。大项目不建议运用,不可控。感兴趣的能够去看对应的文档。
ESM 的循环引证问题
能够看到 Vite
的 Issues
有许多相关的问题评论。
github.com/vitejs/vite… github.com/vitejs/vite…
假如是 Vue SFC 文件的循环引证,按官方文档来就能够解决。
假如是其他文件的循环引证,也能够梳理更改。可是吊诡的当地在于,调用栈会呈现 null
。这个在开发中呈现了底子没办法debug
。有时候有上下文,仅仅中心呈现null
还能揣度一下,假如提示一串null
,那底子没办法开发。
CommonJs 与 ESM 关于循环依靠的处理的战略是天壤之别的,webpack 在运转时注入的webpack_require 逻辑在处理循环依靠时的体现与 CommonJs 规范共同。Webapck 依据 moduleId,先到缓存里去找之前有没有加载过,假如有加载过,就直接拿缓存中的模块。假如没有,就新建一个 module,并赋值给缓存中,然后调用 moduleId 模块。所以由于缓存的存在,呈现循环依靠时才不会呈现无限循环调用的状况。
由于 ESM 的静态 import 才能,能够在代码运转之前对依靠链路进行静态剖析。所以在 ESM 形式下,一旦发现循环依靠,ES6 自身就不会再去执行依靠的那个模块了,所以程序能够正常完毕。这也说明晰 ES6 自身就支撑循环依靠,确保程序不会由于循环依靠陷入无限调用。
正是由于处理机制的不同,导致 Vite
下循环引证的文件都会呈现调用栈为 null
的状况。
找了个webpack
插件circular-dependency-plugin
查看了一下循环引证的文件,发现像下面这样跨多组件引证的当地有几十处。改代码也不太实际,只能先换回webpack
了。
webpack 的优化
webpack
仍是用官方封装的 Vue CLI
。
缓存
webpack4
仍是运用 hard-source-webpack-plugin
为模块供给中心缓存的,可是 webpack5
现已内置了该功用。
module .exports = {
chainWebpack (config ) {
config.cache (true )
}
}
hard-source-webpack-plugin
作者现已被 webpack
招安了,原插件也现已不保护了,所以有条件仍是升级到 webpack5
。
esbuild 编译
编译能够运用 esbuild-loader
来替换 babel-loader
,打包这一块就和 Vite
相差不多了。
看了下 vue-cli
的装备,需求换的 rule
是这几个。大约的装备如下:
chainWebpack (config ) {
const rule = config.module .rule ('js' )
rule.uses .clear ()
rule
.use ('esbuild-loader' )
.loader ('esbuild-loader' )
.options ({
jsxFactory : 'h' ,
jsxFragment : 'Fragment' ,
loader : 'jsx' ,
target : 'es2015'
})
.end ()
const tsRule = config.module .rule ('typescript' )
tsRule.uses .clear ()
tsRule
.use ('ts' )
.loader ('esbuild-loader' )
.end ()
}
留意,上面的 jsx
装备只适用于 Vue3
,由于 Vue2
没有露出 h
办法。
假如要在 Vue2
上运用 jsx
解析,得需求一个解析 Vue2
语法完好运转时的包。
pnpm i @lancercomet/vue2-jsx-runtime -D
React 关于全新 JSX 转化的思维
@lancercomet/vue2-jsx-runtime github
大约便是把 jsx transform
从结构 单独移了出来,以脱离结构适配 SWC
,TSC
或许 ESBuild
的 jsx transform
。
const rule = config.module.rule ('js' )
rule.uses.clear ()
rule
.use ('esbuild-loader' )
.loader ('esbuild-loader' )
.options ({
target : 'es2015' ,
loader : 'jsx' ,
jsx : 'automatic' ,
jsxImportSource : '@lancercomet/vue2-jsx-runtime'
})
.end ()
一起需求修正 tsconfig.json
{
"compilerOptions" : {
...
"jsx" : "react-jsx" , // Please set to "react-jsx" .
"jsxImportSource" : "@lancercomet/vue2-jsx-runtime" // Please set to package name.
}
}
类型查看
类型查看这块开发时能够交给 IDE
来处理,没必要再跑一个线程。
chainWebpack (config ) {
config.plugins .delete ('fork-ts-checker' )
}
代码紧缩
这些其实功用影响现已不大了,聊胜于无。
const { ESBuildMinifyPlugin } = require ('esbuild-loader' )
chainWebpack (config ) {
config.optimization .minimizers .delete ('terser' )
config.optimization .minimizer ('esbuild' ).use (ESBuildMinifyPlugin , [{ minify : true , css : true }])
}
优化成果
这是 Vue-CLI
优化之后的打包,现已和 Vite
底子共同了。至于开发,两者的逻辑不相同,热更新确实是慢。
完毕
Vite
的生态现已很丰厚了,底子能满意绝大多数的需求了。咱们这次搬迁由于平常开发留传的一些问题而失利了。应该检讨平常写代码不能只为了快,而疏忽一些细节。
这便是本篇文章的全部内容了,感谢咱们的观看。
看了下 vue-cli
的装备,需求换的 rule
是这几个。大约的装备如下:
chainWebpack (config ) {
const rule = config.module .rule ('js' )
rule.uses .clear ()
rule
.use ('esbuild-loader' )
.loader ('esbuild-loader' )
.options ({
jsxFactory : 'h' ,
jsxFragment : 'Fragment' ,
loader : 'jsx' ,
target : 'es2015'
})
.end ()
const tsRule = config.module .rule ('typescript' )
tsRule.uses .clear ()
tsRule
.use ('ts' )
.loader ('esbuild-loader' )
.end ()
}
留意,上面的 jsx
装备只适用于 Vue3
,由于 Vue2
没有露出 h
办法。
假如要在 Vue2
上运用 jsx
解析,得需求一个解析 Vue2
语法完好运转时的包。
pnpm i @lancercomet/vue2-jsx-runtime -D
React 关于全新 JSX 转化的思维
@lancercomet/vue2-jsx-runtime github
大约便是把 jsx transform
从结构 单独移了出来,以脱离结构适配 SWC
,TSC
或许 ESBuild
的 jsx transform
。
const rule = config.module.rule ('js' )
rule.uses.clear ()
rule
.use ('esbuild-loader' )
.loader ('esbuild-loader' )
.options ({
target : 'es2015' ,
loader : 'jsx' ,
jsx : 'automatic' ,
jsxImportSource : '@lancercomet/vue2-jsx-runtime'
})
.end ()
一起需求修正 tsconfig.json
{
"compilerOptions" : {
...
"jsx" : "react-jsx" , // Please set to "react-jsx" .
"jsxImportSource" : "@lancercomet/vue2-jsx-runtime" // Please set to package name.
}
}
类型查看
类型查看这块开发时能够交给 IDE
来处理,没必要再跑一个线程。
chainWebpack (config ) {
config.plugins .delete ('fork-ts-checker' )
}
代码紧缩
这些其实功用影响现已不大了,聊胜于无。
const { ESBuildMinifyPlugin } = require ('esbuild-loader' )
chainWebpack (config ) {
config.optimization .minimizers .delete ('terser' )
config.optimization .minimizer ('esbuild' ).use (ESBuildMinifyPlugin , [{ minify : true , css : true }])
}
优化成果
这是 Vue-CLI
优化之后的打包,现已和 Vite
底子共同了。至于开发,两者的逻辑不相同,热更新确实是慢。
完毕
Vite
的生态现已很丰厚了,底子能满意绝大多数的需求了。咱们这次搬迁由于平常开发留传的一些问题而失利了。应该检讨平常写代码不能只为了快,而疏忽一些细节。
这便是本篇文章的全部内容了,感谢咱们的观看。