效果图:
页面代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div id="app">
<h2>{{title}}</h2>
<input v-model="name">
<h1>{{name}}</h1>
<button v-on:click="clickMe">click me!</button>
<p>aaaa{{xxx}}zzzz</p>
</div>
<script type="text/javascript" src="./vue.js"></script>
<script type="text/javascript">
var selfVue = new Vue({
el: '#app',
data: {
title: 'hello world',
name: 'null',
xxx: 'cjh'
},
methods: {
clickMe: function() {
this.title = 'hello world';
}
},
mounted: function() {
window.setTimeout(() => {
this.title = '你好';
}, 2000);
}
});
</script>
<!-- <h1 id="name">{{name}}</h1> -->
</body>
</html>
<script>
vue.js 代码
//劫持监听数据
function observe(data) {
if (typeof data !== 'object') {
throw '参数错误'
}
Object.keys(data).forEach(key => {
defineReactive(data, key, data[key])
})
}
//监听所有属性
function defineReactive(data, key, value) {
if (typeof value == "object") {
observe(value)
}
let dep = new Dep()
Object.defineProperty(data, key, {
enumerable: true, // 可枚举
configurable: false, // 可配置
get: function() {
if (Dep.target) {
dep.add(Dep.target)
}
return value
},
set: function(target) {
value = target;
dep.notify()
},
})
}
//订阅器容器
class Dep {
static targer = null
constructor() {
this.sub = []
}
add(data) {
this.sub.push(data)
}
notify() {
this.sub.forEach(e => {
e.updata()
})
}
}
class Watcher{
constructor(vm,exp,cb) {
this.vm=vm
this.exp=exp
this.cb=cb
this.value=this.get()
}
updata(){
this.run()
}
run(){
let oldValue=this.value
let value=this.vm[this.exp]
if(value!==oldValue){
this.value=value
this.cb.call(this.vm,value,oldValue)
}
}
get(){
Dep.target=this
let value=this.vm.data[this.exp]
Dep.target=null
return value
}
}
class Compile{
constructor(el,vm){
this.vm=vm
this.el=document.querySelector(el)
this.fragment = null;
this.init()
}
init(){
if (this.el) {
this.fragment=this.nodeToFragment(this.el)
this.compileElement(this.fragment);
this.el.appendChild(this.fragment)
} else {
console.log('Dom元素不存在');
}
}
nodeToFragment(el){
let fragment=document.createDocumentFragment()
let child=el.firstChild
while(child){
fragment.appendChild(child)
child=el.firstChild
}
return fragment
}
compileElement(el){
var childNodes = el.childNodes;
var self = this;
Array.prototype.slice.call(childNodes,0).forEach(node=>{
const reg=/\{\{(.*)\}\}/
let text=node.textContent
if(self.isElementNode(node)){
self.compile(node);
}else if(self.isTextNode(node)&®.test(text)){
self.compileText(node,reg.exec(text)[1])
}
if(node.childNodes&&node.childNodes.length){
self.compileElement(node)
}
})
}
compile(node,exp){
let attrs=node.attributes
Array.prototype.map.call(attrs,attr=>{
var attrName = attr.name;
if(this.isDirective(attrName)){
let exp=attr.value
let dir=attr.name.substring(2)
if(this.isEventDirective(attrName)){
this.compileEvent(node, this.vm, exp, dir);
} else { // v-model 指令
this.compileModel(node, this.vm, exp, dir);
}
}
})
}
compileEvent(node, vm, exp, dir){
let cb=vm.methods&&vm.methods[exp]
node.addEventListener(dir,cb.bind(vm),false)
}
compileModel(node, vm, exp, dir){
let val=this.vm[exp];
this.modelUpdatar(node,val)
new Watcher(vm,exp,(value)=>{
this.modelUpdatar(node,value)
})
node.addEventListener('input',(e)=>{
let newValue=e.target.value
if(newValue!==val){
val=newValue
this.vm[exp]=newValue
}
})
}
compileText(node,exp){
let self=this
var initText = this.vm[exp];
this.updataText(node, initText); // 将初始化的数据初始化到视图中
new Watcher(this.vm,exp,(value)=>{
this.updataText(node, value); // 将初始化的数据初始化到视图中
})
}
//更新文本
updataText(node, value) {
node.textContent = typeof value == 'undefined' ? '' : value;
}
//更新模块
modelUpdatar(node, value, oldValue) {
node.value = typeof value == 'undefined' ? '' : value;
}
// 判断是是不是v-指令
isDirective(attr) {
return attr.indexOf('v-') == 0;
}
// 判断是是不是on:事件指令
isEventDirective(dir) {
return dir.indexOf('on:') === 0;
}
isTextNode(node){
return node.nodeType===3
}
isElementNode(node){
return node.nodeType===1
}
}
//ANGLE_instanced_arrays
class Vue {
constructor(data) {
let self = this
this.data = data.data
this.vm=this
this.methods = data.methods
//赋值时,属性的绑定做一层封装
Object.keys(this.data).forEach(function(key) {
self.proxyKeys(key); // 绑定代理属性
});
observe(this.data)
new Compile(data.el, this.vm) //挂载点,实例对象
data.mounted.call(this); // 所有事情处理好后执行mounted函数
return this;
}
proxyKeys(key){
let self = this
Object.defineProperty(this, key, {
enumerable: false,
configurable: true,
get: function proxyGetter() {
return self.data[key];
},
set: function proxySetter(newVal) {
self.data[key] = newVal;
}
});
}
}