py-tofee / Notes

2 stars 0 forks source link

Vue #2

Open py-tofee opened 4 years ago

py-tofee commented 4 years ago

Vue响应式数据原理

vue在初始化数据时,通过Object.defineProperty将数据对象转换成getter/setter的形式来追踪变化,读取数据的时候会触发getter,并在getter中进行依赖收集(收集当前组件的watcher);修改数据的时候会触发setter,setter去通知getter中收集的依赖(watcher);watcher接收到通知后,去触发视图更新或者触发某个回调函数。(发布订阅设计模式)

Array变化侦测:

Array也是在getter中收集依赖,但是是在拦截器中触发依赖; 因为数组是通过方法来改变内容的,所以vue通过创建拦截器(自定义的方法)去覆盖数组原型方法的方式来追踪数组的变化的; 覆盖原型,即覆盖数组的_proto_属性,

// 自定义拦截方法methods,并缓存原始原型方法,拦截操作触发依赖后,调用原始原型方法实现功能
array._proto_ = methods 

如果某些浏览器不支持_proto_属性,那么直接将拦截方法设置到被侦测的数组array上,也是有效的;因为当访问一个对象的方法时,只有其自身不存在这个方法时,才会去他的原型上查找这个方法。 数组的新元素赋值操作,数组设置length操作,不能被拦截到,因为不属于原型上的方法;

let list = []
list[0] = 'a'

list.length = 0 // 清空数组

proxy的优势:

由于Object.defineProperty只能追踪一个数据是否被修改,无法追踪新增和删除属性的变化,只能通过vm.$set(确保新增的属性是响应式的)和vm.$delete(确保数据被删除之后能通知到watcher)解决。 使用proxy代理对象,直接拦截对象的所有操作,包括对象的新增和删除属性,数组的新元素赋值操作,数组设置length操作

py-tofee commented 4 years ago

watch

py-tofee commented 4 years ago

computed与watch的区别

py-tofee commented 4 years ago

nextTick原理

vue实现响应式,并不是数据变化之后,立即更新DOM;而是等同一事件循环中的所有数据变化完成之后,再统一进行视图更新;所以vue是异步执行DOM更新的。 微任务和宏任务(异步)

py-tofee commented 3 years ago

keep-alive

它有两个重要属性: include--字符串或正则表达式,只有匹配的组件才会被缓存; exclude--字符串或正则表达式,任何匹配的组件都不会被缓存; router-view是vue-router内置组件,如果直接被包在keep-alive中,那么所有路径匹配到的视图组件都会被缓存;

<keep-alive>
    <!--插槽用法-->
    <router-view></router-view>
</keep-alive>

activated和deactivated两个钩子函数

py-tofee commented 3 years ago

虚拟DOM

VNode是一个类,可以生成不同类型的vnode实例,文本节点,注释节点等,不同类型的vnode表示不同类型的真实DOM元素;由于vue对组件采用类虚拟DOM来更新视图,当数据发生变化时,整个组件都要进件重新渲染;但是组件内并不是所有的DOM节点都要更新,所以将vnode缓存,并将当前新生成的vnode和上次缓存的oldvnode进行对比,只对需要更新的部分进行DOM操作,可以提升很多性能; 虚拟DOM最核心的部分是patch,patch将vnode渲染成真实的DOM; 通过patch算法,在现有的DOM上,计算出真正需要更新的DOM节点,最大限度减少DOM操作;

  1. 当oldvnode不存在,创建新增节点
  2. 当oldvnode和vnode都存在,且不是同一个节点时,删除以及废弃的节点,使用vnode替换该节点
  3. 当oldvnode和vnode都存在,且是同一个节点,进行更详细的对比,修改需要更新的节点
  4. 创建两个指针,分别用于oldvnode和vnode,递归比较,每次都是同级对比;
py-tofee commented 3 years ago

Vue生命周期钩子

生命周期钩子函数其实是一个回调函数,当vue实例执行到某个状态时,会触发对应的生命周期钩子函数;vue实例创建时,会将实例的生命周期函数与构造函数(父级 Vue.options = {} 定义默认组件选项,影响全局)上定义的生命周期函数合并成一个数组,父级上的生命周期函数会先执行; 与mixin类似,mixin按照某种合并规则与组件选项合并,但是mixin不是组件之间共享的,而是会在组件上定义对应的属性和方法。

