reorder在java中什么意思_Snabbdom 官方demo reorder-animation解说

引言

snabbdom是目前最流行的virtual dom库之一。

它的github仓库有关于它的一切。

本篇博客介绍Snabbdom的一个官方demo => reorder-animation,并解说其基本实现,以方便理解Snabbdom基本用法。

废话不多说直接上图:

202007261103384919.png

这个demo演示了snabbdom对于列表渲染、列表项增删和排序的操作。

这个demo在哪里?

517e485697af6ddf3b37e1f3c8af5eb9.png

进入snabbdom的github仓库,clone到本地。在/examples/reorder-animation目录下找到它。文件夹下script.js为demo源码,build.js为打包后代码。将index.html文件06行

代码替换为:

复制代码

使用打包工具parcel启动index.html

parcel index.html --open

复制代码

代码结构

index.html文件中指定了挂在的元素节点div#container,引入了js文件,以及页面相关的css代码。所有视图和功能的实现均位于script.js。

复制代码

1. 引入

script.js首先引入了snabbdom,并使用snabbdom.init()初始化了patch方法。init方法接受了一个Array类型的参数,注入了class、props、style和eventlisteners模块,h方法用于生成虚拟DOM(VNode)。

var snabbdom = require('../../snabbdom.js');

var patch = snabbdom.init([

require('../../modules/class').default,

require('../../modules/props').default,

require('../../modules/style').default,

require('../../modules/eventlisteners').default,

]);

var h = require('../../h.js').default;

var vnode;

复制代码

其中,patch方法把新节点中变化的内容渲染到真实DOM,返回当前的虚拟DOM对象(VNode)。patch(oldVnode, newVnode)方法接受两个参数,分别为旧节点和新VNode(也可以为DOM对象)。patch方法首先会对比新旧VNode是否位于相同的节点(节点key和sel属性相同)。以下展示源码vnode.d.ts中对VNode接口的声明:

export interface VNode {

sel: string | undefined; // 选择器

data: VNodeData | undefined;

children: Array | undefined;

elm: Node | undefined; // 元素

text: string | undefined;

key: Key | undefined;

}

复制代码

如果不是相同的节点,会删除之前的内容,重新渲染。如果是相同的节点,patch会再判断新的VNode是否text属性,如果有并且和oldVnode的text不同,就会直接将新的文本内容渲染至页面,如果新的VNode有children,则会通过diff算法判断子节点是否变化,diff的过程只进行同层级比较。具体细节可以参考这篇博客=>vue的Virtual Dom实现- snabbdom解密。

2. 渲染操作

在window的DOMContentLoaded事件发生时,执行初次的渲染操作:

var vnode; // 当前patch返回的VNode

var data = [ // 列表数据

{

rank: 1,

title: 'The Shawshank Redemption',

desc:

'Two imprisoned men bond over a number of years, finding solace and eventual redemption through acts of common decency.',

elmHeight: 0, // 元素offsetHeight

},

...

]

window.addEventListener('DOMContentLoaded', () => {

var container = document.getElementById('container');

// 执行patch方法,此时列表高度为0

vnode = patch(container, view(data));

// 执行render函数后,列表拥有高度

render(); // 渲染函数,本demo核心部分

});

复制代码

这一段代码包含了demo核心部分:render()方法和view()方法。这里或许会问道:为什么执行了patch后再一次执行了render?首先,第一次patch将data通过view(data)方法渲染到页面,view方法我倾向于理解为vue组件的render()方法,具体实现如下:

var totalHeight = 0; // 列表高度

function view(data) {

return h('div', [

h('h1', 'Top 10 movies'),

h('div', [

h('a.btn.add', {on: {click: add}}, 'Add'),

'Sort by: ',

h('span.btn-group', [

h('a.btn.rank', {class: {active: sortBy === 'rank'}, on: {click: [changeSort, 'rank']}}, 'Rank'),

h('a.btn.title', {class: {active: sortBy === 'title'}, on: {click: [changeSort, 'title']}}, 'Title'),

h('a.btn.desc', {class: {active: sortBy === 'desc'}, on: {click: [changeSort, 'desc']}}, 'Description'),

]),

]),

h('div.list', {style: {height: totalHeight+'px'}}, data.map(movieView)),

]);

}

