内容简介:作为写业务的码农,几乎不必知道原理。但是当你去找工作的时候,可是需要造原子弹的,什么都得知道一些才行。所以找工作之前可以先复习下,只要是关于vue的,必定会问响应式原理。其实,只需要在修改data值的时候,需要触发一个回调方法,来更新与此值有关的数据,就完了。但是你面试的时候,那些大佬可不会觉得是这样的,需要把整个流程说明白才行。
Vue响应式原理
作为写业务的码农,几乎不必知道原理。但是当你去找工作的时候,可是需要造原子弹的,什么都得知道一些才行。所以找工作之前可以先复习下,只要是关于vue的,必定会问响应式原理。
核心:
//es5 Object.defineProperty(obj,key,{ get() { // 获取obj[key]的时候触发 }, set(val) { // obj[key] = 'xxx'时触发 } })
其实,只需要在修改data值的时候,需要触发一个回调方法,来更新与此值有关的数据,就完了。但是你面试的时候,那些大佬可不会觉得是这样的,需要把整个流程说明白才行。
简单的Vue响应式代码如下:
Vue.js:
class Vue { constructor(opts) { this.opts = opts if (opts.data) this.initData(opts.data); if (opts.watch) this.initWatch(opts.watch); if (opts.computed) this.initComputed(opts.computed); if (opts.el) this.$mount(opts.el) } initData(data) { // 让data上的数据被get的时候能够搜集watcher,data变为观察者 new Observable(data); this.data = data // 数据可在this上直接读取 Object.keys(data).forEach(key => { this.proxy(key) }) } initWatch(watch) { Object.keys(watch).forEach(key => { // new Watcher(this, key, watch[key]) }) } initComputed(data) { Object.keys(data).forEach(key => { new Watcher(this, key) this.proxy(key, { get: data[key], }) }) } $mount(el) { el = document.querySelector(el) this.template = el.innerHTML this.el = el const fn = _ => { const nwTemp = this.parseHTML(this.template) this.el.innerHTML = nwTemp if(this.opts.mounted) { this.opts.mounted.call(this) } } new Watcher(this, fn) } parseHTML(template) { return template.replace(/\{\{(.*?)\}\}/g, (str, str1) => { return this[str1.trim()] }) } // 将数据直接挂到this上,使用getterSetter代理,获取vm.data上的值 proxy(key, getterSetter) { const vm = this getterSetter = getterSetter || { set(value) { vm.data[key] = value }, get() { return vm.data[key] } } Object.defineProperty(vm, key, getterSetter) } } // 给data作为观察者用的 class Observable { constructor(obj) { Object.keys(obj).forEach(key => { this.defineReact(obj, key, obj[key]) }); } defineReact(obj, key, value) { const dep = new Dep() Object.defineProperty(obj, key, { get() { // 获取data里面信息的时候,能够搜集依赖,这些依赖都是watcher实例 if (Dep.target) { dep.append(Dep.target) } return value }, set(val) { value = val // 修改data里面数据的时候,去通知已搜集的依赖更新 dep.notify() } }) } } // 搜集与触发wacher class Dep { constructor() { this.subs = [] } append(watcher) { // 避免重复添加watcher // watcher 在update的时候,会重新获取值,此时不必再添加 if(this.subs.includes(watcher)) return this.subs.push(watcher) } notify() { this.subs.forEach(watcher => { watcher.update() }) } } Dep.target = null class Watcher { // keyOrFn 为字符串或者function,opts.watch为字符串,computed,$mount中为function constructor(vm, keyOrFn, cb) { this.cb = cb if (typeof keyOrFn === 'string') { this.getter = function () { return vm[keyOrFn] // 例:initWatch时,watch:{a(){}} ,a为data里的数据,此处获取vm.a会触发a的收集 } } else { this.getter = keyOrFn // 如果为fn(computed中)值为此函数的返回值 } this.value = this.get() } get() { Dep.target = this // 此watcher记录下来 const value = this.getter(this.vm) // 运行getter,如果是watch,获取一次data里的值,完成收集。如果是computed,运行其方法,其方法中含有data的值时,会触发收集 // 当更新时,也会触发getter Dep.target = null return value } update() { // 更新 const oldValue = this.value // 重新获取值 const value = this.get() if (this.cb) { //如果是watch,会触发watch的函数 this.cb.call(this.vm, value, oldValue) } } }
index.html
<div id="app"> <h2>Vue响应式原理</h2> <br /> msg: {{ msg }} <br /> <p>num: {{ num }}</p> <p>num+1计算属性值:{{ add1 }}</p> <button>add</button> </div> <script src="./Vue.js"></script> <script> const app = new Vue({ el: "#app", data: { msg: "这是msg", num: 1 }, watch: { msg(newVal, oldVal) { console.log(newVal, oldVal); } }, computed: { add1() { return this.num + 1; } }, mounted() { const btn = document.querySelector("button"); btn.onclick = _ => { this.num += 1; }; } }); </script>
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- VCenter监控可实现精细故障排除
- Node.js排除内存泄漏演示
- 熟练使用Wireshark排除网络故障的方法
- 10个PowerShell cmdlet 快速排除网络故障
- ios – Xcode – 搜索范围以排除文件或路径
- html – 如何在CSS中排除元素(通过ID)
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Markdown 在线编辑器
Markdown 在线编辑器
UNIX 时间戳转换
UNIX 时间戳转换