首页

六、HTTP协议中的缓存

seo达人

一、缓存简介

缓存无处不在,浏览器端的缓存、服务器端的缓存、代理服务器的缓存、对象缓存、数据库也有缓存……HTTP中具有缓存功能的是浏览器缓存和代理服务器缓存。下面说一下缓存的优点:



1.减少冗余的数据传输,节省了传输时间



2.减少服务器的负担,大大提高了网站的性能



3.加快了客户端加载网页的速度



二、使用Fiddler如何查看缓存的Header

与缓存相关的内容放在Header中的Cache项里;Requests和Responses均是这样。如图:







三、缓存的新鲜度(通过什么方式判断其是否,如何刷新缓存)

Web服务器通过两种方式判断浏览器缓存是否:



(1)浏览器把缓存文件的最后修改时间通过Header“If-Modified-Since”项传递给Web服务器。



(2)浏览器把缓存文件的ETag通过Header“If-None-Match”项传递给Web服务器。



简单描述一下,浏览器端想请求一个文档,它首先检查本地缓存,发现存在这个文档的缓存,获取缓存文档的最后修改时间,发送请求,将值放在Header中的“If-Modified-Since”项传递给Web服务器。Web服务器接收到请求后,将服务器的文档修改时间(Last-Modified)与HTTP请求中的header中的“If-Modified-Since”值相比较,有两种情况,如下面图示:



1.缓存有效:







2.缓存无效:







截图举例:







上面说的是If-Modified-Since值对比的方式;下面说一下ETa'g;



四、ETag

ETag(Entity Tag):实体标签;是根据实体内容生成的一段hash字符串(类似MD5之后的结果),可以用来标识资源的状态(当资源发送变化时,ETag也会跟着改变);ETag是Web服务端产生的,然后通过response响应给浏览器。使用ETag,主要解决Last-Modified无法解决的情况:



