从Vue转换看Webpack与Vite 代码转换机制差异详解

 更新时间:2022年10月11日 09:04:17   作者:candyTong  
这篇文章主要为大家介绍了从Vue转换看Webpack与Vite代码转换机制差异详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

配置方式

我们知道,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` 文件中的 `<script>` 块
      {
        test: /\.js$/,
        loader: 'babel-loader'
      },
      // 它会应用到普通的 `.css` 文件
      // 以及 `.vue` 文件中的 `<style>` 块
      {
        test: /\.css$/,
        use: [
          'style-loader',
          'css-loader'
        ]
      }
    ]
  },
  plugins: [
    new VueLoaderPlugin()
  ]
}

为什么 webpack 使用 loader 还不够,还需要 Vue plugin?

这个问题我们留在后面说明

Vue 文件编译的流程

下面是一个简单的 Vue SFC (单文件组件):

<script setup>
import { ref } from 'vue'
const msg = ref('Hello World!')
</script>
<template>
  <h2>{{ msg }}</h2>
</template>
<style>
  h2{
    font-size: 50px
  }
</style>

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` 文件中的 `<script>` 块
      {
        test: /\.js$/,
        loader: 'babel-loader'
      },
      // 它会应用到普通的 `.css` 文件
      // 以及 `.vue` 文件中的 `<style>` 块
      {
        test: /\.css$/,
        use: [
          'style-loader',
          'css-loader'
        ]
      }
    ]
  },
}

配置 Vue 时,我们做了如下配置:

  • Vue 文件会交给 vue-loader 处理
  • js 文件给 babel-loader 处理
  • CSS 文件给 css-loader 和 style-loader 处理

我们再来回顾一下这个流程:

  • Main.vue 匹配中 vue-loader,被处理成临时模块
  • ./Main.vue?vue&type=script 匹配中 vue-loader(webpack 会去掉 query 部分,因此 /\.vue$/ 可以匹配),从缓存中取出 Vue SFC script 的内容。

到了这一步,我们会发现,匹配不到其他 loader 了,因为 babel-loader 匹配的规则是 /\.js$/,这样转换就没办法再进行下去了,这就是 webpack loader 机制的局限性。

因此仅仅使用 loader,是没有办法将 JS、CSS 传递给对应 loader 处理的,这也是 webpack loader 机制的局限性

为了解决这个问题,借助 webpack plugin:

// webpack.config.js
const { VueLoaderPlugin } = require('vue-loader')
module.exports = {
  module: {
    rules: [
        // 省略...
    ]
  },
  plugins: [
    new VueLoaderPlugin()
  ]
}

VueLoaderPlugin 做了什么?

VueLoaderPlugin 的内容比较复杂,本文不会详细的说明。这里直说最终的转换结果:

Webpack 提供一种内联 loader 的能力:

import script from "-!babel-loader!vue-loader??ref--0!./App.vue?vue&type=script&setup=true&lang=js"

这种内联 loader 的能力,在 import 的路径中显式的指定了该模块会经过的 loader:

  • 从后往前看,最后的是处理的文件
  • loader 的执行顺序为从右到左(loader 用 ! 分割)

VueLoaderPlugin 会为 script、template、style,根据不同给的类型,生成不同的内联 loader import 语句,使它们能够正确地被其他的 loader 处理

对比和总结

webpack 显式指定了模块对应的 loader,正是这个机制,导致 vue SFC 的 script、template、style,没办法被其他 loader 处理,需要插件做一些复杂的操作,最终用 Inline loader import 强制指定 loader,整个过程比较复杂。

Vite/Rollup 的模块会经过所有的插件,在插件中过滤出需要处理的模块,其他的交给下一个插件处理。这样的机制使 Vue 文件的各个部分,能经过所有插件的处理,从而避免了 webpack 遇到的问题,这也使 Vue 在 Vite/Rollup 中的转换实现更为清晰和简单。

最后,我们通过这样的对比,目的不能说明 Webpack/Vite/Rollup 谁好谁坏,而是在学习过程中,横向对比,加深对它们的了解。

以上就是从Vue转换看Webpack与Vite 代码转换机制差异详解的详细内容,更多关于Vue Webpack Vite代码转换机制差异的资料请关注脚本之家其它相关文章!

相关文章

  • el-table 选择框根据条件设置某项不可选中的操作代码

    el-table 选择框根据条件设置某项不可选中的操作代码

    这篇文章主要介绍了el-table 选择框根据条件设置某项不可选中的操作代码,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧
    2024-03-03
  • vue element实现将表格单行数据导出为excel格式流程详解

    vue element实现将表格单行数据导出为excel格式流程详解

    这篇文章主要介绍了vue element实现将表格单行数据导出为excel格式流程,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习吧
    2022-12-12
  • vite打包去除console.log和debugge的方法实践

    vite打包去除console.log和debugge的方法实践

    本文主要介绍了vite打包去除console.log和debugge的方法实践,vite 已经将这个功能内置了,所以我们只需要修改配置文件,下面就来介绍一下如何修改
    2023-12-12
  • Vue对象的单层劫持图文详细讲解

    Vue对象的单层劫持图文详细讲解

    这篇文章主要介绍了vue2.x对象单层劫持的原理实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-01-01
  • 详解vue使用$http服务端收不到参数

    详解vue使用$http服务端收不到参数

    这篇文章主要介绍了vue使用$http服务端收不到参数,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-04-04
  • Vue el-autocomplete远程搜索下拉框并实现自动填充功能(推荐)

    Vue el-autocomplete远程搜索下拉框并实现自动填充功能(推荐)

    在elementui Input输入框中可以找到远程搜索组件,获取服务端的数据。这篇文章主要给大家介绍Vue el-autocomplete远程搜索下拉框并实现自动填充功能,感兴趣的朋友一起看看吧
    2019-10-10
  • vue.js实现简单计时器功能

    vue.js实现简单计时器功能

    这篇文章主要为大家详细介绍了vue.js实现简单计时器功能,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-08-08
  • vue+swiper实现侧滑菜单效果

    vue+swiper实现侧滑菜单效果

    这篇文章主要为大家详细介绍了vue+swiper实现侧滑菜单效果,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-12-12
  • vue项目中常用解决跨域的方法总结(CORS和Proxy)

    vue项目中常用解决跨域的方法总结(CORS和Proxy)

    在vue项目中,一般我们会遇到跨域的问题,vue项目中解决跨域是非常简单的,下面这篇文章主要给大家介绍了关于vue项目中常用解决跨域的方法,主要解释CROS和Proxy两种方式,需要的朋友可以参考下
    2022-12-12
  • ElementUI年份范围选择器功能实现

    ElementUI年份范围选择器功能实现

    elementUI中有日期范围组件,月份范围选择的,就是没有年份范围选择的,需要加一个类似风格的,下面这篇文章主要给大家介绍了关于ElementUI年份范围选择器功能实现的相关资料,需要的朋友可以参考下
    2023-02-02

最新评论