vue原理20181211
1. 打开浏览器从输入网址到网页呈现出来,经历了什么?
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
打开浏览器从输入网址到网页呈现出来,经历了什么? 1.1 DNS解析域名 1.2 TCP连接:TCP三次握手 1.3 发送HTTP请求 1.4 服务器处理请求并返回HTTP报文 1.5 浏览器解析渲染页面 1.6 断开连接: TCP四次挥手 1.网址 URL(Uniform Resource Locator) 格式:scheme://host.domain:port/path/filename scheme: http/https/ftp host 主机 http 的默认主机是 www domain 域名 baidu.com port 端口 http 的默认端口号是 80 2.IP 地址,32位的二进制数 3.DNS解析:将域名解析成IP地址 baidu.com 220.114.33.55 浏览器并不能直接通过域名找到对应的服务器,而是要通过 IP 地址。 查找域名和IP的对应记录:从浏览器缓存、操作系统缓存、路由器缓存、ISP的DNS服务器、根服务器依次查找记录 根服务器进行递归查询:从查.cn 再查 .com.cn 再查 baidu.com.cn 再查 www.baidu.com.cn 4.TCP三次握手: 第一次握手:浏览器发送数据包到服务器端口,告诉浏览器要发送请求了 第二次握手:服务器发送响应包以传达确认信息,告诉浏览器可以发送了 第三次握手:浏览器再发数据包告诉服务器,马上要发了,准备接受 5.http请求 TCP 三次握手结束后,开始发送 HTTP 请求报文。 请求报文由请求行(request line)、请求头(header)、请求体四个部分组成 请求行包含请求方法(get post)、URL、协议版本(http版本号) example: POST /chapter17/user.html HTTP/1.1 请求头是键值对 请求体 example: name=kang 6. http响应报文 响应报文由响应行(request line)、响应头部(header)、响应主体三个部分组成 响应行包含:协议版本,状态码,状态码描述 状态码 200 404 500 7. 浏览器渲染机制 根据 HTML 解析出 DOM 树 根据 CSS 解析生成 CSS 规则树 结合 DOM 树和 CSS 规则树,生成渲染树 根据渲染树计算每一个节点的信息(布局) 根据计算好的信息绘制页面(paint) 解析DOM树时,遇到script标签,会等脚本先执行 解析CSS树时,js执行会暂停, 精简 CSS 并可以加快 CSS 规则树的构建,从而加快页面相应速度 重绘:绘制页面时,某元素颜色等(不影响其他元素布局的属性)发生变化 回流:绘制页面时,某元素尺寸发生变化,要重新计算渲染树,重新渲染 8. 断开tcp连接,发起tcp四次挥手 第一次挥手:浏览器发送给服务器,表示请求报文已发完,你可以关闭了 第二次挥手:服务器发给浏览器,请求报文接收完,准备关闭 第三次挥手:服务器发给浏览器,响应报文发送完,你准备关闭 第四次挥手:浏览器发给服务器,响应报文完了,准备关闭 HTTP 请求阶段,HTTP 请求分为三个部分:TCP 三次握手、http 请求响应信息、关闭 TCP 连接。 浏览器向dns服务器发送域名,DNS服务器查询并返回IP地址,浏览器再发送到对应服务器请求数据 参考链接:https://mp.weixin.qq.com/s?__biz=MzAxODE2MjM1MA==&mid=2651555392&idx=1&sn=9042c990f82fe5d03f03e6af7536b4c3&chksm=80255181b752d897524a6f1ee332f245761c89a5e01d0cbf0499e3a74a21f3865f8cfff823cf&mpshare=1&scene=1&srcid=1126MqCAj6jUV3jqGvxxDNAF#rd
【以下为vue实现原理相关知识点】
2. document.createDocumentFragment() fragment 文档片段,存于内存中,不在DOM树,性能好 例子来自 mdn
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/** * document.createDocumentFragment() * DocumentFragments 是DOM节点 * 创建文档片段,将元素附加到文档片段,然后将文档片段附加到DOM树。 * 因为文档片段存在于内存中,并不在DOM树中,所以将子元素插入到文档片段时不会引起页面回流(对元素位置和几何上的计算)。因此,使用文档片段通常会带来更好的性能。 */ function fragment () { var element = document.getElementById('ul') // assuming ul exists var fragment = document.createDocumentFragment() // 存于内存,不在DOM树,性能好 var browsers = ['Firefox', 'Chrome', 'Opera', 'Safari', 'Internet Explorer'] browsers.forEach(function (browser) { var li = document.createElement('li') li.textContent = browser fragment.appendChild(li) // 先追加到 fragment }) element.appendChild(fragment) // 再追加到dom节点 } fragment()
3.nodeType 1是元素 3是文本
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
function nodeType () { var p = document.createElement("p"); p.textContent = "很久很久以前..."; console.log(Node.ELEMENT_NODE === 1) // p console.log(Node.TEXT_NODE === 3) // 很久很久以前... console.log(p.nodeType === Node.ELEMENT_NODE) // true console.log(p.firstChild) // 很久很久以前... console.log(p.firstChild.nodeType === Node.TEXT_NODE) // true }
4.white循环 对比for循环
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
function whiteTest() { let arr = [1, 2, 3, 4, 5] let brr = [] i = 0 while (i < 3) { brr.push(arr[i]) i++ } console.log(brr) // [0,1,2] for(var i =0;i<3;i++){ brr.push(arr[i])} }
5.正则 * 0~多 + 1~多 ? 0~1 .匹配所有字符
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/** * 正则 * 0~多 + 1~多 ? 0~1 .匹配所有字符 * @param text * @returns {boolean} */ function isReg (text) { let reg = /\{\{(.*)\}\}/ // * 0~多 + 1~多 ? 0~1 .匹配所有字符 return reg.test(text) } console.log(isReg('{{}}')) // true console.log(isReg('{{.gfg}}')) // true console.log(isReg('{{.g}}')) // true
6. node节点操作 firstChild 节点不要出现换行等
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<p id="para-01"> <span>First span</span> </p> <script type="text/javascript"> /* * 在<p>标签和<span>标签之前,有一个换行符和多个空格充当了一个文本节点,导致 结果为 #text * 操作node节点不要出现空格 * */ var p01 = document.getElementById('para-01'); console.log((p01.firstChild)) // #text 大对象 console.log((p01.firstChild.nodeName)) // #text </script>
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<p id="para-012"><span>First span</span></p> <script type="text/javascript"> var p012 = document.getElementById('para-012'); console.log((p012.firstChild)) // <span>First span</span> console.log((p012.firstChild.nodeName)) // SPAN console.log((p012.firstChild.firstChild.nodeValue)) // First span console.log((p012.firstChild.innerHTML)) // First span </script>
7. li 标签转换成数组操作 也可以用 for 来解决 [].slice.call($Li).forEach(val=>{})
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/* * 原数组的浅拷贝 slice() * arr.slice(); // [0, end] arr.slice(begin); // [begin, end] arr.slice(begin, end); // [begin, end) [].slice.call($li) // li标签复制一份,转换为数组调用 forEach * */ let $li = document.getElementsByTagName('li') console.log($li) for(var i=0;i<$li.length;i++){ console.log($li[i].firstChild.nodeValue) console.log($li[i].innerHTML.replace(/^abc/, '')) } console.log('-------'); console.log('dd',[].slice.call($li)); [].slice.call($li).forEach(val=>{ val.innerHTML=val.innerHTML.replace(/^abc/,'') })
8. 对象的 for循环 Object.keys(obj).forEach(key=>{}) 同时拿到 obj key val
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
let obj = { name:'kang', age:12, date:{ y:2018, m:12, d:13 } } Object.keys(obj).forEach(key=>{ console.log(obj) console.log(key) console.log(obj[key]) })
9.Object.defineProperty() 对象监听
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
let obj = {name: 'kang', age: 12, date: {y: 12}} observe(obj) obj.name = 'jia' obj.age = 20 obj.date.y = 11 for (var i in obj) { // enumerable 为false时,无法使用 for...in 和 object.keys() // console.log('df',i) } var o = { // 用这个方式时,enumerable为 true 可用 for...in d: 1 } /** * 监听对象 * @param obj */ function observe (obj) { if (!obj || typeof obj !== 'object') return // 遍历所有对象 Object.keys(obj).forEach(key => { // obj 原对象 // key : name age date // obj[key]: kang 12 {y:12} // obj[key] 有可能是个对象,所以要再递归 listen(obj, key, obj[key]) }) } /** * Object.defineProperty 监听属性变动 * @param data * @param key * @param val */ function listen (data, key, val) { observe(val) // 递归 先把递归注释掉容易理解点 // Obeject.defineProperty() 来监听属性变动 Object.defineProperty(data, key, { enumerable: true, // 可枚举 默认为false enumerable定义了对象的属性是否可以在 for...in 循环和 Object.keys() 中被枚举。 get: function () { return val }, set: function (newVal) { if (val === newVal) return console.log('监听到新值了', val, '->', newVal) val = newVal } }) }
10. node节点操作 node.firstChild childNodes textContent nodeType attributes
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> <input type="text" v-model="someStr"> <p>{{getHelloWord}}</p> <p v-html="htmlStr"></p> <button v-on:click="clickBtn">change model</button> </div> <script> let $el = document.querySelector('#app') let $fragment = node2Fragment($el) // 转成 文档碎片,在DOM上不会显示出来 $el.appendChild($fragment) // console.log($el.textContent) // 返回该元素下所有文本节点 {{getHelloWord}} change model function node2Fragment ($el) { let fragment = document.createDocumentFragment() while ($el.firstChild) { // console.log($el.firstChild) // #text <input type=...> #text <p>{{... #text <p v...> #text <button...> #text // 两个标签之间的空格换行解析成 #text 文本节点 fragment.appendChild($el.firstChild) } // 方式二 /* for(;$el.firstChild;){ // for 三个条件可以省略第一个,第三个(可能写在for前面、for里面),只要能够执行 fragment.appendChild($el.firstChild) }*/ // 方式三 最好理解 /* ;[].slice.call($el.childNodes).forEach(node=>{ fragment.appendChild(node) })*/ return fragment } /* * https://developer.mozilla.org/en-US/docs/Web/API/Node * * node.firstChild 返回第一个child node or null * node.childNodes 返回nodeList 包含所有 children 如果children变了,nodeList会自动更新 * node.textContent 返回或者设置元素及其子元素的所有文本节点 空格部分为 '' * node.nodeType 1 是元素ELEMENT_NODE <div>...</div> 3 是文本TEXT_NODE #text * */ // console.log($el.firstChild) // #text 只拿到第一个空格部分 // console.log($el.childNodes); // NodeList[text,input,text,p,text,p,text,button,text] 空格解析为text ;[].slice.call($el.childNodes).forEach(node => { // console.log(node) // #text <input type='text' v-model='somestr'> #text // console.log(node.textContent) // '' {{getHelloWord}} change model '' // console.log(node.nodeType) // 3 1 3 // console.log(node.attributes) // undefined NameNodeMap{} undefined if (node.nodeType === 1) { // 处理元素节点 <div class='a'>...</div> elementNode(node) } else if (node.nodeType === 3) { // 处理 {{xx}} let reg = /\{\{.*\}\}/ let text = node.textContent console.log(text) if (reg.test(text)) { console.log(node) } } }) /* * DOM1级主要定义的还是HTML的地层结构,DOM2和DOM3级则在这个结构的基础上引入了更多的交互能力 * * node.attributes DOM4 删除该属性,目前还是DOM2 DOM3 元素节点属性 * * * * * */ function elementNode (node) { var nodeAttr = node.attributes ;[].slice.call(nodeAttr).forEach(attr => { var attrName = attr.name // console.log(attr) // type='text' v-model='someStr' 等 // console.log(attr.name) // type v-model 等 // console.log(attr.value) // text someStr }) } </script> </body> </html>
【暂停】
2-10为vue双向绑定原理知识点
教程 https://segmentfault.com/a/1190000006599500#articleHeader3
源码: https://github.com/gyz418/mvvm