一、依赖注入(provide/inject)
解决深层组件嵌套后,外层向内层传递数据的需求
1.1 传入固定值
- 容器组件
app.component({
provide:{
键名:键值
}
})
- 后代组件
app.component({
inject:['键名']
})
<div id="app">
<my-root></my-root>
</div>
<script>
let app = Vue.createApp({
});
app.component("my-root", {
template: `<div>
第一层组件
<my-foot></my-foot>
</div>`,
provide: {
//为后代组件提供数据
msg: "Hello Child",
},
});
app.component("my-foot", {
template: `<div>
第二层组件
<my-child></my-child>
</div>`,
});
app.component("my-child", {
inject: ["msg"],
template: `<div>第三层组件,{{msg}}</div>`,
});
app.mount("#app");
</script>
1.2 传入响应式数据
- 容器组件
app.component({
data(){
return {
响应式键名:xxx
}
}
provide(){
return {
键名:Vue.computed(()=>{
return this.响应式键名
})
}
}
})
- 后代组件
app.component({
inject:['键名']
})
<div id="app">
<my-root></my-root>
</div>
<script>
let app = Vue.createApp({
});
app.component("my-root", {
data() {
return {
num: 100,
};
},
template: `<div>
第一层组件
<button @click="num++">按钮{{num}}</button>
<my-foot></my-foot>
</div>`,
provide() {
return {
//为后代组件提供响应式数据,使用组合式computed方法
count: Vue.computed(() => {
return this.num;
}),
};
},
});
app.component("my-foot", {
template: `<div>
第二层组件
<my-child></my-child>
</div>`,
});
app.component("my-child", {
inject: ["count"],
template: `<div>第三层组件,{{count}}</div>`,
});
app.mount("#app");
</script>
二、透传 Attribute
- class、style、click 自动透传处理
- @click 内部能够直接触发绑在组件上的点击事件
- props 之外的属性,才能被当做 attributes
- v-bind="$attrs"
- this.$attrs
<div id="app">
<my-comp></my-comp>
<my-comp class="box1"></my-comp>
<my-comp @click="num++"></my-comp>
<h1 @click="num+=2">{{num}}</h1>
</div>
<script>
let app = Vue.createApp({
data() {
return {
num: 100,
};
},
});
app.component("my-comp", {
template: `<div class="box" @click="handleAlert">
演示Attributes的继承性
</div>`,
methods: {
handleAlert() {
alert(123);
},
},
});
app.mount("#app");
</script>
三、$attrs
props 之外的属性,才能被$attrs 拿到
<div id="app">
<my-comp name="三丰" score="100"></my-comp>
</div>
<script>
let app = Vue.createApp({
data() {
return {
num: 100,
};
},
});
app.component("my-comp", {
props: ["name"],
template: `<div class="box">
演示$attr的使用
<h1>{{name}}</h1>
<h2 v-bind="$attrs">
{{$attrs.score}}
</h2>
</div>`,
mounted() {
console.log(this.$attrs);
},
});
app.mount("#app");
</script>
四、$root
<div id="app">
{{msg}}
<my-comp />
</div>
<script>
let app = Vue.createApp({
data() {
return {
msg: "Hello Vue",
};
},
});
app.component("my-comp", {
template: `<div>
演示$root的使用
<button @click="handleRoot">按钮</button>
</div>`,
mounted() {
console.log(this.$root);
},
methods: {
handleRoot() {
this.$root.msg = "你好";
},
},
});
app.mount("#app");
</script>
五、$parent
<div id="app">
<my-root></my-root>
</div>
<script>
let app = Vue.createApp({
});
app.component("my-root", {
template: `<div>
第一层组件
<my-foot></my-foot>
</div>`,
created() {
console.log("在第一层组件获取$parent", this.$parent);
},
});
app.component("my-foot", {
data() {
return {
count: 233,
};
},
template: `<div>
第二层组件
<my-child></my-child>
</div>`,
});
app.component("my-child", {
template: `<div>第三层组件</div>`,
created() {
console.log("在第三层组件获取$parent", this.$parent);
},
});
app.mount("#app");
</script>
六、自定义指令
6.1 全局注册
<div id="app">
<input type="text" v-focus="123" />
<input type="text" v-focus />
</div>
<script>
let app = Vue.createApp({
});
//全局挂载自定义指令
app.directive("focus", {
//钩子函数
mounted(el, binding, vnode) {
console.log(el, binding, vnode);
el.focus();
},
});
app.mount("#app");
</script>
6.2 局部注册
let app = Vue.createApp({
//挂载局部指令
directives: {
mshow: {
//负责初始化效果
mounted(el, binding) {
console.log(el, binding);
let disp = binding.value ? "block" : "none";
el.style.display = disp;
},
//负责根据新数据更新效果
updated(el, binding) {
...
},
},
},
});
6.3 钩子函数
跟生命周期长相一样,未提供 beforeCreate
- 钩子函数的四个参数:
- el 绑了指令的 DOM 元素
- binding 指令参数
- vnode 虚拟 DOM
- prevVnode 原来的旧的虚拟 DOM
七、Transition动画组件
<style>
.box {
width: 200px;
height: 200px;
background-color: #333;
margin: auto;
}
/* 入场动画 */
.fade-enter-from {
opacity: 0;
transform: translateX(-100%);
}
.fade-enter-active {
transition: all 3s;
}
.fade-enter-to {
opacity: 1;
transform: translateX(0);
}
/* 出场动画 */
.fade-leave-from {
opacity: 1;
transform: translateX(0);
}
.fade-leave-active {
transition: all 2s;
}
.fade-leave-to {
opacity: 0;
transform: translateX(100%);
}
</style>
<div id="app">
<button @click="isShow=!isShow">按钮</button>
<Transition name="fade">
<div class="box" v-if="isShow"></div>
</Transition>
</div>
<script>
let app = Vue.createApp({
//创建vue应用
data() {
//数据包
return {
isShow: false,
};
},
});
app.mount("#app");
</script>
八、Transition结合animateCSS动画库
<style>
.box {
width: 200px;
height: 200px;
background-color: #333;
margin: auto;
}
.box.blue {
background-color: blue;
}
</style>
<div id="app">
<button @click="isShow=!isShow">按钮</button>
<Transition
enter-active-class="animate__animated animate__fadeInLeft"
leave-active-class="animate__animated animate__fadeOutRight"
appear
mode="out-in"
@after-leave="handleLeave"
>
<!-- appear首屏出场动画 mode控制离开/进入动画的顺序 -->
<div class="box" v-if="isShow"></div>
<div class="box blue" v-else></div>
</Transition>
</div>
<script>
let app = Vue.createApp({
data() {
return {
isShow: true,
};
},
methods: {
handleLeave() {
// alert("出场动画执行完毕");
},
},
});
app.mount("#app");
</script>
九、虚拟DOM、diff算法、key的作用
9.1 虚拟DOM
虚拟 DOM 的本质是 JSON 对象,是对真实DOM的抽象,状态变更时,记录新树和旧树的差异,最后再把差异更新到真正的DOM中。
{
tag:'div',
class:'box'
...,
children:[
{
tag:'span'
},
{
tag:'p'
}
]
}
- 优点:
- 保证性能下限: 框架的虚拟 DOM 需要适配任何上层 API 可能产⽣的操作,它的⼀些 DOM 操作的实现必须是普适的,所以它 的性能并不是最优的;但是⽐起粗暴的 DOM 操作性能要好很多,因此框架的虚拟 DOM ⾄少可以保证在你不需要⼿动优化的 情况下,依然可以提供还不错的性能,即保证性能的下限;
- ⽆需⼿动操作 DOM: 我们不再需要⼿动去操作 DOM,只需要写好 View-Model 的代码逻辑,框架会根据虚拟 DOM 和 数据双向 绑定,帮我们以可预期的⽅式更新视图,极⼤提⾼我们的开发效率;
- 跨平台: 虚拟 DOM 本质上是 JavaScript 对象,⽽ DOM 与平台强相关,相⽐之下虚拟 DOM 可以进⾏更⽅便地跨平台操作,例如 服务器渲染、weex 开发等等。
9.2 diff算法
diff算法可以看作是一种对比算法,对比的对象是新旧虚拟Dom。
9.3 key的作用
标志每个 DOM 元素的唯一性。key 的作用主要是 为了实现高效的更新虚拟 DOM,提高性能。其原理是vue在patch的过程中通过key可以精准的判断两个节点是否是同一个,从而避免频繁的更新元素,使得整个patch过程更加高效,减少DOM操作量,提高性能。 管理可复用的元素
9.4 Vue 的 DOM 更新流程
旧的真实DOM---旧的虚拟DOM(带唯一key)---Diff算法比对---新的虚拟DOM---新的真实DOM