最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 虚拟DOM库Snabbdom源码解析

    正文概述 掘金(螺旋静)   2020-11-24   812

    报告:Vue.js内部当数据变化后,操作的是虚拟DOM,会对比新旧两次虚拟DOM的差异,再把差异更新到真实DOM

    虚拟DOM库Snabbdom源码解析

    Virtual DOM虚拟DOM:由普通的JS对象来描述DOM对象,因为不是真实的DOM对象,所以叫Virtual DOM

    {
        sel: 'div',
        data: {},
        children: undefined,
        text: '1',
        elm: undefined,
        key: undefined
    }
    

    为什么使用?

    • 手动操作DOM麻烦,还需要考虑浏览器兼容性问题,虽有jQuery等简化DOM操作,随着项目的复杂DOM操作复杂提升
    • 为了简化DOM复杂,有了MVVM框架,解决了视图和状态的同步问题
    • 使用模板引擎简化视图,但是它没有解决跟踪状态变化问题,于是出现了Virtual DOM
    • Virtual DOM好处是当状态改变不需要立即更新DOM,创建一个虚拟树来描述DOM,Virtual DOM内部将弄清楚如何有效(diff)的更新DOM

    描述:

    • 虚拟DOM可以维护程序的状态,跟踪上一次的状态
    • 通过比较前后两次状态的差异更新真实DOM

    作用

    1. 维护视图和状态的关系

    2. 复杂视图情况下提升渲染性能

    3. 除了渲染DOM以外,还可以实现SSR(Nuxt.js/Next.js)、原生应用(Weex/React Native)、小程序(mpvue/uin-app)等

    Virtual DOM库

    • Snabbdom
    • virtual-dom

    创建项目

    • 打包工具:parcel
    • 创建项目,并安装parcel
    yarn init -y
    yarn add parcel-bundler
    
    • 配置package.jsonscripts
    "scripts": {
    	"dev": "parcel index.html --open",
    	"build": "parcel build index.html"
    }
    
    • 创建目录结构
    index.html
    package.json
    -src
    	main.js
    

    导入Snabbdom

    • Snabbdom导入使用commonjs模块语法,流行使用ES6模块化语法import
    • ES6模块与CommonJS模块的差异
    yarn add snabbdom
    
    import { h, thunk, init } from 'snabbdom'
    
    • Snabbdom核心仅提供最基本的功能,只导出了三个函数init()、h()、thunk()

      • init()是一个高阶函数,返回patch()

      • h()返回虚拟节点VNode,这个函数我们在使用Vuejs的时候见过

        new Vue({
            router,
            store,
            render: h => h(App)
        }).$mount('#app')
        
      • thunk()是一种优化策略,可以在处理不可变数据时使用

    • 注意:导入不能使用import snabbdom from 'snabbdom'

      • 原因:末尾到处使用的语法时export到处API,没有使用export default导出默认输出

        export {h} from './h'
        export {thunk} from './thunk'
            
        export function init(modules: Array<Partial<Module>>, domApi?: DOMAPI){
        }
        

    Snabbdom代码演示

    1. 1.hello world
    //main.js
    import {h, init} from 'snabbdom'
    
    /**
    * 1.hello world
    * 参数:数组、模块
    * 返回值:patch函数,作用对比两个vnode的差异更新到真实DOM
    */
    let patch = init([])
    /**
    * 第一个参数:标签+选择器
    * 第二个参数:如果是字符串的话就是标签中内容
    */
    let vnode = h('div#container.cls', 'hello world')
    
    let app = document.querySelector('#app')
    /**
    * 第一个参数:可以是DOM元素,内部会把DOM元素转换为VNode
    * 第二个参数:VNode
    * 返回值:VNode
    */
    let oldVnode = patch(app, vnode)
    
    //假设的时刻:覆盖之前的元素
    vnode = h('div', 'hello snabbdom')
    
    patch(oldVnode, vnode)
    
    1. div中放置子元素h1,p
    import { h, init } from 'snabbdom'
    
    let patch = init([])
    
    let vnode = h('div#contatainer', [
        h('h1', 'hello'),
        h('p', 'p标签')
    ])
    
    let app = document.querySelector('#app')
    
    let oldVnode = patch(app, vnode)
    
    setTimeout(() => {
        vnode = h('div#contatainer', [
        h('h1', '22222'),
        h('p', '11111')
    	])
        patch(oldVnode, vnode)
        
        //清空页面元素-错误:无法读取null的key属性
        //patch(oldVnode, null)
        patch(oldVnode, h('!'))
    },2000)
    
    <script src="./src/main.js"></script>
    <!--要引入-->
    

    模块

    • snabbdom的核心库并不能处理元素的属性/样式/事件等,如果需要处理的话,可以使用模块
    • 常用模块:官方提供了6个模块
      1. attributes
        • 设置DOM元素属性,使用setAttribute()
        • 处理布尔类型的属性
      2. props
        • attributes模块相似,设置DOM元素的属性element[attr] = value
        • 不处理布尔类型的属性
      3. class
        • 切换类样式
        • 注意:给元素设置类样式是通过sel选择器
      4. dataset
        • 设置data-*的自定义属性
      5. eventlisteners
        • 注册和移除事件
      6. style
        • 设置行内样式,支持动画
        • delayed/remove/destroy

    模块使用

    • 模块使用步骤:
      • 导入需要的模块
      • init()中注册模块
      • 使用h()函数创建VNode的时候,可以把第二个参数设置为对象,其它参数往后移
    • 代码
    //main.js
    import { init, h } from 'snabbdom'
    
    // 1.导入需要的模块
    import style from 'snabbdom/modules/style'
    import eventlisteners from 'snabbdom/modules/eventlisteners'
    
    // 2.init()中注册模块
    let patch = init([
        style,
        eventlisteners
    ])
    
    // 3.使用h()函数的第二个参数传入模块需要的数据(对象)
    /**以前的h()
    * let vnode = h('div#contatainer', [
    *     h('h1', 'hello'),
    *     h('p', 'p标签')
    * ])
    */
    
    let vnode = h('div', {
        style: {
            backgroundColor: 'red'
        },
        on: {
            click: eventHandler
        }
    },[
        h('h1','hello!'),
        h('p','这是p')
    ])
    function eventHandler() {
        console.log('1')
    }
    
    let app = document.quertSelector('#app')
    patch(app, vnode)
    

    Snabbdom源码解析

    Snabbdom核心

    • 使用h()函数创建JavaScript对象(VNode)描述真实DOM
    • init()设置模块,创建patch()
    • patch()比较新旧两个VNode
    • 把变化的内容更新到真实DOM树上

    h()

    • Vue中的h()函数支持组件机制,但Snabbdom不支持,但一样可以传入选择器

      new Vue({
          router,
          store,
          render: h => h(App)
      }).$mount('#app')
      
      • h()最早适用于hyperscript,使用JavaScript创建超文本
      • Snabbdom中h()不是创建超文本,而是创建VNode
    • 函数重载

      1. 参数个数类型不同的函数
      2. JavaScript中没有重载的概念
      3. typescript中有重载,不过重载的实现还是通过代码调整参数
    • 可以定义两个重名函数,通过参数个数、类型不同来区分

    • 分析:h.ts中重载的几个h()函数

    VNode

    export interface VNode {
        //选择器
        sel: string | undefined;
        //节点数据:属性/样式/事件
        data: VNodeData | undefined;
        //子节点:和text只能互斥
        children: Array<VNode | string> | undefined;
        //记录vnode对应的真实DOM
        elm: Node | undefined;
        //节点中的内容,和children只能互斥
        text: string | undefined;
        //优化用
        key: Key | undefined;
    }
    

    VNode渲染真实DOM

    patch(oldVnode, newVnode )

    • patch()打补丁,把新节点中变化的内容渲染到真实DOM,最后返回新节点,作为下一次处理的旧节点
    • patchVnode():对比新旧节点VNode是否是相同节点
      • 若不是相同节点,删除之前的内容removeVnodes(),重新渲染
      • 如果是相同节点:
        • 判断新的VNode是否有text,如果有并且和oldVnode的text不同,setTextContent()直接更新文本内容
        • updateChildren():如果新的VNode有children,判断子节点是否有变化,判断子节点的过程使用的就是diff算法
    • diff过程只进行同层级比较

    init函数

    • 返回一个patch(),是高阶函数

    patch函数

    • 把vnode渲染成真实dom

    虚拟DOM库Snabbdom源码解析


    本文首发于我的GitHub博客,其它博客同步更新。


    起源地下载网 » 虚拟DOM库Snabbdom源码解析

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元