12

Vue 相关原理学习笔记

 4 years ago
source link: https://blog.callmewhy.com/post/vue-xiang-guan-yuan-li-xue-xi-bi-ji/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

响应式原理

方案一:Object.defineProperty

基于 Object.defineProperty 通过 setter/getter 方法来监听数据的变化。 getter 进行依赖收集,setter 在数据变更的时候通知订阅者,递归调用监听对象的所有属性。

缺点:

  • 无法检测到对象属性的添加或删除
  • 不能监听数组的变化,需要重写数组方法
function defineReactive(obj, key, value) {
    Object.defineProperty(obj, key, {
        enumerable: true,
        configurable: true,
        get() {
            console.log('get', obj, key, value)
            return value
        },
        set(newVal) {
            observe(newVal)
            if (newVal !== value) {
                console.log('set', obj, key, newVal)
                value = newVal
            }
        }
    })
}

function observe(obj) {
    if (!obj || typeof obj !== 'object') {
        return
    }
    Object.keys(obj).forEach(key => {
        const value = obj[key]
        observe(value)
        defineReactive(obj, key, obj[key])
    })
}

const data = { a: { b: 1 } }
observe(data)
data.a.b = 2

方案二:ES6 Proxy

优点:

  • 针对整个对象,而不是某个属性,所以不需要遍历,但是仍需要递归
  • 支持数组变化监听
const handler = {
    get(target, key) {
        if (typeof target[key] == 'object' && target[key] !== null) {
            return new Proxy(target[key], handler)
        }
        console.log('set', key)
        return Reflect.get(target, key)
    },
    set(target, key, value) {
        if (key === 'length') return true
        console.log('set', value)
        return Reflect.set(target, key, value)
    }
}

const data = { a: { b: 1 } }
const proxy = new Proxy(data, handler)
proxy.a.b = 2

如何收集依赖

两个关键类:

  • Watcher:封装一个观察订阅的依赖,触发 getter 事件
  • Dep:存放 Watcher 数组,需要通知变更的时候遍历 Watcher 发送通知

流程:

  • 对于某个属性,先声明一个 Dep 对象,用来存放依赖
  • Watcher 初始化的时候,会临时将 Dep.target 指向 this ,然后调用 getter 方法
  • 在 getter 中,将 Dep.target 指向的 Water 存入依赖数组
  • 在 setter 中,通过 dp.notify() 通知所有依赖对象

Vue Router 原理

  • hash mode:根据不同 hash 值渲染不同的组件和数据,通过在 window.onhashchange 监听 hash 改变实现路由切换
  • history mode:利用了 HTML5 History API 中的 pushState() 和 replaceState() 方法,虽然改变了当前的 URL ,但浏览器不会向后端发送请求。后台需要配置一个解析规则,避免 404 出现

Vuex 原理

vuex 中的 store 本质就是没有 template 的 vue 组件

Nuxt 原理

在打包之前,有两者的入口文件:

  • Server entry:服务端入口文件主要是返回新创建的 Vue 实例。为了避免在一个单例对象中的多次请求对状态的污染,所以每次渲染都会重复创建 Vue 实例。在这个地方,也会进行组件状态数据的预获取,路由处理等。
  • Client entry:客户端入口文件就是将 Vue 实例挂载到指定的 DOM 元素上。

在打包之后,有两个 bundle 文件:

  • Server bundle:服务器按照请求通过 Node 去生成预渲染的 HTML 字符串返回到请求的客户端,完成初始的渲染。
  • Client bundle:客户端拿到 HTML 之后,会使用这个 bundle 进行混合,使其变为由 Vue 管理的动态 DOM,为之后能够响应后续的数据变化。
NNVbimB.jpg!web

同构渲染:同一份代码,服务端先渲染生成 HTML(Dehydrate),客户端拿到代码后运行 JS ,进行客户端激活(Client-Side Hydration,CSH)的过程。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK