前语

关于Vite和Vue3的评论越来越多,看了官网的特性后,真是按捺不住想测验一下。开发环境秒开?Composition APISFC Style CSS Variable Injection? 看起来哪个都比webpack+Vue2香呀。(尤大都向React推荐Vite了,难道你还不试一下Vite么?)
其实在去年,咱们在LOFTER的哈利波特街区活动中就测验运用了Vite2 + Vue3建立活动主街区页面,当时对Vue3的语法还不是很适应,为啥ref要用value去取值?reactive解构后的呼应性怎么没了?Vite热更新咋不太灵光?为了确保活动页在Android6以下能满足基本交互需求,还引进了es6-proxy-polyfill,友爱的提示用户体系不支撑。出于兼容性和稳定性的考虑,咱们没有在其他项目中运用。
时刻来到本年七月,跟着Vue2.7的正式发布,许多Vue3的编码特性被正式搬迁到了Vue2,更像是Vue3的根底试用版(Vue:再过18个月,我也懒得管Vue2了)。

The Composition API is backported using Vue 2’s getter/setter-based reactivity system to ensure browser compatibility.

便是官方的这句话,让我觉得把现在的Vue2项目彻底升到2.7是一件百利而无一害的工作。咱们的Vue2项目跟着事务迭代承载着愈加杂乱的事务场景,如何提高项目的开发体会也提上了日程。实际运用中,Vite仍是挺多坑要踩的,可是Vue2.7更新的内容,结合社区生态,对事务完成和开发体会的提高都提供了助力

实战——查验Vite+Vue2.7

那就找个场景先预研一下吧,于是稻米节活动被拿来做了试验(希望不会被稻米看到)。
经过对官方文档的阅读,以及检查社区的评论,Vue2.7最次也是支撑option api的(假如遇到处理不了的bug降级一下写法就好了)。已然做试验,就要有试验目的。

  1. 查验SFC setup语法糖的各种写法兼容性
  2. 探究Vite现在开展的怎么样了
  3. 查验Vite出产环境的稳定性

先贴个定论,1和2的结果都挺好,3便是坑多多了。
试验流程:

项目建立

单纯的Vite Getting Started流程实在是过于简单,啥装备都没有,难不成每个功能都要去搜索引擎查?还好有 awesome-vue 这个项目,集结了我们的才智(踩过的坑),参看了各种脚手架模板后,结合事务需求,项目架子初步搭好。
简单贴一下package.json

"dependencies": {
  "@sentry/browser": "^5",
  "@sentry/integrations": "^5", // sentry用来收集反常
  "@vueuse/core": "^9.0.2",  // 根据Composition API的东西函数,一起支撑Vue2, Vue3
  "axios": "^0.27.2",
  "nejsbridge": "^1.7.19",
  ……
  "vue": "^2.7.8",
  "vue-clipboard2": "^0.3.3",
  "vue-router": "^3.5.4"
},
"devDependencies": {
  "@antfu/eslint-config": "^0.25.2", // Anthony Fu是Vue和Vite团队的核心成员,有许多开源作品
  "@vitejs/plugin-legacy": "^2.0.0", // 主动生成传统版别的chunk及与其相对应ES言语特性方面的polyfill
  "@vitejs/plugin-vue2": "^1.1.2", // plugin only works with Vue@^2.7.0.
  "autoprefixer": "^10.4.8", 
  "eslint": "^7.32.0",
  "less": "^4.1.3",
  "nei-ts-helper": "^0.1.3", // 组内接口生成TS声明的东西
  "terser": "^5.4.0", // 出产环境打包代码需求
  "typescript": "^4.6.4",
  "unplugin-auto-import": "^0.10.1",  // vue函数的主动导入
  "unplugin-vue-components": "^0.21.2", // vue组件库的主动按需导入
  "vite": "^3.0.4",
  "vue-tsc": "^0.38.4"
}

重点推荐一下unplugin-auto-importunplugin-vue-components

unplugin-auto-import处理了vue3-hook、vue-router、useVue等多个插件的主动导入,也支撑自定义插件的主动导入,是一个功能强大的Typescript支撑东西。根据unplugin,在构建和打包的时候主动解析模块并引进。

// vite.config.js
plugins: [
  AutoImport({
    imports: [
      'vue',
      'vue-router',
      '@vueuse/core',
    ],
    // 处理eslint报错问题
    eslintrc: {
      enabled: false,
      globalsPropValue: true,
    },
    dts: 'src/auto-imports.d.ts',
  }),
  ……
]
// ==> 主动生成大局声明 auto-imports.d.ts
declare global {
  const asyncComputed: typeof import('@vueuse/core')['asyncComputed']
  const autoResetRef: typeof import('@vueuse/core')['autoResetRef']
  const computedAsync: typeof import('@vueuse/core')['computedAsync']
  ……
}

unplugin-vue-components支撑自定义组件主动引进,相同根据unplugin

