0

0

9个vue3开发技巧,提升效率帮助你早点下班!

青灯夜游

青灯夜游

发布时间:2022-09-14 10:59:15

|

3639人浏览过

|

来源于掘金社区

转载

vue3也发布很长时候了,官方也将默认版本切换为vue3,而且也出现了完善的中文文档,不知同志们是否已经使用了了呢?本渣体验了一段时间,还是相当的丝滑,些许开发经验奉上,望大家能早点下班

善用h(createVNode)和render 函数

我们知道在vue3中导出了一个神奇的createvnode 函数 当前函数它能创建一个vdom,大家不要小看vdom, 我们好好利用它,就能做出意想不到的效果比如我们要实现一个弹窗组件

我们通常的思路是写一个组件在项目中引用进来,通过v-model来控制他的显示隐藏,但是这样有个问题,我们复用的时候的成本需要复制粘贴。我们没有办法来提高效率,比如封装成npm 通过调用js来使用。【相关推荐:vuejs视频教程

然而,有了 createVNode 和render 之后所有问题就迎刃而解了

// 我们先写一个弹窗组件
        const message = {
            setup() {
                const num = ref(1)
                return {
                    num
                }
            },
            template: `
{{num}}
这是一个弹窗
` }
  // 初始化组件生成vdom
  const vm = createVNode(message)
  // 创建容器,也可以用已经存在的
  const container = document.createElement('div')
  //render通过patch 变成dom
  render(vm, container)
// 弹窗挂到任何你想去的地方  
document.body.appendChild(container.firstElementChild)

经过上面这一通骚操作,我们发现我们可以将他封装为一个方法,放到任何想放的地方。

善用JSX/TSX

文档上说了,在绝大多数情况下,Vue 推荐使用模板语法来搭建 HTML。然而在某些使用场景下,我们真的需要用到 JavaScript 完全的编程能力。这时渲染函数就派上用场了。

立即学习前端免费学习笔记(深入)”;

jsx和模板语法的优势对比

jsx和模板语法都是vue 支持的的书写范畴,然后他们确有不同的使用场景,和方式,需要我们根据当前组件的实际情况,来酌情使用

什么是JSX

JSX 是一种 Javascript 的语法扩展,JSX = Javascript + XML,即在 Javascript 里面写 XML,因为 JSX 的这个特性,所以他即具备了 Javascript 的灵活性,同时又兼具 html 的语义化和直观性

模板语法的优势

  • 1、模板语法书写起来不怎么违和,我们就像在写html一样
  • 2、在vue3中由于模板的可遍历性,它能在编译阶段做更多优化,比如静态标记、块block、缓存事件处理程序等
  • 3、模板代码逻辑代码严格分开,可读性高
  • 4、对JS功底不那么好的人,记几个命令就能快速开发,上手简单
  • 5、vue官方插件的完美支持,代码格式化,语法高亮等

JSX的优势

  • 1、灵活、灵活、灵活(重要的事情说三遍)
  • 2、一个文件能写好多个组件
  • 3、只要JS功底好,就不用记忆那么多命令,上来就是一通输出
  • 4、JS和JSX混用,方法即声明即用,对于懂行的人来说逻辑清晰

对比

由于vue对于JSX的支持,社区里,也是争论来争论去,到底要分个高低,然后本渣认为,他俩本来没有高低,您觉得哪个适合,就用哪个即可,缺点放在对的地方他就是优势 要发扬咱们老前辈们传下来的中庸之道,做集大成者,将两者结合使用,就能发挥无敌功效,乱军之中博老板青睐。

接下来说一下本人的一点粗浅理解,我们知道组件类型,分为容器型组件和展示展示型组件 在一般情况下,容器型组件,他由于可能要对于当前展示型组件做一个标准化或者宰包装,那么此时容器型组件中用JSX就再好不过

举个例子:现在有个需求,我们有两个按钮,现在要做一个通过后台数据来选择展示哪一个按钮,我们通常的做法,是通过在一个模板中通过v-if去控制不同的组件

然而有了JSX与函数式组件之后,我们发现逻辑更清晰了,代码更简洁了,质量更高了,也更装X了

我们来看

先整两个组件

//btn1.vue


//btn2.vue

用JSX配合函数式组件来做一个容器组件

