通过学习vue2响应式,我写的不知道为什么,直接通过数组下标赋值它也是响应式的。下面是源码,你们可以试试。
直接复制新建一个html通过控制台测试
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script crossorigin="anonymous" integrity="sha512-pSyYzOKCLD2xoGM1GwkeHbdXgMRVsSqQaaUoHskx/HF09POwvow2VfVEdARIYwdeFLbu+2FCOTRYuiyeGxXkEg==" src="https://lib.baomitu.com/vue/2.6.14/vue.js"></script>
</head>
<body>
<div id="app">
<div>
<p class="a1" style="color:red">{{name}}</p>
<input id="name"/>
</div>
<p>{{b}}</p>
</div>
<script>
const CE=['push','pop'];
let array=Object.create(Array.prototype)
CE.forEach(method=>{
array[method]=function(){
[...arguments].forEach(i=>{
reactive(i)});
return Array.prototype[method].apply(this,arguments);
}
});
function definReactive(target,key,value){
if(Array.isArray(value)){
value.__proto__=array;
reactive(value)
}
if(value instanceof Object){
reactive(value)
}
Object.defineProperty(target,key,{
enumerable:true,
configurable:true,
get(){
return value;
},
set(newVal){
if(typeof newVal =='object' && newVal!=null)
reactive(newVal);
console.log(`响应式${target[key]},${value},新的值${newVal}`);
value=newVal
}
});
}
function reactive(data){
Object.keys(data).forEach((key)=>{
definReactive(data,key,data[key])
});
}
// 为什么使用虚拟dom
// 提升性能
// 生成虚拟节点
class VNode{
constructor(tag,attr,value,type){
this.tag=tag;
this.attrs=attr;
this.value=value;
this.type=type;
this.children=[];
}
appendChild(vnode){
this.children.push(vnode);
}
}
function createVNode(node){
let vnode;
if(node.nodeType==3){
vnode= new VNode(undefined,undefined,node.nodeValue,node.nodeType);
}else if(node.nodeType==1){
let attrObj={};
for (let i = 0; i < node.attributes.length; i++) {
attrObj[ node.attributes[i].nodeName ] = node.attributes[i].nodeValue;
}
vnode=new VNode(node.nodeName,attrObj,undefined,node.nodeType);
// 考虑到有子节点
node.childNodes.forEach(i => {
vnode.appendChild(createVNode(i))
});
}
return vnode;
}
// let vnode=createVNode(document.querySelector('#app'))
// // 将虚拟dom转换成真实dom
function parseVNode(vnode){
// 3是值,1是元素
if(vnode.type==3){
return document.createTextNode(vnode.value);
}else if(vnode.type==1){
let el=document.createElement(vnode.tag);
for (const key in vnode.attrs) {
el.setAttribute(key,vnode.attrs[key]);
}
vnode.children.forEach((i)=>{
el.appendChild(parseVNode(i));
});
return el
}
}
// let t=parseVNode(vnode)
// console.log(t);
// let a= create(document.querySelector('#app'))
// parseVNode(a)
class MyVue{
constructor(options){
this._data=options.data
this._template=document.querySelector(options.el)
this._parent=this._template.parentNode
this.initData(this._data);
this.mount();
}
initData(){
reactive(this._data);
Object.keys(this._data).forEach(key=>{
Object.defineProperty(this,key,{
enumerable:true,
configurable:true,
get(){
return this._data[key];
},
set(newVal){
if(typeof newVal =='object' && newVal!=null)
reactive(newVal);
this._data[key]=newVal
this.mountComponent();
}
});
})
}
mount(){
// 需要提供一个render方法,生成虚拟dom
this.render=this.createRenderFn();
this.mountComponent();
}
mountComponent(){
let mount=()=>{
this.update(this.render())
}
// 本质应该交给watcher来调用
mount();
}
// 这里生成render函数,目的缓存抽象语法树
createRenderFn(){
// 将AST+data => VNode
let ast=createVNode(this._template);
return function(){
// 将带{{}}的vnode和数据结合
return combine(ast,this._data);
}
}
// 将虚拟dom渲染到页面中, diff算法在这里
// 在真正的vue中使用了二次提交的设计结构
update(vnode){
let realDom = parseVNode(vnode)
// 每次会全部替换,模拟的,不是真正的diff算法
this._parent.replaceChild(realDom,document.querySelector('#app'));
}
compile(template){
let childNodes=template.childNodes
for(let i=0;i<childNodes.length;i++){
let e=childNodes[i];
if(e.nodeType==3){
e.nodeValue=e.nodeValue.replace(/\{\{(.*)?\}\}/,(a,b)=>{
let key=b.trim();
if(key.indexOf('.')!=-1){
return MyVue.getValue(key);
}
return this._data[key];
});
}else if(e.nodeType==1){
this.compile(e)
}
}
}
// 递归取出对象里的值
static getValue(key,data){
let arr=key.split('.');
let tmp=data
for (let i = 0; i < arr.length; i++) {
tmp = tmp[arr[i]];
}
return tmp;
}
}
function combine(vnode,data){
let {type,value,tag,children,attrs}=vnode;
let _vnode
if(type==3){
value=value.replace(/\{\{(.*)?\}\}/,(a,b)=>{
let key=b.trim();
if(key.indexOf('.')!=-1){
return MyVue.getValue(key,data);
}
return data[key];
});
_vnode=new VNode(tag,attrs,value,type);
}else if(type==1){
_vnode=new VNode(tag,attrs,value,type);
children.forEach(subchild=>{
_vnode.appendChild(combine(subchild,data))
});
}
return _vnode;
}
let app=new MyVue({
el:'#app',
data:{
name:'zhangsan',
b:5,
c:[1,2,3]
}
})
</script>
</body>
</html>