【复盘】我在 Vue3 开发中踩的坑

点击上方 前端Q,关注公众号

回复加群,加入前端Q技术交流群

大家好,我是winty!送一波新书「Vue.js3应用开发与核心源码解析」,送5本!

送书规则见本文最后!送书规则见本文最后!送书规则见本文最后!

前期准备

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

outside_default.png
esm-兼容

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

outside_default.png
chrome2

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

outside_default.png
esm
outside_default.png
noesm

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

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

组件自动引入unplugin-vue-components

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

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

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

组合式 API

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

outside_default.png
组合式api

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

outside_default.png
no-setup
outside_default.png
setup
响应式
数组

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

outside_default.png
数组响应式
响应式代理

你可能也注意到,对整个数组的变更,我用的是 Object.assign 去实现的,因为只有这样,才能保持数据的响应式。这和 Vue 2 也是有区别的,官网也有做说明响应式代理 vs. 原始值[5],原因和 Vue 3 的数据响应式原理有关。至于Vue 3 的数据响应式原理这里不展开说,可以参考我之前写的另一篇文章关于vue3的Proxy[6]

outside_default.png
reactive.png
双向绑定实现

父组件

<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。_不过,尽量不要混用,因为尤大大说了这么干不好....Vite will likely never support such dependencies.[7]_

import { defineConfig } from 'vite'
import { viteCommonjs } from '@originjs/vite-plugin-commonjs';
export default defineConfig({
  // ...
  plugins: [
    viteCommonjs({
      transformMixedEsModules: true,
    }),
  ]
})
复制代码

个人理解,这个配置类似于 babel 的 sourceType[8]配置项。因为之前在babel也踩过类似的坑,这里贴出对应 issues4039[9]。其实简单概括就是出现了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 })]),
  });
}
复制代码
  1. 关于浏览器兼容问题

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

outside_default.png
globalthis

除此之外, 还可以使用插件 @vitejs/plugin-legacy 进行更多的浏览器兼容问题处理。例如,在内核 chrome 69 版本的360浏览器中,遇到过Uncaught ReferenceError: globalThis is not defined这样的报错。网上搜到可以通过解决浏览器端 globalThis is not defined 报错[12]简单快速的 hotfix 可以解决这个问题,但是我始终觉得不够优雅。

后来翻了下文档,实际可以通过 @vitejs/plugin-legacymodernPolyfills配置去解决这个问题,解决配置如下代码。同理,你也可以 Polyfills 你需要的es[13]

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 报错
    }),
  ]
})
复制代码

说完这么多坑,最后附上一张比较有意思的图2021年前端框架开发:满意度-感兴趣程度-使用度-熟知度[14]

outside_default.png
有意思的图

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

关于本文

作者:Luin

https://juejin.cn/post/7137967499202527239

福利时间

最后,我又来给大家送福利了,这么好的书不送几本给大家怎么行呢?

4265838060fd4a1f9b6cfa4969b76f22.jpeg

本书简介

     本书在讲解Vue 3基础内容的基础上也会围绕这些新的变化和特性进行讲解和应用,同时详细介绍了Vue.js相关的生态,包括Vuex、Vue Router、Vue Cli、Vue动画、Vite、Vue Cli工具等。另外本书还涉及Vue服务端渲染(Node.js、Express)的相关内容,服务端渲染对Vue前端项目的改造提升是非常明显的,不仅有利于搜索引擎的SEO,在首屏体验上也会快很多,但是需要前端开发者关注的点也更多了,这可能需要读者有一定的Node.js基础,以便于对这部分内容的理解。

本书的一大特色是对Vue 3.x的核心源码(响应式原理、双向绑定实现、虚拟DOM、原理和实现)进行了分析和讲解,这不仅有利于读者掌握Vue.js的设计思想,也能提升读者对Vue.js框架的熟练度,同时Vue.js源码知识也是近年来前端面试经常被问到的内容,学习和掌握这些内容是非常必要的。

在本书的最后会应用所讲解的Vue.js相关内容来开发一个实战项目,以帮助读者完整地体验从0到1的开发过程,还包括Vite工具的构建配置和模拟请求后端数据等只会在真实项目中才会用的技能。

这次采用「评论点赞、评论随机抽、点在看随机抽幸运儿」的方式参与!感谢亲爱的读者们,你们的支持也是我持续更文最大的动力。

本次开奖时间为 2022.10.29 22:30

为了避免中奖后失联,提前加我微信哈

ebe76ebc7e63acc9e79c16f94bc0cc3d.jpeg

留言点赞(2本)

在本文文末留言,留言点赞数「第一、二名」可分别获得一本

留言随机抽(1本)

在本文文末留言,随机从留言中抽取一位幸运儿,赠送一本~

点在看随机抽(2本)

本文点在看,随机抽取2位幸运儿,分别可获得一本 

PS:买点赞数等作弊无效,一切解释权归前端Q所有

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值