前言
项目使用的两个第三方库中有使用到 node
环境的api
,vite
本身并不会自动引入这些polyfills,所以需要自己添加他们,中间也遇到了一些坑,包括分包,js加载顺序导致的polyfills没有正常生效,以及 global 和 process对象的特殊处理
代码
下面是我自己用的一个 plugin, 可以参考下
node-polyfills-plugins.ts
import { NodeGlobalsPolyfillPlugin } from '@esbuild-plugins/node-globals-polyfill'
import { NodeModulesPolyfillPlugin } from '@esbuild-plugins/node-modules-polyfill'
import rollupNodePolyFill from 'rollup-plugin-node-polyfills'
export function NodePolyFillsPlugin(): Plugin {
return {
name: 'vite-node-polyfills-plugins',
config: async (options) => {
return {
resolve: {
alias: {
util: 'rollup-plugin-node-polyfills/polyfills/util',
sys: 'util',
events: 'rollup-plugin-node-polyfills/polyfills/events',
stream: 'rollup-plugin-node-polyfills/polyfills/stream',
path: 'rollup-plugin-node-polyfills/polyfills/path',
querystring: 'rollup-plugin-node-polyfills/polyfills/qs',
punycode: 'rollup-plugin-node-polyfills/polyfills/punycode',
url: 'rollup-plugin-node-polyfills/polyfills/url',
string_decoder: 'rollup-plugin-node-polyfills/polyfills/string-decoder',
http: 'rollup-plugin-node-polyfills/polyfills/http',
https: 'rollup-plugin-node-polyfills/polyfills/http',
os: 'rollup-plugin-node-polyfills/polyfills/os',
assert: 'rollup-plugin-node-polyfills/polyfills/assert',
constants: 'rollup-plugin-node-polyfills/polyfills/constants',
_stream_duplex: 'rollup-plugin-node-polyfills/polyfills/readable-stream/duplex',
_stream_passthrough: 'rollup-plugin-node-polyfills/polyfills/readable-stream/passthrough',
_stream_readable: 'rollup-plugin-node-polyfills/polyfills/readable-stream/readable',
_stream_writable: 'rollup-plugin-node-polyfills/polyfills/readable-stream/writable',
_stream_transform: 'rollup-plugin-node-polyfills/polyfills/readable-stream/transform',
timers: 'rollup-plugin-node-polyfills/polyfills/timers',
console: 'rollup-plugin-node-polyfills/polyfills/console',
vm: 'rollup-plugin-node-polyfills/polyfills/vm',
zlib: 'rollup-plugin-node-polyfills/polyfills/zlib',
tty: 'rollup-plugin-node-polyfills/polyfills/tty',
domain: 'rollup-plugin-node-polyfills/polyfills/domain'
}
},
optimizeDeps: {
esbuildOptions: {
define: {
global: 'globalThis'
},
plugins: [
NodeGlobalsPolyfillPlugin({
process: true,
buffer: true,
}),
NodeModulesPolyfillPlugin()
]
}
},
plugins: [rollupNodePolyFill()]
}
}
} as Plugin
}
使用
vite.config.ts
import {NodePolyFillsPlugin} from './node-polyfills-plugins'
export default definConfig({
plugins: [
NodePolyFillsPlugin()
]
})
在vite.config.ts
文件直接引入这个plugin,就能覆盖大部分的node api,如果打包出来发现有没覆盖到的,可以在这个图(库)里找找,根据打包后运行项目的提示,缺啥引啥就行
也可以到这个路径下去找找自己需要的 node_modules.pnpm\rollup-plugin-node-polyfills@0.2.1\node_modules\rollup-plugin-node-polyfills\dist\index.js
global 与 process
虽然前面已经通过plugin注入了这个两个对象,但打包生产后依然提示 global | process is not defined
如果只是缺少这两个对象,只需要做这一步就行。
所以需要再新建一个文件init.ts
内容如下
(window as any).global = window;
(window as any).process = {
env: import.meta.env,
argv: ['']
}
之后在main.ts
中引入(保证他在第一行)
import './init'
import { createApp } from 'vue'
import AppComponent from './App.vue'
import router from '@/router/index'
import pinia from '@stores/index'
import '@assets/tailwind.css'
async function initApp() {
const app = createApp(AppComponent)
app.use(pinia).use(router)
app.mount('#app')
}
initApp()
其他情况
使用node api的库被单独打包
大部分情况下,使用了以上的plugin
就能解决大部分项目中使用了node
环境api
的问题,但是当你在vite中使用manualChunks
将使用了node api
的库单独打包时,依然会有node环境 api is not defined 的报错。
这个问题是因为,使用node api
的库比polyfills
所在的包先加载完,先执行,这时,node
环境的api
还没来得及挂载到window
上,所以依然会报错。
manualChunks
配置
const config = {
plugins: [ViteCompression(),visualizer({
emitFile: true,
// @ts-ignore
file: 'stats.html'
})],
esbuild: {
drop: ["console", "debugger"],
},
build: {
sourcemap: true,
outDir: path.resolve(__dirname, 'dist'),
chunkSizeWarningLimit: 650,
rollupOptions: {
manualChunks: {
mediainfo: ['mediainfo.js'],
slz: ['slz.storage'],
vue: ['vue'],
pinia: ['pinia'],
'element-plus': ['element-plus'],
'lodash-es': ['lodash-es']
}
}
}
}
解决方法
解决的思路主要就是将这个库的加载顺序尽量延后
利用路由懒加载
利用路由懒加载访问到才加载的特性,将包含使用node api库的页面修改为异步加载,这点在webpack上直接使用import()
在vite
中则稍微麻烦一些,需要使用 import.meta.glob
// 正常的页面,可以不用修改直接引入
import Home from '@views/home/home.vue'
// view就是存放页面的文件夹,这里先导入这个文件夹下所有的vue文件
const modules = import.meta.glob('../views/*/*.vue')
// modules实际上是一个对象
function asyncImport(path: string) {
return modules[`../views${path}`]
}
// HomeEdit 这个页面包含使用node api的库,因此需要使用异步加载
const HomeEdit = asyncImport('/home/edit.vue')
modules
长这样
这样操作后,vite
就会将edit.vue
单独打包,访问时再加载
利用异步组件
当页面要求第一时间加载时,就没办法使用懒路由来处理了,这是我们可以把使用了node api
的部分抽离为一个组件,并将他作为异步组件引入,同样能达到引入polyfills
的代码执行后,再加载执行包含node api
代码的效果。
包含node api
的组件正常写就行,主要时组件引入时需要注意下,使用defineAsyncComponent
包一层
<script setup lang="ts">
const upload = defineAsyncComponent(()=>import('@/components/common/up-load.vue'))
</script>