简要实现Vue
【简要实现Vue】实现:
/*
* Time: 2021/09/09
* Author: tony_cyt
* About: 简要实现Vue
*/class Vue {
/**
* @description: Vue构造函数
* @param {*} options
* @return {*}
*/
constructor(options) {
// 赋值el和data
const { el, data, computed } = options;
this.$el = el;
this.$data = https://www.it610.com/article/data;
// 如果$el存在,就走编译流程
if (this.$el) {
new Observer(this.$data);
for (const key in computed) {
Object.defineProperty(this.$data, key, {
get: () => {
return computed[key].call(this);
}
})
}
this.proxyData(this.$data);
new Compiler(this.$el, this);
}
}
proxyData(data) {
for (const key in data) {
Object.defineProperty(this, key, {
get: () => {
return data[key];
}
})
}
}
}/**
* @description: 自定义指令集
* @param {*}
* @return {*}
*/
CompilerUtil = {
getVal(vm, expr) {
return expr.split('.').reduce((data, current) => {
return data[current];
}, vm.$data)
},
setVal(vm, expr, value) {
expr.split('.').reduce((data, current, index, arr) => {
if (index === arr.length - 1) {
return data[current] = value;
}
return data[current];
}, vm.$data);
},
model(node, expr, vm) {
const value = https://www.it610.com/article/this.getVal(vm, expr);
const fn = this.updater['modeUpdater'];
new Watcher(vm, expr, newValue => {
fn(node, newValue);
})
node.addEventListener('input', e => {
this.setVal(vm, expr, e.target.value);
})
fn(node, value);
},
getContentValue(vm, expr) {
return expr.replace(/\{\{(.+?)\}\}/g, (...args) => {
return this.getVal(vm, args[1]);
})
},
text(node, expr, vm) {
const fn = this.updater['textUpdater'];
const content = expr.replace(/\{\{(.+?)\}\}/g, (...args) => {
new Watcher(vm, args[1], () => {
fn(node, this.getContentValue(vm, expr));
})
return this.getVal(vm, args[1]);
})
fn(node, content);
},
html() {},
updater: {
modeUpdater(node, value) {
node.value = https://www.it610.com/article/value;
},
textUpdater(node, value) {
node.textContent = value;
},
htmlUpdater() {}
}
}class Compiler {
/**
* @description: 编译器构造函数
* @param {*} el
* @param {*} vm
* @return {*}
*/
constructor(el, vm) {
this.vm = vm;
this.el = this.isElementNode(el) ? el : document.querySelector(el);
const fragment = this.nodeToFragment(this.el);
this.compile(fragment);
this.el.append(fragment);
}
/**
* @description: 判断是不是元素节点
* @param {*} node
* @return {*}
*/
isElementNode(node) {
return node.nodeType === 1;
}
/**
* @description: 生成文档片段
* @param {*} node
* @return {*}
*/
nodeToFragment(node) {
const fragment = document.createDocumentFragment();
let firstChild;
while (firstChild = node.firstChild) {
fragment.append(firstChild);
}
return fragment;
}
/**
* @description: 根据类型编译
* @param {*} node
* @return {*}
*/
compile(node) {
[...node.childNodes].forEach(child => {
if (this.isElementNode(child)) {
this.compileElement(child);
// 递归节点遍历
this.compile(child);
} else {
this.compileText(child);
}
})
}
/**
* @description: 编译文本节点
* @param {*} node
* @return {*}
*/
compileText(node) {
const reg = /\{\{(.+?)\}\}/;
if (reg.test(node.textContent)) {
CompilerUtil['text'](node, node.textContent, this.vm);
}
}
/**
* @description: 判断是否是指令
* @param {*} attrName
* @return {*}
*/
isDirective(attrName) {
return attrName.startsWith('v-');
}
/**
* @description: 编译元素节点
* @param {*} node
* @return {*}
*/
compileElement(node) {
[...node.attributes].forEach(attr => {
const { name, value: expr } = attr;
if (this.isDirective(name)) {
const [, directive] = name.split('-');
CompilerUtil[directive](node, expr, this.vm);
}
})
}
}/**
* @description: 实现数据响应式
* @param {*}
* @return {*}
*/
class Observer {
constructor(data) {
this.observer(data);
}
observer(data) {
if (data && typeof data =https://www.it610.com/article/=='object') {
for (const key in data) {
this.defineReactive(data, key, data[key]);
}
}
}
defineReactive(obj, key, value) {
this.observer(value);
const dep = new Dep();
Object.defineProperty(obj, key, {
get() {
Dep.target && dep.subs.push(Dep.target);
return value;
},
set: (newValue) => {
if (newValue !== value) {
this.observer(newValue);
value = https://www.it610.com/article/newValue;
dep.notify();
}
}
})
}
}/**
* @description: 存储观察者
* @param {*}
* @return {*}
*/
class Dep {
constructor() {
this.subs = [];
}
addSub(watcher) {
this.subs.push[watcher];
}
notify() {
this.subs.forEach(watcher => {
watcher.update();
})
}
}class Watcher {
constructor(vm, expr, cb) {
this.vm = vm;
this.expr = expr;
this.cb = cb;
this.oldValue = https://www.it610.com/article/this.get();
}
get() {
Dep.target = this;
const value = CompilerUtil.getVal(this.vm, this.expr);
Dep.target = null;
return value;
}
update() {
const newValue = CompilerUtil.getVal(this.vm, this.expr);
if (newValue !== this.oldValue) {
this.cb(newValue);
}
}
}
使用:
MVVM - 锐客网
{{school.name}}
{{school.age}}
{{newName}}
- 1
- 2
效果:
文章图片
推荐阅读
- vue-cli|vue-cli 3.x vue.config.js 配置
- 2020-04-07vue中Axios的封装和API接口的管理
- 关于QueryWrapper|关于QueryWrapper,实现MybatisPlus多表关联查询方式
- MybatisPlus使用queryWrapper如何实现复杂查询
- python学习之|python学习之 实现QQ自动发送消息
- 孩子不是实现父母欲望的工具——林哈夫
- opencv|opencv C++模板匹配的简单实现
- Node.js中readline模块实现终端输入
- java中如何实现重建二叉树
- 人脸识别|【人脸识别系列】| 实现自动化妆