前端开发那些事儿

Vue劫持data

2021-08-09  本文已影响0人  JX灬君

在用Rollup搭建开发环境时,用了format:"umd",的打包模式。
umd的打包模式会在window上添加Vue节点。
例如:在src/index.js中

export default function Vue() {
  console.log('Vue节点');
}

rollup打完包后在html页面中引入生成的vue.js
然后打印Vue

<body>
    <div id="Vue">首页</div>
    <script src="dist/vue.js"></script>
    <script>
        // umd 打包模式会给window添加Vue
        console.log(Vue);
    </script>
</body>

页面结果为


image.png
  1. 在页面配置Vue
new Vue({
    el: '#app', // el 挂载元素的位置,也叫编译模版
    data: {
    }
})
  1. 在js里拿到用户的配置项
export default function Vue(options) {
  console.log(options); // options为用户配置项
}
  1. 设置初始化方法
    新建init.js
export function initMixin(Vue) {
  Vue.prototype._init = function (options) {  
    console.log(options);
  }
}

在index.js中注入调用

import { initMixin } from "./init";

function Vue(options) {
  // 初始化
  this._init (options) // _init 初始化方法 使用前先放到Vue原型链上
}
// 初始化,在init.js文件中给Vue原型链添加_init初始化方法
initMixin(Vue)

export default Vue

初始化状态
新建initState.js

import { initData } from "./initData";

export function initState(vm) {  
  let ops = vm.$options // 获取用户配置项
  console.log(ops);
  // 判断
  if (ops.data) {
    initData(vm) // data初始化
  }
}

初始化data
新建initData.js

export function initData(vm) {
  let data = vm.$options.data
  data = typeof data === "function"?data.call(vm):data; // 注意 通过call指向vue的实例
  // 对数据进行劫持
  
} 

劫持data

import { observe } from "./observe/index";
export function initData(vm) {
  let data = vm.$options.data
  
  // 在vm上添加一个属性 _data
  data = vm._data =  typeof data === "function"?data.call(vm):data; // 注意 通过call指向vue的实例
  // 对数据进行劫持
  // data 有两种类型对象和数组 {a:{b:1}, list:[]}
  observe(data)

} 

判断data是对象还是数组,然后通过创建一个类来劫持对象data

  1. 劫持对象用Object.defineProerty,有个缺点,只能对对象中对一个属性进行劫持
  2. 遍历data进行劫持
  3. 嵌套对data 用递归 进行深度劫持
export function observe(data) {
  console.log(data);
  // 1.判断类型是否是对象
  if (typeof data != 'object' || data == null) {
    return data;
  }
  // 1. 对象,通过一个类来劫持对象data
  return new Observer(data) 
}

// 用来劫持对象的类
class Observer{
  constructor (value){ // constructor 数据观测
    this.walk(value) // 遍历
  }
  walk(data){
    let keys = Object.keys(data) // 获取对象
    for (let i = 0; i < keys.length; i++) {
      // 在这个循环里对每个属性进行劫持
      let key = keys[i];  // 属性
      let value = data[key] // 值
      defineReactive(data, key, value)
    }
  }
}

function defineReactive(data, key, value) { //{a:{b:1}}
  observe(value) // 深度代理,深度劫持
  // 对对象中对属性进行劫持
  Object.defineProperty(data,key,{
    get(){ // 使用获取
      console.log('获取时触发');
      return value
    },
    set(newValue){ // 修改的时候
      console.log('设置时触发');
      if(newValue == value) return ;
      value = newValue
    }
  })
}

// {}
// 重写数组
// 1. 获取原来的数组方法
let oldArrayProtoMethods = Array.prototype
// 2. 继承
export let ArrayMethods = Object.create(oldArrayProtoMethods) // 创建一个对象
// 劫持
let methods = [
  "push",
  "pop",
  "unshift",
  "shift",
  "splice"
]
methods.forEach(item => {
  ArrayMethods[item] = function (...args) {  
    console.log('劫持数组');
    let result = oldArrayProtoMethods[item].apply(this, args) // this执行当前的数组 {list: []}
    return result
  }
})
上一篇下一篇

猜你喜欢

热点阅读