日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

javascript --- Vue初始化 模板渲染

發布時間:2023/12/10 javascript 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 javascript --- Vue初始化 模板渲染 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

不帶響應式的Vue縮減實現

模板

現有模板如下:

<div id ="app"><div class="c1"><div title='tt1' id="id">{{ name }}</div><div title='tt2' >{{age}}</div><div>hello3</div></div><ul><li>1</li><li>2</li><li>3</li></ul> </div> <script>let app = new Vue({el: '#app',data:{name: '張三',age: 19}}) </script>

Vue初始化流程

Vue的初始化,是從new Vue開始的,以下的圖中可以知道在new Vue后,會執行init,再$mount實現掛載,再到編譯compile,生成render函數,接下來是響應式依賴收集,通過pach實現異步更新。render function會被轉化為Vnode節點,Virtual DOM是一棵以JavaScript對象(Vnode節點)為基礎的樹。是對真實DOM的描述。通過patch()轉化為真實DOM。在數據有變化時,會通過setter -> Watcher -> update來更新視圖。整個Vue的運行機制大致就是這樣

實現

  • 在這里實現new Vue -> $mount -> compile -> render function -> Virtual DOM Tree -> patch() -> DOM,即除了響應式的部分.

  • 簡略版

【流程梳理】:

  • 首先要明確目的,我們需要將現有的HTML模板與數據結合,生成一個新的HTML結構,并渲染到頁面上.考慮到性能問題,我們首先將模板讀取到內存中(源代碼是進行HTML解析,生成一棵抽象AST).在這里使用帶mustcache語法的HTML模板代替.

  • 首先是執行new Vue,在Vue函數中會將傳入的數據和模板保存起來,為了后續的方便,會將模板及其父元素也保存起來,然后執行mount

function Vue(options){let elm = document.querySelector(options.el)this._data = options.datathis._template = elmthis._parent = elm.parentNodethis.mount() }
  • 然后是mount函數,在里面做了2件事:
    • 第一件事是將HTML讀取為AST保存在內存中,并返回一個根據AST 和 data 生成 虛擬DOM的render函數
    • 第二件事是調用mountComponent: 將render函數生成的VNode(虛擬DOM)轉換成真實的HTML節點渲染到頁面上

【先看第一件事】

Vue.prototype.mount = function(){this.render = this.createRenderFn() } Vue.prototype.createRenderFn = function(){let AST = getVNode(this._template)return function render(){let _tmp = combine(AST, this._data)return _tmp} }

上面在mount中調用了createRenderFn,生成了一個render函數(AST + DATA -> VNode). 之所以寫出那種形式,

是因為AST僅在一開始讀取DOM結構時候就固定不變了,采用上面的寫法可以提高性能.

getVNode函數根據模板,返回帶mustache語法的虛擬DOM.更多參考

class VNode {constructor(tag ,data, value, type){this.tag = tag && tag.toLowerCase()this.data = datathis.value = valuethis.type = typethis.children = []}appendChild(vnode){this.children.push(vnode)} } function getVNode(node){let nodeType = node.nodeTypelet _vnode = nullif(nodeType == 1){// 元素節點let tag = node.nodeName,attrs = node.attributes,_data = {}for(let i = 0, len = attrs.length; i < len; i++){_data[attrs[i].nodeName] = attrs[i].nodeValue}_vnode = new VNode(tag, _data, undefined, nodeType)// 考慮子元素let childNodes = node.childNodes;for(let i = 0, len = childNodes.length; i< len; i++){_vnode.appendChild(getVNode(childNodes[i]))}} else if(nodeType == 3){// 文本節點_vnode = new VNode(undefined, undefined, node.nodeValue, nodeType)}return _vnode }

此時得到的是一個對象,這個對象中的值類似{{name}}(模擬了AST),下面使用combine將該對象模板與數據結合生成一個新的對象(在Vue中是虛擬的DOM)。即將mustache語法用真實的數據替換

function combine(vnode ,data){let _type = vnode.type, _data = vnode.data, _tag = vnode.tag, _value = vnode.value, _children = vnode.children, _vnode = nullif(_type == 3){// 文本節點_value = _value.replace(/\{\{(.+?)\}\}/g, function(_, g){return getValueByPath(data, g.trim())})_vnode = new VNode(_tag, _data, _value, _type)} else if(_type == 1){// 元素節點_vnode = new VNode(_tag, _data, _value, _type)_children.forEach(_subVNode => _vnode.appendChild(combine(_subVNode, data)))}return _vnode } // getValueByPath,深層次獲取對象的數據. 栗子: 獲取 a.name.age.salary function getValueByPath(obj, path){let res=obj, currProp, props = path.join('.')while(currProp = props.shift()){res = res[props]}return res }

【再看第二件事】

在mountComponent中會使用第一件事中的render函數將AST和Data結合起來生成虛擬DOM,然后調用this.update方法將虛擬DOM渲染到頁面上

Vue.prototype.mountComponent = function(){let mount = () => {this.update(this.render())}mount.call(this) } // 之所以采用this.update,是因為update后面會交付給watcher來調用的 Vue.prototype.update = function (vnode){let realDOM = parseVNode(vnode)this._parent.replaceChild(realDOM, this._template) } function parseVNode(vnode){let type = vnode.type, _node = nullif(type ==3){return document.createTextNode(vnode.value)} else if (type == 1){_node = document.createElement(vnode.tag)let data = vnode.dataObject.keys(data).forEach(key => {_node.setAttribute(key, data[key])})let children = vnode.childrenchildren.forEach(subvnode =>{_node.appendChild(parseNode(subvnode))})}return _node }

整體代碼

<!DOCTYPE html> <html lang="en"><head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>Document</title></head><body><div id="app"><div class="c1"><div title="tt1" id="id">{{ name }}</div><div title="tt2">{{age}}</div><div>hello3</div><ul><li>1</li><li>2</li><li>3</li></ul></div></div><script>/* 虛擬DOM 構造函數 */class VNode {constructor(tag, data, value, type) {this.tag = tag && tag.toLowerCase()this.data = datathis.value = valuethis.type = typethis.children = []}appendChild(vnode) {this.children.push(vnode)}}/* HTML DOM -> VNode(帶坑的Vnode): 將這個函數當做 compiler 函數 *//*Vue中會將真實的DOM結構當作字符串去解析得到一棵 AST此處使用帶有mustache語法的虛擬DOM來代替 AST*/function getVNode(node) {let nodeType = node.nodeTypelet _vnode = nullif (nodeType == 1) {// 元素let nodeName = node.nodeNamelet attrs = node.attributeslet _attrObj = {}for (let i = 0; i < attrs.length; i++) {_attrObj[attrs[i].nodeName] = attrs[i].nodeValue}_vnode = new VNode(nodeName, _attrObj, undefined, nodeType)// 考慮node的子元素let childNodes = node.childNodesfor (let i = 0; i < childNodes.length; i++) {_vnode.appendChild(getVNode(childNodes[i]))}} else if (nodeType == 3) {_vnode = new VNode(undefined, undefined, node.nodeValue, nodeType)}return _vnode}/* 將虛擬DOM轉換成真正的DOM */function parseVNode(vnode){// 創建真實的DOMlet type = vnode.type;let _node = null;if( type == 3){return document.createTextNode(vnode.value)} else if(type == 1){_node = document.createElement(vnode.tag)// 屬性let data = vnode.data // 現在這個data是鍵值對Object.keys(data).forEach((key)=>{let attrName = keylet attrValue = data[key]_node.setAttribute(attrName, attrValue)})// 子元素let children = vnode.children;children.forEach(subvnode =>{_node.appendChild(parseVNode(subvnode))})return _node}}const mustache = /\{\{(.+?)\}\}/g // 匹配{{}}的正則表達式// 根據路徑訪問對象成員function getValueByPath(obj, path) {let res = obj,currProp,props = path.split('.')while ((currProp = props.shift())) {res = res[currProp]}return res}/*模擬 AST -> VNode 的過程將帶有坑(mustache語法)的VNode與數據data結合,得到填充數據的VNode:*/function combine(vnode, data) {let _type = vnode.typelet _data = vnode.datalet _tag = vnode.taglet _value = vnode.valuelet _children = vnode.childrenlet _vnode = nullif (_type == 3) {// 文本節點// 對文本處理_value = _value.replace(mustache, function(_, g) {return getValueByPath(data, g.trim())})_vnode = new VNode(_tag, _data, _value, _type)} else if (_type == 1) {// 元素節點_vnode = new VNode(_tag, _data, _value, _type)_children.forEach(_subVNode => _vnode.appendChild(combine(_subVNode, data)))}return _vnode}function JGVue(options) {// this._options = options;this._data = options.datalet elm = document.querySelector(options.el)this._template = elmthis._parent = elm.parentNodethis.mount() // 掛載}JGVue.prototype.mount = function() {// 需要提供一個render方法: 生成虛擬DOM// if(typeof this._options.render !== 'function'){// }this.render = this.createRenderFn() // 帶有緩存this.mountComponent()}JGVue.prototype.mountComponent = function() {// 執行mountComponent()let mount = () => {// update將虛擬DOM渲染到頁面上this.update(this.render())}mount.call(this) // 本質上應該交給 watcher 來調用// 為什么// this.update(this.render()) // 使用發布訂閱模式,渲染和計算的行為應該交給watcher來完成}/*在真正的Vue中,使用了二次提交的設計結構第一次提交是在內存中,在內存中確定沒有問題了在修改硬盤中的數據1. 在頁面中的DOM和虛擬DOM是一一對應的關系*/// 這里是生成render函數,目的是緩存抽象語法樹(我們使用虛擬DOM來模擬)JGVue.prototype.createRenderFn = function() {let AST = getVNode(this._template)// 將 AST + data => VNode// 我們: 帶坑的VNode + data => 含有數據的 VNodereturn function render() {// 將帶坑的VNode轉換為真正帶數據的VNodelet _tmp = combine(AST, this._data)return _tmp}}// 將虛擬DOM熏染到頁面中: diff算法就在這里JGVue.prototype.update = function(vnode) {// 簡化,直接生成HTML DOM replaceChild 到頁面中// 父元素.replaceChild(新元素,舊元素)let realDOM = parseVNode(vnode)// debuggerthis._parent.replaceChild(realDOM, document.querySelector('#app'))// 這個算法是不負責任的// 每次都會將頁面中的DOM全部替換}let app = new ({el: '#app',data: {name: '張三',age: 19}})</script></body> </html>

總結

以上是生活随笔為你收集整理的javascript --- Vue初始化 模板渲染的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 五月激情在线 | 国产黄色网| 99精品毛片| 免费精品久久 | 黄色成人免费网站 | 欧美日韩在线播放视频 | 欧美区在线观看 | 成人乱码一区二区三区av | 日本www色| 天海翼一二三区 | 色臀| 蜜臀中文字幕 | 成人综合在线观看 | av先锋资源| 伊人99热 | 好色先生视频污 | 女人天堂网站 | 亚洲AV无码国产精品播放在线 | 日韩一级视频在线观看 | 欧美日韩一区二区三区四区五区 | 国产国语对白 | 91精品在线免费 | 色网站在线 | 91污网站| 91爱啪啪| 中国丰满人妻videoshd | 久久久麻豆 | 亚洲图片欧美在线 | 男ji大巴进入女人的视频 | 好吊妞视频一区二区三区 | 人人妻人人爽人人澡人人精品 | 夜色快播 | 激情五月色播五月 | 吃奶摸下的激烈视频 | 根深蒂固在线观看 | 国产一区二区三区高清视频 | 中文区中文字幕免费看 | 中文字幕精品三级久久久 | 男女激情免费网站 | 亚洲av综合色区无码一区爱av | 精品国产乱码久久久久久久软件 | 五月婷婷社区 | 美女黄污网站 | 欧美jizzhd欧美18 | 免费久久一级欧美特大黄 | 亚洲精品一区二区三区不卡 | 女人又爽又黄免费女仆 | av在线网站观看 | 激情综合网五月天 | 伊人影院网 | 激情五月色综合国产精品 | 影音先锋制服丝袜 | 久久久久亚洲日日精品 | 亚洲三级免费观看 | 国产99久久久国产精品 | 高清不卡一区 | 午夜影院在线观看 | 天天操综合网 | 日产精品久久久一区二区 | 东京热加勒比无码少妇 | 日韩城人视频 | 动漫av一区 | 少妇无码一区二区三区免费 | 性欧美极品另类 | 国产每日更新 | 91麻豆国产 | 伊人久久超碰 | 黄色网址在线视频 | 日本成人免费观看 | 日本免费网站 | 国产又粗又猛又爽又黄的网站 | aaa在线视频 | 中文字幕3区 | 深夜免费视频 | 偷偷操网站 | 性欧美在线视频观看 | 啪啪av | jizz中国少妇 | 国产日韩欧美一区二区东京热 | 丁香婷婷成人 | a男人天堂 | 韩日午夜在线资源一区二区 | 精品www| 丁香花完整视频在线观看 | 免费日韩在线 | 少妇一级片 | 国产伦精品一区二区三区高清版 | 日本成人激情 | av中文资源在线 | 亚洲六月丁香色婷婷综合久久 | 精品国产成人亚洲午夜福利 | 91亚洲精华 | 免费看日批| 久久99精品久久久水蜜桃 | 长篇高h肉爽文丝袜 | 少妇极品熟妇人妻无码 | 金鱼妻日剧免费观看完整版全集 | 亚洲成人一区在线 | 桃色视频网 |