<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                企業??AI智能體構建引擎,智能編排和調試,一鍵部署,支持知識庫和私有化部署方案 廣告
                >[success] # 簡單的了解虛擬DOM(二) `Virtual DOM`(虛擬dom)構成 是使用`VNode`(虛擬節點),虛擬節點實際是一種**js對象表現形式**,通過創建一個簡單`VNode`(虛擬節點)類 * 創建一個Vnode class 用來生成Vnode 對象 ~~~ class VNode { constructor(tag, data, children, text, elm) { /*當前節點的標簽名*/ this.tag = tag /*當前節點的一些數據信息,比如props、attrs等數據*/ this.data = data /*當前節點的子節點,是一個數組*/ this.children = children /*當前節點的文本*/ this.text = text /*當前虛擬節點對應的真實dom節點*/ this.elm = elm } } ~~~ * 使用VNode 類去轉換 一個vue模板 ~~~html <template> <span class="demo" v-show="isShow"> // 這里是第一個部分的VNode 對象 This is a span. // 這里是第二個部分的VNode 對象 </span> </template> ~~~ ~~~ function render() { const tag = 'span' const data = { // 指令集和 directives: [ { rawName: 'v-show', expression: 'isShow', name: 'show', value: true, }, ], /* 靜態class */ staticClass: 'demo', } const text = undefined const elm = undefined const children = [ new VNode( undefined, undefined, undefined, 'This is a span.', undefined ), ] return new VNode(tag, data, children, text, elm) } console.log(render()) ~~~ * 打印效果 ~~~ VNode { tag: 'span', data: { directives: [ [Object] ], staticClass: 'demo' }, children: [ VNode { tag: undefined, data: undefined, children: undefined, text: 'This is a span.', elm: undefined } ], text: undefined, elm: undefined } ~~~ >[danger] ##### 將VNode 二次封裝 `VNode` 虛擬節點其實和`dom節點`思路一樣,空節點,文本節點等 * 空節點類 ~~~ function createEmptyVNode () { const node = new VNode(); node.text = ''; return node; } ~~~ * 文本節點類 ~~~ function createTextVNode (val) { return new VNode(undefined, undefined, undefined, String(val)); } ~~~ * 克隆節點類 ~~~ function cloneVNode (node) { const cloneVnode = new VNode( node.tag, node.data, node.children, node.text, node.elm ); return cloneVnode; } ~~~ >[info] ## template 模板 編譯 上面情況是**人為思想去拆解各個部分參數作為VNode參數**,實際情況下會利用`ast`語法樹做一個語法分析 (你可以參看babel章節的ast講解), 獲取到 `ast` 語法樹的結構對應屬性在和`VNode`對接形成`VNode`對象 整體思路就是依次循環字符串,找到符合匹配的規則進行保存,因此需要一個指針方法`advance`,頭部的指針指向接下來需要匹配的部分 ![](https://img.kancloud.cn/f5/dd/f5dd766677e6f93c4e782ad9f9f96981_1055x172.png) * advance(43); ![](https://img.kancloud.cn/96/37/963794ad395f4a640d9438d855482860_1038x146.png) >[danger] ##### ast 結構 ~~~ <div :class="c" class="demo" v-if="isShow"> <span v-for="item in sz">{{item}}</span> </div> ~~~ ~~~ { /* 標簽屬性的map,記錄了標簽上屬性 */ 'attrsMap': { ':class': 'c', 'class': 'demo', 'v-if': 'isShow' }, /* 解析得到的:class */ 'classBinding': 'c', /* 標簽屬性v-if */ 'if': 'isShow', /* v-if的條件 */ 'ifConditions': [ { 'exp': 'isShow' } ], /* 標簽屬性class */ 'staticClass': 'demo', /* 標簽的tag */ 'tag': 'div', /* 子標簽數組 */ 'children': [ { 'attrsMap': { 'v-for': "item in sz" }, /* for循環的參數 */ 'alias': "item", /* for循環的對象 */ 'for': 'sz', /* for循環是否已經被處理的標記位 */ 'forProcessed': true, 'tag': 'span', 'children': [ { /* 表達式,_s是一個轉字符串的函數 */ 'expression': '_s(item)', 'text': '{{item}}' } ] } ] } ~~~ >[danger] 將html 解析ast ~~~ const ncname = `[a-zA-Z_][\\-\\.0-9_a-zA-Z]*`; // abc-aaa const qnameCapture = `((?:${ncname}\\:)?${ncname})`; // <aaa:asdads> const startTagOpen = new RegExp(`^<${qnameCapture}`); // 標簽開頭的正則 捕獲的內容是標簽名 const endTag = new RegExp(`^<\\/${qnameCapture}[^>]*>`); // 匹配標簽結尾的 </div> const attribute = /^\s*([^\s"'<>\/=]+)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/; // 匹配屬性的 const startTagClose = /^\s*(\/?)>/; // 匹配標簽結束的 > <div> // 將html 變成ast 抽象語法樹 export function parseHTML(html) { let root = null; // ast語法樹的樹根 let currentParent; // 標識當前父親是誰 let stack = []; const ELEMENT_TYPE = 1; // dom 元素類型 1 const TEXT_TYPE = 3; // dom 文本類型三 function createASTElement(tagName, attrs) { return { tag: tagName, type: ELEMENT_TYPE, children: [], attrs, parent: null } } function start(tagName, attrs) { // 遇到開始標簽 就創建一個ast元素 let element = createASTElement(tagName, attrs); if (!root) { root = element; } currentParent = element; // 把當前元素標記成父ast樹 stack.push(element); // 將開始標簽創建一個ast元素放到棧中 } function chars(text) { text = text.replace(/\s/g, ''); if (text) { currentParent.children.push({ text, type: TEXT_TYPE }) } } function end(tagName) { let element = stack.pop(); // 拿到的是ast對象 // 我要標識當前這個p是屬于這個div的兒子的 currentParent = stack[stack.length - 1]; if (currentParent) { element.parent = currentParent; currentParent.children.push(element); // 實現了一個樹的父子關系 } } console.log(html) // 將html 字符串解析成ast 語法樹 // 現在循環整個html 字符串安裝對應規則生成對應的ast表示 while (html) { let textEnd = html.indexOf('<') // 如果textEnd索引為0,就說明是一個標簽 可能是開始標簽或者結束標簽 // 例如 <p>woooo</p> if (textEnd === 0) { let startTagMatch = parseStartTag(); // 通過這個方法獲取到匹配的結果 tagName,attrs if (startTagMatch) { start(startTagMatch.tagName, startTagMatch.attrs); // 1解析開始標簽 continue; // 如果開始標簽匹配完畢后 繼續下一次 匹配 } let endTagMatch = html.match(endTag); if (endTagMatch) { advance(endTagMatch[0].length); end(endTagMatch[1]); // 2解析結束標簽 continue; } } // 如果不是< 括號開頭的說明不是標簽 是文本例如 <p>我是文本</p> let text if (textEnd >= 0) { // 獲取文本內容 text = html.substring(0, textEnd); } if (text) { advance(text.length); chars(text); // 3解析文本 } } // 每次從已經匹配過得地方開始截取 function advance(n) { html = html.substring(n); } function parseStartTag() { /** * < div id = "app" ><p> www </p> </div> * ["<div", "div", index: 0, input: "<div id="app ">? <p>www</p>? </div>", groups: undefined] */ let start = html.match(startTagOpen) if (start) { const match = { tagName: start[1], attrs: [] } advance(start[0].length); // 將標簽刪除 將匹配到的內容沖html中截取掉 let end, attr // 開始獲取標簽中的元素存儲到attrs 中 // 如果不是結束標簽并且dom中有屬性就循環取出來 // 上面已經html 截取了一次因此現在 如果有屬性的展示效果 class="name"></div> while (!(end = html.match(startTagClose)) && (attr = html.match(attribute))) { // 將屬性進行解析 advance(attr[0].length); // 將屬性去掉 match.attrs.push({ name: attr[1], value: attr[3] || attr[4] || attr[5] }); } if (end) { // 去掉開始標簽的 > advance(end[0].length); console.log(match) /* { "tagName": "div", "attrs": [{ "name": "id", "value": "app" }] } */ return match } } } return root; } ~~~ * ast 轉換后結構 * 再將獲取ast 語法樹的結構裝進VNode中 ~~~ // 拼出基本格式 function generate(el) { // 開始處理模板的基本模型 基本模型拆分理解 // 1._c的函數包裹 // 第一個參數是dom 標簽節點,第二個參數是dom 上面的屬性,第三個參數如果有子節點 // 就這接著重復_c的效果 let children = genChildren(el); // 子節點 // 拼出基本結構 el.tag 中tag 是ast中一個屬性 let code = `_c("${el.tag}",${ el.attrs.length?genProps(el.attrs):'undefined' }${ children? `,${children}` :'' }) ` return code; } ~~~ >[danger] ##### 可參考 [參考鏈接](https://juejin.cn/book/6844733705089449991/section/6844733705232056334)
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看