vue原理(四)效果图+总结
<div id="container">
{{ msg }}<br>
<input class="inp-text" type="text" v-model="inpText">
<p>{{ inpText }}</p>
<div class="text-box">
<p class="show-text">{{ msg }}</p>
</div>
</div>
javascript:
function Publisher(){
this.subs = [];
}
Publisher.prototype = {
constructor: Publisher,
add: function(sub){
this.subs.push(sub);
},
notify: function(){
this.subs.forEach(function(sub){
sub.update();
});
}
};
function Subscriber(node, vm, name){
Publisher.global = this;
this.node = node;
this.vm = vm;
this.name = name;
this.update();
Publisher.global = null; // 清空
}
Subscriber.prototype = {
constructor: Subscriber,
update: function(){
let vm = this.vm;
let node = this.node;
let name = this.name;
switch(this.node.nodeType){
case 1:
node.value = vm[name];
break;
case 3:
node.nodeValue = vm[name];
break;
default:
break;
}
}
};
function virtualDom(node, data){
let frag = document.createDocumentFragment();
let child;
// 遍历dom节点
while(child = node.firstChild){
compile(child, data);
frag.appendChild(child);
}
return frag;
}
function compile(node, data){
let reg = /\{\{(.*)\}\}/g;
if(node.nodeType === 1){ // 标签
let attr = node.attributes;
for(let i = 0, len = attr.length; i < len; i++){
// console.log(attr[i].nodeName, attr[i].nodeValue);
if(attr[i].nodeName === 'v-model'){
let name = attr[i].nodeValue;
// node.value = data[name];
// ------------------------添加监听事件
node.addEventListener('keyup', function(e){
data[name] = e.target.value;
}, false);
new Subscriber(node, data, name);
}
}
if(node.hasChildNodes()){
node.childNodes.forEach((item) => {
compile(item, data);
});
}
}
if(node.nodeType === 3){ // 文本节点
if(reg.test(node.nodeValue)){
let name = RegExp.$1;
name = name.trim();
// node.nodeValue = data[name];
new Subscriber(node, data, name);
}
}
}
function defineReact(data, key, value){
let publisher = new Publisher();
Object.defineProperty(data, key, {
set: function(newValue){
console.log(`触发setter`);
value = newValue;
console.log(value);
publisher.notify(); // 发布订阅
},
get: function(){
console.log(`触发getter`);
if(Publisher.global){
publisher.add(Publisher.global); // 添加订阅者
}
return value;
}
});
}
// 将data中数据绑定到vm实例对象上
function observe(data, vm){
Object.keys(data).forEach((key) => {
defineReact(vm, key, data[key]);
})
}
function Vue(options){
this.data = options.data;
let id = options.el;
observe(this.data, this); // 将每个data属相绑定到Vue的实例vm上
//------------------------
let container = document.getElementById(id);
let fragment = virtualDom(container, this); // 这里通过vm对象初始化
container.appendChild(fragment);
}
var vm = new Vue({
el: 'container',
data: {
msg: 'Hello world!',
inpText: 'Input text'
}
});
总结:
倘若用一句话来概括vue,那么我首先想到的便是官方文档中的一句话:
Vue.js(读音 /vjuː/,类似于 view) 是一套构建用户界面的渐进式框架。
这句话可能大家并不陌生,但是真正理解这句话的可能并不多,其实,读懂了这句话,也就明白了vue的核心理念.
那么,怎样理解什么是渐进式框架?在这之前,我们首先要理解什么是框架.在最初的前端开发中,为了完成某个功能,我们需要通过js在HTML页面中获得dom节点,随后获得dom节点中的文本内容或者在dom节点上添加事件,进行一系列的程序操作,但是,如果任务量很大的情况下,代码会随着业务的增加而变得臃肿和混乱,在现实的开发中,负责的逻辑和巨大的开发量,是原生js无法完成的.
这个时候,开发人员将js代码分为了三个板块,数据(Model),逻辑控制(*),视图(View),数据板块只负责数据部分,视图板块负责更改样式,逻辑控制负责联系视图板块和数据板块,这样子有很大的好处,当需求发生变动时,只需要修改对应的板块就好
这种开发模式,就是所谓的MV*结构,我们现在了解的MVC,MVP,MVVM都是MV*的衍生物,对比这几种框架模式,我们会总结出来一个本质的特点,那就是这些开发模式都是让视图和数据间不会发生直接联系.对比用原生JS获得dom的操作,你会发现原生dom流其实是将dom作为数据,从dom中获得Model,随后又更改dom来实现更新视图,视图和模型其实混在一起,所以代码自然混乱,不易维护.
在具有响应式系统的Vue实例中,DOM状态只是数据状态的一个映射 即 UI=VM(State) ,当等式右边State改变了,页面展示部分UI就会发生相应改变。很多人初次上手Vue时,觉得很好用,原因就是这个.不过,Vue的核心定位并不是一个框架,设计上也没有完全遵循MVVM模式,可以看到在图中只有State和View两部分, Vue的核心功能强调的是状态到界面的映射,对于代码的结构组织并不重视, 所以单纯只使用其核心功能时,它并不是一个框架,而更像一个视图模板引擎,这也是为什么Vue开发者把其命名成读音类似于view的原因。
上文提到,Vue的核心的功能,是一个视图模板引擎,但这不是说Vue就不能成为一个框架。如下图所示,这里包含了Vue的所有部件,在声明式渲染(视图模板引擎)的基础上,我们可以通过添加组件系统、客户端路由、大规模状态管理来构建一个完整的框架。更重要的是,这些功能相互独立,你可以在核心功能的基础上任意选用其他的部件,不一定要全部整合在一起。可以看到,所说的“渐进式”,其实就是Vue的使用方式,同时也体现了Vue的设计的理念.
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构