这样,我们在生成一个 DOM 的时候,同时添加对data的监听,数据更新时我们会找到对应的nodeIndex,并进行数值更新
虚拟 DOM
因为一个 DOM 节点包括了太多太多的属性、元素和事件对象,通常包括节点内容、元素位置、样式、节点的添加删除等方法,但是我们并不是全部都会用到
而我们通过用 JS 对象表示 DOM 元素的方式,大大降低了比较差异的计算量,所以数据变更时框架先使用虚拟DOM的JS对象进行对比更新
然后再按虚拟DOM进行浏览器渲染,以JS对象的比较更新替代频繁的DOM操作,将多次DOM操作合并成单次更新来提升性能
虚拟 DOM 大概是这么个过程:
1. 用 JS 对象模拟 DOM 树,得到一棵虚拟 DOM 树
2. 当页面数据变更时,生成新的虚拟 DOM 树,比较新旧两棵虚拟 DOM 树的差异
3. 把差异应用到真正的 DOM 树上
概览:
前端页面解析
前端项目代码组成
我们打开一个前端项目,经常会看到很多不同后缀的文件,例如一个页面可能包括
a.html
、a.css
、a.js
,用了 Vue 还有a.vue
,再加上 Typescript 可能还有a.ts
其实最终跑在浏览器中的代码,主要包括三种:HTML、CSS、Javascript,然后就是一些图片等静态资源基础HTML
这里面包括两个子模快
通常来说,一段 HTML 代码,最终在浏览器中会生成一堆 DOM 节点树,例如:
这段代码在浏览器中渲染时,会先渲染外层数据节点,再逐层渲染内部节点
CSS
CSS 主要是给我们的 HTML 元素添加样式,可以通过几个方式匹配:
Javascript
HTML 是简单的网页静态信息,而 JavaScript 可以在网页上实现复杂的功能。
我们常常使用 Javascript 来做以下事情:
Javascript 是单线程的,更多是因为对页面交互的同步处理。作为浏览器脚本语言,JavaScript的主要用途是与用户互动,以及操作 DOM,若是多线程会导致严重的同步问题。
页面渲染
浏览器的渲染机制
我们现在知道一个页面的代码里,主要包括了 HTML、CSS、Javascript 三大块内容,那么浏览器是怎么解析和加载这些内容的呢?
一次浏览器的页面渲染过程中,浏览器会解析三种文件:
CSS 规则树与 DOM 结构树结合,最终生成一个 Render 树(即最终呈现的页面,例如其中会移除 DOM 结构树中匹配到 CSS 里面
display:none
的 DOM 节点),一般来说浏览器绘制页面的过程是:页面的局部刷新
我们的页面更多的不只是静态的页面,还会包括点击、拖拽等事件操作,以及接口请求、数据渲染到页面等动态的交互逻辑,这时候我们会需要更新页面的信息。
我们的业务代码中情况会复杂得多,除了插入内容,还包括内容更新、删除元素节点等。不管是、哪种情况,目前来说前端一般分为两种方式:
1. 绑定映射关系
通过document提供的一些方法将DOM元素映射成JS对象,并使用这些JS对象通过JS内置API去操作DOM元素
这里拿到了的这样一个元素映射,我们在更新内容、处理节点的时候就可以用这个映射来直接操作,如:
如果页面需要绑定变量的元素很多,那每次要更新某块的页面数据,需要保存很多元素映射的JS对象,同时需要调用很多的createElement()/appendChild()/removeChild()这类的原生方法 这种情况下,我们可以使用直接替换内容的方式
2. 直接替换内容
我们每次更新页面数据和状态,还可以通过innerHTML使用字符串拼接的方式来用新的HTML String替换旧的 例如,上面的几次更新 a 元素节点,可以调整成这样实现:
大量的元素更新不管是使用绑定映射关系还是使用直接替换内容,都会导致大量浏览器计算,很容易出现性能问题,导致页面卡顿等现象
页面重排、重绘
我们更新页面元素时,通常会触发浏览器的两种操作:Repaint(重绘) 和 Reflow(重排):
Repaint:页面部分重画,通常不涉及尺寸的改变,常见于颜色的变化 Reflow:意味着节点需要重新计算和绘制,常见于尺寸的改变 在 Reflow 的时候,浏览器会使渲染树中受到影响的部分失效,并重新构造这部分渲染树,完成构造后,浏览器会重新绘制受影响的部分到屏幕中,该过程为 Repaint
重排的性能消耗跟render tree有多少节点需要重新构建有关系,单次操作使用innerHTML更改的节点数量更多会导致更多的开销 如果将多次操作合并为一次innerHTML,减少DOM操作及计算可能相较于绑定映射关系的方式效率更优,所以到底是使用绑定映射表方式,还是使用直接替换内容方式,都是需要按照实际场景具体问题具体分析的
编码方式的改变
事件驱动
事件驱动其实是前端开发中最容易理解的编码方式,例如我们写一个提交表单的页面,用事件驱动的方式来写的话,会是这样一个流程:
1.编写静态页面
2.给对应的元素绑定对应的事件,例如给 input 输入框绑定输入事件:
3.事件触发时,更新页面内容:
以上这个流程,是很常见的前端编码思维,称之为事件驱动模式。
前端思维转变
前端思维转变,将事件驱动的思维模式是过去的常用模式,如今的前端开发过程中,多了很多的新框架、新工具,还有了工程化,带来了很多思维模式的变化
前端框架的出现
最初 AngularJS 占领了比较多的地位,后面 React 迎面赶上,Vue 再结合各种框架的优势,以及非常容易入门的文档,目前被更多人接受,下面以Vue来介绍这些新框架的概念
数据绑定
Vue 文本插值
在 Vue 中,最基础的模板语法是数据绑定,例如:
这里绑定了一个 data的变量,开发者在 Vue 实例 data 中绑定该变量:
最终页面展示内容为
数据绑定的实现
这种DOM中的值与JS中的变量进行绑定的方式,我们称之为数据绑定,数据绑定的过程如下:
模板引擎完成上述过程,
这样,我们在生成一个 DOM 的时候,同时添加对data的监听,数据更新时我们会找到对应的nodeIndex,并进行数值更新
虚拟 DOM
因为一个 DOM 节点包括了太多太多的属性、元素和事件对象,通常包括节点内容、元素位置、样式、节点的添加删除等方法,但是我们并不是全部都会用到 而我们通过用 JS 对象表示 DOM 元素的方式,大大降低了比较差异的计算量,所以数据变更时框架先使用虚拟DOM的JS对象进行对比更新 然后再按虚拟DOM进行浏览器渲染,以JS对象的比较更新替代频繁的DOM操作,将多次DOM操作合并成单次更新来提升性能
虚拟 DOM 大概是这么个过程:
XSS 漏洞
模板引擎还可以协助预防下 XSS 相关漏洞,XSS 的整个攻击过程大概为:
避免 XSS 的方法之一主要是将用户所提供的内容进行编码过滤,而大多数模板引擎会自带 HTML 转义功能 在 Vue 中,默认的数据绑定方式(双大括号、
v-bind
等)会进行 HTML 转义,将数据解释为普通文本,而非 HTML 代码当然,如果你一定要输出 HTML 代码,也可以使用
v-html
指令输出,页面上渲染任意的HTML 可能会非常危险, 因为它很容易导致 XSS 攻击,请只对可信内容使用 HTML 插值,绝不要对用户自定义的输入内容使用插值Vue简介
Vue 是一套用于构建用户界面的渐进式框架。与其它大型框架不同,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。
通过虚拟DOM再进行浏览器渲染的过程中我们可以实现一些功能:
使用框架自带的上述这些必要的功能,可以提升我们的开发效率,节省很多的工作量
对比其他框架,Vue易于上手这块,是大多数人都比较认可的,框架的性能也不错,这也是技术选型中比较重要的一些考虑,其他对比信息见 https://cn.vuejs.org/v2/guide/comparison.html https://github.com/introfei/Blog/issues/28
数据驱动
在 jQuery 年代,我们通常是使用事件驱动的模式去进行开发,使用了 Vue 之后,我们使用数据驱动的方式来进行编码,同样的们写一个提交表单的页面的流程:
1.设计与视图显示数据绑定的数据结构
2.把数据绑定到页面中需要展示的地方
3.事件触发时,更新相应的显示数据
具体使用DOM操作的过程由框架自动完成
所以事件驱动和数据驱动一个很重要的区别在于,我们是从每个事件的触发开始设计我们的代码,还是以数据为中心,接收事件触发和更新数据状态的方式来编码。
页面抽象
使用数据驱动进行编码,我们经常需要对数据和视图进行抽象关联,例如我们现在要写一个列表,数据从后台获取到之后,展示到页面中
1.当我们需要渲染成列表时:
2.当我们需要更新一个列表中某个 id 的其中一个数据时:
在使用数据驱动的时候,模板渲染的事情会交给框架去完成,我们需要做的就是数据处理而已 当我们页面抽象成数据来表示之后,我们就可以将所有的页面元素、组件、展示内容和接口配置等都变成以配置数据的方式进行管理,使得代码变得更加清晰简洁
前端工程化
现在的主流构建工具是 Webpack,包括我们使用 Vue 官方脚手架生成的代码,构建工具也是 Webpack
我们在代码中会使用到很多的资源,图片、样式、代码,还有各式各样的依赖包,而打包的时候怎么实现按需加载、处理依赖关系、不包含多余的文件或内容, 同时提供开发和生产环境,包括本地调试自动更新到浏览器这些能力,都是由 Webpack 进行整合的
npm 依赖包
要实现工程化,前端开发离不开 nodejs 的包管理器 npm,在搭建本地开发服务以及打包编译前端代码等都会用到 在前端开发过程中,经常用到
npm install
来安装所需的依赖我们在写代码的时候,为了可以将注意力集中到业务开发中,会需要使用别人开源的很多工具、框架和代码包 在很早以前,我们是一个个的下载,或是通过的方式去引用
当网站依赖的代码越来越多,管理依赖和版本是一件很麻烦的事情,我们就使用npm来对这些依赖进行集中管理
同时,我们可以在本地构建的时候,忽略依赖包里没用用到的部分,减小代码包的大小等
在安装 Node.js 的时候,npm 会一起安装,确认环境变量设置生效后,我们就可以在cmd或者VScode的终端栏中, 进入package.json文件所在的目录使用npm install 安装所有的依赖或使用npm install xxx安装某个依赖同时加入到配置文件中,具体组件使用时,在通过import的方式进行引入,针对实际应用场景在网上搜索对应实现的依赖包即可
脚手架
脚手架可以让你快速地生成示例代码,也可以更新依赖的版本等,避免开发者自行调整开发环境、打包逻辑等配置,帮我们完成前端工程化的处理并提供基础的项目编码结构
Vue CLI 致力于将 Vue 生态中的工具基础标准化,它确保各种构建工具能够基于智能的默认配置平稳衔接,让开发者专注于应用编码
使用方式很简单:
启动项目
一般来说,每个项目根目录下都会有一个 README.md 文件,你能看到一些简单的项目说明及操作流程,例如:
yarn 跟 npm 都是包管理器,区别在于 yarn 在安装依赖时由于并行,离线等优化,速度更快,以及版本统一管理的比较好 yarn需要单独安装,同时也需要在确认环境变量生效后才能正常使用 部分公司可能有自己的包管理器,比如tnmp,来加载一些公司内部使用的依赖文件
部分项目 README缺失,可以查看或添加package.json文件的scripts部分的指令:
执行上述提供的命令即可,如果是start命令,直接执行npm start即可,其他命令需要添加run关键字,如使用npm run server,执行 "vue-cli-service serve"
从jQuery到Vue React过渡时间出现的一些新知识
在 2016 年学 JavaScript 是一种什么样的体验 https://zhuanlan.zhihu.com/p/22782487