1. new一个Vue实例
new一个Vue实例,参数是一个对象,称为options,Vue实际上是一个函数
// index.js
// 实例一个Vue对象
let vue = new Vue({
props: {},
data() {
return {
a: 1,
b: [1],
c: { d: 1 }
}
},
watch: {},
render: () => {}
})
2.对options对象的初始化
传进来的options对象,需要初始化,执行function Vue (options) {}函数
// index.js
const { initMixin } = require('./init')
function Vue(options) {
// 初始化传进来的options配置
this._init(options) // 来自initMixin
}
// 配置Vue构造函数的_init方法
// 这么做有利于代码分割
initMixin(Vue)
// 实例一个Vue对象
let vue = new Vue({
props: {},
data() {
return {
a: 1,
b: [1],
c: { d: 1 }
}
},
watch: {},
render: () => {}
})
将初始化函数_init挂到Vue的原型上
// init.js
const { initState } = require('./state')
function initMixin(Vue) {
将_init挂载到Vue原型上
Vue.prototype._init = function (options) {
// vm变量赋值为Vue实例
const vm = this
// 将传经来的options对象赋值给vm上的$options变量
vm.$options = options
// 执行初始化状态函数
initState(vm)
}
}
module.exports = {
initMixin: initMixin
}
const { observe } = require('./observer/index')
// 总初始化函数, 会初始化props、methods、data、computed、watch
function initState(vm) {
// 获取vm上的$options对象,也就是options配置对象
const opts = vm.$options
if (opts.props) {
initProps(vm)
}
if (opts.methods) {
initMethods(vm)
}
if (opts.data) {
// 如有有options里有data,则初始化data
initData(vm)
}
if (opts.computed) {
initComputed(vm)
}
if (opts.watch) {
initWatch(vm)
}
}
// 初始化data的函数
function initData(vm) {
// 获取options对象里的data
let data = vm.$options.data
// 判断data是否为函数,是函数就执行(注意this指向vm),否则就直接赋值给vm上的_data
// 这里建议data应为一个函数,return 一个 {},这样做的好处是防止组件的变量污染
data = vm._data = typeof data === 'function' ? data.call(vm) : data || {}
// 为data上的每个数据都进行代理
// 这样做的好处就是,this.data.a可以直接this.a就可以访问了
for (let key in data) {
proxy(vm, '_data', key)
}
// 对data里的数据进行响应式处理
// 重头戏
observe(data)
}
// 数据代理
function proxy(object, sourceData, key) {
Object.defineProperty(object, key, {
// 比如本来需要this.data.a才能获取到a的数据
// 这么做之后,this.a就可以获取到a的数据了
get() {
return object[sourceData][key]
},
// 比如本来需要this.data.a = 1才能修改a的数据
// 这么做之后,this.a = 1就能修改a的数据了
set(newVal) {
object[sourceData][key] = newVal
}
})
}
module.exports = { initState: initState }
4.响应式处理
- Observer:观察者对象,对
对象或数组进行响应式处理的地方 - defineReactive:拦截对象上每一个
key的get与set函数的地方 - observe:响应式处理的入口 流程大概是这样:observe -> Observer -> defineReactive -> observe -> Observer -> defineReactive 递归
const { arraryMethods } = require('./array')
class Observer {
consrtuctor (value) {
// 给传进来的value对象或者数组设置一个_ob_对象
// 如果value上面有了这个_ob_对象,则表示value已经做了响应式处理
Object.defineProperty(value, '_ob_', {
value: this, // 值为this,也就是new出来的Observer实例
enumerable: false, // 不可被枚举
writable: true, // 可以赋值运算符修改_ob_
configurable: true // 可改写可删除
})
// 判断value是数组还是对象
if (Array.isArray(value)) {
// 如果是数组的话就修改原型
value.__proto__ = arrayMethods
this.observeArray(value)
} else {
// 如果是对象,则执行walk函数对对象进行响应式处理
this.walk(value)
}
}
walk (data) {
// 获取data对象的所有key
let keys = Object.keys(data)
// 遍历所有key,对每个key的值进行响应式处理
for (let i = 0; i < keys.length; i++) {
const key = keys[i]
const value = data[key]
defineReactive(data, key, value)
}
}
observeArray (items) {
// 遍历传进来的数组,对数组的每一个元素进行响应式处理
for (let i = 0; i < items.length; i++) {
observe(items[i])
}
}
}
function defineReactive (data, key, value) {
// 递归的重要步骤
// 因为对象里面可能有对象或者数组所以需要递归
observe(value)
// 核心
// 拦截对象里每个key的get和set属性,进行读写监听
// 从而实现了读写都能捕捉到,响应式的底层原理
Object.defineProperty(data, key, {
get () {
return value
}
set (newVal) {
if (newVal === value ) return
value = newVal
}
})
}
function (value) {
// 如果传进来的是对象或者数组则进行响应式处理
if (Object.prototype.toString.call(value) === '[object Object]' || Array.isArray(value)) {
return new Observer(value)
}
}