Vue的思考扩展
1、Vue是如何实现数据双向绑定的
1.1、实现双向绑定的基本原理
数据驱动:Vue会通过Dircetives指令,对DOM做一层封装,当数据发生改变会通知指令去修改对应的DOM,数据驱动DOM变化,DOM是数据的一种自然映射。Vue还会对操作进行监听,当视图发生改变时,vue监听到这些变化,从而改变数据,这样就形成了数据的双向绑定。
数据响应原理:当你把一个普通的 JavaScript 对象传给 Vue 实例的 data选项,Vue 将遍历此对象所有的属性,并使用 Object.defineProperty 把这些属性全部转为 getter/setter,在属性被访问和修改时通知变化。每个组件实例都有相应的 watcher 实例对象,它会在组件渲染的过程中把属性记录为依赖,之后当依赖项的setter被调用时,会通知watcher重新计算,从而致使它关联的组件得以更新。
正如上面所说,vue实现数据双向绑定主要是采用数据劫持结合发布者-订阅者模式的方式。
数据劫持是通过Object.defineProperty()实现的,该函数为每个属性添加setter,getter 的方法,在数据发生改变时 setter 方法会被触发,然后发布消息给订阅者,触发相应监听回调。当把一个普通 Javascript 对象传给 Vue 实例来作为它的 data 选项时,Vue 将遍历它的属性,用 Object.defineProperty 为每个属性添加 setter,getter 的方法。
vue的数据双向绑定主要通过三个模块完成:监听者Observer、订阅者Watcher、Compile解析模板指令。
(1)Observer监听 model 的数据变化,如果有变动的,就通知订阅者
(2)初始渲染页面、为节点绑定函数。通过 Compile 扫描和解析每个节点的相关指令,将模板中的变量替换成数据,并根据初始数据渲染页面视图。并且将每个指令对应的节点绑定函数,一旦视图发生交互,绑定的函数就被触发,改变数据。
(3)watcher 搭起了 observer 和 Compile 之间的通信桥梁,达到数据变化 —>视图更新,视图交互变化(input)—>数据 model 发生变更的双向绑定效果。
- var vm = new Vue({
- data: {
- obj: { a: 1 }
- },
- created: function () {
- console.log(this.obj);
- }
- });
打印Vue实例的data里的某个数据的某个属性,可以看到该属性含有 setter、getter 方法,由此可以得知,每个属性都被添加了setter、getter 方法。
实现mvvm主要包含两个方面,数据变化更新视图,视图变化更新数据:
view更新data通过事件监听即,比如 input 标签监听 'input' 事件就可以实现。关键点在于 data 如何更新view,当数据改变,如何更新视图的。重点是如何知道数据变了,而这可以由Observer实现,通过Object.defineProperty( )对属性设置一个set函数,当数据改变了就会来触发这个函数,然后只要将一些需要更新视图的方法放在这里面就可以实现data更新view了。
1.2、observe 的实现
利用Obeject.defineProperty()来为每个属性添加setter,getter 的方法实现监听属性变动。将需要observe的数据对象进行递归遍历,包括子属性对象的属性,都加上 setter和getter,这样的话,给这个对象的某个值赋值,就会触发setter,那么就能监听到了数据变化。示例代码:
- var data = {
- name: 'kindeng'
- };
- observe(data);
- data.name = 'dmq'; // 哈哈哈,监听到值变化了 kindeng --> dmq
- function observe(data) {
- if (!data || typeof data !== 'object') {
- return;
- }
- // 取出所有属性遍历
- Object.keys(data).forEach(function (key) {
- defineReactive(data, key, data[key]);
- });
- };
- function defineReactive(data, key, val) {
- observe(val); // 监听子属性
- Object.defineProperty(data, key, {
- enumerable: true, // 可枚举
- configurable: false, // 不能再define
- get: function () {
- return val;
- },
- set: function (newVal) {
- console.log('哈哈哈,监听到值变化了 ', val, ' --> ', newVal);
- val = newVal;
- }
- });
- }
通过上面代码就可以实现对每个属性进行监听。
1.3、通过JS实现简单的双向绑定
- <body>
- <div id="app">
- <input type="text" id="txt">
- <p id="show"></p>
- </div>
- </body>
- <script type="text/javascript">
- var obj = {}
- Object.defineProperty(obj, 'txt', {
- get: function () {
- return obj
- },
- set: function (newValue) {
- document.getElementById('txt').value = newValue
- document.getElementById('show').innerHTML = newValue
- }
- })
- document.addEventListener('keyup', function (e) {
- obj.txt = e.target.value
- })
- </script>
上面代码中,首先 defineProperty 为每个属性添加 getter、setter 方法,当数据发生改变, setter 方法被触发,视图也发生改变。setter 里面的执行命令可以看做是一个订阅者 Watcher,将视图和数据连接起来了。最下面的代码为节点绑定方法可以看做是Compile的作用,为指令节点绑定方法,当发生视图交互时,函数被触发,数据被改变,Watcher 收到通知,视图也将发生改变。
2、浏览器渲染页面过程
(浏览器渲染引擎的渲染流程)
2.1、关键渲染路径
关键渲染路径是指浏览器从最初接收请求来的HTML、CSS、javascript等资源,然后解析、构建树、渲染布局、绘制,最后呈现给客户能看到的界面这整个过程。
所以浏览器的渲染过程主要包括以下几步:
- 解析HTML生成DOM树。
- 解析CSS生成CSSOM规则树。
- 将DOM树与CSSOM规则树合并在一起生成渲染树。
- 遍历渲染树开始布局,计算每个节点的位置大小信息。
- 将渲染树每个节点绘制到屏幕。
3、JS操作真实DOM的代价!
4、虚拟DOM的作用
虚拟DOM就是为了解决浏览器性能问题而被设计出来的。假如像上面所说的,若一次操作中有10次更新DOM的动作,会生成一个新的虚拟DOM,将新的虚拟DOM和旧的进行比较,然后将10次更新的 diff 内容保存到一个JS对象中,最终通过这个JS对象来更新真实DOM,由此只进行了一次操作真实DOM,避免大量无谓的计算量。所以,虚拟DOM的作用是将多个DOM操作合并成一个,并且将DOM操作先全部反映在JS对象中(操作内存中的JS对象比操作DOM的速度要更快),再将最终的JS对象映射成真实的DOM,交由浏览器去绘制。
5、实现虚拟DOM
虚拟DOM就是是用JS对象来代表节点,每次渲染都会生成一个VNode。当数据发生改变时,生成一个新的的VNode,通过 diff 算法和上一次渲染时用的VNode进行对比,生成一个对象记录差异,然后根据该对象来更新真实的DOM。原本要操作的DOM在vue这边还是要操作的,不过是统一计算出所有变化后统一更新一次DOM,进行浏览器DOM的一次性更新。
参考:https://baijiahao.baidu.com/s?id=1593097105869520145&wfr=spider&for=pc、https://www.jianshu.com/p/af0b398602bc
6、Vue 中路由的hash模式和history模式
Vue 中路由有 hash 模式和 history 模式,hash 模式带 # 号,history 没有这个 # 号,就是普通的 url 。可以通过在 router 中配置 mode 选项来切换模式。
Vue 中的路由是怎么实现的可以参考:https://segmentfault.com/a/1190000011967786
Vue 中路由的实现是通过监听 url 的改变,然后通过解析 url ,匹配上对应的组件进行渲染实现的。
在 hash 模式下,跳转路由导致后面 hash 值的变化,但这并不会导致浏览器向服务器发出请求。另外每次 hash 值的变化,还会触发 hashchange
这个事件,通过监听这个事件就能知道 hash 值的改变,并能解析出 url。在 hash 模式下,刷新页面和直接输入链接都不会导致浏览器发出请求。
history 模式的实现原理是通过HTML5中的两个方法:pushState
和 replaceState
,这两个方法可以改变 url 地址且不会发送请求,由此可以跳转路由而不刷新页面,不发出请求。但是在 history 模式下,用户如果直接输入链接或者手动刷新时,浏览器还是会发出请求,而会导致服务器寻找该 url 路径下的对应的文件,而该路径下的文件往往不存在,所以会返回 404。为了避免这种情况,在使用 history 模式时,需要后端进行配合使用,配置在URL 匹配不到任何静态资源返回什么东西,比如可以配置在找不到文件时返回项目的主页面。
参考:https://www.cnblogs.com/xufeimei/p/10745353.html
Vue的思考扩展的更多相关文章
- 第3章-Vue.js 指令扩展 和 todoList练习
一.学习目标 了解Vue.js指令的实现原理 理解v-model指令的高级用法 能够使用Vue.js 指令完成 todoList 练习(重点+难点) 二.todoList练习效果展示 2.1.效果图展 ...
- 2、Vue构造器和扩展
1.VUE构造器简介 VUE构造器是一个非常重要的语法. 每个Vue.js应用都是通过构造函数Vue创建一个根实例. New了Vue对象,然后调用了这个vue对象的构造器,并向构造器传入了数据. 在实 ...
- vue 安装sass扩展
1.创建一个基于 webpack 模板的新项目 $ vue init webpack myvue 1 2.在当前目录下,安装依赖 $ cd myvue $ npm install 1 2 3.安装sa ...
- Vue使用的扩展
1.Bus(总线)实现非父子组件通信 Vue2.0提供了Vuex进行非父子组件之间的通信,但在简单的场景下,可以使用一个空的Vue实例作为中央事件总线. 实现代码示例: <div id=&quo ...
- vue开发chrome扩展,数据通过storage对象获取
开发chrome插件时遇到一个问题,那就是单文件组件的data数据需要从chrome提供的storage对象中获取,但是 chrome.storage.sync.get 方法是异步获取数据的,需要通过 ...
- vue - Vue路由(扩展)
忙里偷闲,还在学校,趁机把后面的路由多出来的知识点学完 十.缓存路由组件 让不展示的路由组件保持挂载,不被销毁 在我们的前面案例有一个问题,都知道vue的路由当我们切换一个路由后,另一个路由就会被销毁 ...
- Vue 向下扩展后就类似于 jQuery
https://cn.vuejs.org/v2/guide/comparison.html
- Vue数据双向绑定探究
前面的啰嗦话,写一点吧,或许就有点用呢 使用过vue的小伙伴都会感觉,哇,这个框架对开发者这么友好,简直都要笑出声了. 确实,使用过vue的框架做开发的人都会感觉到,以前写一大堆操作dom,bom的东 ...
- Vue.js——60分钟组件快速入门(上篇)
组件简介 组件系统是Vue.js其中一个重要的概念,它提供了一种抽象,让我们可以使用独立可复用的小组件来构建大型应用,任意类型的应用界面都可以抽象为一个组件树: 那么什么是组件呢?组件可以扩展HTML ...
随机推荐
- jsonify返回中文乱码的问题
参考博客: 解决flask中文乱码的问题 返回内容如图: 实际填写的内容: 从网上找答案,结果很多都是说配置app.config,如下: app.config['JSON_AS_ASCII'] 但我试 ...
- VMware vSphere 虚拟化简介
目录 目录 vSphere 简介 vSphere 提供的工具 vCenter vCenter 的功能 vCenter 管理界面上提供的操作功能 HOST CLUSTER TEMPLATE Virtua ...
- APlayer 媒体播放引擎
APlayer媒体播放引擎 Windows平台的播放内核 封闭式 DirectShow 架构,不受系统解码环境干扰 全媒体文件格式支持 丰富的媒体文件传输协议(http/https/ftp/mms/r ...
- maximize_window()最大化浏览器和刷新当前页面refresh()
from selenium import webdriverdriver = webdriver.Firefox()driver.get("https://www.baidu.com&quo ...
- oracle--序列&视图&索引&视图&可视化操作&分页&数据库备份
--oracle学习内容--oracle的管理系统学习--oracle的数据管理学习--oracle的用户管理--oracle二维表管理--oracle的其他知识 --oracle的序列.视图.索引 ...
- 实现atoi
1. 去掉首位空格 2. 判断首位是否有正负号 3. 判断各位是否是0~9,有其他字符直接返回当前结果 public class Solution { public int atoi(String ...
- 一次特殊的“VARCHAR转numeric失败”错误记录
今天接触到一个很有意思的问题.当我在执行一条INSERT的sql语句时,他总是报字符串转数字类型失败. 问题 首先,该表中的所有数字类型的字段都是非必填,其次,每个数字类型的字段都有默认值.最令我感到 ...
- Java初始化块及执行顺序
理解 初始化块又称为代码块.属于类中的第四大成员.本质上是一个方法,它也有方法体,但没有方法名,没有参数,没有返回,而且也不是通过对象或类名显式调用,而是通过隐式调用 是构造器的补充 语法 [修饰符] ...
- [2019杭电多校第二场][hdu6602]Longest Subarray(线段树)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6602 题目大意为求最长的区间,满足C种数字在区间内要么不出现,要么出现的次数都不小于K. 大致的分析一 ...
- HNUSTOJ-1696 简单验证码识别(模拟)
1696: 简单验证码识别 时间限制: 2 Sec 内存限制: 128 MB 提交: 148 解决: 44 [提交][状态][讨论版] 题目描述 验证码是Web系统中一种防止暴力破解的重要手段.其 ...