首页 / VUE / vue源码讲解 --- 小马哥
vue源码讲解 --- 小马哥
内容导读
互联网集市收集整理的这篇技术教程文章主要介绍了vue源码讲解 --- 小马哥,小编现在分享给大家,供广大互联网技能从业者学习和参考。文章包含6782字,纯文字阅读大概需要10分钟。
内容图文
![vue源码讲解 --- 小马哥](/upload/InfoBanner/zyjiaocheng/1333/3c2d255a3c4343ac94d52bad6147a7f4.jpg)
index.html
<! DOCTYPE html > <!-- saved from url=(0026)https://www.jingjibao.com/ --> < html > < head > < meta http-equiv ="Content-Type" content ="text/html; charset=UTF-8" /> < meta http-equiv ="X-UA-Compatible" content ="IE=edge" /> < meta name ="viewport" content ="width=device-width, initial-scale=1.0, user-scalable=no" /> < style > html, body { width : 100% ; } </ style > </ head > < body > < div id ="app" > < div >{{person.name}} --- {{person.age}}</div><h3>{{person.fav}}</h3><div>{{msg}}</div><ul><li>1</li><li>2</li><li>3</li></ul><div v-text="msg"></div><div v-html="htmlStr"></div><input type="text" v-model="msg"/><button v-on:click="handlClick">点击</button><button @click="handlClick2">点击2</button></div> <script src="Observer.js"></script><script src="Mvue.js"></script><script> let vm =new Mvue({ el: "#app", data: { person: { name: "张三", age: "15", fav: "打架", }, msg: "vue源码解析", htmlStr: "<p>模板解析</p>", }, methods: { handlClick() { console.log("点击事件"); }, handlClick2() { this.$data.person.name ="学不动"; console.log("点击事件2"); console.log(this.$data); }, }, }); </script></body></html>
Mvue.js
class Mvue{ constructor(option){ this.$el = option.el; // 1.是字符串还是节点,有没有值this.$data = option.data; this.$option = option; if(this.$el){ // 1、实现一个数据的观察者new Observer(this.$data) // 2、实现一个数据解析器if(this.$el){ new Compile(this.$el,this) } this.proxyData(this.$data) } console.log(this) } proxyData(data){ for(const key in data){ Object.defineProperty(this, key,{ get(){ return data[key] }, set(newVal){ data[key] = newVal; } }) } } } class Compile{ constructor(el,vm){ this.el = this.isElementNode(el) ? el : document.querySelector(el); this.vm = vm; //1、获取文档碎片化对象,放入内存会减少页面的回流和重绘 const fragment = this.node2Fragment(this.el); //2、编译模板this.compile(fragment) //3、子元素追加到根元素this.el.appendChild(fragment) } compile(fragment){ const childNode = fragment.childNodes; [...childNode].forEach(child=>{ //文本或标签if(this.isElementNode(child)){ // console.log("元素节点",child)this.compileElement(child) }else{ this.compileText(child) // console.log("文本节点",child) } if(child.childNodes && child.childNodes.length){ this.compile(child) } }) } compileElement(node){ const attributes = node.attributes; [...attributes].forEach(attr=>{ const {name , value} = attr; if(this.isDirective(name)){ //是一个指令 const [,directive] = name.split(‘-‘) const [dirName , eventName] = directive.split(‘:‘) console.log(‘dirName-‘+dirName, ‘node-‘+node,‘value-‘+value,‘this.vm-‘+this.vm,‘eventName-‘+eventName ) //更新数据 数据驱动视图 complieUtil[dirName](node, value, this.vm, eventName) //删除有指令的标签上的属性 node.removeAttribute(‘v-‘+directive) }elseif(this.isEventName(name)){ let [, eventName] = name.split(‘@‘); complieUtil[‘on‘](node, value, this.vm, eventName) } }) } isDirective(attrName){ return attrName.startsWith(‘v-‘) } isEventName(attrName){ return attrName.startsWith(‘@‘) } compileText(node){ const content = node.textContent if((/\{\{(.+?)\}\}/).test(content)){ complieUtil[‘text‘](node, content, this.vm) // console.log(content) } } isElementNode(node){ return node.nodeType === 1; } node2Fragment(el){ //创建文档碎片 const f = document.createDocumentFragment() //创建了一虚拟的节点对象,节点对象包含所有属性和方法。 let firstChild; while(firstChild = el.firstChild){ //循环,父节点第一个子节点,定义的firsr ,有值,为真,追加进去 f.appendChild(firstChild) //appendChid的移动性 页面没有,子节点追加到缓存变量中 } return f; } } const complieUtil = { getVal(expr,vm){ return expr.split(‘.‘).reduce((data,currentVal)=>{ // console.log(currentVal)return data[currentVal] },vm.$data) }, setVal(expr,vm, inputVal){ return expr.split(‘.‘).reduce((data,currentVal)=>{ data[currentVal] = inputVal; //新的值// return data[currentVal] },vm.$data) }, getContentVal(expr, vm){ return expr.replace(/\{\{(.+?)\}\}/g, (...args) => { returnthis.getVal(args[1], vm) }) }, text(node, expr, vm){ //expr="msg"// const value = vm.$data[expr]; let value; if(expr.indexOf(‘{{‘) !== -1){ // console.log(‘///////‘) value = expr.replace(/\{\{(.+?)\}\}/g, (...args)=>{ new Watcher(vm, args[1], (newVal)=>{ this.updater.textUpdater(node, this.getContentVal(expr, vm)) }) returnthis.getVal(args[1],vm) }) }else{ value = this.getVal(expr, vm) } this.updater.textUpdater(node, value) }, html(node, expr, vm){ const value = this.getVal(expr, vm); new Watcher(vm, expr, (newVal)=>{ this.updater.htmlUpdater(node, newVal) }) this.updater.htmlUpdater(node, value) }, model(node, expr, vm){ const value = this.getVal(expr, vm); //绑定更新函数 数据=》视图new Watcher(vm, expr, (newVal)=>{ this.updater.modelUpdater(node, newVal) }) // 视图 =》数据=》视图 node.addEventListener(‘input‘, (e)=>{ this.setVal(expr, vm, e.target.value) }) this.updater.modelUpdater(node, value) }, on(node, expr, vm, eventName){ // console.log(vm) let fn = vm.$option.methods && vm.$option.methods[expr] node.addEventListener(eventName, fn.bind(vm), false) }, updater:{ textUpdater(node, value){ node.textContent = value }, htmlUpdater(node, value){ node.innerHtml = value; }, modelUpdater(node, value){ node.value = value } } }
Observer.js
class Watcher{ //判断新值旧值有没有变化,有的话更新// 通过回调函数实现更新的数据通知到视图 constructor(vm, expr, cb){ this.vm = vm; this.expr = expr; this.cb = cb; this.oldVal = this.getOldVal(); } // 获取旧数据 getOldVal(){ //有新值了,回调回去// 在利用getValue获取数据调用getter()方法时先把当前观察者挂载 Dep.target = this; const oldVal = complieUtil.getVal(this.expr, this.vm); // 挂载完毕需要注销,防止重复挂载 (数据一更新就会挂载) Dep.target = null; //挂载完删除return oldVal; } // 通过回调函数更新数据 update(){ const newVal = complieUtil.getVal(this.expr, this.vm) if(newVal !== this.oldVal){ this.cb(newVal) } } } // Dep类存储watcher对象,并在数据变化时通知watcherclass Dep{ constructor(){ this.subs = []; } //收集观察者 addSub(watcher){ this.subs.push(watcher) } //通知观察者去更新 notify(){ console.log("通知了观察者",this.subs) this.subs.forEach(w=>w.update()) } } class Observer{ //劫持监听所有属性 constructor(data){ this.observe(data) } observe(data){ if(data && typeof data === ‘object‘){ Object.keys(data).forEach((key)=>{ this.defineReactive(data,key,data[key]) }) } } defineReactive(obj, key, value){ //劫持数据//递归遍历 value可能是对象this.observe(value); const dep = new Dep() Object.defineProperty(obj, key, { //Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。 enumerable:true, //是否可遍历 configurable:false, //是否可以更改编写 get(){ //订阅数据变化时,往dep添加观察者 Dep.target && dep.addSub(Dep.target) return value }, // 采用箭头函数在定义时绑定this的定义域(拿到this) set:(newVal)=>{ this.observe(newVal); //去劫持新的值if(newVal !== value){ value = newVal } //更改完之后,通知变化 dep.notify(); } }) } }
原文:https://www.cnblogs.com/init00/p/12653738.html
内容总结
以上是互联网集市为您收集整理的vue源码讲解 --- 小马哥全部内容,希望文章能够帮你解决vue源码讲解 --- 小马哥所遇到的程序开发问题。 如果觉得互联网集市技术教程内容还不错,欢迎将互联网集市网站推荐给程序员好友。
内容备注
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 gblab@vip.qq.com 举报,一经查实,本站将立刻删除。
内容手机端
扫描二维码推送至手机访问。