生命周期函数

  1. 两个创建类型
    • beforeCreate:在实例初始化之后,数据观测和事件配置之前被调用,此时实例还未完全创建完成,data, computed, methods 都还不能使用
    • created:在实例创建完成之后调用,此时数据观测data,属性和方法的运算,watch和事件已经完成配置且可以使用,但是还没有挂载到DOM元素上
  2. 两个挂载类型
    • beforeMount:在挂载开始之前被调用,此时相关的render函数首次被调用
    • mounted:组件挂载之后被调用,此时el被vm.$el替换,
  3. 两个更新类型
    • beforeUpdate:在这个钩子中进一步更改状态,不会触发附加的重新渲染过程
    • updated:应该避免在这个钩子中改变状态,容易导致无限循环
  4. 两个销毁类型
    • beforeDestroy:这里实例仍然可用,此时通常可以做一些事件解绑操作:
    • 当前组件中使用$on监听的自定义事件,需要在此处解绑,使用$off,避免在其他地方$emit该事件时,执行事件监听器,发现该组件实例已经不存在了;
    • 清除组件中定义的定时器;
    • 解除dom事件绑定:scroll,mousemove等等。
    • destroyed:组件被完全销毁,实例不可用
  5. 两个keep-alive相关类型
  6. 一个监听子组件及孙子组件错误的钩子函数

父子组件生命周期执行顺序

总结:先执行父组件的before,再执行子组件的before,当子组件ed(完成操作)的时候,父组件的ed才执行。

  1. 父子组件加载渲染过程: 父beforeCreate -> 父created -> 父beforeMount -> 子beforeCreate -> 子created -> 子beforeMount -> 子mounted -> 父mounted
  2. 子组件更新过程:父beforeUpdate->子beforeUpdate->子updated->父updated
  3. 父组件更新过程:父beforeUpdate->父updated
  4. 销毁:父beforeDestroy->子beforeDestroy->子destroyed->父destroyed
py-tofee commented 3 years ago

ElementUI 表单校验 - prop链式写法

<el-form-item prop="fields">
    <el-form-item v-for="(item, index) in list"
       :key="index"
       :prop="'fields.' + index + '.value'">
        <el-input v-model="item.value"></el-input>
    </el-form-item>
</el-form-item>
py-tofee commented 3 years ago

vue模板渲染原理

vue中的模板template无法被浏览器解析渲染,因为这不属于浏览器的标准,不是正确的HTML语法,所以需要将template转换成一个render函数,执行render函数返回对对应的HTML结构;模板编译分三个阶段:

py-tofee commented 3 years ago

条件渲染

  1. :class=""prop 和 class=""属性可以共存
  2. v-show不支持template元素,也不支持v-else
  3. v-if是“真正”的条件渲染,因为他确保在切换过程中,条件块内的事件监听器子组件适当的被销毁和重建;
  4. v-if是惰性的,如果初始时,条件为假值,则不会渲染和初始化条件块内的内容;v-show不管初始条件是什么,元素总是会被渲染,并且只是简单的基于css切换;
  5. 通常v-if具有更高的切换开销,v-show具有更高的初始渲染开销;如果需要频繁的切换,建议用v-show,如果运行时条件很少改变,则使用v-if较好;
  6. 当同时使用v-if和v-for时,v-for具有更高的优先级,内部先执行的v-for,然后将v-if绑定到每个元素上,导致每个元素在执行render函数的时候,都要执行v-if的判断,影响性能;
  7. v-for...in 和 v-for...of 等价,都可以遍历数组和对象;vue解析v-for指令时,用正则匹配in或者of两边的表达式,并处理;
    v-for="(item, key, index) in data"
    v-for="(item, key, index) of data"

    如果data是数组,那么key就是索引,index是undefined; 如果data是对象,那么key就是键值,index是索引; 遍历对象时,按照Object.keys(data)的结果顺序遍历,遍历结果在不同的JS引擎中可能不同;

  8. 特殊指令-key key除了配合v-for使用,也可以用于强制替换元素或者组件,而不是重复使用它;给元素或者组件绑定key值,当key值发生变化时,元素或者组件会被替换,会触发完整的生命周期钩子函数,会触发初始过渡效果;
  9. 执行render函数时,遇到v-if指令,会解析成一个三目运算,只有条件为真时才去创建这个虚拟节点;所以当v-if绑定的值频繁发生变化,会频繁触发虚拟节点的创建和销毁,此时应用v-show代替。
py-tofee commented 3 years ago

事件修饰符

