预编译节点
❶ vc++6.0编译时出现错误,如何调整工具栏目录路径
致命错误 C1010
在查找预编译头时遇到意外的文件结尾。是否忘记了向源代码中添加“#include name”?
用 /Yu 指定的包含文件没有列在源文件中。在大多数的 Visual C++ 项目类型中,此选项默认是启用的,而且“stdafx.h”是此选项指定的默认包含文件。
在 Visual Studio 环境中,请使用下列方法之一消除此错误:
如果项目中没有使用预编译头,请将源文件的“创建/使用预编译头”属性设置为“不使用预编译头”。若要设置此编译器选项,请遵循以下这些步骤:
在项目的“解决方案资源管理器”窗格中,右击项目名称,再单击“属性”。
在左窗格中单击“C/C++”文件夹。
单击“预编译头”节点。
在右窗格中单击“创建/使用预编译头”,再单击“不使用预编译头”。
确保您没有在不注意的情况下从当前项目中删除、重命名或移除头文件(默认为 stdafx.h)。还需要使用 #include "stdafx.h" 在源文件中的任何其他代码之前包含这一文件。(此头文件被指定为“通过文件创建/使用 PCH”项目属性)
http://msdn.microsoft.com/zh-cn/library/d7fz9ckx.aspx
❷ Web前端工程师该如何选择web前端模板引擎
今天小编要跟大家分享的文章是关于Web前端工程师该如何选择web前端模板引擎?如果你正在从事web前端工作,如果你也想要了解如何选择web前端模板引擎的知识,那么就来和小编一起看一看本文为大家介绍的内容吧~
一、Web模板就在那里
模板引擎负责组装数据,以另外一种形式或外观展现数据。浏览器中的页面是Web模板引擎最终的展现。
无论你是否直接使用模板引擎,Web模板一直都在,不在前端就在后端,它的出现甚至可以追溯到超文本标记语言HTML标准正式确立之前。
二、服务端的模板引擎
我所知道最早的Web模板引擎是PHP,它正式诞生于1997年,工作在服务器端。让我们看看PHP官方的intro-whatis:
PHP(“PHP:Hypertext
Preprocessor”,超文本预处理器的缩写)是一种被广泛应用的开放源代码的多用途脚本语言,它可嵌入到HTML中,尤其适合web开发。
PHPer普遍赞同PHP本身就是最天然、原生的PHP模板引擎,因为她本来就是。在PHP的世界里多次出现过再包装的模板引擎,着名的有
smarty。
其它服务器端语言很多都有HTML模板引擎,比如JSP、mustache。
毫无疑问,这些服务器端模板引擎最终生成的结果是HTML(XML)字符串,处理流程逻辑使用宿主语言本身的语法实现。
它们的共同特征:HTML只是个字符串,最终结果可能还需要类似Tidy这样的清洁或修正验证工具。
这里提出一个问题:二次封装的smarty有存在的必要么?
三、浏览器端的模板引擎
我所知道最早的前端模板引擎是jCT,它托管于Google
Code,诞生于2008年,宿主语言是javaScript,工作在浏览器中。很荣幸,我就是jCT的作者,相关早期博客可以查看achun、githubjCT
备份。
直到今天写这篇文章,我才发现pure-js这篇文章里面也提到不少先行者——jemplate最早在2006年就创建了。
今天在OSC搜索JavaScript模板引擎你会得到100+个结果,下边列举一些:
·轻量度:tpl.js、T.js
·认知度:arttemplate、mustache.js、doT.js、handlebars.js、pug
·DOM-tree-based:domTemplate、transparency、plates
·VDOM-based:htmltemplate-vdom、virtual-stache、html-patcher
·流行框架:Vue.js、ReactJS、riot
·Real-DOM:PowJS
它们的共同特征:全都支持插值。
这里还有templating-engines受欢迎度的对比,甚至best-javascript-templating-engines
投票及正反方的理由。
四、如何选择
我认为存在即合理,每个引擎、框架总有可取之处,至少在你的应用里,在某个时代,所以本文不会评论某个引擎哪一点不好,那样是不客观的。现在回答前边提到的问题:smarty
有存在的必要么?我的答案是:有。理由很简单,看给谁用、看大背景。
对于前后端没有分离的应用,或前端人员对后端语言不够熟悉,或因岗位职责需要,那么前端人员掌握一种比较通用的模板语法(语言)是现实的,反之让PHPer自己去使用
smarty那就太浪费技能了。
下面是通常意义上的引擎选择建议:
1.前提,选择的引擎能满足数据渲染需求,且不和现有依赖冲突,如果你已经非常熟悉某个引擎,那你已经有答案了。
2.是一次性的项目需求么?是的话直接选择轻量的,学习复杂度最低的。
3.是要做组件开发么?
4.引擎支持预编译结果,不必每次都实时编译么?
5.要跨平台么?有官方提供支持的,首选类React-JSX的引擎或纯粹的VDOM引擎。
6.选择学习或维护复杂度最低的,众所周知,开发者对调试的时间超过写代码的时间深恶痛绝。
7.最后才是性能对比,性能对比是一件非常细致的工作,他人的对比结果不一定符合你的场景。
我认为应该弱化语法风格的对比,偏好是没有可比性的,一些语法甚至有特殊的背景原因。
为什么最后才是性能对比?
性能的确很重要,但如果性能还没有影响到你的应用体验度,那就忽视它。很难真实地模拟应用场景,通常只有通过真实场景来检验,目前的测试工具还达不到这种效果。
前述问题有些有固定答案,下面讨论余下的问题:如何考虑组件开发、支持预编译、复杂度?
五、组件开发
进行组件开发已经不再是选择模板引擎的问题了,这是生态环境选择的问题。如果你的应用需要更快地完成,那么时间点是第一位的,就选择流行框架,有足够多的组件让你使用或参考。如果你的应用有独立的生态环境,需要技术选型以便长期维护,那继续看下文。
六、预编译
预编译应该具备:
1.编译结果在目标环境中不再需要编译过程。
2.编译结果可调试性,这意味着结果应该包含原生ECMAScript代码,而不是纯粹的数据描述。
大家都知道React-JSX是支持预编译的,官方的说法是ReactWithoutJSX,即总是build过的。
一些基于字符串处理的引擎也支持预编译。如果你需要预编译,建议抛弃编译结果依然是基于字符串拼接的引擎,那样还不如不预编译,那是HTML5未被广泛支持之前的技术手段。
至少也要有类似React-JSX这样的编译结果才具有可调试性。备注:Vue.js支持多种模板引擎,可达到同样的效果。
原ReactJS代码,其中用到了WebComponents技术:classHelloMessageextendsReact.Component{
render(){
return
❸ android系统为什么历史任务里面总有信息任务
若是要回答这个问题,我们需要追溯到上个世纪,去寻找智能手机的起源。
1965 年,贝尔实验室、通用电气和麻省理工学院开始合作开发一套能够兼顾易用性和强大性的操作系统,经过六年时间的通力协作,贝尔实验室的一名软件工程师 Ken Thompson 在休假期间完成了一个名为 Unix 的系统编写,并最终成为贝尔实验室的母公司、美国电信巨头 AT&T的商业产品,并启动了长达数十年的版权运作。尽管后来有着许多变种,但是从严格意义上来讲,Unix 不是一个开源的操作系统。
1991 年,一个芬兰的大学生、同时也是计算机黑客的 Linus Torvalds,他对 Unix 十分着迷,但是买不起运行 Unix 需要的工作站,所以他就尝试自己以同样的编程方式写了一个名为 linux 的操作系统,并在自由软件之父 Richard Stallman 的精神鼓舞之下,将 Linux 加入到了自由软件基金(FSF)当中,允许所有人使用、拷贝、修改甚至销售 Linux 系统,同时承担开源义务,禁止把 Linux 封闭化的企图。
之所以要如此大费周章的讲述 Unix 和 Linux 两个操作系统的故事,是因为 iOS 和 Android,正是分别基于 Unix 和 Linux 而衍生出来的作品。也就是说,是 Unix 和 Linux 的两种特性,造成了 iPhone 与 Android 手机在使用体验上的巨大差异。
乔布斯曾经邀请 Linux 的创始者 Linus Torvalds 到苹果工作,放弃 Linux 的开源,协助开发 Mac OS 封闭式的 Mach 内核,后者与乔布斯大吵一架之后明确表示拒绝。而从 Mac OS 开始,苹果就将操作系统的私有化视为企业战略,用乔布斯的话来讲,他是将 iOS 装进了 iPhone 这个盒子里,然后卖给了用户。所以,iPhone 之所以不会出现“越用越卡”的情况,是因为苹果公司对它的手机从硬件到软件拥有最高的管理权限,在封闭式的环境中,来自第三方的应用程序无法调用超过 iPhone 承受限度的指令,自然也不可能造成持续性的系统损伤。
反观 Android 手机,由于开源的公开条件,Google 无法从代码这一端口约束第三方的应用程序,同时,由于 Linux 核心设定应用在调取系统功能时一定要取得 ROOT 权限,这也导致大量应用因为单一功能的实现需求而获得整个 ROOT 层面的支配,可以在 Android 手机的任意储存位置进行读写,这种高自由度无异于开启了潘多拉魔盒,让 Android 手机无法对恶意 App 事先设防。这也是开源软件备受争议、且在商用领域遭到抵触的原因:它只关心是否授予了用户自由——这个自由也包括逾越边界的自由——而没有从最坏的出发点去考虑如何规避被滥用的风险。尽管 Google 作为巨头,一直在尝试对产业链进行统一管理,但是当这条产业链日益庞大、连 Google 也只能扮演其中之一的角色时,Android 的失控也就在情理之中了。比如,Android 的最新版本通常需要花费超过一年半的时间,才能使激活它的 Android 手机占比超过 50%,但是 iOS 7 只用了两个月,就让半数以上的 iPhone 都更新完毕。另外,一款应用程序如果被苹果从 App Store 中惩罚出去,它就再也无法被安装到任何一款合法的 iPhone 里面,但是如果一款应用程序被 Google 驱逐出 Google Play,但是它还是可以登录各种第三方应用市场,提供正常的下载和安装。
所以,Android 的这种天生短板,又催生出了一个“手机调校”的市场,并带动了新的产业链。
“手机调校”的第一级,在于系统层。在 Android 4.4 以及之后的 Android L 的规划中,它将应用程序的运行模式由 Dalvik 换成了 ART,其原理简单来说是“预编译”效果,即当一款应用程序在第一次被安装到 Android 时,它的字节码就已经被编译成为了本地的机器码,减少后续运行应用程序时的启动和执行时间。
根据 Google 自己公布的结果,在不同的性能测试 App 中,ART 的速度对比 Dalvik 的平均提升幅度达到了 80%,在某些项目中,ART 的提升幅度甚至超过了 1.5 倍,这个结果可谓非常喜人。
这是 Google 希望从源头解决 Android 卡慢问题的努力,但是这只是对性能优化有着作用,无法解决因为应用程序违规调用资源而产生的问题。同时,由于在安装应用程序时进行了“预编译”,整个安装时间将会变长,安装完毕后生成的文件也会变大,比如最新的 Google+ 安装包只有 6.9M,但是它安装后的 APK 大小达到了 28.3M,这对 Android 手机储存空间又存在过多占用的问题。
“手机调校”的第二级,在于 ROM 层。作为全球最大的 Android 市场,中国的许多手机厂商都以开发专用 ROM 来为销售产品添彩,大多数的 ROM,也都会考虑对 Android 系统进行优化,比如 MIUI V6 就宣称“引入多种 Linux 系统内核内存优化技术,提高应用运行效率”。
也就是说,与 Google 做的事情一样,ROM 厂商主要的优化工作,也是对 Linux 动刀,打上各种补丁,使其底层语言能够更好的适配到各种手机终端上。还是以 MIUI V6 为例,在介绍新特性时,其有这么一条:“ZRAM 调度优化技术”,其实 ZARM 就是 Linux 内核里的一个内存模块,作用就是在内存中划出一个部分出来充当虚拟盘,来承载 Linux 的交换分区,将一些任务压缩容纳进去,使内存的使用率提高,让 CPU 来为内存服务(因为目前的智能手机普遍 CPU 过剩、而内存才是瓶颈)。
不过,ROM 也是一把双刃剑,它对于 Android 底层系统的修改,以及它对于内存空间的占用,又都有增加手机负载的风险。
“手机调校”的第三级,在于应用层。大量应用程序在手机中发生的意外或故意占用事件,是造成 Android 手机越来越慢的最核心原因。过多的应用程序热衷于滞留在内存空间里、以及将大量碎片留在储存空间里,是带来麻烦的罪魁祸首。这也是为什么即时清理类应用得以逐渐成为 Android 手机标配。
Android 系统有七类进程,分别是前台进程、可见进程、主要服务、次要服务、后台进程、内容供应节点、空进程,在没有安装清理类应用的时候,一部 Android 手机只能依赖系统默认的分配机制来自动调节内存使用,只要应用程序提出请求,大部分进程只要打开后都会被保留在内存当中,这原本是为了让用户在再度激活这些进程时不需要重新载入、节省时间的初衷考虑,但是 Android 没有料到激烈的市场竞争会驱使应用程序产生“劣币驱良币”的趋势,很多开发者出于商业目的,在不需要留存在内存的情况下也想方设法的让应用程序保持潜在运行状态,一个两个还好说,但是一旦数量更多,Anrdoid 手机就会频频卡顿和发热。
以目前全球用户规模最大的 Android 手机清理类应用“某清理大师”为例,它清理的进程类型,主要放在后台进程、次要服务、内容供应节点和空进程:
后台进程(Hidden)——这个是最优先被扫描和识别出来的进程,因为大部分 Android 用户在切换应用程序时都不会使用返回键退出,而是直接按下 Home 键,前者会让应用进入空进程(占用资源相对较小),而后者则会保留为后台进程(占用资源相对更大),尤其是当游戏类 App 在后台运行时,它会和其他 App 争抢资源,而不会在乎那款 App 是不是用户正在使用。根据某清理大师的统计,约有 20% 的常用 App 即使不运行时也在后台启动联网,主要是提交产品及用户使用信息、获取广告信息、查询是否升级等。
次要服务(Secondary Server)——比如某些企业套件、邮箱联系人、触控接口等,这些进程很多都是系统自带的,有些用户会使用,但是有些用户也可能不会使用或已经有了替代应用,所以某清理大师的清理逻辑是基于用户行为和授权来建立(分为建议清理和深度清理两类);
内容供应节点(Content Provider)——这部分进程没有程序实体,仅仅提供内容给其他应用使用,比如日历供应节点、邮件供应节点等,除了占用内存资源之外,它还会占用网络,所以也会给 Android 手机造成不必要的负担;
空进程(Empty)——如果是通过返回键退出应用,大部分的应用也会在 Android 手机的内存里遗留一个空的进程,这个进程没有数据运行,但是会记录应用的历史信息,几乎没有任何价值,同样,这部分进程内容被干掉的优先级也很高。
除了对内存的过度消耗之外,Android 手机也容易在储存中积累大量冗余数据,包括无法卸载的预装应用、卸载之后的残存文件以及使用应用的过程中产生的缓存,由于 Android 本身没有提供管理工具,即使将手机连接电脑之后也是如同 Windows 树状结构一样的文件夹包,用户很难独立判断哪些文件夹可以删除、哪些文件夹是系统必备的,最后也会导致手机尺寸空间愈来愈窄的情况。
“手机调校”的问题,可能又回带来用户操作的负担增加,其心理压力甚于行为压力,玩着手机还不忘隔三差五的使用清理功能,这种与 iPhone 相比“别具特色”的操作习惯,也是 Android 手机永远像一个半成品或工程机的原因。
❹ pdflib在MFC中的使用
VC环境中和MFC中不是一回事吗?
错误消息
在查找预编译头时遇到意外的文件结尾。是否忘记了向源代码中添加“#include name”?
用 /Yu 指定的包含文件没有列在源文件中。在大多数的 Visual C++ 项目类型中,此选项默认是启用的,而且“stdafx.h”是此选项指定的默认包含文件。
在 Visual Studio 环境中,请使用下列方法之一消除此错误:
如果项目中没有使用预编译头,请将源文件的“创建/使用预编译头”属性设置为“不使用预编译头”。若要设置此编译器选项,请遵循以下这些步骤:
在项目的“解决方案资源管理器”窗格中,右击项目名称,再单击“属性”。
在左窗格中单击“C/C++”文件夹。
单击“预编译头”节点。
在右窗格中单击“创建/使用预编译头”,再单击“不使用预编译头”。
确保您没有在不注意的情况下从当前项目中删除、重命名或移除头文件(默认为 stdafx.h)。还需要使用 #include "stdafx.h" 在源文件中的任何其他代码之前包含这一文件。(此头文件被指定为“通过文件创建/使用 PCH”项目属性)
参考资料:
ms-help://MS.MSDNQTR.v80.chs/MS.MSDN.v80/MS.VisualStudio.v80.chs/dv_vccomp/html/dfd035f1-a7a2-40bc-bc92-dc4d7f456767.htm
❺ 简单的给我介绍下网络协议中的TCP协议的数据结构是怎么样的
sk_buff结构可能是linux网络代码中最重要的数据结构,它表示接收或发送数据包的包头信息。它在<include/linux/skbuff.h>中定义,并包含很多成员变量供网络代码中的各子系统使用。这个结构在linux内核的发展过程中改动过很多次,或者是增加新的选项,或者是重新组织已存在的成员变量以使得成员变量的布局更加清晰。它的成员变量可以大致分为以下几类:Layout 布局General 通用Feature-specific功能相关Management functions管理函数 这个结构被不同的网络层(MAC或者其他二层链路协议,三层的IP,四层的TCP或UDP等)使用,并且其中的成员变量在结构从一层向另一层传递时改变。L4向L3传递前会添加一个L4的头部,同样,L3向L2传递前,会添加一个L3的头部。添加头部比在不同层之间拷贝数据的效率更高。由于在缓冲区的头部添加数据意味着要修改指向缓冲区的指针,这是个复杂的操作,所以内核提供了一个函数skb_reserve(在后面的章节中描述)来完成这个功能。协议栈中的每一层在往下一层传递缓冲区前,第一件事就是调用skb_reserve在缓冲区的头部给协议头预留一定的空间。skb_reserve同样被设备驱动使用来对齐接收到包的包头。如果缓冲区向上层协议传递,旧的协议层的头部信息就没什么用了。例如,L2的头部只有在网络驱动处理L2的协议时有用,L3是不会关心它的信息的。但是,内核并没有把L2的头部从缓冲区中删除,而是把有效荷载的指针指向L3的头部,这样做,可以节省CPU时间。1. 网络参数和内核数据结构 就像你在浏览TCP/IP规范或者配置内核时所看到的一样,网络代码提供了很多有用的功能,但是这些功能并不是必须的,比如说,防火墙,多播,还有其他一些功能。大部分的功能都需要在内核数据结构中添加自己的成员变量。因此,sk_buff里面包含了很多像#ifdef这样的预编译指令。例如,在sk_buff结构的最后,你可以找到:struct sk_buff { ... ... ...#ifdef CONFIG_NET_SCHED _ _u32 tc_index;#ifdef CONFIG_NET_CLS_ACT _ _u32 tc_verd; _ _u32 tc_classid;#endif#endif}
它表明,tc_index只有在编译时定义了CONFIG_NET_SCHED符号才有效。这个符号可以通过选择特定的编译选项来定义(例如:"Device Drivers Networking supportNetworking options QoS and/or fair queueing")。这些编译选项可以由管理员通过make config来选择,或者通过一些自动安装工具来选择。前面的例子有两个嵌套的选项:CONFIG_NET_CLS_ACT(包分类器)只有在选择支持“QoS and/or fair queueing”时才能生效。顺便提一下,QoS选项不能被编译成内核模块。原因就是,内核编译之后,由某个选项所控制的数据结构是不能动态变化的。一般来说,如果某个选项会修改内核数据结构(比如说,在sk_buff
里面增加一个项tc_index),那么,包含这个选项的组件就不能被编译成内核模块。你可能经常需要查找是哪个make config编译选项或者变种定义了某个#ifdef标记,以便理解内核中包含的某段代码。在2.6内核中,最快的,查找它们之间关联关系的方法,就是查找分布在内核源代码树中的kconfig文件中是否定义了相应的符号(每个目录都有一个这样的文件)。在
2.4内核中,你需要查看Documentation/Configure.help文件。2. Layout Fields有些sk_buff成员变量的作用是方便查找或者是连接数据结构本身。内核可以把sk_buff组织成一个双向链表。当然,这个链表的结构要比常见的双向链表的结构复杂一点。就像任何一个双向链表一样,sk_buff中有两个指针next和prev,其中,next指向下一个节点,而
prev指向上一个节点。但是,这个链表还有另一个需求:每个sk_buff结构都必须能够很快找到链表头节点。为了满足这个需求,在第一个节点前面会插入另一个结构sk_buff_head,这是一个辅助节点,它的定义如下:struct sk_buff_head { struct sk_buff * next; struct sk_buff * prev; _ _u32 qlen; spinlock_t lock; }; qlen代表链表元素的个数。lock用于防止对链表的并发访问。sk_buff和sk_buff_head的前两个元素是一样的:next和prev指针。这使得它们可以放到同一个链表中,尽管sk_buff_head要比sk_buff小得多。另外,相同的函数可以同样应用于sk_buff和sk_buff_head。为了使这个数据结构更灵活,每个sk_buff结构都包含一个指向sk_buff_head的指针。这个指针的名字是list。图1会帮助你理解它们之间的关系。Figure 1. List of sk_buff elements
❻ 致命错误:在搜索预编译头文件时遇到意外的文件结尾的指令。错误在哪儿 啊大神们
4个错误
0.NULL不需要你来define
1.printf('delete:%ld\n",num); //左边用了单引号
2.while(num!p1->num&&p1->next==NULL); //应该是num != p1->num
3.sxanf("%ld",&del_num);/*输入要删除的学号*/ // 是scanf
另外
main函数最好声明为int型,这是标准C对main函数的要求
声明为void的main函数在标准C编译器上是通不过编译的
❼ VUE 响应式原理 和 Virtual DOM
在 Vue 中,数据模型下的所有属性,会被 Vue 使用 Object.defineProperty (Vue3.0 使用 Proxy)进行数据劫持代理。响应式的核心机制是观察者模式,数据是被观察的一方,一旦发生变化,通知所有观察者,这样观察者可以做出响应,比如当观察者为视图时,视图可以做出视图的更新。
Vue.js 的响应式系统以来三个重要的概念, Observer 、 Dep 、 Watcher 。
Observe 扮演的角色是发布者,他的主要作用是在组件 vm 初始化的时,调用 defineReactive 函数,使用 Object.defineProperty 方法对对象的每一个子属性进行数据劫持/监听,即为每个属性添加 getter 和 setter ,将对应的属性值变成响应式。
在组件初始化时,调用 initState 函数,内部执行 initState 、 initProps 、 initComputed 方法,分别对 data 、 prop 、 computed 进行初始化,让其变成响应式。
初始化 props 时,对所有 props 进行遍历,调用 defineReactive 函数,将每个 prop 属性值变成响应式,然后将其挂载到 _props 中,然后通过代理,把 vm.xxx 代理到 vm._props.xxx 中。
同理,初始化 data 时,与 prop 相同,对所有 data 进行遍历,调用 defineReactive 函数,将每个 data 属性值变成响应式,然后将其挂载到 _data 中,然后通过代理,把 vm.xxx 代理到 vm._data.xxx 中。
初始化 computed ,首先创建一个观察者对象 computed-watcher ,然后遍历 computed 的每一个属性,对每一个属性值调用 defineComputed 方法,使用 Object.defineProperty 将其变成响应式的同时,将其代理到组件实例上,即可通过 vm.xxx 访问到 xxx 计算属性。
Dep 扮演的角色是调度中心/订阅器,在调用 defineReactive 将属性值变成响应式的过程中,也为每个属性值实例化了一个 Dep ,主要作用是对观察者(Watcher)进行管理,收集观察者和通知观察者目标更新,即当属性值数据发生改变时,会遍历观察者列表(dep.subs),通知所有的 watcher,让订阅者执行自己的update逻辑。
其 dep 的任务是,在属性的 getter 方法中,调用 dep.depend() 方法,将观察者(即 Watcher,可能是组件的render function,可能是 computed,也可能是属性监听 watch)保存在内部,完成其依赖收集。在属性的 setter 方法中,调用 dep.notify() 方法,通知所有观察者执行更新,完成派发更新。
Watcher 扮演的角色是订阅者/观察者,他的主要作用是为观察属性提供回调函数以及收集依赖,当被观察的值发生变化时,会接收到来自调度中心 Dep 的通知,从而触发回调函数。
而 Watcher 又分为三类, normal-watcher 、 computed-watcher 、 render-watcher 。
这三种 Watcher 也有固定的执行顺序,分别是:computed-render -> normal-watcher -> render-watcher。这样就能尽可能的保证,在更新组件视图的时候,computed 属性已经是最新值了,如果 render-watcher 排在 computed-render 前面,就会导致页面更新的时候 computed 值为旧数据。
Observer 负责将数据进行拦截,Watcher 负责订阅,观察数据变化, Dep 负责接收订阅并通知 Observer 和接收发布并通知所有 Watcher。
在 Vue 中, template 被编译成浏览器可执行的 render function ,然后配合响应式系统,将 render function 挂载在 render-watcher 中,当有数据更改的时候,调度中心 Dep 通知该 render-watcher 执行 render function ,完成视图的渲染与更新。
整个流程看似通顺,但是当执行 render function 时,如果每次都全量删除并重建 DOM,这对执行性能来说,无疑是一种巨大的损耗,因为我们知道,浏览器的DOM很“昂贵”的,当我们频繁的更新 DOM,会产生一定的性能问题。
为了解决这个问题,Vue 使用 JS 对象将浏览器的 DOM 进行的抽象,这个抽象被称为 Virtual DOM。Virtual DOM 的每个节点被定义为 VNode ,当每次执行 render function 时,Vue 对更新前后的 VNode 进行 Diff 对比,找出尽可能少的我们需要更新的真实 DOM 节点,然后只更新需要更新的节点,从而解决频繁更新 DOM 产生的性能问题。
VNode,全称 virtual node ,即虚拟节点,对真实 DOM 节点的虚拟描述,在 Vue 的每一个组件实例中,会挂载一个 $createElement 函数,所有的 VNode 都是由这个函数创建的。
比如创建一个 div:
render 函数执行后,会根据 VNode Tree 将 VNode 映射生成真实 DOM,从而完成视图的渲染。
Diff 将新老 VNode 节点进行比对,然后将根据两者的比较结果进行最小单位地修改视图,而不是将整个视图根据新的 VNode 重绘,进而达到提升性能的目的。
Vue.js 内部的 diff 被称为 patch 。其 diff 算法的是通过同层的树节点进行比较,而非对树进行逐层搜索遍历的方式,所以时间复杂度只有O(n),是一种相当高效的算法。
首先定义新老节点是否相同判定函数 sameVnode :满足键值 key 和标签名 tag 必须一致等条件,返回 true ,否则 false 。
在进行 patch 之前,新老 VNode 是否满足条件 sameVnode(oldVnode, newVnode) ,满足条件之后,进入流程 patchVnode ,否则被判定为不相同节点,此时会移除老节点,创建新节点。
patchVnode 的主要作用是判定如何对子节点进行更新,
Diff 的核心,对比新老子节点数据,判定如何对子节点进行操作,在对比过程中,由于老的子节点存在对当前真实 DOM 的引用,新的子节点只是一个 VNode 数组,所以在进行遍历的过程中,若发现需要更新真实 DOM 的地方,则会直接在老的子节点上进行真实 DOM 的操作,等到遍历结束,新老子节点则已同步结束。
updateChildren 内部定义了4个变量,分别是 oldStartIdx 、 oldEndIdx 、 newStartIdx 、 newEndIdx ,分别表示正在 Diff 对比的新老子节点的左右边界点索引,在老子节点数组中,索引在 oldStartIdx 与 oldEndIdx 中间的节点,表示老子节点中为被遍历处理的节点,所以小于 oldStartIdx 或大于 oldEndIdx 的表示未被遍历处理的节点。同理,在新的子节点数组中,索引在 newStartIdx 与 newEndIdx 中间的节点,表示老子节点中为被遍历处理的节点,所以小于 newStartIdx 或大于 newEndIdx 的表示未被遍历处理的节点。
每一次遍历, oldStartIdx 和 oldEndIdx 与 newStartIdx 和 newEndIdx 之间的距离会向中间靠拢。当 oldStartIdx > oldEndIdx 或者 newStartIdx > newEndIdx 时结束循环。
在遍历中,取出4索引对应的 Vnode节点:
diff 过程中,如果存在 key ,并且满足 sameVnode ,会将该 DOM 节点进行复用,否则则会创建一个新的 DOM 节点。
首先, oldStartVnode 、 oldEndVnode 与 newStartVnode 、 newEndVnode 两两比较,一共有 2*2=4 种比较方法。
情况一:当 oldStartVnode 与 newStartVnode 满足 sameVnode,则 oldStartVnode 与 newStartVnode 进行 patchVnode,并且 oldStartIdx 与 newStartIdx 右移动。
情况二:与情况一类似,当 oldEndVnode 与 newEndVnode 满足 sameVnode,则 oldEndVnode 与 newEndVnode 进行 patchVnode,并且 oldEndIdx 与 newEndIdx 左移动。
情况三:当 oldStartVnode 与 newEndVnode 满足 sameVnode,则说明 oldStartVnode 已经跑到了 oldEndVnode 后面去了,此时 oldStartVnode 与 newEndVnode 进行 patchVnode 的同时,还需要将 oldStartVnode 的真实 DOM 节点移动到 oldEndVnode 的后面,并且 oldStartIdx 右移, newEndIdx 左移。
情况四:与情况三类似,当 oldEndVnode 与 newStartVnode 满足 sameVnode,则说明 oldEndVnode 已经跑到了 oldStartVnode 前面去了,此时 oldEndVnode 与 newStartVnode 进行 patchVnode 的同时,还需要将 oldEndVnode 的真实 DOM 节点移动到 oldStartVnode 的前面,并且 oldStartIdx 右移, newEndIdx 左移。
当这四种情况都不满足,则在 oldStartIdx 与 oldEndIdx 之间查找与 newStartVnode 满足 sameVnode 的节点,若存在,则将匹配的节点真实 DOM 移动到 oldStartVnode 的前面。
若不存在,说明 newStartVnode 为新节点,创建新节点放在 oldStartVnode 前面即可。
当 oldStartIdx > oldEndIdx 或者 newStartIdx > newEndIdx,循环结束,这个时候我们需要处理那些未被遍历到的 VNode。
当 oldStartIdx > oldEndIdx 时,说明老的节点已经遍历完,而新的节点没遍历完,这个时候需要将新的节点创建之后放在 oldEndVnode 后面。
当 newStartIdx > newEndIdx 时,说明新的节点已经遍历完,而老的节点没遍历完,这个时候要将没遍历的老的节点全都删除。
借用官方的一幅图:
Vue.js 实现了一套声明式渲染引擎,并在 runtime 或者预编译时将声明式的模板编译成渲染函数,挂载在观察者 Watcher 中,在渲染函数中(touch),响应式系统使用响应式数据的 getter 方法对观察者进行依赖收集(Collect as Dependency),使用响应式数据的 setter 方法通知(notify)所有观察者进行更新,此时观察者 Watcher 会触发组件的渲染函数(Trigger re-render),组件执行的 render 函数,生成一个新的 Virtual DOM Tree,此时 Vue 会对新老 Virtual DOM Tree 进行 Diff,查找出需要操作的真实 DOM 并对其进行更新。
❽ 怎么在预编译加入zigbeepro
zigbee pro可以说是zigbee的升级版协议,有很多增强型的功能,比如轮流寻址、多对一路由、更高的安全性能等等,另外,实际上能支持的网络节点要远多于老版zigbee协议,更接近于商业应用。 从底层来讲,zigbee与RF4CE协议都是遵循802.15.4规范的