DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Documenttitle>
head>
<body>
<div id="app">
<input type="text" value="" v-model="user.name"/>
{{user.name}}哈哈{{user.sex}}
<select v-model="selected">
<option value="">请选择学校option>
<option value="a">学校Aoption>
<option value="b">学校Boption>
select>
<div>
<div style="display: inline-block;">性别div>
<div>
<label for="man">男:label>
<input id="man" type="radio" v-model="user.sex" value="man" name="sex">
<label for="woman">女:label>
<input id="woman" type="radio" v-model="user.sex" value="woman" name="sex">
div>
div>
div>
<script src="vm.js">script>
<script>
let vm = new Vue({
el:'#app'
,data:{
user:{
name:'杨文宇'
,sex:'man'
}
,selected:'a'
}
})
script>
body>
html>
class Vue{
constructor(options){
this.$el = Vue.utils.getEl(options.el);
this.$data = options.data;
new Observe(this,this.$data);
new Compile(this,this.$el);
}
static utils = {
getEl(selector){
return selector.nodeType == Node.ELEMENT_NODE ? selector : document.querySelector(selector)
}
,isDirect(attrName){
return /^v-/.test(attrName)
}
,isTxtTpl(node){
return node.nodeType == Node.TEXT_NODE && /^\s*\{\{.*\}\}\s*$/.test(node.textContent)
}
,getVal(data,expOrFn){
return expOrFn.split(".").reduce((data,cur)=>{
return data[cur];
},data);
}
,setVal(data,expOrFn,value){
expOrFn.split(".").reduce((data,cur,index,arr)=>{
if(index == arr.length-1){
return data[cur] = value;
}
return data[cur];
},data);
}
}
}
//劫持类
class Observe{
constructor(vm,data){
this.observer(data);
//创建代理
this.dataProxy(vm,data);
}
//对数据进行劫持
observer(data){
Object.keys(data).forEach(key=>{
this.reactive(data,key,data[key])
})
}
//响应式函数
reactive(obj,key,value){
typeof value == 'object' && this.observer(value)
let _self = this
//为每一个属性创建依赖容器,因为有可能一个属性会被多个地方依赖
, dep = new Dep();
Object.defineProperty(obj,key,{
get(){
//添加订阅者
dep.depend();
return value;
}
,set(newVal){
if(value == newVal){return}
typeof newVal == 'object' && _self.observer(newVal);
value = newVal;
dep.notify();
}
})
}
dataProxy(obj,data){
Object.keys(data).forEach(key=>{
//let value = data[key];
typeof value == 'object' && this.dataProxy(obj,value)
Object.defineProperty(obj,key,{
get(){
return data[key]
}
,set(newVal){
data[key] = newVal;
}
})
})
}
}
class Compile{
constructor(vm,el){
this.el = el;
this.vm = vm;
this.ready();
}
static tagAttrEvent = {
text:['value','input']
,textarea:['value','input']
,checkbox:['checked','change']
,radio:['checked','change']
,'select-one':['value','change']
,'select-multiple':['value','change']
}
//指令集
static directSet = {
model(vm,node,expOrFn){
let tag = Compile.tagAttrEvent[node.type];
new Watcher(vm,expOrFn,newVal=>{
node[tag[0]] = newVal;
});
node[tag[0]] = Vue.utils.getVal(vm.$data,expOrFn);
node.addEventListener(tag[1],e=>{
Vue.utils.setVal(vm.$data,expOrFn,e.target[tag[0]])
})
}
,text(vm,node,expOrFn){
let content = expOrFn.replace(/\{\{(.+?)\}\}/g,(...arg)=>{
new Watcher(vm,arg[1],newVal=>{
node.textContent = this.getContentVal(vm,expOrFn);
})
return Vue.utils.getVal(vm.$data,arg[1])
})
node.textContent = content;
}
,getContentVal(vm,expOrFn){
return expOrFn.replace(/\{\{(.+?)\}\}/g,(...arg)=>{
let value = Vue.utils.getVal(vm.$data,arg[1])
return typeof value == 'object'? JSON.stringify(value) : value;
})
}
}
ready(){
let fragment = this.node2fragment(this.el)
this.compiler(fragment);
this.el.appendChild(fragment);
}
//编译
compiler(mountEl){
let childNodes = [...mountEl.childNodes];
childNodes.forEach(node=>{
//获取不是文本节点或不是空文本的节点
if(node.nodeType == Node.ELEMENT_NODE){
[...node.attributes].forEach(attr=>{
let {name,value:expOrFn} = attr;
if(!Vue.utils.isDirect(name)){return}
Compile.directSet[name.split('-')[1]](this.vm,node,expOrFn)
})
}
if(Vue.utils.isTxtTpl(node)){
Compile.directSet['text'](this.vm,node,node.textContent)
}
node.childNodes.length > 0 && this.compiler(node)
})
}
//将节点放到文档碎片流中
node2fragment(el){
let fragment = new DocumentFragment()
, child;
while(child = el.firstChild){
fragment.appendChild(child)
}
return fragment;
}
}
/**
* 依赖收集--->收集的就是订阅对象
*/
class Dep{
static target = null;
constructor(){
this.subs = [];
}
//添加订阅者
//谁用到这个数据,谁就是订阅者,反映在html中就是使用数据的这个dom元素
addSub(watcher){
this.subs.push(watcher)
}
depend(){
Dep.target && this.subs.indexOf(Dep.target)<=-1 && this.addSub(Dep.target)
}
//通知所有订阅者
notify(){
this.subs.forEach(sub=>{
sub.update();
})
}
}
/**
* 谁用到这个数据,谁依赖这个数据,谁就是订阅者
* 订阅者-->更新视图
*/
class Watcher{
constructor(vm,expOrFn,cb){
this.vm = vm;
this.expOrFn = expOrFn;
this.cb = cb;
this.oldVal = this.get()
}
get(){
Dep.target = this;
let value = Vue.utils.getVal(this.vm.$data,this.expOrFn);
Dep.target = null;
return value;
}
//更新视图
update(){
let value = Vue.utils.getVal(this.vm.$data,this.expOrFn);
if(this.oldVal != value){
this.cb(value);
this.oldVal = value
}
}
}