(1.某些服务器不能得到文件的If-Modified-Since来判断文件是否更新;



(2.某些文件修改非常频繁,以小于秒的单元进行修改,而Last-Modified最单元为秒;



(3.有些文件最后修改时间变化了,但是内容并没有变化,所以我们不希望浏览器以为文件已变化了;







五、与缓存相关的header

字段名称 释义

Cache-Control:max-age=0 以秒为单位

If-Modified-Since:Fri, 11 Jan 2019 01:55:04 GMT 缓存文件的最后修改时间

If-None-Match:W/"5c37f778-732" 缓存文件的Etag的值

Cache-Control:no-cache 不使用缓存

Pragma:no-cache 不使用缓存

Cache-Control:public 响应被缓存,可以共享与多用户

Cache-Control:private 响应只能私有缓存,不可共享

Cache-Control:no-store 绝对禁止缓存(机密、敏感文件)

Cache-Control:max-age=60 60秒后缓存过期(相对时间)

Date:Fri, 11 Jan 2019 01:55:04 GMT 当前发送时间

Expires:Fri, 11 Jan 2019 01:55:04 GMT 缓存过期时间设置(绝对时间)

Last-Modified:Fri, 11 Jan 2019 01:55:04 GMT 服务器端文件的最后修改时间

Etag:W/"5c37f778-732" 服务器端文件的Etag值

六、关于缓存的常识

1.如何让浏览器不使用缓存?



答:【Ctrl+F5】快捷键强制刷新浏览器,让浏览器不使用缓存;此时Fiddler抓包所看到的请求Header中都会带有Cache-Control:no-cache;







2.如何让浏览器直接使用缓存,不请求服务器进行缓存验证?



答:第一次访问与第二次访问同一个网页;(注意:第二次访问时,输入网址,直接回车)浏览器会直接使用有效的缓存,而不会发生HTTP请求去服务器验证缓存,这种情况称之为缓存命中;



使用Fiddler抓包,现象:第一次访问,有70多个Session;第二次访问,仅40多个请求;



3.浏览器均可在设置中设置不使用缓存;



4.公有缓存、私有缓存的区别:一个可以多个不同的客户端使用,一个只可唯一的客户端使用;


蓝蓝设计www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计、BS界面设计  cs界面设计  ipad界面设计  包装设计  图标定制  用户体验 、交互设计、 网站建设 平面设计服务

淘票票9.0改版背后的设计思考与体验度量

雪涛

Sandy现任阿里影业—淘票票体验设计专家,2015年加入阿里巴巴,深耕B端行业产品,服务于电影产业链中的投资、宣发等角色。2017年起接触C端用户产品,推行价值导向和问题导向。2019年开始实践线上线下全链路设计。

 

活动笔记:

阿里影业的服务涉及的面很广,涉及到b端与c端全流程的体验服务,包括面对片方的制作和宣发、发行、乃至面向用户的售卖与放映,在每个节点都有涉及。而淘票票,经历了四年的产品迭代,以一年一个版本的速度的进行优化。15年的理念是做一个好用的购票工具,16年新增了营销,17年加强营销,18年新赛道探索。到了2019年,改版应该进行新的思考:怎样做?做什么?怎么做?做对了吗?

此次淘票票9.0版本是根据用户现有的习惯与市场的变化,由设计师发起的一次自下而上推进的改版。以下是淘票票9.0的设计策略与设计目标:

  1. 内容设计赋能
    新服务,扩展观影决策节点,帮助用户发现好电影。
  2. 购票流程体验打磨
    新体验,解读用户行为,体验走查,打磨核心购票链路体验。
  3. 年轻化
    新用户,关注用户群体的变化,搜索年轻用户群的品牌认知,提升年轻用户满意度。

接下来将对三点设计策略进行逐一的讲解。

 

1. 新服务—内容的赋能

根据内容类型和场景进行划分,结合内容特点和用户喜好,打造全场景运营,例如提供影讯、通稿、片单、榜单、热点、解读、文章和活动等等多元化内容。通过提供不同的内容展现给用户,将内容进行解构、把触达的场景进行细化、优化设计的表达,从而达到帮助用户可以更好的理解电影的目的。

 

2. 新体验—打磨购票流程的体验设计

设计前,首先应当熟知两种设计思维导向:

  • A.当设计目标是帮助企业完成商业目标时,是以价值为导向。
  • B.当设计目标是为了提升用户体验,则是以思维为导向、问题为导向。

而这次9.0淘票票改版采用的是以问题为导向,期间经历了五个流程:

2.1 找问题

首先出去找问题,找问题的方法有很多,如:用户研究、定性、定量、业务数据和体验走查,收集业务、用户、客满不同视角的疑似问题。

  • 会通过支付宝、淘宝、百度糯米来比较价格高低,对比价格需要反复推出
  • 进入不同的影院和场次,不太方便
  • 希望看过的电影能够产生一些纪念价值
  • 不清楚80元和40元的座位有什么区别
  • 不明确片尾是否有彩蛋
  • 购票平台的评分对自己没有参考价值
  • 电影院的地址、停车场是否免费等信息不够准确
  • 买完票到取票之间,需要反复确认订单信息、分享订单信息,但是订单入口
  • 藏得有些深

2.2 看现象

找完问题之后,基于数据的支撑,去看用户有哪些习惯的变化,看到目前的现象后再进行数据解读。

  • 影院页和选座页返回率高
  • 越来越多的用户购买当日的电影票
  • 多数用户选择更短的买票路径
  • 大多数用户去过的电影院在3家以内

2.3 定位问题

基于使用场景和使用效率,进一步定位问题所在。

2.4 分析原因

分析出症结,以便推进最终的解题环节。

  • 症结1:平台服务与用户决策心理脱节
    现有的流程是线状的:选影片-选影院-选场次-选座-下单。但是用户实际的心理决策是网状的,用户可能在选时间的时候考虑影评好不好,会在选场次的时候考虑价格是不是合理的。
  • 症结2:认知过载
    理想状态是在传达信息后,在用户感知、接收和决策后,希望得到用户方的正反馈,但是现实往往是用户认为认知负担过重,反馈失效的情况。其中信息传达、颜色的设置最直观的感受是“乱”。决策流程上最直观的感受是“打扰”,提醒用户是否确定要买某时候的票、提醒用户确定退出选座页不保留刚才选的座位,所有的提示的设计,都是采用一种打扰的方式进行询问的。

 

2.5 解题

解题1:场景化探索。

以解决问题为目标,达到优化用户体验的目的,对场景进行预判、探索,把场景分为三个典型的场景:

  • 典型场景1:短时决策–例如距离开场时间较短,对价格不敏感,希望找到距离合适的影院,快速完成购票决定。
  • 典型场景2:追求体验–对影厅的要求较高,希望找到放映效果最好的影厅。
  • 典型场景3:价格敏感–找到符合自己价格预期的影院。

解决:针对第一种场景,选坐页可以快速找到选场次的功能,淘票票提供常去影院、附近影院的选择,减少用户决策时间。针对第二种场景,部分观影者不知道价格更高的IMAX厅、杜比厅的观影效果,价格比普通厅贵了50块钱,那么这个钱值在哪?淘票票使用视觉映射和科普的手段,例如当点击进入杜比厅后,下拉可以呼出信息,了解相关的影厅,给予科普;而且界面设计不同,更贵的影厅视觉效果好,界面上也提供用户更强的视觉冲击。

 

解题2:用户视角信息重构,进行信息降噪,减少认知负担

认知负担=信息呈现类型x信息量

以上公式可以看出,假设设认知负担为定值,当信息量增多的时候,需要减少信息呈现类型,适当进行信息降噪与信息结构化。降噪是把想要突出的信息更加突出;信息结构化是把同类型的信息以结构化的呈现出来,让用户自然对信息产生亲密性。

对于信息传达,改变之前比较打扰的提示弹窗,现在淘票票会把所有信息都放在页面中,用一种更轻量的方式提示用户,不再打扰用户。信息重构则是把需要用户确认的信息放在最头部,例如退票、改票,其次界面罗列的是优惠信息,最后才是影城卡营销和卖小食的信息区域。新旧改版对比图很好呈现出淘票票有效减少认知负担所做的优化。

 

3. 新用户—提升年轻用户群对品牌认知

基于调研,淘票票的用户群体趋近年轻化。改版中所制作情绪版、图标、元素、字号、空间结构等视觉语言,注重和品牌元素的结合,产生出新的视觉语言与品牌形象,从而更加贴近年轻人的心理与喜好。

 

4. 体验度量

根据heart模型进行设置,选出适合9.0版本的衡量维度:S,T,A。以体验目标出发,符合业务目标进行探索。不一样的体验目标使用不一样的度量方法。对于内容而言,需要衡量的是用户的接受度,用户需要看到它,接受它,并且需要知道用户是否觉得有用。而对于核心购票流程,该流程则是比较偏向工具,度量方式则是任务完成度、完成任务时所花费的时长、信息有没有被有效的传达给用户等来衡量的。最后对于视觉方向,用户的想法会比较主观,通过满意度和推荐度来衡量。

 

 

 

现场问答:

提问者1:观影者通常会在朋友圈、豆瓣和推荐来决定要看的影片,基本不会在淘票票上寻找值得看的影片,为什么淘票票会做这方面的探索?为什么要做内容决策这件事?

回答:从想看电影到下单的过程哪个地方淘票票是没有做好的、做到的,根据定性调研,发现一半以上用户都不会在淘票票上进行决策。但是淘票票还是希望尝试一下,希望用户可以在淘票票上完成完整的购票观影体验。从数据显示,用户心智还是不好但是有一些提升。而且豆瓣经历了10年从pc到app,保留了用户的历史等数据,没有办法让用户直接转到淘票票进行观影决策,这也不是淘票票希望看到的。淘票票也希望可以和竞品和合作方去提升观影决策,达到共赢的目的。

 

提问者2:淘票票改版之后,有一个衡量它的改版效果S、T、A的度,有没有考虑要做NPS?

回答:淘票票一直有在做,从这次改版之后来,淘票票所有用户群指标提升了应该有五个百分点,然后年轻用户的百分点在八个以上。NPS是一个非常关键的衡量用户满意度的指标。

追问:NPS是不是适合淘票票这个产品?还是适合于所以互联网产品?

回答:NPS在集团内它的重视度是很高,基本上阿里系所有的产品都会有指标。

 

提问者3:我之前是淘票票的用户,曾经用过淘票票做观影决策,看下面的电影评价,结果发现电影评价是虚高的,就是说其实电影没有那么好,但是评论会倾向性的选择一个好的评论放在下面,经过这次经验之后,就再也不用淘票票作为观影决策了。想了解现在淘票票的评论机制,它是怎么个呈现的方式?是不是会优先选择就是比较好的评论放在首评?或者是有一些什么样的计算方式?另一方面对于现在的评论失真的这种情况,有没有想到一些改进的措施?是最近有没有做过一些改版之类的,就是关于这种内容方面的?除了刚才讲的那些界面,视觉方面的,想了解内容方面有没有一些提升?

回答:第一点,用户群的不同。对于影评这个来决策,淘票票可能跟豆瓣用户群有非常大的差异,豆瓣是影迷聚集地,爱电影这帮人的一个粉丝聚集地,圈子比较小,想要进入这个圈子有一定的成本。但是像淘票票它服务的是大众场景,服务的有外卖小哥,也有大城市去打工的用户,也有三四线在国企里那种的员工,淘票票服务的用户是大众的,所以对于评分虚高这件事,不是说它好或者不好,也就是不能单纯的绝对说好还是不好,可能大众的心智就是这样的。用户不像一个资深影迷,看到速度与激情会认为是大烂片,可能反而觉得好,非常值得去看。第二点,关于这种影评的一个分发策略涉及到产品策略,不是很方便讲。但是淘票票在这方面一直有优化的,并且现在也是在持续优化,希望影评可以真正为用户去提供这个观影决策。第三点,淘票票的用户其实不只是c端用户,它还有还有影院还有片方,但是水军应该不会有,至少淘票票平台是非常不鼓励这种情况,而且会有一定的反作弊、返水的机制。

 

提问者4:因为是以设计推动的一次改版,想了解一下推动的过程?第一就是因为平时改版都是产品的来做的,那这一次由设计来去推动的话,那设计跟产品之间的这个协作关系是什么样的?然后改版历时半年,是淘票票的设计历程中是常态吗?如果不是的话,平常的这个改版的节奏是什么样的?

回答:第一点,设计要不断的去跟产品、运营沟通,去跟不同角色沟通,沟通可能是最重要的一点。当所有人都达成共识了,确实有这样的问题需要改,那全部门所有人就会去团结,去把这个事情搞出来。搞出来之后再去向上一层一层的去向上汇报。汇报的可能要经过很多轮,不断有反馈的意见下来,因为本身视角输入的也不够全,接受到的声音也没有上游接触的多,所以团队会去接受意见,然后重新的进行。一轮review下来大概三四个月的时间,然后再去跟开发团队沟通为什么要去升级设计语言,怎么样去帮开发提效?怎么在下一次10.0改版的时候更容易。第二点,团队第一次历时半年进行改版,之前没有停下脚步来去深耕用户体验,所以有一些坑或者一些弯路。平常的改版中基本上是两周发一个版本,非常小步快跑的。对于设计如何跟产品团队去协作?刚刚也有讲过,达成共识之后,然后把这个事儿做起来,提需求、进版本,从需求池里评估需求优先级进版本。交互设计跟产品经理这个角色没有绝对的一个边界,可能都是去不断的去触碰,只要去配合合作的好,把这个产品去做出来,不用管处在一个什么样的一个岗位。

 

提问者5:最开始的时候有问到淘票票和猫眼之间的一些对比,想了解在改版完之后和平时的工作中,是怎么会去了解和竞品?怎么去比较?有没有一些关注的量化的一些指标?

回答:不管服务于什么样的产品,都会做竞品调研。会关注市场的变化、竞品的变化。对于设计团队来说,其实主要的是关注的是用户行为、功能和视觉界面。包括上了哪些个新的功能?在不同的渠道是怎么样去运作的?运营思维是什么样子的?淘票票团队有在研究竞品,有在做竞品的一个分析,衡量的指标也主要是满意度、推荐度,因为没有办法去看竞品的数据,只能通过用户反馈去看竞品跟淘票票的差距。

 文章来源:uxren

JS中钩子函数与回调函数的区别

seo达人

测试代码

<!DOCTYPE html>

<html lang="en">

<head>

  <meta charset="UTF-8">

  <title>Title</title>

</head>

<body>

    <button id="btn">按钮</button>

    <script>

        let btn = document.getElementById("btn");

        btn.onclick = () => { // 钩子函数

            console.log('我是钩子函数');

        }

 

        btn.addEventListener("click",() =>{ // 回调函数

            console.log('我是回调函数');

        });

        // 这是给btn绑定了一个监听器,后面那个函数是它的回调函数,

        // 因为消息捕获的过程我们并不能参与,而在捕获执行完毕的时候,

        // 回调函数才会执行,我们可以把对消息的处理写在回调函数里。

    </script>

</body>

</html>

点击按钮后代码输出如下:







回调函数和钩子函数的区别根本上是

钩子函数在捕获消息的第一时间就执行,而回调函数是捕获结束时,最后一个被执行的。



什么是钩子函数

一般认为,钩子函数就是回调函数的一种,其实还是有差异的,差异地方就是:触发的时机不同。

钩子(Hook)概念源于Windows的消息处理机制,通过设置钩子,应用程序对所有消息事件进行拦截,然后执行钩子函数。 



什么是回调函数

回调函数其实是调用者将回调函数的指针传递给了调用函数,当调用函数执行完毕后,通过函数指针来调用回调函数。



钩子函数和回调函数都是事件处理函数


蓝蓝设计www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计、BS界面设计  cs界面设计  ipad界面设计  包装设计  图标定制  用户体验 、交互设计、 网站建设 平面设计服务

需求评审后,产品经理要干的6件事

雪涛

有些产品经理会陷入这种误区——需求评审做完了,自己就可以放羊不管了。而本文则认为需求评审完,产品经理还要做这六件事。

需求评审完,产品经理还要干的6件事

1. 确认需求评审的遗留问题并同步各方

2. 制定详细&责任到人的项目计划

3. 完成文案设计

4. 按照项目计划,协同各方,往前推进,关键环节必须与各方确认。关键环节包括:

  • 1)交互评审
  • 2)视觉评审
  • 3)推进联调进度
  • 4)推进测试进度
  • 5)项目showcase
  • 6)项目发布

