1.svelte的优势,没有劫持、没有虚拟DOM等,纯编译响应式,它可能是未来的趋势(是编译器,已经不是框架了)
2.vue2,关于响应式的原理大概如下:
// 单一职责,解耦export class Vue {constructor(options = {}) {this.$options = optionsthis.$el = typeof options.el === 'string' ? document.querySelector(options.el) : options.elthis.$data = options.datathis.$methods = options.methodsthis.proxy(this.$data)// Observe 拦截this.$datanew Observer(this.$data)// 编译htmlnew Compiler(this)}// 代理,方便使用,直接this.nameproxy(data) {Object.keys(data).forEach(key => {Object.defineProperty(this, key, {enumerable: true,configurable: true,get() {return data[key]},set(newValue) {if(data[key] === newValue || (Number.isNaN(data[key] && Number.isNaN(newValue)))) {return}data[key] = newValue;}})})}}// 发布订阅模式class Dep {constructor() {this.deps = new Set()}// 收集副作用代码add() {if (dep && dep.update) {this.deps.add(dep);}}notify() {// udpate就是执行副作用 Watcherthis.deps.forEach(dep => dep.udpate())}}// watcher会在编译的时候触发,编译html中的变量->触发当前变量的getter->封装一个watcherclass Watcher {constructor(vm, key, cb) {// vm是vue实例this.vm = vm;this.key = key;this.cb = cb;Dep.target = this;this.__old = vm[key] // 存下初始值,触发getter// 防止我们在别处触发getter,仅在编译的时候Dep.target = null;}update() {let newValue = this.vm[this.key]if (this.__old === newValue) {return;}this.cb(newValue); // 操作DOM的函数}}class Observer {constructor(data) {this.walk(data)}walk(data) {if(!data || typeof data !== 'object') returnObject.keys(data).forEach(key => this.defineReactive(data, key, data[key]))}// { obj: }defineReactive(obj, key, value) {let that = this;let dep = new Dep();this.walk(value);// 这个放在后面可以吗?Object.defineProperty(obj, key, {configurable: true,enumerable: true,get() {Dep.target && dep.add(Dep.target)return value;},set(newValue) {value = newValue;// 赋值的这个也可能是一个对象that.walk(newValue);dep.notify()}})}}class Compiler {constructor(vm) {this.el = vm.$elthis.vm = vmthis.methods = vm.$methodsthis.compiler(vm.$el)}compiler(el) {let childNodes = el.childNodesArray.from(childNodes).forEach(node => {if (this.isTextNode(node)) {this.compileText(node)} else if (this.isElementNode(node)) {this.compileElement(node)}// 递归编译子节点if (node.childNodes && node.childNodes.length) {this.compiler(node)}})}compileElement(node) {if(node.attributes.length) {Array.from(node.attributes).forEach(attr => {let attrName = attr.name// v-on:click and v-modelif(this.isDirective(attrName)) {attrName = attrName.indexOf(':') > -1 ? attrName.substr(5) : attrName.substr(2)let key = attr.valuethis.update(node, key, attrName, this.vm[key])}})}}udpate(node, key, attrName, value) {if (attrName === 'text') {node.textContent = value// 如果加上虚拟DOM diff算法的话,此处的回调函数就不应该直接操作DOM了,应该传入新老树进行对比,最后再渲染new Watcher(this.vm, key, val => {node.textContent = val})} else if (attrName === 'model') {node.value = valuenew Watcher(this.vm, key, val => {node.textContent = val})node.addEventListenner('input', () => {this.vm[key] = node.value})} else if (attrName === 'click') {node.addEventListenner('click', this.methods[key].bind(this.vm))}}isDirective(str) {return str.startsWith('v-')}// 元素节点 <div v-model="msg"></div>isElementNode(node) {return node.nodeType === 1}// {{ count}} 文本节点compileText(node) {let reg = /\{\{(.+?)\}\}/let value = node.textContentif(reg.test(value)) {let key = RegExp.$1.trim()node.textContent = value.replace(reg, thiis.vm[key])new Watcher(this.vm, key, val => {node.textContent = val})}}isTextNode(node) {return node.nodeType === 3}// 缺少调度啊???// 没有考虑作用域?????}
3.vue3.0相关响应式如下:
最大的优势: 考虑到内存的问题,不用每次都像2.0那样,new Dep()
// vue3.0
function isObject(data) {
return data && typeof data === 'object'
}
export function reactive(data) {
if(!isObject(data)) return
return new Proxy(data, {
get(target, key, receiver) {
const ret = Reflect.get(target, key, receiver);
// 依赖收集
track(target, key)
return isObject(ret) ? reactive(ret) : ret
},
set(target, key, value, receiver) {
return Reflect.set(target, key, value, receiver);
},
deleteProperty(target, key, receiver) {
const ret = Reflect.deleteProperty(target, keu, receiver)
// todo 通知
return ret
}
})
}
// proxy无法监听基本类型, 采用一个对象包起来
export function ref(target) {
let value = target
const obj = {
get value() {
track(obj, 'value')
return value
},
set value(newValue) {
if (value === newValue) return
value = newValue
trigger(obj, 'value')
}
}
return obj
}
