vue3中可以帮助你早点下班的9个开发技巧!
序
vue3也发布很长时候了,官方也将默认版本切换为vue3,而且也出现了完善的中文文档[1],不知同志们是否已经使用了了呢?
本渣体验了一段时间,还是相当的丝滑,些许开发经验奉上,望大家能早点下班
也可以看下 源码解析github vue-next-analysis[2](未完待续持续更新中....)
善用h(createVNode)和render 函数
我们知道在vue3中导出了一个神奇的createVNode 函数 当前函数它能创建一个vdom,大家不要小看vdom, 我们好好利用它,就能做出意想不到的效果比如我们要实现一个弹窗组件
我们通常的思路是写一个组件在项目中引用进来,通过v-model来控制他的显示隐藏,但是这样有个问题,我们复用的时候的成本需要复制粘贴。我们没有办法来提高效率,比如封装成npm 通过调用js来使用。
然而,有了 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
<div>
这是btn1{{ num }}
<slot>slot>
div>
</template>
复制代码
善用依赖注入(Provide / Inject)
在善用依赖注入之前是,我们先来了解一些概念,帮助我们更全面的了解依赖注入的前世今生
IOC 和DI 是什么
控制反转(Inversion of Control,缩写为IoC),是面向对象编程[3]中的一种设计原则,可以用来减低计算机代码之间的耦合度[4]。其中最常见的方式叫做依赖注入(Dependency Injection,简称DI),还有一种方式叫“依赖查找”(Dependency Lookup)。通过控制反转,对象在被创建的时候,由一个调控系统内所有对象的外界实体,将其所依赖的对象的引用传递(注入)给它。
什么是依赖注入
依赖注入 用大白话来说:就是将实例变量传入到一个对象中去
vue中的依赖注入
在vue中,我们套用依赖注入的概念,其实就是在父组件中声明依赖,将他们注入到子孙组件实例中去
,可以说是能够很大程度上代替全局状态管理
的存在
我们先来看看他的基本用法
父组件中声明provide
//parent.vue
<child @setColor="setColor">child>
<button @click="count++">添加button>
</template>
复制代码
子组件中注入进来
//child.vue
//使用inject 注入
<div>这是注入的内容{{ count }}div>
<child1 v-bind="$attrs">child1>
</template>
复制代码
正因为依赖注入的特性,我们很大程度上代替了全局状态管理
,相信谁都不想动不动就引入那繁琐的vuex
吧
接下来我们来举个例子,现在我么有个页面主题色,他贯穿所有组件,并且可以在某一些组件内更改主题色,那我们常规的解决方案中,就是装个vuex然后通过他的api下发颜色值,这时候如果想改,首先要发起dispatch
到Action ,然后在Action
中触发Mutation
接着在Mutation中再去改state
,如此一来,你是不是发现有点杀鸡用牛刀
了,我就改个颜色而已!
我们来看有了依赖注入 应该怎么处理
首先我们知道vue是单项数据流,也就是子组件不能修改父组件的内容,于是我们就应该想到使用$attrs
使用它将方法透传给祖先组件,在组件组件中修改即可。
我们来看代码
//子孙组件child1.vue
<div :style="`color:${color}`" @click="setColor">这是注入的内容的颜色div>
</template>
复制代码
以上代码参考vue版本的Composition API
库所有完整版请参考[5]
善于使用getCurrentInstance 获取组件实例
getCurrentInstance
支持访问内部组件实例, 通常情况下他被放在 setup中获取组件实例,但是 getCurrentInstance
只暴露给高阶使用场景,典型的比如在库中。
强烈反对在应用的代码中使用 getCurrentInstance
。请不要把它当作在组合式 API 中获取 this
的替代方案来使用。
那他的作用是什么呢?
还是逻辑提取,用来代替Mixin,这是在复杂组件中,为了整个代码的可维护性,抽取通用逻辑这是必须要去做的事情,我们可以看element-plus[6] 中table的复用逻辑,在逻辑提取中由于涉及获取props、proxy、emit
以及能通过当前组件获取父子组件的关系等,此时getCurrentInstance
的作用无可代替
如下element-plus
代码中利用getCurrentInstance 获取父组件parent
中的数据,分别保存到不同的变量中,我们只需要调用当前useMapState即可拿到数据
// 保存数据的逻辑封装
function useMapState<T>() {
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
<div>这是一个标准化组件div>
<input type="text" :value="num" @input="setInput" />
</template>
复制代码
我们发现当前包装组件中使用了$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')
复制代码
使用时
"app">
<component-a>component-a>
<component-b>component-b>
<component-c>component-c>
</div>
复制代码
然而经过大佬的奇技淫巧的开发,我们发现可能使用注册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
等组件都是用如此方式注册组件。
善用
复制代码
子组件中使用 title的props
以及规定吐出update:title的emit
<div>{{ title }}div>
<input type="text" @input="setInput" />
</template>