// vite.config.js
plugins: [
  Components({
    transformer: 'vue2', // vue2.7必需
    dirs: ['src/components'],
    extensions: ['vue'],
    dts: 'src/components.d.ts',
  }),
  ……
]
// ==> 主动生成大局声明 auto-imports.d.ts
declare module '@vue/runtime-core' {
  export interface GlobalComponents {
    Container: typeof import('./components/Container.vue')['default']
    Header: typeof import('./components/Header.vue')['default']
    RouterLink: typeof import('vue-router')['RouterLink']
    RouterView: typeof import('vue-router')['RouterView']
    ……
  }
}

事务开发

  • 运用插件后代码的书写简便了许多,格式愈加清爽,import理论上能够经过完善插件做到彻底消除
<script setup>
  import {
    getNosThumbWebP,
  } from '@/common/utils';
  const props = defineProps(['blogNickName']);
  const name = computed(() => (props.blogNickName ? `@${props.blogNickName}` : '你的'));
  const bgUrl = `url(${getNosThumbWebP('', {
    thumbnail: '1125x0',
  })})`;
</script>
<template>
  <div class="header">
    <div class="user-name">{{name}}</div>
  </div>
</template>
<style scoped lang="less">
  .header {
    width: 100%;
    height: 1333px;
    background-image: v-bind(bgUrl);
    background-size: 100% 100%;
    background-repeat: no-repeat;
    position: relative;
    .user-name {
      ……
    }
  }
</style>
  • SFC Style CSS Variable Injection,力荐的style办理方案。能够完成JS和CSS的通信与样式的呼应式更新,在主题切换场景中运用非常便利。具体能够检查
    Vue3 的 SFC Style CSS Variable Injection 提案完成的背后

留意:v-bind作用的元素假如有v-if的场景,最好不是template里的根元素,否则可能导致css变量无处挂载。

  • setup语法糖,自己用了才知道,真的好用。还需求在更杂乱的事务场景中实践,结合相似vueuse的用法做逻辑封装。
  • 热更新经常无效,Vue.extend(ToastConfig)创建的组件彻底不会更新,需求重启dev server才行。

打包测验

  1. 踩过坑才知道下面这部分内容有多重要
  • 构建出产版别-浏览器兼容性
  • 用于出产环境的构建包会假定方针浏览器支撑现代 JavaScript 语法。默许状况下,Vite 的方针是能够 支撑原生 ESM script 标签支撑原生 ESM 动态导入 import.meta 的浏览器:
  • 你也能够经过 build.target 装备项 指定构建方针,最低支撑 es2015。
  • 请留意,默许状况下 Vite 只处理语法转译,且 默许不包括任何 polyfill。你能够前往 Polyfill.io 检查,这是一个根据用户浏览器 User-Agent 字符串主动生成 polyfill 包的服务。
  • 传统浏览器能够经过插件 @vitejs/plugin-legacy 来支撑,它将主动生成传统版别的 chunk 及与其相对应 ES 言语特性方面的 polyfill。兼容版的 chunk 只会在不支撑原生 ESM 的浏览器中进行按需加载。
// 经过监测支撑import.meta.url和动态引进判断是否为现代浏览器
<script type="module">try{
  import.meta.url;
  import("_").catch(()=>1);
}catch(e){}
window.__vite_is_modern_browser=true;
</script>
  <script type="module">!function(){
  if(window.__vite_is_modern_browser)return;
  console.warn("vite: loading legacy build because dynamic import or import.meta.url is unsupported, syntax error above should be ignored");
  var e=document.getElementById("vite-legacy-polyfill"),n=document.createElement("script");n.src=e.src,n.onload=function(){System.import(document.getElementById('vite-legacy-entry').getAttribute('data-src'))},document.body.appendChild(n)
}();</script>

线上的一个白屏问题,发现报错源自try{}catch{}这种ES10才支撑的语法。
vite3修正了默许的打包装备,仅支撑Chrome >=87。需求经过修正build: target提高构建产品的兼容性

  1. _VITE_ASSET legacy.js打包的css相对途径不对,全换成cdn资源处理(最新版vite已修复该问题)。

暂没有遇到其他兼容性问题。

进阶——将一个重度Webpack+Vue2工程升级为Vite+Vue2.7

LOFTER阛阓工程用webpack的重度体现在

  1. 一套dev+build+init命令行交互
  2. 自定义页面参数装备,结合HtmlWebpackPlugin形成动态页面装备,HtmlWebpackPlugin确实是多页运用构建的神器
  3. 有些年头的css,babel装备
  4. 私有的webpack插件,离线包插件等

这些要么移除掉,要么在Vite中找到替代方案。

开始升级。。。

  1. 安装vitevue2.7@vitejs/plugin-vue2@vitejs/plugin-legacy去掉vue-template-compiler
  2. 增加vite.config.js,装备aliasless,vite会默许按root装备的文件目录,主动查找文件目录下的index.html,页面模板中增加代码
<script type="module" src="/cardHome/index.js"></script>

然后便是疯狂报错

  • require url问题
vue.runtime.esm.js:4573 [Vue warn]: Error in created hook: "ReferenceError: require is not defined"

