vue源码系列02_数据监控与劫持

Vue实例

在src目录下创建一个index.js
在这里插入图片描述
该模块主要放的就是vue实例(核心代码放置的位置)

// vue 核心代码 只是Vue的一个声明
function Vue(options){
	
}
export default Vue

当我们需要进行初始化,渲染,监控等流程的时候,再向其中导入对应的方法。由此可见,该模块是用于整合其他方法

初始化操作

因为我们在页面刚加载的时候,需要对页面等操作进行渲染,如 data,computed,watch等进行初始化,所以我们需要vue实例创建的时候,运行初始化操作

init 函数

在vue中,初始化操作被放在了vue的原型上,编写初始化代码我们放到init.js中,然后再引入到index.js中,如
index.js

// vue 核心代码 只是Vue的一个声明
import {initMixin} from './init'
function Vue(options){
    // 进行vue初始化操作
    this._init(options); //这个init方法在 initMixin 中

}
// 通过引入文件的方式给 Vue原型添加方法
initMixin(Vue);
export default Vue

init.js

  1. 首先我们暴露一个 initMixin方法,用于向vue原型上创建一个_init 方法,这样在 index.js 中就可以直接调用了
  2. _init 方法 主要是用来进行初始化操作的
  3. initState() 用于描述 初始化流程,该方法被封装在 state.js 中 。注意:在初始化一开始,vue通过 const vm = this; vm.$options = options; 将 this 付给了vm,并将属性传给了 vm.$options,于是当我们给 initState 传入 vm 时,就已经把Vue实例传给了 initState
import {initState} from './state'
// 在原型上添加一个 init 方法
export function initMixin(Vue){ //向外暴露该方法,方便实例化操作
    // 初始化流程
    Vue.prototype._init = function(options){
        // 数据的 劫持
        const vm = this; // vue 中使用 this.$options 指代的就是用户传递的属性
        vm.$options = options;
        initState(vm); // 分隔代码
        // 如果用户传入了 el数据,需要页面渲染
        // 如果用户传入了 el, 就要实现挂载流程

        if(vm.$options.el){
            vm.$mount(vm.$options.el)
        }
    }
    function query(el){
        if(el==='String'){
            return document.querySelector(el);
        }
        return;
    }

    Vue.prototype.$mount = function(el){
        const vm = this;
        const options = vm.$options;
        el = vm.$el = query(el)
        // 默认先查找有没有 render 方法,没有render会采用template template也没有就用el中的内容

        let updateComponent = ()=>{
            console.log("更新和渲染的实现")
        }

        // new Watcher(vm,updateComponent)

        if(!options.render){
            //对模板进行编译
            let template = options.template; //取出模板
            if(!template && el){
                template = el.outerHTML;
            }

            
            const render = compileToFunction(template); //把template挂载
            options.render = render;
            // 我们将 template 转化为render方法,需要使用 虚拟DOM 
        }
    }
}

state.js

  1. 用于初始化状态
  2. 工作原理:
    1. 通过 vm.$options 我们可以拿到vue的数据来源,属性,方法,计算属性,watch等…
    2. 于是我们可以用逐一判断我们所传入的属性是否存在,然后运行相对应的方法
import { observe } from './observer/index.js'
export function initState(vm) {
    const opts = vm.$options;
    // vue的数据来源 属性 方法 数据 计算属性 watch
    if (opts.props) {
        initProps(vm);
    }
    if (opts.methods) {
        initMethod(vm);
    }
    if (opts.data) {
        initData(vm);
    }
    if (opts.computed) {
        initComputed(vm);
    }
    if (opts.watch) {
        initWatch(vm);
    }
}

function initProps() { }
function initMethod() { }
function initData(vm) { } // 该方法用于初始化数据
function initComputed() { }
function initWatch() { }

initData(vm) 数据初始化

  • 工作原理:
  1. 获取vm的data属性
  2. 判断data是否为函数,如果是,就直接触发,并把this指向实例,如果不是,就直接返回data就好了。(这也就解释了为什么我们平时可以直接用this.xxx获取到data的值)
  3. 进行数据劫持,也就是 MVVM 模式(数据变化驱动视图变化)
  4. 进行响应式操作 observe(data)
function initData(vm) {
    // 数据初始化工作
    let data = vm.$options.data; //用户传递的data
    data = vm._data = typeof data === 'function' ? data.call(vm) : data; //如果data是一个函数,就直接执行,并把this指向vue实例,否则就获取data对象
    // 对象劫持,用户改变了数据 我希望可以得到通知 => 刷新页面
    // MVVM 模式 数据变化驱动视图变化
    // Object.defineProperty() 给属性添加get方法和set方法
    observe(data); // 响应式原理
}

observer目录(所有响应式操作都写这)

  1. 在src下创建observer目录,并创建index.js
  2. 向外暴露 observe() 方法

observe() (响应式原理)

  1. 说到响应式,无非就是通过 Object.defineProperty() 给属性添加get方法和set方法
  2. 我们传入data数据后,给所有的数据都使用 Object.defineProperty() 重新定义get方法和set方法
  3. 它是 es5 的方法,不能兼容 IE8 及以下,所以 Vue2 无法兼容 IE8 版本
  4. 工作原理:
    1. 首先判断 data 是不是对象或为空
    2. 返回 Observer 实例,该方法用于观测数据(数据劫持)

Observer类

  1. 如果数据为对象,我们还要递归该对象对里面的数据进行响应式处理(正因如此,我们编写数据的时候尽量不要写太多层)
  2. 利用 walk(value) 遍历得到每一个值
  3. 取到值之后给每一个值定义响应式数据 defineReactive()

defineReactive

function defineReactive(data, key, value) {
    observe(value) // 递归,对data中的对象进行响应式操作,递归实现深度检测
    Object.defineProperty(data, key, {
        get() { // 获取值的时候做一些操作
            console.log("获取数据")
            return value;
        },
        set(newValue) { // 也可以做一些操作
            console.log("更新数据")
            if (newValue == value) return;
            observe(newValue); //继续劫持用户设置的值,因为用户设置的值有可能是一个对象
            value = newValue;
        }
    })
}

写到这里,我们就成功对元素进行响应式处理了(数组劫持在下一篇)

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值