首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >大数据开发自学vue3踩坑实录:努力成为vue高高手

大数据开发自学vue3踩坑实录:努力成为vue高高手

原创
作者头像
叫我阿柒啊
发布2024-02-22 17:04:36
3100
发布2024-02-22 17:04:36
举报

前言

2023再学vue,几年时间已从vue2变成了vue3。这次通过解构实现开源项目BuildAdmin,完成了vue系列的系统化学习,并且在学习过程中输出了近20篇关于前端的文章。

其中包括Buildadmin后台管理系统各个模块的技术实现和原理,后来又自己通过vue3实现了一个关于腾讯视频国漫的评分展示系统。本篇文章主要分享几个我对vue的一些使用心得和踩过的坑。

vue技术架构

首先,vue和初学前端使用的jQuery一样,都只是个js框架。想要实现一个前端页面,仅仅依靠js是不够的,需要html、js、css前端三剑客共同协作。

html的框架我用的是ElmentUI(新版本叫Elemnt Plus),css有sass、less两种语言选择。当然,基于vue框架上开发使用的js也不是原生js,用的是基于JavaScript的强类型编程语言typescript

大部分浏览器只认识JavaScript,所以要将ts转换成js就要用到babel(一个插件,安装即用)。我们知道vue项目的入口通常是main.ts,从main.ts开始,需要逐级解析各个模块import和require的依赖,并将项目中的所有模块打包成浏览器需要静态资源,所以就需要一个打包工具,webpackvite二选一,据说vite比webpack快,我这里选择了熟悉的webpack。

最后就是vue的官方路由库vue-router,以及状态管理库vuex或者pinia。我前端开发的技术架构就是:vue3 + vue-router + typescript + Elment Plus + sass + pinia。

vue2和vue3

在vue的学习中,从vue2开始学,墨迹到vue3才学完。我们就看看相对于vue2,vue3带来了哪些新特性。

组合式开发

在初学vue时,常常在vue2的选项式开发中迷茫。

var vm = new Vue({
  el: '#app',
  data: {
    fullName: 'Foo Bar'
  },
  watch: {
    fullName: function () {
      console.log('hello world')
    }
  }
})

这里就仅仅是定义了data、watch两个选项属性,如果加上methods、computed以及生命周期函数,代码阅读性就会差那么一丢丢。好在vue3迎来了组合式开发,终于告别了vue2的选项式开发。

<script setup lang='ts'>
  import {ref, watch, computed} from 'vue'
  const fullName = ref('Foo Bar')
  watch(() => fullName, () => console.log('hello world'))
</script>

vue3三行代码就实现上面的代码功能。

setup语法糖

上面vue3的代码离不开setup。<script setup> 是在单文件组件 (SFC) 中使用组合式 API 的编译时语法糖,只要更少的样板内容,更简洁的代码,并能够使用纯TypeScript声明 props 和自定义事件等,里面的代码会被编译成组件setup() 函数的内容。

这意味着与普通的 <script> 只在组件被首次引入的时候执行一次不同,<script setup> 中的代码会在每次组件实例被创建的时候执行。

同时,vue3组件无需export defalut导出之后才能引用,引用组件也无需在components中声明。除此之外还提供了defineProps、defineEmits、defineExpose、withDefaults等简洁方便的API。

使用心得

这里简单分享一下几个在项目开发中的使用心得。

1. babel可选链

经常发现在很多地方 ?. 的写法。

这就是可选链的写法,这里需要安装babel的可选链插件在typecript支持可选链。

npm install --save-dev @babel/plugin-proposal-optional-chaining

然后在babel.config.js中配置。

plugins: [
  '@babel/plugin-proposal-optional-chaining'
]

可选链有什么用呢,当一个对象的属性不存在时,会抛出TypeError导致页面操作,如果使用了可选链,这条表达式会被中断,不执行任何操作,并返回undefined。写代码测试一下:

let obj = {}
console.log(obj?.a?.c)
console.log(obj.a.c)

定义了一个空数组obj,然后访问a属性的c属性,当然a、c是不存在的。我们一个使用了可选链,一个没有使用,运行测试:

从控制台可以看到,使用了可选链的表达式返回了undefined。没有使用可选链的抛出了TypeError。我们也可以在可选链中使用 ?? 来给空值添加默认值。

console.log(obj?.a?.c ?? 'Hello World')

如果a、c不存在,则使用默认值Hello Wolrd。测试输出:

2. defineComponent()

在vue3新增了defineComponent() ,我在定义Icon组件是使用了这个方法。

图中的代码是选项式定义vue组件的方式。其中defineComponent的作用就是完全为了服务typescript而存在的选项式组件只有在defineComponent中定义,才能开启typescript的类型推导(划重点)。

看看什么是类型推导

我们在typescript的环境中,使用vue2中的new Vue的方法来创建HomeView组件,然后在其他组件中引用此组件。

typescript编译就开始报错。

这就是没有使用defineComponent开启typescript的类型推导,导致在引用组件时无法将组件匹配为typescript需要的组件类型。

使用defineComponent开启typescript的类型推导之后,就搞定了。不过,话说上面还说不喜欢选项式所以使用组合式,这怎么又变卦了?在一些场景下,还真得需要使用选项式,例如上面定义Icon组件的例子,接着往下看。

3. h()渲染

h()就是vue中的createVnode(),用于创建虚拟DOM节点(vnode),简单理解就是创建html元素。在vue3中,h()通常与setup()一起使用。

什么时候需要setup()呢?当需要在基于选项式API的组件中集成基于组合式API的代码时。又回到上面那个问题了?什么时候需要使用选项式呢。我的个人理解就是:当<template>模板无法满足我们的组件的定义时,就要使用h()来创建元素。

在<script setup>中是无法使用使用h(),所以使用setup()。上面也讲了,setup是setup()的语法糖,最后setup中的代码也会被编译到setup()中。

这里拿Element Plus的图标组件和font-awesome图标为例,如果我们想要使用图标,要做以下的定义:

<!-- 定义icons-vue图标 -->
<el-icon :size="size" :color="color">
   <Edit />
</el-icon>

<!--  定义font-awesome图标 -->
<i class="fa fa-camera-retro fa-lg"></i>

我们将使用setup()和h()封装了一个Icon组件,详细实现参考文章BuildAdmin03:为什么要定义图标组件

setup(props) {
    const iconStyle = computed(() => {
        const {size, color} = props
        let s = `${size.replace('px', '')}px`
        return {
            fontSize: s,
            color: color,
        }
    })
    if (props.name.startsWith('el-icon-')) {
        return () => h('el-icon', {
            class: 'icon el-icon',
            style: iconStyle.value
        }, [h(resolveComponent(props.name))])
    } else if (props.name.startsWith('fa')) {
        return () => h('i', {
            class: 'icon ' + props.name,
            style: iconStyle.value
        })
    }
}

这里代码是接着上面defineComponent第一张图片的代码。这样,只需要Icon标签就可以使用上面两种图标了。

在setup中,使用了h()和resolveComponent() 两个组合式的API。如果我们使用template来定义html模板,那么Element Plus的图标就很难定义。图标是根据后台传过来的JSON串进行渲染的。

图中表示HomeView组件要使用Element Plus的UserFilled组件,el-icon前缀是我们用于区分图标种类的。我们上面的样例中定义了Edit图标。

<template>
  <el-icon :size="size" :color="color">
     <Edit />
  </el-icon>
</template>

那么将UserFilled将template的Edit图标替换掉,是有一定难度的,那么使用h()就可以轻松实现。

h('el-icon', {
        class: 'icon el-icon',
        style: iconStyle.value
    }, 
    [h(resolveComponent(props.name))])

第一个参数是要创建的html元素,第二个是属性集合,第三个就是children子元素。这里子元素使用resolveComponent解析程序中已经加载的UserFilled图标组件就可获得。

4. nextTick()

nextTick可以说是使用频率比较高的了。当在Vue中更改响应式状态时,最终的DOM更新并不是同步生效的,这时候你可能获取不到最新的dom。当DOM更新生效之后,就会触发nextTick中的回调函数,这样就能获取到最新的dom了。

在倒数第三行代码中,我获取了一个div元素,然后调用selectNavTab来实现滑动块,可以参考BuildAdmin08:导航栏tab的滑动块如何实现。如果不在nextTick执行,就经常提示div undfined的错误。

踩过的坑

1. import加载组件

在之前写过的一篇动态路由加载中,使用import() 根据后台传过来的component路径来加载vue组件。当时我的写法是:

为什么 import() 的参数那么奇怪呢?因为,webapck中用于引入component的import的参数,是不支持完全使用变量的,也就是必须有字符串。可以来测试一下:

// 方式一:固定字符串,正常运行
() => import("@/views/AboutView.vue")

// 方式二:全变量,报错
const component = "@/views/AboutView.vue"
()=> import(component)

// 方式三:字符串 + 变量,正常运行。
const component = "views/AboutView"
()=> import(`@/${component}.vue`)

起始路径一定要是字符串,且能表示文件的位置信息,即@/,文件后缀也要是字符串,这样Typescript才能解析。

方式二报错信息如下:

2. requiure加载图片

与import同样的问题,我使用Element Plus的轮播图el-carousel组件时,需要根据后台传过来的cid来匹配图片。

使用v-bind绑定img的src属性,这里必须使用require()或者import(),而且参数是字符串+变量的形式。

如果没有使用require将图片加载进去,src属性直接绑定变量,这样就会404找不到图片,以为webpack在打包时对图片做了一些处理。

结语

这些就是个人作为vue爱好者,简单整理自学和使用vue过程中的部分心得。其实还有很多vue的开发心得可以分享,只是感觉很多地方还没有理解到位,还需要持续地深入学习。

我正在参加腾讯云者社区第4期热点技术征文,快来和我一起创作吧!

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • vue技术架构
  • vue2和vue3
    • 组合式开发
      • setup语法糖
      • 使用心得
        • 1. babel可选链
          • 2. defineComponent()
            • 3. h()渲染
              • 4. nextTick()
              • 踩过的坑
                • 1. import加载组件
                  • 2. requiure加载图片
                  • 结语
                  领券
                  问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档