import.meta.url 是一个 ESM 的原生功能,会露出当前模块的 URL。将它与原生的 URL 构造器 组合运用,在一个 JavaScript 模块中,经过相对途径咱们就能得到一个被完好解析的静态资源 URL

// icon: require('@/common/images/card/upgrade/home.png') =>
icon: new URL('@/common/images/card/upgrade/home.png', import.meta.url).href
  • svg 雪碧图插件替换,svg-sprite-loader->vite-plugin-svg-icons

symbolId的拼装方式不同,stroke color替换后者有问题,暂时能够经过样式掩盖处理

  • css-loader中@value的用法在vite中不支撑,能够运用less变量的方式替换,不大局运用css变量一个是兼容性,还有是阛阓自身颜色不需求切换
/* @value --default_common_bg_tertiary: #3E3E3E;  */
@default_common_bg_tertiary: #3E3E3E;
/* background: --default_common_bg_tertiary; */
background: @default_common_bg_tertiary;
  1. 运用HTTP2,能够装备server.https来启用,像卡牌主页的代码模块许多,经过H2能够改进恳求量大导致的页面刷新耗时较长的问题。可是

留意:当 server.proxy 选项 也被运用时,将会仅运用 TLS。

这会导致假如装备了接口代理的状况下项目又用不了H2了。
能够运用nginx处理该问题,最新版nginx(1.22.0)支撑装备listen 443 ssl http2;结合server.httpsbase装备的途径,能够完成在开发环境下运用H2加快页面刷新。

Webpack与Vite开发环境比较

根据win10,i5-6500 CPU,16.0 GB内存

webpack
devtool: ‘cheap-module-eval-source-map’,
vite
便于比较,运用http/1.1
无缓存run dev 悉数40s
主页+背包24s
暂无悉数数据
主页+背包3s+,需求更长烘托时刻
有缓存run dev 悉数30s
主页+背包18s
暂无悉数数据
主页+背包3s-
热更新 主页7s左右 无提示,看起来很快,有时候改了组件代码刷新也没用,只能重新run dev
背包页无缓存刷新 38 requests
3.1 MB transferred
12.7 MB resources
Finish: 5.44 s
DOMContentLoaded: 1.79 s
Load: 2.52 s
191 requests
3.7 MB transferred
3.7 MB resources
Finish: 3.49 s
DOMContentLoaded: 1.81 s
Load: 3.46 s
背包页有缓存刷新 34 requests
12.6 kB transferred
12.7 MB resources
Finish: 1.99 s
DOMContentLoaded: 1.20 s
Load: 1.91 s
191 requests
446 kB transferred
3.7 MB resources
Finish: 3.02 s
DOMContentLoaded: 1.48 s
Load: 2.99 s
主页无缓存刷新 82 requests
20.1 MB transferred
38.0 MB resources
Finish: 5.55 s
DOMContentLoaded: 2.57 s
Load: 4.35 s
278 requests
20.8 MB transferred
24.9 MB resources
Finish: 4.84 s
DOMContentLoaded: 2.46 s
Load: 4.42 s
主页有缓存刷新 81 requests
120 kB transferred
38.0 MB resources
Finish: 3.39 s
DOMContentLoaded: 1.82 s
Load: 3.09 s
278 requests
557 kB transferred
24.4 MB resources
Finish: 3.88 s
DOMContentLoaded: 1.92 s
Load: 3.15 s
  • 比较1:在运用esm的状况下,页面的烘托速度并没有显著提高,运用H2或加强模块异步加载的处理睬稍微好点。

在实际项目中,Rollup 通常会生成 “共用” chunk
Vite 将运用一个预加载过程主动重写代码,来分割动态导入调用,以完成当 A 被恳求时,C 也将 一起 被恳求:
Entry —> (A + C)
C 也可能有更深的导入,在未优化的场景中,这会导致更多的网络往复。Vite 的优化会跟踪所有的直接导入,不管导入的深度如何,都能够彻底消除不必要的往复。

  • 比较2:esm会发出更多的requests,浏览器等待耗时显着,偶然可能会堵塞导致更长的烘托时刻
  • 比较3:Vite热更新不抱负,需求经常刷新或许重启,重启dev尽管很快,可是页面烘托等待耗时可能较长

关于vite的热更新:
Vite 经过特殊的 import.meta.hot 对象露出手动 HMR API。
Vite 热更新问题排查
能够监听自定义 HMR 事件,在插件中处理未更新的状况

还有一些运用场景进一步实践后再和我们分享。

一些感想:

  • Vite更像是一个上层建筑,集合了esbuild,rollup,各种loader等,假如项目比较杂乱,依然需求深度定制,逃不开装备的递归运用
  • 新项目仍是很推荐用Vite构建,直接运用Vite和rollup的生态
  • 老项目从webpack搬迁仍是缺少一些完备的插件,也是为社区贡献的机会,开发环境Vite出产环境webpack理论上是可行的,需求处理两者之间的差异,比如环境变量,插件的差异等等。开发环境和出产环境的构建方式不同总感觉会有坑,仍是需求慎重