vue源码系列02_数据监控与劫持
Vue实例在src目录下创建一个index.js该模块主要放的就是vue实例(核心代码放置的位置)// vue 核心代码 只是Vue的一个声明function Vue(options){}export default Vue当我们需要进行初始化,渲染,监控等流程的时候,再向其中导入对应的方法。由此可见,该模块是用于整合其他方法初始化操作因为我们在页面刚加载的时候,需要对页面等操作进行渲染,如 da
·
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
- 首先我们暴露一个 initMixin方法,用于向vue原型上创建一个_init 方法,这样在 index.js 中就可以直接调用了
- _init 方法 主要是用来进行初始化操作的
- 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
- 用于初始化状态
- 工作原理:
- 通过 vm.$options 我们可以拿到vue的数据来源,属性,方法,计算属性,watch等…
- 于是我们可以用逐一判断我们所传入的属性是否存在,然后运行相对应的方法
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) 数据初始化
- 工作原理:
- 获取vm的data属性
- 判断data是否为函数,如果是,就直接触发,并把this指向实例,如果不是,就直接返回data就好了。(这也就解释了为什么我们平时可以直接用this.xxx获取到data的值)
- 进行数据劫持,也就是 MVVM 模式(数据变化驱动视图变化)
- 进行响应式操作
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目录(所有响应式操作都写这)
- 在src下创建observer目录,并创建index.js
- 向外暴露 observe() 方法
observe() (响应式原理)
- 说到响应式,无非就是通过 Object.defineProperty() 给属性添加get方法和set方法
- 我们传入data数据后,给所有的数据都使用 Object.defineProperty() 重新定义get方法和set方法
- 它是 es5 的方法,不能兼容 IE8 及以下,所以 Vue2 无法兼容 IE8 版本
- 工作原理:
- 首先判断 data 是不是对象或为空
- 返回 Observer 实例,该方法用于观测数据(数据劫持)
Observer类
- 如果数据为对象,我们还要递归该对象对里面的数据进行响应式处理(正因如此,我们编写数据的时候尽量不要写太多层)
- 利用 walk(value) 遍历得到每一个值
- 取到值之后给每一个值定义响应式数据 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)