作用:使事件处理器绑定的方法只处理数据逻辑,而不用处理DOM事件细节;

  1. .capture:在捕获阶段触发事件,点击内层div,依次输出1-3-4-2,点击外层div,依次输出1-2
    <!--@click.capture与@click是不同的事件处理器,可以同时存在-->
    <div @click.capture="log(1)" @click="log(2)" >
    <div @click.capture="log(3)" @click="log(4)" ></div>
    </div>
  2. .stop:阻止事件冒泡,点击内层div,依次输出2-3;由于阻止了内层div的点击事件冒泡,所以外层div的事件不会触发
    <div @click="log(1)" >
    <div @click.stop="log(2)" @click="log(3)" ></div>
    </div>
  3. .prevent:阻止事件默认行为,但绑定的方法仍然会被触发;点击a标签,不会跳转页面,依次输出3-2-1
    <div class="box-out" @click="log(1)" >
    <div class="box-in" @click="log(2)" >
    <a @click.prevent="log(3)" href="/internal/black">A</a>
    </div>
    </div>
  4. .self:只有当event.target是当前元素自身时才触发,事件不是从内部元素冒泡触发;点击a标签,依次输出3-1,点击box-in,依次输出2-1
    <div class="box-out" @click="log(1)" >
    <div class="box-in" @click.self="log(2)" >
    <a @click.prevent="log(3)" href="/internal/black">A</a>
    </div>
    </div>
  5. .once:在一次生命周期内,支触发一次事件回调;第一次点击box-in,输出2-1;之后再点击box-in,只输出1;
    <div class="box-out" @click="log(1)" >
    <div class="box-in" @click.once="log(2)"></div>
    </div>

    补充:once修饰符除了用于原生DOM事件,还可以用于自定义的组件事件上,当once为true,表示当事件监听器被添加之后,会在其第一次被调用之后自动移除;

  6. .left/.right/.middle:当点击鼠标的左键/右键/中键时触发
  7. 修饰符串联:
    • @click.prevent.self="handle",总是先执行prevent,无论是内部元素或者元素自身触发的click事件,都会触发prevent阻止事件默认行为,只有当event.target是元素自身时,才会触发handle方法
    • @click.self.prevent,总是先执行self,只有当event.target是元素自身时,才会触发prevent和handle,元素内部可以冒泡触发该元素的默认行为
  8. .passive:passive为true时,表示永远不会阻止事件的默认行为,当passive和prevent一起使用,passive的优先级更高,prevent会被忽略,且浏览器会展示一个警告
  9. 以上是对v-bind指令所绑定的事件添加事件修饰符,自定义指令可以实现同样的效果,例如自定义了一个指令v-mybind,在指令的钩子函数bind中,获取传入的事件修饰符binding.modifiers,遍历处理即可;v-mybind:click.stop="handle"
  10. 滚动事件和滚动行为,滚动时会立马触发滚动事件onscroll,而不是等滚动结束再触发,要禁止滚动事件,可以阻止mousewheel和touchmove等事件的默认行为
  11. 当ViewModel被销毁时,它关联的所有事件处理器都会被自动移除
py-tofee commented 3 years ago

v-model修饰符

  1. .lazy:默认情况下,表单和数据在input事件触发后就进行同步,添加lazy修饰符后,变为在change事件之后进行同步,即失去焦点之后进行同步;目前只有原生的input的v-model支持lazy修饰符,<input v-model.lazy="msg" />,elementUI的el-input不支持,因为el-input组件解析后input被包裹在<div class="el-input"></div>中;
  2. .number:将输入值转换为数值类型
  3. .trim:自动过滤输入的首位空格

    vue3的v-model

    vue3升级了v-model,去掉了.sync修饰符;

    • 在vue2中,一个组件或者表单元素只能绑定一个v-model,如果需要对多个prop实现双向绑定,则需要使用.sync修饰符:propName.sync="value",然后子组件配合$emit('update:propName', value)实现更新;
    • 在vue3中,可以给v-model指定propName,v-model:propName="value",这样一个组件或者表单元素可以实现多个prop的双向绑定了
      <my-component
      v-model.capitalize:="value"
      v-model:title.capitalize:="title"
      ></my-component>

      my-component组件内部:

      {
      props: ['modelValue', 'modelValueModifiers', 'title', 'titleModifiers'], // Modifiers保存的是修饰符
      emits: ['update:modelValue', 'update:title']
      }
      // this.modelValueModifiers = {capitalize: true}
      // this.titleModifiers = {capitalize: true}
