# 善用h(createVNode)和 render 函数
我们知道在vue3中导出了一个神奇的createVNode函数,当前函数它能创建一个vdom,大家不要小看vdom,我们好好利用它,就能做出意想不到的效果--比如实现一个弹窗组件
我们通常的思路是写一个组件在项目中引用进来,通过v-model来控制它的显示隐藏,但是这样有个问题,我们复用的时候的成功需要复制粘贴。我们有没有办法来提高效率,比如封装成npm通过调用js来使用
然而,有了createVNode和render之后所有问题就迎刃而解了
// 我们先写一个弹窗组件
const message = {
setup() {
const num = ref(1);
return {
num
}
},
template: `<div>
<div>{{num}}</div>
<div>这是一个弹窗</div>
</div>`
}
// 初始化组件生成vdom
const vm = createVNode(message);
// 创建容器,也可以用已经存在的
const container = document.createElement('div');
// rnder通过path 变成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的语义化和直观性
模板语法的优势
- 模板语法写起来不怎么违和,我们就像在写html一样
- 在vue3中由于模板的可遍历性,它能在编译阶段做更多优化,比如静态标记、块block、缓存事件处理程序等。
- 模板代码逻辑代码严格分开,可读性高
- 对JS功底不那么好的人,记几个命令就能快速开发,上手简单
- vue官方插件的完美支持,代码格式化,语法高亮等。
JSX的优势
- 灵活、灵活、灵活(重要的事情说三遍)
- 一个文件能写好多个组件
- 只要JS功底好,就不用记忆那么多命令,上来就是一通输出
- JS和JSX混用,方法即声明即用,对于懂行的人来说清晰
对比
由于vue对于JSX的支持,社区里,也是争论来争论去,到底要分个高低,然后本渣认为,他俩本来没有高低,您觉得哪个适合,就用哪个即可,缺点放在对的地方他就是优势 要发扬咱们老前辈们传下来的中庸之道,做集大成者,将两者结合使用,就能发挥无敌功效,乱军之中博老板青睐。
接下来说一下本人的一点粗浅理解,我们知道组件类型,分为容器性组件和展示型组件 在一般情况下,容器性组件,它由于可能要对于当前展示型组件做一个标准化或者在包装,那么此时容器性组件中用JSX就在好不好了。
举个例子:现在有个需求,我们有两个按钮,现在要做一个通过后台数据来选择展示哪一个按钮,我们通常的做法,是通过在一个模板中通过v-if去控制不同的组件
然而有了JSX与函数式组件之后,我们发现逻辑更清晰了,代码更简洁了,质量更高了,也更装X了
我们来看
先整两个组件
// btn1.vue <template> <div> 这是btn1 {{ name }} <slot></slot> </div> </template> <script> import { ref, defineComponent } from 'vue'; export default defineComponent({ name: 'btn1', setup() { const num = ref(1); return { num } } }) </script> //btn2.vue <template> <div> 这是btn2{{ num }} <slot></slot> </div> </template> <script> import { ref, defineComponent } from 'vue' export default defineComponent({ name: 'btn2', setup() { const num = ref(2) return { num } } }) </script>用JSX配合函数式组件来做一个容器组件
// 容器组件 import btn1 from './btn1.vue'; import btn2 from './btn2.vue'; export const renderFn = function(props, context) { return props.type === 1 ? <btn1>{context.slots.default()}</btn1> : <btn2>{context.slots.default()}</btn2> }引入业务组件
<!-- 业务组件 --> <template> <renderFn :type="1">111111</renderFn> </template> <script> import { renderFn } from './components'; console.log(renderFn); export default { components: { renderFn } setup() {}, } </script>
# 善用依赖注入(Provide/Inject)
在善用依赖注入之前是,我们先来了解一些概念,帮助我们更全面的了解依赖注入的前世今生
# IOC 和 DI 是什么?
控制反转(Inversion of Control,缩写为IOC),是面向对象编程 (opens new window)中一种设计原则,可以用来降低计算机代码之间的耦合度。其中最常见的的方式叫做依赖注入(Dependency Injection,简称DI),还有一种方式叫做"依赖查找(dependency Lookup)"。通过控制反转,对象在被创建的时候,由一个调控内所有对象的外界实体,将其所以来的对象的引用传递(注入)给它
# 什么是依赖注入
依赖注入用大白话来说:就是将实例变量注入到一个对象中去
# Vue中的依赖注入
在vue中,我们套用依赖注入的概念,其实就是在父组件中声明依赖,将他们注入到子孙组件实例中去,可以说是能够很大程度上代替全局状态管理的存在
基本用法
父组件中声明provide
<!-- parent.vue -->
<template>
<child @setColor="setColor"></child>
<button @click="count++">添加</button>
</template>
<script>
import { defineComponent, provide, ref } from 'vue';
import Child from './child.vue';
export default defineComponent({
components: {
Child
},
setup() {
const count = ref(0);
const color = ref('#000');
provide('count', count);
provide('color', color);
function setColor(val) {
color.value = val;
}
return {
count,
setColor
}
}
})
</script>
子组件注入进来
<!-- child.vue -->
<!-- 使用inject注入 -->
<template>
<div>这是注入的内容:{{count}}</div>
<child1 v-bind="$attrs"></child1>
</template>
<script>
import { defineComponent, inject } from 'vue';
import child1 from './child1.vue';
export default defineComponent({
components: {
child1
},
setup(props, {attrs}) {
const count = inject('count');
console.log(count);
console.log(attrs);
return {
count
}
}
})
</script>
正因为依赖注入的特性,我们很大程度上代替了全局状态管理,相信谁都不想动不动就引入那繁琐的vuex吧
接下来我们来举个例子,现在我么有个页面主题色,他贯穿所有组件,并且可以在某一些组件内更改主题色,那我们常规的解决方案中,就是装个vuex然后通过他的api下发颜色值,这时候如果想改,首先要发起dispatch到Action ,然后在Action中触发Mutation接着在Mutation中再去改state,如此一来,你是不是发现有点杀鸡用牛刀了,我就改个颜色而已!
我们来看有了依赖注入 应该怎么处理
首先我们知道vue是单向数据流,也就是子组件不能修改父组件的内容,于是我们就应该想到使用$attrs,使用它将方法投创给祖先组件,在组建中修改既可
<!-- 子孙组件child1.vue -->
<template>
<div :style="`color: ${color}`" @click="setColor">这是注入的内容的颜色</div>
</template>
<script>
import { defineComponent, inject } from 'vue';
export default defineComponent({
setup(props, { emit }) {
const color = inject('color');
function setColor() {
console.log(0);
emit('setColor', 'red');
}
return {
color,
setColor
}
}
})
</script>
将当前子孙组件嵌入到child.vue中去,就能利用简洁的方式来修改颜色了