橙晨燕

vuePress-theme-reco Visionwuwu    2020 - 2021
橙晨燕 橙晨燕

Choose mode

  • dark
  • auto
  • light
主页
分类
  • 大学课程
  • 前端
  • 力扣算法
  • 随笔
标签
时间线
收藏集
  • 爱看的书
  • 爱追的剧
其他
  • 每日壹题 📚
  • CET-4 🛰️
  • 技术文档 📖
  • 收藏文章 📮
  • 项目展示 🚀
  • 项目总结 📝
  • 工具箱 📦

    • 图片压缩
    • 阿里云
    • 腾讯云
    • 掘金
    • CSDN
关于我
GitHub
author-avatar

Visionwuwu

25

文章

8

标签

主页
分类
  • 大学课程
  • 前端
  • 力扣算法
  • 随笔
标签
时间线
收藏集
  • 爱看的书
  • 爱追的剧
其他
  • 每日壹题 📚
  • CET-4 🛰️
  • 技术文档 📖
  • 收藏文章 📮
  • 项目展示 🚀
  • 项目总结 📝
  • 工具箱 📦

    • 图片压缩
    • 阿里云
    • 腾讯云
    • 掘金
    • CSDN
关于我
GitHub
  • 开始
  • JavaScript

  • Vue

    • 简易版vue2.0的mvvm实现
      • 工具类函数
      • 创建一个observer进行数据劫持
      • 定义响应式数据defineReactive
      • 重写监听的数组的7个原型方法
      • 使用observer实现数据响应式
    • vuex源码实现
  • React

简易版vue2.0的mvvm实现

vuePress-theme-reco Visionwuwu    2020 - 2021

简易版vue2.0的mvvm实现


Visionwuwu 2020-06-08 vue

  • 工具类函数
  • 创建一个observer进行数据劫持
  • 定义响应式数据defineReactive
  • 重写监听的数组的7个原型方法
  • 使用observer实现数据响应式

# 工具类函数

/**
 * 定义一个遍历对象的方法
 * @param {*} obj 遍历的源对象
 * @param {*} cb 传入的回调取值
 */
const forEach = (obj, cb)=>{
    Object.keys(obj).map(key=>{
        cb && cb(key, obj[key])
    })
}

/**
 * 判断是否为对象
 * @param {*} obj 传入的值
 */
const isObject = (obj)=>{
    return typeof obj === 'object' && obj !== null
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# 创建一个observer进行数据劫持

将用户传入的数据进行劫持转化为getter和setter

// 创建一个观察者observer
function observer(data){
    // 如果是数组,给每个成员进行劫持监听
    if(Array.isArray(data)){
        observerArray(data)
    }else if(data && isObject(data)){ // 存在且为对象
       Object.keys(data).map(key=>{
           // 定义响应式数据 只能劫持一层
           defineReactive(data, key, data[key])
            // 递归劫持
           observer(data[key])
       })
    }
    return data
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 定义响应式数据defineReactive

// 定义响应式数据defineReactive
function defineReactive(data, key, val){
    let dep = new Dep()
    // 数据劫持
    Object.defineProperty(data, key, {
        get(){
            // 依赖收集,添加订阅数据变化,触发相应回调更
            // 取值
            return val
        },
        set(newVal){
            // 值发生改变
            if(newVal !== val){ // 新值不等于旧值
                // 如果赋值的是对象继续劫持
                observer(newVal)
                // 重新赋值
                val = newVal
                // 通知订阅者更新
            }
        }
    })
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# 重写监听的数组的7个原型方法

  • splice
  • pop
  • push
  • unshift
  • shift
  • sort
  • reverse
const oldArrayPrototype = Array.prototype

// 重写的数组方法
const reWriteArrayMethods = [
    'pop',
    'push',
    'unshift',
    'shift',
    'splice',
    'sort',
    'reverse'
]

// 创建对象并将原型指向oldArrayPrototype 相当于 _arrProrotype.__proto__ = oldArrayPrototype
const _arrPrototype = Object.create(oldArrayPrototype)

// 重写数组方法
function reWriteMethods(name){
    return function(...args){ // 面向切片编程
        // 会改变数组原来项
        // 如果调用的是push unshift splice这仨个方法会新增数组数据,新增的可能是对象
        // val.push(1,2,{},[])
        // val.splice(0,1,{}) 数组的删除 新增 修改,前俩个是固定的第三个是参数
        let inserted;
        switch(name){
            case 'push':
            case 'unshift':
                inserted = args
                break;
            case 'splice':
                inserted = args[2]
                break;
        }
        // 如果有插入继续接劫持
        if(inserted){
            observer(inserted)
        }
        // 调用数组原型方法,将this指向于当点调用者
        oldArrayPrototype[name].apply(this, args)
        // 可以刷新视图了
    }
}

reWriteArrayMethods.forEach(name=> {
    _arrPrototype[name] = reWriteMethods(name)
})

// 劫持数组
function observerArray(arr){
    arr.forEach(item=>{
        observer(item)
    })
    // 重写数组的7个原型方法
    arr.__proto__ = _arrPrototype
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56

# 使用observer实现数据响应式

let data = observer({
    name: '张三',
    age: 10,
    obj: {
        a: 1,
        b: 2
    },
    arr: [1,2,{'xx':1,'bb': 2},[]]
})
console.log(data)
1
2
3
4
5
6
7
8
9
10
在 GitHub 上编辑此页 !
最后一次更新: 5 个月前