Vue - day04

22 阅读3分钟

一、依赖注入(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