Vue 组件化 响应式 vdom 和 diff 模板编译 渲染过程 原理

Vue原理

Vue响应式

  • 组件data的数据一旦变化,立刻触发视图的更新

  • 实现数据驱动视图的第一步

  • 核心 API-Object.defineProperty(Vue3.0启用Proxy,但是Proxy兼容性不好,且无法polyfill)

    • Object.defineProperty(obj, prop, descriptor)

      • obj:要定义属性的对象。

      • prop:要定义或修改的属性的名称或 Symbol

      • descriptor:要定义或修改的属性描述符

    • const data = {}
      const name = 'xzt'
      Object.defineProperty(data,'name',{
      	get(){
      		return name
      	},
      	set(newVal){
      		name = newVal
      	}
      })
      
  • Object.defineProperty缺点

    • 深度监听,需要递归到底,一次性计算量大

    • 无法监听新增属性/删除属性(Vue.set Vue.delete

    • 无法原生监听数组,需要特殊处理

      • // 重新定义数组原型
        const oldArrayProperty = Array.prototype
        // 创建新对象,原型指向 oldArrayProperty ,再扩展新的方法不会影响原型
        const arrProto = Object.create(oldArrayProperty);
        ['push', 'pop', 'shift', 'unshift', 'splice', 'sort','reverse'].forEach(methodName => {
            arrProto[methodName] = function () {
                updateView() // 触发视图更新
                oldArrayProperty[methodName].call(this, ...arguments)
                // Array.prototype.push.call(this, ...arguments)
            }
        })
        
        // observer function 中 
        if (Array.isArray(target)) {
        	// 这样就避免了污染全局 Array 的 原型
        	target.__proto__ = arrProto
        }
        

虚拟DOM(Virtual DOM)

  • vdom是显示vueReact的重要基石

  • diff算法是vdom中最核心、最关键的部分

  • DOM操作非常消耗性能

  • 以前使用JQ,可以自行控制DOM操作的实际,手动调整

  • vdom-用JS模拟DOM结构,计算出最小的变更,操作DOM

    • JS模拟DOM 哈哈

      模拟模拟

    •     const Vnode = {
               tag: "div",
               attr: {
                   id: "container"
               },
               children: [
                   {
                       tag: "h1",
                       attr: {
                           class: "title"
                       },
                       children: [
                           "JS模拟DOM",
                           {
                               tag: "span",
                               children: [
                                   "哈哈"
                               ]
                           }
                       ]
                   },
                   {
                       tag: "p",
                       attr: {
                           class: "content"
                       },
                       children: [
                           "模拟模拟"
                       ]
                  }
               ]
           }
         
      

通过snabbdom学习dom

  • 简洁强大的vdom库,易学易用
  • Vue参考它的实现的vdomdiff
  • https://github.com/snabbdom/snabbdom

diff算法

  • diff算法是vdom中最核心、最关键的部分

  • diff算法能在日常使用Vue React中体现出来(如key

  • diff即对比,是一个广泛的概念,如linux diffgit diff

  • 俩个js对象也可以做diffhttps://github.com/cujojs/jiff

  • 俩颗树做diff,如这里的vdom diff

    • diff的时间复杂度O(N^3)
  • diff优化到时间复杂度到O(n)

    • 只比较同一层级,不跨级比较
    • tag不相同,则直接删掉重建,不在深度比较
    • tagkey,俩者都相同,则认为是相同节点,不在深度比较
  • diff算法总结

    • patchVnode
    • addVnodes removeVnodes
    • updateChildren(key的重要性)
  • vdom核心概念很重要 :hvnodepatchdiffkey

  • vdom存在的价值更加重要:数据驱动视图、控制DOM操作

模板编译

  • 模板是vue开发中最常用的部分,即与使用想关联的原理
  • 它不是html、有指令、插值、JS表达式
  • 前置知识:JSwith语法
    • 使用with,能改变 {} 内自由变量的查找方式
    • 将 {} 内自由变量,当作 obj 的属性来查找,但是没定义会报错
    • with要慎用,它打破了作用域规则,易读性变差
  • vue template complier将模板编译为render函数
  • 执行render函数生成 vnode

编译模板

  • 模板不是html有指令、插值、JS表达式、能实现判断、循环
  • html是标签语言,只有JS才能实现判断、循环(图灵完备)
  • 因此,模板一定是转换为某种JS代码,即编译模板
  • 模板编译为render函数,执行render函数返回vnode
  • 基于vnode在执行patchdiff
  • 使用webpack vue-loader,会在开发环境下编译模板(重要)

vue组件中使用render代替template

Vue.component('anchored-heading', {
  render: function (createElement) {
    return createElement(
      'h' + this.level,   // 标签名称
      this.$slots.default // 子节点数组
    )
  },
  props: {
    level: {
      type: Number,
      required: true
    }
  }
})

模板编译总结

  • with语法
  • 模板到render函数,再到vnode,再到渲染和更新
  • vue组件可以用render代替template

组件 渲染 / 更新 过程

  • 一个组件渲染到页面,修改data触发更新(数据驱动视图)

  • 渲染组件时,会通过Vue.extend方法构建子组件的构造函数,并进行实例化。最终手动调用

    $mount() 进行挂载。更新组件时会进行 patchVnode 流程.核心就是diff算法

  • 初次渲染过程

  • 更新过程

  • 异步渲染

总结

  • 响应式:监听到data属性getter setter(包括数组)
  • 模板编译:模板到render函数,再到vnode
  • vdom:patch(elem,vnode)patch(vnode,newVnode)

初次渲染过程

  • 解析模板为render函数(或在开发环境已经完成,vue-loader
  • 触发响应式,监听到data属性getter setter(包括数组)
  • 执行render函数,生成vnode,patch(elem,vnode)

更新过程

  • 修改data,触发setter(此前在getter中已被监听)
  • 重新执行render函数,生成newVnode
  • patch(vnode,newVnode)

异步渲染

  • $nextTick
  • 汇总data的修改,一次性更新视图
  • 减少DOM操作次数,提高性能

前端路由原理

  • 稍微复杂一点的spa,都需要路由

  • 路由模式

    • hash
    • H5 history:需要后端支持
  • 网页url组成部分

    • origin: “http://pzy.pub”

    • protocol: “http:”

    • host: “pzy.pub”

    • hostname: “pzy.pub”

    • port: “”

    • pathname: “/”

    • search: “”

    • hash: “”

    • href: “http://pzy.pub/”

hash的特点

  • hash变化会触发网页跳转,即浏览器的前进、后退
  • hash变化不回刷新页面,spa必需的特点
  • hash永远不回提交到server
  • 通过windowonhashchange监听

H5 history

  • url规范的路由,但跳转时不刷新页面
  • history.pushState
  • window.onpopstate

你可能感兴趣的:(Vue)