1.指令的本质是什么?
在Vue中,内置指令有,v-text、v-html、v-show、v-if、v-else、v-else-if、v-for、v-on、v-bind、v-model、v-slot、v-pre、v-clock、v-once,这一些都是语法糖,我们的标志位;
<template> <div> h2>v-text</h2> <div v-text="'hello vue'">hello world</div> <h2>v-html</h2> <div v-html="'<span style=&qot;color: red">hello vue</spans'">hello world </div> <h2>v-show</h2> <div v-show-"show">hello vue</div> <button @click="show = !show" >change show</button> <h2>v-if v-esle-if v-else</h2> <div v-if=""number === 1">hello vue {{ number }}</div> <div v-else-if=""number ===2">hello world {{ number }}</div> <div v-else>hello geektime {{ number }}</div> <h2>v-for v-bind</h2> <div v-for="num in [1,2,3]"v-bind: key="num" >hello vue {{ num }}</div> <h2>v-on</h2> <button v-on:click="number = number + 1"">number++/button> <h2>v-model</h2> <input v-model="message"/> <h2>v-pre</h2> <div v-pre>{{ this will not be compiled }}</div> <h2>v-once</h2> <div v-once> {{number }}</div> </div></template>
看第一个最终显示还是 hello vue ,因为 v-text 会把子元素下面所有的内容替换掉。v-html 则是插入标签,替换掉所有子元素(一般是不使用的)。v-show 是加了 display : none。v-if 是通过删掉和添加元素的方式控制元素显隐。一般来说{{}}是有特殊意义的,而加了 v-pre 的话则直接输出
出来,不用转化什么的。 v-once 是对差值表达式里面的变量做一次解析,后面不管变 量再如何修改都不会去改变,方便性能优化,但是很少使用 (更多是出现在论坛等长篇大论很少修改的地方)。 v-cloak 更加无用,对单页面无所吊用。 指令就是语法糖在template 编译成 render 函数的时候会变成 js 代码.
内置指令是非刚性需求,可以通过其它方式解决,但是可能增加开发成本。
<template> <div> <button @click="show = !show"> 销毁 </button> <button v-if="show" v-append-tex"hello ${number) " eclick="number++"">按钮 </button>/div> </template><script> export default {directives:{appendText: { bind() { console.log("bind");}, inserted(el,binding){ el.appendChild(document.createTextNode(binding.value);console.log("inserted",el,binding); ), update({ console.log("update );, componentupdated(el, binding){ el.removeChild(el.childNodes [el.childNodes.length - 11);el.appendChild(document.createTextNode(binding.value)D;console.log("componentUpdated""}; }, unbind(){ console.log("unbind"); )
2.常用的高级特性provide inject
这些常常是使用在底层通用组件,主要是解决组件间通信的问题;
组件通信
如上图一般就是 A 传属性给 B 实现通信,B 通过 this.$emit 传事件给 A 实现通信。而 A 与 E 通信则需要层层传递(借助 C),这样子成本是比较高的而且比较脆弱。 用特性就是 A 提供数据然后 E 通过层层冒泡去 A 那取数据 (injext注入)。
</template> <script> import ChildrenB from "./ChildrenB": import ChildrenC from "./ChildrenC"; import ChildrenD from "./ChildrenD"; export default { components:{ ChildrenB, ChildrenC, ChildrenD }, provide() { return { theme: { color: this.color } }; }, //provide() { // return { //1 //theme: this // }; data() { return color: "blue" }; }, methods: { changeColor(color) { if (color) { this.color = color; } else { this.color = this.color === "blue" ? "red" : "bLue ; } } } </script>
首先是 A 通过 provide 通过了一个 theme 属性,下面有一个蓝色。
E 通过 inject 的方式注入 theme,找到颜色,故为蓝色。
F 是利用 form 起了个别名 theme1 避免当前组件的 theme 与 注入的 theme 起冲突。
I 是函数式组件,所以注入不太一样。
我们发现点击第一个按钮没有用,因为 color 并不是响应式的,我们可以直接取 this ,毕竟 data 、 method 什么的就是挂载在 this 下面的,可以响应式。
我们发现点击第一个按钮没有用,因为 color 并不是响应式的,我们可以直接取 this,毕竟 data、method 什么的就是挂载在 this 下面的,可以响应式。
provide(){ return { theme:this }; }
如此按钮就能正确的改变颜色!
<template> <div class="border1"> <h2>C结点</h2> <ChildrenE /> <ChildrenF /> </div> </template> <script> import ChildrenE from " /ChildrenE" ; import ChildrenF from "./ChildrenF"; export default { components: { ChildrenE , ChildrenF provide() { return { theme: { color: "green" } }; }; </script>
回到 C 节点,如果同样提供了 provide 数据,则颜色只能改变 I ,其余的不再改变,因为 E 往上找的时候就找到了 C 就不会再往上找的,类似事件的冒泡。 问题:虽然 this 可以解决 provide 数据要响应式的问题,但是 this 下面有很多我们不需要的数据,如何按需提供需要的数据?
3.如何获取跨层级组件实例(拒绝递归)
上次将的是跨层级通信,这次是跨层级获取实例。 组件定义完成后就是一个实例,一般只需要关注数据就行, 但是网页业务越来越复杂则需要访问到这个实例了。比如我们使用 ECharts 、需要一个 input 节点处于 false 状态等等时候就很容易需要访问实例,一般是通过 vue 提供的 ref 访问。
ref引用信息
如果是普通的元素如 p 节点,获取到的是真实的 DOM 元素,如果是自定义组件获取到的就是组件的实例了。Ref 只是比较方便获取当前上下文的实例,如果是跨层级就比较复(如.parent 、 .children 什么的,太多级就很不方便)。如下图 递归方式!(每次都要去获取,因为组件无法缓存,且子组件的更新也很难传到父组件);
比较高效的是通过回调: A 如果要获取 E 的实例则只需要 A提供一个钩子函数,E 实例生成或更新的时候主动调用这个钩子函数来通知 A ,再由 A 进行获取就能保证每次都是最新的!
callback ref
<template> <div class="border"> <h1>A结点</h1> <button @click="getEH3Ref">获取E h3 Ref</button> <ChildrenB /> <ChildrenC /> <ChildrenD /> </div> </template> <script> import ChildrenB from "./ChildrenB"; import ChildrenC from "./ChildrenC"; import ChildrenD from "./ChildrenD"; export default { components: { ChildrenB, ChildrenC, ChildrenD }, provide() { return { setChildrenRef: (name, ref) > { this[name] = ref; getChildrenRef: name => { 6 return Fhis [namel; }, 8 getRef: () => { return this ; } }; }, data() { return { color: "blue" };
A 通过 provide 通过主动通知和获取的钩子函数, setxxx 将传递过来的 ref 进行缓存(
name 对应的 ref ), getxxx 就是直接获取 name 对应的 ref 。
<template> <div class="border1"> <h2>D 结点</h2> <ChildrenG /> <ChildrenH v-ant-ref-"c D setChi ldrenRef('childrenH', C)" /> <ChildrenI /> </div> </ template> <script> import ChildrenG from "./ChildrenG" ; import ChildrenH from "./ChildrenH" ; import ChildrenI from "./ChildrenI" ; export default { components: { ChildrenG, ChildrenH, ChildrenI inject: { setChildrenRef: { default: () => {} } };
子节点则通过 inject 的方式去调用, v-ant-ref 自定义指令(具体内容如下)每次组件更新或实例化都会通知父组件。
4.template 和 JSX 的对比以及它们的本质
JSX 是伴随 react 产生但是实际上两个是相对独立的,我们通过插件的方式也可以在 vue 中使用。
JSX 不只是模板语法,因为可以写很多逻辑非常灵活!
二者相比较
也就是说首先推荐使用 template,如果系统越来越复杂,需要逻辑则可以使用 JSX 或者 render 函数。 目前如果是需要根据传来的 number 去控制显示 h 几则 template 是需要一个个去判断的。
<template> <h1 v-if="level == 1"> <slot></slot> </h1> <h2 v-else-if="level == 2"> <slot></s Lot> </h2> <h3 v-else- if="level = 3"> <sLot></slot> </h3> <h4 v-else-if="level == 4"> <slot></slot> </h4> <h5 v-else-if="level === 5"> <slot></slot> </h5> <h6 v-else-if="level == 6"> <slot></slot> </h6> </template> <script> export default { props: { level: { type: Number, default:1 } } }; </script>
但是如果是利用 JSX 则只需要获取这个 number 然后输出就行.
export default props: { level: { type: Number , default: 1 } }, render: function(h) { const Tag =”h${this. level}” ; return <Tag>{this.$s lots. default}</Tag>; } };
最终都是编译成 render(可以直接写但是毕竟复杂就还是利用 JSX)
export default { props: { level: { type: Number, default: 1 } }, render: function(createElement) { return createElement ( "h”+ this.level, //标签名称 this. $s lots.default //子元素数组 ); } };
其实也不一定就一定要切来切去,是可以混合使用的!
<script> import AnchoredHeading1 f rom ". /AnchoredHeading.vue"; import AnchoredHeading2 from "./AnchoredHeading.js"; import AnchoredHeading3 from "./AnchoredHeading.jsx"; export default { components: { AnchoredHeading1, AnchoredHeading2, AnchoredHeading3, VNodes: { functional: true, render: (h, ctx) => ctx. props. vnodes } }, data() { return { msg: "hello vue" }; methods: { getJSXSpan() { return <span>Message: {this ,msg}</span>; }, getAnchoredHeading(level) { const Tag =' h$(level}' ; return <Tag>Hello world!</Tag>; } } };
因为两者都是语法糖,最终都是编译成 createElement.