5. 准备项目review

6. 开始下个需求的方案设计和需求文档准备

这六件事具体怎么做?

产品经理A:需求终于评审完了。有种放飞的感觉,可以休假,去浪了!

产品经理B:你说真的吗?为什么我评审完,还一直在被开发、测试、法务、财务穷追不舍?

产品经理C:你说真的吗?为什么我评审完,从来都是我在穷追不舍开发、测试、法务、财务?

产品经理D:你们开玩笑的吧?就我这么惨!我不但要紧追不舍开发、法务、测试、法务、财务,还要被老板、被客户穷追不舍。

产品经理A、B、C:哥们,来讲讲,最喜欢听惨兮兮的故事了。你的伤痛最能抚平我的内心。

产品经理D:好吧。需求评审只讲清楚了产品的骨架、细节,让各方开始投资源。评审完,产品经理还有一堆事要推进,没法放羊。

要跟的事情主要有下面6件:

1. 确认需求评审的遗留问题并同步各方

需求评审总有一些遗留问题要进一步确认,而后同步给各方。我不是圣人,有时候有些问题或者细节没想到,评审的时候,大家提出来了,得赶快明确。

有时候需求评审中还有很大的bug没想到,必须快速解决,要在开发没动工前,都捋顺。要不然变成需求变更,或者上线后被推倒重来,欲哭无泪。

我这种求生欲这么强,也没人罩着的,必须狠命把需求做到95分以上。100分也不太敢说,毕竟众口难调。

2. 制定详细&责任到人的项目计划

产品经理还得身兼项目管理,项目管理从来都是事有轻重、事无巨细,难以假手他人。虽然我会尽可能调动大家的积极性,让大家自驱管理项目,但还得牵扯不少精力。

项目管理的关键点:明确项目计划、关键节点、每个关键节点的负责人、验收方案。

比如什么时候交互评审、视觉评审、联调、showcase、发布?分别是谁主要牵头负责,哪些人需要参与。

为了防止项目延期,每个节点都还得提前赶。真是操碎了心。

3. 完成文案设计

文案从来不是随便写写。文案是和客户交流的重要途径,整几个客户看不懂的文案上去,后面客户咨询搞死人!

文案设计除了客户视角之外,也不是自己想怎么写就怎么写,还要和法务、客服团队沟通。因为文案被客户投诉的案例,又不是没有。

还有啊,我的产品有3种语言,简体中文、繁体中文、英文,虽然每种语言有专门的文案设计师,但得跟他们说清楚,也要花不少时间、精力。

当然,也有很多产品经理,不管文案这种小事。可我觉得文案体现了产品经理最基本的素养,是产品的底子

4. 按照项目计划,协同各方,往前推进,关键环节必须确认

关键环节有6个:

1)交互评审

一般来说会由交互设计师发起,开发、测试、法务、财务都要参与。

这样能保证大家在说同一件事情,避免我要的是头牛,结果开发给了头驴。

如果设计师项目参与度低,交互评审还得我自己上。哪里缺人,我就得到哪补坑。

2)视觉评审

一般来说,交互和视觉评审会一起。

有时候项目很复杂,或者交互、视觉分工明确,那就得分开了。

通常由视觉设计师发起。同样,如果视觉设计师参与度低的话,我还是得补坑。

3)推进联调进度

联调是很容易扯皮的环节,大家来自不同域、不同职能团队,各有各的小九九,所以得盯着,避免联调成为坑王。

4)推进测试进度

进入到测试就意味着开发的七七八八了,当然有时候为了压缩项目周期,开发、测试会阶段性并行。

除了测试进度,还得关注测试发现的问题,可能开发还得返工,也可能会发现需求评审中大家都没有注意到的问题,得及时补救。

5)showcase

Showcase,说白了就是项目验收。

验收前,得先列出来要验收哪些内容,主流程、分支流程、逆向流程、重大关键节点。Showcase,也有可能发现新的问题,但基本上要避免在showcase环节发现重大问题,不然就得重大需求变更了。

showcase有时候由测试主导,有时候没资源,我得自己上。

6)项目发布

如果一路顺利,就该发布项目了。

项目发布计划虽然也是之前就定好的,但要考虑的方方面面也还挺多的,可以看之前的文章《项目发布要考虑的因素》。

总而言之,要和各方沟通好,要保证项目顺利发布呦。

5. 准备项目review

项目终于上线了,可我得天天得看客户反馈,看数据,跟客户聊,跟业务聊,准备复盘review。

产品狗似乎永远都在准备复盘、复盘中、复盘后反思的路上。

6. 开始下个需求的方案设计和需求文档

项目通常是并行的。在需求评审完后,我已经开始下一个需求的研究、设计了。

开发资源从上一个需求释放出来的时候,产品经理肯定得把下一个需求方案设计好,开始新的需求评审,妥妥的做好资源衔接。资源一旦释放出来,下次想要资源,难上加难啊。

产品也需要持续迭代,让客户感受到,我们的产品在成长、进步,给人希望。

文章来源:人人都是产品经理

以人为中心的设计指南(一)

分享达人

前言


《以人为中心的设计指南》是我翻译的第二本完整的设计书籍。在我开始广泛的阅览国外优秀的设计团队、设计公司的沉淀输出后,收获颇丰。我意识到很多年轻的设计师尤其是传统意义上的UI,苦于进阶无门,很难系统的去学习设计理论、设计方法以及设计流程,无法系统化梳理设计知识,就只能在有限的设计范围内修修补补,就像你在局限于视觉时没有意识到体验,在忠实于体验时又忽略了商业,总是无法全面的考虑和输出设计方案,这是源于没有系统化的学习。


国外的人机交互、体验设计领域的确发展较早,有着大量的理论和方法的沉淀,国内各大设计团队虽然也开始持续输出一些本土化的内容,但仍然摆脱不了系统化这个问题。因此我从今年开始,就有了翻译国外设计论论体系及对应的成熟可用的设计方法的冲动。如果你读了我往期的文章,你应该了解到了设计思维的五个步骤、了解了用户体验的七要素、可用性方法以及众多可操作的实践方法。


我翻译的内容既有各大设计机构无私分享的内容,也有我自费购买的设计课程,我希望为大家带来一些真正持续影响设计生涯的内容,要实现这一目标,软件技法做不到,只有思维方法、系统化知识可以做到。


本次翻译的《以人为中心的设计指南》是IDEO出产的一本设计工具书,包括具体的设计思路,规范的设计流程,以及各种关键设计原则。它将结合实例,教授设计师如何解决问题,如何以人为本的解决复杂问题。


本书内容量较大,共有四大模块,及73个小章节,我将以连载的形式发布译文,完整翻译后,为大家排版一份PDF格式的电子书,分享给读者,也分享给国内的所有设计师,每个人都可以尽一份力让国内的涉及环境变得更好。


本篇文章主要翻译介绍部分和第一模块的7个小节(标红部分),阐述了IDEO团队在设计过程中一直坚守的心态。下面开始正文。



介绍


以人为中心的设计师意味着什么?
拥抱以人为本的设计意味着相信所有的问题,即使是那些看似棘手的问题,比如贫困、性别平等。此外,这意味着相信每天面对这些问题的人是掌握答案的人。以人为本的设计为任何类型的问题解决者提供了与社区与用户一起设计的机会,深入了解我们所服务的人,构思各种想法,并根据人们的实际需求创造创新性的解决方案。
在IDEO,我们几十年来一直使用以人为中心的设计来创造产品、服务、体验和社会企业,这些产品、服务、体验和企业之所以被采纳和接受,是因为我们把人们的生活和诉求放在核心位置。社会各个领域的创新时机已经成熟,我们已经一次次证明我们的方法行之有效。作为一名以人为中心的设计师,你要相信,只要你坚持从别人身上学到的东西,你的团队就能找到世界需要的新解决方案。有了这份实地指南,你现在就拥有了将这种信念变为现实所需的工具。

接受不同的心态
以人为中心的设计师不同于其他的问题解决者——我们修修补补,不断测试,我们一开始就失败了,而且经常失败,我们花了大量的时间但可能仍然不知道问题的答案。然而,我们勇往直前。我们是乐观主义者和创造者,是实验者和学习者,我们感同身受,不断重复,我们在意想不到的地方寻找灵感。我们相信解决方案就在那里。
通过专注于我们的目标群体并提出正确的问题,我们将一起达成目标。我们会想出很多点子,有些行得通,有些行不通。我们把我们的想法具体化,这样我们就可以进行测试。


