摘要:所以方法,是对默认进行扩展,从而实现扩展。这里我用了这个库提供的合并方法,用来合并两个对象,并不会修改原对象的内容。测试符合我们的预期,方法也就实现了,下一步,实现父子组件。系列文章地址优化优化总结
看这篇之前,如果没有看过之前的文章,移步拉到文章末尾查看之前的文章。
组件的扩展在 Vue 中有 extend 方法可以扩展 Vue 的实例,在上一步中,有一些实现是必须要通过子父组件才能实现,而子组件相当于一个特殊的 Vue 实例,所以这步,我们先把这个扩展实例的方法实现。
我们先来看看官网对于 extend 方法的介绍:
使用基础 Vue 构造器,创建一个“子类”。参数是一个包含组件选项的对象。
从后面一句和具体的使用方法可以得出其实是我们创建实例时,对于传入参数的扩展。对于这个参入参数我们就叫它 options。
我们接着往下想,既然这个 options 是能扩展的,那么原 Vue 类下,肯定保存着一个默认 options ,而创建 Vue 实例时,会把传入的 options 和默认的 options 进行合并。
所以 extend 方法,是对默认 options 进行扩展,从而实现扩展。
mergeOptionsok 有了思路,我们来实现它:
首先是默认的 options ,同时我们假设一个方法(mergeOptions)用来合并 options
let uid = 0 export class Vue extends Event { ··· _init(options) { let vm = this // 为了方便引用合并的 options 我们把它挂载在 Vue 实例下 vm.$options = mergeOptions( this.constructor.options, options, vm ) ··· } } // 默认的 options Vue.options = { // 组件列表 components: {}, // 基类 _base: Vue }
接着我们来实现 mergeOptions :
import R from "ramda" export function mergeOptions(parent, child) { // data/methods/watch/computed let options = {} // 合并 data 同名覆盖 options.data = mergeData(parent.data, child.data) // 合并 methods 同名覆盖 options.methods = R.merge(parent.methods, child.methods) // 合并 watcher 同名合并成一个数组 options.watch = mergeWatch(parent.watch, child.watch) // 合并 computed 同名覆盖 options.computed = R.merge(parent.computed, child.computed) return options } function mergeData(parentValue, childValue) { if (!parentValue) { return childValue } if (!childValue) { return parentValue } return function mergeFnc() { return R.merge(parentValue.call(this), childValue.call(this)) } } // 由于 watcher 的特殊性,我们不覆盖同名属性,而是都保存在一个数组中 function mergeWatch(parentVal, childVal) { if (!childVal) return R.clone(parentVal || {}) let ret = R.merge({}, parentVal) for (let key in childVal) { let parent = ret[key] let child = childVal[key] if (parent && !Array.isArray(parent)) { parent = [parent] } ret[key] = parent ? parent.concat(child) : Array.isArray(child) ? child : [child] } return ret }
目前我们仅仅实现了 data/methods/watch/computed 这 4 个 options 中的内容,所以我们先合并这 4 项,由于 data/methods/computed 这 3 项是具有唯一性(比如 this.a 应该是一个确定的值),所以采用同名属性覆盖的方式,而 watch 是当发生变化时候执行方法,所以所有注册过的方法都应该执行,因而采用同名属性的内容合并成一个数组。
这里我用了 ramda 这个库提供的合并方法,用来合并两个对象,并不会修改原对象的内容。
extendok 合并 options 的方法写好了,我们接着来实现 extend 同过上面的分析,extend 函数仅仅是对默认 options 的扩展
Vue.extend = function (extendOptions) { const Super = this class Sub extends Super { constructor(options) { super(options) } } Sub.options = mergeOptions( Super.options, extendOptions ) Sub.super = Super Sub.extend = Super.extend return Sub }
同样的我们使用 mergeOptions 来合并一下 options 即可,同时将 super 指向父类、获取 extend 方法。
测试import {Vue} from "./Vue.mjs" let subVue = Vue.extend({ data() { return { dataTest: 1 } }, methods: { methodTest() { console.log("methodTest") } }, watch: { "dataTest"(newValue, oldValue) { console.log("watchTest newValue = " + newValue) } }, computed: { "computedTest": { get() { return this.dataTest + 1 } } } }) let test = new subVue({ data() { return { subData: 11 } }, methods: { subMethod() { console.log("subMethodTest") } }, watch: { "subData"(newValue, oldValue) { console.log("subWatch newValue = " + newValue) } }, computed: { "subComputed": { get() { return this.subData + 1 } } } }) console.log(test.dataTest) // 1 console.log(test.subData) // 11 console.log(test.computedTest) // 2 console.log(test.subComputed) // 12 test.methodTest() // methodTest test.subMethod() // subMethodTest test.dataTest = 2 // watchTest newValue = 2 test.subData = 12 // subWatch newValue = 12 console.log(test.constructor === subVue) // true console.log(subVue.super === Vue) // true
ok 符合我们的预期,extend 方法也就实现了,下一步,实现父子组件。
系列文章地址VUE - MVVM - part1 - defineProperty
VUE - MVVM - part2 - Dep
VUE - MVVM - part3 - Watcher
VUE - MVVM - part4 - 优化Watcher
VUE - MVVM - part5 - Observe
VUE - MVVM - part6 - Array
VUE - MVVM - part7 - Event
VUE - MVVM - part8 - 优化Event
VUE - MVVM - part9 - Vue
VUE - MVVM - part10 - Computed
VUE - MVVM - part11 - Extend
VUE - MVVM - part12 - props
VUE - MVVM - part13 - inject & 总结
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/94796.html
摘要:看这篇之前,如果没有看过之前的文章,移步拉到文章末尾查看之前的文章。而该组件实例的父实例却并不固定,所以我们将这些在使用时才能确定的参数在组件实例化的时候传入。系列文章地址优化优化总结 看这篇之前,如果没有看过之前的文章,移步拉到文章末尾查看之前的文章。 前言 在上一步,我们实现 extend 方法,用于扩展 Vue 类,而我们知道子组件需要通过 extend 方法来实现,我们从测试例...
摘要:通过装作这些变化,我们实现了从而到达了数据变化触发函数的过程。于此同时,我们还实现了来扩展这个可响应的结构,让这个对象拥有了触发和响应事件的能力。最后,根据我们的实现,这是最终的产出,一个框架,了解一下系列文章地址优化优化总结 看这篇之前,如果没有看过之前的文章,移步拉到文章末尾查看之前的文章。 provide / inject 在上一步我们实现了,父子组件,和 props 一样 pr...
摘要:事件是什么在标准浏览器中,我们经常使用来为一个添加一个事件等。仔细看这些情况,归结到代码中,无非就是一个行为或情况的名称,和一些列的动作,而在中动作就是,一系列的动作就是一个函数的集合。 看这篇之前,如果没有看过之前的文章,可拉到文章末尾查看之前的文章。 事件是什么? 在标准浏览器中,我们经常使用:addEventListener 来为一个 DOM 添加一个事件(click、mouse...
摘要:关于中的的实现,差不多也就这样了,当然这仅仅是基础的实现,而且视图层层渲染抽象成一个函数。不同于中的实现,这里少了很多各种标记和应用标记的过程。 看这篇之前,如果没有看过之前的文章,可拉到文章末尾查看之前的文章。 回顾 首先我们思考一下截止当前,我们都做了什么 通过 defineReactive 这个函数,实现了对于数据取值和设置的监听 通过 Dep 类,实现了依赖的管理 通过 Wa...
摘要:在中关于如何实现在网上可以搜出不少,在看了部分源码后,梳理一下内容。换个说法,当我们取值的时候,函数自动帮我们添加了针对当前值的依赖,当这个值发生变化的时候,处理了这些依赖,比如说节点的变化。 在 VUE 中关于如何实现在网上可以搜出不少,在看了部分源码后,梳理一下内容。 首先,我们需要了解一下 js 中的一个 API :Object.defineProperty(obj, prop,...
阅读 565·2021-11-22 14:45
阅读 3051·2021-10-15 09:41
阅读 1494·2021-10-11 10:58
阅读 2674·2021-09-04 16:45
阅读 2584·2021-09-03 10:45
阅读 3217·2019-08-30 15:53
阅读 1203·2019-08-29 12:28
阅读 2101·2019-08-29 12:14