首页

ES6新特性Promise异步调用

seo达人

如果您想订阅本博客内容,每天自动发到您的邮箱中, 请点这里

Promise 的含义

Promise 是异步编程的一种解决方案,比传统的解决方案–回调函数和事件--更合理和更强大。它由社区最早提出和实现,ES6将其写进了语言标准,统一了语法,原生提供了Promise

所谓Promise ,简单说就是一个容器,里面保存着某个未来才回结束的事件(通常是一个异步操作)的结果。从语法上说,Promise是一个对象,从它可以获取异步操作的消息。 
Promise 对象的状态不受外界影响

三种状态:

  • pending:进行中
  • fulfilled :已经成功
  • rejected 已经失败

状态改变: 
Promise对象的状态改变,只有两种可能:

  • 从pending变为fulfilled
  • 从pending变为rejected。

这两种情况只要发生,状态就凝固了,不会再变了,这时就称为resolved(已定型

基本用法

ES6规定,Promise对象是一个构造函数,用来生成Promise实例


    
  1. const promist = new Promise(function(resolve,reject){
  2. if(/*异步操作成功*/){
  3. resolve(value);
  4. }else{
  5. reject(error);
  6. }
  7. })
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

resolve函数的作用是,将Promise对象的状态从“未完成”变为“成功”(即从 pending 变为 resolved),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去; 
reject函数的作用是,将Promise对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。

Promise的源码分析:

1.回调地狱

曾几何时,我们的代码是这样的,为了拿到回调的结果,不得不callback hell,这种环环相扣的代码可以说是相当恶心了


            
  1. let fs = require('fs')
  2. fs.readFile('./a.txt','utf8',function(err,data){
  3. fs.readFile(data,'utf8',function(err,data){
  4. fs.readFile(data,'utf8',function(err,data){
  5. console.log(data)
  6. })
  7. })
  8. })

终于,我们的盖世英雄出现了,他身披金甲圣衣、驾着七彩祥云。好吧打岔儿了,没错他就是我们的Promise,那让我们来看看用了Promise之后,上面的代码会变成什么样吧


            
  1. let fs = require('fs')
  2. function read(url){
  3. return new Promise((resolve,reject)=>{
  4. fs.readFile(url,'utf8',function(error,data){
  5. error && reject(error)
  6. resolve(data)
  7. })
  8. })
  9. }
  10. read('./a.txt').then(data=>{
  11. return read(data)
  12. }).then(data=>{
  13. return read(data)
  14. }).then(data=>{
  15. console.log(data)
  16. })

2.重点开始,小眼睛都看过来

2.1 Promise/A+

首先我们要知道自己手写一个Promise,应该怎么去写,谁来告诉我们怎么写,需要遵循什么样的规则。当然这些你都不用担心,其实业界都是通过一个规则指标来生产Promise的。让我们来看看是什么东西。传送门☞Promise/A+

2.2 constructor

我们先声明一个类,叫做Promise,里面是构造函数。如果es6还有问题的可以去阮大大的博客上学习一下(传送门☞es6


            
  1. class Promise{
  2. constructor(executor){
  3. //控制状态,使用了一次之后,接下来的都不被使用
  4. this.status = 'pendding'
  5. this.value = undefined
  6. this.reason = undefined
  7. //定义resolve函数
  8. let resolve = (data)=>{
  9. //这里pendding,主要是为了防止executor中调用了两次resovle或reject方法,而我们只调用一次
  10. if(this.status==='pendding'){
  11. this.status = 'resolve'
  12. this.value = data
  13. }
  14. }
  15. //定义reject函数
  16. let reject = (data)=>{
  17. if(this.status==='pendding'){
  18. this.status = 'reject'
  19. this.reason = data
  20. }
  21. }
  22. //executor方法可能会抛出异常,需要捕获
  23. try{
  24. //将resolve和reject函数给使用者
  25. executor(resolve,reject)
  26. }catch(e){
  27. //如果在函数中抛出异常则将它注入reject中
  28. reject(e)
  29. }
  30. }
  31. }

那么接下来我会分析上面代码的作用,原理

  • executor:这是实例Promise对象时在构造器中传入的参数,一般是一个function(resolve,reject){}
  • status:``Promise的状态,一开始是默认的pendding状态,每当调用道resolve和reject方法时,就会改变其值,在后面的then方法中会用到
  • value:resolve回调成功后,调用resolve方法里面的参数值
  • reason:reject回调成功后,调用reject方法里面的参数值
  • resolve:声明resolve方法在构造器内,通过传入的executor方法传入其中,用以给使用者回调
  • reject:声明reject方法在构造器内,通过传入的executor方法传入其中,用以给使用者回调

2.3 then

then方法是Promise中最为重要的方法,他的用法大家都应该已经知道,就是将Promise中的resolve或者reject的结果拿到,那么我们就能知道这里的then方法需要两个参数,成功回调和失败回调,上代码!


            
  1. then(onFufilled,onRejected){
  2. if(this.status === 'resolve'){
  3. onFufilled(this.value)
  4. }
  5. if(this.status === 'reject'){
  6. onRejected(this.reason)
  7. }
  8. }

这里主要做了将构造器中resolve和reject的结果传入onFufilledonRejected中,注意这两个是使用者传入的参数,是个方法。所以你以为这么简单就完了?要想更Swag的应对各种场景,我们必须得再完善。继续往下走!

3.异步的Promise

之前我们只是处理了同步情况下的Promise,简而言之所有操作都没有异步的成分在内。那么如果是异步该怎么办?

3.1 callback!!!!

最早处理异步的方法就是callback,就相当于我让你帮我扫地,我会在给你发起任务时给你一个手机,之后我做自己的事情去,不用等你,等你扫完地就会打手机给我,诶,我就知道了地扫完了。这个手机就是callback,回调函数。

首先我们需要改一下构造器里的代码,分别添加两个回调函数的数组,分别对应成功回调和失败回调。他们的作用是当成功执行resolve或reject时,执行callback。


            
  1. //存放成功回调的函数
  2. this.onResolvedCallbacks = []
  3. //存放失败回调的函数
  4. this.onRejectedCallbacks = []
  5. let resolve = (data)=>{
  6. if(this.status==='pendding'){
  7. this.status = 'resolve'
  8. this.value = data
  9. //监听回调函数
  10. this.onResolvedCallbacks.forEach(fn=>fn())
  11. }
  12. }
  13. let reject = (data)=>{
  14. if(this.status==='pendding'){
  15. this.status = 'reject'
  16. this.reason = data
  17. this.onRejectedCallbacks.forEach(fn=>fn())
  18. }
  19. }

然后是then需要多加一个状态判断,当Promise中是异步操作时,需要在我们之前定义的回调函数数组中添加一个回调函数。


            
  1. if(this.status === 'pendding'){
  2. this.onResolvedCallbacks.push(()=>{
  3. // to do....
  4. let x = onFufilled(this.value)
  5. resolvePromise(promise2,x,resolve,reject)
  6. })
  7. this.onRejectedCallbacks.push(()=>{
  8. let x = onRejected(this.reason)
  9. resolvePromise(promise2,x,resolve,reject)
  10. })
  11. }

ok!大功告成,异步已经解决了

3.2 resolvePromise

这也是Promise中的重头戏,我来介绍一下,我们在用Promise的时候可能会发现,当then函数中return了一个值,我们可以继续then下去,不过是什么值,都能在下一个then中获取,还有,当我们不在then中放入参数,例:promise.then().then(),那么其后面的then依旧可以得到之前then返回的值,可能你现在想很迷惑。让我来解开你心中的忧愁,follow me


            
  1. then(onFufilled,onRejected){
  2. //解决onFufilled,onRejected没有传值的问题
  3. onFufilled = typeof onFufilled === 'function'?onFufilled:y=>y
  4. //因为错误的值要让后面访问到,所以这里也要跑出个错误,不然会在之后then的resolve中捕获
  5. onRejected = typeof onRejected === 'function'?onRejected:err=>{ throw err ;}
  6. //声明一个promise对象
  7. let promise2
  8. if(this.status === 'resolve'){
  9. //因为在.then之后又是一个promise对象,所以这里肯定要返回一个promise对象
  10. promise2 = new Promise((resolve,reject)=>{
  11. setTimeout(()=>{
  12. //因为穿透值的缘故,在默认的跑出一个error后,不能再用下一个的reject来接受,只能通过try,catch
  13. try{
  14. //因为有的时候需要判断then中的方法是否返回一个promise对象,所以需要判断
  15. //如果返回值为promise对象,则需要取出结果当作promise2的resolve结果
  16. //如果不是,直接作为promise2的resolve结果
  17. let x = onFufilled(this.value)
  18. //抽离出一个公共方法来判断他们是否为promise对象
  19. resolvePromise(promise2,x,resolve,reject)
  20. }catch(e){
  21. reject(e)
  22. }
  23. },0)
  24. })
  25. }
  26. if(this.status === 'reject'){
  27. promise2 = new Promise((resolve,reject)=>{
  28. setTimeout(()=>{
  29. try{
  30. let x = onRejected(this.reason)
  31. resolvePromise(promise2,x,resolve,reject)
  32. }catch(e){
  33. reject(e)
  34. }
  35. },0)
  36. })
  37. }
  38. if(this.status === 'pendding'){
  39. promise2 = new Promise((resolve,reject)=>{
  40. this.onResolvedCallbacks.push(()=>{
  41. // to do....
  42. setTimeout(()=>{
  43. try{
  44. let x = onFufilled(this.value)
  45. resolvePromise(promise2,x,resolve,reject)
  46. }catch(e){
  47. reject(e)
  48. }
  49. },0)
  50. })
  51. this.onRejectedCallbacks.push(()=>{
  52. setTimeout(()=>{
  53. try{
  54. let x = onRejected(this.reason)
  55. resolvePromise(promise2,x,resolve,reject)
  56. }catch(e){
  57. reject(e)
  58. }
  59. })
  60. })
  61. })
  62. }
  63. return promise2
  64. }

一下子多了很多方法,不用怕,我会一一解释

  1. 返回Promise?:首先我们要注意的一点是,then有返回值,then了之后还能在then,那就说明之前的then返回的必然是个Promise
  2. 为什么外面要包一层setTimeout?:因为Promise本身是一个异步方法,属于微任务一列,必须得在执行栈执行完了在去取他的值,所以所有的返回值都得包一层异步setTimeout。
  3. 为什么开头有两个判断?:这就是之前想要解决的如果then函数中的参数不是函数,那么我们需要做处理。如果onFufilled不是函数,就需要自定义个函数用来返回之前resolve的值,如果onRejected不是函数,自定义个函数抛出异常。这里会有个小坑,如果这里不抛出异常,会在下一个then的onFufilled中拿到值。又因为这里抛出了异常所以所有的onFufilled或者onRejected都需要try/catch,这也是Promise/A+的规范。当然本人觉得成功的回调不需要抛出异常也可以,大家可以仔细想想。
  4. resolvePromise是什么?:这其实是官方Promise/A+的需求。因为你的then可以返回任何职,当然包括Promise对象,而如果是Promise对象,我们就需要将他拆解,直到它不是一个Promise对象,取其中的值。

那就让我们来看看这个resolvePromise到底长啥样。


            
  1. function resolvePromise(promise2,x,resolve,reject){
  2. //判断x和promise2之间的关系
  3. //因为promise2是上一个promise.then后的返回结果,所以如果相同,会导致下面的.then会是同一个promise2,一直都是,没有尽头
  4. if(x === promise2){//相当于promise.then之后return了自己,因为then会等待return后的promise,导致自己等待自己,一直处于等待
  5. return reject(new TypeError('循环引用'))
  6. }
  7. //如果x不是null,是对象或者方法
  8. if(x !== null && (typeof x === 'object' || typeof x === 'function')){
  9. //为了判断resolve过的就不用再reject了,(比如有reject和resolve的时候)
  10. let called
  11. try{//防止then出现异常,Object.defineProperty
  12. let then = x.then//取x的then方法可能会取到{then:{}},并没有执行
  13. if(typeof then === 'function'){
  14. //我们就认为他是promise,call他,因为then方法中的this来自自己的promise对象
  15. then.call(x,y=>{//第一个参数是将x这个promise方法作为this指向,后两个参数分别为成功失败回调
  16. if(called) return;
  17. called = true
  18. //因为可能promise中还有promise,所以需要递归
  19. resolvePromise(promise2,y,resolve,reject)
  20. },err=>{
  21. if(called) return;
  22. called = true
  23. //一次错误就直接返回
  24. reject(err)
  25. })
  26. }else{
  27. //如果是个普通对象就直接返回resolve作为结果
  28. resolve(x)
  29. }
  30. }catch(e){
  31. if(called) return;
  32. called = true
  33. reject(e)
  34. }
  35. }else{
  36. //这里返回的是非函数,非对象的值,就直接放在promise2的resolve中作为结果
  37. resolve(x)
  38. }
  39. }

它的作用是用来将onFufilled的返回值进行判断取值处理,把最后获得的值放入最外面那层的Promise的resolve函数中。

  1. 参数promise2(then函数返回的Promise对象),x(onFufilled函数的返回值),resolve、reject(最外层的Promise上的resolve和reject)。
  2. 为什么要在一开始判断promise2x?:首先在Promise/A+中写了需要判断这两者如果相等,需要抛出异常,我就来解释一下为什么,如果这两者相等,我们可以看下下面的例子,第一次p2是p1.then出来的结果是个Promise对象,这个Promise对象在被创建的时候调用了resolvePromise(promise2,x,resolve,reject)函数,又因为x等于其本身,是个Promise,就需要then方法递归它,直到他不是Promise对象,但是x(p2)的结果还在等待,他却想执行自己的then方法,就会导致等待。

            
  1. let p1 = new Promise((resolve,reject)=>{
  2. resolve()
  3. })
  4. let p2 = p1.then(d=>{
  5. return p2
  6. })
  1. called是用来干嘛的?:called变量主要是用来判断如果resolvePromise函数已经resolve或者reject了,那就不需要在执行下面的resolce或者reject。
  2. 为什么取then这个属性?:因为我们需要去判断x是否为Promise,then属性如果为普通值,就直接resolve掉,如果是个function,就是Promise对象,之后我们就需要将这个x的then方法进行执行,用call的原因是因为then方法里面this指向的问题。
  3. 为什么要递归去调用resolvePromise函数?:相信细心的人已经发现了,我这里使用了递归调用法,首先这是Promise/A+中要求的,其次是业务场景的需求,当我们碰到那种Promise的resolve里的Promise的resolve里又包了一个Promise的话,就需要递归取值,直到x不是Promise对象。

4.完善Promise

我们现在已经基本完成了Promise的then方法,那么现在我们需要看看他的其他方法。

4.1 catch

相信大家都知道catch这个方法是用来捕获Promise中的reject的值,也就是相当于then方法中的onRejected回调函数,那么问题就解决了。我们来看代码。


            
  1. //catch方法
  2. catch(onRejected){
  3. return this.then(null,onRejected)
  4. }

该方法是挂在Promise原型上的方法。当我们调用catch传callback的时候,就相当于是调用了then方法。

4.2 resolve/reject

大家一定都看到过Promise.resolve()、Promise.reject()这两种用法,它们的作用其实就是返回一个Promise对象,我们来实现一下。


            
  1. //resolve方法
  2. Promise.resolve = function(val){
  3. return new Promise((resolve,reject)=>{
  4. resolve(val)
  5. })
  6. }
  7. //reject方法
  8. Promise.reject = function(val){
  9. return new Promise((resolve,reject)=>{
  10. reject(val)
  11. })
  12. }

这两个方法是直接可以通过class调用的,原理就是返回一个内部是resolve或reject的Promise对象。

4.3 all

all方法可以说是Promise中很常用的方法了,它的作用就是将一个数组的Promise对象放在其中,当全部resolve的时候就会执行then方法,当有一个reject的时候就会执行catch,并且他们的结果也是按着数组中的顺序来排放的,那么我们来实现一下。


            
  1. //all方法(获取所有的promise,都执行then,把结果放到数组,一起返回)
  2. Promise.all = function(promises){
  3. let arr = []
  4. let i = 0
  5. function processData(index,data){
  6. arr[index] = data
  7. i++
  8. if(i == promises.length){
  9. resolve(arr)
  10. }
  11. }
  12. return new Promise((resolve,reject)=>{
  13. for(let i=0;i<promises.length;i++){
  14. promises[i].then(data=>{
  15. processData(i,data)
  16. },reject)
  17. }
  18. })
  19. }

其原理就是将参数中的数组取出遍历,每当执行成功都会执行processData方法,processData方法就是用来记录每个Promise的值和它对应的下标,当执行的次数等于数组长度时就会执行resolve,把arr的值给then。这里会有一个坑,如果你是通过arr数组的长度来判断他是否应该resolve的话就会出错,为什么呢?因为js数组的特性,导致如果先出来的是1位置上的值进arr,那么0位置上也会多一个空的值,所以不合理。

4.4 race

race方法虽然不常用,但是在Promise方法中也是一个能用得上的方法,它的作用是将一个Promise数组放入race中,哪个先执行完,race就直接执行完,并从then中取值。我们来实现一下吧。


            
  1. //race方法
  2. Promise.race = function(promises){
  3. return new Promise((resolve,reject)=>{
  4. for(let i=0;i<promises.length;i++){
  5. promises[i].then(resolve,reject)
  6. }
  7. })
  8. }

原理大家应该看懂了,很简单,就是遍历数组执行Promise,如果有一个Promise执行成功就resolve。

Promise语法糖 deferred

语法糖这三个字大家一定很熟悉,作为一个很Swag的前端工程师,对async/await这对兄弟肯定很熟悉,没错他们就是generator的语法糖。而我们这里要讲的语法糖是Promise的。


            
  1. //promise语法糖 也用来测试
  2. Promise.deferred = Promise.defer = function(){
  3. let dfd = {}
  4. dfd.promise = new Promise((resolve,reject)=>{
  5. dfd.resolve = resolve
  6. dfd.reject = reject
  7. })
  8. return dfd
  9. }

什么作用呢?看下面代码你就知道了


            
  1. let fs = require('fs')
  2. let Promise = require('./promises')
  3. //Promise上的语法糖,为了防止嵌套,方便调用
  4. //坏处 错误处理不方便
  5. function read(){
  6. let defer = Promise.defer()
  7. fs.readFile('./1.txt','utf8',(err,data)=>{
  8. if(err)defer.reject(err)
  9. defer.resolve(data)
  10. })
  11. return defer.Promise
  12. }

没错,我们可以方便的去调用他语法糖defer中的Promise对象。那么它还有没有另外的方法呢?答案是有的。我们需要在全局上安装promises-aplus-tests插件npm i promises-aplus-tests -g,再输入promises-aplus-tests [js文件名] 即可验证你的Promise的规范。


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


我用这个极限推敲法,有效推进视觉设计

涛涛

每当提到设计方法论时,总难免会给人一种比较虚空偏理论,或者说比较难应用到设计实战中的感觉;但是这一次,经过了自己的实践之后,我觉得确实有一个比较靠谱的视觉推进方法可以分享给大家,所以才有了这篇总结。

先直接概括一下这个方法,极限推敲法:遇到难题时,先把问题拆解成一个或者几个维度,选取某个维度,往两个极端进行尝试,随后逐渐中和极端方案,逐步找到自己想要的预期点,明确该维度的方向。

一、使用方法

  • 第一步,提炼一个或者几个关键维度,作为尝试的方向;
  • 第二步,优先尝试两端极限的方案;
  • 第三步,进一步在其间琢磨并尝试方案,直到找到符合预期的方案;
  • 第四步,若一直没有满意的方案,则可以继续以某个方案为基准,精细微调方案进行探索;
  • 第五步,得出最终方向,并最终调整形成终稿。

二、案例

例一:雄狮的logo设计

期望比较简洁的同时,又不能丢了狮子的辨识度,比较纠结的是,这个度该如何把握?

按照极限推敲法来,我们先确立「极简」和「写实」作为关键维度,然后优先尝试两端比较极限的方案;了解清楚两种极限的可能性之后,继续尝试,加减细节,寻找一个符合预期的图形状态;最后的定稿,既保持了鲜明的特征和辨识度,同时也不失简洁。

(推进过程中出方案的顺序在图中用数字标出,「1」即为第一次尝试,以此类推;具体推敲过程不在此赘述。)

例二:网易邮箱大师Tab icon改版设计

在网易邮箱大师5.0改版中,常驻底部的 Tab icon 是单独进行推敲优化的。在之前的版本中,图标的样式相对比较的刻板,偏向纯表意,表现力不强;在5.0视觉改版的策略中,我们是以「优雅」作为一个关键方向进行优化的,所以轴的两端就定为「刻板」和「优雅」,虽然并不是完全对立的两个方向,同时也包含了一些「表意」等其他层面的因素,但是其中有部分感受是存在对立面的,我们就以此作为关键点进行推敲。

可以从图中看出,在不影响表意的前提下,各方案间的图标改动非常微小,在有限的空间内去做一些变化;几经尝试之后,还是选择了更常规更偏表意优先,但是也适当加入曲线因素体现「优雅」的方案作为最终方向。

例三:内容信息流列表设计

期望内容列表的展示能给人以精品的感觉,但又不失去过多的阅读效率。

首先确立以信息量的多少作为关键维度,在形成了初步的样式之后,继续调整在一屏内条目的疏密以及排版来感知信息量的多少;在尝试之后,最终讨论决定,在初期内容源还不够「精」的情况下,还是最右侧方案的信息呈现更为合适,并以此做了最终调整。

三、方法原理

很多时候,极端情况是很难想象或者预判出来的,只有尝试之后,才能更清楚的了解到实际会出来什么效果,达到怎样的预期;在多次推敲之后,就能更好的了解期望的状态到底应该在哪个「位置」,让模糊的概念逐渐变得清晰;该方法最大的好处在于,让尝试变的更有目标,而不是胡乱尝试,让每一次尝试都更有意义。

这个方法也算是视觉标准可量化的一次探索,我称之为「伪量化」,因为最后的决策仍然是依靠感官去衡量判断的;把错综复杂的感官拆解成一些更具体单一的关键维度也是非常关键的一个步骤,是开始「伪量化」的前提。

四、适用场景

在以上所举的例子中,有 logo设计,icon设计和界面设计(所用到的例子都是实战,均为过往项目);其实对象可以是各种各样的设计,只要存在某种程度把握上的纠结,无法预判或者不明确方向的情况,都可以通过这个方法有效的推进并找到当下的最优解。在实战中,假如没有那么多的时间和精力去细化,可以仅选取关键维度,然后在关键维度内尝试方向探索性的方案(粗略的尝试),只要能感受到差异,即是有效的方案;当然如果有充足的时间,那完全可以精细化出方案,这样方案所传达的感受也会更加精准细致。

五、误区

要注意的是,这个方法并不是一种妥协的方式,也不是一种择中方案;我们所要寻找的是在一个维度中感受符合预期的位置,如果适合,也可以是非常激进的方案。

六、注意事项

这个方法只是提供了一种思路,如何更有效的尝试方案来推进视觉产出的思路;其中最主要的环节还是靠自己去摸索出方案的实践部分,去实打实的尝试;在这其中投入精力的多少,也受其他客观因素的影响,例如项目截止时间等等;不过我认为个人的主观能动性才是主导,一定不能懒,要作图,要动起来,仅靠凭空想象方案在脑中「出图」,一来说服不了别人,二来自己也底气不足。只要图形图像语言一直存在,那么我觉得对视觉设计师来说有一句话就是永远的真理:没图说个XX。

智能汽车UI与手机UI的设计区别

蓝蓝设计的小编

随着特斯拉智能电动汽车凭借其全新的电动能源和智能化的大屏操作系统在全球的走红。在中国也引起了强烈的蝴蝶效应,小鹏汽车,蔚来汽车和威马汽车等主打互联网智能的汽车应运而生。在新的一年汽车UI可能会成为下一个设计热点。

么去控制浏览器对资源文件的处理行为

seo达人

如果您想订阅本博客内容,每天自动发到您的邮箱中, 请点这里

通常当用户打开一个资源的url,如果浏览器支持这个格式的文件的情况下,浏览器会尝试去再页面里展示它而不是直接下载。例如一张图片(jpg, png, gif等),几乎所有浏览器都会去将图片在浏览器里面展示。


对于压缩格式的文件(zip, tar, gzip等)浏览器总是会直接去下载它们,另外一些格式的文件,根据浏览器的不同也会有差异的处理方法,例如Microsoft Word文件(doc, docx)在ie浏览器下通常会在浏览器中展示,但是其他浏览器几乎都会直接下载它,同样的对于PDF文件chrome有自己的pdf 转换器它会尝试去在浏览器上展示该文件。

强制下载文件

对于浏览器这种行为,一般情况下是可以接受的,因为用户可以在浏览器显示文件后将文件保存到电脑中,但是一些情况下用户可能希望文件直接被下载而不是在浏览器中被打开,比如有时候用户想去下载一个歌曲,但是浏览器可能回去播放该音频。那么怎么让浏览器强制去下载文件要怎么做呢

a标签的download属性

html5中 a 标签新增了 download 属性,该属性指示浏览器下载url的内容而不是导航到url,因此如果配置了此属性用户会直接下载url的内容。作为开发如果想直接触发该事件我们可以直接用代码模拟a标签和点击事件


    
  1. const link = document.createElement('a');
  2. link.addEventListener('click', function() {
  3. link.download = xxx;
  4. link.href = xxx;
  5. });
  6. const e = document.createEvent('MouseEvents');
  7. e.initEvent('click', false, false);
  8. link.dispatchEvent(e);
  • download属性只在同域时候有效,当跨域请求时候该属性将会被忽略。
  • 当前并非所以浏览器都支持该属性,需要浏览器考虑兼容性。

改变资源格式

浏览器会根据资源类型去判断是否支持,如果支持时会尝试去在页面上展示该资源。浏览器判断资源类型是根据返回头Content-Type的值,因此一方面我们在服务端可以设置返回头字段为文件流'Content-Type': 'application/octet-stream',或者根据一些具体资源直接压缩后传输,浏览器不能分析zip之类的压缩文件所以会直接去下载它们。

配置Content-Disposition

在HTTP场景中,Content-Disposition 消息头指示回复的内容该以何种形式展示,是以内联的形式(即网页或者页面的一部分),还是以附件的形式下载并保存到本地。

  • inline 默认参数表示消息体会以页面的一部分或者整个页面的形式展示。
  • attachment 消息体应该被下载到本地,将参数filename的值预填为下载后的文件名

因此当我们希望该资源被直接下载时候,我们可以设置返回头Content-Disposition的值为attachment


    
  1. //example
  2. Content-Disposition: attachment; filename="fname.ext"

这里设置名称时候,要注意下filename的编码格式。

用户自己在浏览器设置

浏览器既然定义了该行为,根据浏览器的不同用户可能在设置页面去配置某些格式的文件是否希望浏览器去展示该文件。一些有争议的情况下,你也可以提示用户自己根据需求去设置这些参数。


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


设计师如何洞察需求本质,做出一稿过的设计?

涛涛

本文主要从需求类型、表现形式、如何做正确设计来分析,设计师应如何洞察需求本质?如何做正确的设计?

前言:设计师的痛

有这样两种情况在设计师工作中经常会遇到:业务方改来改去,经过几十遍的修改最终选了第一稿;一群非专业的人或领导在设计师身边指来指去,出发点都是「我觉得」主观感受,没一个能提出有点含金量的意见。

  • 客户或业务反复改
  • 众人指点江山

实际工作里我们经常遇到业务方一直在针对几个问题要求设计师在不停修改 :颜色、大小、氛围等。但是却很少看到设计师跟业务方沟通设计结果出现的场景?是为了达到什么目标?导致设计师一直处于不停「改改改」的苦逼状态。

本篇文章内容为:设计师如何洞察需求本质?如何做正确的设计?

一、设计对接的需求来源类型

设计师在工作中接触到的需求一般分为三类:业务需求、用户需求、产品功能需求。

1. 业务需求

业务需求是指产品和公司为了实现商业目标产生的需求。不以结婚为目的的谈恋爱就是耍流氓,不以赚钱为目的的产品也是耍流氓,说来说去最终目的都是为了实现商业价值。

业务需求多数来源老板、公司战略、运营策略等,一般围绕如何提升公司销售额度、如何提升用户量、如何提升利润、如何提升用户转化、如何推广运营等等。

2. 用户需求

用户需求是指通过调研或客服投诉搜集到的用户反馈,一般来源于用户在使用产品过程中的需求。在 kano模型中对用户需求一般分为:基本需求、期望需求、魅力需求、无差异需求、逆向需求。针对这些需求公司都会结合发展战略与优先级去分析,可分为必做、应做、可做、不做。

3. 产品功能需求

基于业务和用户价值进行需求分析的结果,为满足业务和用户提出的一组产品功能列表,功能需求构成了一个完整的产品抽象模型,是团队之间进行产品设计研发的基础。

二、三类需求常见的表现形式

1. 业务需求常见形式

这有个万能的公式:销售额=流量x客单价x转化率

拿某某购物中心举个例子:销售额就是我们听到的某某购物中心在五一期间卖了xxx亿的额度,流量就是五一期间有多少顾客进来了这个购物中心,客单价就是额度除以购买人数,转化率是订单除以访客。在同等访客量的情况下订单越低,转化率越低。

这个公式换到互联网产品中也一样,多少用户进入 app,有多少人产生购买行为,以及每个人购买的单价,这些因素都是决定这个产品最终的销售额。

业务需求是流量增长

在产品发展初期,面临的第一个问题就是访问量,也就是产品的曝光率,用户到达率。我们在生活中会经常看到一些公司请一些自带流量的网红、明星来做宣传为公司带来一些流量。在互联网产品中常见的一些流量增长的策划为拉新奖励、高流量为低流量导流、免费效应等。

拉新奖励:推荐有奖、注册有奖。

导流:高流量为低流量导流。

免费:0元购、秒杀等。

业务需求是活跃度

产品活跃度是指用户每天开启 app 的频次,每天停留的时间。不同属性的产品活跃度的规律也不一定,比如:新闻类、理财类、工具类的产品,针对不同用户、不同属性的产品提升活跃度的方式不一样。

比较常见的提升活跃度的策略是:奖励刺激、有价值的内容吸引、通过成长等级来引起用户参与。

奖励:补贴、答题奖励、分享得积分、返现金、签到、打卡奖励。

内容:推送激活、话题讨论、焦点&热点内容。

荣誉感:排行榜、等级。

业务需求是用户留存

经过运营渠道推广流量提升后,倒入一些新用户,用户经过一段时间体验后是否留存关键在于:用户需求被满足、以及体验流程流畅决定的。当用户发现产品对自己没有价值、体验没有好感就会选择离开。

比较常见的留存策略是:翻倍奖励、产品体验迭代优化、评分、用户成长体系等。

业务需求是品牌感知

用户对同类产品的选择很大程度取决于产品品牌的影响力,业务方在推广中也会有针对品牌提升感知力。

比较常见的有:h5的情感推广、公益活动等。

网易的h5活动在2017年经常刷爆全屏,他们主打的是情感系列,深入人心,不仅提升主品牌影响,也提升了用户对网易哒哒的产品感知。

小结:业务需求设计要点

  • 用户感知:在设计中对设计表现要处理好文案层级关系/视觉氛围吸引度/提升用户注意力。
  • 效果目标:要考虑业务需求的目标是什么,通过什么形式(创意/用户记忆/内容价值)达到拉新、留存、流量的目的。
  • 数据推导:通过数据可以让我们理性分析出用户关注点是什么?
2. 用户需求常见形式

对于用户提出的一些功能的需求,设计师不能盲目听从,需要真正去识别有用的用户、有价值的需求。我们可以通过一些调研、观察,分析用户的特征、习惯、场景和行为,以及期望得到什么效果,挖掘用户的内在动机和原因,只有搞明白了用户需求的本质,才有可能做出用户满意的产品。

KANO模型定义了五类需求,我主要针对下面3类经常遇见的说下,也可以分为3个步骤去做:基本需求 — 期望需求 — 魅力需求。

用户基本需求

指用户认为产品必须要有的属性或者功能,比如:购买流程的完整体验、完整注册流程,这些基本需求一般来源产品初期迭代中的优化完善。

用户期望需求

用户在主流程体验过程中增加一些利于自己快捷、便利的需求,这些功能并不影响主流程体验,比如:购买过程中对收藏、历史价格对比、搜索等,这些期望需求可以转化成一些潜在的竞争优势。

例如:下图用户需求搜集中用户集中在对历史价位走势、提醒等功能的一些期望需求上。

△ 根据用户期望需求调整的设计构思

用户兴奋魅力需求

魅力需求是每一个产品追寻的方向,给用户带来一些超越他们预期的体验。可以理解为在满足用户的基本需求、期望需求的基础上给用户创造附加价值体验,属于创新设计思维范围,可以提升好感及忠诚度。比如:京东推出vr口红试妆体验,天猫推出试装直播等。

小结:对用户需求设计要点

  • 用户集体共性体验:在主流程上识别有价值用户群体,角色化的用户的共同特征,符合用户基本功能流畅、无障碍体验。
  • 体验体验场景维度设计:对用户使用的场景考虑,设计符合用户的习惯的操作。比如:课堂播放,用户使用场景是碎片时间:通勤、吃饭、健身等场景下,用户对产品期望是储存、收听便捷的期望体验。
  • 用户情感维度设计:超越用户预期,带来惊喜、共情。
3. 产品功能需求

注册流程简化

大量注册资料让用户失去耐心,流程简化提升新用户流量。

漏斗数据下的流程转化

在公司xx流程里,我们看到每个页面的流失漏斗,发现用户在购买决策「结果分析」那一步流失特别多,决定砍掉了介绍步骤,把之前5步改成三步决策。

这是一个非常妙的设计:对用户来说,直接触达决策前的结果分析,用户对结果的强感知转化下一步操作,从数据来看这一步转化率提高不少。

△ 公司实际创新项目不做露出具体页面

留存

新用户进入 app 后,一般分为3个时期:振荡期间、选择期、稳定期。震荡期用户流失较多,经过一些市场对比后选择留下的用户会进入一个稳定期。一个用户在产品内留存越久,带来的商业价值越高。

在产品体验流程中根据不同需求,策略也有不同。举一个 ofo 退押金例子:ofo 在不久前遭遇了资金问题,我在退押金这一流程体验中误操作了多次,最主要问题就是信息引导。

△ 主流程中对用户再三挽留

△ 主流程操作中弱化强需求信息

小结:对功能需求设计要点

  • 统一、体验:减少用户页面跳转,能1步完成不要放到3、4步完成。
  • 信息明确:在主流程中给用户传达正确信息关系,减少误操作。
  • 数据验证可用性价值:通过数据观察用户行为,验证需求产生的价值。

四、如何从需求达到设计目标

1. 需求到设计目标的过程

在需求发起到设计目标会经过这样几个过程:罗列与信息搜集➝深入挖掘➝聚焦核心➝创新发散➝实现与落地。

这五步的节奏是:先发散 – 聚拢 – 再发散 – 聚拢。

罗列与信息搜集

  • 需求罗列:根据佐藤可士和整理术,把需求按照类别、优先级进行。
  • 用户调研:定性于定量调研结果得到用户真正的诉求点。
  • 数据结果:通过数据结果能观察用户的行为特征。

深入挖掘

5w:what、when、wher、who、why这五步定位法,是让设计师清楚需求的本质与动机。

聚焦核心:滤 — 炼 — 导

结合用研结果、数据结果、定位结果去除不重要、不必要的信息、提炼有价值的信息、导出核心关键。

创新发散

我们发现前三步是基于一些基础方法论,让设计师具备一些观察用户、分析用户行为、洞悉本质、同理心的训练,目的是让我们把思维聚焦在核心的关键点上,而真正超越预期体验的来源于第4步创新设计思维。

  • 创新设计思维:通过发散寻找针对聚焦点不一样的方案的突破口,很多创新思维来源于多人思维火花的碰撞。
  • 移情设计:在发散后结合用户情感的体验,打造深入人心、愉悦的体验感受。
  • 技术可行性。

实现与落地

  • 草图沟通;
  • 开发—测试;
  • 上线—迭代—优化。

总结:设计的道与术

  • 道:设计创新、价值观、愿景。
  • 术:思维方式、工作流程、洞察力、观察、分析。

对于设计师,解决问题能力远高于单纯作图能力,而了解需求本质是解决问题的前提条件,在没有明确需求的动机和目的,设计方向是迷茫的。洞察、分析是设计师的基本能力,勤于思考分析,做正确有价值的设计。

互联网行业几点意见

蓝蓝设计的小编

在这个国家领导人都在强调打造一个网络强国的时代,互联网行业无疑是大学毕业生入职最好的选择。互联网很大,很多传统行业都在向互联网转型,如果抓住这个机遇,学习一技之能,那么你就将拥有撬动地球的杠杆。

Javascrpit之打字机效果

seo达人

如果您想订阅本博客内容,每天自动发到您的邮箱中, 请点这里

今天来看看怎么实现炫酷的打字机效果。即把一段话一个字一个字的显示出来。

效果图:

实现思路:

首先规定好显示字数的速度即settimeout执行间隔用来控制每个字之间输出速度。再把判断段落的总字数,循环段落总字数来实现一个字一个字的输出。

js代码:


    
  1. var theNewsNum;
  2. var theAddNum;
  3. var totalNum;
  4. var CurrentPosion=0;
  5. var theCurrentNews;
  6. var theCurrentLength;
  7. var theNewsText;
  8. var theTargetLink;
  9. var theCharacterTimeout;
  10. var theNewsTimeout;
  11. var theBrowserVersion;
  12. var theWidgetOne;
  13. var theWidgetTwo;
  14. var theSpaceFiller;
  15. var theLeadString;
  16. var theNewsState;
  17. function startTicker(){
  18. // ------ 设置初始数值
  19. theCharacterTimeout = 50;//字符间隔时间
  20. theNewsTimeout = 2000;//新闻间隔时间
  21. theWidgetOne = "_";//新闻前面下标符1
  22. theWidgetTwo = "-";//新闻前面下标符
  23. theNewsState = 1;
  24. theNewsNum = document.getElementById("incoming").children.AllNews.children.length;//新闻总条数
  25. theAddNum = document.getElementById("incoming").children.AddNews.children.length;//补充条数
  26. totalNum =theNewsNum+theAddNum;
  27. theCurrentNews = 0;
  28. theCurrentLength = 0;
  29. theLeadString = " ";
  30. theSpaceFiller = " ";
  31. runTheTicker();
  32. }
  33. // --- 基础函数
  34. function runTheTicker(){
  35. if(theNewsState == 1){
  36. if(CurrentPosion<theNewsNum){
  37. setupNextNews();
  38. }
  39. else{
  40. setupAddNews();
  41. }
  42. CurrentPosion++;
  43. if(CurrentPosion>=totalNum||CurrentPosion>=1){
  44. CurrentPosion=0;//最多条数不超过num_gun条
  45. }
  46. }
  47. if(theCurrentLength != theNewsText.length){
  48. drawNews();
  49. }
  50. else{
  51. closeOutNews();
  52. }
  53. }
  54. // --- 跳转下一条新闻
  55. function setupNextNews(){
  56. theNewsState = 0;
  57. theCurrentNews = theCurrentNews % theNewsNum;
  58. theNewsText = document.getElementById("AllNews").children[theCurrentNews].children.Summary.innerText;
  59. theTargetLink = document.getElementById("AllNews").children[theCurrentNews].children.Summary.children[0].href;
  60. theCurrentLength = 0;
  61. document.all.hottext.href = theTargetLink;
  62. theCurrentNews++;
  63. }
  64. function setupAddNews() {
  65. theNewsState = 0;
  66. theCurrentNews = theCurrentNews % theAddNum;
  67. theNewsText = document.getElementById("AllNews").children[theCurrentNews].children.Summary.innerText;
  68. theTargetLink = document.getElementById("AllNews").children[theCurrentNews].children.Summary.children[0].href;
  69. theCurrentLength = 0;
  70. document.all.hottext.href = theTargetLink;
  71. theCurrentNews++;
  72. }
  73. // --- 滚动新闻
  74. function drawNews(){
  75. var myWidget;
  76. if((theCurrentLength % 2) == 1){
  77. myWidget = theWidgetOne;
  78. }
  79. else{
  80. myWidget = theWidgetTwo;
  81. }
  82. document.all.hottext.innerHTML = theLeadString + theNewsText.substring(0,theCurrentLength) + myWidget + theSpaceFiller;
  83. theCurrentLength++;
  84. setTimeout("runTheTicker()", theCharacterTimeout);
  85. }
  86. // --- 结束新闻循环
  87. function closeOutNews(){
  88. document.all.hottext.innerHTML = theLeadString + theNewsText + theSpaceFiller;
  89. theNewsState = 1;
  90. setTimeout("runTheTicker()", theNewsTimeout);
  91. }
  92. window.onload=startTicker;
蓝蓝设计www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计、BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、 网站建设 平面设计服务

这7种广泛存在的认知偏差,影响了我们太多的决策

涛涛

如今心理学和行为学已经是UX和产品设计领域当中诸多理论的来源和实践的依据,它们是UI/UX设计师和产品设计师的必修课。

系统的心理学和行为学的知识,对于设计工作其实有着极为深刻的影响。用户体验设计很大程度上是在心理学和行为学的研究基础上逐渐进化和发展起来的,对于想要深耕这一领域的设计而言,心理学和行为学是绕不开的必修课。

心理学和行为学早已并非是单独存在的学科,它们和许多不同领域的知识交汇融合,围绕着人性进行多维度全方位的探讨。如今我们所熟知的许多优秀的产品,也大多从这些领域技汲取营养,不少四两拨千斤借力逆袭的成功案例,也往往借助的是这些深深植根于人性的杠杆。

这两个宏大的命题并非简单几段话说得清楚,但是这当中有许多有意思的现象,非常具有启发性。许多植根于人性本能中的非理性心理和行为,在日常生活中主导着我们作出决策,有的是一些不易觉察的心理效应,有的仅仅是不算太显著的认知偏差,但是在设计和实际产品当中运用,则常常呈现出极为惊艳的效果。

对于这些心理学效应和认知偏差,设计人员需要在自身进行决策时候尽量回避,避之不及确实会面临车毁人亡的局面;在设计过程中,可以巧妙地将这些因素纳入到考虑当中来,因为它们同样可以影响用户,成为撬动产品打开局面的重要的契机。

幸存者偏差

幸存者偏差(Survivorship bias)虽然被称为认知偏差,但是实际上,它更接近于是一种逻辑谬误下产生的一种错误认知。幸存者偏差指的是人往往会注意到某种经过筛选之后所产生的结果,同时忽略了这个筛选的过程,而被忽略的过程往往包含着关键性的信息。

读书无用论是一种最常见的幸存者偏差。读书无用论中最多的说法是XX并没有好好上学但是照样挣大钱了,而XX努力读书反而混得并不好。

这些个案并不涉及到具体数据,从数据角度上来说,也不难解释这种“主观感受”的成因。根据第六次全国人口普查,大专及以上学历的人口仅占总人口的8.7%,高学历者落魄更容易受到媒体的关注,而低学历中的成功者也常常能够吸引普通人的目光,即使低学历人口中涌现的成功者的比例远低于高学历者,由于基数差异,这个数值对比也是相当可观的。正是因为忽略了数据中沉默的大多数,读书无用论这种错误的观点才“得以成立”。

枪击案中的幸存者大多伤的是手和脚,那么是否要加强手脚的保护呢?实际的情况上,在躯干和头部中枪的人,大都没有挺过来,相反更加需要保护是躯干和头部。就像能活着回来的战斗机中弹的通常都是翅膀和机身,因为引擎中弹的大都已经牺牲了。这个案例也许能够帮你更好地理解幸存者偏差。

对于事件发展过程中筛选机制的忽视,容易让人产生错误的结论,从而导向错误的方向,在设计决策过程中,出现这样的过程中,极有可能是致命的。

在需求分析和调研过程中,如果忽视了幸存者偏差,很有可能搜集到的数据,体现的仅仅知识少部分用户的需求和想法,最终将伪需求和小众需求当作主要需求来作为设计易于,则可能让产品从一开始就走向万劫不复。

在搜集产品的用户反馈信息的时候,也很容易遭遇幸存者偏差。绝大多数的用户在正常使用产品的时候,如果没有合理的触发机制,或者产品没有特别突出的特点的情况下,很少会主动“好评”,相反,遭遇问题的用户则更倾向于主动吐槽并分享糟糕的体验和问题,这也很容易造成“这个产品哪哪儿都是问题”的错觉。

基本归因偏差

基本归因偏差这个名词同样源自于心理学,比较学术表述是“对他人行为进行归因的时候,往往会倾向于把别人的行为归因为其内在因素,而低估了情境因素的影响,而对于自身的行为归因的时候,则倾向于把自己的行为归因为外在因素,而忽略自身因素的影响”,基本归因偏差的含义表述可能有点拗口,但是并不难理解。

基本归因偏差是一种重要的人类心理防御机制,是人类进化过程中所产生的一种局限性的思维定势。和复杂多样的环境因素相比,行为者本身的问题是更容易被发现的,所以观察者在观察行为者的时候,诸如社会环境、社会角色、情景压力等外部因素更难以引起注意,将问题更多归因于行为者本身的行为举动和个人内因。比如,当看到某个人生活拮据的时候,会简单归因于他不够勤奋等等。类似的,在面对生活中的诸多不顺的时候,直接归因于“水逆”而很少会仔细思考是否有自身的原因。当事人和观察者对于事件的归因不同,争议和问题往往因此而起。

对于UX设计师而言,基本归因偏差是矛盾和问题的常见的来源,而且这种认知偏差存在于用户也同样存在于设计师本身。无论是面对设计问题,还是做调研,进行测试,涉及到多方面影响因素的时候,基本归因偏差都会对设计方案、设计决策产生影响。

这需要设计师能够清楚地分辨“我的观点”和“我的行为”,能够真正将自己从自己所处的立场、角色、职能上抽离出来,复盘整个局面,首先接纳全部的状况和现实,包括他人的想法、观点,先不去判断对错,而是先接纳所有的状况和全部的元素,明白事物的动态变化过程,不再单一地去判断,任何一个视角必然会存在一个立场,它们是当前事物诸多层面中的一方面而已。

面对复杂多变,但是因果清晰、逐步成长变化的事物,用动态而全面的思维方式来看待,才能真正看清楚事物变化的轨迹,更有针对性地设计,作出合理的决策。

后视偏见

后视偏见(Hindsight Bias)则是另外一种常见的认知偏差,它指的是人在事情发生以后觉得自己在事情发生之前就已经预测到结果了,实际上,他们并不如自己想象的那样能够准确的进行预测。这种行为在观察者眼中常常会被称为“马后炮”。

后视偏见在很多人身上都存在,人的主观性和记忆本身会造成这种偏差。对于设计工作者而言,厚实偏差的存在无疑是会带来负面影响的。后视偏见会让人沉迷于“我早就预料到了”这种感受当中,相应的无法真正从事件中真正汲取到有用的经验,也难于使用公平的眼光来评判客观事物和他人,主观上也很容易选择性忽略许多客观条件和事实。

后视偏见确实会给人带来快感,相应的,会在工作中影响决策的正确性和公平性,从而带来潜在风险。纠正后视偏见的方法并不复杂,在确知事情结果之前,记录下自己的想法,事后做验证,并统计成功与失败的数据。

事件的结果并非是最好的判断因素。在调整好认知之后,对于事件和情况的过程和相关因素进行相对全面的衡量,尽可能少的掺杂主观情绪来进行分析,这样的决策的有效性会更强。

曝光效应

曝光效应(the exposure effect)也被称为多看效应(和多看这款应用并没有关系),接触效应。曝光效应本身的描述也不复杂:人会更加偏好自己熟悉的事物。在社会心理学领域,曝光效应的另外一个称谓是“熟悉定律”,这可能更好理解。

自己总倾向于购买自己熟悉的品牌的产品,谈恋爱的时候总会有绕不开的老同学和老朋友,这些都是曝光效应之下的人类认知偏差。我们常常说的“一旦接受了这个设定还挺带感的”就是对于这一效应的真实侧写。

曝光效应在广告行业、产品营销和社交媒体有着天然的亲和力,对于营销人员、运营人员、品牌设计师和UX设计师而言,这一效应的活学活用是非常有必要的。

不过曝光效应本身并非是无条件的,相反它和产品属性、品牌设计、企业形象以及运营策略有着极为紧密的关联。

曝光效应在许多不同的社会实验当中呈现出一种不稳定性,可能是因为相关的影响因素多而复杂。有的研究表明,即使产品和服务曝光过程中绝大多数的内容是正面的,公司和企业本身的名气高低和品牌形象,同样会影响曝光效应的高低。

曝光效应在发挥的过程中,即使曝光的内容全部是正面的,对于用户的影响也会逐步呈现出一种矛盾性,也就是用户会对产品或者服务产生正面联想的同时,还会产生不利的、负面的感受。

曝光效应发挥效果最好的时段,始终是绝大多数用户对于产品认知不足的阶段,这个时候曝光效应会最大化的对用户产生影响。

值得特别注意的是,一开始就让人觉得厌恶和不适的产品是无法产生正面的曝光效应的,如果一开始存在冲突,曝光效应只会加深误会和冲突。过量的曝光一定会带来厌恶,由于复杂而大量不可控的影响因素,甚至会导致失控式的崩盘。

对于UX设计师和产品的策划和运营者而言,在合适时机加大产品的正面曝光是有效度最高的策略。深入了解曝光效应,才会明白何时放,而何时收。

可得性偏差

可得性偏差(Availability Heuristic)也是一种常见的认知偏差,它是启发式偏差的一种,人们往往会根据自己认知上的易得性来判断事情的可能性,甚至于会根据自己看到和听到的只言片语来做决策,而不是根据统计学数据和系统化的知识来做判断。

可得性偏差同样是一种广泛存在的认知偏差,用户认为自己熟悉的、更容易获得的信息在复杂的系统中发挥着更大的作用,主观上忽略其他的部分。可得性偏差和熟悉定律(也就是曝光效应)有些许共通的地方。

可得性偏差在金融领域有着巨大的影响,许多准备上市的企业会在上市前夕吸引大量的新闻报道,让投资者在信息的狂轰滥炸之下不知不觉地去关心,无意识地去了解,并最终购买这支股票拉高股价。这种策略就是借助投资者的可得性偏差来实现的。下面是谷歌搜索关键词阿里巴巴的频率和阿里巴巴的股价变化情况,两者呈现出一种明显的关联性。

可得性偏差的广泛存在,使得设计师甚至可以在自己的产品中借助有有意识的设计,来引导用户的行为。作为设计师本身而言,可得性偏差同样是会影响到设计决策,因为可得性偏差同样会影响产品设计中的设计决策的走向。消除可得性偏差的方法也不难,需要设计者不要被已知的信息和容易想起来的信息所左右,而是尽量深入地从多个角度来考虑问题,跳出固有的思维定势,多角度看待问题,作出合理的决策。

锚定效应

锚定效应也被称为沉锚效应,它一样是一个听起来高大上但是含义并不复杂的概念,它指的是人们对某人某事作出判断的时候,容易受到第一印象或者第一信息的影响和支配。这种现象就像船只被锚固定在特定位置而无法移动,因此而得名。

人们在做判断的时候,往往会受到第一印象和首个接收到的信息的影响。就像鸟类会将第一个看到的生物视之为自己的母亲一样,人们往往会借助第一印象来作为后续判断一个事物的参考标准,甚至在人际交往中都是金科玉律。即使是没有刻意地去参考第一信息,人们也会在潜意识当中,重视第一次获得的数据。

锚定效应的另外一个层面是对比,当拥有了锚定的参考之后,用户在后续会以此为基准进行对比。对比并不存在绝对意义上的好坏,但是在不同的基准之下,或者说参考的基点不同的情况下,发挥的作用其实是截然不同的。

举个简单的例子,粥铺的服务员问客人“加一个鸡蛋还是加两个鸡蛋”比“要不要加鸡蛋”所产生的销售额高出不少,因为前者会人的心理基点是“要加蛋”,而后一种问法,则让客人先思考“要不加蛋”,前者的转化率自然更高。

正是因为先入为主这种心理现象大量影响着用户的决策,使得市场竞争中,许多企业和产品都不得不去力图争个第一,赢得主导权。

iPhone X的刘海屏对于手机市场有多大影响想必大家有目共睹。而后续的蓝绿两厂的升降式摄像头设计和“真·全面屏”也因为其突出性和强大的“第一印象”的构建,而赢得了无数赞誉。

但是静下心来想想,开机解锁还要等待摄像头升起,好像还是一个蛮奇怪的事情。可是最早的“真·全面屏”确实够惊艳。

免费认知偏差

优惠促销是各种产品销售中最常用到的一种策略,而在花样迭出的促销手段当中,有为数不多的集中手段,呈现出令人惊艳的效果。

免费的赠品和满额减免就是这其中的典型。购买产品A,赠送一款B,这种赠品策略的玩法可以说是效果极佳,早年间阿芙精油声名鹊起的阶段,其中最令用户欲罢不能的,就是他们的“赠品”。用户每一次购买精油产品的时候,阿芙的团队会随之一起发出精油的入门口袋书以及多达五六种精心挑选的精油试用装,这种体贴且让人觉得“狠赚一笔”的心理感受,使得许多新用户迅速转为稳定客户。

置于满多少减多少,则同样是一种成功的促销策略。各种电商平台和购物节都会采用花样繁多的满减服务,相应的,用户在这种服务之下,总忍不住“凑单”。平心而论,真实的情况是,用户购买了自己真正需要的商品,以及一系列为了凑单而同时购买的非必须商品,对于商家和平台而言,这种策略很好的拉高了销量,也减少了许多商品的库存,而用户也感觉自己赚到了,皆大欢喜。

至于“包邮”,我就不赘述了。这个效果同样非常突出。

相比之下,打折促销所产生的效果,好像就没有那么显著和“暴力”了。归根结底,问题是出在“免费”的认知偏差之上。对于用户而言,无论是赠品、满减还是包邮,本质上都会让用户迅速地在大脑中形成一种“免费获得某种东西”的感觉,赠品对应的是免费的物品,满减对应的是免费的现金回馈,包邮则是免费的邮寄服务。

心理学家和行为学家通过大量的实验验证了免费确实和折扣等其他的促销策略有着本质的差别,它所带来的非理性行为要明显太多,免费的吸引力无与伦比,说是认知偏差毫不为过。

你一定特别想点击上面的图片免费下载素材。不好意思它只是个图片。

人类本能地惧怕损失,选择免费的赠品不会有损失,而相比之下选额其他则会让人在潜意识中产生风险感,可能会蒙受损失。这样一来,免费的赠品在人心中的价值就开始高于实际价值,随之而来的就是非理性消费。

在设计的过程中,想要让免费的认知偏差发挥效果,让人快速地感知到这种“免费获得”的感受,应当是明显的,否则很难快速地触发非理性的反馈。

结语

人类先天的生物性无处不在,我们自诩的理智在现实生活中发挥的作用并没有我们想象中那么多,无处不在的各种认知偏差主导了太多的

nubia UI的那些事

蓝蓝设计的小编

说起nubia,很多人的印象当中就是硬件很不错,但是系统很渣,自己也是4年的老牛仔了,对于Nubia的系统,是看着它一点一点成长的,咱们先谈谈Nubia UI 的历史:

DOM常见的操作方式有哪些

seo达人

如果您想订阅本博客内容,每天自动发到您的邮箱中, 请点这里

1.什么是DOM

DOM 是 Document Object Model(文档对象模型)的缩写。


DOM是中立于平台和语言的接口,它允许程序和脚本动态地访问和更新文档的内容、结构和样式。


在 HTML DOM中,所有事物都是节点。DOM 是被视为节点树的 HTML。


什么是节点
2.什么是DOM节点
HTML 文档中的所有内容都是节点


整个文档是一个文档节点


每个 HTML 元素是元素节点


HTML 元素内的文本是文本节点


每个 HTML 属性是属性节点


注释是注释节点


demo
3.常见的DOM属性
属性是节点(HTML 元素)的值,您能够获取或设置。


innerHTML 属性
nodeName 属性


nodeName 属性规定节点的名称


元素节点的 nodeName 与标签名相同


属性节点的 nodeName 与属性名相同


文本节点的 nodeName 始终是 #text


文档节点的 nodeName 始终是 #document


nodeValue 属性
nodeValue 属性规定节点的值。


元素节点的 nodeValue 是 undefined 或 null


文本节点的 nodeValue 是文本本身


属性节点的 nodeValue 是属性值


4.访问元素的方法



通过使用 getElementById() 方法


通过使用 getElementsByTagName() 方法


通过使用 getElementsByClassName() 方法


5.修改元素的方法
改变 HTML 内容


document.getElementById("p1").innerHTML="New text!";


改变 CSS 样式


document.getElementById("p2").style.color="blue";


改html和css
追加子元素的方法


首先必须创建该元素(元素节点),然后把它追加到已有的元素上。var para=document.createElement("p");


创建新的 HTML 元素 - appendChild() 在父元素的最后追加


创建新的 HTML 元素-element.insertBefore(para,child);在指定位置给父级追加子元素


删除 HTML 元素,您必须清楚该元素的父元素:parent.removeChild(child);


替换 HTML 元素:parent.replaceChild(para,child); 方法


6.DOM - 事件
允许 JavaScript 对 HTML 事件作出反应


onclick 事件——当用户点击时


onload 事件——用户进入


onunload 事件——用户离开


onmouseover事件——鼠标移入


onmouseout事件——鼠标移出


onmousedown事件——鼠标按下


onmouseup 事件——鼠标抬起


7.参考文献



8.更多讨论

问题1:getElementsByClassName() 使用时需要注意的地方

回答:它取出来的是个数组,及时只有一个那也是数组对象,所以改变指定元素时需使用角标

问题2:innerHTML 和innerText的区别

回答:innerText替换是会将标签替换成字符串,而innerHTML 则会保留标签插入到节点当中

问题3:dom事件

回答:事件是文档或者浏览器窗口中发生的,特定的交互瞬间。事件是用户或浏览器自身执行的某种动作,如click,load和mouseover都是事件的名字。事件是javaScript和DOM之间交互的桥梁。

事件发生,调用它的处理函数执行相应的JavaScript代码给出响应。


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




日历

链接

个人资料

蓝蓝设计的小编 http://www.lanlanwork.com

存档