内容简介:在上一步,我们实现从例子可知:所以我们这一步要做两件事
在上一步,我们实现 extend
方法,用于扩展 Vue
类,而我们知道子组件需要通过 extend
方法来实现,我们从测试例子来入手,看看这一步我们需要实现什么:
let test = new Vue({ data() { return { dataTest: { subTest: 1 } } }, components: { sub: { props: { propsStaticTest: { default: 'propsStaticTestDefault' }, propsDynamicTest: { default: 'propsDynamicTestDefault' } }, watch: { 'propsDynamicTest'(newValue, oldValue) { console.log('propsDynamicTest newValue = ' + newValue) } } } } }) 复制代码
从例子可知: sub
是 test
的子组件,同时 test
组件向 sub
组件传递了 propsStaticTest/propsDynamicTest
两个 props
。
所以我们这一步要做两件事
- 实现子组件生成树结构
-
实现
props
,从例子上可以看出需要实现静态和动态两种prop
VUE 中组件的生成
虽然在之前的步骤中,我们一直没有涉及到模板,仅仅是把页面的渲染抽象成一个函数,主要是为了把 MVVM
中的数据绑定过程给解释清楚,但是父子组件的实现却必须要通过模板来联系,所以我们这里简单的介绍下 Vue
中由模板到生成页面渲染函数的过程
-
得到模板(
DOM
字符串)或是render
函数 -
分析模板,得到
HTML
语法树(AST
),生成render
函数。如果直接给的是render
则没有这个步骤 -
由
render
函数生成VNode
这就是虚拟树了 -
将
Vnode
作为参数传入一个函数中,就能得到html
渲染函数
ok 看起来和组件好像没有什么关系,我们分析下组件写法
<sub propsStaticTest="propsStatiValue" :propsDynamicTest="dataTest.subTest"> 复制代码
由上面这个标签我们可以得到什么?
-
这是一个子组件,组件名:
sub
-
传递了一个静态的
prop
:propsStaticTest
-
传递了一个动态的
prop
:propsDynamicTest
静态说明这个属性不会发生变化,动态会,最明显的区别就是:动态属性有 :/v-bind
修饰
结合上面的第2个步骤,会分析出一些东西。仅仅针对 props
,假设模板解析引擎会解析出下面这样一个结构
let propsOption = [{ key: 'propsStaticTest', value: 'propsStaticValue', isDynamic: false }, { key: 'propsDynamicTest', value: 'dataTest.subTest', isDynamic: true }] 复制代码
注:这里仅仅是我的假设,方便理解,在 Vue
中的模板解析出来的内容要比这个复杂。
ok 有了上面的铺垫我们来实现父子组件和 props
父子组件
实例初始化的实例我们需要做的仅仅就是保存组件之间的关系就行,ok 我们来实现它
class Vue extends Event { ··· _init(options) { let vm = this ··· // 获取父节点 let parent = vm.$options.parent // 将该节点放到父节点的 $children 列表中 if (parent) { parent.$children.push(vm) } // 设置父节点和根节点 vm.$parent = parent vm.$root = parent ? parent.$root : vm // 初始化子节点列表 vm.$children = [] } } 复制代码
我们需要做的仅仅就是给传入 options
设置 parent
,就能明确组件之间的关系。
接着我们模拟一下当模板编译的时候碰到 <sub></sub> 的情况,具体的来说就是会执行以下代码:
let testSubClass = Vue.extend(test.$options.components.sub) let testSub = new testSubClass({parent: test}) console.log(testSub.$parent === test) // true 复制代码
ok 现在我们先不想模板编译具体是如何进行的,从这两行代码中,我们可以看出我们先使用了 extend
扩展了 Vue
实例,生成一个子类( testSubClass
),接着我们实例化该类,传入参数确定父实例。
想象下一,我们为什么要分两步把参数传入。
我们知道当我们写好子组件的配置时,子组件的内部状态就已经确定了,所以我们可以根据这些固定的配置去扩展 Vue
类方便我们调用(使用的时候 new
一下就可以)。
而该组件实例的父实例却并不固定,所以我们将这些在使用时才能确定的参数在组件实例化的时候传入。
接着我们来想象一下,如果子组件( sub
)里面还有子组件( sub-sub
)会怎么样?
-
使用
extend
扩展Vue
类 -
确定父实例,
new
的时候传入,而这个parent
就是sub
这样调用过多次之后,一颗 Vue
的实例树就生成了,每一个节点都保留着父实例的引用,子组件列表还有根实例。
希望你的脑子里已经长出了这颗树~
ok 接下来我们来实现 props
props
希望你还记得下面这几行代码:
let propsOption = [{ key: 'propsStaticTest', value: 'propsStaticValue', isDynamic: false }, { key: 'propsDynamicTest', value: 'dataTest.subTest', isDynamic: true }] 复制代码
这个是我们模拟模板编译时关于 props
的部分产出,具体的来说就是键值对,以及是否有 :/v-bind
修饰,而我们知道在 Vue
中这个修饰符是表示是否是动态绑定,所以我在这里使用 isDynamic
来标志。
首先我们来获取属性的数据,由于动态绑定的 props
是取值路径,所以我们得去父对象下获取值。
let propsData = {} propsOption.forEach(item => { if (item.isDynamic) { // eg: 'dataTest.subTest' => test.dataTest.subTest 将字符串转换成取值 propsData[item.key] = item.value.split('.').reduce((obj, name) => obj[name], test) } else { propsData[item.key] = item.value } }) console.log(propsData) // { propsStaticTest: 'propsStaticValue', propsDynamicTest: 1 } 复制代码
ok 我们拿到中属性对应的值,接着把 propsData
给传进去
let testSub = new testSubClass({parent: test, propsData}) 复制代码
接着我们在 _init
方法中来处理 props
_init(options) { ··· let props = vm._props = {} let propsData = vm.$options.propsData for (let key in vm.$options.props) { let value = propsData[key] // 如果没有传值,使用默认值 if (!value) { value = vm.$options.props[key].default } props[key] = value } observe(props) for (let key in props) { proxy(vm, '_props', key) } ··· } 复制代码
porps
的处理和 data
类似,需要变成可监听结构,代理到 this
对象下,无非 data
是从传入的函数取值,而 props
从传入的 propsData
中取值。
ok 直到现在为止,看起来都很美好,但是部分 props
是动态的,父组件相应值的变化是需要同步到子组件中的,但目前我们还没有实现父组件和子组件的联系,仅仅是把值给取出放在子组件内而已。
其实一看到监听变化就理所当然的想到 Watcher
,ok 我们用 Watcher
来实现它:
propsOption.forEach(item => { if (item.isDynamic) { new Watcher({}, () => { return item.value.split('.').reduce((obj, name) => obj[name], test) }, (newValue, oldValue) => { testSub[item.key] = newValue }) } }) 复制代码
ok 最后一步完成, 完整的测试代码
本来还想实现下 provide/inject
但篇幅有点大了,下一步实现,也做个总结。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
莱昂氏UNIX源代码分析
(澳)John Lions / 尤晋元 / 机械工业出版社 / 2000-7-1 / 49.00
本书由上、下两篇组成。上篇为UNIX版本6的源代码,下篇是莱昂先生对UNIX操作系统版本6源代码的详细分析。本书语言简洁、透彻,曾作为未公开出版物广泛流传了二十多年,是一部杰出经典之作。本书适合UNIX操作系统编程人员、大专院校师生学习参考使用。一起来看看 《莱昂氏UNIX源代码分析》 这本书的介绍吧!