deep-in-vue
deep-in-vue copied to clipboard
从源码的角度看vue的成长历程。
## the super tiny vue.js 一个极简版vue的实现,希望对于想了解vue内部实现原理以及学习其源码而又无从下手的人有所帮助。 ### 实现功能: * v-text、v-model、v-on-[event]指令 * seter/getter数据监测,自动更新 * mvvm简易模型
为什么要单独将v-if/v-for指令提出来单独说明,因为与v-text/v-show等指令不同,他们会改变DOM结构,改变DOM结构就会影响到vue的编译过程,同时会涉及到重新编译的过程。 以v-for为例,根据条件的不同,v-for指令会删除或是新增该DOM节点,如果是删除,那么会导致父节点的子元素的个数减少一个,那么久会影响父节点后续节点的编译,新增的过程会更麻烦,因为新增后的节点需要重新编译,为了不影响整个组件,所以需要单独进行编译处理。 ### DOM结构更改问题 因为指令的预处理会更改节点的结构,所以会对其余节点的编译,所以需要自己了解哪些节点已编译,哪些节点是未编译的,不会少编译也不会重复编译。 如下为compilerNode函数的部分代码: ```js function compilerNode (el) { for (; el.index < el.childNodes.length; el.index++) { /** * el.index 表示当前第几个子元素,在compileNode函数中可能会更改el的子元素结构, * 所以需要el.index来标识编译的节点索引 */ let child = el.childNodes[el.index]; compileNode(child);...
为了更好的了解数据驱动更新原理,我们先来要理解如下几个组成部分: * data * template * directive * binding ### data -> template 模板使用data中的数据来实现动态渲染,如下所示: Template ```html ``` Data ```js new TinyVue( data: { hello: 'Hello World!' } ) ``` 模板中的`v-text='hello'`会渲染为`data.hello`的值,记得到如下的html....
对于刚接触vue的同学会经常遇到数据更新了但是模板没有更新的问题,下面将结合vue的响应式特性以及异步更新机制分析常见的错误: ### 异步更新带来的数据响应式误解 异步数据的处理基本是一定会遇到的,处理不好就会遇到数据不更新的问题,但有一种情况是在未正确处理的情况下也能正常更新,这就会造成一种误解,详情如下所示: * 模板 ```html {{dataObj.text}} ``` * js ```js new Vue({ el: '#app', data: { dataObj: {} }, ready: function () { var self = this; /**...
### 按索引取值监测 因为数组属于对象类型,所以对于`arr[0]`形式取值与普通对象无异,都可以通过Object.defineProperty来对其进行处理,从而达到响应式的目的。 如下所示: ```js var person = [{}] var p = [] Object.defineProperty(person, '0', { get: function () { return p[0]; }, set: function (val) { p[0] = val;...
组件系统实现原理
组件系统功能说明: * 全局组件 * 局部组件 * 组件间参数传递(props) ### 组件系统结构 组件按父子关系形成一个组件树,父子组件具有自己的上下文即他们有自己的viewModel结构(可能是一个单独的vm实例,也可能是vm树形结构),父子之间的数据是不能直接访问的。 当数据发生变化时,组件单独更新。 ### 组件编译 以``组件为例,当解析该组件的时候,对该组件进行编译并将编译后的结果替换``自定义标签,这样就将父子组件关联起来。 说明,因为是自定义标签,所以需要声明组件名,以此和自定义标签名关联起来。因为html规范中不区分大小写,所以只能以中横线的形式命名,考虑到编码方面的便利,需要在驼峰和中横线直接进行转换。 ### 组件间通信 ```js ` ``` 通过props传递信息,支持静态属性和动态属性。 如上`:msg`即为动态属性,`message`对于父组件的一个属性,当`message`发生变更时,子组件能够监听到变化并自动更新。 具体实现还是通过`$watch`方法来监听即可,在子组件中watch父组件的值,当发生变化时触发自身指令的更新即可。
(注:这里是笔者自己尝试的一种实现方式,也许与vue的实现方式会有所区别) 这里对数据的响应式定义进行了调整,之前的tinyVue采用的是遍历 DOM 节点,获取指令对应的key然后将获得的key所对应的对象进行了getter/setter操作的处理,这种实现方式对watch功能和计算属性的实现不便,所以更改为直接将 option.data 对象转换为响应式对象,这样就不会受限于模板中声明的指令对应key的限制,从而未绑定指令的key也可以转换为可监测的。 tinyVue中是通过给每个key建立对应的binding对象来实现响应式的(setter/getter监测由binding对象内部实现)。tinyVue则维护数据模型得到的所有的binding,存储在_bindings中([关于binding的详细说明可参考这里](https://github.com/xiaofuzi/deep-in-vue/issues/8))。 ### 什么是watch监测? 简单的说就是监测到某一属性的变化后执行相应的回调。 例如: ```js var vm = new TinyVue({ data: { name: 'xiaofu', info: { height: 170 } }, watch: { name:...
[源码:https://github.com/xiaofuzi/re-vue/tree/9de26c017dc937e19faec6b962d28a444cea7af4](https://github.com/xiaofuzi/re-vue/tree/9de26c017dc937e19faec6b962d28a444cea7af4) ### API * el Type: `String | Node` 根节点选择器或是根节点dom元素。 * data Type: `Object` 初始化响应式数据模型 * computed Type: `Object` 计算属性,每一个元素对应一个函数 注: * computed属性依赖于data中的响应式数据 * computed属性可依赖computed属性 * computed禁止赋值操作 * methods Type:...
### 指令式声明 指令式声明以其简洁、复合html语法、易于学习等特点为其带来很大的优势,但在可编程性方面却很差。 ### jsx/hyperscript * jsx 在保留了HTML似的模板特性的基础上,增加了逻辑编程的能力,即结合了html和javascript两者的有点。 * hypescript 采用函数式的编程方式,完全放弃了模板特性,充分发挥逻辑编程的能力。 HTML一直在web编程中占有很大的地位,HTML这种标记类型的语言的确经受住了万维网发展的考验,在文本传输和显示上如鱼得水。但现在对网页的要求已不再是内容的展示,UI交互已成为重点,而这点上,HTML显得很乏力。 从组件化的角度来考虑,HTML也有局限,复用性和正交性比较弱,而这在组件化方案中是很受重视的。 函数式编程思想逐渐得到大众认可,其优点也逐渐凸显,基于函数式的UI生成方式也成为一种不错的选择. ### 函数指令解决方案 * 一组标签原子API,类似于hyperscript * 函数指令API,如 vfor, vif等 采用函数生成dom而不是解析dom的形式,这样可以避免dom解析遍历资源浪费的问题,因为dom是通过相应的函数指令生成的,所以viewModel可以精确的追踪view中的相关节点。 待更新。。。
### vue数据响应式特性 vue通过Object.defineProperty方法实现对数据更改的监测来实现数据的响应式,这是vue比较让人着迷的一个特性,刚接触vue的时候真的太为它着迷了,但也有它自身的问题。 #### Object.defineProperty简要说明: ```js function bindAccessors (vm, key, binding) { Object.defineProperty(vm, key, { get: function () { return binding.value; }, set: function (value) { binding.value = value; binding.directives.forEach(function...