function baseCompile(
template: string,
options: CompilerOptions
) {
const ast = parse(template.trim(), options) // 1.将模板转化成ast语法树
if (options.optimize !== false) { // 2.优化树
optimize(ast, options)
}
const code = generate(ast, options) // 3.生成树
return {
ast,
render: code.render,
staticRenderFns: code.staticRenderFns
}
})
const ncname = `[a-zA-Z_][\\-\\.0-9_a-zA-Z]*`;
const qnameCapture = `((?:${ncname}\\:)?${ncname})`;
const startTagOpen = new RegExp(`^<${qnameCapture}`); // 标签开头的正则 捕获的内容是
标签名
const endTag = new RegExp(`^<\\/${qnameCapture}[^>]*>`); // 匹配标签结尾的
const VueTemplateCompiler = require('vue-template-compiler');
let r1 = VueTemplateCompiler.compile(`hello`);
/**
with(this) {
return (true) ? _c('div', _l((3), function (i) {
return _c('span', [_v("hello")])
}), 0) : _e()
}
*/
const VueTemplateCompiler = require('vue-template-compiler');
let r2 = VueTemplateCompiler.compile(``);
/**
with(this) {
return _c('div', {
directives: [{
name: "show",
rawName: "v-show",
value: (true),
expression: "true"
}]
})
}
*/
// v-show 操作的是样式 定义在platforms/web/runtime/directives/show.js
bind(el: any, { value }: VNodeDirective, vnode: VNodeWithData) {
vnode = locateNode(vnode)
const transition = vnode.data && vnode.data.transition
const originalDisplay = el.__vOriginalDisplay =
el.style.display === 'none' ? '' : el.style.display
if (value && transition) {
vnode.data.show = true
enter(vnode, () => {
el.style.display = originalDisplay
})
} else {
el.style.display = value ? originalDisplay : 'none'
}
}
const VueTemplateCompiler = require('vue-template-compiler');
let r1 = VueTemplateCompiler.compile(`hello`);
/**
with(this) {
return _l((3), function (i) {
return (false) ? _c('div', [_v("hello")]) : _e()
})
}
*/
console.log(r1.render);
function $createElement(tag,data,...children){
let key = data.key;
delete data.key;
children = children.map(child=>{
if(typeof child === 'object'){
return child
}else{
return vnode(undefined,undefined,undefined,undefined,child)
}
})
return vnode(tag,props,key,children);
}
export function vnode(tag,data,key,children,text){
return {
tag, // 表示的是当前的标签名
data, // 表示的是当前标签上的属性
key, // 唯一表示用户可能传递
children,
text
}
}
两个树的完全的 diff 算法是一个时间复杂度为 O(n3) , Vue 进行了优化·O(n3) 复杂度的问题转换成
O(n) 复杂度的问题(只比较同级不考虑跨级问题) 在前端当中, 你很少会跨越层级地移动Dom元素。 所
以 Virtual Dom只会对同一个层级的元素进行对比。
const oldCh = oldVnode.children // 老的儿子
const ch = vnode.children // 新的儿子
if (isUndef(vnode.text)) {
if (isDef(oldCh) && isDef(ch)) {
// 比较孩子
if (oldCh !== ch) updateChildren(elm, oldCh, ch, insertedVnodeQueue,
removeOnly)
} else if (isDef(ch)) { // 新的儿子有 老的没有
if (isDef(oldVnode.text)) nodeOps.setTextContent(elm, '')
addVnodes(elm, null, ch, 0, ch.length - 1, insertedVnodeQueue)
} else if (isDef(oldCh)) { // 如果老的有新的没有 就删除
removeVnodes(oldCh, 0, oldCh.length - 1)
} else if (isDef(oldVnode.text)) { // 老的有文本 新的没文本
nodeOps.setTextContent(elm, '') // 将老的清空
}
} else if (oldVnode.text !== vnode.text) { // 文本不相同替换
nodeOps.setTextContent(elm, vnode.text)
}
function updateChildren(parentElm, oldCh, newCh, insertedVnodeQueue,
removeOnly) {
let oldStartIdx = 0
let newStartIdx = 0
let oldEndIdx = oldCh.length - 1
let oldStartVnode = oldCh[0]
let oldEndVnode = oldCh[oldEndIdx]
let newEndIdx = newCh.length - 1
let newStartVnode = newCh[0]
let newEndVnode = newCh[newEndIdx]
let oldKeyToIdx, idxInOld, vnodeToMove, refElm
// removeOnly is a special flag used only by
// to ensure removed elements stay in correct relative positions
// during leaving transitions
const canMove = !removeOnly
if (process.env.NODE_ENV !== 'production') {
checkDuplicateKeys(newCh)
}
while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) {
if (isUndef(oldStartVnode)) {
oldStartVnode = oldCh[++oldStartIdx] // Vnode has been moved left
} else if (isUndef(oldEndVnode)) {
oldEndVnode = oldCh[--oldEndIdx]
} else if (sameVnode(oldStartVnode, newStartVnode)) {
patchVnode(oldStartVnode, newStartVnode, insertedVnodeQueue, newCh,
newStartIdx)
oldStartVnode = oldCh[++oldStartIdx]
newStartVnode = newCh[++newStartIdx]
} else if (sameVnode(oldEndVnode, newEndVnode)) {
patchVnode(oldEndVnode, newEndVnode, insertedVnodeQueue, newCh,
newEndIdx)
oldEndVnode = oldCh[--oldEndIdx]
newEndVnode = newCh[--newEndIdx]
} else if (sameVnode(oldStartVnode, newEndVnode)) { // Vnode moved right
patchVnode(oldStartVnode, newEndVnode, insertedVnodeQueue, newCh,
newEndIdx)
canMove && nodeOps.insertBefore(parentElm, oldStartVnode.elm,
nodeOps.nextSibling(oldEndVnode.elm))
oldStartVnode = oldCh[++oldStartIdx]
newEndVnode = newCh[--newEndIdx]
} else if (sameVnode(oldEndVnode, newStartVnode)) { // Vnode moved left
patchVnode(oldEndVnode, newStartVnode, insertedVnodeQueue, newCh,
newStartIdx)
canMove && nodeOps.insertBefore(parentElm, oldEndVnode.elm,
oldStartVnode.elm)
oldEndVnode = oldCh[--oldEndIdx]
newStartVnode = newCh[++newStartIdx]
} else {
if (isUndef(oldKeyToIdx)) oldKeyToIdx = createKeyToOldIdx(oldCh,
oldStartIdx, oldEndIdx)
idxInOld = isDef(newStartVnode.key)
? oldKeyToIdx[newStartVnode.key]
: findIdxInOld(newStartVnode, oldCh, oldStartIdx, oldEndIdx)
if (isUndef(idxInOld)) { // New element
createElm(newStartVnode, insertedVnodeQueue, parentElm,
oldStartVnode.elm, false, newCh, newStartIdx)
} else {
vnodeToMove = oldCh[idxInOld]
if (sameVnode(vnodeToMove, newStartVnode)) {
patchVnode(vnodeToMove, newStartVnode, insertedVnodeQueue, newCh,
newStartIdx)
oldCh[idxInOld] = undefined
canMove && nodeOps.insertBefore(parentElm, vnodeToMove.elm,
oldStartVnode.elm)
} else {
// same key but different element. treat as new element
createElm(newStartVnode, insertedVnodeQueue, parentElm,
oldStartVnode.elm, false, newCh, newStartIdx)
}
}
newStartVnode = newCh[++newStartIdx]
}
}
if (oldStartIdx > oldEndIdx) {
refElm = isUndef(newCh[newEndIdx + 1]) ? null : newCh[newEndIdx + 1].elm
addVnodes(parentElm, refElm, newCh, newStartIdx, newEndIdx,
insertedVnodeQueue)
} else if (newStartIdx > newEndIdx) {
removeVnodes(oldCh, oldStartIdx, oldEndIdx)
}
}
function VueComponent(){}
VueComponent.prototype.$options = {
data:{name:'zf'}
}
let vc1 = new VueComponent();
vc1.$options.data = 'jw';
let vc2 = new VueComponent();
console.log(vc2.$options.data);
同一个组件被复用多次,会创建多个实例。这些实例用的是同一个构造函数,如果 data 是一个对象的
话。那么所有组件都共享了同一个对象。为了保证组件的数据独立性要求每个组件必须通过 data 函数
返回一个对象作为组件的状态。
Sub.options = mergeOptions(
Super.options,
extendOptions
)
function mergeOptions() {
function mergeField(key) {
const strat = strats[key] || defaultStrat
options[key] = strat(parent[key], child[key], vm, key)
}
}
strats.data = function (
parentVal: any,
childVal: any,
vm?: Component
): ?Function {
if (!vm) { // 合并是会判断子类的data必须是一个函数
if (childVal && typeof childVal !== 'function') {
process.env.NODE_ENV !== 'production' && warn(
'The "data" option should be a function ' +
'that returns a per-instance value in component ' +
'definitions.',
vm
)
return parentVal
}
return mergeDataOrFn(parentVal, childVal)
}
return mergeDataOrFn(parentVal, childVal, vm)
}
Vue 的事件绑定分为两种一种是原生的事件绑定,还有一种是组件的事件绑定,
let compiler = require('vue-template-compiler');
let r1 = compiler.compile('');
let r2 = compiler.compile('' );
console.log(r1); // {on:{click}}
console.log(r2); // {nativeOnOn:{click},on:{click}}
function updateDOMListeners (oldVnode: VNodeWithData, vnode: VNodeWithData) {
if (isUndef(oldVnode.data.on) && isUndef(vnode.data.on)) {
return
}
const on = vnode.data.on || {}
const oldOn = oldVnode.data.on || {}
target = vnode.elm
normalizeEvents(on)
updateListeners(on, oldOn, add, remove, createOnceHandler, vnode.context)
target = undefined
}
function add (
name: string,
handler: Function,
capture: boolean,
passive: boolean
) {
target.addEventListener( // 给当前的dom添加事件
name,
handler,
supportsPassive
? { capture, passive }
: capture
)
}
vue 中绑定事件是直接绑定给真实 dom 元素的
export function updateComponentListeners (
vm: Component,
listeners: Object,
oldListeners: ?Object
) {
target = vm
updateListeners(listeners, oldListeners || {}, add, remove, createOnceHandler,
vm)
target = undefined
}
function add (event, fn) {
target.$on(event, fn)
}
组件绑定事件是通过 vue 中自定义的 $on 方法来实现的
后续文章: 专题三.