function movieView(movie) {

return h('div.row', {

key: movie.rank, // 该DOM元素的唯一标识,相当于Vue中的key

style: {opacity: '0', transform: 'translate(-200px)',

delayed: {transform: `translateY(${movie.offset}px)`, opacity: '1'},

remove: {opacity: '0', transform: `translateY(${movie.offset}px) translateX(200px)`}},

hook: {insert: (vnode) => { movie.elmHeight = vnode.elm.offsetHeight; }},

}, [

h('div', {style: {fontWeight: 'bold'}}, movie.rank),

h('div', movie.title),

h('div', movie.desc),

h('div.btn.rm-btn', {on: {click: [remove, movie]}}, 'x'),

]);

}

复制代码

列表DOM元素通过data.map方法获得,具体实现在movieView中,h()方法第一个参数接受一个选择器字符串,第二个参数接受init()方法注入的属性,第三个参数接受字符串作为text或数组作为子元素。

style属性除了指定相关样式外,还可以指定delayed和remove属性,分别指定了该元素在下一frame和销毁时的样式,配合transition和transform可以实现炫酷的动画效果。这里需要指出,key属性对于实现demo动画效果至关重要,不信邪的小伙伴可以注掉key试试:)。

key和hook属性依赖于注入init中的props模块。key为元素的唯一标识,类似于Vue中的:key。hook为该元素的声明周期钩子,demo中使用的insert钩子在该元素插入到DOM并完成patch后触发。demo用来获取每个列表项目元素的元素高度offsetHeight,并将这个高度赋值到每个movie对象(就是要渲染的那个item)中。(其他的钩子建议去文档观摩观摩)。

以上属性皆可在snabbdom文档中ctrl+f细细品味。

h方法源码的声明代码如下,采用了函数重载:

import { VNode, VNodeData } from './vnode';

export declare type VNodes = Array;

export declare type VNodeChildElement = VNode | string | number | undefined | null;

export declare type ArrayOrElement = T | T[];

export declare type VNodeChildren = ArrayOrElement;

export declare function h(sel: string): VNode;

export declare function h(sel: string, data: VNodeData): VNode;

export declare function h(sel: string, children: VNodeChildren): VNode;

export declare function h(sel: string, data: VNodeData, children: VNodeChildren): VNode;

export default h;

复制代码

执行完上述代码后,此时页面中列表高度为0,不过一轮操作下来已经拿到了所有元素的高度参数。接下来执行render()方法展开列表,并且带上了流畅的css3动画。

function render() {

data = data.reduce((acc, m) => {

// debugger;

var last = acc[acc.length - 1];

// debugger;

m.offset = last ? last.offset + last.elmHeight + margin : margin;

return acc.concat(m);

}, []);

totalHeight = data[data.length - 1].offset + data[data.length - 1].elmHeight;

vnode = patch(vnode, view(data)); // 更新视图

}

复制代码

render()方法通过reduce()累加遍历为每个列表项设置了显示高度offset,使其位于每个last元素的高度之下(demo中列表项使用绝对定位将所有列表项定位在顶部,列表项排列通过translateY实现,见movieView方法style属性)。之后,render()拿到了totalHeight,也就是整个列表的高度。最后,在完成reduce循环后触发patch方法更新视图。

3. 排序操作

以上讲完了本demo最核心的部分,以下是demo中的排序方法,没有什么难点和重点,不过我挺喜欢这段代码的风格,贴出来就不一一介绍啦。不过有一点需要解释,在添加了新的元素后,要执行两遍render(),不然第一个元素以下的所有元素其高度都不能正确地渲染出来。

var sortBy = 'rank'; // 排序方式有rank、title和desc

function changeSort(prop) {

sortBy = prop;

data.sort((a, b) => {

if (a[prop] > b[prop]) {

return 1;

}

if (a[prop] < b[prop]) {

return -1;

}

return 0;

});

render();

}

function add() {

var n = originalData[Math.floor(Math.random() * 10)];

data = [{rank: nextKey++, title: n.title, desc: n.desc, elmHeight: 0}].concat(data);

render();

render();

}

function remove(movie) {

data = data.filter((m) => { return m !== movie; });

render();

}

复制代码

b739ec46bb5c46d9c0aa4ce35ba1ea56.png

关于找一找教程网

本站文章仅代表作者观点,不代表本站立场,所有文章非营利性免费分享。

本站提供了软件编程、网站开发技术、服务器运维、人工智能等等IT技术文章,希望广大程序员努力学习,让我们用科技改变世界。

[Snabbdom 官方demo reorder-animation解说]http://www.zyiz.net/tech/detail-144984.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值