问题思考
- 什么是单线程,和异步什么关系
- 什么是event-loop
- 是否用过jQuery的Defferred
- Promise的基本使用和原理
- 介绍一下async/await(和promise的区别、联系)
- 总结当前JS解决异步的方案
什么是单线程,和异步的关系
- 单线程:只有一个线程,同时只能做一件事,两段js不能同时执行
- 原因:避免DOM渲染的冲突
- 解决方案:异步
event-loop (事件轮询)
- 事件轮询,JS实现异步的具体解决方案
- 同步的代码,直接执行
- 异步函数先放在异步队列中
- 待同步函数执行完毕,轮询执行异步队列的函数
jQuery 的 Deferred (延迟)
- 无法改变JS异步和单线程的本质
- 只能从写法上杜绝callback这种形式
- 他是一种语法糖形式,但是解耦了代码
- 很好的体现:开放封闭原则(对扩展开放,对修改封闭)
//jQuery1.5之前版本的ajax请求
var ajax = $.ajax({
url: './data.json',
success: function () {
console.log('success1')
console.log('success2')
console.log('success3')
},
error: function () {
console.log('fail')
}
})
console.log(ajax)//返回的是一个XHR对象
//jQuery1.5之后版本的ajax请求
var ajax = $('./data.json')
ajax.done(function () {
console.log('success a')
}).fail(function () {
console.log('error')
}).done(function () {
console.log('success b')
})
console.log(ajax) //返回一个deferred对象
//当然1.5之后还可以按以下方式书写,类似今后的promise书写方式
var ajax = $('./data.json')
ajax.then(function () {
console.log('success a')
}, function () {
console.log('error1')
}).then(function () {
console.log('success b')
}, function () {
console.log('error2')
})
如何简单封装,使用deferred
function waitHandle() {
var dtd = $.Deferred()
var wait = function (dtd) {
var task = function () {
console.log('执行完成')
dtd.resolve()
//console.log('执行失败')
//dtd.reject()
}
setTimeout(task, 2000)
return dtd.promise() //注意这里返回的是primise对象而不是deferred对象
}
return wait(dtd)
}
//使用
var w = waitHandle()
$.when(w)
.then(function () {
console.log('ok 1')
})
.then(function () {
console.log('ok 2')
})
Promie的基本使用和原理
- promise语法
function loadImg(src) {
const promise = new Promise(function (resolve, reject) {
var img = document.createElement('img')
img.onload = function () {
resolve(img)
}
img.onerror = function () {
reject()
}
img.src = src
})
return promise
}
var src = './logo.png'
var result = loadImg(src)
result.then(function (img) {
console.log(img.width)
return img
}, function () {
console.log('fail')
}).then(function (img) {
console.log(img.height)
})
-
如果在低级浏览器上不支持promise,则在之前引入bluebird.js,这样高低级浏览器都会支持promise
问题
- vdom是什么?为什么会存在
- vdom如何应用,核心API是什么
- 介绍一下diff算法
什么是vdom
- virtual dom ,虚拟DOM
- 用js模拟DOM结构
- DOM变化的对比,放在js层来做(图灵完备语言)
- 提高重绘性能
<ul id="list">
<li class="item">item1</li>
<li class="item">item2</li>
</ul>
//例子1:
//用js模拟以上DOM结构
{
tag: 'ul',
attrs: {
id: 'list'
},
children: [
{
tag: 'li',
attrs: { className: 'item' },
children: ['item1']
}, {
tag: 'li',
attrs: { className: 'item' },
children: ['item2']
}
]
}
用jQuery渲染一段数据成表格,改变数据内容,表格跟着改变
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div id="container"></div>
<button id="btn-change">change</button>
<script type="text/javascript" src="https://cdn.bootcss.com/jquery/3.4.1/jquery.js"></script>
<script type="text/javascript">
var data1 = [
{
name: '张三',
age: '20',
address: '北京'
}, {
name: '李四',
age: '21',
address: '上海'
}, {
name: '王五',
age: '24',
address: '北京'
}
]
function render(data) {
var $container = $('#container');
$("#container").html('');
var $table = $('<table>');
$table.append($('<tr><th>name</th><th>age</th><th>address</th></tr>'));
data.forEach(function (item) {
$table.append($('<tr><td>' + item.name + '</td><td>' + item.age + '</td><td>' + item.address + '</td></tr>'));
})
$container.html($table);
}
render(data1);
$("#btn-change").click(function () {
data1[0].age = 29;
data1[1].address = '湖南';
render(data1)
})
</script>
</body>
</html>
以上操作遇到的问题
- DOM操作是昂贵的,js运行效率高
- 尽量减少DOM操作,而不是推倒重来
- 项目越复杂,影响越严重
- vdom能解决此问题
vdom如何应用,核心API是什么
snabbdom
- 是可实现vdom操作的方法之一
- 俩重要函数:h函数,patch函数
//用h函数表示例子1的js树结构
var vnode = h('ul#list', {}, [
h('li.item', {}, 'item1'),
h('li.item', {}, 'item2')
])
//参数1:标签名+id+class
//参数2:标签的属性,例如{on: {clcik: someFn}} 或者 {props:{href: '/foo'}} 或者 {style: {fontWeight: 'normal', fontSize: 'italic'}} 等;
//参数3:标签里面内容
//patch函数
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', {}, 'item1'),
h('li.item', {}, 'itemB'),
h('li.item', {}, 'item3')
])
patch(vnode, newVnode)
})
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>使用snabbdom来渲染</title>
</head>
<body>
<div id="container"></div>
<button id="btn-change">change</button>
<script src="https://cdn.bootcss.com/snabbdom/0.7.3/snabbdom.js"></script>
<script src="https://cdn.bootcss.com/snabbdom/0.7.3/snabbdom-class.js"></script>
<script src="https://cdn.bootcss.com/snabbdom/0.7.3/snabbdom-props.js"></script>
<script src="https://cdn.bootcss.com/snabbdom/0.7.3/snabbdom-style.js"></script>
<script src="https://cdn.bootcss.com/snabbdom/0.7.3/snabbdom-eventlisteners.js"></script>
<script src="https://cdn.bootcss.com/snabbdom/0.7.3/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.item', {}, 'item1'),
h('li.item', {}, 'item2')
])
patch(container, vnode)
var btnChange = document.getElementById('btn-change')
btnChange.addEventListener('click', function () {
var newVnode = h('ul#list', {}, [
h('li.item', {}, 'item1'),
h('li.item', {}, 'itemB'),
h('li.item', {}, 'item3')
])
patch(vnode, newVnode)
})
</script>
</body>
</html>
介绍下diff算法
- 是linux的基础命令
- vdom中使用siff算法是为了找出需要更新的节点
- 实现:pctch(container, vnode) 和 patch(vnode, newVnode)