py-tofee commented 3 years ago

vue3的emits和props组件选项

  1. 组件选项emits,可以跟props一样,用数组或者对象的形式列出已抛出的事件,并且可以为事件添加一个验证函数;验证函数接收传递给$emit调用的参数,并返回一个布尔值指示事件是否有效。
    {
    emits: {
    click: null, // 没有验证
    submit: ({name}) => {
      return !!name
    } // 验证submit事件,判断name是否有值
    }
    }
  2. 组件选项props
    • v-bind="param"表示传入param对象的所有属性
    • prop验证:
      props: {
          name: {
            type: String/Number/Boolean/Array/Object/Date/Function/Symbol/自定义构造函数, // 类型
            required: true, // 是否必传
            default: 'a', // 默认值
            validator(value) { // 自定义验证函数,校验prop是否满足条件
              return value.includes('a')
            }
          }
        }
    • 非prop属性(class/id/style等),渲染后,会自动添加到组件根节点属性中;设置组件选项inheritAttrs: false,实现禁止根元素继承attribute,通过访问组件实例的$attrs属性,可以将$attrs应用于非根元素v-bind="$attrs";如果一个组件有多个根元素,那么必须显示的将$attrs绑定。
py-tofee commented 3 years ago

插槽

slot又名插槽,是Vue的内容分发机制;分三类,默认插槽,具名插槽和作用域插槽。 实现原理:当子组件vm实例化时,获取到父组件传入的slot标签的内容,存放在vm.$slot中,默认插槽为vm.$slot.default,具名插槽为vm.$slot.xxx,xxx 为插槽名,当组件执行渲染函数时候,遇到slot标签,使用$slot中的内容进行替换,此时可以为插槽传递数据,若存在数据,则可称该插槽为作用域插槽。(子组件数据传递给父组件)

插槽与作用域插槽的区别

  1. 具名插槽
    <!--父组件-->
    <template v-slot:header></template>
    <!--子组件-->
    <slot name="header"></slot>
  2. 作用域插槽
    <!--父组件-->
    <template v-slot:header="slotProps">{{slotProps.title}}</template>
    <!--父组件-解构插槽prop-->
    <template v-slot:header="{ item, title }">{{title}}</template>
    <!--父组件-解构插槽prop-重命名和设置默认值-->
    <!--给index重命名为key,给title设置了默认值'hello'-->
    <template v-slot:header="{ item, title="hello", index: key }">{{title}} {{key}}</template>
    <!--子组件-->
    <slot name="header" :item="item" :title="title"></slot>
  3. 通常v-slot只能用于<template>元素上,但当只存在默认插槽时,v-slot可以用于组件标签上:
    <todo-list v-slot="slotProps">{{slotProps.item}}</todo-list>
    <!--子组件-只存在默认插槽-->
    <slot :item="item" :index="index"></slot>
  4. 动态指令参数-动态插槽名
    <template v-slot:[dynamicSlotName]></template>
    <!--跟动态指令参数一样,如下-->
    <a v-bind:[attributeName]="url"> ... </ a>
    <a v-on:[eventName]="doSomething"> ... </ a>
  5. 具名插槽缩写:v-slot:缩写为##后面必须带有插槽名
    <template v-slot:header="{item}">...</template> 缩写为 <template #header="{item}">...</template>
    <template v-slot:default="{item}">...</template> 缩写为 <template #default="{item}">...</template>
    <template v-slot="{item}">...</template> 缩写为 <template #default="{item}">...</template>
  6. vue2中废弃的插槽语法
    <template slot="todo" slot-scope="{ todo }"></template>

    vue2.6.0后用 v-slot 代替 slot和slot-scope

    <template v-slot:todo="{todo}"></template>
py-tofee commented 3 years ago