然后我们提炼它们。最终,我们的方法等同于疯狂的创造力,不断地推动创新并给予我们自信,它将我们引向我们开始时从未意识到的解决方案。现在,我们要分享我们的设计哲学和心态。
让我们与众不同的七种心态:同理心、乐观、迭代、创造自信、实践、接受不确定性,以及从失败中学习。

理解的过程
以人为中心的设计并不是一个完美的线性过程,每个项目都有自己的特点,但无论你面临什么样的项目,你都将经历三个主要阶段:灵感、构思和实现。通过依次采取这三个阶段,你将与你为之设计的用户产生深刻的理解;你会弄清楚如何将你学到的东西变成设计新解决方案的机会;在最终将你的产品正式上线之前,你会建立并测试你的想法。在IDEO,我们使用以人为中心的设计来处理大量的设计挑战,尽管我们的项目涵盖社会企业到传播活动到医疗设备各个领域,但这种创造性解决问题的特殊方法每次都能帮助我们度过难关。

灵感
在这个阶段,你将学习如何更好地理解别人。你会观察他们的生活,听到他们的诉求和愿望,并逐渐加深你对这个设计挑战的理解。
构思
在这里,你将理解你所了解的一切,产生大量的想法,确定设计的机会,并测试和改进您的解决方案。
实现
现在是你把你的解决方案带到现实中的机会。你将弄清楚如何把你的想法推向市场,以及如何最大限度地发挥它在世界上的影响力。


使用工具
虽然没有哪两个以人为中心的设计项目是完全相同的,但是我们使用相同的设计流程和工具为每个项目进行设计。


例如,为了与我们要服务的人建立深厚的同理心,我们总是对他们进行访谈。


为了保持创造力和活力,我们总是在进行团队协作。


为了使我们的思维更有创造力、更加敏锐,我们总是把我们的想法做成有形的原型,因为它帮助我们解决问题。


因为我们很少在第一次就成功,所以我们总是分享我们的产出,并根据我们得到的反馈进行迭代。


这本指南中的57种方法提供了一套全面的练习和活动,可以帮助你从制定设计挑战到将其推向市场。有些方法你会用两到三次,有些则不会。但是作为一个整体,他们会让你走上持续创新的道路,同时让你所设计的社区或用户成为你工作的中心。 



相信这个过程,即使它让你感到不舒服
以人为中心的设计是一种独特的解决问题的方法,这种方法有时会让人觉得很疯狂而不像一个方法——但如果你总是使用传统的解决方案,你就很少能找到创新的解决方案。这个过程的目的是让你直接从用户那里学习,敞开心扉,接受各种各样的创造可能性,然后专注于最理想、最可行的方案。你会发现自己在这个过程中频繁地推翻假设,当你完成它的三个阶段时,你会从具体的观察到高度抽象的思考快速变换,然后再回到原型的具体细节。我们称之为发散和收敛(菱形设计法则)。 


在构思阶段,通过更广泛的思考(大范围,多领域),我们想出了各种可能的解决方案。


但因为我们的目标是最具可行性的方案,所以我们必须确定,在这一系列的想法中,哪一个最有可能真正发挥作用。你会发散和收敛几次,每一次新的周期,你会越来越接近一个投放市场级别的解决方案。


创建真正可用的方案
以人为中心的设计是一种独特的定位,最终目的是达成解决方案的可取性(用户角度),技术可行性,商业可行性。
首先从用户开始,了解他们的希望、恐惧和诉求,我们很快就能发现什么是用户亟需的。一旦我们确定了一系列能够吸引用户的解决方案,我们就开始着手于在技术上可行的方案以及如何使解决方案在经济上的可行性。这是一个平衡的过程,但这对于设计成功且可持续的解决方案至关重要。

Image title


一:心态


1.创造性的信心

任何人都可以像设计师一样理解世界。作为一个问题解决者,解锁这种潜力所需要的只是创造性的信心。创造性的自信是指每个人都具有创造力的信念,而创造力不是绘画或构图或雕刻的能力,而是一种理解世界的方式。
创造性信心是以人为本的设计师在实现思维的飞跃,信任他们的直觉以及追求他们尚未完全弄清楚的解决方案时所依赖的品质。我们相信,您可以并且将会为问题提出创造性的解决方案,并且相信所有这一切都需要着手去实践且深入尝试。
它是一种自信,相信你所需要做的只是卷起袖子投入进去。创造性的自信会驱使你去做事情,去测试它们,去不断犯错,然后继续前进,确信你会到达你需要去的地方,并且你一定会一路持续创新。
建立创造性的自信需要时间,实现这一目标的部分原因是相信以人为中心的设计过程将向您展示如何以创造性的方式解决手头的任何问题。从微小的成功开始,然后建立一个更大的目标,你会看到你的创造性信心的增长,不久你就会发现自己变成了一个非常有创造力的人。


2.着手去做

作为以人为本的设计师,我们之所以这样做,是因为我们相信有形(实物、可视化)的力量。我们知道,把一个想法变成现实,可以揭示出许多仅仅靠理论所不能揭示的东西。当目标是将有效的解决方案投入使用时,您不能生活在抽象和理论中。你必须让它们成为现实。 以人为中心的设计师是实干家、修补匠、工匠和建设者。我们使用任何我们可以使用的东西,从纸板和剪刀到复杂的数字工具。我们建立我们的想法,这样我们就可以测试它们,在真正去做一些东西后,我们会发现以前从来没有想到的机会和复杂性。制作也是一种奇妙的思考方式,它有助于我们关注设计的可行性。此外,把想法变成现实是一种非常有效的分享方式。如果没有来自人们的反馈,我们将不知道如何进一步完善我们的想法。



3.从失败中学习


失败是一个非常强大的工具,设计实验原型、交互和测试他们是以人为本设计的核心。要知道并非所有这些能发挥作用,当我们想要解决一个大问题时,我们一定会失败,但如果我们保持良好的心态,我们就会从中受益。


以人为中心的设计是从一个不知道如何解决设计挑战的地方开始的。只有通过倾听、思考、构建和提炼我们的答案,我们才能得到对我们的用户有用的东西。“早失败,早成功”是IDEO的一个常见口号,它的力量之一就是允许出错。由于拒绝冒险,一些设计师实际上与真正的创新机会失之交臂。
托马斯·爱迪生说得好:“我没有失败。我刚刚发现了10000种行不通的方法。对于以人为中心的设计师来说,找出行不通的东西是找到有用的东西的一部分。
失败是以人为中心的设计中固有的一部分,因为我们很少在第一次尝试时就能取得成功。事实上,第一次尝试就做好并不是重点。关键是把一些东西创造出来,然后用它来不断学习,不断提问,不断测试。如果以人为本的设计人员最终取得成功,那是因为他们曾经失败了无数次。


4.同理心


同理心是指站在别人的立场上,理解他们的生活,并从他们的角度解决问题的能力。 
以人为本的设计是以同理心为前提的,你为之设计的用户是你通往创新解决方案的路标。你所要做的就是感同身受,理解他们,并在设计过程中把他们时刻挂在心里。


比如长期以来,国际发展界一直在设计解决贫困问题的方案,却没有真正同情和理解它所要服务的人民。但是,通过设身处地为我们的设计对象着想,以人为中心的设计师可以开始通过一个新的、强大的视角来看待这个世界,以及所有改善它的机会。
让自己沉浸在另一个世界中,不仅会为你打开新的创造可能性,而且它能让你抛开先入为主的理念和过时的思维方式。与你为之设计的用户产生共鸣是真正理解他们生活的背景和复杂性的最佳途径。但最重要的是,它将使你的设计过程完全围绕用户,从而产出对他们来讲真正有用的东西。

5.接受不确定性


以人为中心的设计师总是从不确定的问题开始。在一些注重于快速找到方案的公司文化中,这或许不是一个好的开始。但是从第一步开始,接受不确定性的设计人员会勇敢的走出去,追求很多不同的想法,甚至找到意想不到的解决办法。通过接受这种模糊性,并相信以人为中心的设计过程将引导我们走向一个创新的答案,我们实际上迸发出更强的创新能力。 


将以人为中心的设计师与其他设计师区别开来的一个特点是,我们相信总会有更多的创意。我们不再执着于自己的传统想法,因为我们知道我们会拥有更多新的想法。因为以人为中心的设计是一个如此富有创造力的过程,而且因为我们倡导团队合作构思,所以很容易抛弃不好的想法,保留一些一般的想法,并最终得到最好的想法。
虽然这似乎违反直觉,但这种不确定性实际上促使以人为中心的设计师进行创新。
如果我们一开始就知道答案,我们能学到什么呢?我们怎样才能想出创造性的解决方案呢?我们为之设计的人会在哪里指引我们?拥抱不确定性实际上解放了我们的创造力,鼓励我们去追求一个最初无法想象的答案,这让我们走上了持续性创新的道路

