您现在的位置是:网站首页> 编程资料编程资料
从Vue转换看Webpack与Vite 代码转换机制差异详解_vue.js_
2023-05-24
192人已围观
简介 从Vue转换看Webpack与Vite 代码转换机制差异详解_vue.js_
配置方式
我们知道,Webpack 是使用 loader 转换代码的,而 Vite/Rollup 则是使用插件转换代码,那这两种机制有什么差异呢?我们用 Vue 的转换来说明一下。
Vite 使用插件转换代码,直接在 plugins 使用 @vitejs/plugin-vue 即可
// vite.config.js import vue from '@vitejs/plugin-vue' export default { plugins: [vue(), /* 其他插件 */ ] } Webpack 使用 loader 转换代码,有时候需要同时配合 Plugin 才能完成代码转换(例如 Vue)
// webpack.config.js const { VueLoaderPlugin } = require('vue-loader') module.exports = { mode: 'development', module: { rules: [ { test: /\.vue$/, loader: 'vue-loader' }, // 它会应用到普通的 `.js` 文件 // 以及 `.vue` 文件中的 `{{ msg }}
Vue SFC 分为 3 个部分:
- script,可以是 JS、TS 等语法
- template(会被转换成 render 函数)
- style,可以是 CSS、Less 等语法
由于 Vue 文件包含三个部分,而一个模块经过转换后仍然是一个模块(例如经过 loader 转换后,仍然是一份代码,不能变成三个部分)
但我们可以用一个巧妙的办法去解决这个问题:使用一个临时模块,去分别引入 script、template、style,并将其组合,伪代码如下:
// 引入 main script,获取到的是组件的配置对象 import script from './Main.vue?vue&type=script' // 引入 template import { render } from './Main.vue?vue&type=template&id=xxxxxx' // 引入 css import './Main.vue?vue&type=style&index=0&id=xxxxxx' // 给组件对象设置 render 函数 script.render = render // 设置一些元信息,在开发环境有用 script.__file = 'example.vue' // style 的 scope id,用于组件样式隔离 script.__scopeId = 'xxxxxx' export default script 一个 Vue 的会有大致如下的处理流程:
- 将 Vue SFC 转换成临时模块,分别引入 script、template、style
- vue-loader/插件会保存 script、template、style 的内容
- 打包工具遇到 import 语句,会分别处理:
- script:从 vue-loader/插件中,取出之前缓存的 script,然后交给其他 JS loader/插件处理(如 babel)
- template:从 vue-loader/插件中,取出之前缓存的 template,然后交给其他 JS loader/插件处理(因为 template 转换成 render 函数,这部分也是 JS 类型)
- style:从 vue-loader/插件中,取出之前缓存的 style,然后交给其他 Style loader/插件处理(如 Less)

Vue 的转换,在 webpack 和 vite 都是类似的思路,只不过由于 webpack 和 Vite 的机制不同,在 Vue 的转换插件上的的使用和实现上,也会有所差异。
Vite 的 Vue 转换流程
Vite/Rollup 使用插件转换模块,由于没有显式地声明模块跟插件的匹配规则(例如 webpack 显式声明了 Vue 文件用 vue-loader 处理),因此每个模块的转换都需要经过所有的插件
插件只能处理它能处理的模块(例如:Vue 插件不能后处理 less 模块),Vite/Rollup 插件必须要在插件内部对模块类型进行判断,然后后决定是否进行处理。
export default function vuePlugin() { return { name: 'transform-vue', transform(source, id) { // source 文件的内容或上一个插件转换过的内容 // id 一般为文件的真实路径,需要在插件内判断文件是否为 vue 后缀 if (isVueFile(id)) { // 对 Vue 模块进行转换 return // 返回转换后的内容 } // 其他类型模块不作处理 } } } 上面的插件,就只对 Vue 模块进行处理,其他的模块,则直接交给下一个插件处理。
Vite Vue 插件的大致处理流程如下:
./Main.vue在 load 阶段,会依次经过所有插件,如果没有被处理,则默认是读取文件的内容。(一般情况下也不需要处理)./Main.vue在 transform 阶段,会依次经过所有插件,经过 Vue 处理后(分离 template、script、style),会转换成临时模块,然后再经过其他插件处理(例如 babel)- 打包工具解析转换后的代码,遇到
./Main.vue?vue&type=script ./Main.vue?vue&type=script在 load 阶段,会依次经过所有插件,经过 Vue 插件,从之前的缓存中,取出 script 部分(如果插件执行 load 阶段时有返回值,则立即结束 load 阶段)./Main.vue?vue&type=script在 transform 阶段,会依次经过所有插件,最终得到转换后的代码

template 和 style 部分类似就不重复写了。
需要注意的是,这跟 @vite/plugin-vue 实际的处理方式不完全一致,主要的区别是:我们这里在临时模块,引入了 template、script、style 三个部分,实际上,可以直接将 template、script 内联到临时模块,这样就只需要 import style 部分即可。
Webpack 的 Vue 转换流程
在 webpack 的配置文件中,需要显式声明 rule,为对应的模块配置对应的 loader。
// webpack.config.js { rules: [ { test: /\.vue$/, loader: 'vue-loader' }, // 它会应用到普通的 `.js` 文件 // 以及 `.vue` 文件中的 `