Mixin-混入

  1. 当mixin对象的data函数和组件的data函数,存在属性冲突时,以组件自身的数据为优先;
  2. 当mixin对象和组件存在同名钩子函数,将钩子函数合并成一个数组,mixin对象的钩子函数将在组件钩子函数之前调用;
  3. 对于值为对象的组件选项,例如component,directives,methods,将被合并为一个对象,当对象的键名冲突时,取组件选项的键值对;
  4. 全局mixin,将影响之后创建的每一个组件,所以通常全局mixin只应当应用于自定义组件选项(只有当组件包含该自定义组件选项时,才执行mixin中的逻辑操作)
    const app = Vue.createApp({
    myOption: 'hello!'
    })
    // 为自定义的选项 'myOption' 注入一个处理器。
    app.mixin({
    created() {
    const myOption = this.$options.myOption
    if (myOption) {
      console.log(myOption)
    }
    }
    })
    app.mount('#app') // => "hello!"
  5. mixin自定义合并策略,在app.config.optionMergeStrategies中配置自定义选项的合并策略
  6. mixin不足:很容易发生冲突;可重用性有限,不能像mixin传递任何参数来改变它的逻辑;vue3的组合式API-step函数能很好的解决这些问题;

    vue3的组合式API-step函数

    setup(props, context)

  7. 参数props:组件的props,是响应式的,当传入新的props时,参数props会被更新;
  8. 参数context:是一个JS对象, {attrs,slots,emits}
  9. 在组件实例创建之前执行,在step中应避免使用this,因为step中的this不会指向组件实例;
  10. 执行setup时,只能访问props,attrs,slots,emits,不能访问methods,data,computed;
  11. 如果setup函数返回一个对象,那么在组件的其余部分(计算属性,方法,生命周期钩子函数等)以及组件模板中都可以访问到;
  12. setup函数也可以返回一个render函数
  13. 使用场景:当组件很大的时候,逻辑关注点的列表也会增长,在处理单个逻辑关注点时,我们必须不断地“跳转”到相关代码区域,查阅某一个功能的代码,可能需要在data,watch,computed,methods,钩子函数等之间不断“跳转”,使得组件变得难以维护和阅读;
  14. setup函数能够将一个逻辑关注点的代码收集在一起;如果有多个需要收集的逻辑关注点,我们可以将每一个逻辑关注点的代码提取到一个独立的组合式函数中(js文件),函数中可以注册组件钩子函数,异步获取数据等操作,该函数返回组件需要使用的响应式数据;在组件中导入这些独立的组合式函数,并在setup函数中调用,将调用结果返回,供组件其他地方使用即可;
py-tofee commented 3 years ago

自定义指令

  1. 根实例的directive API注册全局指令,组件的directive选项注册局部指令;
  2. 指令的生命周期钩子函数:create, beforeMount, mounted, beforeUpdate, updated, beforeUnmount, ummounted;
  3. 指令可以带参数,且参数可以是动态的:v-mydirective:[argument]="value",指令的值也可以是动态的,可以绑定组件的数据,可以是所有合法的JavaScript表达式:v-mydirective:[argument]="{value: 'a', desc: 'A'}";
  4. 和attribute类似,自定义指令在组件上使用时,总是会被应用到组件的根节点上,但在vue3中,如果组件存在多个根节点,指令会被忽略,并且会抛出一个警告;
  5. 和attribute不同,自定义指令不会包含在$attrs中,不会通过v-bind="$attrs"被传入另一个元素。

    vue3-teleport

    允许我们控制当前组件或者元素在指定的父节点下渲染,但不影响vue-devtools中的组件上下级关系

    <teleport to="body"><modal>...</modal></teleport>
    <teleport to="#box"><div>...</div></teleport>
    <div id="box"></div>

函数式组件

  1. 指组件自身没有任何组件状态的组件类型,在渲染过程中不会创建组件实例,且没有常规的组件生命周期钩子函数
  2. 用来定义那些没有响应数据,只接受一些props,attrs,emit,slots作为参数,来定义怎么显示组件。
    const FunctionalComponent = (props, context) => {
    // ...
    }

    第二个参数 context 包含三个 property:attrs、emit 和 slots。它们分别相当于实例的 $attrs、$emit 和 $slots 这几个 property。

py-tofee commented 3 years ago

