资讯专栏INFORMATION COLUMN

VueJS源码学习——工具类函数实现(二)

fish / 2118人阅读

摘要:它被当做一个轻量版本的使用,用于存储已排好版的或尚未打理好格式的片段。最大的区别是因为不是真实树的其中一部分,它的变化不会引起树的重新渲染的操作,或者导致性能影响的问题出现。

原文地址
项目地址

工具类
/**
 * Simple bind, faster than native
 *
 * @param {Function} fn
 * @param {Object} ctx
 * @return {Function}
 */

export function bind (fn, ctx) {
  return function (a) {
    var l = arguments.length
    return l
      ? l > 1
        ? fn.apply(ctx, arguments)
        : fn.call(ctx, a)
      : fn.call(ctx)
  }
}

绑定函数,利用 apply 和 call 方法进行 this 的绑定,

/**
 * Check if two values are loosely equal - that is,
 * if they are plain objects, do they have the same shape?
 *
 * @param {*} a
 * @param {*} b
 * @return {Boolean}
 */

export function looseEqual (a, b) {
  /* eslint-disable eqeqeq */
  return a == b || (
    isObject(a) && isObject(b)
      ? JSON.stringify(a) === JSON.stringify(b)
      : false
  )
debug 类
import config from "../config"

let warn

if (process.env.NODE_ENV !== "production") {
  const hasConsole = typeof console !== "undefined"
  warn = function (msg, e) {
    if (hasConsole && (!config.silent || config.debug)) {
      console.warn("[Vue warn]: " + msg)
      /* istanbul ignore if */
      if (config.debug) {
        if (e) {
          throw e
        } else {
          console.warn((new Error("Warning Stack Trace")).stack)
        }
      }
    }
  }
}

export { warn }

debug 类有意思的是根据环境是否是生产环境来确定

dom 类 dom 元素插入删除替换操作

用惯了 JQ, 还记得怎么用原生函数添加元素么?


/**
 * Insert el before target
 *
 * @param {Element} el
 * @param {Element} target
 */

export function before (el, target) {
  target.parentNode.insertBefore(el, target)
}

/**
 * Insert el after target
 *
 * @param {Element} el
 * @param {Element} target
 */

export function after (el, target) {
  if (target.nextSibling) {
    before(el, target.nextSibling)
  } else {
    target.parentNode.appendChild(el)
  }
}

/**
 * Remove el from DOM
 *
 * @param {Element} el
 */

export function remove (el) {
  el.parentNode.removeChild(el)
}

/**
 * Prepend el to target
 *
 * @param {Element} el
 * @param {Element} target
 */

export function prepend (el, target) {
  if (target.firstChild) {
    before(el, target.firstChild)
  } else {
    target.appendChild(el)
  }
}

/**
 * Replace target with el
 *
 * @param {Element} target
 * @param {Element} el
 */

export function replace (target, el) {
  var parent = target.parentNode
  if (parent) {
    parent.replaceChild(el, target)
  }
}
元素类的添加删除
/**
 * Add class with compatibility for IE & SVG
 *
 * @param {Element} el
 * @param {Strong} cls
 */

export function addClass (el, cls) {
  if (el.classList) {
    el.classList.add(cls)
  } else {
    var cur = " " + (el.getAttribute("class") || "") + " "
    if (cur.indexOf(" " + cls + " ") < 0) {
      el.setAttribute("class", (cur + cls).trim())
    }
  }
}

/**
 * Remove class with compatibility for IE & SVG
 *
 * @param {Element} el
 * @param {Strong} cls
 */

export function removeClass (el, cls) {
  if (el.classList) {
    el.classList.remove(cls)
  } else {
    var cur = " " + (el.getAttribute("class") || "") + " "
    var tar = " " + cls + " "
    while (cur.indexOf(tar) >= 0) {
      cur = cur.replace(tar, " ")
    }
    el.setAttribute("class", cur.trim())
  }
  if (!el.className) {
    el.removeAttribute("class")
  }
}

对元素的类的操作在老版本只支持 setAttribute 来操作,而随着版本的更新,浏览器开始支持 classList 属性 的 add 和 remove 方法来操作元素的类,但依旧要兼容旧版本的浏览器

DocumentFragment
/**
 * Extract raw content inside an element into a temporary
 * container div
 *
 * @param {Element} el
 * @param {Boolean} asFragment
 * @return {Element}
 */
export function extractContent (el, asFragment) {
  var child
  var rawContent
  /* istanbul ignore if */
  if (
    isTemplate(el) &&
    el.content instanceof DocumentFragment
  ) {
    el = el.content
  }
  if (el.hasChildNodes()) {
    trimNode(el)
    rawContent = asFragment
      ? document.createDocumentFragment()
      : document.createElement("div")
    /* eslint-disable no-cond-assign */
    while (child = el.firstChild) {
    /* eslint-enable no-cond-assign */
      rawContent.appendChild(child)
    }
  }
  return rawContent
}

DocumentFragment 接口表示一个没有父级文件的最小文档对象。它被当做一个轻量版本的 Document 使用,用于存储已排好版的或尚未打理好格式的XML片段。最大的区别是因为DocumentFragment不是真实DOM树的其中一部分,它的变化不会引起DOM树的重新渲染的操作(reflow) ,或者导致性能影响的问题出现。

更多信息可以参考MDN 文档

元素的生命周期相关函数
import { removeWithTransition } from "../transition/index"

....

/**
 * Map a function to a range of nodes .
 *
 * @param {Node} node
 * @param {Node} end
 * @param {Function} op
 */

export function mapNodeRange (node, end, op) {
  var next
  while (node !== end) {
    next = node.nextSibling
    op(node)
    node = next
  }
  op(end)
}

/**
 * Remove a range of nodes with transition, store
 * the nodes in a fragment with correct ordering,
 * and call callback when done.
 *
 * @param {Node} start
 * @param {Node} end
 * @param {Vue} vm
 * @param {DocumentFragment} frag
 * @param {Function} cb
 */

export function removeNodeRange (start, end, vm, frag, cb) {
  var done = false
  var removed = 0
  var nodes = []
  mapNodeRange(start, end, function (node) {
    if (node === end) done = true
    nodes.push(node)
    removeWithTransition(node, vm, onRemoved)
  })
  function onRemoved () {
    removed++
    if (done && removed >= nodes.length) {
      for (var i = 0; i < nodes.length; i++) {
        frag.appendChild(nodes[i])
      }
      cb && cb()
    }
  }
}

两个函数实现的是将一段范围内的元素删除的实现

env 类

env 提供了监测是否是浏览器环境?属于IE? 属于安卓?css transition 和 animation 是否需要 webkit 前缀?

/**
 * Defer a task to execute it asynchronously. Ideally this
 * should be executed as a microtask, so we leverage
 * MutationObserver if it"s available, and fallback to
 * setTimeout(0).
 *
 * @param {Function} cb
 * @param {Object} ctx
 */

export const nextTick = (function () {
  var callbacks = []
  var pending = false
  var timerFunc
  function nextTickHandler () {
    pending = false
    var copies = callbacks.slice(0)
    callbacks = []
    for (var i = 0; i < copies.length; i++) {
      copies[i]()
    }
  }
  /* istanbul ignore if */
  if (typeof MutationObserver !== "undefined") {
    var counter = 1
    var observer = new MutationObserver(nextTickHandler)
    var textNode = document.createTextNode(counter)
    observer.observe(textNode, {
      characterData: true
    })
    timerFunc = function () {
      counter = (counter + 1) % 2
      textNode.data = counter
    }
  } else {
    timerFunc = setTimeout
  }
  return function (cb, ctx) {
    var func = ctx
      ? function () { cb.call(ctx) }
      : cb
    callbacks.push(func)
    if (pending) return
    pending = true
    timerFunc(nextTickHandler, 0)
  }
})()

最后一个关于 nextTick 的实现暂时看不懂

options 类

实现了一个 strats 类,但具体用在什么地方还不清楚

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/85119.html

相关文章

  • VueJS源码学习——工具函数实现

    摘要:原文地址项目地址上一篇遗留的作为版本频繁使用的,在构造函数里以引入类实现了多种过滤方法包括方法的实现其实是对的对象的方法做了一层封装,有意思的是发现发现方法的第二参数和第三个参数,查了才知道后两个参数分别代表要提取的属性以及缩进格式,在这里 原文地址项目地址 上一篇遗留的 filter 作为 1.0 版本频繁使用的 filter,在 构造函数里以 Vue.Options.filters...

    adam1q84 评论0 收藏0
  • 前端资源系列(4)-前端学习资源分享&前端面试资源汇总

    摘要:特意对前端学习资源做一个汇总,方便自己学习查阅参考,和好友们共同进步。 特意对前端学习资源做一个汇总,方便自己学习查阅参考,和好友们共同进步。 本以为自己收藏的站点多,可以很快搞定,没想到一入汇总深似海。还有很多不足&遗漏的地方,欢迎补充。有错误的地方,还请斧正... 托管: welcome to git,欢迎交流,感谢star 有好友反应和斧正,会及时更新,平时业务工作时也会不定期更...

    princekin 评论0 收藏0
  • Vue.js资源分享

    摘要:中文官网英文官网组织发出一个问题之后,不要暂时的离开电脑,如果没有把握先不要提问。珍惜每一次提问,感恩每一次反馈,每个人工作还是业余之外抽出的时间有限,充分准备好应有的资源之后再发问,有利于问题能够高效质量地得到解决。 Vue.js资源分享 更多资源请Star:https://github.com/maidishike... 文章转自:https://github.com/maid...

    vpants 评论0 收藏0
  • 架构师之路

    摘要:因为用户不用在第一次进入应用时下载所有代码,用户能更快的看到页面并与之交互。译高阶函数利用和来编写更易维护的代码高阶函数可以帮助你增强你的,让你的代码更具有声明性。知道什么时候和怎样使用高阶函数是至关重要的。 Vue 折腾记 - (10) 给axios做个挺靠谱的封装(报错,鉴权,跳转,拦截,提示) 稍微改改都能直接拿来用~~~哟吼吼,哟吼吼..... 如何无痛降低 if else 面...

    NikoManiac 评论0 收藏0
  • VueJS源码学习——项目结构&目录

    摘要:所以整个的核心,就是如何实现这三样东西以上摘自囧克斯博客的一篇文章从版本开始这个时候的项目结构如下源码在里面,为打包编译的代码,为打包后代码放置的位置,为测试代码目录。节点类型摘自资源另一位作者关于源码解析 本项目的源码学习笔记是基于 Vue 1.0.9 版本的也就是最早的 tag 版本,之所以选择这个版本,是因为这个是最原始没有太多功能拓展的版本,有利于更好的看到 Vue 最开始的骨...

    ad6623 评论0 收藏0

发表评论

0条评论

fish

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<