设<A,R>为偏序集,其中集合p上的偏序关系A={1,2,3,4,9,12},R是A上的整除关系,画出哈斯图

 上传我的文档
 下载
 收藏
本人从事小学语文教育,在小学语文教育方面积累了一定的资料和学习心得,愿意与诸位分享经验。
 下载此文档
2016离散复习练习题
下载积分:1500
内容提示:2016离散复习练习题
文档格式:DOCX|
浏览次数:2|
上传日期: 02:07:11|
文档星级:
全文阅读已结束,如果下载本文需要使用
 1500 积分
下载此文档
该用户还上传了这些文档
2016离散复习练习题
关注微信公众号离散数学有关偏序集哈斯图的题求详细解答过程_百度知道
离散数学有关偏序集哈斯图的题求详细解答过程
下面第一个图是问题,第二个图是答案,想知道是怎么求的,麻烦给个详细答案,明天就要考试了,挺急的,拜托了!!!
我有更好的答案
这种问题在纸上还真有点说不清楚,你尽量理解吧 (1)S是包含R的具有自反性和传递性的最小二元关系要使S具有自反性,S应包括所有的&1,1&,&2,2&,&3,3&,&4,4&,&5,5&,&6,6&,&7,7&,&8,8&要使S具有传递性,S = R ∪ R^2 ∪ R^3 ...∪ R^8其实就是比如R中含有&8,2&和&2,7&,则S中就必须有&8,7& (2)画关系S的哈斯图:哈斯图按照如下的方法构成:①用小圆圈表示偏序集的元素;②规定其方向是自下而上,即:若a≤b,则将a画在b的下方;③如果对于偏序集中任意两个元素x和y,有x≤y,并且不存在元素a,使得: x≤a∧a≤y那么就在x与y之间画一无向弧。 (3)(4)上界: (存在a)(a∈A∧(任意的x)(x∈B→ x≤a)) 下界: (存在a)(a∈A∧(任意的x)(x∈B→a≤x)) 上确界:
B的上界中的最小者;下确界:
B的下界中的最大者 (5) 依据哈斯图,从下往上写(上面一层的一定在下面一层的后面)
采纳率:92%
来自团队:
为您推荐:
其他类似问题
偏序的相关知识
换一换
回答问题,赢新手礼包
个人、企业类
违法有害信息,请在下方选择后提交
色情、暴力
我们会通过消息、邮箱等方式尽快将举报结果通知您。离散数学 设偏序集《A,R&如下图所求 关系r的表达式_百度知道
离散数学 设偏序集《A,R&如下图所求 关系r的表达式
我有更好的答案
(1)R = {&a,a&,&b,b&,&c,c&,&d,d&,&e,e&,&f,f&,&g,g&,&h,h&,&g,g&,&b,d&,&b,e&,&b,f&,&c,d&,&c,e&,&c,f&,&d,f&,&e,f&,&g,h&}(2)极小元:a,b,c,g
极大元:a,f,h
最大元:无
最小元:无(3)上界:d,f
最小上界:d
最大下界:无答题不易,请及时采纳,谢谢!
采纳率:92%
来自团队:
为您推荐:
其他类似问题
偏序的相关知识
换一换
回答问题,赢新手礼包
个人、企业类
违法有害信息,请在下方选择后提交
色情、暴力
我们会通过消息、邮箱等方式尽快将举报结果通知您。&p&作为一只半入行的PPT设计师,分享几个PPT的“&b&隐藏功能&/b&“,并且大多数普通人也能迅速掌握!&/p&&hr&&p&&b&&a href=&https://zhuanlan.zhihu.com/p/& class=&internal&&1.解锁洪荒之力—布尔运算/编辑顶点&/a& &/b&&/p&&p&布尔运算是个什么鬼?&/p&&figure&&img src=&https://pic1.zhimg.com/v2-9e60ed0fe29b30d7fd68_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&1280& data-rawheight=&720& class=&origin_image zh-lightbox-thumb& width=&1280& data-original=&https://pic1.zhimg.com/v2-9e60ed0fe29b30d7fd68_r.jpg&&&/figure&&p&具体来说,布尔运算就是将图形进行&b&相交/组合/剪切&/b&等操作。&/p&&p&就在这里!&/p&&p&菜单栏—格式—合并形状&/p&&p&但是只有在选中任意两个图形之后才会激活&b&合并形状&/b&这个按钮。&/p&&figure&&img src=&https://pic4.zhimg.com/v2-f469574daf7d79afcb3b7_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&1280& data-rawheight=&720& class=&origin_image zh-lightbox-thumb& width=&1280& data-original=&https://pic4.zhimg.com/v2-f469574daf7d79afcb3b7_r.jpg&&&/figure&&p&详细教程参考:&a href=&https://zhuanlan.zhihu.com/p/& class=&internal&&教程02—解锁PPT洪荒之力!布尔运算&/a&&/p&&p&&b&将布尔运算运用熟练后能达到什么效果呢?&/b&&/p&&p&做一份仿苹果发布会海报的封面!&/p&&figure&&img src=&https://pic1.zhimg.com/v2-dedfa9a6b3e621bd9d19b3c4_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&1280& data-rawheight=&720& class=&origin_image zh-lightbox-thumb& width=&1280& data-original=&https://pic1.zhimg.com/v2-dedfa9a6b3e621bd9d19b3c4_r.jpg&&&/figure&&p&做一些基础的平面海报也不在话下。&/p&&figure&&img src=&https://pic3.zhimg.com/v2-d7fd6afdfb6_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&720& data-rawheight=&2010& class=&origin_image zh-lightbox-thumb& width=&720& data-original=&https://pic3.zhimg.com/v2-d7fd6afdfb6_r.jpg&&&/figure&&p&与布尔运算相搭配的还有编辑顶点,可以轻松做出如下图的不规则图形!&/p&&figure&&img src=&https://pic3.zhimg.com/v2-7d654f85deb306dc0fb9e592_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&720& data-rawheight=&405& class=&origin_image zh-lightbox-thumb& width=&720& data-original=&https://pic3.zhimg.com/v2-7d654f85deb306dc0fb9e592_r.jpg&&&/figure&&figure&&img src=&https://pic3.zhimg.com/v2-1e7ca6aabedd4c2f7c4c3eae_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&1280& data-rawheight=&720& class=&origin_image zh-lightbox-thumb& width=&1280& data-original=&https://pic3.zhimg.com/v2-1e7ca6aabedd4c2f7c4c3eae_r.jpg&&&/figure&&p&学会了这两招,大多数的PPT图形制作已经不是难事~&/p&&hr&&p&&b&&a href=&https://zhuanlan.zhihu.com/p/& class=&internal&&2.懒人秘笈—PPT中插入矢量素材&/a& &/b&&/p&&p&虽然布尔运算已经足够强大,但是老师,我懒啊~不想动手怎么办?这时候就靠矢量素材大法了!&/p&&figure&&img src=&https://pic1.zhimg.com/v2-cf91aab4d395c893a3121ec_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&1280& data-rawheight=&720& class=&origin_image zh-lightbox-thumb& width=&1280& data-original=&https://pic1.zhimg.com/v2-cf91aab4d395c893a3121ec_r.jpg&&&/figure&&p&做PPT首先就是要找合适的图片,但是经常会遇到找不到合适图片的情况,这时候就不妨可以考虑用矢量素材,&b&AI的矢量文件在简单转化格式之后可以被PPT兼容!&/b&&/p&&p&例如这个素材网站:&/p&&p&&a href=&//link.zhihu.com/?target=http%3A//www.lanrentuku.com/& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&http://www.&/span&&span class=&visible&&lanrentuku.com/&/span&&span class=&invisible&&&/span&&/a&&/p&&p&在搜索“工作”之后得到如下的矢量素材&/p&&figure&&img src=&https://pic3.zhimg.com/v2-3bddeadd751abf6e62d476_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&720& data-rawheight=&568& class=&origin_image zh-lightbox-thumb& width=&720& data-original=&https://pic3.zhimg.com/v2-3bddeadd751abf6e62d476_r.jpg&&&/figure&&p&选取其中一个下载之后,得到eps/ai格式矢量文件&/p&&p&用AI打开它&/p&&figure&&img src=&https://pic1.zhimg.com/v2-144fa28dddcded00babbf8_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&1366& data-rawheight=&746& class=&origin_image zh-lightbox-thumb& width=&1366& data-original=&https://pic1.zhimg.com/v2-144fa28dddcded00babbf8_r.jpg&&&/figure&&p&最重要的一步来了!&/p&&p&&b&文件—导出—选择EMF格式文件&/b&&/p&&p&之后将EMF文件拖入PPT中&/p&&figure&&img src=&https://pic1.zhimg.com/v2-0bfc5adce7bfc051a6fc_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&1366& data-rawheight=&746& class=&origin_image zh-lightbox-thumb& width=&1366& data-original=&https://pic1.zhimg.com/v2-0bfc5adce7bfc051a6fc_r.jpg&&&/figure&&p&在弹出的对话框中选择确定~&b&之后再进行右键—取消组合&/b&&/p&&p&&b&所有的基本图形部分变为了PPT中可编辑的元素&/b&,可以在PPT中单独调整他们的大小/位置/颜色!&/p&&p&换上一件巴西味的纪念衫~&/p&&figure&&img src=&https://pic3.zhimg.com/v2-b3bc54e1bad05c2daa42a_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&809& data-rawheight=&455& class=&origin_image zh-lightbox-thumb& width=&809& data-original=&https://pic3.zhimg.com/v2-b3bc54e1bad05c2daa42a_r.jpg&&&/figure&&p&或者在旁边配上文字内容也是不错的&/p&&figure&&img src=&https://pic2.zhimg.com/v2-984fc3f721_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&1280& data-rawheight=&720& class=&origin_image zh-lightbox-thumb& width=&1280& data-original=&https://pic2.zhimg.com/v2-984fc3f721_r.jpg&&&/figure&&p&&b&还能怎么玩?!&/b&&/p&&p&在前面的&a href=&https://zhuanlan.zhihu.com/p/& class=&internal&&干货01—PPT素材全搜罗&/a&里提到过一个网站&/p&&p&&a href=&//link.zhihu.com/?target=http%3A//pictogram2.com/& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&http://&/span&&span class=&visible&&pictogram2.com/&/span&&span class=&invisible&&&/span&&/a&&/p&&figure&&img src=&https://pic4.zhimg.com/v2-df1bedbdfeab_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&1183& data-rawheight=&603& class=&origin_image zh-lightbox-thumb& width=&1183& data-original=&https://pic4.zhimg.com/v2-df1bedbdfeab_r.jpg&&&/figure&&p&提供各种人形素材,而这些素材均有矢量文件下载,因此可以随下随用&/p&&figure&&img src=&https://pic3.zhimg.com/v2-f304eaac11d2_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&776& data-rawheight=&585& class=&origin_image zh-lightbox-thumb& width=&776& data-original=&https://pic3.zhimg.com/v2-f304eaac11d2_r.jpg&&&/figure&&p&可以选择下方AI/JPG/PNG格式下载,导入PPT方式和上面的矢量图类似。&/p&&p&&b&掌握之后结合上期所讲的布尔运算等操作不难做出下图的效果&/b&&/p&&figure&&img src=&https://pic2.zhimg.com/v2-a9a7dfbd_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&1010& data-rawheight=&1696& class=&origin_image zh-lightbox-thumb& width=&1010& data-original=&https://pic2.zhimg.com/v2-a9a7dfbd_r.jpg&&&/figure&&p&更多EMF矢量图在PPT中的用法&/p&&figure&&img src=&https://pic4.zhimg.com/v2-a7fb5a1e2b89f287b9c1b_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&1280& data-rawheight=&720& class=&origin_image zh-lightbox-thumb& width=&1280& data-original=&https://pic4.zhimg.com/v2-a7fb5a1e2b89f287b9c1b_r.jpg&&&/figure&&figure&&img src=&https://pic1.zhimg.com/v2-bb013d1b00ccf466def84_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&1280& data-rawheight=&720& class=&origin_image zh-lightbox-thumb& width=&1280& data-original=&https://pic1.zhimg.com/v2-bb013d1b00ccf466def84_r.jpg&&&/figure&&p&提升效率,加班必备,值得拥有!&/p&&hr&&p&&b&&a href=&https://zhuanlan.zhihu.com/p/& class=&internal&&3.一懒再懒—好用到爆的PPT插件&/a&&/b&&/p&&p&为什么别人完成一份PPT只要半小时,而我却要花三四倍的时间?Office的功能总归有限,难不成效率不够,手速来凑?(误)&/p&&p&其实自从Office 2013版本开始,已经有大量优秀插件可以作为加载项集成到Powerpoint当中。如果你是PPT的重度使用者,为大量繁复的操作所困,这篇文章或许会让你的效率飞升!&/p&&figure&&img src=&https://pic3.zhimg.com/v2-04052aea6d6fdb5fe612_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&1280& data-rawheight=&480& class=&origin_image zh-lightbox-thumb& width=&1280& data-original=&https://pic3.zhimg.com/v2-04052aea6d6fdb5fe612_r.jpg&&&/figure&&p&1&i&.&/i&&a href=&//link.zhihu.com/?target=https%3A//www.islide.cc/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&全能选手-iSlide(原Nordri tools)&/a&&/p&&figure&&img src=&https://pic3.zhimg.com/v2-45a6bfd0ce17d8748cf6_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&1280& data-rawheight=&720& class=&origin_image zh-lightbox-thumb& width=&1280& data-original=&https://pic3.zhimg.com/v2-45a6bfd0ce17d8748cf6_r.jpg&&&/figure&&p&作为国内老牌PPT插件之一,在Nordri tools时代它就已经风靡P圈,同时也是我用的最多的一款插件。简单易用的工作界面,即使是初学者也可以轻松上手。&/p&&figure&&img src=&https://pic3.zhimg.com/v2-2db2713168e_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&716& data-rawheight=&128& class=&origin_image zh-lightbox-thumb& width=&716& data-original=&https://pic3.zhimg.com/v2-2db2713168e_r.jpg&&&/figure&&p&最著名的功能——PPT拼图,可以在瞬间完成PPT长图的拼接,效果如下。&/p&&figure&&img src=&https://pic4.zhimg.com/v2-e7ada9df3bcb_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&816& data-rawheight=&913& class=&origin_image zh-lightbox-thumb& width=&816& data-original=&https://pic4.zhimg.com/v2-e7ada9df3bcb_r.jpg&&&/figure&&p&后来新增的图标库,图片库,色彩库等功能,更是让诸多的专业素材网站汗颜。更为方便(偷懒)的是,其中的图片/图标/配色可以一键插入到PPT当中。&/p&&figure&&img src=&https://pic1.zhimg.com/v2-177dabbe438df0cbd69c0ec_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&1280& data-rawheight=&720& class=&origin_image zh-lightbox-thumb& width=&1280& data-original=&https://pic1.zhimg.com/v2-177dabbe438df0cbd69c0ec_r.jpg&&&/figure&&p&对于大多数PPT轻度使用者来说,或许一个iSlide就够用了。&/p&&p&&br&&/p&&p&2.&a href=&//link.zhihu.com/?target=http%3A//www.papocket.com/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&动画爱好者福音—PA口袋动画&/a&&/p&&figure&&img src=&https://pic4.zhimg.com/v2-f9fc89fca5deed09b0ef0f2f_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&1280& data-rawheight=&720& class=&origin_image zh-lightbox-thumb& width=&1280& data-original=&https://pic4.zhimg.com/v2-f9fc89fca5deed09b0ef0f2f_r.jpg&&&/figure&&p&某些特殊情况下,需要用PPT实现一些高质量的动画效果,这时候PPT原生的动画编辑器常常让人无话可说,而近几年兴起的PA动画就是为了解决这一问题而诞生。紧凑而强大的界面,会让热衷PPT动画的你无法自拔。&/p&&figure&&img src=&https://pic1.zhimg.com/v2-3f1c059c57c8a93c8afc_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&1069& data-rawheight=&127& class=&origin_image zh-lightbox-thumb& width=&1069& data-original=&https://pic1.zhimg.com/v2-3f1c059c57c8a93c8afc_r.jpg&&&/figure&&p&更可以一键插入预设的动画效果,拯救动画小白。&/p&&figure&&img src=&https://pic3.zhimg.com/v2-902bade858fd7562dce6_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&391& data-rawheight=&210& class=&content_image& width=&391&&&/figure&&p&&br&&/p&&p&3.&a href=&//link.zhihu.com/?target=http%3A//oktools.xyz/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&排版/图片神器-OneKeyTools&/a&&/p&&figure&&img src=&https://pic1.zhimg.com/v2-afe6eeae168_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&1280& data-rawheight=&720& class=&origin_image zh-lightbox-thumb& width=&1280& data-original=&https://pic1.zhimg.com/v2-afe6eeae168_r.jpg&&&/figure&&p&身为三大神器之一的OneKeyTools功能同样强大,相比前两者,它的排版功能尤为突出。无论是文字还是图形的排版,种类丰富强大。&/p&&figure&&img src=&https://pic4.zhimg.com/v2-28fd58a3ed1dd3e64cb2df59600a80bb_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&967& data-rawheight=&124& class=&origin_image zh-lightbox-thumb& width=&967& data-original=&https://pic4.zhimg.com/v2-28fd58a3ed1dd3e64cb2df59600a80bb_r.jpg&&&/figure&&p&在选中几个图形之后,一键排版什么的不在话下。&/p&&figure&&img src=&https://pic2.zhimg.com/v2-a069ef508f7adaeb2b9283f1_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&600& data-rawheight=&411& class=&origin_image zh-lightbox-thumb& width=&600& data-original=&https://pic2.zhimg.com/v2-a069ef508f7adaeb2b9283f1_r.jpg&&&/figure&&p&对于图片的处理也有多种滤镜可选,如一键马赛克/图片极坐标/图片三维折叠等。&/p&&figure&&img src=&https://pic2.zhimg.com/v2-edc141a7eb71bd54ba759_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&600& data-rawheight=&496& class=&origin_image zh-lightbox-thumb& width=&600& data-original=&https://pic2.zhimg.com/v2-edc141a7eb71bd54ba759_r.jpg&&&/figure&&hr&&p&&b&&a href=&https://zhuanlan.zhihu.com/p/& class=&internal&&4.突破天际—PPT中3D模型的使用&/a& &/b&&/p&&p&去年开始,Office2016开发者预览版放出了一项炸裂眼球的新功能——3D模型导入。&/p&&p&话不多说,先看效果。&/p&&figure&&img src=&https://pic2.zhimg.com/v2-b34d0ab5c9482fcb9ddd_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&547& data-rawheight=&303& class=&origin_image zh-lightbox-thumb& width=&547& data-original=&https://pic2.zhimg.com/v2-b34d0ab5c9482fcb9ddd_r.jpg&&&/figure&&figure&&img src=&https://pic3.zhimg.com/v2-b66cc42e451ccfc103676_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&547& data-rawheight=&303& class=&origin_image zh-lightbox-thumb& width=&547& data-original=&https://pic3.zhimg.com/v2-b66cc42e451ccfc103676_r.jpg&&&/figure&&p&刚看到这些效果时,我是一副见了鬼的样子,这也是PPT?!&/p&&figure&&img src=&https://pic4.zhimg.com/v2-933e7aab_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&225& data-rawheight=&225& class=&content_image& width=&225&&&/figure&&p&然而,微软的这波更新已将“未来技术”带到了现实,在某种程度上,Powerpoint已经可以做到一些“发布会”级别的效果。&/p&&p&只需要寥寥几步,你也可以掌握简单的3D演示技巧,到底怎么做呢?&/p&&p&&b&一. 将Office更新至最新的开发者预览版。&/b&&/p&&figure&&img src=&https://pic3.zhimg.com/v2-ed738ba60f94d0a265fa0ce_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&1280& data-rawheight=&551& class=&origin_image zh-lightbox-thumb& width=&1280& data-original=&https://pic3.zhimg.com/v2-ed738ba60f94d0a265fa0ce_r.jpg&&&/figure&&p&具体的更新教程可以参考:&br&&a href=&//link.zhihu.com/?target=https%3A//products.office.com/zh-CN/office-insider%3Ftab%3DWindows-Desktop& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&https://&/span&&span class=&visible&&products.office.com/zh-&/span&&span class=&invisible&&CN/office-insider?tab=Windows-Desktop&/span&&span class=&ellipsis&&&/span&&/a&&/p&&p&&b&二. 建立/寻找3D模型&/b&&/p&&p&目前支持导入的模型格式有fbx/obj/3mf/ply/stl/glb。那么问题来了,这些模型到哪里去找呢?下面推荐几种快速获得模型的方法:&/p&&p&1. Paint 3D&/p&&figure&&img src=&https://pic3.zhimg.com/v2-0c6b070942cbb61e220ff36b93230a4e_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&1075& data-rawheight=&539& class=&origin_image zh-lightbox-thumb& width=&1075& data-original=&https://pic3.zhimg.com/v2-0c6b070942cbb61e220ff36b93230a4e_r.jpg&&&/figure&&p&这款是微软自家开发的3D建模软件,操作简洁,适合新手入门,也可以用来制作一些简单的PPT模型&/p&&figure&&img src=&https://pic4.zhimg.com/v2-9c7dd77e1fc0a81f4bf4bb_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&1282& data-rawheight=&1001& class=&origin_image zh-lightbox-thumb& width=&1282& data-original=&https://pic4.zhimg.com/v2-9c7dd77e1fc0a81f4bf4bb_r.jpg&&&/figure&&p&在建模完成之后,可以选择直接输出fbx或者3mf格式的文件,并且能够直接导入PPT当中&/p&&figure&&img src=&https://pic2.zhimg.com/v2-a31fff68cebb4bd547c8d4c70c849ced_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&649& data-rawheight=&290& class=&origin_image zh-lightbox-thumb& width=&649& data-original=&https://pic2.zhimg.com/v2-a31fff68cebb4bd547c8d4c70c849ced_r.jpg&&&/figure&&p&Paint 3D下载链接:&br&&a href=&//link.zhihu.com/?target=https%3A//www.microsoft.com/en-us/store/p/paint-3d-preview/9nblggh5fv99& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&https://www.&/span&&span class=&visible&&microsoft.com/en-us/sto&/span&&span class=&invisible&&re/p/paint-3d-preview/9nblggh5fv99&/span&&span class=&ellipsis&&&/span&&/a&&/p&&p&&br&&/p&&p&2.从素材网站下载&br&作为一名爱偷懒设计爱好者,用模板绝对是完成工作省时省力的好方法。平时也收集了一些免费的3D模型网站,今天终于派上了用场,希望对你也有所帮助&/p&&p&&a href=&//link.zhihu.com/?target=https%3A//archive3d.net/& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&https://&/span&&span class=&visible&&archive3d.net/&/span&&span class=&invisible&&&/span&&/a&&/p&&figure&&img src=&https://pic3.zhimg.com/v2-f504fbd7fbd1ff1d443e5be_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&1363& data-rawheight=&699& class=&origin_image zh-lightbox-thumb& width=&1363& data-original=&https://pic3.zhimg.com/v2-f504fbd7fbd1ff1d443e5be_r.jpg&&&/figure&&p&&a href=&//link.zhihu.com/?target=http%3A//www.sccnn.com/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&www.sccnn.com&/a&&/p&&figure&&img src=&https://pic4.zhimg.com/v2-e4c352fcb357ebfa0acfb3_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&954& data-rawheight=&772& class=&origin_image zh-lightbox-thumb& width=&954& data-original=&https://pic4.zhimg.com/v2-e4c352fcb357ebfa0acfb3_r.jpg&&&/figure&&p&&br&&/p&&p&&a href=&//link.zhihu.com/?target=http%3A//www.top3dmodels.com/products/3d-models/free& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Free 3d Models&/a&&/p&&figure&&img src=&https://pic2.zhimg.com/v2-aef9a8c746b26c6cdafd92d5_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&1002& data-rawheight=&884& class=&origin_image zh-lightbox-thumb& width=&1002& data-original=&https://pic2.zhimg.com/v2-aef9a8c746b26c6cdafd92d5_r.jpg&&&/figure&&p&&br&&/p&&p&&a href=&//link.zhihu.com/?target=http%3A//www.dmi-3d.net/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&DMI Car 3D Models&/a&&/p&&figure&&img src=&https://pic1.zhimg.com/v2-660d110e6aff3da5ea1bb8_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&834& data-rawheight=&745& class=&origin_image zh-lightbox-thumb& width=&834& data-original=&https://pic1.zhimg.com/v2-660d110e6aff3da5ea1bb8_r.jpg&&&/figure&&p&&br&&/p&&p&&a href=&//link.zhihu.com/?target=http%3A//www.scifi3d.com/list.asp%3FintGenreID%3D14%26intCatID%3D36& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&http://www.&/span&&span class=&visible&&scifi3d.com/list.asp?&/span&&span class=&invisible&&intGenreID=14&intCatID=36&/span&&span class=&ellipsis&&&/span&&/a&&/p&&figure&&img src=&https://pic2.zhimg.com/v2-ce520d311cab1_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&1069& data-rawheight=&889& class=&origin_image zh-lightbox-thumb& width=&1069& data-original=&https://pic2.zhimg.com/v2-ce520d311cab1_r.jpg&&&/figure&&p&&br&&/p&&p&&a href=&//link.zhihu.com/?target=http%3A//www.model3d.biz/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&3D Furniture Models&/a&&/p&&figure&&img src=&https://pic2.zhimg.com/v2-697ebe67dd3fe82b5df33d_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&1125& data-rawheight=&747& class=&origin_image zh-lightbox-thumb& width=&1125& data-original=&https://pic2.zhimg.com/v2-697ebe67dd3fe82b5df33d_r.jpg&&&/figure&&p&&br&&/p&&p&3. 使用专业建模软件制作&/p&&p&在找不到合适模型的时候,可以尝试自己建模,例如Cinema 4D/3ds max/Rhino等,但是使用3D建模软件需要较高的软件技巧,建议有时间有精力的同学尝试&/p&&figure&&img src=&https://pic3.zhimg.com/v2-25c1f0aed0dabaa5c4f3fe_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&1280& data-rawheight=&720& class=&origin_image zh-lightbox-thumb& width=&1280& data-original=&https://pic3.zhimg.com/v2-25c1f0aed0dabaa5c4f3fe_r.jpg&&&/figure&&p&&br&&/p&&p&&b&三. 导入PPT实现动画&/b&&/p&&p&在更新到最新版的PPT之后,会在插入一栏出现“3D模型”一项,通过它就可以将兼容格式的模型直接导入&/p&&figure&&img src=&https://pic1.zhimg.com/v2-2ffd0ba68db702f5df7e50_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&448& data-rawheight=&158& class=&origin_image zh-lightbox-thumb& width=&448& data-original=&https://pic1.zhimg.com/v2-2ffd0ba68db702f5df7e50_r.jpg&&&/figure&&p&导入后的模型如下图所示&/p&&figure&&img src=&https://pic1.zhimg.com/v2-676d401bb92caf94a112a4_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&1109& data-rawheight=&640& class=&origin_image zh-lightbox-thumb& width=&1109& data-original=&https://pic1.zhimg.com/v2-676d401bb92caf94a112a4_r.jpg&&&/figure&&p&单击模型,会在中央出现旋转按钮,按住它拖动鼠标即可实现图像的任意旋转,如下图所示&/p&&figure&&img src=&https://pic2.zhimg.com/v2-a4ee0ac7e61_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&600& data-rawheight=&348& class=&origin_image zh-lightbox-thumb& width=&600& data-original=&https://pic2.zhimg.com/v2-a4ee0ac7e61_r.jpg&&&/figure&&p&但是,要实现模型在PPT中的变化,只靠这一张页面是不够的,我们需要将它复制一次,然后在第二张上对模型进行大小调整,同时也可以添加文字&/p&&figure&&img src=&https://pic3.zhimg.com/v2-b6eca0073011cfa3b2e2fe2_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&863& data-rawheight=&243& class=&origin_image zh-lightbox-thumb& width=&863& data-original=&https://pic3.zhimg.com/v2-b6eca0073011cfa3b2e2fe2_r.jpg&&&/figure&&p&最后一步,在第二张上添加切换效果——平滑,即可大功告成&/p&&figure&&img src=&https://pic2.zhimg.com/v2-bae91345_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&904& data-rawheight=&454& class=&origin_image zh-lightbox-thumb& width=&904& data-original=&https://pic2.zhimg.com/v2-bae91345_r.jpg&&&/figure&&p&最终效果图如下&/p&&figure&&img src=&https://pic4.zhimg.com/v2-713e5a011af3da071ffe44cdcf62249b_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&600& data-rawheight=&332& class=&origin_image zh-lightbox-thumb& width=&600& data-original=&https://pic4.zhimg.com/v2-713e5a011af3da071ffe44cdcf62249b_r.jpg&&&/figure&&p&需要注意的是,目前PPT的3D处于刚刚起步的阶段,支持的格式还不够丰富。相信经过长时间的版本更迭以后,会有更多的素材与功能会加入到其中。&/p&&hr&&a href=&https://zhuanlan.zhihu.com/guanhaippt& data-draft-node=&block& data-draft-type=&link-card& data-image=&https://pic4.zhimg.com/v2-9df34b1eb4eab_ipico.jpg& data-image-width=&541& data-image-height=&541& class=&internal&&做生活的设计师&/a&&p&微信公众号:观海PPT&/p&
作为一只半入行的PPT设计师,分享几个PPT的“隐藏功能“,并且大多数普通人也能迅速掌握! 布尔运算是个什么鬼?具体来说,布尔运算就是将图形进行相交/组合/剪切等操作。就在这里!菜单栏—格式—合并形状但是只有在选中…
&figure&&img src=&https://pic4.zhimg.com/ff47bb6a4c_b.jpg& data-rawwidth=&1293& data-rawheight=&576& class=&origin_image zh-lightbox-thumb& width=&1293& data-original=&https://pic4.zhimg.com/ff47bb6a4c_r.jpg&&&/figure&原文:&a href=&https://link.zhihu.com/?target=https%3A//keelii.github.io//something-have-to-say-with-JD-item/& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&https://&/span&&span class=&visible&&keelii.github.io/2016/0&/span&&span class=&invisible&&7/31/something-have-to-say-with-JD-item/&/span&&span class=&ellipsis&&&/span&&/a&&h2&简介&/h2&&p&详情页也叫做单品页,域名以「&a href=&https://link.zhihu.com/?target=http%3A//item.jd.com/skuid.html& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&http://&/span&&span class=&visible&&item.jd.com/skuid.html&/span&&span class=&invisible&&&/span&&/a&」为格式的页面。是负责展示京东商品 SKU 的落地页面。主要任务是展示和商品相关的信息,如:价格、促销、库存、推荐,从而引导用户进入购买流程。同时单品页有很多版本。一般分为两类。一类我们通常看到的「通用类目详情页」—— 所有类目都可以使用,一类是不经常看到的「垂直属性详情页」—— 一些有特殊属性的商品集合&/p&&figure&&img src=&https://pic1.zhimg.com/89adc904bfb2ade6eea193_b.jpg& data-rawwidth=&646& data-rawheight=&350& class=&origin_image zh-lightbox-thumb& width=&646& data-original=&https://pic1.zhimg.com/89adc904bfb2ade6eea193_r.jpg&&&/figure&&br&&p&首先。由于详情页大量(sku上亿)、高并发(日 pv 约 5000 万)等特性,在很长的一段时间里,单品页面都是后端程序生成静态页面使用 CDN 来解决大量、高并发的问题&/p&&p&其次。单品页涉及的「三方」系统特别多,比如:促销、库存、合约、秒杀、预售、推荐、IM、店铺、评价社区。而单品页的主要任务就是展示这些系统的信息,并且适当的处理他们之间的冲突关系,而这些系统的接口一般都使用 异步 Ajax 来完成,因为 其一 CDN 无法做到页面的动态化,其二 一些系统的信息对实时性要求特别高(价格、秒杀),即使使用后端动态渲染也很难做到无缓存 0 延迟&/p&&p&基于上面两个原因,注定了单品页是一种重多系统业务逻辑展示型页面。重前端页面。我大概汇总了一下页面上异步接口,总共约有 30 个,页首屏的接口特别重要,接口之间几乎都有耦合关系&/p&&figure&&img src=&https://pic4.zhimg.com/ff47bb6a4c_b.jpg& data-rawwidth=&1293& data-rawheight=&576& class=&origin_image zh-lightbox-thumb& width=&1293& data-original=&https://pic4.zhimg.com/ff47bb6a4c_r.jpg&&&/figure&&h2&前端的发展历程&/h2&&h3&混沌时期&/h3&&p&混沌时期的单品页并没有前端开发的概念。核心的功能脚本只有三个:促销价格(promotion.js)、库存地区(iplocation.js)、其它逻辑(pshow.js)。这三个脚本分别是三个不同团队的同事负责维护,当时我刚进入京东的时候在 UED 部门,负责页面脚本整体的维护工作和 pshow的开发。那时候我自己维护的 pshow.js 脚本压缩后只有 80 kb,所有的代码都是过程式的,没有任何使用模式和代码技巧,JS 最多也只被用来做个判断渲染 DOM。那时候的前端工作内容只在 UI 层面,写样式和一些交互脚本&/p&&p&这个阶段给我最深刻的感觉是单品页后端模板很少维护(后端架构是最老的 aspx 版本)。大多数的改动都要用 JavaScript 去动态渲染。因为后端页面是一个生成器生成的。如果页面后端模板有改动那么就需要全量的生成一次,过程可能需要几个小时&/p&&h3&初见端倪&/h3&&p&当我接手这个项目时刚好有一次大改版,就在这时候老大说页面上的脚本都要放在我们手里维护。然后就是一大波的重构、重写。基本上 pshow 被重写了大概 80% 其它的因为业务逻辑的问题并没有完全重写,只是做了些代码层面的优化&/p&&p&有一个模板引擎叫 &a href=&https://link.zhihu.com/?target=http%3A//code.google.com/p/trimpath/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&trimPath&/a&,知道这个的估计都算老前端的了。最早的客户端 JavaScript MVC 模式代表作品,只到现在还是使用。这个阶段像评价这种完全异步加载的模块特别适合使用模板引擎来减少维护的工作量。这个时候虽然页面上的代码并不都是我们写的,但基本上前端对页面的 JavaScript 有了控制权,接下来的事情就是寻找机会逐个优化&/p&&p&这段时间是最痛苦的时候,维护的工作统一到前端。然后后端几乎没有变化,只是在一段时间将后台的架构从 aspx 过渡到了 java。本质上并没有什么改变。前端却做了比以前更多的事情,也是在这个时候我接手了大量的维护工作(包含全站公共库的维护)使得我意识到了一些自动化、工程化方面的重要性,后文会主要讲解,顺便说下,那时候前端自动化工具 Grunt 刚面世,但是我自己却用的是&a href=&https://link.zhihu.com/?target=http%3A//ant.apache.org/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&apache ant&/a&,不过不久就切换到了 Grunt 来构建项目&/p&&h3&拨云见日&/h3&&p&单品页不仅重系统逻辑,也重维护&/p&&p&在这段时间里一方面有正常的维护类需求要做,一方面自己也不断的学习新知识为以后的改版做铺垫。不过就在这时单品页有历史意义的一次技改出现了 —— 单品页动态化技改。关于后端部分的改造细节可以去 &a href=&https://link.zhihu.com/?target=http%3A//jinnianshilongnian.iteye.com/blog/2235572& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&开涛的文章&/a& 了解&/p&&p&总的来说这次的改版后很多数据直接从后端读取,不再从前端异步获取而且我们也做过一些异步加载的优化,多接口 combo 从统一服务吐出给前端使用。这时前端就不用再为异步接口的加载时苦脑了,只需要专注系统接口的逻辑&/p&&p&随着这次技改,前端的代码也迎来了模块化的时代。我们把所有的前端代码都进行了模块化然后基于 SeaJS 重写,配合 Nginx concat 功能实现了本地模块化开发,线上服务端合并&/p&&h2&单品页前端模块的结构与划分&/h2&&h3&概览&/h3&&figure&&img src=&https://pic3.zhimg.com/bbf8dea030eb_b.jpg& data-rawwidth=&1531& data-rawheight=&838& class=&origin_image zh-lightbox-thumb& width=&1531& data-original=&https://pic3.zhimg.com/bbf8dea030eb_r.jpg&&&/figure&&br&&p&上图可以看出,基本上最核心的模块都在首屏。每个模块都有单独的一/多个脚本。代码行数(LOC)由 230+ ~ 1200+ 不等。通常来说代码行数越多代码复杂性就越高,逻辑越复杂。很难想象「购买方式」这种只有一行属性选择功能的代码行数却 &b&高达 1200 多行&/b&。其主要原因就在于购买方式所在的系统和其它首屏核心系统(库存、促销、地址选择、白条)都有逻辑上的耦合&/p&&p&看着不错,然而在一个前端工程师眼里至少应该是这样的(我只取了一些典型的模块,并不是全部):&/p&&figure&&img src=&https://pic1.zhimg.com/6c1da4dfde4bd3bfd41ef08da7324051_b.jpg& data-rawwidth=&1435& data-rawheight=&899& class=&origin_image zh-lightbox-thumb& width=&1435& data-original=&https://pic1.zhimg.com/6c1da4dfde4bd3bfd41ef08da7324051_r.jpg&&&/figure&&br&&p&这就可以解释为什么有的时候只是加一个很小的东西我们都为考虑再三然后通过 AB 测试提取相关数据,最后后再进行决策。单品页的首屏可以说是寸土寸金&/p&&h3&按什么维度划分模块&/h3&&p&起初我按模块的属性划分,比如:核心、公共脚本、模块脚本。但用了一段时候以后发现这样划分在单品这种大型系统中并不科学,因为这样划分出来的代码只有划分的人知道是什么规则,其它人接手代码很难快速掌握代码架构,而且尤其在模块比较多的时候不方便维护&/p&&p&后来我尝试完全以功能模块在页面上出现的位置维度划分。这样以来维护起来方便多了,需要修改某个模块代码只需要对照着图里面标识的模块信息就能轻易找到代码&/p&&h3&整体核心模块&/h3&&p&我们按页面上的模块结构首屏划分出来这几个核心模块:&/p&&ul&&li&&p&curmb - 面包屑&/p&&/li&&li&&p&concat - 联系咨询相关店铺信息&/p&&/li&&li&&p&prom - 价格促销信息&/p&&/li&&li&&p&address - 地区库存选择,配送服务&/p&&/li&&li&&p&color - 颜色尺码&/p&&/li&&li&&p&buytype - 合约机购买方式&/p&&/li&&li&&p&suits - 套装购买&/p&&/li&&li&&p&jdservice - 增值服务&/p&&/li&&li&&p&baitiao - 白条支付&/p&&/li&&li&&p&buybtn - 购买按钮&/p&&/li&&li&&p&info - 地区提示信息&/p&&/li&&/ul&&p&项目的整体树形结构是这样的:&/p&&figure&&img src=&https://pic3.zhimg.com/dded4f1c86f21f6ad5fb9c_b.jpg& data-rawwidth=&512& data-rawheight=&254& class=&origin_image zh-lightbox-thumb& width=&512& data-original=&https://pic3.zhimg.com/dded4f1c86f21f6ad5fb9c_r.jpg&&&/figure&&h3&模块内部结构&/h3&&p&比如下面这个大图预览的功能,我全部放在一个文件夹里面维护,但是逻辑上的 JavaScript 模块是分离的,只是说文件夹(preview)就代表页面上的某一部分功能集合&/p&&figure&&img src=&https://pic4.zhimg.com/be7bdb4c430be3d8bbb8_b.jpg& data-rawwidth=&677& data-rawheight=&279& class=&origin_image zh-lightbox-thumb& width=&677& data-original=&https://pic4.zhimg.com/be7bdb4c430be3d8bbb8_r.jpg&&&/figure&&p&注意文件夹的命名有一定的规则:&/p&&ul&&li&&p&模块脚本与样式名必须一样&/p&&/li&&li&&p&需要制作 sprite 的图片统一放在 module/i 目录下面,生成的 sprite 图片也在其中&/p&&/li&&li&&p&生成的 mixin 在模块根目录下,便于其它样式文件调用&/p&&/li&&/ul&&p&我们再来看下自动生成生成的 __sprite.scss 是什么内容:&/p&&div class=&highlight&&&pre&&code class=&language-css&&&span&&/span&&span class=&c&&/* __sprite.scss 自动生成 */&/span&
&span class=&k&&@mixin&/span& &span class=&nt&&sprite-arrow-next&/span& &span class=&p&&{&/span&
&span class=&nt&&width&/span&&span class=&o&&:&/span& &span class=&nt&&22px&/span&&span class=&o&&;&/span&
&span class=&nt&&height&/span&&span class=&o&&:&/span& &span class=&nt&&32px&/span&&span class=&o&&;&/span&
&span class=&nt&&background-image&/span&&span class=&o&&:&/span& &span class=&nt&&url&/span&&span class=&o&&(&/span&&span class=&nt&&i&/span&&span class=&o&&/&/span&&span class=&nt&&__sprite&/span&&span class=&nc&&.png&/span&&span class=&o&&);&/span&
&span class=&nt&&background-position&/span&&span class=&o&&:&/span& &span class=&nt&&-0px&/span& &span class=&nt&&-30px&/span&&span class=&o&&;&/span&
&span class=&p&&}&/span&
&span class=&c&&/* preview.scss 手动添加 */&/span&
&span class=&k&&@import&/span& &span class=&s2&&&./__sprite&&/span&&span class=&p&&;&/span&
&span class=&nc&&.sprite-arrow-next&/span& &span class=&p&&{&/span&
&span class=&o&&@&/span&&span class=&n&&include&/span& &span class=&n&&sprite&/span&&span class=&o&&-&/span&&span class=&n&&arrow&/span&&span class=&o&&-&/span&&span class=&n&&next&/span&&span class=&p&&;&/span&
&span class=&p&&}&/span&
&/code&&/pre&&/div&&p&注意引用的 mixin 名称和我们需要手动添加的样式类名一致。当然也可以直接生成一个类名对应的样式,但是灵活性不好。比如 hover 的时候是另外一张图片就没法自动生成了&/p&&h2&前端技能树&/h2&&h3&HTML&/h3&&h4&DOM 节点数&/h4&&p&与重业务逻辑的页面不同,重展示的页面一般具有很高的 DOM 节点数。比如京东首页,正常情况加载完页面一共有 3500 多个 DOM 节点,基本上全部用于展示商品信息、广告图和内容布局,页面上的三方异步服务也比较少。尤其像频道页基本上没有什么业务上的逻辑,全部是静态页面。这种页面的特点是更新换代频率高,一年两三次改版很正常,CMS 做模块化后两天换个皮肤都是没问题的。但是这种思路并不适合单品页。单品页更重业务逻辑,同时展示层 UI 逻辑也有很多关系&/p&&p&我自己的经验是:&i&页面上的 DOM 节点数绝对不能超过 5000 个,否则页面滚动的时候就会出现卡顿的情况,尤其是移动端&/i&&/p&&h4&同步渲染还是异步加载&/h4&&p&理论情况下最好做法是后端同步动态渲染页面,但是由于 Web 应用中很多功能都是用户行为驱动的。同步加载不可避免的消耗了后端服务资源。比如:非首屏模块(公共头尾、评价)、点击事件触发的 DOM 内容(异步 tab)&/p&&p&所以我的经验是:&i&能放到后端做判断渲染的 DOM 就尽量放在后端(尤其是首屏)。这样做的好处有四点好处&/i&&/p&&ol&&li&&p&后端渲染页面相对稳定,不像前端 JavaScript 动态渲染 DOM,可能因为脚本报错或者不可用造成模块都无法展示&/p&&/li&&li&&p&可访问性、SEO 及用户体验也比较好。不会产生脚本的渲染抖动问题&/p&&/li&&li&&p&一定程度上减少了前端渲染页面的复杂性,减少前端代码复杂度&/p&&/li&&li&&p&逻辑统一到一个地方维护起来也方便,而且后端应该为业务逻辑负责,前端应该为展示UI 交互负责&/p&&/li&&/ol&&p&对于异步渲染的模块来说,后端通常需要判断 「&b&页面有什么元素&/b&」,以及元素之间的依赖对应关系;而前端需要专注于 「&b&元素应该怎么展示&/b&」,UI 层面的交互以及模块与模块之前的逻辑关系&/p&&p&其实更多的时候 异步是一种没有办法的办法,也就是说异步是其它方案都解决不了的情况下才考虑的&/p&&h4&外链静态资源&/h4&&p&尽量使用外链 CSS 和 JavaScript 资源,一方面便于缓存,减少服务同步输出的资源浪费。IE 6 里面会有一些可怪的 bug,比如有内联样式 style 标签的页面 A 如果在另外一个页面 B 中的 link 标签中引用,那么这段 style 会在 B 页面也起作用&/p&&h4&使用双协议的 URL&/h4&&p&使用 // 来代替http: 和 https: 浏览器会自动适应两种协议的资源访问,兼容性较好。注意 IE 8 下使用脚本更新 src 为双协议时会出现 bug,建议使用 location.protocol 来判断然后做兼容处理&/p&&h4&删除元素默认属性&/h4&&p&比如 script 标签默认的 type 就是 text/javascript,如果 script 里面的内容是 JavaScript 时可以不用写 type。另外如果要在页面里面插入一段不需要浏览器解析的 HTML 片段时可以将 type 写成 text/x-template(任意不存在的 type) 用于放置模板文件,通常用来在脚本中获取其 innerHTML 而无任何负作用&/p&&h4&给脚本控制元素加上类钩子&/h4&&p&在脚本中取页面元素使用 J- 前缀类名,与普通样式类分离。这样做会生成很多冗余的类名,但却很好的降低了样式和脚本的耦合,并且在重构和脚本职位分开团队里会是一条最佳实践&/p&&h3&CSS&/h3&&h4&样式分类&/h4&&p&所有页面只共享一个 sass Mixin,里面包含了基础的 sass 语法糖、常用类(清浮动、页面整体颜色字体等)&/p&&p&模块级的样式分为两类:&/p&&ol&&li&&p&与脚本无关的公共样式,单独在模块文件夹中组织。比如:按钮、标签页。全部放在 common 模块中维护&/p&&/li&&li&&p&与脚本相关的模块级样式,与对应模块脚本放在一起,可以引用 common 中的公共样式,但不可以被其它模块引用&/p&&/li&&/ol&&h4&雪碧图&/h4&&p&关于雪碧图 我经验是:永远不要想把所有的图标拼合在一起。按模块而不是按页面去拼 sprite 更合理,更方便维护,然后配合构建工具自动接合生成样式文件才是最好的解决方案。当然如果你的页面比较简单,那这条规则并不适用。说到这个问题我就得把珍藏多年的图片拿出来 show 一把,用事实来说明为什么把所有图片都拼在一张图上就一定是对的&/p&&p&早期由于年轻笃信将所有的 icon 拼在一张图上才是完美的(图 1)&/p&&figure&&img src=&https://pic2.zhimg.com/e30aa6f088854ffb4c0089_b.jpg& data-rawwidth=&273& data-rawheight=&212& class=&content_image& width=&273&&&/figure&&p&后来维护起来实在不方便,就把按钮全部单独接合起来。注意,当时的按钮都是图片,设计方面要求的很严格。加入购物车按钮做的也非常漂亮(图 2)&/p&&figure&&img src=&https://pic1.zhimg.com/699d89fe9fd131a942af_b.jpg& data-rawwidth=&421& data-rawheight=&159& class=&origin_image zh-lightbox-thumb& width=&421& data-original=&https://pic1.zhimg.com/699d89fe9fd131a942af_r.jpg&&&/figure&&p&然后这些都不是最典型的,下面这个 promise icon 才是 (图 3)&/p&&figure&&img src=&https://pic1.zhimg.com/b9f7eb2aede_b.jpg& data-rawwidth=&238& data-rawheight=&196& class=&content_image& width=&238&&&/figure&&p&从图里面可以看到,这个功能在第一个版本的时候只有 7 个 icon,后来不断增加,最多的时候达到 77 个。以至于当时每周都会添加两个的频率&/p&&p&同时这个 icon 当时接合的时候技术上也有问题:不应该把文字也切到图片里面,主要原因是早期 icon 比较少加上外边框样式对齐的问题综合选择了直接使用图片&/p&&p&后来我就觉得这样是不对的。然后通过和产品的沟通,说明我的考虑以及新的解决方案后得到了认同。结果就是对图片不进行拼合,后台上传经过审核的不带文字 icon,文字由接口输出,然后在产品上做了约定:icon 最多不能超过 4 个,代码里也做了相应限制。这样就能保证页面上的请求数不会太多同时方便系统维护,问题得到了解决&/p&&h4&适当使用 DataURI&/h4&&p&这个在一些小图片场景方面特别适合,比如 1*1 的占位图、loading 图等,不过 IE 6 并不支持这种写法,需要的时候可以加上一些兼容写法:&/p&&div class=&highlight&&&pre&&code class=&language-css&&&span&&/span&&span class=&nc&&.ELazy-loading&/span& &span class=&p&&{&/span&
&span class=&nb&&background&/span&&span class=&o&&:&/span& &span class=&sx&&url(data:image/base64,R0lGODlhKwAeAJEAAP///93d3Xq9VAAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQFFAAAACwDAA0AJQADAAACEpSPAhDtHxacqcr5Lm416f1hBQAh+QQJFAAAACwDAA0AJQADAAACFIyPAcLtDKKcMtn1Mt3RJpw53FYAACH5BAkUAAAALAMADQAlAAMAAAIUjI8BkL0CoxQtrYrenPjcrgDbVAAAOw==)&/span& &span class=&nb&&center&/span& &span class=&nb&&center&/span& &span class=&nb&&no-repeat&/span&&span class=&p&&;&/span&
&span class=&o&&*&/span&&span class=&nb&&background-image&/span&&span class=&o&&:&/span& &span class=&sx&&url(//misc.360buyimg.com/lib/skin/e/i/loading-jd.gif)&/span&&span class=&p&&;&/span&
&span class=&p&&}&/span&
&/code&&/pre&&/div&&h4&关于兼容性&/h4&&p&兼容性可以说是前端工程师在平常开发中花费很大量无意义工作的地方。关于兼容性我想说的是 如果你不愿意去说服周围的人放弃或者让他们意识到兼容性是个不可能完全解决的问题,那么你就得为那些低级浏览器给你带来的痛苦埋单&/p&&p&其实更好的办法是你和设计、产品沟通然后给出一种分级支持的方案。把每种浏览器定义一个级别。然后在开发功能的时候以「渐进增强」的方式。通常来讲我们的解决方案是在低级浏览器里面保证流程正常进行、模块可以使用,但忽略一些无关紧要的错位、不透明等问题,在高级浏览器里面需要对设计稿进行精确还原,适当的加上一些井上添花在细节。比如微小的动画、逻辑细节上的处理等&/p&&p&举个例子吧,下面这个进度条表示预约的人数,它是接口异步加载完才展示的。如果加载完就立即设置进度条宽度会显得生硬无趣,但是如果加上一点动画效果的话就好多了。然而问题又来了,如果加上动画那么逻辑上这个进度条应该是一点点的增加,对应的人数也应该是逐个增加。于是我就做了个优化,让人数在这段时间内均匀的增加。这个细节并不是很容易被人发现,但是这种设计会让用户感觉很用心而且有意思(不动戳大)&/p&&figure&&img src=&https://pic2.zhimg.com/e3e36dded77ae597fb0e97f_b.jpg& data-rawwidth=&747& data-rawheight=&256& class=&origin_image zh-lightbox-thumb& width=&747& data-original=&https://pic2.zhimg.com/e3e36dded77ae597fb0e97f_r.jpg&&&/figure&&h3&JavaScript&/h3&&figure&&img src=&https://pic4.zhimg.com/ff1fa10eb7_b.jpg& data-rawwidth=&542& data-rawheight=&362& class=&origin_image zh-lightbox-thumb& width=&542& data-original=&https://pic4.zhimg.com/ff1fa10eb7_r.jpg&&&/figure&&p&单品页的脚本加载/执行顺序:&/p&&ol&&li&&p&等待页面准备就绪(DOM Ready)&/p&&/li&&li&&p&准备就绪后加载入口脚本(main.js),脚本负责其它功能模块的调度,动态接合模块通过 seajs 的 require.async 方法异步调用&/p&&/li&&li&&p&公共模块(common.js)负责加初始化全局变量并挂载到 pageConfig 命名空间&/p&&/li&&li&&p&动态模块数组,这个是后端通过程序判断处理生成的一个模块名列表。一般只包含首屏需要加载的模块&/p&&/li&&li&&p&后加载模块(lazyinit.js)初始化,这个脚本只做一些页面滚动才加载的模块事件绑定。当模块出现在视口内再使用 require.async 异步加载模块的资源及初始化&/p&&/li&&/ol&&h4&入口脚本&/h4&&p&大致代码如下&/p&&div class=&highlight&&&pre&&code class=&language-js&&&span&&/span&&span class=&cm&&/**&/span&
&span class=&cm&&* 模块入口(1. 公共脚本 2. 首屏模块资源 3. 非首屏「后加载模块」)&/span&
&span class=&cm&&*/&/span&
&span class=&kd&&var&/span& &span class=&nx&&entries&/span& &span class=&o&&=&/span& &span class=&p&&[];&/span&
&span class=&c1&&// 页面公共脚本样式&/span&
&span class=&nx&&entries&/span&&span class=&p&&.&/span&&span class=&nx&&push&/span&&span class=&p&&(&/span&&span class=&s1&&'common'&/span&&span class=&p&&);&/span&
&span class=&c1&&// 页面使用到的首屏模块(后端开发根据页面不同配置需要调用的模块)&/span&
&span class=&nx&&entries&/span& &span class=&o&&=&/span& &span class=&nx&&entries&/span&&span class=&p&&.&/span&&span class=&nx&&concat&/span&&span class=&p&&(&/span&&span class=&nx&&config&/span&&span class=&p&&.&/span&&span class=&nx&&modules&/span&&span class=&p&&);&/span&
&span class=&c1&&// 非首屏「后加载模块」&/span&
&span class=&nx&&entries&/span&&span class=&p&&.&/span&&span class=&nx&&push&/span&&span class=&p&&(&/span&&span class=&s1&&'lazyinit'&/span&&span class=&p&&);&/span&
&span class=&k&&for&/span& &span class=&p&&(&/span&&span class=&kd&&var&/span& &span class=&nx&&i&/span& &span class=&o&&=&/span& &span class=&mi&&0&/span&&span class=&p&&;&/span& &span class=&nx&&i&/span& &span class=&o&&&&/span& &span class=&nx&&entries&/span&&span class=&p&&.&/span&&span class=&nx&&length&/span&&span class=&p&&;&/span& &span class=&nx&&i&/span&&span class=&o&&++&/span&&span class=&p&&)&/span& &span class=&p&&{&/span&
&span class=&nx&&entries&/span&&span class=&p&&[&/span&&span class=&nx&&i&/span&&span class=&p&&]&/span& &span class=&o&&=&/span& &span class=&s1&&'MOD_ROOT/'&/span& &span class=&o&&+&/span& &span class=&nx&&entries&/span&&span class=&p&&[&/span&&span class=&nx&&i&/span&&span class=&p&&]&/span& &span class=&o&&+&/span& &span class=&s1&&'/'&/span& &span class=&o&&+&/span& &span class=&nx&&entries&/span&&span class=&p&&[&/span&&span class=&nx&&i&/span&&span class=&p&&];&/span&
&span class=&p&&}&/span&
&span class=&k&&if&/span& &span class=&p&&(&/span&&span class=&sr&&/debug=show_modules/&/span&&span class=&p&&.&/span&&span class=&nx&&test&/span&&span class=&p&&(&/span&&span class=&nx&&location&/span&&span class=&p&&.&/span&&span class=&nx&&href&/span&&span class=&p&&))&/span& &span class=&nx&&console&/span&&span class=&p&&.&/span&&span class=&nx&&log&/span&&span class=&p&&(&/span&&span class=&nx&&entries&/span&&span class=&p&&);&/span&
&span class=&nx&&require&/span&&span class=&p&&.&/span&&span class=&nx&&async&/span&&span class=&p&&(&/span&&span class=&nx&&entries&/span&&span class=&p&&,&/span& &span class=&kd&&function&/span&&span class=&p&&()&/span& &span class=&p&&{&/span&
&span class=&kd&&var&/span& &span class=&nx&&modules&/span& &span class=&o&&=&/span& &span class=&nb&&Array&/span&&span class=&p&&.&/span&&span class=&nx&&prototype&/span&&span class=&p&&.&/span&&span class=&nx&&slice&/span&&span class=&p&&.&/span&&span class=&nx&&call&/span&&span class=&p&&(&/span&&span class=&nx&&arguments&/span&&span class=&p&&);&/span&
&span class=&kd&&var&/span& &span class=&nx&&len&/span& &span class=&o&&=&/span& &span class=&nx&&modules&/span&&span class=&p&&.&/span&&span class=&nx&&length&/span&&span class=&p&&;&/span&
&span class=&k&&for&/span& &span class=&p&&(&/span&&span class=&kd&&var&/span& &span class=&nx&&i&/span& &span class=&o&&=&/span& &span class=&mi&&0&/span&&span class=&p&&;&/span& &span class=&nx&&i&/span& &span class=&o&&&&/span& &span class=&nx&&len&/span&&span class=&p&&;&/span& &span class=&nx&&i&/span&&span class=&o&&++&/span&&span class=&p&&)&/span& &span class=&p&&{&/span&
&span class=&kd&&var&/span& &span class=&nx&&module&/span& &span class=&o&&=&/span& &span class=&nx&&modules&/span&&span class=&p&&[&/span&&span class=&nx&&i&/span&&span class=&p&&];&/span&
&span class=&k&&if&/span& &span class=&p&&(&/span&&span class=&nx&&module&/span& &span class=&o&&&&&/span& &span class=&k&&typeof&/span& &span class=&nx&&module&/span&&span class=&p&&.&/span&&span class=&nx&&init&/span& &span class=&o&&===&/span& &span class=&s1&&'function'&/span&&span class=&p&&)&/span& &span class=&p&&{&/span&
&span class=&nx&&module&/span&&span class=&p&&.&/span&&span class=&nx&&init&/span&&span class=&p&&(&/span&&span class=&nx&&config&/span&&span class=&p&&);&/span&
&span class=&p&&}&/span& &span class=&k&&else&/span& &span class=&p&&{&/span&
&span class=&nx&&console&/span&&span class=&p&&.&/span&&span class=&nx&&warn&/span&&span class=&p&&(&/span&&span class=&s1&&'Module[%s] must be exports a init function.'&/span&&span class=&p&&,&/span& &span class=&nx&&entries&/span&&span class=&p&&[&/span&&span class=&nx&&i&/span&&span class=&p&&]);&/span&
&span class=&p&&}&/span&
&span class=&p&&}&/span&
&span class=&p&&});&/span&
&/code&&/pre&&/div&&p&注意模块路径中的 &b&MOD_ROOT&/b& 是提前在页面定义好的一个 seajs path。目的是为了把前端版本号更新的控制权释放给后端,从而解决了前后端依赖上线不同步造成的缓存延迟问题,配置脚本中只有几个定义好的路径:&/p&&div class=&highlight&&&pre&&code class=&language-js&&&span&&/span&&span class=&nx&&seajs&/span&&span class=&p&&.&/span&&span class=&nx&&config&/span&&span class=&p&&({&/span&
&span class=&nx&&paths&/span&&span class=&o&&:&/span& &span class=&p&&{&/span&
&span class=&s1&&'MISC'&/span& &span class=&o&&:&/span& &span class=&s1&&'//misc.360buyimg.com'&/span&&span class=&p&&,&/span&
&span class=&s1&&'MOD_ROOT'&/span& &span class=&o&&:&/span& &span class=&s1&&'//static.360buyimg.com/item/default/1.0.12/components'&/span&&span class=&p&&,&/span&
&span class=&s1&&'PLG_ROOT'&/span& &span class=&o&&:&/span& &span class=&s1&&'//static.360buyimg.com/item/default/1.0.12/components/common/plugins'&/span&&span class=&p&&,&/span&
&span class=&s1&&'JDF_UI'&/span&
&span class=&o&&:&/span& &span class=&s1&&'//misc.360buyimg.com/jdf/1.0.0/ui'&/span&&span class=&p&&,&/span&
&span class=&s1&&'JDF_UNIT'&/span& &span class=&o&&:&/span& &span class=&s1&&'//misc.360buyimg.com/jdf/1.0.0/unit'&/span&
&span class=&p&&}&/span&
&span class=&p&&});&/span&
&/code&&/pre&&/div&&p&还有一点,在测试环境的页面中版本号(上面代码中的 1.0.12 是一个&b&全量&/b&的版本号)是后端从 URL 上动态读取的(使用参数访问就可以命中对应版本 &a href=&https://link.zhihu.com/?target=http%3A//item.jd.com/sku.html%3Fversion%3D1.0.12& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&http://&/span&&span class=&visible&&item.jd.com/sku.html?&/span&&span class=&invisible&&version=1.0.12&/span&&span class=&ellipsis&&&/span&&/a&)。这样以来测试环境上就可以并行测试不同版本的需求,而且互不影响。当然如果不同版本的后端代码也有修改的话这样是不行的,因为后端代码也需要有个对应的版本号&/p&&p&不过我们已经解决了这个问题。后端会在测试环境里 &b&动态加载 &/b&后端模板 并且可以做到版本号与前端一致。这样以来配合 git 方便的分支策略就可以同时并行开发测试多个需求,不用单独配多个测试环境。什么?你还在使用 SVN!哦。那当我没说过&/p&&h4&事件处理模型&/h4&&p&客户端的 JavaScript 代码基本上都是事件驱动的,代码的加载解析依赖于浏览器提供的 DOM 事件。比如 onload, mouseover, scroll 等&/p&&p&事件驱动的的模型特别适用于异步编程,而 JavaScript 天生就是异步,所有的异步操作行为都最终会在一个回调函数(callback)中触发&/p&&p&比如单品页中价格接口,加载完成后需要更新 DOM 元素来展示实时价格;地区选择接口加载完成后会更新配送信息、库存/商品状态等,伪代码如下:&/p&&div class=&highlight&&&pre&&code class=&language-js&&&span&&/span&&span class=&cm&&/* onPriceReady 和 onAreaChange 可以认为都是一个 Ajax 异步函数调用&/span&
&span class=&cm&& * code 1 和 code 2 执行到的时间是不确定先后顺序的&/span&
&span class=&cm&& */&/span&
&span class=&cm&&/* prom.js */&/span&
&span class=&nx&&onPriceReady&/span&&span class=&p&&(&/span&&span class=&kd&&function&/span&&span class=&p&&(&/span&&span class=&nx&&price&/span&&span class=&p&&)&/span& &span class=&p&&{&/span&
&span class=&c1&&// code 1&/span&
&span class=&nx&&$&/span&&span class=&p&&(&/span&&span class=&s1&&'#price'&/span&&span class=&p&&).&/span&&span class=&nx&&html&/span&&span class=&p&&(&/span&&span class=&nx&&price&/span&&span class=&p&&);&/span&
&span class=&p&&});&/span&
&span class=&cm&&/* address.js */&/span&
&span class=&nx&&onAreaChange&/span&&span class=&p&&(&/span&&span class=&kd&&function&/span&&span class=&p&&(&/span&&span class=&nx&&area&/span&&span class=&p&&)&/span& &span class=&p&&{&/span&
&span class=&c1&&// code 2&/span&
&span class=&nx&&$&/span&&span class=&p&&(&/span&&span class=&s1&&'#stock'&/span&&span class=&p&&).&/span&&span class=&nx&&html&/span&&span class=&p&&(&/span&&span class=&nx&&area&/span&&span class=&p&&.&/span&&span class=&nx&&stockInfo&/span&&span class=&p&&);&/span&
&span class=&p&&});&/span&
&/code&&/pre&&/div&&p&上面的两段代码分别在两个脚本中维护,因为他们的逻辑相对独立。早期并没有关联关系。后来需求有变,他们之间需要共享一些对方的数据(切换地区后需要重新获取价格数据并展示)。但是物理上又不能放在一起通过使用全局变量的方式共享,而且它们都是异步加载接口后才取到数据的,并不好确定谁先谁后(非要做到那就只能用全局变量双向判断)。所以这样并不能很好的解决问题,而且代码的耦合度会成倍增加&/p&&p&这时候我们引入了一种设计模式来解决这种问题 —— &a href=&https://link.zhihu.com/?target=https%3A//zh.wikipedia.org/wiki/%25E5%258F%%25B8%%25AE%25A2%25E9%& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&发布者/订阅者&/a&,我们把这种模式抽象成了自定义事件代码来解决这一问题。这段代码是由 YUI 核心开发者 &a href=&https://link.zhihu.com/?target=https%3A//www.nczonline.net/blog//custom-events-in-javascript/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Nicholas C. Zakas&/a& 实现的。代码很简单,事件对象主要有两个方法 addListener(type, listener) 和fire(event)&/p&&p&于是我们重构了上面的伪代码:&/p&&div class=&highlight&&&pre&&code class=&language-js&&&span&&/span&&span class=&cm&&/* prom.js */&/span&
&span class=&c1&&// 在代码中注册一个地区变化事件,获取变化后的地区 id&/span&
&span class=&c1&&// 然后重新请求价格接口并展示&/span&
&span class=&nx&&Event&/span&&span class=&p&&.&/span&&span class=&nx&&addListener&/span&&span class=&p&&(&/span&&span class=&s1&&'onAreaChange'&/span&&span class=&p&&,&/span& &span class=&kd&&function&/span&&span class=&p&&(&/span&&span class=&nx&&data&/span&&span class=&p&&)&/span& &span class=&p&&{&/span&
&span class=&nx&&getAreaPrice&/span&&span class=&p&&(&/span&&span class=&nx&&data&/span&&span class=&p&&.&/span&&span class=&nx&&areaIds&/span&&span class=&p&&)&/span&
&span class=&p&&});&/span&
&span class=&nx&&onPriceReady&/span&&span class=&p&&(&/span&&span class=&kd&&function&/span&&span class=&p&&(&/span&&span class=&nx&&price&/span&&span class=&p&&)&/span& &span class=&p&&{&/span&
&span class=&nx&&$&/span&&span class=&p&&(&/span&&span class=&s1&&'#price'&/span&&span class=&p&&).&/span&&span class=&nx&&html&/span&&span class=&p&&(&/span&&span class=&nx&&price&/span&&span class=&p&&);&/span&
&span class=&nx&&Event&/span&&span class=&p&&.&/span&&span class=&nx&&fire&/span&&span class=&p&&({&/span&
&span class=&nx&&type&/span&&span class=&o&&:&/span& &span class=&s1&&'onPriceReady'&/span&&span class=&p&&,&/span&
&span class=&nx&&data&/span&&span class=&o&&:&/span& &span class=&s1&&'Any data you want'&/span&
&span class=&p&&})&/span&
&span class=&p&&});&/span&
&span class=&cm&&/* address.js */&/span&
&span class=&nx&&onAreaChange&/span&&span class=&p&&(&/span&&span class=&kd&&function&/span&&span class=&p&&(&/span&&span class=&nx&&area&/span&&span class=&p&&)&/span& &span class=&p&&{&/span&
&span class=&nx&&$&/span&&span class=&p&&(&/span&&span class=&s1&&'#stock'&/span&&span class=&p&&).&/span&&span class=&nx&&html&/span&&span class=&p&&(&/span&&span class=&nx&&area&/span&&span class=&p&&.&/span&&span class=&nx&&stockInfo&/span&&span class=&p&&);&/span&
&span class=&c1&&// 在地区变化后除了做自己该做的事情以外&/span&
&span class=&c1&&// 触发一个名为 onAreaChange 的事件,用来&/span&
&span class=&c1&&// 通知其它订阅者事件完成,并传递地区相关参数&/span&
&span class=&c1&&// 这个时候在 onAreaChange Ajax 回调函数&/span&
&span class=&c1&&// 中就只需要关心自己的逻辑,其它模块的耦合关系&/span&
&span class=&c1&&// 交给它们自己通过订阅事件来处理&/span&
&span class=&nx&&Event&/span&&span class=&p&&.&/span&&span class=&nx&&fire&/span&&span class=&p&&({&/span&
&span class=&nx&&type&/span&&span class=&o&&:&/span& &span class=&s1&&'onAreaChange'&/span&&span class=&p&&,&/span&
&span class=&nx&&data&/span&&span class=&o&&:&/span& &span class=&nx&&area&/span&&span class=&p&&.&/span&&span class=&nx&&ids&/span&
&span class=&p&&})&/span&
&span class=&p&&});&/span&
&/code&&/pre&&/div&&p&需要注意的一点是,&b&必须确保事件先注册后触发执行&/b&,也就是说先 addListener, 再 fire&/p&&h4&一些典型的性能优化点&/h4&&p&基本上客户端的 JavaScript 性能问题都来自于 DOM 查找和遍历,在用于的时候一定要小心,可能不经意的一个操作就会损失很多性能,尤其在低端浏览器中。顺便多说一点,现代的 JavaScript 解释器本身是很快的,语言层面的性能问题很少遇到。DOM 查找慢是因为 浏览器给 JavaScript 访问页面提供的一套 DOM API 本身慢&/p&&ol&&li&&p&缓存 DOM 查找,同时 DOM 查找不要超过 2000 个,低级浏览器会卡顿&/p&&/li&&li&&p&不要使用链式调用 find,如:find('li').find('a') 而是 find('li a')&/p&&/li&&li&&p&在切换元素显示状态的时候,如果元素很多。优先使用 show()/hide() 方法,而不是 css('display', 'block/none')前者有缓存,后者会强制触发 reflow&/p&&/li&&li&&p&给节点添加 data-xx 属性在存放一些数据,通过使用 jQuery 的 data('xx') 方法取更高效,减少 DOM 属性访问&/p&&/li&&li&&p&高密度事件(scroll, mousemove)触发场景请使用节流方法&/p&&/li&&li&&p&使用事件代理,而不是直接绑定。如果不确定代码被调用次数,可以先解除绑定再绑定具有命名空间的事件处理函数&/p&&/li&&li&&p&尽量少用 DOM 动画,使用 CSS 3 动画代替&/p&&/li&&/ol&&h2&前端工程化&/h2&&h3&原由&/h3&&p&前端工程化其实并不是最近两年才有的概念。大约在 2013 年的时候 Grunt 问世的时候就已经有所涉及。这类打包工具主要的目的是自动化一些开发流程,我最早使用 Grunt 来构建代码的时候只解决了三个问题:&/p&&ol&&li&&p&合并压缩优化样式脚本&/p&&/li&&li&&p&上线完自动备份&/p&&/li&&li&&p&单个文件打包到多目录(历史原因一个文件线上的路径有两种,需要传两个目录)&/p&&/li&&/ol&&p&当时我还在组内做过一个分享,有兴趣的可以去围观一下 &a href=&https://link.zhihu.com/?target=https%3A//speakerdeck.com/keelii/best-workflow-with-grunt& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Best Workflow With Grunt&/a&&/p&&p&其实这些工具出现的原因是:当时前端领域的各种基础设施很缺乏,而前端的工作内容又相对零散。工作时需要开很多的软件。再加上 JavaScript 语言本身也很弱,就连包管理这种基础的东西也没有内置,以至于模块化要通过一些第三方类库来实现,比如:RequireJS, SesJS&/p&&p&工具的重要性可以在我之前的一个分享中找到 &a href=&https://link.zhihu.com/?target=https%3A//speakerdeck.com/keelii/qian-duan-kai-fa-gong-ju& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&前端开发工具系列&/a&&/p&&h3&现状&/h3&&p&如今前端工程的生态环境由于 NodeJS 的出现已经变得很好了。你可以根据自己的需求选一个合适的直接用到项目里面。像 Grunt, Gulp, browserify, webpack 等。不过要明白这些工具的出现从另一方面证明了前端开发天生存在很多的问题:&/p&&ul&&li&&p&HTML 从诞生到 HTML 5 之前几乎没有任何变化,DOM 性能天生缺失。所以才有了 Virtual DOM 这种东西&/p&&/li&&li&&p&CSS 只是一门描述型的语言,没有变量、逻辑控制、语句。所以才出现了 Sass, Less 这种预编译工具&/p&&/li&&li&&p&JavaScript 号称「高阶的(high-level)、动态的(dynamic)、弱类型的(untyped)解释型(interpreted)编程语言,适合面向对象(oop)和函数式的(functional)编程风格」的编程语言,但是语言本身有很多问题(ES 6 之前)。不适合大型项目的开发、没有一些高级特性的支持、同时被其它语言诟病的 callback 风格、单线程执行等。所以才出现了像 TypeScript, Babel 这种编译成 JavaScript 代码的语言&/p&&/li&&/ul&&p&这些问题几乎都是历史性的原因和兼容性因素造成的。作为一名好的前端工程师要看清楚现状,然后按自己项目的需求去定制一些前端工程化的方案,而不是随波逐流。&/p&&h3&选择&/h3&&p&其实现在自己开发一套前端工程化/自动化流程的成本已经很低了,你只需要学习一些 NodeJS 的知识,配合 NPM 包管理机制,随手就搞出一个构建工具出来。因为并不需要你去实现什么东西,所有的东西都有现成的包。脚本压缩有 UglifyJS,CSS 优化有 CSS-min,图片压缩优化有 PNG-quant 等等。你只需要想清楚自己要达到什么目的,解决什么问题就可以抄家伙自己写一套工作流出来&/p&&p&我自己的经历也从 Grunt, GulpJS 到现在自造轮子。自己根据需求开发出来一套集成的打包工具,有兴趣的可以去围观一下 &a href=&https://link.zhihu.com/?target=https%3A//github.com/keelii/wo& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Wooo&/a&&/p&&p&当然你也可以不用任何打包工具,自己写一些 NPM Script 来完全定制化项目开发/测试/打包流程。我猜这也是为什么现在类似 Grunt 不再那么火,Gulp 迟迟没有发布 4.0 版本的原因。写一个构建工具的成本太低了,而且这种集成的工具很难满足差异的开发需求。君不知已有人意识到了这一点么&a href=&https://link.zhihu.com/?target=https%3A//medium.freecodecamp.com/why-i-left-gulp-and-grunt-for-npm-scripts-3d& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&why-i-left-gulp-and-grunt-for-npm-scripts&/a&&/p&&h2&程序、设计、产品&/h2&&p&我始终认为程序、设计是为了产品服务的。好的产品是要重视设计的,好的(前端)工程师是要有一些审美素养&/p&&p&其实很多时候技术解决方案都是要根据产品的定位来设计的,了解产品需求以后才能定制出真正合适的高效的解决方案。好比前面讲到的那个 sprite 案例,如果一开始就和产品讨论好方案后来也不可能有那种失控的情况发生。在产品形成/上线前期能发现问题比上线后发现问题更容易解决&/p&&p&这部分内容和代码无关,就不多说了。然而早年我还有一次分享关于&a href=&https://link.zhihu.com/?target=https%3A//speakerdeck.com/keelii/qian-duan-%2Cgai-bian-front%255Bnil%255Dend-makes-change& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&前端、改变&/a&&/p&&h2&总结&/h2&&p&关于单品页的前端开发本篇文章只是冰山一角,还有很多没有提及,每个小东西都可以单独写一篇文章来分享。随后希望可以有更多的总结和分享&/p&
原文:简介详情页也叫做单品页,域名以「」为格式的页面。是负责展示京东商品 SKU 的落地页面。主要任务是展示和商品相关的信息,如:价格、促销、库存、推荐,从而引导用户进入购买流程。同时单品页有很多版本。…
&figure&&img src=&https://pic2.zhimg.com/v2-adf86c39b16f1ee6de1c65_b.jpg& data-rawwidth=&630& data-rawheight=&404& class=&origin_image zh-lightbox-thumb& width=&630& data-original=&https://pic2.zhimg.com/v2-adf86c39b16f1ee6de1c65_r.jpg&&&/figure&&p&即时聊天(IM)系统需要解决消息可靠性及消息一致性问题。&/p&&p&消息可靠性,简单来说就是不丢消息,会话一方发送消息,消息成功到达对方并正确显示;消息一致性,包括发送一方消息一致及会话双方消息一致,要求消息不重复,不乱序。&/p&&h2&消息发送实现过程&/h2&&p&消息发送一般的实现过程可以分为两个阶段:发送方发送消息,服务端接收,返回消息 ACK 给发送方;服务端将消息推送到接收方。判断消息发送是否成功主要依据第一阶段,即服务器是否接受到消息,消息状态可以分为三类:正在发送、发送成功、发送失败。其节点分别是:&/p&&p&1. 正在发送:发送方触发发送事件开始,到收到服务端返回消息对应 ACK 之前;&/p&&p&2. 发送成功:发送方收到消息对应 ACK 回复;&/p&&p&3. 发送失败:超过一定重发次数,未收到消息对应 ACK 回复。&/p&&p&消息发送流程图:&/p&&figure&&img src=&https://pic4.zhimg.com/v2-0fb7c380b46b6bfaf9cc7_b.jpg& data-rawwidth=&1310& data-rawheight=&806& class=&origin_image zh-lightbox-thumb& width=&1310& data-original=&https://pic4.zhimg.com/v2-0fb7c380b46b6bfaf9cc7_r.jpg&&&/figure&&h2&消息可靠性&/h2&&p&&b&重发机制&/b&&/p&&p&保证消息发送第一阶段消息成功发送的方法是设立重发机制,依据一定时长内是否收到消息对应 ACK,判断消息是否要重发,如果超过预设时长,就重新发送。当重发次数超过预设次数,就不再重发,判定该消息发送失败,修改消息发送状态。&/p&&p&&b&会话记录检查&/b&&/p&&p&消息发送第二阶段服务端推送消息到接收方,如果连接断开,会丢失消息,所以要保证消息完整,就需要在建立连接后,根据上一条消息(已经 ACK)时间戳,获取会话记录,一次返回一段时间内所有消息。&/p&&p&另一种保证方法是加入定时轮询,检查消息完整性。&/p&&p&建立连接流程图:&/p&&figure&&img src=&https://pic4.zhimg.com/v2-d6cde0bd642a_b.jpg& data-rawwidth=&1316& data-rawheight=&970& class=&origin_image zh-lightbox-thumb& width=&1316& data-original=&https://pic4.zhimg.com/v2-d6cde0bd642a_r.jpg&&&/figure&&p&&b&两个问题&/b&&/p&&p&消息重发、会话记录检查需要考虑两个问题:消息是否会重复发送,消息顺序是否会被打乱。举两个例子:&/p&&p&1. 消息重发,如果丢消息的点在消息达到服务端之前,服务端并没有收到消息,发送方重新发送丢失消息,服务端接收成功,不会产生两条相同消息;而如果服务端接收到消息,返回 ACK 丢失,这时再发送一次相同消息,就可能造成消息重复。&/p&&p&2. 消息顺序,如果发送方连发三条消息,第一、第三条成功被服务端接收,第二条丢了,那第三条消息是否会被记录?如果这时第二条消息达到服务端,其顺序是在第三条时间之前还是之后(服务端一般都会给记录打一个时间戳)?&/p&&h2&消息一致性&/h2&&p&&b&使用 uuid 消息去重&/b&&/p&&p&对于消息重发问题,可以给每条消息增加属性 uuid 作为消息唯一标识,重发消息 uuid 不变,前端根据 uuid 去重。&/p&&p&&b&使用向量时钟进行消息排序&/b&&/p&&p&对于消息排序问题,因为在聊天中,消息的顺序对于发送方的表述有重要的影响,消息不完整或顺序颠倒都可能造成语意不连贯,甚至曲解。所以需要保证发送方发送消息顺序,而会话双方消息排序需要考虑实际情况。&/p&&p&在一般的认知里,状态是正在发送的消息,应该还没有被对方看到,只有发送成功的消息,才会被对方看到。但在实现中,消息发送成功是以服务器接收消息并返回 ACK 成功为判断依据,而不是被对方接收到。&/p&&p&那么就会出现这样一个问题,如果一条消息状态是正在发送,此时收到一条消息,那么收到的消息是在正在发送的消息之前还是之后?&/p&&p&这是一个上下文关系,关键问题是发送方是以哪条所见消息为依据发送消息的。&/p&&p&这里提供一种思路,借鉴分布式系统中的向量时钟算法。先简单描述向量时钟算法:&/p&&p&向量时钟算法用于在分布式系统中生成事件偏序关系,并纠正因果关系。一个系统包含 N 个节点,每个节点产生的消息体中包含该节点的逻辑时钟,整体系统的向量时钟由 N 维逻辑时钟组成,并在每个节点产生的消息体中传递。&/p&&p&向量时钟算法的具体实现:&/p&&p&1. 初始状态,向量值为 0;&/p&&p&2. 每次节点处理完节点事件,该节点时钟加一;&/p&&p&3. 每次节点发送消息,将包含自身时钟的系统向量时钟一起发送;&/p&&p&4. 每次节点收到消息,更新系统向量时钟,该节点时钟加一,其他节点对比每个节点本地保留的向量时钟的值和消息体中向量时钟的值,取最大值。&/p&&p&5. 节点同时收到多条消息,判断接收消息的向量时钟之间是否存在偏序关系&/p&&p&
1. 如果存在偏序关系,则合并向量时钟,取偏序较大的向量时钟;&/p&&p&
如果不存在偏序关系,则不能合并。&/p&&ul&&li&偏序关系:如果 A 向量中的每一维都大于等于 B 向量,则 A、B 之间存在偏序关系,否则不存在偏序关系。&/li&&/ul&&p&对于消息排序,其实就是处理消息的上下文语境,决定消息之间的因果关系。参考向量时钟算法,假设有 N 个消息会话方,系统的向量时钟由 N 维时钟组成,向量时钟在各方发送的消息体中传递,并依据向量时钟排序,具体实现:&/p&&p&1. 系统向量时钟设为 (0, 0, …, N);&/p&&p&2. 节点发送消息,更新系统向量时钟,该节点时钟加一,其他节点不变;&/p&&p&3. 节点接收消息,更新系统向量时钟,该节点时钟加一;其他节点对比每个节点本地保留的向量时钟的值和消息中向量时钟的值,取最大值。&/p&&p&4. 依据消息体内系统向量时钟的偏序关系决定消息顺序:&/p&&p&
1. 如果可以确定偏序关系,则根据偏序关系由小到大显示;&/p&&p&
2. 如果多条消息不能确定偏序关系,则按照自然顺序(接收到的顺序)显示。&/p&&p&向量时钟在理论上可以解决大部分消息一致性的问题,但在实现中还需要考虑实际使用时的体验,这其中最需要关注的问题是:是否要强制排序,或者说,如果实际显示顺序和向量时钟之间的偏序关系不一致,是否要移动消息之间的顺序。&/p&&p&举个例子,在一个有多人的会话中,如果有一方网速特别慢,收不到消息,也发不出消息。在他看到的最后的消息之后,其他人已经开始新的话题,这时他关于上一个话题的消息终于发送成功,并被其他人收到,此时就存在这样一个问题:&/p&&p&这条关于上一个话题的消息是显示在最后,还是移到较早时间?如果显示在最后,但消息内容和目前的话题不相关,其他人可能会感到莫名其妙;如果把消息移到较早时间,那么这条消息可能不会被其他人看到,或者看到前面多了一条消息,会有种突兀的感觉。&/p&&p&IM 的场景很多,也很复杂,更多的时候需要从产品角度考虑问题。对于消息是否需要排序的问题,这里只提出一个比较通用的方案:建议会话中不强制排序,会话历史记录中按照向量时钟的偏序关系进行排序。&/p&&h2&小结&/h2&&p&对于 IM 系统消息可靠性及一致性问题,通

我要回帖

更多关于 偏序集构成格 的文章

 

随机推荐