// 容器组件
import btn1 from './btn1.vue'
import btn2 from './btn2.vue'
export const renderFn = function (props, context) {
  return props.type == 1 ? {context.slots.default()} : {context.slots.default()}
}

引入业务组件

//业务组件

善用依赖注入(Provide / Inject)

在善用依赖注入之前是,我们先来了解一些概念,帮助我们更全面的了解依赖注入的前世今生

IOC 和DI 是什么

控制反转(Inversion of Control,缩写为IoC),是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。其中最常见的方式叫做依赖注入(Dependency Injection,简称DI),还有一种方式叫“依赖查找”(Dependency Lookup)。通过控制反转,对象在被创建的时候,由一个调控系统内所有对象的外界实体,将其所依赖的对象的引用传递(注入)给它。

什么是依赖注入

依赖注入 用大白话来说:就是将实例变量传入到一个对象中去

vue中的依赖注入

在vue中,我们套用依赖注入的概念,其实就是在父组件中声明依赖,将他们注入到子孙组件实例中去,可以说是能够很大程度上代替全局状态管理的存在

1.png

我们先来看看他的基本用法

父组件中声明provide

//parent.vue


子组件中注入进来

//child.vue
//使用inject 注入


正因为依赖注入的特性,我们很大程度上代替了全局状态管理,相信谁都不想动不动就引入那繁琐的vuex

接下来我们来举个例子,现在我么有个页面主题色,他贯穿所有组件,并且可以在某一些组件内更改主题色,那我们常规的解决方案中,就是装个vuex然后通过他的api下发颜色值,这时候如果想改,首先要发起dispatch到Action ,然后在Action中触发Mutation接着在Mutation中再去改state,如此一来,你是不是发现有点杀鸡用牛刀了,我就改个颜色而已!

我们来看有了依赖注入 应该怎么处理

首先我们知道vue是单项数据流,也就是子组件不能修改父组件的内容,于是我们就应该想到使用$attrs使用它将方法透传给祖先组件,在组件组件中修改即可。

我们来看代码

//子孙组件child1.vue


将当前子孙组件嵌入到child.vue中去,就能利用简洁的方式来修改颜色了

善用Composition API抽离通用逻辑

众所周知,vue3最大的新特性,当属Composition API 也叫组合api ,用好了他,就是你在行业的竞争力,你也有了不世出的技能

我们一步步来分析

什么是Composition API

使用 (datacomputedmethodswatch) 组件选项来组织逻辑通常都很有效。然而,当我们的组件开始变得更大时,逻辑关注点的列表也会增长。尤其对于那些一开始没有编写这些组件的人来说,这会导致组件难以阅读和理解。

于是在vue3中为了解决当前痛点,避免在大型项目中出现代码逻辑分散,散落在当前组件的各个角落,从而变得难以维护,Composition API横空出世

所谓Composition API 就是在组件配置对象中声明 setup函数,我们可以将所有的逻辑封装在setup函数中,然后在配合vue3中提供的响应式API 钩子函数、计算属性API等,我们就能达到和常规的选项式同样的效果,但是却拥有更清晰的代码以及逻辑层面的复用

Amazon Nova
Amazon Nova

亚马逊云科技(AWS)推出的一系列生成式AI基础模型

下载

基础使用


通过以上代码我们可以看出,一个setup函数我们干出了在传统选项式中的所有事情,然而这还不是最绝的,通过这些api的组合可以实现逻辑复用,这样我们就能封装很多通用逻辑,实现复用,早点下班

举个例子:大家都用过复制剪贴板的功能,在通常情况下,利用navigator.clipboard.writeText 方法就能将复制内容写入剪切板。然而,细心的你会发现,其实赋值剪切板他是一个通用功能,比如:你做b端业务的,管理系统中到处充满了复制id、复制文案等功能。

于是Composition API的逻辑复用能力就派上了用场

