- 原型的实际应用
- 原型如何实现它的扩展性
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
协议,更安全总结