vue2源码解读一

101 阅读2分钟

rollup的环境配置

1. rolllup搭建环境

1.1 安装rollup

npm init
npm install @babel/preset-env @babel/core rollup rollup-plugin-babel rollup-plugin-serve -D

1.2 新建rollup.config.js 配置文件

import babel from 'rollup-plugin-babel'
import serve from 'rollup-plugin-serve'


export default {
    input: './src/index.js',  // 打包入口(在对应的位置添加文件)
    output: {
        file: 'dist/vue.js', 
        format: 'umd',  // 在window 上 Vue    new Vue
        name: 'Vue',
        sourcemap: true,  // 转换前后的代码进行映射
    },
    plugin: [
        babel({
            exclude: 'node_modules/**',  // 排除文件
        }),
        serve({  
            port: 3000,
            contentBase: '', // 空字符表示当前目录
            openPage: '/index.html'
        })
    ]
    
}

1.3 创建babelrc 文件,配置babel

{
  "presets": [
      "@/babel/preset-env"
  ]
}

1.4 在package.json 文件中的script,添加脚本

"dev": "rollup -c -w", // -c 执行rollup.config.js; -w 代码的检测更新

1.5 使用npm run dev 注意: Cannot use import statement outside a module 则需要在package.json添加属性

      "type": "module"

2. vue响应式

2.1 初始化Vue

2.1.1 创建Vue


function Vue(options) {
console.log('配置项:', options);
}


export default Vue

2.1.2 往Vue的prototype添加_init初始化参数

export function initMixin(Vue) {
    Vue.prototype._init = function (options) {
        console.log('初始化:', options);
    }
}

2.1.3 在index.js中导入初始化的方法

import { initMixin } from './init.js'

function Vue(options) {
    // console.log('配置项:', options);
    this._init(options)
}

initMixin(Vue)

export default Vue

2.2.1 实现对data中对象的属性进行劫持

对象属性劫持通过Object.defineProperty()进行劫持 Object.defineProperty() 有缺点,只能劫持对象的一个属性 Vue中对对象进行劫持的步骤 1、Object.defineProperty() 缺点: 只能对对象中的一个而属性进行劫持

function defineReactive(data, key, value) {
    Object.defineProperty(data, key, {
        get() {
            console.log('获取');
            return value
        },
        set(newVal) {
            if(newVal === value) return

            Observe(newVal)  // 值是对象,要进行递归
            console.log('设置');
            value = newVal
        }
    })
}

2、循环

class Observer{
    constructor(value){
        if(Array.isArray(value)) {
            value.__proto__ = ArrayMethods
            console.log('数组');
            
        } else {
            this.walk(value)
        }

    }
    walk(data) {
        let keys = Object.keys(data)
        for(let i=0; keys.length > i; i ++ ) {
            let key = keys[i]
            let value = data[key]
            defineReactive(data, key, value)
        }
    }
}

3、递归

function defineReactive(data, key, value) {

    // 递归
    Observe(value)  // 深度代理
    
    
    
    Object.defineProperty(data, key, {
        get() {
            console.log('获取');
            return value
        },
        set(newVal) {
            if(newVal === value) return

            Observe(newVal)  // 值是对象,要进行递归
            console.log('设置');
            value = newVal
        }
    })
}

2.2.1 对数组进行劫持

   方法函数劫持,劫持数组方法
   
// 重写数组
// 1、获取原数组方法
let oldArrayProtoMethods = Array.prototype

// 2、继承
export let ArrayMethods = Object.create(oldArrayProtoMethods)


// 劫持
let methods = ['pop', 'push', 'shift', 'unshift', 'slice', 'splice']

methods.forEach( item => {

    ArrayMethods[item] = function(...args) {
        console.log('劫持数组方法:',item);
        let result =  oldArrayProtoMethods[item].apply(this, args)

        return result
    }   
})

2.2.2 往数组添加属性

1、现在Observer定义好一个__ob__属性,该属性的value值为Observer的上下文

Object.defineProperty(value, "__ob__", {
            enumerable: false,
            value: this
        })

2、在劫持的数组方法中,调用Observer的observeArray方法,对新添加的属性进行数据劫持

// 重写数组
// 1、获取原数组方法
let oldArrayProtoMethods = Array.prototype

// 2、继承
export let ArrayMethods = Object.create(oldArrayProtoMethods)


// 劫持
let methods = ['pop', 'push', 'shift', 'unshift', 'slice', 'splice']

methods.forEach( item => {

    ArrayMethods[item] = function(...args) {
        console.log('劫持数组方法:',item, args);
        let result =  oldArrayProtoMethods[item].apply(this, args)

        let inserted = ''

        switch(item) {
            case 'push':
            case 'unshift':
                inserted = args
                break
            case 'splice': 
                inserted = args.splice(2)
                break
        }

        let ob = this.__ob__  // Observer
        if(inserted) {
            ob.observeArray(inserted)
        }

        return result
    }

        
})

2.3 将data上的所有属性代理到实例上

function proxy(vm, source, key ) {
    Object.defineProperty(vm[source], key, {
        get() {
            return vm[source][key]
        },
        set(newVal) {
            vm[source][key] = newVal
        }
    }) 
}