找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

2123

积分

0

好友

271

主题
发表于 9 小时前 | 查看: 0| 回复: 0

由于 Vite 在开发态是基于 ESM 进行模块化开发,而 ESM 的浏览器兼容版本有限,如下图所示。

ESM浏览器兼容性支持表

所以,如果你打算使用 Vite 作为构建工具去开发,你至少需要一个合适版本的浏览器。如果你和我一样, Chrome 版本浏览器比较低,但又不想升级,想留着偶尔方便自测和定位浏览器兼容问题,那我推荐你安装一个 Chromium 。这样你就可以一个电脑里面拥有两个 Chrome没有两个 Chrome 的前端不是好前端[狗头]。

Chrome与Chromium快捷方式对比

这时候,你可能又会有另外一个问题,什么是 ESM? 关于这个问题,这里不展开说。通俗易懂的理解,就是在开发态,我们加载的是模块化的 ts 或者 js,而在打包后,我们加载的就是的 CommonJS ,如下图。

开发态使用ESM加载模块

构建后使用nomodule加载脚本

除此之外,你要升级你的 node 环境到 node 14 以上版本。而如果你也是用的 windows 7 ,这就有了第二个问题,如何在 windows 7 下安装 node 14 需要将下载的 node 包放在指定的 nvm 文件夹同时将系统变量 NODE_SKIP_PLATFORM_CHECK 设置为 1

组件准备:因为希望组件风格和之前保持一致,为了更加灵活的修改组件,我们基于 antdv 进行了简单封装,并发布到私有的 npm 仓库。

组件自动引入 unplugin-vue-components

上面的封装也带来另外一个坑,就是会导致无法使用 unplugin-vue-components 。我去提了 issues 希望可以支持组件名动态设置 和 PR ,应该下个版本 AntDesignVueResolver 就可以支持了。

你可能要习惯的和 Vue2 的不同

在实际开发过程中,从 Vue2 升级到 Vue3 ,我觉得有几个地方或许是需要适应一下的,这里也提一下。

组合式 API

组合式 API 是一系列 API 的集合,它是 Vue 3 和 Vue 2.7 的内置功能,而对于更老的 Vue 2 版本,则可以使用 @vue/composition-api 包。组合式 API 包括:

Vue3组合式API核心概念

<script setup> 是在单文件组件 (SFC) 中使用组合式 API 的编译时语法糖。个人感觉,不用这个语法糖写法上和 Vue 2 更加接近,而使用这个语法糖写起来则更丝滑些,写法对比如下图:

不使用script setup语法的Vue3组件

使用script setup语法的Vue3组件

响应式

数组

有两种实现方式,如下图。我个人用下来,觉得 写法一 更丝滑些。

数组响应式数据操作示例

响应式代理

你可能也注意到,对整个数组的变更,我用的是 Object.assign 去实现的,因为只有这样,才能保持数据的响应式。这和 Vue 2 也是有区别的,原因和 Vue 3 的数据响应式原理有关。至于 Vue 3 的数据响应式原理这里不展开说。

响应式对象操作注意事项

双向绑定实现

父组件

<template>
  <div class="hello">
    <h1 @click="showModal">打开弹窗</h1>
    <Modal v-model="visible"></Modal>
  </div>
</template>

<script setup lang="ts">
  import Modal from './modal-setup.vue'
   defineProps<{ msg: string }>()
  const visible = ref(false)
  const showModal = () => {
    visible.value = true
  }
</script>

<style scoped>
  .hello {
    position: relative;
    width: 100px;
  }
</style>

子组件

<template>
  <teleport to="#app">
    <div class="modal" @click="hideModal" v-show="visible">
      modal
    </div>
  </teleport>
</template>

<script setup lang="ts">
  const props = defineProps<{ modelValue: Boolean }>()
  const emit = defineEmits(['update:modelValue'])
  const visible = computed({
    get: () => props.modelValue,
    set: val => {
      emit('update:modelValue', val)
    }
  })
  const hideModal = () => {
    visible.value = false
  }
</script>

<style scoped>
  .modal {
    position: absolute;
    top: 0;
    right: 0;
    background: #999;
    width: 300px;
    height: 100vh;
  }
</style>

echarts 使用

<template>
  <div v-for="(card, index) in cardList" :key="`${card.id}-${index}`">
    <div class="card">
      <!-- 当你放置echart的元素是动态渲染时, 需要动态挂载元素-->
      <template v-if="card.type === 1">
        <div :ref="(el) => setEchartRef(el, index)" class="chart"></div>
      </template>
      <div v-else>empty-box</div>
    </div>
  </div>