6.乐观

我们相信设计本身就是乐观的。 


为了迎接一个艰难的挑战,特别是像贫困这样巨大而棘手的挑战,我们必须相信,创新是唯一的方法。如果我们不这样做,我们甚至不会去尝试。乐观是对可能性的拥抱,即使我们不知道答案,但我们相信总会有一个方案等待我们去挖掘。
除了推动我们走向解决方案,乐观还使我们更有创造力,鼓励我们在遇到死胡同时继续前进,并帮助项目中的所有参与人员凝聚在一起。从解决问题的角度来处理问题,会给整个过程注入解决最棘手问题所需的能量和动力。
以人为中心的设计师总是专注于可能发生的事情,而不是可能遇到的无数障碍。这是我们的核心信念——每一个问题都是可以解决的——这就是以人为中心的设计师的乐观。

7.迭代

作为以人为中心的设计师,我们采用迭代的方法来解决问题,从我们的用户那里得到反馈是促使解决方案诞生的关键部分。通过不断地迭代、持续改进我们的工作,我们会有更多的想法,尝试各种各样的方法,不断释放我们的创造力,更快地找到成功的解决方案。


迭代使我们保持敏捷、反应灵敏,并训练我们一直专注于核心的想法,经过几次迭代后,每个细节都恰到好处。如果你每次建立一个原型或分享一个想法时都追求完美,那么你就会花很长时间去完善一些不一定有效的东西。但是,通过构建、测试和迭代,您可以不花费过多的时间和资源来推进您的想法,直到您确定它是正确的。


在项目早期,我们不断进行迭代,因为我们知道第一次不会得到正确的结果。迭代让我们有机会去探索,去犯错,去验证我们的直觉,最终会得到一个将被采纳的解决方案。我们进行迭代,因为它让我们不断学习。我们不是躲在工作室里,打赌某个想法、产品或服务会大受欢迎,而是迅速地走出去,让我们的用户成为我们的向导。



第一部分就到这里了,重新回顾下,在本篇文章中,你应该了解到IDEO在设计过程中一直坚守的7个心态。

他们分别是创造性的信心、着手去做、从失败中学习、同理心、接受不确定性、乐观、持续迭代。

数组常用的方法

seo达人

数组常见方法

数组的方法

一、join() 方法 ----->不会改变原数组

二、concat() 方法 ----->不会改变原数组