vue3响应式

  1. 利用proxy(target, handler)完全代理跟对象的交互,在get中收集依赖,在set中触发依赖,且在handler中,使用Reflect(内置对象),保证不管proxy怎么修改对象的默认行为,总是可以在reflect上获取并执行对象的默认行为,确保在收集依赖和触发依赖之后,能完成对对象的原有行为。
  2. 组件的data返回一个对象,这个对象在编译时,vue内部使用reactive()函数,将其变成响应式对象$data;组件模板会被编译成render函数,在render函数中可以使用$data,因为依赖跟踪的关系,当响应式数据发生变化时,会触发视图更新。render函数返回VNode,VNode组成Virtual DOM树,当响应式数据更新时,JavaScript不会直接操作DOM去更新页面,而是先对比新旧virtual dom,执行diff函数,找出需要更新的内容,批量进行更新;
  3. reactive方法将对象变成响应式的,ref方法将独立原始值(数值,字符串等)变成响应式,ref方法返回一个只包含value属性的对象;
  4. 解构响应式对象,解构出的属性会失去响应式,let {title} = book;用toRefs方法将响应式对象转换为一组ref,let {title} = toRefs(book),这个时候title仍然保留与源对象的响应式关联,修改title,book.title也会被修改。
  5. 基于原始响应式对象,使用readonly函数,将创建一个只读的proxy对象,用于防止某些对象被修改。
    const original = reactive({ count: 0 })
    const copy = readonly(original)

    修改copy将会失败并出现告警

  6. vue不允许动态添加组件根级响应式属性,会触发告警(试图访问不存在的属性);这样可以使组件实例能更好的配合类型检查系统工作,提前声明所有的响应式property,有利于组件的维护和阅读

    vue异步更新队列

  7. vue在侦听到数据变化时,将开启一个队列,将数据变更push到这个队列中,如果同一个侦听器被多次触发,只会被推入到队列中一次,避免不必要的计算和DOM操作;然后在下一个事件循环tick中,vue刷新队列并执行队列中的数据变更操作。
  8. 如果需要在数据变更之后立即执行回调函数,vue提供实例方法 this.$nextTick(callback),该函数返回一个promise,所以可以使用await/async简写
    await this.$nextTick();
    // todo something
py-tofee commented 3 years ago

vue组件通信

py-tofee commented 3 years ago

Vue.set(taget: Array|Object, key, val)

源码

export function set (target: Array<any> | Object, key: any, val: any): any {
  if (process.env.NODE_ENV !== 'production' &&
    (isUndef(target) || isPrimitive(target))
  ) {
    warn(`Cannot set reactive property on undefined, null, or primitive value: ${(target: any)}`)
  }
  if (Array.isArray(target) && isValidArrayIndex(key)) {
    target.length = Math.max(target.length, key)
    target.splice(key, 1, val)
    return val
  }
  if (key in target && !(key in Object.prototype)) {
    target[key] = val
    return val
  }
  const ob = (target: any).__ob__
  if (target._isVue || (ob && ob.vmCount)) {
    process.env.NODE_ENV !== 'production' && warn(
      'Avoid adding reactive properties to a Vue instance or its root $data ' +
      'at runtime - declare it upfront in the data option.'
    )
    return val
  }
  if (!ob) {
    target[key] = val
    return val
  }
  defineReactive(ob.value, key, val)
  ob.dep.notify()
  return val
}
py-tofee commented 3 years ago

$on, $emit, $once, $off 实例方法

原理:组件实例的自定义事件都存在事件中心_events属性中,不管是添加、移除还是触发事件,都是操作_events。

py-tofee commented 3 years ago

ajax请求放在哪个生命周期函数中比较合适

在create的时候,还不能访问dom,在mounted的时候,dom可以访问,由于生命周期钩子函数都是同步执行的,ajax是异步执行,不会阻塞生命周期钩子函数,所以无论放在create还是mounted,都是在生命周期初始化执行完成之后,再执行ajax(事件循环:先执行同步,在执行异步); 通常为了保证统一,一般在客户端渲染中,将ajax放在mounted中;而服务端渲染不支持mounted,所以服务端渲染一般放在create中。

py-tofee commented 3 years ago

vue中事件绑定的原理

原生事件和组件事件

事件代理的应用

因为JavaScript中,页面上的事件处理程序数量直接关系到页面额度性能,因为事件处理程序就是函数,函数就是对象,就会占用内存,而且每个事件处理程序,都会访问DOM,访问DOM次数过多,也会影响页面的交互时间。

// 下面这种情况,当list数据量大的时候,会使用addEventListener绑定很多事件监听器
<div v-for="item in list" @click="handleClick"></div>
// 使用事件代理优化(事件冒泡):将事件提升到父元素,这样只需要绑定一个事件处理器
<div @click="handleClick">
    <div v-for="item in list" @click="handleClick"></div>
</div>
py-tofee commented 3 years ago

v-model原理

py-tofee commented 3 years ago

v-html

py-tofee commented 3 years ago

异步组件

如果组件的功能很多,那么子组件可以使用异步加载的方式,主要是利用import函数实现;当组件需要被渲染的时候,才去加载这个组件。

components: {
    addItem: (resolve) => import("../components/addItem")
}