vue2源码简单实现stage1
其实看了很多的vue源码,很多的东西只是脑子会了,能说就是写不出来。vue的源码有很多的边界判断的代码,纯粹的核心原理代码其实并不多,刚好看到一个比较好的vue2源码精简版,自己手动跟着敲一敲,作为记录,希望自己对源码的理解更深一点。Talk is cheap,Show me the code,让我们愉快的开始实现vue2核心代码吧。这一节我们先实现一下如何让虚拟dom转变为真是的dom
mouted主要干了什么?
// vue-0.1.js
const app = new Vue({
render: h => h(App)
});
app.$mount('#app');
我们可以理解挂在的时候就是生成虚拟dom并其转为真实的dom,让后替换掉我们指定的div元素。
;(function(){
function mount(el){
// 生成vdom
var vnode = render();
//转成真实dom并且替换原来的指定的dom
patch(el, vnode)
}
})()
初始虚拟dom
用普通js对象来描述DOM结构,因为不是真实DOM,所以称之为虚拟DOM。虚拟 dom 是相对于浏览器所渲染出来的真实dom而言的
// vue-0.1.js
{
tag: 'div',// 表述标签,如p、sapn
data: {
attrs: {
'class': 'wrapper'
}
},
children: [
tag: "p",
data:{
attrs: {
'class': 'inner'
}
},
children:[
text:'Hello world'
]
text:''
],
text: 'virtual dom', //标签内的文本,
}
// 构造render函数,生成virtual dom,这里为了让代码更加简洁,这里就先直接忽略template模板的的循环结构部分,直接将描述模板写到render里面,生成虚拟dom
// vue-0.1.js
;(function(){
function vnode (tag, data, children, text) {
this.tag = tag;
this.data = data;
this.children = children;
this.text = text;
}
function render () {
return new vnode(
'div',
{
attrs: {
'class': 'wrapper'
}
},
[
new vnode(
'p',
{
attrs: {
'class': 'inner'
}
},
[new vnode(undefined, undefined, undefined, 'Hello world')]
)
],
'virtual dom'
)
}
function mount(el){
// 生成vdom
var vnode = render();
//转成真实dom并且挂载在根元素上
patch(el, vnode)
}
})()
打印上面的vnode
// 通过上面的方法我们已经得到了一个描述以下真实dom的虚拟dom了
<div class= 'wrapper'>
<p class= 'inner'>
'Hello world'
</p>
'virtual dom'
</div>
下面我们要通过createElm将虚拟dom转变为真实的dom元素
// vue-0.1.js
;(function(){
function createElm (vnode) {
var tag = vnode.tag;
var data = vnode.data;
var children = vnode.children;
var text = vnode.text
//生成对应的标签
if (tag !== undefined) {
vnode.elm = document.createElement(tag);
//循环vnode下面的属性
if (data.attrs !== undefined) {
var attrs = data.attrs;
for (var key in attrs) {
vnode.elm.setAttribute(key, attrs[key])
vnode.elm.innerHTML = text!==undefined? text:''
}
}
if (children) {
createChildren(vnode, children)
}
} else {
vnode.elm = document.createTextNode(vnode.text);
}
return vnode.elm;
}
function createChildren (vnode, children) {
for (var i = 0; i < children.length; ++i) {
vnode.elm.appendChild(createElm(children[i]));
}
}
function patch (oldVnode, vnode) {
createElm(vnode)
// virtual node has no `nodeType` property
var isRealElement = oldVnode.nodeType !== undefined ;
//这一判断逻辑也给出了vue的绑定的元素不能是body和document元素
if (isRealElement) {
var parent = oldVnode.parentNode;
if (parent) {
parent.insertBefore(vnode.elm, oldVnode);
parent.removeChild(oldVnode);
}
}
return vnode.elm
}
function vnode (tag, data, children, text) {
this.tag = tag;
this.data = data;
this.children = children;
this.text = text;
}
function render () {
return new vnode(
'div',
{
attrs: {
'class': 'wrapper'
}
},
[
new vnode(
'p',
{
attrs: {
'class': 'inner'
}
},
[new vnode(undefined, undefined, undefined, 'Hello world')]
)
],
'virtual dom'
)
}
function mount(el){
// 生成vdom
var vnode = render();
//转成真实dom并且挂载在根元素上
patch(el, vnode)
}
})()