三、splice(index, num, item) ----->会改变原数组

  1. splice(index, num) 删除功能
  2. splice(index, 0, ...item)
  3. splice(index, num, item)
  4. splice(index)

    四、slice() -----> 不会改变原数组

    五、push() 和 pop() ----->会改变原数组

    六、shift() 和 unshift() -----> 会改变原数组

    七、sort() -----> 会改变原数组

    八、reverse() ----->会改变原数组

    九、indexOf() 和 lastIndexOf()

    十、includes()

    十一、forEach()

    十二、map() -----> 不会改变原数组

    十三、filter() -----> 不会改变原数组

    十四、every()

    十五、some()

    十六、reduce() 和 reduceRight()

    十七、Array.from() 将类数组转化为数组

    十八、Array.of() 方法用于将一组值转化为数组

    十九、数组实例的 find() 和 findIndex()

    二十、扁平化数组 flat() 方法 -----> 不会改变原数组

    数组的方法

    一、join() 方法 ----->不会改变原数组

    该方法可以将数组里面的元素,通过指定的分隔符以字符串的形式连接起来

    返回值:返回新的字符串



    // join() 将数组转化为字符串

    let arr = [1, 2, 3, 4, 5]

    let str1 = arr.join('|')

    console.log(arr) // [1, 2, 3, 4, 5]

    console.log(str1) // 1|2|3|4|5

    // 当传空字符串时

    let str2 = arr.join('') // 12345

    // 当不传时

    let str3 = arr.join() // 1,2,3,4,5

    1

    2

    3

    4

    5

    6

    7

    8

    9

    二、concat() 方法 ----->不会改变原数组

    该方法可以把两个数组里的元素拼接成一个新的数组

    返回值:返回拼接后的新数组



    let arr1 = [0, 1, 2]

    let arr2 = [2, 3, 4]

    let arr = arr1.concat(arr2)

    // 传入二维数组

    let arrCopy = arr1.concat([12, [17, 26]])

    console.log(arrCopy) // [0, 1, 2, 12, [17, 26]]

    console.log(arr) // [0, 1, 2, 2, 3, 4]

    console.log(arr1) // [0, 1, 2]

    console.log(arr2) // [2, 3, 4]



    // ES6 扩展运算符

    let a = [1, 2]

    let b = [2, 3]

    let ab = [...a, ...b] // [1, 2, 2, 3]

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    三、splice(index, num, item) ----->会改变原数组

    index 参数:必传,整数,规定添加或者删除的位置,使用负数时,从数组尾部规定位置

    num 参数:必传,要删除的数量,如果为 0,则不删除项目

    item 参数:可选参数,可以是多个,想数组添加的新项目

    splice 具有删除,插入,替换的功能


  5. splice(index, num) 删除功能

    index 参数:开始位置的索引



    num 参数:要删除元素的个数



    返回值:返回的是包含被删除元素的数组对象



    // 删除功能

    let array = [1, 2, 3, 4, 5]

    let newArr = array.splice(2, 2)

    console.log(newArr) // [1, 2, 5]

    console.log(array) // [3, 4]



    1

    2

    3

    4

    5

    6
  6. splice(index, 0, …item)

    index 参数:插入元素的索引值

    0 参数:不删除

    // 插入功能

    let arr = ['a', 'b', 'c', 'd', 'e']

    let newArr = arr.splice(1, 0, ['插入', 1217])

    console.log(newArr) // []

    console.log(arr) // ['a', ['插入', 1217], 'b', 'c', 'd', 'e']



    1

    2

    3

    4

    5

    6
  7. splice(index, num, item)

    index 参数:开始的索引位置

    num 参数:删除的项个数

    item 参数:替换项

    返回值:返回包含被删除的元素的数组对象

    let arr = [1, 2, 3, 4, 5]

    let newArr = arr.splice(2, 2, '替换', '1226')

    console.log(newArr) // [3, 4]

    console.log(arr) // [1, 2, '替换', '1226', 5]



    1

    2

    3

    4

    5
  8. splice(index)

    当只传一个值时,表示从传入的 index 索引值开始截取到最后

    let arr = [1, 2, 3, 4, 5]

    let newArr = arr.splice(3)

    console.log(newArr) // [4, 5]

    console.log(arr) // [1, 2, 3]

    1

    2

    3

    4

    四、slice() -----> 不会改变原数组

    返回从原数组中指定开始下标到结束下标之间的项组成的新数组

    slice() 方法可以接受一或两个参数,即要返回项的起始位置和结束位置

    array.slice(2) 若只设置一个参数,起始位置为2(包含下标2)开始到数组最后

    array.slice(2, 5) 若设置两个参数,起始下标为2(包含下标2)到结束下标5(不包含下标5)的数组

    当 slice() 参数中有负数时,将负数加上数组的长度值来替换该位置的数

    let arr = [1, 2, 3, 4, 5, 6, 7, 8, 9]

    let copyArr1 = arr.slice(2) // [3, 4, 5, 6, 7, 8, 9]

    let copyArr2 = arr.slice(2, 5) // [3, 4, 5] 

    let copyArr3 = arr.slice(-2) // [8, 9]

    let copyArr4 = arr.slice(2, -2) // [3, 4, 5, 6, 7]

    let copyArr5 = arr.slice(-2, -5) // []

    let copyArr6 = arr.slice(-5, -2) // [5, 6, 7]

    console.log(arr) // [1, 2, 3, 4, 5, 6, 7, 8, 9]



    1

    2

    3

    4

    5

    6

    7

    8

    9

    五、push() 和 pop() ----->会改变原数组

    push() 可以接受任意数量的参数,将其逐个添加到数组的末尾,并返回修改后数组的长度(改变了原数组)

    pop() 删掉数组末尾最后一项,改变了数组的 length 值,并返回删除的项

    let arr = [1, 2, 3, 4, 5]

    let count = arr.push(0, 1217)

    console.log(count) // 7

    console.loh(arr) // [1, 2, 3, 4, 5, 0, 1217]



    let item = arr.pop()

    console.log(item) // 1217

    console.log(arr) // [1, 2, 3, 4, 5, 0]

    1

    2

    3

    4

    5

    6

    7

    8

    六、shift() 和 unshift() -----> 会改变原数组

    shift() 删除原数组的第一项,并返回删除元素的值,如果数组为空折返回 undefined

    unshift() 将参数添加到原数组的开头,并返回数组的长度

    let arr = [1, 2, 3, 4, 5]

    let item = arr.shift()

    console.log(item) // 1

    console.log(arr) // [2, 3, 4, 5]



    let count = arr.unshift(0, 'Jack')

    console.log(count) // 6

    console.log(arr) // [0, 'Jack', 2, 3, 4, 5]

    1

    2

    3

    4

    5

    6

    7

    8

    七、sort() -----> 会改变原数组

    在排序时,sort() 方法会调用每个数组项的 toString() 转型方法,然后比较得到的字符串,已确定如何排序,其本质是比较字符串的 ASCII 编码

    即使数组中的每一项都是数值,sort() 方法比较的也是字符串,因此会出现以下情况:

    let arr1 = ['a', 'd', 'e', 'b', 'c']

    console.log(arr1.sort()) // ['a', 'b', 'c', 'd', 'e']



    let arr2 = [12, 26, 3, 99, 52]

    console.log(arr2.sort()) // [12, 26, 3, 52, 99]



    1

    2

    3

    4

    5

    6

    解决办法



    let arr3 = [12, 26, 3, 99, 52]

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

    return a -b

    })

    console.log(arr3) // [3, 12, 26, 52, 99]

    1

    2

    3

    4

    5

    冒泡排序(优化版)



    function mySort (arr) {

    let count = 0 // 记录循环次数

    // 外层循环  控制循环的次数,每次找到最大值

    for (let i = 0; i < arr.length - 1; i++) {

    count++

    // 判断是否排序好了

    let isSort = true // 初始值默认已经排序好了

    for (let j = 0; j < arr.length - 1 - i; j++) {

    count++

    if (arr[j] > arr[j + 1]) {

    isSort = false

    // 交换位置

    let tmp = arr[j]

    arr[j] = arr[j + 1]

    arr[j + 1] = tmp

    }

    }

    // 某一趟结束,判断一下是否结束

    // 如何判断是否排序好,根据是否发生了位置交换,如果发生了位置交换就说明没有排序好

    if (isSort) {

    break

    }

    }

    console.log(count)

    return arr

    }

    mySort([12, 26, 17, 520, 99])



    // 打印结果:count 9

    // arr [12, 17, 26, 99, 520]



    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    25

    26

    27

    28

    29

    30

    31

    八、reverse() ----->会改变原数组

    反转数组项的顺序

    let arr = [1, 2, 3, 4, 5]

    arr.reverse()

    console.log(arr) // [5, 4, 3, 2, 1]



    1

    2

    3

    4

    九、indexOf() 和 lastIndexOf()

    indexOf() 接受两个参数:要查找的项和查找起点位置的索引(可选项),其中从数组的开头开始向后查找

    lastIndexOf() 接受两个参数:要查找的项和查找起点位置的索引(可选项),其中是从数组的末尾开始向前查找

    返回值:当存在时,返回要查找的项在数组中首次出现的索引值;当不存在时,返回 -1

    可以用来判断一个值是否存在数组中



    let arr = [1, 2, 3, 5, 7, 7, 8, 5, 12, 17]

    console.log(arr.indexOf(5)) // 3

    console.log(arr.lastIndexOf(5)) // 7



    console.log(arr.indexOf(5, 2)) // 3

    console.log(arr.lastIndexOf(5, 4)) // 3



    console.log(arr.indexOf('5')) // -1

    1

    2

    3

    4

    5

    6

    7

    8

    十、includes()

    Array.prototype.includes() 数组实例的方法

    返回一个布尔值,表示某个数组是否包含给定的值

    推荐使用:可以用来判断一个值是否存在数组中



    let arr = [1, 2, 3, 4, 5]

    console.log(arr.includes(3)) // true

    console.log(arr.includes(0)) // false

    1

    2

    3

    十一、forEach()

    对数组进行遍历,对数组中的每一项运行给定函数,没有返回值



    forEarch(function (item, index, array) {})



    参数 item:表示数组中的每一项

    参数 index:表示数组中每一项对应的索引值

    参数 array:表示数组本身

    let arr = [1, 2, 3, 4, 5]

    arr.forEach((item, index, array) => {

    console.log(item + '---' + index + '---' + (arr === array))

    })

    1

    2

    3

    4

    十二、map() -----> 不会改变原数组

    map() 方法是将数组中的每一项调用提供的函数,结果返回一个新数组,并没有改变原来的数组

    映射



    let arr = [1, 2, 3, 4, 5]

    let newArr = arr.map(item => item * item)



    console.log(newArr) // [1, 4, 9, 16, 25]

    console.log(arr) // [1, 2, 3, 4, 5]

    1

    2

    3

    4

    5

    十三、filter() -----> 不会改变原数组

    filter() 方法是将数组中所有的元素进行判断,将满足条件的元素作为一个新数组返回

    过滤



    let arr = [12, 17, 26, 520, 1314]

    let newArr = arr.filter((item, index) => {

    return item > 20

    })

    console.log(newArr) // [26, 520, 1314]

    1

    2

    3

    4

    5

    十四、every()

    every() 判断数组中每一项都是否满足条件,只有所有项都满足条件才返回 true,否则返回 false

    let arr = [1, 2, 3, 4, 5]

    let boolean1 = arr.every(item => item > 0)

    let boolean2 = arr.every(item => item > 3)



    console.log(boolean1) // true

    console.log(boolean2) // false

    1

    2

    3

    4

    5

    6

    十五、some()

    some() 判断数组中是否存在满足条件的项,只要有一项满足条件,就会返回 true,否则返回 false

    let arr = [1, 2, 3, 4, 5]

    let boolean3 = arr.some(item => item > 3)

    let boolean4 = arr.some(item => item < 0)



    console.log(boolean3) // true

    console.log(boolean4) // false

    1

    2

    3

    4

    5

    6

    十六、reduce() 和 reduceRight()

    reduce() 方法是所有元素调用返回函数,返回值为最后结果,传入的值必须是函数类型

    接受两个参数:每一项调用的函数和作为归并基础的初始值(可选项)

    这个函数返回的任何值都会作为第一个参数自动传给下一项。第一次迭代发生在数组的第二项上,因此第一个参数是数组的第一项,第二个参数就是数组的第二项。



    // 利用 reduce() 方法实现数组求和,给数组一开始家里一个初始值 3

    let arr = [1, 2, 3, 4, 5]

    let sum = arr.reduce((prev, cur, index, array) => {

    // 函数接受 4 个参数:

    // 前一个值、当前值、项的索引值、原数组对象

    console.log(prev, '---', cur, '---', index, '---', array)

    return prev + cur

    }, 3)

    console.log(sum) // 18 = 15 + 3

    1

    2

    3

    4

    5

    6

    7

    8

    9

    与之相对应的还有一个 Array.reduceRight() 方法,区别是这个是从右向左操作的



    十七、Array.from() 将类数组转化为数组

    let arrayLike = {

    '0': 'a',

    '1': 'b',

    '2': 'c',

    '3': 'd',

    length: 4

    }

    // ES5 写法

    let arr1 = [].slice.call(arrayLike) // ['a', 'b', 'c', 'd']



    // ES6

    let arr2 = Array.from(arrayLike) // ['a', 'b', 'c', 'd']

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    十八、Array.of() 方法用于将一组值转化为数组

    Array.of总是返回参数值组成的数组。如果没有参数,就返回一个空数组。



    Array.of(1, 2, 3, 4, 5) // [1, 2, 3, 4, 5]

    Array.of('abcd') // ['abcd']

    Array.of('abcd').length // 1

    Array.of() // []



    // Array.of 方法的实现

    function ArrayOf () {

    return [].slice.call(arguments)

    }

    1

    2

    3

    4

    5

    6

    7

    8

    9

    十九、数组实例的 find() 和 findIndex()

    数组实例的 find() 方法,用于找出第一个符合条件的数组成员

    它的参数是一个回调函数,所有数组成员依次执行该回调函数,直到找出第一个返回值为 true 的成员,然后就返回该成员,如果没有符合条件的成员,则返回 undefined

    let arr = [1, 2, 3, 4, 5]

    let value= arr.find((item, index, array) => {

    // item 表示循环遍历数组的每一项

    // index 每一项对应的索引值

    // array 原数组对象

    return item > 3

    })

    console.log(value) // 4

    console.log(arr) // [1, 2, 3, 4, 5]



    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    数组实例的 findIndex方法的用法与find方法非常类似,返回第一个符合条件的数组成员的位置,如果所有成员都不符合条件,则返回 -1

    let arr = [1, 2, 3, 4, 5]

    let index = arr.findIndex((item, index, array) => {

    return item > 3

    })



    console.log(index) // 3



    [NaN].indexOf(NaN) // -1

    [NaN].findIndex(value => Object.is(NaN, value)) // 0

    [NaN].includes(NaN) // true

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    由此可见:一般用来判断数组中是否存在某个值,推荐使用 includes



    二十、扁平化数组 flat() 方法 -----> 不会改变原数组

    let arr = [1, [2, 3, [4, 5, [6]]]]

    let arrFlat = arr.flat(Infinity)

    console.log(arrFlat) // [1, 2, 3, 4, 5, 6]

    console.log(arr) // [1, [2, 3, [4, 5, [6]]]]

    1

    2

    3

    4

    利用递归实现数组扁平化



    let arrayFlat = []

    let myFlat = (arr) => {

    for (let i = 0; i < arr.length; i++) {

    let item= arr[i]

    // 判断 arr[i] 是否是数组

    if (Array.isArray(item)) {

    // 如果是数组,继续调用函数 myFlat

    myFlat(item)

    } else {

    arrayFlat.push(item)

    }

    }

    return arrayFlat

    }



    蓝蓝设计www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计、BS界面设计  cs界面设计  ipad界面设计  包装设计  图标定制  用户体验 、交互设计、 网站建设 平面设计服务

