vue 启动时卡死_十分钟浅入Vue 原理
vue原理
引用
众所周知vue
是一个MVVM 渐进式框架,MVVM是vue的设计模式,在vue框架中数据会自动驱动视图。
1、MVVM设计模式
解释
View是视图,就是DOM;对应视图也就是HTML部分--代表UI组件,它负责将数据模型转化成UI展现出来。 Model是模型,就是vue组件里的data,或者说是vuex里的数据;--代表数据模型,也可以在Model中定义数据修改和操作的业务逻辑。 ViewModel--监听模型数据也就是data的的改变和控制视图行为、处理用户交互,简单理解就是一个同步View和Model的对象,连接Model和View。
总结
在MVVM架构下,View
和Model
之间并没有直接的联系,而是通过ViewMode
进行交互,Model和ViewModel之间的交互是双向的,因此View数据的变化会同步到Model中,而Model数据的变化也会立即反应到View上。
ViewModel
通过双向数据绑定把View层和Model层连接了起来,而View和Model之间的同步工作完全是自动的,无需人为干涉,因此开发者只需关注业务逻辑,不需要手动操作DOM,不需要关注数据状态的同步问题,复杂的数据状态维护完全由MVVM来统一管理。
由此,我们可以引出vue是响应式的
2、响应式
说明
Vue 的响应式原理是核心是通过 ES5 的保护对象的 Object.defindeProperty
中的访问器属性中的 get
和 set
方法,data 中声明的属性都被添加了访问器属性,当读取 data 中的数据时自动调用 get 方法,当修改 data 中的数据时,自动调用 set 方法,检测到数据的变化,会通知观察者 Wacher,观察者 Wacher自动触发重新render 当前组件(子组件不会重新渲染),生成新的虚拟 DOM 树,Vue 框架会遍历并对比新虚拟 DOM 树和旧虚拟 DOM 树中每个节点的差别,并记录下来,最后,加载操作,将所有记录的不同点,局部修改到真实 DOM 树上。
2.1 双向数据绑定原理
检测data变化的核心API
Object.defindeProperty
基本使用
const data = {};
let name = "张三";Object.defineProperty(data,'name',{get:function(){console.log('触发get')return name},set:function(newVal){console.log('触发set')name=newVal}
})//测试
console.log(data.name) // 触发get 张三
data.name = '李四' // 触发set
这样就是可以实现数据的获取和赋值的监听
2.2 接下来看看vue是如何监听data变化的
//触发更新视图
function updateView() {console.log('视图更新')
}
//重新定义数组原型
const oldArrayProperty = Array.prototypo
//创建新对象,原型指向oldArrayProperty,在拓展新的方法(这样不会影响原型)
let arrayProto = Array.prototype
let methods = ['pop', 'shift', 'unshift', 'sort', 'reverse', 'splice', 'push']
methods.forEach(methodName => {arrayProto[methodName] = function () {updateView ()oldArrayProperty[methodName].call(this,...arguments)}
})
//监听对象属性
function observer(target){if(typeof target !=='object' || target === null) {//不是对象或者数组return target}//重新定义数组原型if (Array.isArray(target)) {target.__proto__ = arrProto}//重新定义各个属性(for in 对象/数组都可以遍历)for(let key in target) {defineReactive(target,key,target[key])}
}
//重新定义属性,监听起来
function defineReactive (target, key, value){//递归深度监听observer(value)//核心API Object.defineProperty(target,key,{get(){return value},set(newValue){if(newValue !== value) {// 深度监听observer(newValue)//设置新值value = newvalue//触发更新视图updateView()}}})
}
// 准备数据
const data = {name: 'zhangsan',age: 20,info: {address: '北京' // 需要深度监听},nums: [10, 20, 30]
}data.name = 'lisi' //视图更新data.age = 21 //视图更新console.log('age', data.age) //age 21data.x = '100' // 新增属性,监听不到 —— 所以有 Vue.setdelete data.name // 删除属性,监听不到 —— 所以有 Vue.detedata.info.address = '上海' // 深度监听data.nums.push(4) // 视图更新
缺点
- 深度监听
obj
,需要递归到底,一次性计算量大,如果数据过大页面,页面可能会卡死 - 无法监听新增属性/删除属性(所以vue提供了
Vue.set
Vue.delete
) - 无法原生监听数组,需要做特殊处理
3、vdom和diff
背景
DOM操作是非常耗时的,Vue 和React 是数据驱动视图,就是 通过 虚拟DOM(vdom)来解决的这个问题
3.1 vdom
vdom
就是一段js形式的html
代码
用 js
模拟 DOM
结构
<div id ='app' class='box'><p>p标签的文本</p><ul style='font-size:20px'><li>li标签文本</li></ul>
</div>{tag: 'div',props: {id: 'app',className: 'box'},children: [{tag: 'p',children: 'p标签的文本'},{tag: 'ul',props: {style: 'font-size:20px'},children: [{tag: 'li',children:'li标签文本'}]}]}
- tag 标签
- props 属性(包括 id、className 、 style、事件等)
- children 子元素,数组或者字符串
拓展
可以通过学习snabbdom 进一步了解
var snabbdom = require('snabbdom');
var patch = snabbdom.init([ // Init patch function with chosen modulesrequire('snabbdom/modules/class').default, // makes it easy to toggle classesrequire('snabbdom/modules/props').default, // for setting properties on DOM elementsrequire('snabbdom/modules/style').default, // handles styling on elements with support for animationsrequire('snabbdom/modules/eventlisteners').default, // attaches event listeners
]);
var h = require('snabbdom/h').default; // helper function for creating vnodesvar container = document.getElementById('container');var vnode = h('div#container.two.classes', {on: {click: someFn}}, [h('span', {style: {fontWeight: 'bold'}}, 'This is bold'),' and this is just normal text',h('a', {props: {href: '/foo'}}, 'I'll take you places!')
]);
// Patch into empty DOM element – this modifies the DOM as a side effect
patch(container, vnode);var newVnode = h('div#container.two.classes', {on: {click: anotherEventHandler}}, [h('span', {style: {fontWeight: 'normal', fontStyle: 'italic'}}, 'This is now italic type'),' and this is still just normal text',h('a', {props: {href: '/bar'}}, 'I'll take you places!')
]);
// Second `patch` invocation
patch(vnode, newVnode); // Snabbdom efficiently updates the old view to the new state
解析
- h 是一个函数,接收三个参数(标签或选择器,属性,子节点数组),返回一个vnode结构;
- patch 补丁的意思;
- patch(containerro/容器, vnode/虚拟dom) ,表示把vnode渲染到DOM结构中
- patch(vnode, newVnode); 表示更新已有的内容(具体怎么计算更新对应的内容就是使用
diff
算法) - patch(containerro, null); 清空DOM结构
3.2 diff算法,新旧vnode对比,计算出最小的更新范围
核心
- 同层级比较(只比较同一层级,不跨级比较)
- tag 不相同,则直接删除重建,不在深度比较
- tag 和 key,两个都相同,则认为是相同节点,不在深度比较
再此推荐两篇文章
- 解析vue2.0的diff算法
- VirtualDOM与diff(Vue实现)
4、渲染过程
vue组件渲染/更新过程(异步渲染)
- 初次渲染过程
- 更新过程
4.1初次渲染过程
- 解析模板为
render
函数(一般在开发环境已经完成,vue-loader)4 - 触发响应式,监听data属性
getter
setter
(模板中使用到的变量会触发getter
) - 执行
render
函数(触发getter
),生成vnode,patch(elem,vnode)
渲染到页面上
注意
如果模板中没有用的data数据就不会触发getter
,因为和视图没关系(vue里面的优化)
4.2 更新过程
- 修改data的数据,触发
setter
(此前data数据在getter
中已被监听) - 重新执行
render
函数,生成newVnode(新的虚拟dom)
- 使用
patch(vnode,newVnode)
更新到页面上
- 1、编译模板生成
render
函数,生成vdom
- 2、执行
render
函数,触发data中的getter
- 3、
getter
方法收集依赖(通俗点就是,在模板里面触发了哪个变量的getter
,就把哪个变量观察起来) - 4、在依赖中
setter
修改data中的数据的时候,Notify
看下修改的那个数据是不是之前被观察起来的 - 5、如果是之前观察起来的,就重新渲染(
re-render
),重新生成render
函数,生成newVdom
形成一个闭环
5、路由
vue分为hash
(默认)以及 history
两个路由模式
解析
- protocol - 协议
- hostname - 主机名
- port - 端口
- pathname - url 路径
- search - ?号之后的参数
- hash - #号之后的部分
5.1 hash
特点
- hash 变化会触发页面跳转,即浏览器的前进,后退
- hash 变化不会刷新页面,SPA(单页面)必须的特点
- hash 永远不会提交到server 端
vue中就是通过hash 的变化触发路由的变化,来触发视图的渲染
js 实现hash
<body><p>hash路由</p><button id='btn'>修改 hash</button>
</body><script>//hash 变化 包括;//a. js 修改URL//b. 手动修改url的hash//c.浏览器的前进、后退//页面初次加载获取hashwindow.addEventListener ('DOMCintentLoaded',() =>{console.log('hash',location.hash)})//hash变化触发window.onhashchange = (event) =>{console.log('hash',location.hash)}//js 修改 urldocument.getElementById('btn').addEventListener('click',()=>{location.href = '#/user'})
</script>
5.2 history
h5 history 主要是通过 history.pushState
跳转 和 window.onpopstate
监听页面的前进和后退
<body><p>historyh路由</p><button id='btn'>修改 url</button>
</body><script>//页面初次加载获取hashwindow.addEventListener ('DOMCintentLoaded',() =>{console.log('load',location.pathname)})//js 修改 urldocument.getElementById('btn').addEventListener('click',()=>{//pushState 有三个参数//第一个参数是个js对象,可以放任何的内容,可以在onpostate事件中(后面介绍)获取到便于做相应的、处理。//第二个参数是页面标题:目前所有浏览器都不支持,填空字符串即可//第三个参数是个字符串,就是保存到history中的url。let state= {title:'新页面'}history.pushState(state,'','user')})//监听浏览器的前进、后退window.onpostate = (event) => {console.log(event.state) // {title:'新页面'}console.log(location.pathname)}
</script>
上面的代码如果放在本地html 文件中运行 js代码会报错,需要放在web服务器
注意
history 模式需要后端配合,就是无论用户访问什么路由,所有路由的切换都由前端来做,后端只需要返回index.html
的文件,如果后面没有配置兼容,当访问user这个路由,点击刷新,就会报user页面找不到
以上内容纯属个人理解,若有不对,请留言纠正!
关注【前端知识小册】,第一时间获取前端优质文章!
vue 启动时卡死_十分钟浅入Vue 原理相关推荐
- vue 启动时卡死_使用 Vue 两年后
先来一组数据 使用时长:2年 团队人数:4人 项目数量:4个 最大项目代码量: 33623行 最大项目 SFC: 643个 最大项目页面: 121个 累计编写通用组件:24个 通用组件代码量:2174 ...
- word关闭时卡死_如何修复卡死的Mac
无论是由单个应用还是由操作系统引起的,许多macOS用户至少经历了一次卡死的计算机.好消息是,大多数情况下,这些问题会自行解决,而无需用户采取任何措施.如果确实发生了卡死(无论多么罕见),则可以采取一 ...
- linux pandas教程_十分钟入门 Pandas
# 十分钟入门 Pandas 本节是帮助 Pandas 新手快速上手的简介.烹饪指南里介绍了更多实用案例. 本节以下列方式导入 Pandas 与 NumPy: In [1]: import numpy ...
- python分类预测降低准确率_十分钟掌握python机器学习特征选择
十分钟掌握python机器学习特征选择-1.jpg (33.2 KB, 下载次数: 0) 2018-6-26 23:01 上传 在机器学习中,特征选择是实践中的一步, 帮你在所有特征中选择对结果贡献最 ...
- java程序设计_十分钟说课—Java程序设计(耿祥义)
"十分钟说课"-Java程序设计 耿祥义视频说课: 课程概述 1. Java入门 1) 重点内容,三个基本步骤: 编写源文件,编译,运行. 2)JDK,使用JDK11以上版本,注意 ...
- 逗号后面统一加空格_十分钟搞定字幕,教你做加字幕的“快手菜”
平台上许多同学有疑问:做视频是否有加字幕的必要呢? 其实除了外语需要翻译.语速过快加字幕方便理解.普通话不标准等情况之外,还是建议有余力的同学可以加上字幕,提升用户的观看体验. 那么问题来了,存在以下 ...
- vue data数据修改_史上最强vue总结,万字长文
vue框架篇 vue的优点 轻量级框架:只关注视图层,是一个构建数据的视图集合,大小只有几十kb: 简单易学:国人开发,中文文档,不存在语言障碍 ,易于理解和学习: 双向数据绑定:保留了angular ...
- vue和react相同点_我在React和Vue中创建了相同的应用程序。 这是区别。
vue和react相同点 by Sunil Sandhu 由Sunil Sandhu 我在React和Vue中创建了相同的应用程序. 这是区别. (I created the same app in ...
- vue 箭头函数兼容性_前端学习计划之VUE学习(二)
创建一个实例 每个Vue应用都是通过Vue函数创建一个新的Vue实例开始的: 数据与方法 当Vue实例按照上述方式被创建时,Vue的响应式系统中就加入了data对象,在使用中可以直接通过属性的调用方式 ...
最新文章
- 谷歌丰田联合成果ALBERT了解一下:新轻量版BERT,参数小18倍,性能依旧SOTA
- 将客户端移植到Linux和MAC OS
- 攻防世界-Misc-stegano(巨详细.零基础)
- [SpringMVC]定义多个前缀映射的问题
- Shell脚本中$0、$?、$!、$$、$*、$#、$@
- C++ 类和对象(二):构造函数、析构函数、拷贝构造函数、运算符重载
- centeros6.8 mysql_centeros7安装mysql8,以及设置root密码
- 直接从chrome中复制的body到postman中希望能自动识别去除空格
- Spring.io本地服务器解决超时问题
- 【华为云技术分享】云小课 | 购买的数据盘在服务器看不到?磁盘初始化很重要!
- 高通:预计未来几年向苹果公司出售的芯片将减少
- C++基础new和delete运算符,new和delete简介
- 读SRE Google运维解密有感(一)
- JS键盘事件对象之keyCode、charCode、which属性对比
- MongoDB 数据库_集合_文档 操作
- 01--DNS服务器2
- android手机charles证书下载
- git 拉取所有远程分支
- HTML 复选框元素-复选框(checkbox)
- 落单的数IV --- lintcode 824
热门文章
- docker 查看日志_8个优秀Docker容器监控工具,收藏了
- html中高与行高的区别,深入了解css的行高Line Height属性
- 15crmo焊接后多长时间探伤_承压设备渗透探伤检测方法简单操作思路
- 新生必会的linux命令,jstat命令详解
- 参数php_PHP多参数方法的重构
- 汽车电子专业知识篇(二)-汽车以太网如何影响ECU和传感器设计
- 面试:数据分析面试SQL操作真题解析
- linux cmake 安装mysql5.5.11_以及更高版本_linux cmake 安装mysql5.5.11,以及更高版本
- 安徽阜阳计算机高中学校排名,安徽阜阳排名靠前的三大高中,有争议?2020年高考成绩说话!...
- php 接入微信 验证,PHP实现微信公众平台企业号验证接口