/** * 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 類,但具體用在什麼地方還不清楚