/** * 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 的绑定,node
/** * 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 )
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 类有意思的是根据环境是不是生产环境来肯定git
用惯了 JQ, 还记得怎么用原生函数添加元素么?github
/** * 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 方法来操做元素的类,但依旧要兼容旧版本的浏览器web
/** * 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 文档app
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() } } }
两个函数实现的是将一段范围内的元素删除的实现dom
env 提供了监测是不是浏览器环境?属于IE? 属于安卓?css transition 和 animation 是否须要 webkit 前缀?async
/** * 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 的实现暂时看不懂ide
实现了一个 strats 类,但具体用在什么地方还不清楚