JavaScriptは簡単なMVVMフロントエンドフレームワークを実現する(ES 6構文)
6592 ワード
前言
フロントエンドの各フレームワークの台頭に伴い、私たちの普段の開発にかなりの便利さをもたらし、私たちはずっと応用面にとどまることができず、今日は自分で乞食版のMVVMの小さなフレームワークを実現しました.
完全コードgithubアドレス
効果
基本的にはvueの呼び出し方式を模倣します
実装手順データハイジャックObserve データエージェント(Mvvmオブジェクトがデータを処理できるようにする) テンプレートコンパイルCompile 発行購読 ビューデータに関連付ける 双方向データバインディング を実現する.
コード解析
小結
コードにはすでに詳細なコメントが書かれていますが、理解しにくい点があるかもしれません.このときはもっと練習して、MVVMの原理をもっと熟知させるかもしれません.
フロントエンドの各フレームワークの台頭に伴い、私たちの普段の開発にかなりの便利さをもたらし、私たちはずっと応用面にとどまることができず、今日は自分で乞食版のMVVMの小さなフレームワークを実現しました.
完全コードgithubアドレス
効果
MVVM
{{a}}
{{b.b}}
const vm = new Mvvm({
el: '#app',
data: {
a: 1,
b: { b : 2 }
}
})
基本的にはvueの呼び出し方式を模倣します
実装手順
コード解析
// mvvm.js
// Mvvm, new Mvvm()
class Mvvm {
constructor(options){
/**
* options
* {
el: '#app',
data: {
a: 1,
b: { b : 2 }
}
}
*/
const {el,data} = options;
this._data = data;
Observe.observeData(data); // data
this.mount(data); // data this, Mvvm
Mvvm.compile(el,this); // , HTML {{a}} {{b.b}}
}
// data this
mount(data){
// data defineProperty this
for(let key in data){
Object.defineProperty(this,key,{
enumerable:true, //
get(){
return this._data[key];
},
set(newVal){
this._data[key] = newVal;
}
})
}
}
//
static compile(el,_that){
new Compile(el,_that);
}
}
//
class Observe{
constructor(data){
this.deepObserve(data);
}
deepObserve(data){
let dep = new Dep(); //
for(let key in data){
let value = data[key];
Observe.observeData(value); //
this.mount(data,key,value,dep); //
}
}
mount(data,key,value,dep){
// data defineProperty
Object.defineProperty(data,key,{
enumerable:true,
get(){
Dep.target && dep.addSub(Dep.target); //Dep.target = watcher ,
return value; // get
},
set(newVal){
// ,
if(newVal === value){
return;
}
value = newVal;
Observe.observeData(newVal);//
dep.notify(); //
}
})
}
static observeData(data){
// , !! mvvm
if(typeof data !== 'object'){
return ;
}
return new Observe(data);
}
}
class Compile{
constructor(el,vm){
// vm = this
vm.$el = document.querySelector(el);
//
let fragment = document.createDocumentFragment();
let child;
while(child = vm.$el.firstChild){
// DOM, ( )
fragment.appendChild(child);
}
// replace HTML
this.replace(fragment,this,vm);
// DOM,
vm.$el.appendChild(fragment);
}
// DOM
replace(fragment,that,vm){
// DOM
Array.from(fragment.childNodes).forEach(function (node) {
let text = node.textContent; //
let reg = /\{\{(.*)\}\}/; // {{}}
// nodeType === 3
if(node.nodeType === 3 && reg.test(text)){
let arr = RegExp.$1.split('.'); // RegExp.$1 b.b , .
let val = vm; // val vm
arr.forEach(function (k) {
val = val[k]; // vm['b']
});
// node watcher ,
new Watcher(vm,RegExp.$1,function (newVal) {
node.textContent = text.replace(reg,newVal);
});
// {{a}} ==> 1
node.textContent = text.replace(reg,val);
}
//
if(node.nodeType === 1){
let nodeAttrs = node.attributes; // DOM
//
Array.from(nodeAttrs).forEach((attr)=>{
let name = attr.name; // v-model
let exp = attr.value; // "a"
if(name.startsWith('v-')){
node.value = vm[exp]; // a input
}
// node watcher ,
new Watcher(vm,exp,function (newVal) {
node.value = newVal; //
});
//
node.addEventListener('input',function (e) {
// set , dep.notify()
vm[exp] = e.target.value;
},false);
})
}
// DOM
if(node.childNodes){
that.replace(node,that,vm);
}
});
}
}
//
class Dep{
constructor(){
this.subs = [];
}
addSub(sub){
this.subs.push(sub);
}
notify(){
this.subs.forEach(sub=>{
sub.update();
})
}
}
// Watcher , node 。 node Watcher ,
class Watcher{
constructor(vm,exp,fn){
this.vm = vm; // this
this.exp = exp; //
this.fn = fn; //
Dep.target = this; // Dep, target = this watcher
let val = vm;
let arr = exp.split('.');
arr.forEach(function (k) {
val = val[k]; // get, watcher
});
Dep.target = null;
}
// watcher update ,
update(){
// this , , watcher , node ,
let val = this.vm;
let arr = this.exp.split('.');
arr.forEach(function (k) {
val = val[k];
});
this.fn(val); // val
}
}
小結
コードにはすでに詳細なコメントが書かれていますが、理解しにくい点があるかもしれません.このときはもっと練習して、MVVMの原理をもっと熟知させるかもしれません.