vue2.0数据劫持原理
vue2.0数据劫持
数据劫持的意义?
一个数据操作,我们希望在他做赋值的过程当中,我们还可以给他增加一些事情,比如说像这个视图上的改变,我们希望的是当你数据变化的这个过程当中,那我们就拦截这些行为,在这个行为的这个过程当中,还能做更多的事,而不是单纯的操作数据
数据劫持都做了哪些事呢?
首先,new了一个vue实例,传参options如:new Vue({el:"#app",data(){return {}title:‘xxx’,testArr:[1,2,3]}})。这里只讲data,data是一个函数,需要执行之后才能拿到数据对象。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HvqQmp6A-1623068431810)(C:\Users\YuPeng Zheng\AppData\Roaming\Typora\typora-user-images\image-20210606161111128.png)]
然后,到vue/index.js中,声明了Vue构造函数。任何一个程序都会有初始化的过程。然后在他的原型上挂在了一个init方法,把options挂载到vm实例上vm.$options = options,然后再进行initState(vm)初始化数据状态
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mmRkAsY6-1623068431815)(C:\Users\YuPeng Zheng\AppData\Roaming\Typora\typora-user-images\image-20210606161125192.png)]
然后,initState(init.js)中,进行初始化各种数据的状态,可能会有data,watch,computed。。。等等。这里只讲data。initData(vm),判断data的类型,是函数就执行是对象就直接返回对象并赋值到vm._data
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kUBuUpee-1623068431816)(C:\Users\YuPeng Zheng\AppData\Roaming\Typora\typora-user-images\image-20210606161036850.png)]
然后,为了解决访问方式,更改this指向,使得vm.data.title变为vm.title,就进行代理劫持。proxy.js,循环data,并且调用proxyData(vm,"-data".key),Object.fineProperty将vm.tilte解析为vm.-data.title,这样就改写了访问方式,把data中的每个对象都变成这样的访问方式,并且此时还对vm._-data进行观察observe(vm.-data)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6u8nIUYr-1623068431818)(C:\Users\YuPeng Zheng\AppData\Roaming\Typora\typora-user-images\image-20210606161053286.png)]
再然后,observe.js。判断类型不是对象不是数组或者为null直接return,否则观察者进行观察他,new Observer(data)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xLkiHiSj-1623068431820)(C:\Users\YuPeng Zheng\AppData\Roaming\Typora\typora-user-images\image-20210606160922043.png)]
然后,Observer.js,判断数组还是对象,分别处理,对象就递归调用observe,数组就重写数组方法,再进行观察里面的对象。
对象的时候:Observer的原型上挂载walk(data),然后遍历data,进行响应式数据劫持,defineReactiveData(data,key,value)。这里面就是obeject.defineProperty的递归执行了,然后再利用get()set(),reactive.js(defineReactiveData)中,
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Q8B5toxF-1623068431821)(C:\Users\YuPeng Zheng\AppData\Roaming\Typora\typora-user-images\image-20210606160806193.png)]
数组的时候:array.js读取config.js中的数组处理的几种方法,因为数组操作会更改原数组,也是要监测到他数组变化的过程,去做一些其他的事情,(例如视图更新)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HX43GzUZ-1623068431822)(C:\Users\YuPeng Zheng\AppData\Roaming\Typora\typora-user-images\image-20210606162310298.png)]
数组变化的时候还要劫持数组的元素就需要observerArr.js
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Aj1yb6QV-1623068431823)(C:\Users\YuPeng Zheng\AppData\Roaming\Typora\typora-user-images\image-20210606162443903.png)]
总之就是来回的执行observe,数组操作很巧妙。
1,首先配置webpack,使用webpack搭建服务
2,然后更改创建各项文件夹及文件。
3,模拟vue引入,手动创建vue文件夹以及index.js,webpack中重写路径,当import vue from 'vue’的时候会指向vue文件夹下的index.js
4,new一个vue实例,并传参
import Vue from 'vue'
let vm = new Vue({
el:'#app',
data(){
return{
order:'目标数据',
myObj:{
status:'故障',
type:{
name:'类型1'
}
}
}
}
})
5,那么该vue接收里面的options。
//单独拿出去 初始化数据的方法
import {initState} from './init'
function Vue(options){
this._init(options)
}
//实际vue就是将很多方法和数据挂载到vue的原型上
//只看data 一定会有一个初始化的方法 初始化数据
Vue.prototype._init = function(options){
var vm = this //赋值保存this
vm.$options = options
//初始化数据状态
initState(vm);
}
export default Vue
6,initState.js,初始化状态,判断data的类型,如果是函数,就得执行他,否则直接vm.data.order是拿不到里面的数据的
import proxyData from './proxy'
function initState(vm){
// console.log(vm)
var options = vm.$options
//针对不同的数据都要初始化 可能options还会有computed watch 都要分别init
if(options.data){
initData(vm)
}
}
function initData(vm){
var data = vm.$options.data; //保留data原数据 接收原数据
//需要判断data是对象的话就执行他并接收他执行的返回结果 如果是对象就。。
data = vm._data = typeof data === 'function'?data.call(vm):data||{}
//到上面这一步还不能进行劫持 数据访问需要 vm._data.order而不能直接通过vm.order拿到
//下面用代理实现他
//原理是在访问的时候进行拦截 重新定义它
for(var key in data){
proxyData(vm,'_data',key)
}
}
export{
initState
}
7,proxyData.js,进行劫持,当界面获取、设置data里面的数据的时候,就会进入这里进行劫持,去掉_data,使得访问数据的方式从vm._data.order变成vm.order。
function proxyData(vm,target,key){
Object.defineProperty(vm,key,{
get(){
return vm[target][key]
},
set(newVal){
vm[target][key] = newVal
}
})
}
export default proxyData
但是至此以上也仅仅是改变了访问data里面数据的方式而已。但是还并没有对data里面的数据或data里面的对象里面的数据进行劫持。(所谓劫持就是在获取或赋值的时候进行拦截,让他重新返回或者走一步自定义的逻辑),还需要对每一项数据进行代理劫持。
8,观察者observe.js,观察vm.data
import defineReactiveData from './reactive'
function observe(data){
if(typeof(data)!=='object'||data===null)return
return new Observer(data)
}
function Observer(data){
//处理数组
if(Array.isArray(data)){
}else{ //处理对象
this.walk(data)
}
}
Observer.prototype.walk = function(data){
var keys = Object.keys(data)
for (var i=0;i<keys.length;i++){
var key = keys[i]
var value = data[key]
//
defineReactiveData(data,key,value)
}
}
export default observe
9,在reactive.js中进行数据劫持,因为data[key]里面的还有可能是对象,就需要递归执行
import observe from './observe'
function defineReactiveData(data,key,value){
//这个时候的value有可能还是个对象 递归执行观察
observe(value)
Object.defineProperty(data,key,{
get(){
console.log('响应式数据:获取',value)
return value
},
set(newValue){
if(newValue===value)return
value = newValue
}
})
}
export default defineReactiveData
以上完成了对data以及data中的对象以及data.obj.obj的劫持,还需要对当中的数组进行劫持,但是操作数组的很多方法都会改变原数组,所以需要自己定义这些数组方法。
10,未整理完,后续补全
码云地址: