| |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
-> JavaScript知识库 -> 说说Vue的核心特点(一,数据驱动) -> 正文阅读 |
|
[JavaScript知识库]说说Vue的核心特点(一,数据驱动) |
? ? ? ? Vue.js的核心思想之一就是数据驱动。所谓数据驱动就是视图是依靠数据驱动实现的,我们对视图的修改不会直接去操作DOM,而是通过操作数据,Vue帮我们修改视图。相比于传统的jQuery的原生DOM开发,大大简化了代码量,而且用户体验也更加友好,不需要花费大量精力去维护。尤其是在业务量十分复杂的时候,只关心数据的改变,会让代码的逻辑更加清晰,也更易于维护。 下面我们基于Vue的源码来分析,数据是如何渲染成最终DOM的 首先我们来看看new Vue 的时候发生了什么(建议在vue官网下载一份源码来学习) ????????我们先从入口文件开始看起,“src/core/instance/index.js”, 这个文件非常简单,其实就是调用了一个初始化函数(this._init(options)),再找到“src/core/instance/init.js” ,其中该初始化函数主要做了以下几件事,合并配置、初始化生命周期(initLifecycle(vm))、初始化事件中心(initEvents(vm))、初始化渲染(initRender(vm))、初始化data(initState(vm))、初始化props(initProvide(cm))、computed、watcher。之后做了最重要的一件事,就是将vm挂载值容器上了(vm.$mount(vm.$options.el)),下面我们接着分析Vue的挂载过程。 Vue中我们是使用$mount方法挂载vm实例的,这个方法在多个文件中都有定义,因为$mount方法的实现是与平台,构建方式挂钩的,所以对于不同平台的vue实现,同样也有着不同的$mount方法,这里我们主要针对compiler版本的进行分析,具体文件在“src/platform/web/entry-runtime-with-compiler.js” ,这段代码主要是对Vue原型上的$mount进行了重写,首先,他对el进行了响应的限制,不能是body、html这样的根节点。然后是一段很关键的逻辑,如果我们没有自己定义render方法,那么它会将el或者template转换为render方法,不管我们是用来单文件的形式(.vue),还是写了el或者template属性,最终都会转换为render方法,这个过程是Vue的一个在线编译的过程,这个编译是通过complierToFuntions,感兴趣的可以去看看。(这里我提一嘴,vue属于是一种编译时+运行时的一种框架),最后调用了原型上的$mount挂载,该方法支持传入两个参数,第一个参数为el,表示挂载的容器元素,可以使字符串,也可以是DOM对象,如果是字符串,会在浏览器环境下转换为DOM对象。第二个参数是与服务端渲染有关的(hydrating),最终$mount回去调用一个,mountCompont方法,该方法定义在“src/core/instance/lifecycle.js” ,在该方法中,主要定义了一个watcher,会调用调用了上面定义的updateComponent方法,在这个方法中,先调用了_render()方法生成虚拟Node,最终调用vm._update方法更新(生成)DOM。这里定义的watcher有两个作用,第一个作用就是初始化执行回调函数,?第二个作用就是监测vm中的数据变化,从而调用回调函数更新DOM。最后判断了一下当前实例是否已经挂载了,挂载过了则会调用响应的钩子(beforeUpdate),没挂载也会调用响应的钩子(mounted),这里的vm.$node,表示的是父虚拟节点,当为null时表示没有挂载。 然后让我们知道了,最终核心的方法是_render(),以及_update() ? ? ? ? 我们先来看看_render方法,该方法是实例上的一个私有方法,用来将实例转化为虚拟Node,定义在文件“src/core/instance/render.js”中。嗯...一般情况下我们都不会手写render方法,一般都是我们写好template模板,然后在挂载之前编译成render,而编译的这个过程较为复杂,在这不展开细说,可能有些人也不理解render方法是个什么样的东西,可以看看下列代码:
?编译成render
会到_render方法中,render方法的调用,根据这行代码
?其实是调用了$createElement方法,所以我们需要跳到createElement方法中继续分析(这里的vm.createElement其实就是定义在一开始初始化渲染的函数中(vm.initRender(options),而除了定义了这个方法,还定义了一个vm._c方法,他是在将模板编译成render的时候调用的,而$createElement是用来解析用户手写render的,两个方法支持的参数都一样,最终都是去调用了createElement方法,返回一个虚拟Node),然后则是根据虚拟Node最终调用_update方法。 在分析最后的createElement和_update之前我们可以先来了解一下虚拟DOM的概念,如果已经知道,可以直接跳过 ????????Virtual DOM这个名词相比大家在一开始接触Vue的时候就已经听过很多遍了,那么既然我们以及有真实的DOM,为什么还需要整个虚拟DOM出来,我们可以试着去浏览器中执行一下一下代码:
可以发现,打印出了特别多的属性,真实的DOM是十分庞大的,因为浏览器把DOM设计的十分复杂。当我们频繁更新真实DOM的时候,就有很大的性能问题。那么虚拟DOM则是用js对象去描述DOM节点,所以它比创建一个真实的DOM要轻松的多。在Vue.js中虚拟DOM是用一个VNode的class去描述的(“src/core/vdom/vnode.js”),但是这个类中其实还是比较复杂的,实际上Vue.js中是借鉴了一个第三方库snabbdom,然后添加了Vue.js中的特色的一些东西。而虚拟DOM的出现就是为了映射真实的DOM结构,不需要 类似DOM身上的那些API,所以更加轻量简单。 ? ? ? ? 了解完虚拟DOM之后我们会到之前的地方,通过createElement方法创建虚拟DOM,该方法定义在“src/core/vdom/create-element.js”,嗯... 该方法其实是对_createElement方法的封装维护,使传入的参数更加灵活,处理完参数之后,就去调用了_createElement,该方法就在上面方法的下面,_createElement主要的两个流程就是,规范化children、创建VNode,该方法有五个参数,分别是context(VNode上下文环境,Component类型)、tag(标签名,可以使字符串,也可以是Component类型)、data(VNode的数据,VNodeData类型数据),children(子节点)、normalizationType(子节点规范的类型,主要是参考render是用户手写的还是生成的),可以看到,只有第四个参数children是没有做任何类型限制的,所以_createElement第一个首要的任务就是,规范子节点的数据类型,这里根据normalizationType的不同,调用了两个方法,分别是,normalizeChildren(children)、simpleNormalizeChildren(children),他们定义在“src/core/vdom/helpers/normalzie-children.js”中。simpleNormalizeChildren方法调用场景为render函数是编译生成的,normalizeChildren方法的调用场景有两种,一个是render方法是有用户自己手写的,另一个场景是编译slot、v-for这种会产生嵌套数组的时候。该方法接受两个参数,第一个参数是children,表示要规范的子节点,第二个参数为nextedIndex,表示嵌套的索引,由于单个子节点的类型可能是基本类型,也可能是一个数组,也可能是一个列表,所以当为数组的时候就递归调用该方法,为基本类型的时候就调用createTextNode方法转换为VNode类型,当为列表且还存在嵌套的时候则根据嵌套索引更新Key;值得注意的是,这里的三总情况都做了一个操作,就是如果存在两个连续的text节点,会对其做合并操作。经过以上children规范之后,children就变成了一个VNode的Array了,接下来,就是通过createElement创建生成VNode了。 ? ? ? ? 回到createElement函数,规范完之后,对tag进行判断,如果是个字符串,则继续判断是否是内置的标签节点,如果是,则直接创建一个普通的VNode,如果不是内置的,而是一个已注册的组件名,则通过createComponent创建组件类型的VNode,否则创建一个未知标签的VNode;那如果tag不是一个字符串,而是一个Component类型的话,就直接通过createComponent创建组件类型的VNode,最后返回创建完成的VNode,去到_update渲染成真实DOM挂载到页面 ????????_update()方法是实例的一个私有方法,被调用的时机有两个,一个是首次渲染的时候,一个是数据更新的时候;这次我们只看首次渲染的时候,该方法定义在“src/core/instance/lifecycle.js”,该方法核心就是调用了vm.__patch__方法,嗯.....这个方法也存在不同平台的差异,我们只分析web版本的,定义在“src/platforms/web/runtime/index.js”中,可以注意到,是否为服务端渲染也会影响__patch__方法的执行,因为服务端渲染没有真实的浏览器DOM环境,所以也不需要将虚拟DOM转为真实的DOM,在浏览器端,最终指向了patch方法,该方法定义在“src/platforms/web/runtime/patch.js”,该方法最终是调用了另一个方法的返回值(createFunction,定义在“src/core/vdom/patch.js”),这个方法最终返回了一个方法给到patch调用;其中调用createFunction的时候传入了两个参数,一个是nodeOps(封装了一些DOM方法),另一个参数是modules(定义了一些模块钩子的实现);接着我们来分析下其内部做了什么事,主要看看返回的patch方法,该方法接受四个参数,oldNode表示旧的VNode节点,可以为null,或者是一个DOM对象vnode表示通过_render渲染出来的VNode节点,hydrating表示是否为服务端渲染,removeOnly是用来给transtion-group使用的,这里不做拓展;其实这里主要的参数就是前两个;这里由于逻辑分支太复杂,我们用文章上面的一段render作为例子:
此时的四个参数就分别为,第一个参数就是传入的vm.$el的一个DOM对象,表示的就是index.html模板中的<div id="app"></div>,第二个参数则是上面render函数的返回值,最后两个参数都为false,确定参数之后我们来看看patch的几个关键逻辑,如下:
因为我们传入的oldVNode是一个DOM容器,所以这里isRealElement判断为true,然后就通过emptyNodeAt方法将oldVNode转化为VNode对象,然后调用createEle方法,该方法十分重要,让我们来看看:
该方法的作用就是通过虚拟DOM创建为真实的DOM插入到父节点中;这createComponent是尝试创建子组件,这里不做拓展,在当前我们这个例子的情况下,它的返回值为false;接下来判断VNode是否包含tag,如果包含,则需要对其做简单的合法性校验,然后再去调用平台的DOM方法创建一个占位元素,然后调用createChildren方法创建子节点,该方法比较简单,就是递归调用createEle创建节点(这里就是利用了深度搜索),这里在递归遍历的时候,会把父容器的DOM节点的占位元符传入;接着执行invokeCreateHooks执行所有的create钩子,将VNode? push到insertedVnodeQueue中;最终调用insert方法将DOM节点插入到父节点中(就是当初的第一个参数vm.$el),因为是递归调用,所以子节点会最先插入,所以顺序是先子后父;insert方法定义在“src/core/vdom/patch.js”,insert方法很简单,就是通过一些辅助方法将DOM插入到父节点中,而这些辅助方法也就是调用了原生DOM函数(定义在“src/platforms/web/runtime/node-ops.js”),绕了那么多到这,是不是突然豁然开朗了,本质上还是调用了原生方法动态的创建DOM;在这个例子中子节点就是一个文本节点,父节点就是id为app的div,实际就是递归创建了一颗完整的DOM树插入到了Body中; 最终附上Vue技术揭秘中的图片 ? ?以上就是首屏数据驱动的过程了,想看更详细的讲解可以到数据驱动看 |
|
JavaScript知识库 最新文章 |
ES6的相关知识点 |
react 函数式组件 & react其他一些总结 |
Vue基础超详细 |
前端JS也可以连点成线(Vue中运用 AntVG6) |
Vue事件处理的基本使用 |
Vue后台项目的记录 (一) |
前后端分离vue跨域,devServer配置proxy代理 |
TypeScript |
初识vuex |
vue项目安装包指令收集 |
|
上一篇文章 下一篇文章 查看所有文章 |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 | -2024/11/23 10:09:07- |
|
网站联系: qq:121756557 email:121756557@qq.com IT数码 |