- 原型的實際應用
- 原型如何實現它的擴展性
jquery和zepto的簡單使用
<p>jquery test1</p> <p>jquery test2</p> <p>jquery test3</p> <div id="div1"> <p>jquery test in div</p> </div>
var $p = $("p") $p.css('font-size','40px') //css是原型方法 console.log($p.html()) //html是原型方法 var $div1 = $("#div1") $div1.css('color','blue') //css是原型方法 console.log($div1.html()) //html是原型方法
//空對象 var zepto = {} zepto.init = function(){ //源碼中,這裏的處理狀況比較複雜。但由於是本次只是針對原型,所以這裏就弱化了 var slice = Array.prototype.slice var dom = slice.call(document.querySelectorAll(selector)) return zepto.Z(dom,selector) } //即便用zepto時候的$ var $ = function(selector){ return zepto.init(selector) }
//這就是構造函數 function Z(dom, selector) { var i, len = dom ? dom.length : 0 for (i = 0; i < len; i++) { this[i] = dom[i] } this.length = len this.selector = selector || '' } zepto.Z = function(dom, selector) { return new Z(dom, selector) }
$.fn = { constructor:zepto.Z, css:function(key,value){}, html:function(value){} } zepto.Z.prototype = Z.prototype = $.fn
簡單的zepto實現
myZepto.js實現javascript
(function(window) { var zepto = {}; function Z(dom, selector) { var i, len = dom ? dom.length : 0; for (i = 0; i < len; i++) { this[i] = dom[i]; } this.length = len; this.selector = selector || ''; } zepto.Z = function(dom, selector) { return new Z(dom, selector); } zepto.init = function(selector) { var slice = Array.prototype.slice; var dom = slice.call(document.querySelectorAll(selector)); return zepto.Z(dom, selector); } var $ = function(selector) { return zepto.init(selector); } window.$ = $; $.fn = { css: function(key, value) { console.log(key, value); }, html: function() { return "html"; } } Z.prototype = $.fn })(window)
var jQuery = function(selector){ //注意new關鍵字,第一步就找到了構造函數 return new jQuery.fn.init(selector); } //定義構造函數 var init = jQuery.fn.init = function(selector){ var slice = Array.prototype.slice; var dom = slice.call(document.querySelectorAll(selector)); var i,len=dom?dom.length:0; for(i = 0;i<len;i++) this[i] = dom[i]; this.length = len; this.selector = selector || ''; } //初始化jQuery.fn jQuery.fn = jQuery.prototype = { constructor: jQuery, css: function(key, value) {}, html: function(value) {} } //定義原型 init.prototype = jQuery.fn
如何體現原型的擴展性
zepto
和jquery
原型的使用
爲何要把原型放在
$.fn
上
init.prototype = jQuery.fn;
css
zepto.Z.prototype = Z.prototype = $.fn
html
由於要擴展插件前端
//作一個簡單的插件 $.fn.getNodeName = function(){ return this[0].nodeName }
好處
$
會暴露在window
全局變量上$.fn.xxx
這一接口,方便使用爲何js只有一個線程:避免DOM渲染衝突
- 事件輪詢,JS實現異步的具體解決方案
- 同步代碼,直接執行(主線程)
- 異步函數先放在異步隊列中(任務隊列)
- 待同步函數執行完畢,輪詢執行異步隊列的函數
setTimeout(function(){ console.log(1); },100); //100ms以後才放入異步隊列中,目前異步隊列是空的 setTimeout(function(){ console.log(2); //直接放入異步隊列 }) console.log(3) //直接執行 //執行3以後,異步隊列中只有2,把2拿到主線程執行 //2執行完以後,異步隊列中並無任務,因此一直輪詢異步隊列 //直到100ms以後1放入異步隊列,將1拿到主線程中執行
$.ajax({ url:'./data.json', success:function(){ //網絡請求成功就把success放入異步隊列 console.log('a'); } }) setTimeout(function(){ console.log('b') },100) setTimeout(function(){ console.log('c'); }) console.log('d') //打印結果: //d //d //c //c //a //b //b //a //真實環境不會出現dacb
解決方案存在的問題
- jQuery1.5的變化
- 使用jQuery Deferred
- 初步引入Promise概念
jQuery1.5以前
var ajax = $.ajax({ url:'./data.json', success:function(){ console.log('success1'); console.log('success2'); console.log('success3'); }, error:function(){ console.log('error'); } }) console.log(ajax); //返回一個XHR對象
jQuery1.5以後
第一種寫法:vue
var ajax = $.ajax('./data.json'); ajax.done(function(){ console.log('success1') }) .fai(function(){ console.log('fail') }) .done(function(){ console.log('success2'); }) console.log(ajax); //deferred對象
第二種寫法:java
var ajax = $.ajax('./data.json'); ajax.then(function(){ console.log('success1') },function(){ console.log('error1'); }) .then(function(){ console.log('success2'); },function(){ console.log('error'); })
- 沒法改變JS異步和單線程的本質
- 只能從寫法上杜絕callback這種形式
- 它是一種語法糖,可是解耦了代碼
- 很好的提現:開放封閉原則(對擴展開放對修改封閉)
使用jQuery Deferred
給出一段很是簡單的代碼,使用setTimeout函數node
var wait = function(){ var task = function(){ console.log('執行完成'); } setTimeout(task,2000) } wait();
新增需求:要在執行完成以後進行某些特別複雜的操做,代碼可能會不少,並且分好幾步jquery
function waitHandle(){ var dtd = $.Deferred(); //建立一個deferred對象 var wait = function(dtd){ // 要求傳入一個deferred對象 var task = function(){ console.log("執行完成"); dtd.resolve(); //表示異步任務已完成 //dtd.reject() // 表示異步任務失敗或者出錯 }; setTimeout(task,2000); return dtd; } //注意,這裏已經要有返回值 return wait(dtd); } //使用 var w = waithandle() w.then(function(){ console.log('ok1'); },function(){ console.log('err2'); }) .then(function(){ console.log('ok2'); },function(){ console.log('err2'); }) //還有w.wait w.fail
總結:dtd
的API
可分紅兩類,用意不一樣
dtd.resolve
、 dtd.reject
dtd.then
、dtd.done
、dtd.fail
能夠在上面代碼中最後執行dtd.reject()
試一下後果linux
使用
dtd.promise()
function waitHandle(){ var dtd = $.Deferred(); var wait = function(){ var task = function(){ console.log('執行完成'); dtd.resolve(); } setTimeout(task,2000) return dtd.promise(); //注意這裏返回的是promise,而不是直接返回deferred對象 } return wait(dtd) } var w = waitHandle(); //promise對象 $.when(w).then(function(){ console.log('ok1'); },function(){ console.log('err1'); }) //w.reject() //w.reject is not a function
監聽式調用:只能被動監聽,不能干預promise的成功和失敗git
- 能夠jQuery1.5對ajax的改變舉例
- 說明如何簡單的封裝、使用deferred
- 說明promise和Defrred的區別
要想深刻了解它,就須要知道它的前世此生
基本語法回顧
fucntion loadImg() { var promise = new Promise(function(resolve,reject) { var img = document.getElementById("img") img.onload = function(){ resolve(img) } img.onerror = function(){ reject() } }) return promise } var src = "" var result = loadImg(src) result.then(function() { console.log(1, img.width) return img }, fucntion() { console.log('error 1') }).then(function(img) { console.log(1, img.width) })
異常捕獲
規定:then
只接受一個函數,最後統一用catch
捕獲異常
var src = "" var result = loadImg(src) result.then(function() { console.log(1, img.width) return img }).then(function(img) { console.log(1, img.width) }).catch(function(ex) { //最後統一捕獲異常 console.log(ex) })
多個串聯
var scr1 = 'https://www.imooc.com/static/img/index/logo_new.png'; var result1 = loadImg(src1); var src2 = 'https://www.imooc.com/static/img/index/logo_new1.png'; var result2 = loadImg(src2); result1.then(function(img1) { console.log('第一個圖片加載完成', img1.width); return result2; //重要 }).then(function(img2) { console.log('第二個圖片加載完成', img2.width); }).catch(function(ex) { console.log(ex); })
Promise.all
和Promise.race
Promise.all
接收一個promise
對象的數組- 待所有完成後,統一執行
success
Promise.all([result1, result2]).then(datas => { //接收到的datas是一個數組,依次包含了多個promise返回的內容 console.log(datas[0]); console.log(datas[1]); })
Promise.race
接收一個包含多個promise
對象的數組- 只要有一個完成,就執行
success
Promise.race([result1, result2]).then(data => { //data即最早執行完成的promise的返回值 console.log(data); })
Promise標準
狀態
- 三種狀態:
pending
,fulfilled
,rejected
- 初始狀態:
pending
pending
變爲fulfilled
,或者pending
變爲rejected
- 狀態變化不可逆
then
promise
必須實現then
這個方法then()
必須接收兩個函數做爲標準then
返回的必須是一個promise
實例
沒有return
就是默認返回的result
Error
和reject
)Promise.all
和Promise.race
Promise
標準 - 狀態變化、then
函數es7提案中,用的比較多,babel也已支持
koa框架 generator替換async/await
解決的是異步執行和編寫邏輯
then
只是將callback
拆分了
var w = watiHandle() w.then(function(){...},function(){...}) .then(function(){...},function(){...})
async/await
是最直接的同步寫法
const load = async function(){ const result1 = await loadImg(src1) console.log(result1) const result2 = await loadImg(src2) console.log(result2) } load()
用法
- 使用
await
,函數後面必須用async
標識await
後面跟的是一個Promise
實例- 須要
balbel-polyfill
(兼容)特色
Promise
,並無和Promise
衝突JS
單線程、異步的本質什麼是vdom
DOM
<ul id="list"> <li class="item">Item 1</li> <li class="item">Item 2</li> </ul>
虛擬DOM
{ tag: 'ul', attrs: { id: 'list' }, children: [{ tag: 'li', attrs: { className: 'item' }, children: ['item1'] }, { tag: 'li', attrs: { className: 'item' }, children: ['item2'] } ] } //className代替class,由於class是js的保留字
瀏覽器最耗費性能就是DOM操做,DOM操做很是昂貴
如今瀏覽器執行JS速度很是快
這就是vdom存在的緣由
一個需求場景
//將該數據展現成一個表格 //隨便修改一個信息,表格也隨着修改 [ { name: 'zhangsan', age: 20, address: 'beijing' }, { name: 'lisi', age: 21, address: 'shanghai' }, { name: 'wangwu', age: 22, address: 'guangzhou' } ]
用jQery
實現
//渲染函數 funciton render(data) { //此處省略n行 } //修改信息 $('#btn-change').click(function(){ data[1].age = 30; data[2].address = 'shenzhen'; render(data); }) //初始化時渲染 render(data)
//render函數具體寫法 function render(data) { $container = $("#container"); //清空現有內容 $container.html(''); //拼接table var $table = $('<table>') $table.append($('<tr><td>name</td><td>age</td><td>address</td></tr>')) data.forEach(item => { $table.append($('<tr><td>' + item.name + '</td><td>' + item.age + '</td><td>' + item.address + '</td></tr>')) $container.append($table) }); } //只執行了一次渲染,相對來講仍是比較高效的 //DOM渲染是最昂貴的,只能儘可能避免渲染
遇到的問題
var div = document.createElement('div'); var item,result = ''; for(item in div){ result += '|' + item; } console.log(result); //瀏覽器默認建立出來的DOM節點,屬性是很是多的 //result |align|title|lang|translate|dir|dataset|hidden|tabIndex|accessKey|draggable|spellcheck|autocapitalize|contentEditable|isContentEditable|inputMode|offsetParent|offsetTop|offsetLeft|offsetWidth|offsetHeight|style|innerText|outerText|onabort|onblur|oncancel|oncanplay|oncanplaythrough|onchange|onclick|onclose|oncontextmenu|oncuechange|ondblclick|ondrag|ondragend|ondragenter|ondragleave|ondragover|ondragstart|ondrop|ondurationchange|onemptied|onended|onerror|onfocus|oninput|oninvalid|onkeydown|onkeypress|onkeyup|onload|onloadeddata|onloadedmetadata|onloadstart|onmousedown|onmouseenter|onmouseleave|onmousemove|onmouseout|onmouseover|onmouseup|onmousewheel|onpause|onplay|onplaying|onprogress|onratechange|onreset|onresize|onscroll|onseeked|onseeking|onselect|onstalled|onsubmit|onsuspend|ontimeupdate|ontoggle|onvolumechange|onwaiting|onwheel|onauxclick|ongotpointercapture|onlostpointercapture|onpointerdown|onpointermove|onpointerup|onpointercancel|onpointerover|onpointerout|onpointerenter|onpointerleave|nonce|click|focus|blur|namespaceURI|prefix|localName|tagName|id|className|classList|slot|attributes|shadowRoot|assignedSlot|innerHTML|outerHTML|scrollTop|scrollLeft|scrollWidth|scrollHeight|clientTop|clientLeft|clientWidth|clientHeight|attributeStyleMap|onbeforecopy|onbeforecut|onbeforepaste|oncopy|oncut|onpaste|onsearch|onselectstart|previousElementSibling|nextElementSibling|children|firstElementChild|lastElementChild|childElementCount|onwebkitfullscreenchange|onwebkitfullscreenerror|setPointerCapture|releasePointerCapture|hasPointerCapture|hasAttributes|getAttributeNames|getAttribute|getAttributeNS|setAttribute|setAttributeNS|removeAttribute|removeAttributeNS|hasAttribute|hasAttributeNS|toggleAttribute|getAttributeNode|getAttributeNodeNS|setAttributeNode|setAttributeNodeNS|removeAttributeNode|closest|matches|webkitMatchesSelector|attachShadow|getElementsByTagName|getElementsByTagNameNS|getElementsByClassName|insertAdjacentElement|insertAdjacentText|insertAdjacentHTML|requestPointerLock|getClientRects|getBoundingClientRect|scrollIntoView|scrollIntoViewIfNeeded|animate|computedStyleMap|before|after|replaceWith|remove|prepend|append|querySelector|querySelectorAll|webkitRequestFullScreen|webkitRequestFullscreen|scroll|scrollTo|scrollBy|createShadowRoot|getDestinationInsertionPoints|ELEMENT_NODE|ATTRIBUTE_NODE|TEXT_NODE|CDATA_SECTION_NODE|ENTITY_REFERENCE_NODE|ENTITY_NODE|PROCESSING_INSTRUCTION_NODE|COMMENT_NODE|DOCUMENT_NODE|DOCUMENT_TYPE_NODE|DOCUMENT_FRAGMENT_NODE|NOTATION_NODE|DOCUMENT_POSITION_DISCONNECTED|DOCUMENT_POSITION_PRECEDING|DOCUMENT_POSITION_FOLLOWING|DOCUMENT_POSITION_CONTAINS|DOCUMENT_POSITION_CONTAINED_BY|DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC|nodeType|nodeName|baseURI|isConnected|ownerDocument|parentNode|parentElement|childNodes|firstChild|lastChild|previousSibling|nextSibling|nodeValue|textContent|hasChildNodes|getRootNode|normalize|cloneNode|isEqualNode|isSameNode|compareDocumentPosition|contains|lookupPrefix|lookupNamespaceURI|isDefaultNamespace|insertBefore|appendChild|replaceChild|removeChild|addEventListener|removeEventListener|dispatchEvent
介紹snabbdom
一個實現vdom
的庫,vue升級2.0借鑑了snabbdom
的算法
var 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: anotherEventHandle } }, [ 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') ]) //send `patch` invocation patch(vnode, newVnode); //Snabbdom efficiently updates the old view to the new state
h函數
{ tar: 'ul', attrs: { id: 'list' }, children: [ { tag: 'li', attrs: { className: 'item', children: ['item1'] } }, { tag: 'li', attrs: { className: 'item' }, children: ['item2'] } ] }
對應的vnode
var vnode = h('ul#list', {}, [ h('li.item', {}, 'Item1'), h('li.item', {}, 'Item') ])
patch函數
var vnode = h('ul#list', {}, [ h('li.item', {}, 'Item1'), h('li.item', {}, 'Item2') ]) var container = document.getElementById('container') patch(container, vnode) //模擬改變 var btnChange = document.getElementById('btn-change') btnChange.addEventListener('click', function() { var newVnode = h('ul#list', {}, [ h('li.item', {}, 'Item 111'), h('li.item', {}, 'Item 222'), h('li.item', {}, 'Item 333') ]) patch(vnode, newVnode) })
<div id="container"></div> <button id="btn-change">change</button> <script src="https://cdn.bootcss.com/snabbdom/0.7.2/snabbdom.js"></script> <script src="https://cdn.bootcss.com/snabbdom/0.7.2/snabbdom-class.js"></script> <script src="https://cdn.bootcss.com/snabbdom/0.7.2/snabbdom-props.js"></script> <script src="https://cdn.bootcss.com/snabbdom/0.7.2/snabbdom-style.js"></script> <script src="https://cdn.bootcss.com/snabbdom/0.7.2/snabbdom-eventlisteners.js"></script> <script src="https://cdn.bootcss.com/snabbdom/0.7.2/h.js"></script>
<script type="text/javascript"> var snabbdom = window.snabbdom //定義patch var patch = snabbdom.init([ snabbdom_class, snabbdom_props, snabbdom_style, snabbdom_eventlisteners ]) //定義h函數 var h = snabbdom.h var container = document.getElementById('container') //生成vnode var vnode = h('ul#list',{},[ h('li.list',{},'Item1'), h('li.list',{},'Item2'), ]) patch(container,vnode) document.getElementById('btn-change').addEventListener('click',function(){ //生成newVode var newVnode = h('ul#list',{},[ h('li.list',{},'Item1'), h('li.item',{},'Item B'), h('li.item',{},'Item 3') ]) patch(vnode,newVnode) }) </script>
從新實現前面的demo(用snabbdom
實現)
var snabbdom = window.snabbdom var patch = snabbdom.init([ snabbdom_class, snabbdom_props, snabbdom_style, snabbdom_eventlisteners ]) var h = snabbdom.h var container = document.getElementById('container') var btnChange = document.getElementById('btn-change') var vnode var data = [{ name: 'zhangsan', age: 20, address: 'beijing' }, { name: 'zhangsan', age: 20, address: 'shanghai' }, { name: 'zhangsan', age: 20, address: 'shenzhen' } ] data.unshift({ name:'姓名:', age:'年齡:', address:'地址:' }) render(data); function render(data){ var newVnode = h('table',{},data.map(function(item){ var tds = [],i for(i in item){ if(item.hasOwnProperty(i)){ tds.push(h('td',{},item[i]+'')) } } return h('tr',{},tds) })) if(vnode){ patch(vnode,newVnode) }else{ patch(container,newVnode) } vnode = newVnode //存儲當前vnode結果 } btnChange.addEventListener('click',function(){ data[1].age = 30 data[2].address = '深圳' //re-render render(data) })
核心API
h('<標籤名>',{...屬性...},[...子元素...]) h('<標籤名>',{...屬性...},'...')
patch(container,vnode) patch(vnode,newVnode) //rerender
什麼是diff算法
linux
中的diff:找出兩個文件中的不一樣:
diff log1.txt log2.txt
git diff
:修改以前和修改以後版本的差別
git diff xxxx.js
網上的一些在線差別化網站
http://tool.oschina.net/diff/
diff
算法並非vdom
提出的概念,一直存在
如今應用到vdom
中,對比的是兩個虛擬dom
去繁就簡
diff
算法很是複雜,實現難度很大,源碼量很大
vdom爲什麼要使用
diff
diff
算法diff
算法實現
diff
實現的過程
如何用vnode生成真實的dom節點
{ tag: 'ul', attrs: { id: 'list' }, children: [ { tag: 'li', attrs: { className: 'item' }, children:['item 1'] } ] }
<ul id = "list"> <li class='item'>Item 1</li> </ul>
簡單實現算法
function createElement(vnode) { var tag = vnode.tag; var attrs = vnode.attrs || {}; var children = vnode.children || []; if (!tag) { return null } //建立元素 var elem = document.createElement(tag); //屬性 var attrName; for (attrName in atts) { if (attrs.hasOwnProperty(attrName)) { elem.setAttribute(attrName, attrs[attrName]) } } //子元素 children.array.forEach(childVnode => { elem.appendChild(createElement(childVnode)) }); return elem; }
{ tag: 'ul', attrs: { id: 'list' }, children: [{ tag: 'li', attrs: { className: 'item' }, children: ["Item 1"] }, { tag: 'li', attrs: { className: 'item', children: ['Item 2'] } } ] }
對比:
{ tag: 'ul', attrs: { id: 'list' }, children: [{ tag: 'li', attrs: { className: 'item' }, children: ["Item 1"] }, { tag: 'li', attrs: { className: 'item', children: ['Item 222'] } }, { tag: 'li', attrs: { className: 'item', children: ['Item 3'] } } ] }
簡單實現
function updateChildren(vnode, newVnode) { var children = vnode.children || []; var newChildren = newVnode.children || []; //遍歷現有的children children.forEach((child, index) => { var newChild = newChildren[index]; if (newChild == null) { return; } if (child.tag === newChild.tag) { updateChildren(child, newChild) } else { replaceNode(child, newChild) } }); }
數據與視圖的分離,解耦(封閉開放原則)
jquery
中數據與視圖混合在一塊兒了,不符合開放封閉原則vue
:經過Vue對象將數據和View徹底分離開來了以數據驅動視圖
jquery
徹底違背了這種理念,jquery
直接修改視圖,直接操做DOM
vue
對數據進行操做再也不須要引用相應的DOM對象,經過Vue對象這個vm實現相互的綁定。以數據驅動視圖,只關心數據變化,DOM操做被封裝
MVC
MVVM
MVVM不算是創新,ViewModel
算是一種微創新
是從mvc發展而來,結合前端場景的創新
Vue三要素
vue
如何監聽到data
的每一個屬性變化:
vue的模板如何渲染成
html`,以及渲染過程什麼是響應式
data
屬性以後,vue
馬上監聽到(而後馬上渲染頁面)data
屬性被代理到vm上
Object.defineProperty
ES5中加入的API,因此Vue不支持低版本瀏覽器(由於低版本瀏覽器不支持這個屬性)
var obj = {}; var _name = 'zhangsan'; Object.defineProperty(obj,"_name",{ get:function(){ console.log("get"); return _name; }, set:function(newVal){ console.log(newVal); _name = newVal; } }); console.log(obj.name) //能夠監聽到 obj.name = 'list'
模擬實現
var vm = new Vue({ el: '#app', data: { price: 100, name: 'zhangsan' } })
var vm = {} var data = { name: 'zhangsan', price: 100 } var key, value for (key in data) { //命中閉包。新建一個函數,保證key的獨立的做用域 (function(key) { Object.defineProperty(vm, key, { get: function() { console.log('get') return data[key] }, set: function(newVal) { console.log('set') data[key] = newVal } }) })(key) }
模板是什麼
模板最終必須轉換成JS代碼
先了解with()的使用
function fn1() { with(obj) { console.log(name); console.log(age); getAddress() } }
最簡單的一個示例
<div id="app"> <p>{{price}}</p> </div>
with(this) { //this:vm return _c( 'div', { attrs: { "id": "app" } }, [ _c('p',[_v(_s(price))] ) //price代理到了vm上 ] ) }
//vm._c ƒ (a, b, c, d) { return createElement(vm, a, b, c, d, false); } //vm._v ƒ createTextVNode (val) { return new VNode(undefined, undefined, undefined, String(val)) } //vm._s ƒ toString (val) { return val == null? '': typeof val === 'object'? JSON.stringify(val, null,2): String(val) }
this
即vm
price
即this.price
,即data
中的price
_c
即this._c
即vm._c
更復雜的一個例子
<div id="app"> <div> <input v-model="title"> <button v-on:click="add">submit</button> </div> <div> <ul> <li v-for="item in list">{{item}}</li> </ul> </div> </div>
如何尋找render函數:code.render
模板如何生成render函數:
vue2.0開始就支持預編譯,咱們在開發環境下寫模板
,通過編譯打包
,產生生產環境的render函數(JS
代碼)
with(this){ // this 就是 vm return _c( 'div', { attrs:{"id":"app"} }, [ _c( 'div', [ _c( 'input', { directives:[ { name:"model", rawName:"v-model", value:(title), expression:"title" } ], domProps:{ "value":(title) }, on:{ "input":function($event){ if($event.target.composing)return; title=$event.target.value } } } ), _v(" "), _c( 'button', { on:{ "click":add } }, [_v("submit")] ) ] ), _v(" "), _c('div', [ _c( 'ul', _l((list),function(item){return _c('li',[_v(_s(item))])}) ) ] ) ] ) }
//vm._l function renderList(val,render) { var ret, i, l, keys, key; if (Array.isArray(val) || typeof val === 'string') { ret = new Array(val.length); for (i = 0, l = val.length; i < l; i++) { ret[i] = render(val[i], i); } } else if (typeof val === 'number') { ret = new Array(val); for (i = 0; i < val; i++) { ret[i] = render(i + 1, i); } } else if (isObject(val)) { keys = Object.keys(val); ret = new Array(keys.length); for (i = 0, l = keys.length; i < l; i++) { key = keys[i]; ret[i] = render(val[key], key, i); } } if (isDef(ret)) { (ret)._isVList = true; } return ret }
render函數與DOM
v-for
,v-if
)的問題另外,vm_c是什麼?render
函數返回了什麼
snabbdom
中的h
函數render
函數執行以後,返回的是vnode
vm._update(vnode) { const prevVnode = vm._vnode vm._vnode = vnode if (!prevVnode) { vm.$sel = vm.__patch__(vm.$sel, vnode) //與snabbdom中的patch函數差很少 } else { vm.$sel = vm.__patch__(prevVnode, vnode) } } funciton updateComponent() { //vm._render即上面的render函數,返回vnode vm._update(vm._render()) }
updateComponent
中實現了vdom
的patch
updateComponent
data
中每次修改屬性,執行updataCommponent
第一步:解析模板成
render
函數
render
函數包含data
中的屬性,都變成了js變量v-model
、v-if
、v-on
都變成了js邏輯第二步:響應式監聽
Object.defineProperty
data
屬性代理到vm
上第三步:首次渲染,顯示頁面,且綁定依賴
updateaComponent
,執行vm._render()
render
函數,會訪問到vm.list
和vm.title
會被響應式的get
方法監聽到(爲何監聽get?直接監聽set不就好了嗎?)
data
中有不少屬性,有些會被用到,有些可能不會被用到get
,不被用到的不會走get
get
中的屬性,set
的時候咱們也無需關係updateComponent
,會走到vdom
的patch
方法patch
將vnode
渲染成DOM
,初次渲染完成第四步:data
屬性變化,觸發rerender
set
監聽到set
中執行updateaComponetn
updateComponent
從新執行vm._render()
vnode
和prevVnode
,經過patch
進行對比html
中// ## 組件化和React
hybrid
是什麼,爲什麼用hybrid,如何實現
hybrid
文字解釋
hybrid
即「混合」,即前端和客戶端的混合開發server
端
存在價值,爲什麼會用hybrid
webview
webview
,也能夠沒有)file://協議
其實在一開始接觸html開發,已經使用file協議了
要作到和原生同樣的體驗,就必需要求加載速度特別的快,變態的快,和客戶端幾乎同樣的快
hybrid
適用場景hybrid
具體實現分析
具體實現
hybrid
和h5
區別優勢:
缺點:
適用場景:
hybrid
:產品的穩定功能,體驗要求高,迭代頻繁h5
:單次的運營活動(如xx紅包)或不經常使用功能hybrid
適合產品型,h5
適合運營型前端如何獲取內容
hybrid
,前端如何獲取新聞內容ajax
是http
協議),第二速度慢。JS和客戶端通信的基本形式
![]()
function invokScan() { window['_invok_scan_callback'] = function(result){ alert(result) } var iframe = document.createElement('iframe') iframe.style.display = 'none' iframe.src = 'weixin://dl/scan?k1=v1&k1=v2&callback=_invok_scan_callback' //重要 var body = document.body body.appendChild(iframe) setTimeout(() => { body.removeChild(iframe) iframe = none }); } document.getElementById('btn').addEventListener('click', function() { invokScan(); })
咱們但願封裝後達到的效果:
/*傻瓜式調用,並且不用再本身定義全局函數 */ window.invok.share({title:'xxxx',content:'xxx'},funciton(result){ if(result.error === 0){ alert('分享成功') }else{ //分享失敗 alert(result.message) } })
//分享 function invokeShare(data,callback){ _invoke('share',data,callback) } //登陸 function invokeLogin(data,callback){ _invoke('login',data,callback) } //打開掃一掃 function invokeScan(data,callback){ _invoke('scan',data,callback) }
//暴露給全局 window.invoke = { share:invokeShare, login:invokeLogin, scan:invokeScan }
function _invoke(action,data,callback){ //拼接schema協議 var schema = 'myapp://utils'; scheam += '/' + action; scheam += '?a=a'; var key; for(key in data){ if(data.hasOwnProperty(key)){ schema += '&' + key + '=' +data[key] } } }
//處理callback var callbackName = '' if(typeof callback == 'string'){ callbackName = callback }else{ callbackName = action + Date.now() window[callbackName] = callback } schema += '&callback' + callbackName
(function(window, undefined) { //調用schema封裝 function _invoke(action, data, callback) { //拼裝schema協議 var schema = 'myapp://utils/' + action; //拼接參數 schema += '?a=a'; var key; for (key in data) { if (data.hasOwnProperty(key)) { schema += '&' + key + '=' + data[key]; } } //處理callback var callbackName = ''; if (typeof callback === 'string') { callbackName = callback } else { callbackName = action + Date.now() window[callbackName] = callback } schema += 'allback = callbackName' } //暴露到全局變量 window.invoke = { share: function(data, callback) { _invoke('share', data, callback) }, scan: function(data, callback) { _invoke('scan', data, callback) }, login: function(data, callback) { _invoke('login', data, callback) } } })(window)
內置上線
invoke.js
,內置到客戶端webview
,都默認執行invoke.js
schema
協議,更安全總結