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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

一套代码小程序WebNative运行的探索02

發布時間:2025/4/5 编程问答 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 一套代码小程序WebNative运行的探索02 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

接上文:一套代碼小程序&Web&Native運行的探索01,本文都是一些探索性為目的的研究學習,在最終版輸出前,內中的內容可能會有點亂

參考:

https://github.com/fastCreator/MVVM

https://www.tangshuang.net/3756.html

https://www.cnblogs.com/kidney/p/8018226.html

經過之前的學習,發現Vue其實與小程序框架相識度比較高,業內也有mpvue這種還比較成熟的方案了,我們這邊依舊不著急去研究成熟的框架,現在看看自己能做到什么程度,最近也真正的開始接觸了一些Vue的東西,里面的代碼真的非常不錯,研究學習了下Vue的結構,發現其實跟我們要的很類似,這里想要嘗試初步的方案:提供Html模板->解析Html模板,其實這里就是Vue里面Parse部分的邏輯,一小部分代碼,這樣有很多Vue的代碼可以借鑒,也變相的學習Vue的源碼,一舉兩得,于是我們速度開始今天的學習

首先,我們設置一個簡單的目標:設置一段簡單的小程序模板,當我們做完web版本后,他可以在小程序中運行

<view class="c-row search-line" data-flag="start" ontap="clickHandler"><view class="c-span9 js-start search-line-txt">{{name}}</view> </view> 1 Page({ 2 data: { 3 name: 'hello world' 4 }, 5 clickHandler: function () { 6 this.setData({ 7 name: '葉小釵' 8 }) 9 } 10 })

這里第一個關鍵便是將html模板轉換為js代碼,如果是之前我們直接會用這種代碼:

1 _.template = function (text, data, settings) { 2 var render; 3 settings = _.defaults({}, settings, _.templateSettings); 4 5 // Combine delimiters into one regular expression via alternation. 6 var matcher = new RegExp([ 7 (settings.escape || noMatch).source, 8 (settings.interpolate || noMatch).source, 9 (settings.evaluate || noMatch).source 10 ].join('|') + '|$', 'g'); 11 12 // Compile the template source, escaping string literals appropriately. 13 var index = 0; 14 var source = "__p+='"; 15 text.replace(matcher, function (match, escape, interpolate, evaluate, offset) { 16 source += text.slice(index, offset) 17 .replace(escaper, function (match) { return '\\' + escapes[match]; }); 18 19 if (escape) { 20 source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'"; 21 } 22 if (interpolate) { 23 source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'"; 24 } 25 if (evaluate) { 26 source += "';\n" + evaluate + "\n__p+='"; 27 } 28 index = offset + match.length; 29 return match; 30 }); 31 source += "';\n"; 32 33 // If a variable is not specified, place data values in local scope. 34 if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n'; 35 36 source = "var __t,__p='',__j=Array.prototype.join," + 37 "print=function(){__p+=__j.call(arguments,'');};\n" + 38 source + "return __p;\n"; 39 40 try { 41 render = new Function(settings.variable || 'obj', '_', source); 42 } catch (e) { 43 e.source = source; 44 throw e; 45 } 46 47 if (data) return render(data, _); 48 var template = function (data) { 49 return render.call(this, data, _); 50 }; 51 52 // Provide the compiled function source as a convenience for precompilation. 53 template.source = 'function(' + (settings.variable || 'obj') + '){\n' + source + '}'; 54 55 return template; 56 }; underscore里面的代碼

將上述代碼做字符串處理成字符串函數,然后將data傳入,重新渲染即可。然而技術在變化,在進步。試想我們一個頁面某個子節點文字發生了變化,全部重新渲染似乎不太劃算,于是出現了虛擬DOM概念(React 導致其流行),他出現的意義就是之前我們使用jQuery操作10次dom的時候瀏覽器會操作10次,這里render過程中導致的坐標計算10次render tree的形成可能讓頁面變得越來越卡,而虛擬DOM能很好的解決這一切,所以這里我們就需要將我們模板中的代碼首先轉換為虛擬DOM,這里涉及到了復雜的解析過程

PS:回到最初Server渲染時代,每次點擊就會導致一次服務器交互,并且重新渲染頁面

Virtual DOM

我們做的第一步就是將模板html字符串轉換為js對象,這個代碼都不要說去實現,光是想想就知道里面必定會有大量的正則,大量的細節要處理,但我們的目標是一套代碼多端運行,完全沒(能力)必要在這種地方耗費時間,所以我們直接閱讀這段代碼:https://johnresig.com/blog/pure-javascript-html-parser/,稍作更改后,便可以得到以下代碼:

1 /* 2 * Modified at https://github.com/blowsie/Pure-JavaScript-HTML5-Parser 3 */ 4 5 // Regular Expressions for parsing tags and attributes 6 let startTag = /^<([-A-Za-z0-9_]+)((?:\s+[a-zA-Z_:@][-a-zA-Z0-9_:.]*(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)>/, 7 endTag = /^<\/([-A-Za-z0-9_]+)[^>]*>/, 8 attr = /([a-zA-Z_:@][-a-zA-Z0-9_:.]*)(?:\s*=\s*(?:(?:"((?:\\.|[^"])*)")|(?:'((?:\\.|[^'])*)')|([^>\s]+)))?/g 9 10 // Empty Elements - HTML 5 11 let empty = makeMap("area,base,basefont,br,col,frame,hr,img,input,link,meta,param,embed,command,keygen,source,track,wbr") 12 13 // Block Elements - HTML 5 14 let block = makeMap("a,address,article,applet,aside,audio,blockquote,button,canvas,center,dd,del,dir,div,dl,dt,fieldset,figcaption,figure,footer,form,frameset,h1,h2,h3,h4,h5,h6,header,hgroup,hr,iframe,ins,isindex,li,map,menu,noframes,noscript,object,ol,output,p,pre,section,script,table,tbody,td,tfoot,th,thead,tr,ul,video") 15 16 // Inline Elements - HTML 5 17 let inline = makeMap("abbr,acronym,applet,b,basefont,bdo,big,br,button,cite,code,del,dfn,em,font,i,iframe,img,input,ins,kbd,label,map,object,q,s,samp,script,select,small,span,strike,strong,sub,sup,textarea,tt,u,var") 18 19 // Elements that you can, intentionally, leave open 20 // (and which close themselves) 21 let closeSelf = makeMap("colgroup,dd,dt,li,options,p,td,tfoot,th,thead,tr") 22 23 // Attributes that have their values filled in disabled="disabled" 24 let fillAttrs = makeMap("checked,compact,declare,defer,disabled,ismap,multiple,nohref,noresize,noshade,nowrap,readonly,selected") 25 26 // Special Elements (can contain anything) 27 let special = makeMap("script,style") 28 29 function makeMap(str) { 30 var obj = {}, items = str.split(","); 31 for (var i = 0; i < items.length; i++) 32 obj[items[i]] = true; 33 return obj; 34 } 35 36 export default function HTMLParser(html, handler) { 37 var index, chars, match, stack = [], last = html; 38 stack.last = function () { 39 return this[this.length - 1]; 40 }; 41 42 while (html) { 43 chars = true; 44 45 // Make sure we're not in a script or style element 46 if (!stack.last() || !special[stack.last()]) { 47 48 // Comment 49 if (html.indexOf("<!--") == 0) { 50 index = html.indexOf("-->"); 51 52 if (index >= 0) { 53 if (handler.comment) 54 handler.comment(html.substring(4, index)); 55 html = html.substring(index + 3); 56 chars = false; 57 } 58 59 // end tag 60 } else if (html.indexOf("</") == 0) { 61 match = html.match(endTag); 62 63 if (match) { 64 html = html.substring(match[0].length); 65 match[0].replace(endTag, parseEndTag); 66 chars = false; 67 } 68 69 // start tag 70 } else if (html.indexOf("<") == 0) { 71 match = html.match(startTag); 72 73 if (match) { 74 html = html.substring(match[0].length); 75 match[0].replace(startTag, parseStartTag); 76 chars = false; 77 } 78 } 79 80 if (chars) { 81 index = html.indexOf("<"); 82 83 var text = index < 0 ? html : html.substring(0, index); 84 html = index < 0 ? "" : html.substring(index); 85 86 if (handler.chars) 87 handler.chars(text); 88 } 89 90 } else { 91 html = html.replace(new RegExp("([\\s\\S]*?)<\/" + stack.last() + "[^>]*>"), function (all, text) { 92 text = text.replace(/<!--([\s\S]*?)-->|<!\[CDATA\[([\s\S]*?)]]>/g, "$1$2"); 93 if (handler.chars) 94 handler.chars(text); 95 96 return ""; 97 }); 98 99 parseEndTag("", stack.last()); 100 } 101 102 if (html == last) 103 throw "Parse Error: " + html; 104 last = html; 105 } 106 107 // Clean up any remaining tags 108 parseEndTag(); 109 110 function parseStartTag(tag, tagName, rest, unary) { 111 tagName = tagName.toLowerCase(); 112 113 if (block[tagName]) { 114 while (stack.last() && inline[stack.last()]) { 115 parseEndTag("", stack.last()); 116 } 117 } 118 119 if (closeSelf[tagName] && stack.last() == tagName) { 120 parseEndTag("", tagName); 121 } 122 123 unary = empty[tagName] || !!unary; 124 125 if (!unary) 126 stack.push(tagName); 127 128 if (handler.start) { 129 var attrs = []; 130 131 rest.replace(attr, function (match, name) { 132 var value = arguments[2] ? arguments[2] : 133 arguments[3] ? arguments[3] : 134 arguments[4] ? arguments[4] : 135 fillAttrs[name] ? name : ""; 136 137 attrs.push({ 138 name: name, 139 value: value, 140 escaped: value.replace(/(^|[^\\])"/g, '$1\\\"') //" 141 }); 142 }); 143 144 if (handler.start) 145 handler.start(tagName, attrs, unary); 146 } 147 } 148 149 function parseEndTag(tag, tagName) { 150 // If no tag name is provided, clean shop 151 if (!tagName) 152 var pos = 0; 153 154 // Find the closest opened tag of the same type 155 else 156 for (var pos = stack.length - 1; pos >= 0; pos--) 157 if (stack[pos] == tagName) 158 break; 159 160 if (pos >= 0) { 161 // Close all the open elements, up the stack 162 for (var i = stack.length - 1; i >= pos; i--) 163 if (handler.end) 164 handler.end(stack[i]); 165 166 // Remove the open elements from the stack 167 stack.length = pos; 168 } 169 } 170 }; View Code

這是一段非常牛逼的代碼,要寫出這種代碼需要花很多功夫,繞過很多細節,自己寫很難還未必寫得好,所以拿來用就好,不必愧疚......,但是我們需要知道這段代碼干了什么:

他會遍歷我們的字符串模板,解析后會有四個回調可供使用:start、end、chars、comment,我們要做的就是填充里面的事件,完成我們將HTML轉換為js對象的工作:

1 <!doctype html> 2 <html> 3 <head> 4 <title>起步</title> 5 </head> 6 <body> 7 8 <script type="module"> 9 10 import HTMLParser from './src/core/parser/html-parser.js' 11 12 let html = ` 13 <div class="c-row search-line" data-flag="start" ontap="clickHandler"> 14 <div class="c-span9 js-start search-line-txt"> 15 {{name}}</div> 16 </div> 17 ` 18 19 function arrToObj(arr) { 20 let map = {}; 21 for(let i = 0, l = arr.length; i < l; i++) { 22 map[arr[i].name] = arr[i].value 23 } 24 return map; 25 } 26 27 //存儲所有節點 28 let nodes = []; 29 30 //記錄當前節點位置,方便定位parent節點 31 let stack = []; 32 33 HTMLParser(html, { 34 /* 35 unary: 是不是自閉和標簽比如 <br/> input 36 attrs為屬性的數組 37 */ 38 start: function( tag, attrs, unary ) { //標簽開始 39 /* 40 stack記錄的父節點,如果節點長度大于1,一定具有父節點 41 */ 42 let parent = stack.length ? stack[stack.length - 1] : null; 43 44 //最終形成的node對象 45 let node = { 46 //1標簽, 2需要解析的表達式, 3 純文本 47 type: 1, 48 tag: tag, 49 attrs: arrToObj(attrs), 50 parent: parent, 51 //關鍵屬性 52 children: [], 53 text: null 54 }; 55 56 //如果存在父節點,也標志下這個屬于其子節點 57 if(parent) { 58 parent.children.push(node); 59 } 60 //還需要處理<br/> <input>這種非閉合標簽 61 //... 62 63 //進入節點堆棧,當遇到彈出標簽時候彈出 64 stack.push(node) 65 nodes.push(node); 66 67 debugger; 68 }, 69 end: function( tag ) { //標簽結束 70 //彈出當前子節點,根節點一定是最后彈出去的,兄弟節點之間會按順序彈出,其父節點在最后一個子節點彈出后會被彈出 71 stack.pop(); 72 debugger; 73 }, 74 chars: function( text ) { //文本 75 //如果是空格之類的不予處理 76 if(text.trim() === '') return; 77 let node = nodes[nodes.length - 1]; 78 //如果這里是表達式{{}}需要特殊處理 79 if(node) node.text = text.trim() 80 debugger; 81 } 82 }); 83 84 console.log(nodes) 85 86 </script> 87 88 </body> 89 </html>

這里輸出了我們想要的結構:

第一個節點便是跟節點,我們可以根據他遍歷整個節點,我們也可以根據數組(里面有對應的parent關系)生成我們想要的結構,可以看出借助強大的第三方工具庫可以讓我們的工作變得更加高效以及不容易出錯,如果我們自己寫上述HTMLParser會比較困難的,什么時候需要自己寫什么時候需要借助,就要看你要做那個事情有沒有現成確實可用的工具庫了,第二步我們嘗試下將這些模板標簽,與data結合轉換為真正的HTML結構

簡單的Virtual DOM TO HTML

這里需要data加入了,我們簡單實現一個MVVM的類,并且將上述Parser做成一個方法:

1 <!doctype html> 2 <html> 3 <head> 4 <title>起步</title> 5 </head> 6 <body> 7 8 <div id="app"> 9 10 </div> 11 12 <script type="module"> 13 14 import HTMLParser from './src/core/parser/html-parser.js' 15 16 let html = ` 17 <div class="c-row search-line" data-flag="start" ontap="clickHandler"> 18 <div class="c-span9 js-start search-line-txt"> 19 {{name}}</div> 20 <input type="text"> 21 <br> 22 </div> 23 ` 24 25 function arrToObj(arr) { 26 let map = {}; 27 for(let i = 0, l = arr.length; i < l; i++) { 28 map[arr[i].name] = arr[i].value 29 } 30 return map; 31 } 32 33 function htmlParser(html) { 34 35 //存儲所有節點 36 let nodes = []; 37 38 //記錄當前節點位置,方便定位parent節點 39 let stack = []; 40 41 HTMLParser(html, { 42 /* 43 unary: 是不是自閉和標簽比如 <br/> input 44 attrs為屬性的數組 45 */ 46 start: function( tag, attrs, unary ) { //標簽開始 47 /* 48 stack記錄的父節點,如果節點長度大于1,一定具有父節點 49 */ 50 let parent = stack.length ? stack[stack.length - 1] : null; 51 52 //最終形成的node對象 53 let node = { 54 //1標簽, 2需要解析的表達式, 3 純文本 55 type: 1, 56 tag: tag, 57 attrs: arrToObj(attrs), 58 parent: parent, 59 //關鍵屬性 60 children: [] 61 }; 62 63 //如果存在父節點,也標志下這個屬于其子節點 64 if(parent) { 65 parent.children.push(node); 66 } 67 //還需要處理<br/> <input>這種非閉合標簽 68 //... 69 70 //進入節點堆棧,當遇到彈出標簽時候彈出 71 stack.push(node) 72 nodes.push(node); 73 74 // debugger; 75 }, 76 end: function( tag ) { //標簽結束 77 //彈出當前子節點,根節點一定是最后彈出去的,兄弟節點之間會按順序彈出,其父節點在最后一個子節點彈出后會被彈出 78 stack.pop(); 79 80 // debugger; 81 }, 82 chars: function( text ) { //文本 83 //如果是空格之類的不予處理 84 if(text.trim() === '') return; 85 text = text.trim(); 86 87 //匹配 {{}} 拿出表達式 88 let reg = /\{\{(.*)\}\}/; 89 let node = nodes[nodes.length - 1]; 90 //如果這里是表達式{{}}需要特殊處理 91 if(!node) return; 92 93 if(reg.test(text)) { 94 node.children.push({ 95 type: 2, 96 expression: RegExp.$1, 97 text: text 98 }); 99 } else { 100 node.children.push({ 101 type: 3, 102 text: text 103 }); 104 } 105 // debugger; 106 } 107 }); 108 109 return nodes; 110 111 } 112 113 class MVVM { 114 /* 115 暫時要求必須傳入data以及el,其他事件什么的不管 116 117 */ 118 constructor(opts) { 119 120 //要求必須存在,這里不做參數校驗了 121 this.$el = typeof opts.el === 'string' ? document.getElementById(opts.el) : opts.el; 122 123 //data必須存在,其他不做要求 124 this.$data = opts.data; 125 126 //模板必須存在 127 this.$template = opts.template; 128 129 //存放解析結束的虛擬dom 130 this.$nodes = []; 131 132 //將模板解析后,轉換為一個函數 133 this.$initRender(); 134 135 //渲染之 136 this.$render(); 137 debugger; 138 } 139 140 $initRender() { 141 let template = this.$template; 142 let nodes = htmlParser(template); 143 this.$nodes = nodes; 144 } 145 146 //解析模板生成的函數,將最總html結構渲染出來 147 $render() { 148 149 let data = this.$data; 150 let root = this.$nodes[0]; 151 let parent = this._createEl(root); 152 //簡單遍歷即可 153 154 this._render(parent, root.children); 155 156 this.$el.appendChild(parent); 157 } 158 159 _createEl(node) { 160 let data = this.$data; 161 162 let el = document.createElement(node.tag || 'span'); 163 164 for (let key in node.attrs) { 165 el.setAttribute(key, node.attrs[key]) 166 } 167 168 if(node.type === 2) { 169 el.innerText = data[node.expression]; 170 } else if(node.type === 3) { 171 el.innerText = node.text; 172 } 173 174 return el; 175 } 176 _render(parent, children) { 177 let child = null; 178 for(let i = 0, len = children.length; i < len; i++) { 179 child = this._createEl(children[i]); 180 parent.append(child); 181 if(children[i].children) this._render(child, children[i].children); 182 } 183 } 184 185 186 } 187 188 189 let vm = new MVVM({ 190 el: 'app', 191 template: html, 192 data: { 193 name: '葉小釵' 194 } 195 }) 196 197 198 199 200 </script> 201 202 </body> 203 </html> View Code 1 <div class="c-row search-line" data-flag="start" ontap="clickHandler">
<
div class="c-span9 js-start search-line-txt"><span>葉小釵</span></div>
<
input type="text">
</
div>

這個代碼非常簡陋,只是對text部分做了處理,沒有對屬性,style等做處理,但是越是功能簡單的代碼理解起來越容易,后續的style以及屬性大同小異,我們這里開始處理,介于篇幅,下次繼續

總結

以上是生活随笔為你收集整理的一套代码小程序WebNative运行的探索02的全部內容,希望文章能夠幫你解決所遇到的問題。

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

主站蜘蛛池模板: 99精彩视频| 日日麻批免费视频播放 | 日日夜夜狠狠干 | 色婷婷一区二区三区四区 | 午夜试看120秒 | 欧美极品第一页 | 午夜aaa片一区二区专区 | 国产xxx视频| 久久6精品 | 亚洲综合在线一区 | 亚洲欧美日韩国产一区 | 26uuu成人网 国产精品久久久久久久久久直播 | 永久免费的网站入口 | 黄色午夜 | 久久久久国产精 | 操老女人视频 | 亚洲av综合色区无码一二三区 | 先锋影音中文字幕 | 国产黄色影院 | 国产中文字幕免费 | 99re国产| 精品国产乱码久久久久久久软件 | 欧美韩国日本在线 | 欧美视频一区二区三区在线观看 | gay男互凵gay男同偷精 | 欧美视频一区二区三区 | 久久99免费视频 | 九九热精彩视频 | 巨大胸大乳奶电影 | 精品久久精品 | 国产乱子伦精品视频 | 国产成人三级在线观看 | 日韩一卡二卡三卡 | 黄色av电影在线 | 69av视频在线| 欧美一区二区三区在线 | 久久性感美女视频 | 成人午夜淫片100集 伊人久久国产 | 视频在线看 | 中文字幕精品在线视频 | h网站在线播放 | 美女又爽又黄视频毛茸茸 | 日韩av电影一区 | 国产免费一区二区三区免费视频 | 波多野结衣一区二区三区高清 | 成年人国产视频 | 在线观看国产欧美 | 国产精品一品二品 | 影音先锋中文字幕在线播放 | 国产精品亚洲第一 | 伊人操| 国内精品人妻无码久久久影院蜜桃 | 无码人妻丰满熟妇区毛片18 | 精品不卡一区二区三区 | √8天堂资源地址中文在线 欧美精品在线一区二区 | 国产精品视频久久久 | 在线不卡av | 国产成人免费在线 | 毛片9 | 欧美性极品少妇xxxx | 一级片黄色片 | 成人三级晚上看 | 尤物av无码色av无码 | 色片在线免费观看 | 天堂资源在线播放 | av手机在线观看 | av最新| 午夜激情电影在线观看 | 午夜激情视频在线播放 | 国内外免费激情视频 | 亚洲欧美变态另类丝袜第一区 | 日韩精品第一 | 欧美资源在线观看 | 国产精品69久久久久 | 性歌舞团一区二区三区视频 | 日韩在线视频网址 | 99久久婷婷国产一区二区三区 | 丰满秘书被猛烈进入高清播放在 | 老熟女毛茸茸 | 国产精品无码av无码 | 人妖交videohd另类 | 精品久久一区二区 | 成人听书哪个软件好 | 鲁一鲁av| 国产亚洲性欧美日韩在线观看软件 | 欧美在线视频免费播放 | 91成人一区 | 欧美成人激情在线 | 天天躁日日躁狠狠躁av麻豆男男 | 天天操天天操天天操 | 九月婷婷| 手机在线免费观看av | 韩国美女福利视频 | 美女网站在线 | 男人的天堂免费 | 免费手机av | 高清毛片aaaaaaaaa郊外 | 中日韩欧美在线观看 | 无码人妻精品一区二区三区99不卡 |