import { watch, getCurrentScope, onScopeDispose, unref, ref } from "vue"
export const isString = (val) => typeof val === 'string'
export const noop = () => { }
export function unrefElement(elRef) {
    const plain = unref(elRef)// 拿到本来的值
    return (plain).$el ?? plain //前面的值为null、undefined,则取后面的值,否则都取前面的值
}
export function tryOnScopeDispose(fn) {
    // 如果有活跃的effect
    if (getCurrentScope()) {
        //在当前活跃的 effect 作用域上注册一个处理回调。该回调会在相关的 effect 作用域结束之后被调用
        //能代替onUmounted
        onScopeDispose(fn)
        return true
    }
    return false
}
//带有控件的setTimeout包装器。
export function useTimeoutFn(
    cb,// 回调
    interval,// 时间
    options = {},
) {
    const {
        immediate = true,
    } = options

    const isPending = ref(false)

    let timer

    function clear() {
        if (timer) {
            clearTimeout(timer)
            timer = null
        }
    }

    function stop() {
        isPending.value = false
        clear()
    }

    function start(...args) {
        // 清除上一次定时器
        clear()
        // 是否在pending 状态
        isPending.value = true
        // 重新启动定时器
        timer = setTimeout(() => {
            // 当定时器执行的时候结束pending状态
            isPending.value = false
            // 初始化定时器的id
            timer = null
            // 执行回调
            cb(...args)
        }, unref(interval))
    }
    if (immediate) {
        isPending.value = true

        start()
    }

    tryOnScopeDispose(stop)

    return {
        isPending,
        start,
        stop,
    }
}
//轻松使用EventListener。安装时使用addEventListener注册,卸载时自动移除EventListener。
export function useEventListener(...args) {
    let target
    let event
    let listener
    let options
    // 如果第一个参数是否是字符串
    if (isString(args[0])) {
        //结构内容
        [event, listener, options] = args
        target = window
    }
    else {
        [target, event, listener, options] = args
    }
    let cleanup = noop
    const stopWatch = watch(
        () => unrefElement(target),// 监听dom
        (el) => {
            cleanup() // 执行默认函数
            if (!el)
                return
            // 绑定事件el如果没有传入就绑定为window
            el.addEventListener(event, listener, options)
            // 重写函数方便改变的时候卸载
            cleanup = () => {
                el.removeEventListener(event, listener, options)
                cleanup = noop
            }
        },
        //flush: 'post' 模板引用侦听
        { immediate: true, flush: 'post' },
    )
    // 卸载
    const stop = () => {
        stopWatch()
        cleanup()
    }

    tryOnScopeDispose(stop)

    return stop
}

export function useClipboard(options = {}) {
    //获取配置
    const {
        navigator = window.navigator,
        read = false,
        source,
        copiedDuring = 1500,
    } = options
    //事件类型
    const events = ['copy', 'cut']
    // 判断当前浏览器知否支持clipboard
    const isSupported = Boolean(navigator && 'clipboard' in navigator)
    // 导出的text
    const text = ref('')
    //导出的copied
    const copied = ref(false)
    // 使用的的定时器钩子
    const timeout = useTimeoutFn(() => copied.value = false, copiedDuring)

    function updateText() {
        //解析系统剪贴板的文本内容返回一个Promise
        navigator.clipboard.readText().then((value) => {
            text.value = value
        })
    }

    if (isSupported && read) {
        // 绑定事件
        for (const event of events)
            useEventListener(event, updateText)
    }
    // 复制剪切板方法
    //navigator.clipboard.writeText 方法是异步的返回一个promise
    async function copy(value = unref(source)) {
        if (isSupported && value != null) {
            await navigator.clipboard.writeText(value)
            // 响应式的值,方便外部能动态获取
            text.value = value
            copied.value = true
            timeout.start()// copied.value = false 
        }
    }

    return {
        isSupported,
        text,
        copied,
        copy,
    }
}

这时我们就复用了复制的逻辑,如下代码中直接引入在模板中使用即可


以上代码参考vue版本的Composition API库所有完整版请参考

善于使用getCurrentInstance 获取组件实例

getCurrentInstance 支持访问内部组件实例, 通常情况下他被放在 setup中获取组件实例,但是getCurrentInstance 只暴露给高阶使用场景,典型的比如在库中。

强烈反对在应用的代码中使用 getCurrentInstance。请不要把它当作在组合式 API 中获取 this 的替代方案来使用。

那他的作用是什么呢?

还是逻辑提取,用来代替Mixin,这是在复杂组件中,为了整个代码的可维护性,抽取通用逻辑这是必须要去做的事情,我们可以看element-plus 中table的复用逻辑,在逻辑提取中由于涉及获取props、proxy、emit 以及能通过当前组件获取父子组件的关系等,此时getCurrentInstance 的作用无可代替

如下element-plus代码中利用getCurrentInstance 获取父组件parent中的数据,分别保存到不同的变量中,我们只需要调用当前useMapState即可拿到数据

// 保存数据的逻辑封装
function useMapState() {
  const instance = getCurrentInstance()
  const table = instance.parent as Table
  const store = table.store
  const leftFixedLeafCount = computed(() => {
    return store.states.fixedLeafColumnsLength.value
  })
  const rightFixedLeafCount = computed(() => {
    return store.states.rightFixedColumns.value.length
  })
  const columnsCount = computed(() => {
    return store.states.columns.value.length
  })
  const leftFixedCount = computed(() => {
    return store.states.fixedColumns.value.length
  })
  const rightFixedCount = computed(() => {
    return store.states.rightFixedColumns.value.length
  })

  return {
    leftFixedLeafCount,
    rightFixedLeafCount,
    columnsCount,
    leftFixedCount,
    rightFixedCount,
    columns: store.states.columns,
  }
}

善用$attrs

$attrs 现在包含了所有传递给组件的 attribute,包括 class 和 style

$attrs在我们开发中到底有什么用呢?

通过他,我们可以做组件的事件以及props透传

首先有一个标准化的组件,一般是组件库的组件等等

//child.vue


接下来有一个包装组件,他对当前的标准化组件做修饰,从而使结果变成我们符合我们的预期的组件

//parent.vue
 

我们发现当前包装组件中使用了$attrs,通过他透传给标准化组件,这样一来,我们就能对比如element UI中的组件做增强以及包装处理,并且不用改动原组件的逻辑。

优雅注册全局组件技巧

vue3的组件通常情况下使用vue提供的component 方法来完成全局组件的注册

代码如下:

const app = Vue.createApp({})

app.component('component-a', {
  /* ... */
})
app.component('component-b', {
  /* ... */
})
app.component('component-c', {
  /* ... */
})

app.mount('#app')

使用时

然而经过大佬的奇技淫巧的开发,我们发现可能使用注册vue插件的方式,也能完成组件注册,并且是优雅的!

vue插件注册

插件的格式

//plugins/index.js
export default {
  install: (app, options) => {
      // 这是插件的内容
  }
}

插件的使用

import { createApp } from 'vue'
import Plugin from './plugins/index.js'
const app = createApp(Root)
app.use(Plugin)
app.mount('#app')

其实插件的本质,就是在use的方法中调用插件中的install方法,那么这样一来,我们就能在install方法中注册组件。

index.js中抛出一个组件插件

// index.js
import component from './Cmponent.vue'
const component = {
    install:function(Vue){
        Vue.component('component-name',component)
    }  //'component-name'这就是后面可以使用的组件的名字,install是默认的一个方法 component-name 是自定义的,我们可以按照具体的需求自己定义名字
}
// 导出该组件
export default component

组件注册

// 引入组件
import install from './index.js'; 
// 全局挂载utils
Vue.use(install);

上述案例中,就是一个简单的优雅的组件注册方式,大家可以发现包括element-plus、vant 等组件都是用如此方式注册组件。

善用setup>

 是在单文件组件 (SFC) 中使 的编译时语法糖。相比于普通的  语法,它具有更多优势:

  • 更少的样板内容,更简洁的代码。
  • 能够使用纯 Typescript 声明 props 和抛出事件。
  • 更好的运行时性能 (其模板会被编译成与其同一作用域的渲染函数,没有任何的中间代理)。
  • 更好的 IDE 类型推断性能 (减少语言服务器从代码中抽离类型的工作)。

它能代替大多数的setup函数所表达的内容,具体使用方法,大家请看请移步文档

但是由于setup函数它能返回渲染函数的特性,在当前语法糖中却无法展示,于是遍寻资料,找到了一个折中的办法



如此一来,我们就能在语法糖中返回渲染函数了

v-model的最新用法

我们知道在vue2中想要模拟v-model,必须要子组件要接受一个value props 吐出来一个 叫input的emit

然而在vue3中他升级了

父组件中使用v-model

 

子组件中使用 title的props 以及规定吐出update:title的emit



有了以上语法糖,我们在封装组件的时候,就可以随心所欲了,比如我自己封装可以控制显示隐藏的组件我们就能使用v-model:visible单独控制组件的显示隐藏。使用正常的v-model 控制组件内部的其他逻辑,从而拥有使用更简洁的逻辑,表达相同的功能

最后

