Vue 代理模式(手写Vue源码)


本文章出自全栈Ace VIP课程 b战搜索全栈Ace 仅用作学习记录
准备工作,新建一个文件,index.html

1.index.html 代码如下:

点击查看代码
<!DOCTYPE html>
<html lang="en">

<head>
	<meta charset="UTF-8">
	<meta http-equiv="X-UA-Compatible" content="IE=edge">
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js"></script>
	<title>Document</title>
</head>

<body>
	<div id="app">
		{{userName}}
	</div>
	<script>
		var vm = new Vue({
			el: "#app",
			data: {
				userName: "Ace"
			}
		})
		vm.$data.userName = "Taro"
	</script>
</body>

</html>

2.新建一个文件myVue.js

myVue.js
/**
 * Vue类的创建
 * @param {*} options vue实例中的众多对象 (data,method,el)
 */
function Vue(options) {
  this._init(options)
}
/**
 * 创建一个方法用于获取vue对象中的众多属性,并把这个方法赋值给Vue的原型对象_init
 * @param {*} options vue实例中的众多对象 (data,method,el)
 */
Vue.prototype._init = function (options) {
  //此时this指向Vue类
  let vm = this

  console.log(`this 为:${this}`)
  //创建Vue的对象 $options,来接收传入的options对象
  vm.$options = options
  //初始化vue
  initState(vm)
}
/**
 * 初始化vue
 * @param {*} vm vue对象
 */
function initState(vm) {
  let opt = vm.$options
  // 初始化data对象
  if (opt.data) {
    initData(vm)
  }
  // 初始化watch
  //   if (opt.watch) {
  //     initWatch()
  //   }
  //
}
/**
 * 初始化Data
 * @param {*} vm vue对象
 */
function initData(vm) {
  //获取data
  let data = vm.$options.data
  //vue 对象中创建$data 方法
  //data || {} 表示如果data为null则直接返回{}空对象
  vm.$data = data || {}
  if (typeof data != 'object' || data == null) {
    return
  } else {
    new Observe(data, vm)
  }
}
/**
 * 监听类 用于监听data的变化
 * @param {*} data 数据对象
 * @param {*} vm vue对象
 */
function Observe(data, vm) {
  /**
   * 重构data 并对其进行get/set 属性代理
   * @param {*} data
   * @param {*} vm
   */
  function restructure(data, vm) {
    let keys = Object.keys(data)
    console.log(keys)
    // 循环遍历每一个key 并进行代理化改造
    // ★核心内容
    for (let index = 0; index < keys.length; index++) {
      let key = keys[index]
      let value = data[key]
      //先定义vm中的对象,就不会出现后面重复定义的错误
      vm[key] = value
      // 验证value是否依旧是对象(递归)
      if (typeof value != 'object' || value == null) {
        //啥都不做
        // vm[key] = value
        // console.log('到了')
        // console.log(vm)
      } else {
        new Observe(value, vm)
      }
      // 给data中的变量配置get/set属性用于监听
      //或者可以理解成把指定key挂载到data中
      Object.defineProperty(data, key, {
        get: function () {
          return value
        },
        set: function (newValue) {
          if (newValue != value) {
            //验证newValue是否是对象
            if (typeof newValue != 'object' || newValue == null) {
              //啥都不做
            } else {
              new Observe(newValue, vm)
            }
            value = newValue
            //这里只是做一个日志输出,如果是一个正常的vue,则应该是更新视图方法
            console.log(`我现在是[${value}] 啦`)
          }
        },
      })
      //直接将data中的变量挂载到vm中
      //注意: 如果之前vm中未曾定义过这些属性,若是有两个相同的key则会出现重复定义属性的错误
      //解决方案:在之前每一次for循环后先通过vm[key] = value
      //定义一次,那么在这里就不会再次定义了
      //这和js中xxx.xxx的意思一样,如果xxx.xx的xx在xxx中不存在则会定义,存在则只是调用
      Object.defineProperty(vm, key, {
        get: function () {
          return value
        },
        set: function (newValue) {
          if (newValue != value) {
            //验证newValue是否是对象
            if (typeof newValue != 'object' || newValue == null) {
              //啥都不做
            } else {
              new Observe(newValue, vm)
            }
            // value = newValue
            console.log(`我现在是[${value}] 啦`)
          }
        },
      })
    }
  }
  restructure(data, vm)
}


上述代码分为如下步骤:

  • 创建vue类
  • 写vue全局扩展类(.NET 是这么叫)
  • 初始化vue
  • 初始化data
  • 监听类 用于监听data的变化(重构data)

3. index.html更新

点击查看代码
<!DOCTYPE html>
<html lang="en">

<head>
	<meta charset="UTF-8">
	<meta http-equiv="X-UA-Compatible" content="IE=edge">
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<!-- <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js"></script> -->
	<script src="./myVue.js"></script>
	<title>Document</title>
</head>

<body>
	<div id="app">
		{{userName}}
	</div>
	<script>
		var vm = new Vue({
			data: {
				userName: "Ace",
				user: {
					userName: "Taro",
					age: 18
				}
			}
		})
		vm.user.userName = "GaoSi"
	</script>
</body>

</html>

原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/280010.html

(0)
上一篇 2022年8月11日
下一篇 2022年8月11日

相关推荐

发表回复

登录后才能评论