目录页该如何设计?-8个常用的排版方式

雪涛

目录页主要出现在一些篇幅比较长的书籍(画册)中,其位置一般被安排在扉页之后,目录虽然不是正文,却概括了一本书的所有内容,具有便于读者快速了解及查阅书本内容的作用,所以,即使说目录页是一本书中最重要的页面也不为过。

既然如此,那么目录页的设计自然也是十分重要,于是乎,葱爷整理了 8 个很不错的目录设计技巧分享给大家。

直线排版

直线在目录设计中的作用主要有以下三个。

1. 连接

即把每节内容的标题与其对应的页码连起来,这是比较常规的一种做法,可以使目录更加清晰,重复排列的线条会形成统一、规整的美感。采用这种排版方式时,标题与页码一般会设置成两端对齐,这样的效果更加整洁、清晰。

2. 创造形式

对于一些文字内容比较少的目录页,如果像上面的例子一样排列会显得比较单调和小气,所以可以借助直线来增加其趣味性和张力。由于内容不多,所以即使不严格对齐也不会影响阅读。

3. 信息区隔

比如在下图的例子中,直线起到了两个信息区隔的作用,一是区隔页码与大标题;二是使七个大章节的内容独立开来。

加图片

这种形式也适合内容比较少的目录页,当有了图片后,目录页似乎也变成了一个内页版面,更加丰富、饱满。图片在目录设计中主要有两个用法。

1. 概括章节的主要内容

其功能与标题一样,所以如果要使用这种方式,那么就需要为目录中的每个大标题都搭配对应的图片。

2. 装饰

这里的图片不是与标题一一对应的,其目的是为了消除纯文字目录的单调感,使版面更丰富、更好看。

格子排版

即将目录中的元素(页码、标题、图片)用表格的形式来排列,这么做也可以使信息更加清晰,更有秩序。由于这种做法在目录设计中并不常见,所以显得很特别。

大页码

页码或者序号是目录页必不可少的元素,章节细分比较多的目录都会标明页码,而分类比较少的目录一般会采用序列号,用来区分几个大板块。把页码或序号拉大并使用笔画比较粗的字体,除了可以使其更明了、醒目以外,还可以增加版面的大小对比,提升设计感。

把页码裁切一下,会更有设计感。

分栏排版

即把文字信息竖向等分成两份或两份以上,适用于文字比较多的版面,所以如果目录页的内容比较多时也适合分栏排版。由于每一栏的内容都严格对齐,且页码比较大,所以栏与栏之间即使错位排列也不会影响阅读。

除了文字还可以用图片来分栏。

轴排版

即把目录信息沿着某条轴排列,这种形式在目录设计中也比较少见,适用于内容比较少的目录页。轴的形式一般为竖轴和横轴,排列的形式通常为错位排版。

上图并不是以线为轴,而是以图片和色块为轴。

网格排版

我们都知道网格系统是画册设计的常用工具,可以有效组织版面信息,使其更有序、更整洁,所以内容比较多的目录页也可以用网格系统来排版,为了避免单调和无趣,通常会加入图片元素。

留白

如果目录的文字比较少,版面就容易显得很空、很单调,常见的做法是增加图片或者把文字拉大,其实主动保留大量空白也是一种解决办法,比如把内容集中排列在版面的顶部、底部、左下角、右下角等位置,留出其他位置的空白。这么处理的版面虽然有一种不平衡感,但动感和设计感更强,大面积的留白还可以适当缓解眼睛的疲劳。

总结

无论做什么设计我们都要以它的最终目的为设计准则,由于目录是为了方便浏览和查阅书本内容,所以设计时一定要注意视觉的整洁与信息的清晰,所以可以发现对齐和统一是最常被用到的两个技巧。

如何让你的摄影更有张力?

雪涛

如果经常听评片会,许多朋友会听过这样一句话:「这样拍会显得画面更有张力……」就摄影而言,我们认为张力,是让一幅摄影作品在视觉上出效果的重要「内力」。此外「张力」还被广泛应用在点评一段演唱,一幅画,一次表演……我们都似乎能感受「张力」的意境,却又似乎不能参透它的意思。

如何理解「张力」?如何借此来提高我们的摄影感悟?下面我就试着做个粗浅的分析吧。

追根溯源:「张力」到底是什么?

关于「张力」有很多略显深奥的解释。我认为下面这种解释较为通俗,同时与摄影有一些关联:

互补物、相反物和对立物之间的冲突或摩擦。……一般而论,凡是存在着对立而又相互联系的力量、冲动或意义的地方,都存在着张力。(转引自杨果〈隐藏的视点:中西「张力」范畴再辨〉,《江汉学术》,2013年10月)

尽管这段话不是针对摄影,甚至不是针对视觉艺术而写的,但是却很好的解释了张力一词。「张力」(tension)由「内涵」(intension)和「外延」(extension)二词而来。其实它在物理学上的意思便是其在美学上的意义的绝佳比喻。

那么我们便可以粗浅感受一下「张力」在一张照片(或者说任何一种视觉艺术)中表达的是什么了:它通常表示了一幅照片对立而又统一的相互作用,这种作用通常产生紧张感、拉扯感,产生一种繁复的韵味。力的暗示、不平衡、断裂、突发等效果都属于张力范畴。

举个极其通俗的例子,如果我要用图片表达「跨栏」。

这张「没有张力」,因为常规、平衡、稳定:

这张「有张力」,因为夸张,不平衡:

当我们在谈论「张力」的时候,我们在谈论什么?

当我们说一张照片的「张力」的时候,更多表达的是照片给我们带来的主观感受。通常情况下,一张「有张力」的照片背后蕴含着以下的特质。

1. 拉扯感

即我们可以感觉到「力量的存在」,似乎被某种力量拉扯着。哇,这是再直白不过的张力了。

以拍街头行人为例,「奔跑」「转向」或其他肢体较为夸张的动作和表情通常能让我们体会到力量。人的潜意识总是会倾向于,试着让一些看起来没有达到稳态的东西趋向于达到稳态,夸张的动作同时也暗示着那些行人「将要跨出下一步」「将要转过去」「将会拉扯肌肉」等等。

△ 韩松 / 摄 iPhone 7p, 莫斯科。奔跑的人。

△ 韩松 / 摄 iPhone 6, 马尔丁,孩子的表情较为夸张,能感受到肌肉的拉扯,画面感染力较强。

△ 韩松 / 摄 iPhone 6, 马尔丁。同样是人物照,这张就画面张力略显单薄。

而另一种拉扯感来自所拍摄的元素的形态本身。例如夕阳下被拉长的影子,具有纵深感的空间,夸张尖锐的几何形态等。它们都会让我们有被拖拽、被虹吸、被缠绕等感觉。

△ 韩松 / 摄 iPhone 7p, 布达佩斯。影子。

2. 冲突

制造各种对比与冲突。

例如强烈的色相对比,阴影和高光的高调对比等等。这无疑用画面的色彩击中我们的眼球。

再如一切能造成冲击力的题材冲突:如抽象和具象,虚幻和真实,细腻与粗糙,动态与静态等等。

△ 韩松 / 摄 iPhone 6, 香港。红衣人物和绿色墙面的「撞色」。

△ 韩松 / 摄 iPhone 6, 里斯本。行人和影子。

3. 构图

夸张的构图也能产生张力。

斜线的构图通常更有张力,因为它夸张而强烈。但斜线的构图是需要磨练的,有时候「为了斜线而斜线」会显得矫揉造作(之前的文章我建议大家先从摆正,拍平开始)。

△ 谭凌飞 / 摄 iPhone 6, 萨尔茨堡。斜射的影子让不起眼的场景有了张力。

极度不遵从「三分法」「对称」「平衡」等原则,而又在某种意义上很精妙地构图,有时候也能造成张力。这种构图的本质是打破了平衡感,扰动了我们潜意识里的平衡球。元素的极大和极小的对比,大量留白和极度撑满,以及一些「莫名其妙」的夸张形式,都有可能成为视觉张力的发力点。

△ 韩松 / 摄 iPhone 6s, 北京。

△ 韩松 / 摄 iPhone 7p, 日本高松。「极大」的场景和「极小」的人物,拉开了比例。

△ 韩松 / 摄 iPhone 7p, 巴黎。雕塑和观众的比例和颜色对比形成张力,然而他们的动作又有一些联系。