</template>

<script setup lang="ts">
  import * as echarts from 'echarts/core';
  import { PieChart } from 'echarts/charts';
  import { CanvasRenderer } from 'echarts/renderers';
  import { GridComponent, TooltipComponent } from 'echarts/components';
   echarts.use([GridComponent, PieChart, CanvasRenderer, TooltipComponent]);
  const cardList = ref([]);
  const echartsRef = ref<HTMLElement[]>([]);

  function setEchartRef = (el: HTMLElement, index: number) => {
    echartsRef.value[index] = el;
  }
  function drawEchart(index) {
    cardList.value[index].echart = echarts.init(echartsRef?.value?.[index] as unknown as HTMLElement);
    cardList.value[index].echart.setOption({
      // ...
    })
  }
  function setEchartData() {
    cardList.value[index].type = 1;
    await nextTick();
    drawEchart(index);
  }
</script>

关于构建部署踩的坑

1. 混用 requireimport

如果项目中存在混用 CommonJS 和 ES6 模块的情况,需要使用 @originjs/vite-plugin-commonjs 这个插件的 transformMixedEsModules 配置进行 hotfix。不然会报错 Uncaught ReferenceError: require is not defined不过,尽量不要混用。

import { defineConfig } from 'vite'
import { viteCommonjs } from '@originjs/vite-plugin-commonjs';

export default defineConfig({
  // ...
  plugins: [
    viteCommonjs({
      transformMixedEsModules: true,
    }),
  ]
})

个人理解,这个配置类似于 babelsourceType 配置项。其实简单概括就是出现了 import 和 module.exports 的混用

所以,原来项目中用 h 函数渲染图片的写法也要改为 ES 引入,如下:

import exampleImg from './assets/example.png'
import { h } from 'vue';
function renderModal() {
   Modal.confirm({
    title: '操作确认',
    icon: null,
    content: () =>
      h('div', { style: 'text-align: center;padding-bottom: 32px;' }, [
        // 原来vue2的写法 h('img', {attrs: {src: require('./assets/example.png')}})
        h('img', { src: exampleImg })]),
  });
}

2. 关于浏览器兼容问题

vitebuild.target 配置项可以配置希望兼容的浏览器版本或者 ES 版本, cssTarget 可以对 CSS 的压缩设置一个 target ,该配置应针对非主流浏览器使用。例如,安卓微信中的 webview 并不支持 CSS 中的十六进制颜色符号,此时将 build.cssTarget 设置为 chrome61 ,可以防止 vitergba() 颜色转化为 #RGBA 十六进制符号的形式。

低版本浏览器报错globalThis未定义

除此之外,还可以使用插件 @vitejs/plugin-legacy 进行更多的浏览器兼容问题处理。例如,在内核 chrome 69 版本的 360 浏览器中,遇到过 Uncaught ReferenceError: globalThis is not defined 这样的报错。后来翻了下文档,实际可以通过 @vitejs/plugin-legacymodernPolyfills 配置去解决这个问题。同理,你也可以 Polyfills 你需要的 ES 特性。

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import legacy from '@vitejs/plugin-legacy'

export default defineConfig({
  server: {
    port: 8080
  },
  build: {
    target: 'es2015', // js兼容处理
    cssTarget: 'chrome49', // css兼容处理
  }
  plugins: [
    vue(),
    legacy({
      targets: ['chrome 49'],
      modernPolyfills: ['es.global-this'], // 解决浏览器端 globalThis is not defined 报错
    }),
  ]
})

踩了这么多坑,你可能会问,后悔在新项目里面用 Vue3 了吗?我的答案是没有。对于一个不太重的新项目,你又想尝试一下 Vue3 的各种新特性,我个人觉得或许是个不错的选择。

希望这篇来自实战的避坑经验,能帮你少走些弯路。如果你想了解更多关于 Vue.js 及现代 前端工程化 的实践技巧,可以持续关注像云栈社区这样的开发者技术论坛,那里有更多来自一线开发者的经验分享。




上一篇:FEVM FN60G迷你主机深度解析:LGA1700平台、可换独显与黑苹果实战
下一篇:DeepSeek MODEL1代码泄露引猜测:是R2模型还是V4架构?
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-1-24 17:47 , Processed in 0.359410 second(s), 42 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表