1、构造器
constructor(propsArgus = {}) {
const {
data = () => {},
methods = {},
watch = {},
computed = {}, // 待实现
props = {}, // 待实现
created = () => {}, // created钩子函数
mounted = () => {}, // mounted钩子函数
destroyed = () => {} // destroyed钩子函数
} = propsArgus
this.$watch = watch // watch方法集合
this.$watchers = {} // 收集器集合
this.$methods = methods // 执行方法集合
this.$data = data() || {} // 数据定义集合
this.$computed = computed // 计算属性集合 待开发
this.$props = props // 传递属性集合
this.$created = created // created生命周期注入
this.$mounted = mounted // mounted生命周期注入
this.$destroyed = destroyed // destroyed生命周期注入
bindProperties(this, this.$data)
this.observe(this)
bindProperties(this, this.$methods)
this.$created() // created钩子执行 在dom挂载之前执行
}
2、属性方法讲解及示例
1、data
写法是不是跟vue2一样啊,都是函数返回对象形式
data: () => {
return {
article: {
title: '前标题',
text: {
word: '文字'
}
},
author: '前林大大哟'
};
}
2、methods
方法对象,跟vue2写法一致
methods: {
onButtonClick() {
this.article.title = "标题"
this.author = '林大大哟'
},
itemClick(item, type) {
console.log(item, type);
}
}
3、watch
监听引用类型和基本类型,但写法都是一致的,和vue2不同的是并没有对对象内部单个值进行监听
watch: {
author(value) {
console.log("watch 监听author:", value);
},
article(value){
console.log("watch 监听 article.title:", value);
}
}
4、created, mounted, destroyed
这三个生命周期都和vue2用法一致,内部核心如下,这一点我倒是没有参考任何东西,根据自己想法写的~
3、核心源码讲解
数据响应式,是mvvm架构核心,vue2用的是Object.defineProperty,vue用的是Proxy,那我将这两者结合起来用,引用类型我用Proxy监测,基本类型我用Object.defineProperty监测
源码如下,都是遍历data中的数据,当发现是基本类型时候,用Object.defineProperty转换成响应式类型,而updateWatcher方法,就是被动触发页面结构中包含这数据的dom,然后让他进行更新(这部分vue源码中清除解释了,将同一数据的dom都放进收集器中,当此数据需要更新时,则通知收集器中的各个dom进行依赖更新),而watch对象中有监听这数据的话,就主动的去触发watch中的方法:data.$watch[key].call(data, val)。
/**
* 转换观测模式
* @param {*} data 当前作用指针
* @returns
*/
observe (data) {
if (!data || getDataType(data) !== 'object') { // 按顺序写一般都不会进入这一步 用于初始化
return
}
const arrayMethods = ['push', 'pop', 'shift', 'unshift', 'splice', 'reverse', 'sort']
const keys = Object.keys(this.$data)
for (let key of keys) {
let value = data[key]
if (getDataType(value) === 'object' ||
getDataType(value) === 'array' ||
getDataType(value) === 'null'
) {
value = getDataType(value) === 'null' ? {} : value // Proxy不能传入不是对象的类型,所以在这里转一下
data[key] = this.deepProxy(key, value)
} else {
Object.defineProperty(data, key, {
configurable: false,
enumerable: true,
set(val) {
value = val
updateWatcher(this.$watchers[key], val) // 数据改变 watcher触发修改对应界面上的值
if (data.$watch[key]) {
data.$watch[key].call(data, val) // 主动触发watch对象的方法
}
},
get() {
return value
}
})
}
}
}
当发现是引用类型时候,就用Proxy转换此引用类型,当然至于LveProxy类是啥样,这个我下篇再来写,核心其实就是Proxy和Reflect
/**
* 深度遍历并转换为自定义Proxy代理
* @param {*} obj
* @returns
*/
deepProxy(key, obj) {
if (getDataType(obj) === 'array') { // 数组深度遍历
obj.forEach((v, i) => {
if (getDataType(obj) === 'object') {
obj[i] = this.deepProxy(v)
}
})
} else { // 对象深度遍历
if (Object.keys(obj).length === 0) return
Object.keys(obj).forEach(v => {
if (getDataType(obj[v]) === 'object') {
obj[v] = this.deepProxy(key, obj[v])
}
});
}
const proxy = new LveProxy(this, obj).init(key) // proxy代理
return proxy
}
所以组件类的大体逻辑出来了,通过构造器将data,watch等这些传进去,然后mount到dom后,逻辑层和视图层就有了关联,逻辑层中数据以data中的为主,在dom中有关于data中的数据,都会被加入对应的收集器中(依赖收集),当data有数据更改,会将收集器中的依赖进行统一更新(依赖更新),至此MVVM框架的核心也就出来了,当然至于Proxy和模板如何转换的,那我将在下一篇博客中讲解~