自己封装Vue实例
程序员文章站
2022-06-24 16:50:06
大家好,我是热狗得小舔狗!给大家分享一下,如何自己封装一个Vue实例。看图!!!
- {{count}}
- {{count}}
- {{count}} ...
大家好,我是热狗得小舔狗!
给大家分享一下,如何自己封装一个Vue实例。
看图!!!
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>new vue</title>
</head>
<body>
<div id="app">
<input type="text" name="in" v-model="count">
<ul>
<li><div><p><ul>{{message}}</ul></p></div></li>
<li>{{count}}</li>
<li>--------------</li>
</ul>
{{message}}
</div>
</body>
<script src="js/compile.js"></script>
<script src="js/mvvm.js"></script>
<script type="text/javascript">
let app = new Mvvm({
el: '#app',
data: {
message: 'hello',
count: 1
}
})
</script>
</html>
那么它是如何实现的呢。
我们都知道在vue中实现双向绑定需要
1 模板的编译
2 数据劫持
3 Watcher
这篇主要实现简单模板的编译。
大致思想:使用类的概念,创建实例时new MVVM({…})对里的对象当参数处理。
input等标签通过js获取v-model属性和值,节点,然后把传来的对应data参数渲染出去。
{{message}}通过匹配 获取{{}}里字符,在把传来的对应data参数渲染出去。
- 首先 index.html文件
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>new vue</title>
</head>
<body>
<div id="app">
<input type="text" name="in" v-model="count">
<ul>
<li><div><p><ul>{{message}}</ul></p></div></li>
<li>{{count}}</li>
<li>--------------</li>
</ul>
{{message}}
</div>
</body>
<script src="js/compile.js"></script>
<script src="js/mvvm.js"></script>
<script type="text/javascript">
let app = new Mvvm({
el: '#app',
data: {
message: 'hello',
count: 1
}
})
</script>
</html>
上面代码大家也可以看到,还需要两个js文件。
MVVM.js文件
class Mvvm {
constructor(obj) {
this.$el = obj.el;
this.$data = obj.data;
if(this.$el) {
// 渲染
new Compile(this);
}
}
}
这里就是写一个MVVM类。这里constructor函数有点python的__init__()的味道,在new MVVM()的创建实例时候,会自动调用该函数。options为传入参数{ el: “app”, data: {count: 2} }。
Compile.js
class Compile {
constructor(app) {
// 判断传入是dom整体元素 还是dom的id
// this.el使用dom整体元素
this.el = this.isElementNode(app.$el) ? app.$el : document.querySelector(app.$el);
this.data = app.$data;
this.app = app
if(this.el) {
// 返回内存dom对象
let fragment = this.CreateFragment(this.el);
// 编译内存dom
this.compile(fragment);
// 取出内存dom
// 不然浏览器上就会没有显示
this.el.appendChild(fragment)
}
}
// 创建内存dom
CreateFragment(el) {
let fragment = document.createDocumentFragment();
let firstChild;
while(firstChild = el.firstChild) {
fragment.appendChild(firstChild)
}
return fragment;
}
// 编译
compile(fragment) {
let childNodes = fragment.childNodes;
Array.from(childNodes).forEach(node => {
if(this.isElementNode(node)){
// 元素节点
this.compileElement(node);
// 深度编译
// 因为有标签嵌套
this.compile(node);
}else{
// 文本节点
this.compileText(node);
}
})
}
// 编译元素节点中指令
// ex: v-model
compileElement(node) {
let attrs = node.attributes;
Array.from(attrs).forEach(attr => {
let attrName = attr.name;
if(attrName.includes('v-')) {
let [, type] = attrName.split('-');
let attrValue = attr.value;
CompileUtil[type](node, this.app, attrValue);
}
})
}
// 编译文本中指令
// ex: {{...}}
compileText(node) {
let textContent = node.textContent;
let reg = /\{\{([^}]+)\}\}/g;
if(reg.test(textContent)) {
CompileUtil['text'](node, this.app, textContent);
}
}
isElementNode(node) {
return node.nodeType === 1;
}
}
/**
* 编译工具类
*
* 主要实现vue指令 最终渲染
*/
CompileUtil = {
model(node, app, attrValue) {
node.value = app.$data[attrValue];
},
text(node, app, textContent) {
let value = textContent.replace(/\{\{([^}]+)\}\}/g, (...args) => args[1]).replace(/^\s+|\s+$/g,"");
node.textContent = app.$data[value];
}
}
大家可以直接copy运行一下,我的已经可以跑起来了。不过呢,注意文件路径!!!
下一篇文章将会写一下数据劫持和Watcher。实现最终的效果!
本文地址:https://blog.csdn.net/Nan_gao_0/article/details/111084910