怎样拍有张力?

1. 手机外置广角或鱼眼镜头

手机定焦镜头较为平和,如果加上外置的广角或鱼眼镜头能够拍一些较有冲击力的照片。广角顾名思义是有很宽广的视角,能够近距离将比较宽的场景元素都拉扯在一起,让斜线、曲线都更为夸张。

2. 勇敢面对那些「不舒服」的素材吧

其实本质还是在于不平衡。拥挤、窥视、抖动、不稳定的、转瞬即逝,这些东西都不太舒服,但又常常因此形成视觉上的动感。拥挤人群中的一瞥,从奇特角度的窥探人物的活动、快速运动的骑车的人,匆匆而过的行人汽车等等,让我们感到有「丝毫不安」,却也预示着「这不是稳态」,是矛盾而拉扯着的。

△ 韩松 / 摄 iPhone 7p, 布拉格。拥挤的人群。

△ 韩松 / 摄 iPhone 7p, 日本奈良。透过吃草的鹿看后面的鹿。

△ 谭凌飞/ 摄 iPhone 6s, 巴黎。差点被遮住的铁塔。

3. 不平衡的构图

自不多说。对角线、夸张的引导线,螺旋线,撑满画面的构图。那些找不到几何中心而又精妙的构图。

△ 韩松 / 摄 iPhone 7p, 日本。螺旋线。

4. 拍摄运动物体

跑步的人,快走的人,跳跃的人,快速通过的车。

△ 韩松 / 摄 iPhone 6s, 伊斯坦布尔。撑伞的人走过。

△ 韩松 / 摄 iPhone 7p, 莫斯科。拉手风琴的人与匆匆而过的行人。

5. 后期

通过调高饱和度等参数强化色相的对比等等。

△ 韩松 / 摄 iPhone 7p, 日本。后期调高了些饱和度,强化了人物和背景的颜色对比。

不得不承认,张力还是一个比较抽象的概念,我们从来不会以「张力」为目的去拍一张照片,更多的时候,我们是无意识地被「有张力」的场景所吸引,也具有从一堆原片中甄选出「有张力的照片」的能力。重要的是会在取景框中通过构图的运用来制造张力的可能性。

文章来源:优设

彻底解决小程序无法触发SESSION问题

seo达人

一、首先找到第一次发起网络请求的地址,将服务器返回set-cookie当全局变量存储起来

wx.request({
  ......
  success: function(res) {
    console.log(res.header);
    //set-cookie:PHPSESSID=ic4vj84aaavqgb800k82etisu0; path=/; domain=.fengkui.net

    // 登录成功,获取第一次的sessionid,存储起来
    // 注意:Set-Cookie(开发者工具中调试全部小写)(远程调试和线上首字母大写)
    wx.setStorageSync("sessionid", res.header["Set-Cookie"]);
  }
}) 

二、请求时带上将sessionid放入request的header头中传到服务器,服务器端可直接在cookie中获取

wx.request({
  ......
  header: {
    'content-type': 'application/json', // 默认值
    'cookie': wx.getStorageSync("sessionid")
    //读取sessionid,当作cookie传入后台将PHPSESSID做session_id使用
  },
  success: function(res) {
    console.log(res)
  }
}) 

三、后台获取cookie中的PHPSESSID,将PHPSESSID当作session_id使用

<?php
// 判断$_COOKIE['PHPSESSID']是否存在,存在则作session_id使用
if ($_COOKIE['PHPSESSID']) {
    session_id($_COOKIE['PHPSESSID']);
}

session_start();
echo session_id(); 

jqGrid 表格底部汇总、合计行footerrow处理

seo达人

jqGrid提供了表格底部汇总、合计行功能,我们先看下user-guide关于jqGrid合计行都有哪些说明?然后再看个DEMO,看看jqGrid表格底部汇总、合计行到底如何实现。



1、user-guide关于jqGrid合计行的说明

1)表格配置:footerrow, boolean, 默认false

If set to true this will place a footer table with one row below the gird records and above the pager. The number of columns equal those specified in colModel

表格是否显示底部合计行。



2)表格配置:userDataOnFooter,boolean,默认false

When set to true we directly place the user data array userData in the footer if the footerrow parameter is set to true. The rules are as follows: If the userData array contains a name which matches any name defined in colModel, then the value is placed in that column. If there are no such values nothing is placed. Note that if this option is used we use the current formatter options (if available) for that column. See footerData method.

如果设为true,则userData可以用来填充汇总行。



3)汇总行赋值:footerData([string action], [object data], [boolean format])

This method gets or sets data on the grid footer row. When set data in the footer row, the data is formatted according to the formatter (if defined) in coModel. The method can be used if footerrow option is set to true.

parameters

string action - can be ‘get’ or ‘set’. The default is get. ‘get’ returns an object of type name:value, where the name is a name from colModel. This will return data from the footer. The other two options have no effect in this case. ‘set’ takes a data object and places the values in the footer The value is formatted according to the definition of the formatter in colModel - see next parameter. The object should be in name:value pair, where the name is the name from colModel

object data - data to be set in the footer in name:value pair, where the name should correspond to the name of colModel in order to be set in the appropriate cell.

boolean format - default is true. This instruct the method to use the formatter (if set in colModel) when new values are set. A value of false will disable the using of formatter



2、一个DEMO,如何利用gridComplete事件进行表格数据汇总并赋值给合计行

1)案例截图



2)html代码



<!DOCTYPE html>

<html>

<head>

<meta charset="UTF-8" />

<title>jggrid底部汇总行</title>



<link rel="stylesheet" href="https://cdn.bootcss.com/twitter-bootstrap/3.3.7/css/bootstrap.min.css" />

<link rel="stylesheet" href="https://cdn.bootcss.com/font-awesome/4.5.0/css/font-awesome.min.css" />

<link rel="stylesheet" href="https://cdn.bootcss.com/jqueryui/1.11.0/jquery-ui.min.css" />

<link rel="stylesheet" href="https://js.cybozu.cn/jqgrid/v5.3.1/css/ui.jqgrid.css" />

<script src="https://cdn.bootcss.com/jquery/1.11.1/jquery.min.js"&gt;&lt;/script&gt;

<script src="https://js.cybozu.cn/jqgrid/v5.3.1/js/jquery.jqGrid.min.js"&gt;&lt;/script&gt;

<script src="https://js.cybozu.cn/jqgrid/v5.3.1/js/i18n/grid.locale-en.js"&gt;&lt;/script&gt;

</head>

<body>

<div class="page-content container">

<div class="page-body"> <!-- page-body -->

<div class="panel panel-default" id="panel-orders">

<table id="orders"></table>

</div>

</div>

</div>

<script type="text/javascript">

var data = [];

function getBills() {

var rowCount = 10;

for (var i = 0; i < rowCount; i ++) {

data.push({

sid: i,

goods_no: i + 1,

goods_name: '零件名称' + rowCount + i,

car_type_name: '车型' + rowCount + i,

package_name: '包装器具' + rowCount + i,

unit_name: '件',

snp: 0.89,

bill_amount: rowCount + i,

goods_count: rowCount + i,

bill_no: 'BN0000000' + i,

qrcode: '1000000000' + i,

barcode: '1000000000' + i,

})

}

$("#orders").jqGrid("clearGridData").jqGrid('setGridParam',{data: data || []}).trigger('reloadGrid');

}

$(function() {

$("#orders").jqGrid({

colModel: [

{label: "零件号", name: "goods_no", width: 60},

{label: "零件名称", name: "goods_name", search:false, width: 180},

{label: "车型", name: "car_type_name", width: 70},

{label: "包装器具", name: "package_name", width: 70},

{label: "单位", name: "unit_name", width: 40},

{label: "订单号", name: "bill_no", width: 120},

{label: "订单数量", name: "goods_count", width: 80},

],

datatype: 'local',

rownumbers: true,

height: 300,

rowNum: 1000,

footerrow: true,

gridComplete: function() {

var rows = $("#orders").jqGrid("getRowData"), total_count = 0;

        for(var i = 0, l = rows.length; i<l; i++) {

        total_count += (rows[i].goods_count - 0);

        }

        $("#orders").jqGrid("footerData", "set", {goods_name:"--合计--",goods_count:total_count});

        }

});

getBills();

});

</script>

</body>

</html>



3)代码说明:



表格构建时,设置:footerrow: true

gridComplete(jqGridGridComplete)事件处理,进行数据汇总并赋值给合计行

gridComplete fires after all the data is loaded into the grid and all other processes are complete. Also the event fires independent from the datatype parameter and after sorting paging and etc. Does not fire if datatype is a defined as function.



4)获取汇总行数据

var row = $("#orders").jqGrid(“footerData”, “get”);

蓝蓝设计www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计、BS界面设计  cs界面设计  ipad界面设计  包装设计  图标定制  用户体验 、交互设计、 网站建设 平面设计服务

日历

链接

blogger

蓝蓝 http://www.lanlanwork.com

存档