Vue2数据劫持原理 rollup打包Vue源码配置类库,工具库一般使用rollup打包,打包出的文件比较干净
1 2 yarn add rollup rollup-plugin-babel @babel/core @babel/preset-ent rollup-plugin-serve -D
rollup简单配置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 import babel from 'rollup-plugin-babel' import serve from 'rollup-plugin-server' export default { input :"./src/index.js" , output :{ format :'umd' , file :"./dist/vue.js" , name :"Vue" , sourcemap :true }, plugins :[ babel ({ exclude :'node_modules/**' }), serve ({ open :true , port :3000 , contentBase :'' , openPage :'/public/index.html' }) ] } { "presets" : [ "@babel/preset-env" ] }
如何在原型上扩展方法 1 2 3 4 5 6 7 8 import { initMixin } from './init' function Vue (options ){ this ._init (options); } initMixin (Vue );
1 2 3 4 5 6 7 8 9 export function initMixin (Vue ){ Vue .prototype ._init = function (options ){ } Vue .prototype .$mount = function (el ){ } }
对象的数据劫持 初始化时调用initState(vm)
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 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 import { initState } from './state' ;export function initMixin (Vue ){ Vue .prototype ._init = function (options ){ const vm = this ; vm.$options = options; initState (vm); } } import { observe } from './observer/index' ;export function initState (vm ){ const opts = vm.$options ; if (opts.data ){ initDate (vm); } } function initDate (vm ){ let data = vm.$options .data vm._data = data = typeof data == 'function' ?data.call (vm):data; Object .keys (data).forEach (key => { proxy (vm,'_data' ,key) }) observe (data); } function proxy (vm,data,key ){ Object .defineProperty (vm,key,{ get ( ){ return vm[data][key] }, set (newVal ){ vm[data][key] = newVal; } }) } class Observer { constructor (value ){ this .walk (value); } walk (data ){ Object .keys (data).forEach (key => { defineReactive (data,key,data[key]) }) } } function defineReactive (target,key,value ){ observe (value); Object .defineProperty (target,key,{ get ( ){ return value }, set (newValue ){ if (value!=newValue){ observe (newValue); value = newValue; } } }) } export function observe (data ){ if (typeof data!= 'object' || data == null ){ return ; } return new Observer (data); }
数组的数据劫持
核心思想是把数组的原有方法重写,在执行之前先执行自己写的方法 (AOP切片编程)
只有data中的数组才会先执行自己定义的,在外部不受影响 (所有不能直接修改数组原型)
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 57 58 59 60 61 62 63 64 65 66 67 68 69 let oldArrayMethods = Array .prototype ; export let arrayMethods = Object .create (Array .prototype ); let methods = [ 'pop' , 'push' , 'shift' , 'unshift' , 'splice' , 'revert' , 'sort' , ] methods.forEach (method => { arrayMethods[method] = function (...args ){ let result = oldArrayMethods[method].call (this ,args); let insert; let ob = this .__ob__ ; switch (method){ case "push" : case "unshift" : insert = args; break ; case "splice" : insert = args.slice (2 ); break ; default : break ; } if (insert) ob.observeArray (insert) return result } }) import { arrayMethods } from './array.js' class Observer { constructor (value ){ Object .defineProperty (value,'__ob__' ,{ value :this , enumerable :false , configurable :false }) if (Array .isArray (value)){ value.__proto__ = arrayMethods; this .observeArray (value); }else { this .walk (value); } } observeArray (value ){ value.forEach (val => { observe (val) }) } ... }
Object.create()与new Object()的区别