目前开发中总结的经验就分享到这里了,错误之处,请大佬指出!

然后对vue源码有兴趣的大佬,可以看下这个文章 写给小白(自己)的vue3源码导读

也可以直接看本渣的源码解析github   vue-next-analysis

其中包含了vue源码执行思维导图,源码中的代码注释,整个源码的结构,各个功能的单独拆解等。错误之处请大佬指出!

(学习视频分享:web前端开发编程基础视频

相关专题

更多
js获取数组长度的方法
js获取数组长度的方法

在js中,可以利用array对象的length属性来获取数组长度,该属性可设置或返回数组中元素的数目,只需要使用“array.length”语句即可返回表示数组对象的元素个数的数值,也就是长度值。php中文网还提供JavaScript数组的相关下载、相关课程等内容,供大家免费下载使用。

538

2023.06.20

js刷新当前页面
js刷新当前页面

js刷新当前页面的方法:1、reload方法,该方法强迫浏览器刷新当前页面,语法为“location.reload([bForceGet]) ”;2、replace方法,该方法通过指定URL替换当前缓存在历史里(客户端)的项目,因此当使用replace方法之后,不能通过“前进”和“后退”来访问已经被替换的URL,语法为“location.replace(URL) ”。php中文网为大家带来了js刷新当前页面的相关知识、以及相关文章等内容

372

2023.07.04

js四舍五入
js四舍五入

js四舍五入的方法:1、tofixed方法,可把 Number 四舍五入为指定小数位数的数字;2、round() 方法,可把一个数字舍入为最接近的整数。php中文网为大家带来了js四舍五入的相关知识、以及相关文章等内容

727

2023.07.04

js删除节点的方法
js删除节点的方法

js删除节点的方法有:1、removeChild()方法,用于从父节点中移除指定的子节点,它需要两个参数,第一个参数是要删除的子节点,第二个参数是父节点;2、parentNode.removeChild()方法,可以直接通过父节点调用来删除子节点;3、remove()方法,可以直接删除节点,而无需指定父节点;4、innerHTML属性,用于删除节点的内容。

470

2023.09.01

JavaScript转义字符
JavaScript转义字符

JavaScript中的转义字符是反斜杠和引号,可以在字符串中表示特殊字符或改变字符的含义。本专题为大家提供转义字符相关的文章、下载、课程内容,供大家免费下载体验。

389

2023.09.04

js生成随机数的方法
js生成随机数的方法

js生成随机数的方法有:1、使用random函数生成0-1之间的随机数;2、使用random函数和特定范围来生成随机整数;3、使用random函数和round函数生成0-99之间的随机整数;4、使用random函数和其他函数生成更复杂的随机数;5、使用random函数和其他函数生成范围内的随机小数;6、使用random函数和其他函数生成范围内的随机整数或小数。

989

2023.09.04

如何启用JavaScript
如何启用JavaScript

JavaScript启用方法有内联脚本、内部脚本、外部脚本和异步加载。详细介绍:1、内联脚本是将JavaScript代码直接嵌入到HTML标签中;2、内部脚本是将JavaScript代码放置在HTML文件的`<script>`标签中;3、外部脚本是将JavaScript代码放置在一个独立的文件;4、外部脚本是将JavaScript代码放置在一个独立的文件。

652

2023.09.12

Js中Symbol类详解
Js中Symbol类详解

javascript中的Symbol数据类型是一种基本数据类型,用于表示独一无二的值。Symbol的特点:1、独一无二,每个Symbol值都是唯一的,不会与其他任何值相等;2、不可变性,Symbol值一旦创建,就不能修改或者重新赋值;3、隐藏性,Symbol值不会被隐式转换为其他类型;4、无法枚举,Symbol值作为对象的属性名时,默认是不可枚举的。

538

2023.09.20

ip地址修改教程大全
ip地址修改教程大全

本专题整合了ip地址修改教程大全,阅读下面的文章自行寻找合适的解决教程。

27

2025.12.26

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Vue3.x 工具篇--十天技能课堂
Vue3.x 工具篇--十天技能课堂

共26课时 | 1.3万人学习

Vue3.x 核心篇--十天技能课堂
Vue3.x 核心篇--十天技能课堂

共30课时 | 1.4万人学习

Vue3.x新特性篇--十天基础课堂
Vue3.x新特性篇--十天基础课堂

共20课时 | 1.1万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号