首页

复盘和表达能力不好,到底要怎样去提升?

鹤鹤

最近在社群里有人问,有什么总结和复盘的经验吗?感觉自己表达能力很差啊。今天,彩云就跟大家分享一些我的经验,希望能帮到有类似问题的朋友。

undefined 

以上类似的问题,相信不少朋友都遇到过:

1)明明自己做的设计不差,但不懂得怎么去跟其他人推进自己的方案

2)明明自己已经很努力,设计能力也不差,但总是被其他能说的同事占了主导

3)项目做完了,不知道怎么复盘和包装,跟面试官介绍项目的时候感觉讲不出亮点


复盘表达能力在职场中的作用不用多说,大家都懂,那如何提升呢?我给你支几招,绝对好用:

1)善于记录,把想法转化成文字

2)做事多想想附加价值

3)多跟别人分享,输出倒逼输入

4)学会模仿,掌握说话的节奏

5)复盘的具体操作


复盘和表达其实是2个能力维度的事,一个是文字总结提炼能力,一个是语言组织表达能力。所以,我在平时会有针对性的去做下面这些练习,分享给大家。


1.善于记录,把想法转化成文字


脑子中的想法和口中说的话,往往是比较零碎不系统的,而文字则让你有更多时间思考和梳理。不管是从逻辑性还是简练程度上,文案能力都会更加能锻炼一个人的总结能力。


可能从一开始让你写大段的文章对很多人来说,是很困难的一件事,但你可以从小事开始。比如每天写总结,记录自己的想法,这些想法可以来自于学到的知识、突然的灵感或者对某件事的看法等等。


关于记录,我一般先快速写下当时的想法,几个关键字,句子不通顺都没关系。但一定不能拖延,要不然过了那个时刻,你可能就觉得好像没啥值得记录的或者干脆就忘记了。等写的差不多了,再回头来调整语句,调整逻辑问题,就跟设计做改稿一样,当时记录的是初稿,然后再不断优化就好了。切忌完美主义者,动手记录更加重要。


undefined


我自己是从毕业以来一直保持着记录的习惯,而且随着写的量越来越多,写的速度也是越来越快。比如这段文字,就是在班车上20多分钟内写下来的。


undefined


2.做事多想想附加价值


高质量的记录内容来自哪里?除了完成需求外,多想想这件事做完之后,还能给自己带来什么其他的价值没有?比如产品让你做一个设计需求,做完它并过稿是最基本的要求,因为这部分是交换工资的价值。那还需要去思考,如何通过这个需求去放大价值。比如,这个需求能不能成为自己作品集中的一个作品?能不能把这个需求总结成一篇文章?能不能把这个需求做出亮点然后成为自己出去分享的案例等等。


undefined


如果答案是肯定的,有时间的话,一定要去达成后面的这些增值的部分。如果是有这些目标的话,你可能就会很自然的去记录过程中遇到的问题,思考需求的前因后果,记录最终的数据等等,因为你可能将来某一天要出去跟别人讲的嘛。


通过这样的动作,就能加深对需求本身的理解,也就是所谓的深入。有了这些记录和思考,让你再去写一篇总结也就是水到渠成的事了。


3.多跟别人分享,输出倒逼输入


把自己的想法讲出来给别人,其实是一个非常考验人的事。不知道大家有没有这样的感觉,明明这个项目是自己从头到尾跟进的,但让你完整的跟别人讲出来却发现很有难度。


undefined


要跟别人讲明白一个道理,就必须自己真正理解透彻才可以。这就会倒逼你,真正梳理好自己的所学。而当你真正跟别人讲过一次之后,关于这个知识点或者项目的细节,你会印象深刻。


我习惯在学到一个新的知识点后,兴奋的找同事朋友们去分享我的收获,一方面是能加深自己对知识的理解,进一步巩固所学,另一方面也想锻炼自己的表达能力。


演讲也是需要练习的,没有谁天生就擅长,讲的多了,应变能力、总结能力都能提上来。


4.学会模仿,掌握说话的节奏


表达能力怎样提升?模仿是一个很好的方法。我自己经常会去看一些大佬的演讲,像俞敏洪的演讲我是非常喜欢的。然后还有TED的演讲,佩服他们能在短短5-10分钟内,把一个内容很好的讲完,并且听众还容易理解。


学习大佬们在什么情况下会停顿,如何与观众进行互动,甚至包括演讲时的姿态,表情,手势都可以模仿和研究。听的时候,带着目的去观察,相信能学到很多平时不注意的细节。


undefined


注意说话时的停顿和语速,可以理解为设计中的留白和行距。好的留白间距,能使得读者获得更好的阅读体验,演讲也是如此。但说的容易,做起来难,如果缺少练习,在实际的汇报、述职或公开演讲中由于紧张或者其他状况,很难让你从容应对。


这部分也是需要对应的学习和练习的。


5.复盘的具体操作


针对实际项目,一般可以从以下6个方面来进行复盘:

undefined

1)定位目标

从前面说的记录中,或者可以咨询产品、运营,某个具体的需求目标是什么,从产品维度,运营维度,设计维度等多角度进行目标回顾。


2)应对过程

简单说,就是自己遇到这个需求后是怎么去解决这个问题的。仔细思考自己的应对过程,并分析过程中有没有可以改进的点。


3)评估结果

最终的结果,有没有达到预期?是好的还是不好的,把一些数据和用户评价拉出来,作为结果。


4)分析原因

不论结果如何,都应该分析其成功或者失败的原因,从过程中的每一个环节去思考,这些经验都是最宝贵的。


5)发现规律

从原因中找到规律,这个规律也就是所谓的方法论,这些方法论是否具有普适性,也就是你得到的这套经验能不能复用到其他项目中,或者没有做过此类项目的设计师拿到你的方法论,能不能很好的复用你的经验把项目做到你这样的结果或者是能做的更好。


6)沉淀文档

最终,把这些方法论、设计规范、经验教训等等,以文档的形式记录下来,就完成了一个真正的完整项目沉淀。这些也是设计师做需求时的一种价值放大,很多优秀的资深设计师都擅长做这件事。


在面试或演讲时,也应该按照这样的逻辑顺序去讲你的项目,心里有谱,讲话才靠谱。


总结


复盘和演讲,在职场中都是非常重要的能力,但那些能说会道的人,PPT写的很厉害的人,也并非天生就如此。他们也都是后天在工作中不断的练习得到提升的。


像解决所有困难的事情一样,学会拆解,总结能力也是如此。

文章来源:站酷  作者:彩云Sky

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

JS笔记之快乐的数组

前端达人


数组(Array)

概念:数组是一组数据的集合,其中每一个数据称为数组元素,js的数组可以放任意数据类型的元素,数组是一种将一组数据元素存储在单个变量名下的优雅方式。

示例解释:

//普通变量名只能存一个数据 var num = 10 //数组存储一组数据 var arr = [1,2,3,4,5,'a','b','c'] 
  • 1
  • 2
  • 3
  • 4

一、数组的创建

数组的创建方式:
1、通过new关键字创建(不常用)

//注意A要大写 var 数组名 = new Array() var arr = new Array()//创建一个空数组 var arr = new Array(5)//创建一个长度为5的空数组 
  • 1
  • 2
  • 3
  • 4

2、通过数组字面量创建(常用)

//1、使用数组字面量创建空数组 var 数组名 = [] //2、使用数组字面量创建带初始值的数组 var 数组名 = [元素1,元素2,元素3···] 如:var arr = [1,3,5,'a','c','e',7,9]//数组元素可以是任意数据类型的 
  • 1
  • 2
  • 3
  • 4
  • 5

小结:
1.数组是一组数据的集合,是将一组数据存放在同一变量的优雅方式
2.数组的字面量:[ ]
3.声明数组并赋值称为数组的初始化
4.数组中元素可以是任意类型的数据
5.注意:数组元素为字符或字符串要加引号,不加的话解释器会认为是变量从而报错

二、数组元素的访问

1.通过数组索引访问

索引(下标):用来存放数组元素的序号(数组下标从0开始)
        我们可以通过索引来访问、设置、修改相应的元素,注意:数组的长度可以不指定,它可以随元素的增加自动扩容,当访问未赋值的元素时,返回undefined。获取方式: 数组名[索引号]
下面通过例子来演示一下:

//定义数组 var arr = [1,3,5,'a','b','c']---->//注意字符串要加引号,不然解释器会认为是变量而报错 console.log(arr)---------------->Array(6)//输出数组长度 console.log(arr[0])------------->1 console.log(arr[2])------------->5 console.log(arr[4])------------->b
 console.log(arr[6])---------- -->undefined//索引下标6没有元素,所以返回undefined 注意:数组的下标从0开始哟!!! 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

2.通过循环遍历

请看下面代码:

var arr = ["red","blue","green"] console.log(arr[0]) console.log(arr[1]) console.log(arr[2]) //输出结果:red blue green 
  • 1
  • 2
  • 3
  • 4
  • 5

从上面代码观察发现,取出每一个元素只是数组下标改变而已,如果数组元素多的话,一行一行访问速度很慢,为提高效率,我们可以用循环

//1、使用for循环遍历 var arr = ["red","blue","green"] for(var i = 0;i<arr.length;i++){//length是数组的属性,即为数组长度,没有() console.log(arr[i]) } //输出结果:red blue green 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
//2、使用for in循环遍历 var arr = ["red","blue","green"] for(var i in arr){ console.log(arr[i]) } //输出结果:red blue green 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

以上访问数组元素的结果是一样的,使用循环可是很便利的
下面做两个小案例来演示下数组的魔力吧!
案例1
使用数组对一组数求和并求平均值,思路:定义2个变量,一个存总和,一个存平均数,通过遍历数组求和,然后除以数组长度求平均数

案例1实现代码 //使用数组对一组数求和并求平均值 var arr = [1,3,5,7,9,2,4,6,8] //声明一个数组并初始化 var sum = 0; //创建一个变量来存取各数字之和 var avg = 0; //创建一个变量来存取平均数 for(i=0;i<arr.length;i++){ sum += arr[i]; //相当于 sum = sum + arr[i] } console.log("sum:"+sum) //打印sum的值,即所有数的和 avg = sum/arr.length //求平均值并赋值给avg(arr.lengt是数组的个数) console.log("avg:"+avg) //打印avg的值,即所有数的平均数 //输出结果:sum:45 avg:5(这里仅是演示,没有对结果为多位小数进行处理) 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

案例2
使用数组对一组数求最大值,图例思路:把数组第一个元素赋值给max,依次遍历比较,如果有元素大于max,则把此元素赋值给max,遍历后比完max即为最大值
在这里插入图片描述

案例2实现 //使用数组对一组数求最大值 var arr = [1,3,5,7,9,2,4,6,8] //声明一个数组并初始化 var max=arr[0]; //创建一个变量来存取最大值,把arr[0],即1赋值给max for(i=01;i<arr.length;i++){ if(arr[i]>max){ max=arr[i]//判断是否有数组元素大于max,有的话就把大于max的值赋值给max } } console.log("max:"+max) //打印max的值,即数组中最大值 //输出结果:max:9 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

三、数组的操作

1.数组长度及数组扩容

  1. 使用length属性获取数组长度
// 注意!length是属性,不是函数,所以不用加() var arr = [1,2,3,4,5]; console.log(arr.length) //输出结果:5 通常数组的length属性跟数组遍历一起使用,使用循环遍历数组要注意
数组元素最后一位的索引下标是数组长度-1(arr.length-1) 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  1. 使用length属性实现数组扩容
// 创建数组,通过length属性扩容数组 var arr = ['a','b','c','d']; console.log(arr.length)//输出结果为4,数组长度为4 console.log(arr[3]) //输出数组最后一位元素的值为d arr.length=6; //数组长度扩容为6 console.log(arr) //输出结果为6,数组长度为6,第四第五个元素的值为empty console.log(arr[4]) //未赋值,默认为undefined console.log(arr[5]) //未赋值,默认为undefined 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

输出结果入图
在这里插入图片描述
3. 通过索引号实现数组扩容

// 创建数组,通过索引号扩容数组 var arr = ['a','b','c','d']; console.log(arr.length)//输出结果为4,数组长度为4 console.log(arr[3]) //输出数组最后一位元素的值为d arr[4]='A' //给第五个元素进行赋值,因为第5个元素没被占用,所以会在数组后面追加元素 arr[5]='B' //给第六个元素进行赋值 arr[10] = 'C' //给第11个元素进行赋值 console.log(arr) console.log(arr[6]) 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

运行结果如图,可以发现,JS数组可以直接通过索引号进行扩容,而未赋值的元素默认为empty,返回结果是undefined,如果元素不存在,则在数组后面追加元素,若已经存在,则替换数组元素–>arr[3]

在这里插入图片描述

注意:不要直接给数组名赋值,否则会覆盖原有数组!

// 演示 var arr=[1,2,3,4,5] console.log(arr)//打印原数组 arr="有点意思!" console.log(arr) 
  • 1
  • 2
  • 3
  • 4
  • 5

在这里插入图片描述


2.数组的常用方法

 1. shift()删除数组中的第一个元素并且返回被删除的元素,
    同时其他元素的索引向前移动一位 
  • 1
  • 2
var fruits = ['banana', 'orange', 'apple', 'mango'] console.log(fruits) //打印原数组 var x = fruits.shift(); //首元素删除 console.log(fruits) //删除后的数组,数组元素向前索引移动一位 console.log(x) //打印被删除元素的值banana 
  • 1
  • 2
  • 3
  • 4
  • 5

结果如图:
在这里插入图片描述

 2. pop()删除数组中的最后一个元素并且返回被删除的元素 
  • 1
var fruits = ['banana', 'orange', 'apple', 'mango'] console.log(fruits) //打印原数组 var x = fruits.pop(); //末尾元素删除 console.log(fruits) //删除后的数组 console.log(x) //打印被删除元素的值mango 
  • 1
  • 2
  • 3
  • 4
  • 5

结果如图:
在这里插入图片描述

 3. unshift(参数1,参数2···)向数组的开头添加一个或者更多的元素并且返回新的长度,
    同时数组元素的索引向后移动 
  • 1
  • 2
var fruits = ['orange', 'apple', 'mango'] console.log(fruits) //打印原数组 var x = fruits.unshift('banana'); //添加后返回数组长度 console.log(fruits) //添加后的数组 console.log(x) //打印新数组的总长度 
  • 1
  • 2
  • 3
  • 4
  • 5

结果如图:
在这里插入图片描述

 4. push()向数组末尾增添一个或者多个数组元素,并且返回数组的长度 
  • 1
var fruits = ['orange', 'apple', 'mango'] console.log(fruits) //打印原数组 var x = fruits.push('banana'); //添加一个数后返回数组长度 console.log(fruits) //添加一个元素后的数组 console.log(x) //打印添加一个数后新数组的总长度 var x = fruits.push('pineapple','strawberry');//添加多个元素后返回数组长度 console.log(fruits) //添加多个元素后的数组 console.log(x) //添加多个元素后的总长度 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

结果如图
在这里插入图片描述

 5. reverse()翻转数组 
  • 1
 var fruits = ['orange', 'apple', 'mango'] console.log(fruits); //打印原数组 console.log(fruits.reverse()) //打印翻转后的数组 
  • 1
  • 2
  • 3

结果如图:
在这里插入图片描述
6. sort()数组排序,对字符串排序,比较的是ASCII码的顺序

 1>如["a", "A", "1"], 按ASCII码顺序排列后为["1", "A", "a"](数字>大写字母>小写字母)
 2>如["ab", "ac", "abc"], 排序后为["ab", "abc", "ac"]
 3>字符串数字:如["10", "5", "18" , "50"], 排序后为["10", "18", "5", "50"]
 比较方法:
(第一位相同比较第二位,b>c,因此ab和abc在ac之前;再比较ab与abc, ab没有第三位,因此排在abc之前)!!!

如需要对字符串形式的数字进行排序,可以采用以下两种方式:
    1、统一格式,补足位数,如["10", "5", "18" , "50"]中, 将"5"写为"05"
    2、转换为数字,使用特殊方法对数字排序 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
 var arr1 = ["a", "A", "1"] var arr2 = ["ab", "ac", "abc"] var arr3 = ["10", "5", "18", "50"] var arr4 = ["10", "05", "18", "50"] console.log(arr1.sort()); console.log(arr2.sort()); //比较方法:按ASCII码比较排序  console.log(arr3.sort()); console.log("数字排序(5改成'05'):" + arr4.sort()); 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

结果如图:
在这里插入图片描述

3.拓展:数字排序

1、匿名函数当做参数使用数组排序(一维数组)

//语法格式:
arr.sort ( function (a, b) {   //arr为需要排序的数组名
    return a - b               //升序
})  
根据比较函数返回值正负来决定排列顺序,为正升序,为负降序
使用如下: 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
 function sortby(a, b) { return a - b //升序 } //降序就改成return b-a var arr = [10, 5, 8, 50]; arr.sort(sortby)//把排序匿名函数当做参数进行调用 console.log(arr);//输出排序后的结果 //匿名函数的函数名任意 结果如图: 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

在这里插入图片描述

2、匿名函数当做参数使用数组排序(二维数组)

//语法格式:
arr.sort (function (a, b) {   //arr为需要排序的数组名
    return a[1] - b [1]      //升序,降序改为b[1]-a[1]
})  
使用如下: 
  • 1
  • 2
  • 3
  • 4
  • 5
 function sortby(a, b) { return a[1] - b[1] //升序 } //降序就改成return b-a var arr = [["Alice", "95"], ["Bob", "80"], ["Cart", "100"]] arr.sort(sortby)//把排序匿名函数当做参数进行调用 console.log(arr);//输出排序后的结果 //匿名函数的函数名任意 结果如图: 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

在这里插入图片描述

3.拓展:数组筛选(巧用length属性)

将数组[2,4,5,6,8,9,12,15,3,7,26,14]中大于10的元素存入新数组
//分析:
    1、创建一个数组arr=[2,4,5,6,8,9,12,15,3,7,26,14]
    2、创建一个新数组newArr来存放大于10的元素
    3、遍历旧数组,找出大于10的元素
    4、依次添加到新数组中 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
// 方法1,通过变量来增加数组长度 var arr = [2,4,5,6,8,9,12,15,3,7,26,14] //创建一个数组 var newArr=[] //创建空的新数组,用于存放大于10的元素 var j =0 ; //定义变量,用于增加新数组的长度 for(var i = 0 ;i<arr.length;i++){ //for循环遍历数组 if(arr[i]>10){ //判断数组元素是否大于10 newArr[j]=arr[i] //如果arr数组元素>10,则存入新数组 j++; //当存入元素,长度+1,即j++ } } console.log(newArr)//打印新数组 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
// 方法2,巧用length属性 var arr = [2,4,5,6,8,9,12,15,3,7,26,14] //创建一个数组 var newArr=[] //创建空的新数组,用于存放大于10的元素 for(var i = 0 ;i<arr.length;i++){ //for循环遍历数组 if(arr[i]>10){ //判断数组元素是否大于10 newArr[newArr.length]=arr[i]//如果arr数组元素>10,则存入新数组 } //巧用length属性,数组索引从0开始增加 } console.log(newArr)//打印新数组 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

结果如图:
在这里插入图片描述

四、二维数组

概念:二维数组就是一维数组的每个元素是一个一维数组

1.二维数组的创建与访问

// 创建与访问 
  • 1
var fruits = [["1", "Banana"], ["2", "Orange"]];//创建二维数组 var element = ["3", "Apple"]; fruits[2] = element; //给第三个元素赋值 console.log(fruits); //打印二维数组 console.log(fruits[0]); // ["1", "Banana"]//打印第一个元素(数组) console.log(fruits[0][1]); // "Banana"   //打印第一个元素的第2个值 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

结果如图:
在这里插入图片描述

2.遍历二维数组

通过for in循环遍历二维数组

var fruits = [["1", "Banana"], ["2", "Orange"], ["3", "Apple"]]; for (var i in fruits) {//控制外层数组元素 for (var j in fruits[i]) {//控制内层数组元素 console.log(fruits[i][j]);//输出数值中每一个元素 } } 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

结果如图:
在这里插入图片描述
小结:二维数组跟一维数组使用差不多,只是稍微麻烦一点,访问二维数组的具体元素值:arr[外层数组索引][内层数组索引]
遍历二维数组使用双重for循环

3.使用二维数组进行排序(冒泡排序法)

图例解释:
在这里插入图片描述

//代码实现如下 var arr = [5,4,3,2,1]; for (var i = 0; i < arr.length - 1; i++) {//外层循环控制交换趟数,趟数为数组长度-1 for (var j = 0; j < arr.length - 1 - i; j++) {//内层循环控制每一趟需要交换的次数 if (arr[j] > arr[j + 1]) {//判断前面一个数是否大于后面一个数,如果是则交换两数 var temp = arr[j];//临时变量,用于交换两数的临时存取 arr[j] = arr[j + 1]; arr[j + 1] = temp; } } } console.log(arr);//输出排序后的结果 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

结果如图:
在这里插入图片描述

总结

本次笔记主要记录了数组的基本使用,但是数组的妙用远不止这些,需要我们自己去探索。


转自:csdn论坛 作者:想要搞钱

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


JavaScript基础:手写实现JSONP

前端达人

在这里插入图片描述

一、原理

jsonp的核心原理是利用script标签没有同源限制的方式,可以发送跨域的get请求(只能发送get请求)。script标签中的src属性将请求参数和当前请求的回调函数名拼接在链接上。最终由服务端接收到请求之后拼接成前端可执行的字符串的形式返回。这个结果字符串最终会在前端的script标签中解析并执行。

利用<script>标签没有跨域限制的漏洞,网页可以得到从其他来源动态产生的JSON数据。JSONP请求一定需要对方的服务器做支持才可以。

  1. JSONP和AJAX对比: JSONP和AJAX相同,都是客户端向服务端发送请求,从服务端获取数据的方式。但AJAX属于同源策略,JSONP属于非同源策略(跨域请求)。
  2. JSONP优缺点: JSONP优点是简单兼容性好,可用于解决主流浏览器的跨域数据访问的问题。缺点是仅仅支持get方法具有局限性,不安全,可能会遭受XSS攻击。

二、实现

1.正常实现

  1. 将jsonp回调函数的名称callbackName拼接到src上
  2. 构建一个script标签,设置它的src属性
  3. 在全局设置一个callbackName回调函数,等待script标签请求结束,并调用
 /**
     * jsonp获取请求数据
     * @param {object}options
     */ function jsonp(options) { // console.log(options); // 1. 产生不同的函数名(函数名随机) let callBackName = 'itLike' + Math.random().toString().substr(2)+Math.random().toString().substr(2); // console.log(callBackName); // 2. 产生一个全局函数 window[callBackName] = function (params) { // console.log(params); // console.log(options.success); if(params !== null){ options.success(params); }else{ options.failure(params); } // 2.1 删除当前脚本标签 scriptE.remove(); // 2.2 将创建的全局函数设为null window[callBackName] = null; }; // 3. 取出url地址 let jsonpUrl; if(options.data !== undefined){ jsonpUrl = options.url + '?' + options.data + '&callBack=' + callBackName; }else { jsonpUrl = options.url + '?callBack=' + callBackName; } // console.log(jsonpUrl); // 4. 创建script标签 let scriptE = document.createElement('script'); scriptE.src = jsonpUrl; document.body.appendChild(scriptE); }  
  • 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
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40

服务端(express)

router.get('/api/v1', function(req, res, next) { res.json({ 'name': '前端收割机', 'address': '广东', 'intro': '前端技术交流公众号' }); });  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

调用jsonp

btn.addEventListener('click', ()=>{ jsonp({ url: 'http://localhost:3000/api/v1', data: 'name=前端收割机&age=20', success: function (data) { console.log(data); }, failure:function(data){ console.log(数据请求失败); } }); });  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

2.利用promise封装

  1. 声明一个回调函数,其函数名(如show)当做参数值,要传递给跨域请求数据的服务器,函数形参为要获取目标数据(服务器返回的data)
  2. 创建一个 <script src=>标签 ,把那个跨域的API数据接口地址,赋值给script的src, 还要在这个地址中向服务器传递该函数名(可以通过问号传参?callback=show)。
  3. 服务器接收到请求后,需要进行特殊的处理:把传递进来的函数名和它需要给你的数据拼接成一个字符串,例如:传递进去的函数名是show,它准备好的数据是 show('前端收割机')。
  4. 最后服务器把准备的数据通过HTTP协议返回给客户端,客户端再调用执行之前声明的回调函数(show),对返回的数据进行操作。
/**
* jsonp获取请求数据
* @param {string}url
* @param {object}params
* @param {function}callback
*/ function jsonp({ url, params, callback }) { return new Promise((resolve, reject) => { let script = document.createElement('script'); params = JSON.parse(JSON.stringify(params)); let arrs = []; for (let key in params) { arrs.push(`${key}=${params[key]}`); } arrs.push(`callback=${callback}`); script.src = `${url}?${arrs.join('&')}`; document.body.appendChild(script); window[callback] = function (data) { resolve(data); document.body.removeChild(script); } }) }  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

服务器(express)

// 后端响应 // 这里用到了 express var express = require('express'); var router = express.Router(); var app = express(); router.get('/say',function(req,res,next) { //要响应回去的数据 let data = { username : 'zs', password : 123456 } let {wd , callback} = req.query; console.log(wd); console.log(callback); // 调用回调函数 , 并响应 res.end(`${callback}(${JSON.stringify(data)})`); }) app.use(router); app.listen(3000);  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

调用jsonp

// 前端调用 btn.addEventListener('click', ()=>{ jsonp({ url: 'http://localhost:3000/say', params: { wd: '前端收割机' }, callback: 'show' }).then(data => { console.log(data) }); }); 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

三、总结

基本原理:利用 script 标签的 src 没有跨域限制来完成实现。

优缺点:只能 GET;兼容性好。

简单实现:通过 url, params, callback 来定义 JSONP() 方法的参数。



转自:csdn论坛 作者:imagine_tion

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

几个招数让网站快速收录文章

seo达人

 

一、提交网站地图
 
   我们每天更新好站内文章之后,要将网站的地图更新一下,然后提交地图到百度,让其可以通过地图访问你的网站。对于网站跟新比较频繁的可以一周更新一次,更新不频繁的一个月更新一次就行。
 
二、按照百度官方的说法去执行
 
   1、原创内容优先展示。当然,能原创是最好的,但是往往我们在维护的客户网站专业性质很强,我们的水平有限,也只能做到伪原创。
 
   2、对于一些权重比较高的网站,如果采集了一些小站的文章,那么百度可能会不确定小网站的情况,所以小网站可以通过ping机制ping给百度,这样有利于让百度知道哪个是原创的。
 
   3、笔者一般是按照百度官方的这些指示做好,再到一些收录比较快,权重比较高的网站去发表一下外链。站内链接
 
三、网站站内文章之间的链接
 
   不论是文章之间还是栏目和网站首页之间,全部要有一个或者几个链接的路径,这个路径是蜘蛛在你网站上面爬行的一种链接,同时也可以让用户点击的链接,对于网站的收录,排名和权重的提高都是比较不错的。

                                         
 
四、和一些更新比较频繁的网站交换链接
 
   友链的作用大家也都知道的,友情链接对于网站的排名起到一定的作用,同时友情链接又引导蜘蛛在网站之间来回爬行的一种链接,对于网站的排名和收录都是非常有帮助的,所以我们要经常和一些更新比较频繁的网站交换链接。
 
五、发外链吸引蜘蛛的方法
 
   1、很多站长发布外链都是带个网站的首页网址就算了,我认为这种优化方式比较单一,如果你的网站权重低,更新不频繁的话,可能蜘蛛通过链接到了你的网站就不再深入去爬行了。
 
   2、一般更新完文章,就可以去各大论坛和博客发布文章,然后带上刚刚发布的文章地址。这种效果是比较不错的。
 
六、文章模块的设置
 
   1.文章每个页面设置一个最新文章模块,每次更新的文章在各个页面中展示,这样就可以每个页面给这篇文章增加权重,从而让新文章更容易收录。
   
   2.每个页面设置一个随机文章模块,这样百度蜘蛛来这个网站就会保证这个网站是每天更新的,每天有新的内容,这样蜘蛛会更加喜欢你的网站,来网站抓取的频次更高,内容更容易收录。
 
   只要根据以上的几点方法来对我们的网站进行seo优化,那我们的网站就可以很轻松的被搜索引擎收录了。
  
文章来源:飞羽优化
蓝蓝设计( www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计、BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、 网站建设 、平面设计服务
 
 

SEO优化方法

seo达人

网站前言:核心思想 。


1、四处一词

(1)文章标题带关键词

(2)文章关键词标签,描述标签带关键词

(3)文章内容带关键词

(4)其他文章锚文本设置关键词


2、五金定律

(1)你是否重要?

指的是某个url的权重,这个权重可以是首页,也可以是其他页面,一个网站只有权重高,才能有好的排名。

(2)你是什么?

我就是那个关键词,说明你网站关于那个关键词最专业。这里可以借鉴四处一词设置。

(3)你是否是原创

一个好的内容才能更容易排名,百度喜欢新鲜的内容,最差是伪原创。

(4)你是否是最新的

每个页面都有最新模块、随机模块,这样可以保证搜索引擎来抓取每个页面的时候都是不一样的。

(5)你是否是最受欢迎的

指的是网站的PV值比同行高,这个后期可以慢慢培养。




主内容


一、域名的选择

(1)如果是做英文关键词,域名首字母应该是是英文关键字。

例:我想做seo这个词,我的域名是 www.seofeiyu.cn

(2)如果是中文关键词可以随便,最好做成公司品牌的域名,这样有助于以后宣传,让人们记住。

(3)域名可以选择老域名指的是:域名之前做过网站无任何违法乱纪行为,可以继承原网站的权重,不必从新再来。


二、服务器的选择

(1)服务器最好选择云服务器,独立的IP,不要选择空间、主机,这样可以防止别的网站降权影响不到别的网站,和网站被入侵其他网站不影响。


三、首选域,url的标准化

(1)首选域:域名解析,如果有www的和不带www的,我们应该301重定向到一个主域名,这样有利于权重集中,不分散。

(2)url的标准化:网站内所有指向首页的url链接,应该是统一的,不应该有 .index .php 后缀的,这样会让搜索引擎分辨不出哪个是首页,我们应该主动提交,让搜索引擎知道那个是我们的主页。


四、TDK的设置(标题标签,关键词标签,描述标签) 重要

1、标题标签 :关键词1_关键词2 _关键词3 - 品牌词

最重要的词放在最左边,剩下的依次排序,关键词最好出现3-5次左右,关键词越多权重越分散

2、关键词标签:关键词的选择最好在标题中提取组合,关键词写7个左右最好,关键词不要重复堆彻,容易造成降权。

3、描述标签:对公司的介绍,产品的描述等,字数在80-100个字左右,关键词出现2-3次最好,不要重复

TDK设置好了对整个网站主关键字提升起到非常大的作用,反之会对网站排名造成影响。


五、网站的结构

1、因为人们的习惯是从上往下看,从左往右看,所以网站的结构遵从 F 型结构 。

2、网站首页应该有 最新文章模块、随机文章模块、应该还保留一个文章不收录的模块,因为首页的权重最高,可以增加文章的权重入口。其他页面也应该有这几个文章模块,可以增加文章收录,保证蜘蛛来抓页面的时候都是最新的,这样蜘蛛会很喜欢。

3、关键词的布局:网站首页应该做 2-3个主关键词,栏目页2-3个关键词,文章页1-2个关键词,其他关键词分散到聚合页面。

4、网站页面链接最好是静态链接,如果是动态链接可以做成伪静态,因为静态链接蜘蛛好抓取收录,动态链接虽然也能抓取,但链接参数有可能让蜘蛛进入死循环,不利于抓取收录。

5、网站连接到首页应该采用绝地地址,不要用相对地址,好处是给首页集权,防着网站被镜像。


六、网站导航设置

1、主导航:主导航文字设置,最好加上关键词,这样会增加关键字在页面出现的次数,有利于排名,如:seo资讯 seo培训。

2、位置导航:如:seo首页>栏目页,首页链接加上主关键词,这样相当于锚文本链接到首页,增加首页关键词的排名,其他页面也如这种形式。

3、次导航:也称为页脚导航,在网站页脚写一段话,出现主关键词连接到首页,不要重复,做好了可以增加关键词排名,用不好会给网站造成影响,慎用!


七、锚文本的设置 重要!

1、页面锚文本:可以设置两个,一个指向首页、栏目页或聚合页面,另一个指向文章页面。

2、内部锚文本链轮:可以在5-6页面锚文本做成一个链轮,指向一个关键词页面,在指向首页、栏目页或聚合页面,这样页面之间的权重互相传递,给关键词增加权重。 有的说这样不好,慎用!


八、网站外部链接的设置

1、友情链接的设置:

(1)网站友情链接不要超过10个,多了的话会造成权重流失严重,对面的网站也是多了的话,给自己的权重有限得不偿失,如果对方的网站很有价值的话可以不用考虑。

(2)链接的网站也应该注意考察对方的网站有没有降权,和违法内容,如果不注意会牵连自己网站,造成降权等严重后果,友情链接在精而不在多。

(3)链接的网站应该有自己的网站有相关性。

2、外链的设置:

外链可以写软文带上自己网站的链接,发布到各大平台,增加百度抓取的入口。也可以在微博等社交平台留下自己网站的链接。

注意:外链的发布应该考察对方平台的可靠性,有没有被百度惩罚,和违法内容,在这里给大家提供一个平台 “搜外网” 大家可以去试试。


九、网站其他设置

1、robots.txt的设置:这个文件放在网站的根目录下,搜索引擎来网站爬取的第一个文件,可以告诉搜索引擎蜘蛛,该抓取那个不抓取那个,如果不会,可以不用设置。设置好了再百度平台更新下,测试抓取看有没有问题。

3、网站地图的设置:这个很有必要,可以让搜索引擎蜘蛛快速的抓取网站所有连接,可以在robots文件写上,让蜘蛛主动过来抓取网站连接,设置好了百度平台提交。网站地图的形式有xml、txt格式的。

注意:网站地图最多放5万条链接,多了的话可以设置多个地图。

优先级别:allow (可以抓取) 级别高于Disallow (不可以抓取)

4、熊掌号的绑定:在百度注册个账号,登录百度平台绑定网站,网站绑定熊掌号,这样百度会增加对网站的信任度。

5、404页面的制作:放在网站根目录下,网站死链接自动跳转到404页面,用户手动点击跳转选择,不要自动跳转,听说这样有可能会被百度惩罚。




常见问题总结


1、网站内容不收录怎么办?

(1)网站每个页面设置个专门放不收录文章的模块,增加权重入口。

(2)已收录的文章锚文本链接到没收录的文章,权重传递增加收录几率。

(3)百度平台文章诊断把没收录的文章诊断一下,如果没问题可以增加收录。

(4)未收录的文章在首页展示一段时间,增加权重入口。


2、怎么让百度知道你的网站是干什么的?

(1)标题、关键词。描述标签出现主关键词。

(2)页面多次出现关键词,密度在5% - 8% 左右,关键词布局要自然




网站内部优化细节


一、网站选词

1.定词根 比如:seo

2.根据词根,按照需求扩业务,比如:seo培训 ; 扩需求:比如:seo优化工具

注意:
1、围绕 主词(也就是词根)扩词,这样网站的相关度高,优化起来也比较好优化
2、相同的主词,可以放在同一栏目页面优化,比如:网站推广、网络推广


二、栏目标题写法

1、专注主词+主词修饰词+品牌词后跟主词

例如:seo_seo网站优化-飞羽seo

2、专题页面写法:

标题可以写问题,描述可以写答案,吸引人悬念

标题写法:飞羽seo可以免费优化

描述写法:为了回馈这一年来新老客户的信任和推荐,因此过年国家法定节假日期间,飞羽seo免费为大家优化网站作为感谢!


三、为什么网站可以秒排名

1、标题很内容,主题集中

2、标题关键词在内容页面中的密度


四、什么样的内容页面才算一个优质的页面?

有文字、图片或视频、评论、相关内容展示等,内容种类越多页面越优质。

注意:图片占文字内容的60%最好,左右两边各空20%。


五、什么样的内容算是一个好的内容?

不通顺的内容 < 空的内容 < 伪原创内容 < 原创的内容 < 优质的原创内容


六、新站页面如何快速收录(首页收录的越快网站的优化结构基础越好,百度给出的基础评分也就越高)

1、网页打开速度(影响打开速度的有:服务器的配置、图片大小、加载的js和css)

2、标题、关键词、描述标签的设置,关键词标签选词从标题关键词组合设置

3、网站页面的总数量

总数量包括:首页+栏目页+内容页

4、网站每天跟新文章的数量越多,百度抓取的频率越高,收录的几率越高

注意:收录的文章把文章标题放在百度搜索中,出现在百度首页第一位是有效索引,不是第一位的话,只代表收录了,没建立有效索引,不参与关键词排名,只有有效索引的页面才会参与关键词排名。

5、内容质量 内容质量+数量(内容丰富字多)


七、什么样的文章页面容易建立有效索引?

1、页面内容丰富:不少于500字以上的内容+清晰的图片尺寸(水印不影响图片的浏览)+ 访客的评论内容+内容的相关文章+其他(种类越多越好)

注意:内容文章不要便宜主词,如果文章总数便宜30%,那么主词很难优化上去,因为你的网站偏离主题了。


八、总结:秒排名的7项因素

1、TDK的设置(标题标签、关键词标签、描述标签)

2、网站打开速度(百度规定网站1.5秒全部加载完会给网站一个加分)

3、页面质量(内容排版、内容原创度、图片清晰、内容丰富长度)

4、主题集中 (最好是标题里的关键词在内容页面全部有展示)

对等性(整个网站围绕一个主题词根进行延伸和扩展)

丰富性(网页内的每一个版块范围,内容尽量饱满一点,无空缺)

5、需求维度 (页面内容的多样性)

6、关键词频次(关键词的位置远比关键词的密度重要)

tdk的位置

Html前段页面的位置

H标签 > 加粗、> 超链接 > 纯文本

7、非作弊

关键词堆砌:标签关键词堆砌 内容关键词堆砌

恶意点击:不安全的点击系统,容易造成网站被百度惩罚。

链接作弊:发垃圾外链 买外链

什么是垃圾外链:一个网站大量的新闻页面,百度检测到时同一个IP

恶意广告:弹窗广告、下载信息不正确,页面电话展示太多(一个页面留的联系方式太多,百度会认为这是广告的形式)

注意:交换友情链接加分周期是3个月左右,才可以在重新换一批链接


九、总结:诊断网站的15个小细节

1、标题规范

不堆砌关键词、出现违规词


2、描述规范

描述在60个字以内,标题里的关键词出现4词左右

3、内容质量检测(原创度)

优质的原创内容所在网站的比例,比例越高,排名越容易获得

4、内容排版检测(浏览体验)

字体工整、图文并茂

5、内容丰富度检测

页面内容字数多,并且内容价值高(非常清晰的吧当前内容话题讲述明白)

6、网页广告检测

无任何弹窗广告,不影响用户体验

7、外部链接质量检测(链接质量)

外联的质量,看交换的网站是否导出的链接过多、网站是否降权

8、网页速度检测

网页打开速度在1.5秒以内会有百度加分,移动端也是

9、URL规范检测

尽量无中文,不利于搜索引擎识别

URL层级最好控制在3层左右

静态链接比动态链接好抓取,动态链接可改为伪静态链接

10、页面抓取检测

网页打开正常,并不一定百度蜘蛛也能正常打开,去百度平台看下。

网页打不开的因素:服务器配合低,或者服务器不稳定

11、网站安全检测

通过百度网站安全检测功能去检测即可

12、死链接检测

是否有打不开的页面链接,有的话七百度平台提交下

13、恶意点击检测

突然性的高流量,会引起百度的关注,如果被百度发现作弊,会引起降权

14、网站收录检测(索引)

去统计网站所有页面收录的比例,把没收录的文章去百度提交下,做下锚文本链接,首页展示下,争取收录

15、网站排名分析

统计每段时间的排名变化,并分析原因做了那些操作,不断去完善他

 

文章来源:飞羽优化

 

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

 

 

APP闪屏的设计门道

涛涛


一、启动页与闪屏的区别

首先我们要知道APP的启动页和闪屏不是一个东西,启动页是用户打开产品第一眼看到的页面,闪屏是启动页之后出现的页面。

1. 启动页

启动页是一个APP必不可少的页面,在iOS规范中,上架AppStore必须有启动页,Android系统会有1-2s的白屏,所以两个端都需要启动页。苹果官方给的解释是,为了增加APP启动时的用户体验,确实如此,当打开一个产品时,首页内容都需要一定的时间加载。

启动页的设计角度一般是品牌信息传递,建立用户与产品的认知,一般时常都在2s以内(看网速和手机性能)。启动页的设计一般不做动画效果,因为只要是动画,就会让用户感觉等待时间变长了。

2. 闪屏

闪屏是启动页后面紧接着出现的页面,当然有很多产品没有闪屏,如微信、淘宝等,因为产品的定位和运营模式不同。闪屏与启动页不同,闪屏是用来运营的,比如投放日常广告、活动运营、节日等等,大多数产品闪屏出现的时间是3s或5s,一般都提供“跳过”按钮。

闪屏的设计为了运营,所以可以适当有动画效果或者视频,目的是能够更加吸引用户,加深印象或是提高点击率。

3. 为什么启动页不能用来运营?

启动页是写在安装包里面的程序,如果更换就得发版,闪屏的程序设计是从后台配置完成,所以可以满足日常更换。

二、如何减少用户等待感知

对用户来讲启动页和闪屏展示的时间越短越好,那时间减少不了,就可以通过设计让用户对时间的感知变少,从而提高体验。看下图,两张图通过平滑过度,给用户的感觉是一张图在变化,这样时间上给用户的感知是变快的。

【精华篇】APP闪屏的设计门道

用户等待感知短

当然对于不同的产品,设计的倾向性是不一样的,比如网易云音乐,启动页是强烈的红色,然后生硬的切换到一个跟启动页没有任何视觉关联的闪屏。这样的设计形式,用户等完一个页面,又等一个页面,那就会给用户在时间上的感知是变长的。

【精华篇】APP闪屏的设计门道

品牌感知传递强

但是,对于一个音乐产品,渲染产品调性传递品牌,要远远大于减少用户等待时间的体验。所以,从这点来看,网易云音乐这样的设计形式,也是非常恰当的,设计应该根据产品的定位,来确定设计的倾向性。

敲黑板,划重点! 下面的内容才是本文重点!

三、定义闪屏设计尺寸

手机尺寸那么多,闪屏设计尺寸应该如何定义,很多APP的解决方案是使用两张不同比例的闪屏,还有一部分APP是区分系统,iOS适配一张图,Android适配一张图。

下面我以小米商城闪屏的改版为例,分享如何用一张闪屏尺寸适配所有机型,并详细介绍适配的原理。下图是改版前的闪屏,需要上传两张图,一张1080*2070(不带底部logo),另一张是720*1280(带底部logo)。

【精华篇】APP闪屏的设计门道

老版本闪屏需要上传两个尺寸图

两张图,设计人员就需在两个模版上进行排版设计,小米商城闪屏更换频率非常高,这样其实会付出很多时间成本。所以,我们团队尝试使用一张图适配所有机型,这其中的难点就是,找到一个合适的尺寸适配所有手机,并且闪屏内容的呈现在任意手机上都得合适,内容不能被裁剪。

先跟大家普及一下小米公司APP的设计稿尺寸,因为小米手机是Android系统,所以UI设计稿会优先适配安卓的主流手机,即1080*2340,这个尺寸接近iPhone12的比例和尺寸,切图相当于3倍图。

所以,闪屏宽度设定一定是1080px,然后高度分为两部分组成,一个是内容运营区(闪屏内容设计区域),一个是logo位,如下图所示。

【精华篇】APP闪屏的设计门道

蓝色部分为闪屏

logo位部分由开发写到程序中,所以闪屏页面高度要去掉logo位高度。闪屏设计尺寸即是蓝色部分,正常来看应该是主流长屏分辨率手机与现存短屏分辨率手机的平均数即可,但其实并不可取。

因为,短屏分辨率手机毕竟使用人群是少数,设计的宗旨一向都是优先考虑大多数用户。所以,定义闪屏尺寸的原则是让类似小米11、iPhone12等主流比例手机呈现的完美,短屏分辨率手机如iPhone8只要呈现的不出错即可。

以安卓主流分辨率1080*2340为例(这个比例接近iPhone12),如下图,logo位高度设定为270px,把这个图切给开发,让开发等比缩放去适配所有手机即可。

【精华篇】APP闪屏的设计门道

很多安卓手机底部会有一个系统高度,这个位置不可以占用,但开发可以改变颜色,所以这部分颜色可以跟启动页或闪屏颜色调成一致。页面分辨率2340减去270的logo位就是2070,这就是长屏主流手机闪屏大概要呈现的高度,然后需要用这个尺寸去兼顾短屏分辨率手机。

把短屏手机也计算出来,安卓最短的手机比例是16:9(比例等同于iOS的iPhone8),以1080*1920为例,如下图,1920同样是减去logo位的270像素等于1650。

【精华篇】APP闪屏的设计门道

接下来就是最关键的时刻,要用1080*2070和1080*1650两个尺寸设定闪屏的设计模版。先说一下手机的适配原理,如果一个张图片的比例和手机屏幕不一致,开发设定是撑满手机屏幕,这时手机上展示的图片就会出现上下或左右被裁剪的情况。

所以,综合两个不同比例的尺寸,短屏手机裁剪上下,长屏手机裁剪左右,然后把呈现的内容保证在不被裁剪掉的区域。

【精华篇】APP闪屏的设计门道

闪屏模版尺寸设定尝试

最后经过很多次的尝试,最终选用1080*1920作为闪屏的设计尺寸,向上向下适配内容的呈现都非常合适。下图是iOS和安卓手机的适配效果,向下适配16:9的手机(裁剪上下),向上适配20:9的手机(裁剪左右)。

【精华篇】APP闪屏的设计门道

长短屏手机闪屏适配效果

【精华篇】APP闪屏的设计门道

定制闪屏设计模版

模版中页面上方留了较大面积,是为了让标题内容能够呈现在长屏手机更恰当的位置上,长屏手机内容靠上,看上去不会很舒适。底部空间留的较少,因为底部再加上logo位的高度,就会让页面看起来很协调。

最终小米商城的闪屏就是用一个尺寸适配了所有分辨率的手机,思路和适配原理都已经讲清楚,如果自家的产品是优先满足iOS手机,完全可以用上面介绍的方法尝试。

敲黑板! 下面内容可能会颠覆你对UI设计的认知!

四、“跳过”按钮位置设计

闪屏尺寸说完了,闪屏上面还有一个“跳过”按钮,这个按钮的位置设计非常非常重要,而且大有学问,会牵扯到广告学,按钮合理的设计会让一个产品的收入倍增。

还是以小米商城的闪屏为例,之前的闪屏“跳过”按钮在页面右下角,新版我把“跳过”按钮放到了右上角,为什么?

【精华篇】APP闪屏的设计门道

可以肯定的是,“跳过”按钮,放在右上角,用户体验是不够好的,右下角用户操作起来才会更便捷,改为右上角其实就是为了让用户少点击。用户不去点击“跳过”按钮,那闪屏内容曝光的时间就会更长,这符合小米商城自营平台的定位,自营平台闪屏展示都是自己平台的内容。

有很多产品闪屏的“跳过”按钮,放在右下角,如微博、网易云音乐,优先满足用户体验,是因为广告的性质不同。

【精华篇】APP闪屏的设计门道

“跳过”按钮在右下角的产品

在广告学中有这样几个词:CPS、CPC、CPM、CPT。

  • CPS:Cost Per Sales是一种广告的推广方式,是通过实际的销售量进行收费,比如每卖一单收取多少钱,转化越多,收入越多。
  • CPC:Cost Per Click这种推广方式是,按照点击量来进行收费,点击一次,收一次费。
  • CPM:Cost Per Mille此种推广方式是按曝光量进行计算收费,只要用户看见这个广告,就会计费一次。
  • CPT:Cost Per Time这种推广方式是,通过时间进行收费,比如包一个月包一个季度等。

了解完常见的几种广告推广方式后,我们再看一下微博和网易云音乐两款产品。他们的广告如果是CPM(曝光量)和CPT(包时间)的推广方式,那“跳过”按钮放到右下角可以提升用户体验,还不会影响推广的收入。

再来看一下脉脉和花瓣两个产品,他们的推广方式大概率是按CPC(点击率)或CPS(转化率)收费的。闪屏的“跳过”按钮放在右上角,内容的设计形式上强调并引导用户去点击广告。

【精华篇】APP闪屏的设计门道

“跳过”按钮在右上角的产品

聊一下脉脉和花瓣的闪屏设计,脉脉把闪屏设计成弹窗的形式,用一种骗的形式,引导用户点击假弹窗的“知道了”或关闭图标。用户的点击行为与心里目标完全不一样,这样的设计虽然能让平台获得更多的广告收入,但也损害了用户对平台的好感。

花瓣闪屏的设计较为友好,实实在在的告诉用户这是广告,你有兴趣就点“去看看”去了解,没兴趣就等一等,或点击“跳过”。花瓣这种设计形式较为适合CPS(转化率)的推广放过,因为点击了解的用户,都是对广告兴趣的精准人群。

五、后语

最后总结一下文章的内容,启动页和闪屏是两个东西,通过设计可以让用户等待的时间感知更快,从而提高体验。闪屏可以用一张图适配所有大中小手机,闪屏的“跳过”按钮大有学问,合适的设计可以让平台的收入倍增。

设计从来都是用来满足商业目的的,一个小小的按钮设计,都会很大影响到平台的收入,所以UI设计任何时候都需要清楚的了解产品定位以及商业模式,这样才能有效的产出设计。

文章来源:人人都是产品经理  作者:吴星辰

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

两个图标改了13版,我是如何逐步优化的?

之晨

今天这个需求是直播间 pk 后,主播胜利与失败的两个状态设计,过程经历了很多次修改,现在来分享一下自己的感受。

  • 状态还是需要有一些设计感的
  • 结构不能乱画,要有基本逻辑在
  • 颜色层级一定要分明
  • 根据情绪设计形象
  • 根据情绪选择色系
状态还是需要有一些设计感的

其实最开始我有点天真了,以为这种状态直接画个圈圈,写个胜、败两个字,然后做下颜色区分就完事了:

两个图标改了13版,我是如何逐步优化的?

但这根本行不通,看起来像是未完成的交互稿,一点设计感都没有。

于是我开始思考如何增加设计感了。

结构不能乱画,要有基本逻辑在

如何增加设计感?

其实无非就是给这个状态增加细节呗,尤其是这个胜利的状态,我想到麦穗、翅膀等元素,最后选用了翅膀的元素,绘制了一个样例如下:

两个图标改了13版,我是如何逐步优化的?

但被说到这个结构是乱画的,哈哈,一下戳到了我的痛点,确实是胡乱画的,各种圆圈没什么逻辑。

于是后来仔细思考了一下,把圆圈分为三层:

  • 一层底托
  • 一层中间层放主形象
  • 一层修饰层放在最外面

效果如下:

两个图标改了13版,我是如何逐步优化的?

然后最中间放主形象,两边放翅膀,顶部放皇冠,最终效果如下:

两个图标改了13版,我是如何逐步优化的?

这样整体看起来结构就是清晰的。

我们可以看下结构清晰和不清晰的对比:

两个图标改了13版,我是如何逐步优化的?

后面因为整体调性需要可爱一点,所以把翅膀改成了比较圆润的造型:

两个图标改了13版,我是如何逐步优化的?

效果还算不错。

颜色层级一定要分明

其实这个点是我经常和大家强调的,但有时候确实是当局者迷,自己也容易犯这样的错,比如最开始的那个效果,很明显中间暗部和周边亮部没有拉开层次:

两个图标改了13版,我是如何逐步优化的?

所以在后面的取色上,会把中间暗部做的很深,形成鲜明对比:

两个图标改了13版,我是如何逐步优化的?

这样层次才会更加明显,不然就会糊在一起。

根据情绪设计形象

最开始本来想用圆形来做失败的状态,但是后来发现有点太惨淡了,于是想着还是加点细节吧,怎么加呢?

这时候是需求方那边给的一点灵感,说失败不就是很颓的样子,就拿把翅膀下沉和胜利形成反差不就可以了,我感觉说的挺有道理,于是就尝试做了下:

两个图标改了13版,我是如何逐步优化的?

确实还可以。

根据情绪选择色系

本来想着胜利用紫色、失败用蓝绿色:

两个图标改了13版,我是如何逐步优化的?

但是发现蓝绿色多少还是会有点分散“胜利”的注意力,于是就在思考,失败是表达一种负向的情绪,那是不是可以用灰一点的颜色?

于是尝试了几个不一样的灰度:

两个图标改了13版,我是如何逐步优化的?

最后选择最后最后那个几乎接近灰色的版本,干脆一点,对比强烈一点。

我们看下最终效果:

两个图标改了13版,我是如何逐步优化的?

再看下过程稿吧:

两个图标改了13版,我是如何逐步优化的?

调整了很对次,过程还是挺值得记录的,回头一看,很有成就感!

总结

以上就是这个小需求过程中总结的一些知识点,后面还会加入一些微动效,增加氛围感。

希望可以给大家一点灵感和启发。



文章来源:优设网       作者:菜心设计铺



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

我们对2021年的用户体验有何期待?

鹤鹤

用户体验设计是一个动态领域,每年都会为我们带来新的趋势,最近十二个月也不例外。全球新冠病毒大流行和国家封锁使我们与数字世界和现实世界之间的互动方式发生了突然的变化。人们不仅开始在网上花费更多的时间;他们的思考方式和表现方式也发生了不同。

本文主要阐述2021用户体验的趋势,我们相信这将在新的一年里占据主导地位。查看一下您可以利用哪些趋势超越竞争对手并在人群中脱颖而出吧。


语音介面

我们在2020年UX状态预测中讨论了语音用户界面(VUI)。如今年所示,语音指令仍然是UX设计中最热门的趋势之一。可以肯定地说,明年不应忽略它。

毫无疑问,到2021年,语音聊天机器人和虚拟助手将继续流行。用户在数字体验方面一直在寻求简单性和效率。市场需求,高期望值以及人工智能技术的迅速发展使品牌别无选择,只能在其产品中包含基于语音的功能。

已经有许多企业为广泛的VUI实施奠定了基础。例如,星巴克推出了一种名为My Starbucks Barista的基于AI的聊天机器人。其目标是通过允许用户通过语音命令购买自己喜欢的饮料来改善咖啡订购体验。



简约的UI

极简主义可能是当今视觉设计中最明显的趋势之一。用户正在体验越来越多的网站所有者想要传递的关键消息。Cookie弹出窗口,打折广告和各种通知旨在吸引和转化网站访问者,但它们也吸引了我们的注意力。这就是简约的以用户为中心的设计应运而生的地方。

但是,“极简主义”并不意味着“沉闷”或“原始”。意思是“优雅”和“高效”。尽管必须使用数量有限的颜色,设计元素和明亮的组合,但UX设计人员仍然有很大的发挥空间。此外,元素的功能最为重要,正确突出产品功能并传达正确信息的能力需要大量的创造力。相反,仅具有装饰目的的组件正逐渐失去其重要性。




内容,消息和导航的清晰度是UX设计的简约方法的另一个重要方面。信息过载对于大多数现代用户来说是一个痛苦,这意味着网站所有者应努力使自己的UX文字简洁明了。


负空间

负空间是用户体验设计的一种大趋势,它已经脱离了对简约UI的市场需求。简而言之,负空间是页面布局中对象周围(宏空间)或对象内部(微空间)的空白区域。它已经成为独立的设计元素,在视觉美学和用户体验优化中起着至关重要的作用。谷歌的主页,苹果的官方商店以及一些用Webflow创建的网站就是很好的例子。






留出一定的空间,在设计中添加“寂静的感觉”,必须是始终有意义的。否则,用户可以将其视为缺少信息。负空间的主要功能是整理网页,以吸引用户对关键对象和消息的注意。建立清晰的内容层次结构有助于UX设计人员将用户的注意力吸引到最重要的内容上。




负空间有时也称为“空白区域”或“留白区域”。所有这些术语都是可以互换的。当区域中没有元素时,您还可以在深色模式或任何其他颜色下使用此用户体验趋势。

不完美的元素

在发生COVID-19大流行之后的在线体验将与我们习惯的在线体验不同。UX设计以相关方式反映了缺陷。为了使品牌的数字化形象更具关联性,设计师有意在版面设计中实现了一些“缺陷”。 它可以是任何东西,从手绘对象到构图或页面的不寻常元素。







通常,不完美的设计可以很好地证明品牌的身份并突出其独特性。但是,如果要应用这种UX趋势,则必须记住一个关键规则:必须保持平衡。如果你做得过多,那是行不通的。



同态

中性风格是UI设计中其他两种大规模方法(拟态和平面设计)的组合,它们通常被认为彼此相对。拟态化是关于模仿现实世界中的物体以及我们与物体交互的方式。

几十年前,当需要使用超现实元素来创建直观且用户友好的UI时,它很流行。垃圾桶就是例子之一。另一方面,平面设计是一个更新的,简化的概念,围绕二维元素,极简主义和鲜艳的色彩。

同质性兼顾了两者的优点。它使用图形强度大的元素,阴影和渐变来使按钮和卡片类似于自然界中的对象,而不是精确地重新创建它们。中性风格不会将现实主义推到极致。取而代之的是,它努力实现浅色和微妙对比度的“柔和”外观。



在过去大约一年的时间里,同质化一直是UI / UX专业人员中讨论最多的主题之一。尽管仍然没有很多真正的数字产品的用户界面遵循这种方法,但许多设计人员对该概念感到兴奋。像Behance和Dribbble这样的专业平台已经包含了许多同态的例子。因此,我们有充分的理由相信,这一趋势最终将在2021年出现在我们的手机和笔记本电脑中。



3D元素和视差

视差效果和3D元素并不是UX设计中的全新内容。我们已经看到了如何在许多Web设计解决方案中实现它们。但是,我们仍然观察到这些用户体验趋势正在逐步普及。

首先,它的使用量显着增加。如今,3D元素和视差效果已不再是网络上奇特的事物。它们更为常见,特别是在代表着脱颖而出的时尚和电子商务品牌的网站和应用上。




另一种趋势是尝试在一个界面中结合视差效果和3D图形。使用视差滚动时,网页背景和前景元素以不同的速度移动。仅凭它就能产生深度感。通过为此添加3D对象,您可以创建一种真正的身临其境的体验,并将在用户的记忆中保留很长时间。


 



  

不对称

随着我们设备屏幕的变宽,UI / UX设计中出现了不对称趋势。通常,不对称是野兽派的一种属性,这是艺术和网页设计中与极简主义相反的一种风格。但是,如果与其他残酷的设计元素分开使用,它可以使您的网站看起来有趣,同时使它保持微妙和优雅。


不对称布局的UI / UX趋势通常与其他创造性的Web设计技术一起实现,例如破碎的网格,重叠的元素和分割的屏幕。您也可以将不对称性应用于排版。如果操作正确,它将使您的品牌信息更加引人注目和令人难忘。




但是,请务必记住,非对称设计并不意味着“随机放置的UI元素”。在布局上定位对象的不寻常方式应该引导用户的眼睛朝正确的方向前进,并帮助品牌强调重要信息。

动画制作

今天,当我们在互联网上进入随机网站时,很可能会看到GIF,微型动画,动画插图或其他一些运动设计元素。动画在用户体验设计中仍然很流行,并且使用频率不断增长。

除了具有视觉吸引力之外,移动物体还可以改善用户参与度并简化导航。他们还可以为数字产品或服务注入生命,使它们更具个性。



如果要在用户界面设计中使用这种软件开发趋势,那么明智地执行此操作至关重要。漂浮在屏幕上没有特定目的的对象可能会使访问者感到困惑,从而促使他们甚至比计划的要早离开网页。动画元素也不应使用户界面不必要地复杂。它们始终必须是相关的,有价值的和平稳的。

信息架构

信息体系结构是在页面上组织和构造不同内容的方式。它是以用户为中心的设计(UCD)的基本要素,旨在使用户的数字环境更加舒适。与用户研究和可用性测试一起,构建有效,有用且一致的信息体系结构是UCD流程的必不可少的阶段。



为了创建一个实用的信息体系结构,UX设计人员需要对产品的目标受众,其行为以及使用数字解决方案的原因有深入的了解。用户应该无需花费太多精力就能获得所需的结果。因此,在设计过程的线框阶段始终必须考虑用户的目标,清晰的导航和内容表示(包括盲点监视)。

最后的想法

我们创建此文章是为了让您在当今竞争异常激烈的数字世界中站稳脚跟。这里提到的所有UX趋势绝对可以改善几乎每个软件解决方案或网站的用户体验。即使用户期望很高,它们也可以使您的产品在视觉上对目标受众更具吸引力。

文章来源:站酷  作者:ZZiUP

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

你要知道的交互成本

鹤鹤

交互成本是用户在与网站交互中必须执行以达到其目标所需的精力(无论是精神上还是物理上)。

理想情况下,我们希望用户访问网站并在他们眼前找到他们正在寻找的答案。这将意味着零交互成本,并且是检验可用性的里程碑。

不幸的是,由于大多数站点和应用程序都提供了用户可能想要做的许多事情,因此很少能达到零交互成本。大多数时候,用户必须四处浏览,阅读,可能滚动,找到有希望的链接,单击它,等待页面加载,然后重新进行整个过程。有时,一个新窗口可能会在现有窗口的顶部弹出,在这种情况下,用户必须将注意力转移到新窗口上,也许还需要回头看旧窗口以将信息集成到两个窗口中。在其他情况下,用户可能需要记住一页上的信息,然后将其应用于另一页。所有这些动作都需要认知努力并弥补了交互成本。

可用站点将实现各种用户目标所需的交互成本降至最低。也就是说,它们最小化:

  • 阅读

  • 滚动

  • 环顾四周以查找相关信息

  • 理解提供的信息

  • 单击或触摸(不会出错)

  • 打字

  • 页面加载和等待时间

  • 注意开关

  • 内存负载-用户必须记住的信息才能完成其任务


这些用户动作对总交互成本的贡献不同。它们的相对重要性可能取决于用户-例如,阅读障碍的用户阅读时间可能比点击周围的时间困难,而操作障碍的用户可能会觉得点击困难。它们还取决于设备-连接到高速网络的台式机上的页面加载可能微不足道,但是如果蜂窝网络覆盖较慢,则移动设备上的页面加载可能会花费很长时间。

许多可用性准则都解决了使交互成本的各个组成部分最小化的问题。例如,网络写作规则通过推荐要点和简短的要点句子和段落来降低阅读成本。

交互成本示例

让我们举一个简单的例子。假设我们要查找“仪式”一词的来源。我们将使用Dictionary iPhone应用程序执行此任务。我们将忽略在手机上查找应用程序所涉及的成本,并且在启动Dictionary应用程序后立即开始分析。

启动应用程序后出现的第一件事是启动屏幕。


在这一点上,交互成本包括等待几秒钟,以使启动屏幕消失并为应用程序的第一个可操作屏幕腾出空间:


在此页面上,互动成本来自定位搜索框。幸运的是,搜索框非常显眼地位于页面顶部,因此我们可以放心地认为,用户只需很少的精力就可以找到它。找到后,用户需要触摸搜索框以将输入焦点移到搜索字段中。搜索框是一个相当大且易于触摸的目标,因此交互成本也可能会降至最低。


接下来,用户必须编辑搜索查询。

当输入焦点在搜索字段中移动时,自动建议的词将自动显示。

用户必须查看自动建议并确定它们不相关,然后清除当前查询。他们可以通过按右侧的灰色x按钮(如果他们熟悉iOS规则)来执行此操作,也可以通过按触摸屏上的Delete键来逐个清除字符。

删除后,用户将开始输入目标词“仪式”。当他们开始输入内容时,关联词会显示在下方。用户可以检查关联词并决定是否要继续键入或停止并选择关联词。


选择(或键入)单词“ ceremony”后,用户必须按Search进入结果页面。他们需要等待片刻才能显示新页面:


用户必须推断Origin可能包含有关单词来源的信息。(对于大多数用户而言,这是一个简单的推论,因此认知成本很低;但是,如果改用“词源”一词,则某些用户可能会在阅读和理解其含义时遇到更多麻烦;因此,“来源”是更好的选择,因为它降低了交互成本。)



让我们总结一下交互成本的各个组成部分,以找到“仪式”一词的由来:

  1. 等待启动页面

  2. 搜索

    1. 输入几个字符

    2. 扫描自动提示列表,以查看所需单词是否在其中

    3. 如果否,请输入更多字符并在上一步中重复

    4. 如果是,请通过点击选择所需的单词

    5. 找到搜索框,然后点击将输入焦点移至该搜索框

    6. 阅读搜索框中显示的查询和自动建议

    7. 决定查询不相关

    8. 删除搜索框中显示的查询

    9. 键入或选择自动建议

    10. 点击搜索

  3. 等待结果页面

  4. 在结果页面上找到相关的词源信息

    1. 向下滚动页面并扫描内容以查找词源信息

    2. 找到标签并阅读

    3. 请注意,右侧还有更多隐藏的标签

    4. 推断词源可能是隐藏的标签之一

    5. 注意到滑动会向右暴露内容

    6. 向右滑动

    7. 阅读Origin并将该单词与目标联系起来,以找到单词的来源

    8. 点击词源

  5. 了解“仪式”一词的来源

如您所见,一个相当简单而轻松的过程需要很多步骤和子步骤;他们每个人都会产生互动费用。对于某些人而言,交互成本微不足道-例如,记住人们向右滑动即可显示更多内容,因此交互成本非常低,因为人们在移动设备或Web上已经多次遇到水平滚动。可以优化其他步骤以最小化交互成本;因此,在搜索框中使用灰色的小x按钮可以大大降低删除显示在搜索框中的查询的成本。同样,将按钮调大可以帮助点击目标。选项卡的位置和外观设计会影响人们找到选项卡的速度。(当然,选项卡本身的选择与使用其他方式来构造内容的方式也会影响用于在结果页上查找相关信息的位置的交互成本。)


预期效用

请注意,对于前面各节中的某些步骤,用户可以有多种选择。例如,他们可以按灰色的x按钮删除当前字符串,也可以多次使用Delete键。或者,他们可以从自动建议列表中选择建议,也可以在最后输入字符串。

人们如何决定采取哪种行动?答案在于预期效用的概念:

预期效用=预期收益–预期交互成本

用户尝试最大化一项操作的预期效用:换句话说,他们权衡了每个操作的收益和成本,然后选择收益与成本之间最佳平衡的方案。

当有几种方法可以实现具有相似利益的相同目标时,用户通常倾向于选择使估计的交互成本最小的操作。



例如,许多人可能不会在自动提示列表中向下滚动以找到“仪式”一词,而是可能会再键入一个(或几个)字符,直到可见“仪式”一词为止,因为向下滚动小列表和扫描列表中的正确单词比击中一个甚至几个字符的成本更高。


这种类型的想法也普遍适用于站点级别。如果看起来真的很难在任何给定站点上实现其目标,则除非与初始站点进行交互的收益确实很高,否则大多数用户只会以较低的估计交互成本转移到另一个站点。举个例子,如果用户真的想购买Apple电脑,他们可能会坚持使用Apple的网站,因为他们不太可能在其他地方购买。在这种情况下,用户的动机确实很高,因此他们可能愿意忍受很高的交互成本。但是,如果用户要购买烧烤架,则可能不在乎是否从Home Depot或Lowe's或其他站点购买,并且会离开互动成本高的站点。

营销和品牌推广通常会增加与特定网站或品牌互动的用户动机和预期收益;可用性可以降低交互成本。两种方法最终都解决了增加使用站点或软件的预期效用的问题。

为什么您应该关心交互成本

交互成本是可用性的直接度量。实际上,该概念早在人机交互时就被引入,以评估软件系统的可用性。所有的可用性启发将用户的交互成本降至最低。

从长远来看,快速评估设计的交互成本可以节省大量资金,因为它可以很好地衡量用户界面的难易程度。它也可以用作设计备选方案之间的比较工具:通常,将交互成本最小化的方案具有更大的成功机会。

文章来源:站酷 作者:ZZiUP

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


一文带你了解如何排查内存泄漏导致的页面卡顿现象

前端达人

不知道在座的各位有没有被问到过这样一个问题:如果页面卡顿,你觉得可能是什么原因造成的?有什么办法锁定原因并解决吗?

这是一个非常宽泛而又有深度的问题,他涉及到很多的页面性能优化问题,我依稀还记得当初面试被问到这个问题时我是这么回答的:

  1. 先会检查是否是网络请求太多,导致数据返回较慢,可以适当做一些缓存
  2. 也有可能是某块资源的bundle太大,可以考虑拆分一下
  3. 然后排查一下js代码,是不是某处有过多循环导致占用主线程时间过长
  4. 浏览器某帧渲染的东西太多,导致的卡顿
  5. 在页面渲染过程中,可能有很多重复的重排重绘
  6. emmmmmm…不知道了

后来了解到了,感官上的长时间运行页面卡顿也有可能是因为内存泄漏引起的

内存泄漏的定义

那什么是内存泄漏呢?借助别的大佬给出的定义,内存泄漏就是指由于疏忽或者程序的某些错误造成未能释放已经不再使用的内存的情况。简单来讲就是假设某个变量占用100M的内存,而你又用不到这个变量,但是这个变量没有被手动的回收或自动回收,即仍然占用100M的内存空间,这就是一种内存的浪费,即内存泄漏

JS的数据存储

JavaScript的内存空间分为栈内存和堆内存,前者用来存放一些简单变量,后者用来存放复杂对象

  • 简单变量指的是JS的基本数据类型,例如:String、Number、Boolean、null、undefined、Symbol、BigInt
  • 复杂对象指的是JS的引用数据类型,例如:Object、Array、Function…

JS垃圾回收机制

根据内存泄漏的定义,有些变量或数据不再被使用或不需要了,那么它就是垃圾变量或垃圾数据,如果其一直保存在内存中,最终可能会导致内存占用过多的情况。那么此时就需要对这些垃圾数据进行回收,这里引入了垃圾回收机制的概念

垃圾回收的机制分为手动和自动两种

例如C/C++采用的就是手动回收的机制,即先用代码为某个变量分配一定的内存,然后在不需要了后,再用代码手动释放掉内存

而JavaScript采用的则是自动回收的机制,即我们不需要关心何时为变量分配多大的内存,也不需要关心何时去释放内存,因为这一切都是自动的。但这不表示我们不需要关心内存的管理!!!!否则也不会有本文讨论的内存泄露了

接下来就讲一下JavaScript的垃圾回收机制

通常全局状态(window)下的变量是不会被自动回收的,所以我们来讨论一下局部作用域下的内存回收情况

function fn1 () { let a = { name: '零一' } let b = 3 function fn2() { let c = [1, 2, 3] } fn2() return a } let res = fn1() 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

以上代码的调用栈如下图所示:

img

图中左侧为栈空间,用于存放一些执行上下文和基本类型数据;右侧为堆空间,用于存放一些复杂对象数据

当代码执行到fn2()时,栈空间内的执行上下文从上往下依次是 fn2函数执行上下文 => fn1函数执行上下文 => 全局执行上下文

待fn2函数内部执行完毕以后,就该退出fn2函数执行上下文了,即箭头向下移动,此时fn2函数执行上下文会被清除并释放栈内存空间,如图所示:

img

待fn1函数内部执行完毕以后,就该退出fn1函数执行上下文了,即箭头再向下移动,此时fn1函数执行上下文会被清除并释放相应的栈内存空间,如图所示:

img

此时处于全局的执行上下文中。JavaScript的垃圾回收器会每隔一段时间遍历调用栈,假设此时触发了垃圾回收机制,当遍历调用栈时发现变量b和变量c没有被任何变量所引用,所以认定它们是垃圾数据并给它们打上标记。因为fn1函数执行完后将变量a返回了出去,并存储在全局变量res中,所以认定其为活动数据并打上相应标记。待空闲时刻就会将标记上垃圾数据的变量给全部清除掉,释放相应的内存,如图所示:

img

从这我们得出几点结论:

  1. JavaScript的垃圾回收机制是自动执行的,并且会通过标记来识别并清除垃圾数据
  2. 在离开局部作用域后,若该作用域内的变量没有被外部作用域所引用,则在后续会被清除

补充: JavaScript的垃圾回收机制有着很多的步骤,上述只讲到了标记-清除,其实还有其它的过程,这里简单介绍一下就不展开讨论了。例如:标记-整理,在清空部分垃圾数据后释放了一定的内存空间后会可能会留下大面积的不连续内存片段,导致后续可能无法为某些对象分配连续内存,此时需要整理一下内存空间;交替执行,因为JavaScript是运行在主线程上的,所以执行垃圾回收机制时会暂停js的运行,若垃圾回收执行时间过长,则会给用户带来明显的卡顿现象,所以垃圾回收机制会被分成一个个的小任务,穿插在js任务之中,即交替执行,尽可能得保证不会带来明显的卡顿感

Chrome devTools查看内存情况

在了解一些常见的内存泄漏的场景之前,先简单介绍一下如何使用Chrome的开发者工具来查看js内存情况

首先打开Chrome的无痕模式,这样做的目的是为了屏蔽掉Chrome插件对我们之后测试内存占用情况的影响

img

然后打开开发者工具,找到Performance这一栏,可以看到其内部带着一些功能按钮,例如:开始录制按钮;刷新页面按钮;清空记录按钮;记录并可视化js内存、节点、事件监听器按钮;触发垃圾回收机制按钮等等

img

简单录制一下百度页面,看看我们能获得什么,如下动图所示:

img

从上图中我们可以看到,在页面从零到加载完成这个过程中JS Heap(js堆内存)、documents(文档)、Nodes(DOM节点)、Listeners(监听器)、GPU memory(GPU内存)的最低值、最高值以及随时间的走势曲线,这也是我们主要关注的点

再来看看开发者工具中的Memory一栏,其主要是用于记录页面堆内存的具体情况以及js堆内存随加载时间线动态的分配情况

img

堆快照就像照相机一样,能记录你当前页面的堆内存情况,每快照一次就会产生一条快照记录,如图所示:

img

如上图所示,刚开始执行了一次快照,记录了当时堆内存空间占用为13.9MB,然后我们点击了页面中某些按钮,又执行一次快照,记录了当时堆内存空间占用为13.4MB。并且点击对应的快照记录,能看到当时所有内存中的变量情况(结构、占总占用内存的百分比…)

然后我们还可以看一下页面动态的内存变化情况,如图所示:

img

在开始记录后,我们可以看到图中右上角有起伏的蓝色与灰色的柱形图,其中蓝色表示当前时间线下占用着的内存;灰色表示之前占用的内存空间已被清除释放。

从上图过程来看,我们可以看到刚开始处于的tab所对应显示的页面中占用了一定的堆内存空间,成蓝色柱形,在点击别的tab后,原tab对应的内容消失,并且原来蓝色的柱形变成灰色(表示原占用的内存空间得到了释放),同时新tab所对应显示的页面也占用了一定的堆内存空间。因此后续我们就可以针对这个图来查看内存的占用与清除情况

内存泄漏的场景

那么到底有哪些情况会出现内存泄漏的情况呢?这里列举了常见的几种:

  1. 闭包使用不当引起内存泄漏
  2. 全局变量
  3. 分离的DOM节点
  4. 控制台的打印
  5. 遗忘的定时器

接下来介绍一下各种情况,并尝试用刚才讲到的两种方法来捕捉问题所在

1.闭包使用不当

文章开头的例子中,在退出fn1函数执行上下文后,该上下文中的变量a本应被当作垃圾数据给回收掉,但因fn1函数最终将变量a返回并赋值给全局变量res,其产生了对变量a的引用,所以变量a被标记为活动变量并一直占用着相应的内存,假设变量res后续用不到,这就算是一种闭包使用不当的例子

接下来尝试使用Performance和Memory来查看一下闭包导致的内存泄漏问题,为了使内存泄漏的结果更加明显,我们稍微改动一下文章开头的例子,代码如下:

<button onclick="myClick()">执行fn1函数</button> <script> function fn1 () { let a = new Array(10000) // 这里设置了一个很大的数组对象 let b = 3 function fn2() { let c = [1, 2, 3] } fn2() return a } let res = [] function myClick() { res.push(fn1()) } </script> 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

设置了一个按钮,每次执行就会将fn1函数的返回值添加到全局数组变量res中,是为了能在performacne的曲线图中看出效果,如图所示:

img

在每次录制开始时手动触发一次垃圾回收机制,这是为了确认一个初始的堆内存基准线,便于后面的对比,然后我们点击了几次按钮,即往全局数组变量res中添加了几个比较大的数组对象,最后再触发一次垃圾回收,发现录制结果的JS Heap曲线刚开始成阶梯式上升的,最后的曲线的高度比基准线要高,说明可能是存在内存泄漏的问题

在得知有内存泄漏的情况存在时,我们可以改用Memory来更明确得确认问题和定位问题

首先可以用Allocation instrumentation on timeline来确认问题,如下图所示:

img

在我们每次点击按钮后,动态内存分配情况图上都会出现一个蓝色的柱形,并且在我们触发垃圾回收后,蓝色柱形都没变成灰色柱形,即之前分配的内存并未被清除

所以此时我们就可以更明确得确认内存泄漏的问题是存在的了,接下来就精准定位问题,可以利用Heap snapshot来定位问题,如图所示:

img

第一次先点击快照记录初始的内存情况,然后我们多次点击按钮后再次点击快照,记录此时的内存情况,发现从原来的1.1M内存空间变成了1.4M内存空间,然后我们选中第二条快照记录,可以看到右上角有个All objects的字段,其表示展示的是当前选中的快照记录所有对象的分配情况,而我们想要知道的是第二条快照与第一条快照的区别在哪,所以选择Object allocated between Snapshot1 and Snapshot2,即展示第一条快照和第二条快照存在差异的内存对象分配情况,此时可以看到Array的百分比很高,初步可以判断是该变量存在问题,点击查看详情后就能查看到该变量对应的具体数据了

以上就是一个判断闭包带来内存泄漏问题并简单定位的方法了

2.全局变量

全局的变量一般是不会被垃圾回收掉的,在文章开头也提到过了。当然这并不是说变量都不能存在全局,只是有时候会因为疏忽而导致某些变量流失到全局,例如未声明变量,却直接对某变量进行赋值,就会导致该变量在全局创建,如下所示:

function fn1() { // 此处变量name未被声明 name = new Array(99999999) } fn1() 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

此时这种情况就会在全局自动创建一个变量name,并将一个很大的数组赋值给name,又因为是全局变量,所以该内存空间就一直不会被释放

解决办法的话,自己平时要多加注意,不要在变量未声明前赋值,或者也可以开启严格模式,这样就会在不知情犯错时,收到报错警告,例如:

function fn1() { 'use strict'; name = new Array(99999999) } fn1() 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

3.分离的DOM节点

什么叫DOM节点?假设你手动移除了某个dom节点,本应释放该dom节点所占用的内存,但却因为疏忽导致某处代码仍对该被移除节点有引用,最终导致该节点所占内存无法被释放,例如这种情况:


改动很简单,就是将对.child节点的引用移动到了click事件的回调函数中,那么当移除节点并退出回调函数的执行上文后就会自动清除对该节点的引用,那么自然就不会存在内存泄漏的情况了,我们来验证一下,如下图所示:

img

结果很明显,这样处理过后就不存在内存泄漏的情况了

4.控制台的打印

控制台的打印也会造成内存泄漏吗????是的呀,如果浏览器不一直保存着我们打印对象的信息,我们为何能在每次打开控制的Console时看到具体的数据呢?先来看一段测试代码:

<button>按钮</button> <script> document.querySelector('button').addEventListener('click', function() { let obj = new Array(1000000) console.log(obj); }) </script> 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

我们在按钮的点击回调事件中创建了一个很大的数组对象并打印,用performance来验证一下:

img

开始录制,先触发一次垃圾回收清除初始的内存,然后点击三次按钮,即执行了三次点击事件,最后再触发一次垃圾回收。查看录制结果发现JS Heap曲线成阶梯上升,并且最终保持的高度比初始基准线高很多,这说明每次执行点击事件创建的很大的数组对象obj都因为console.log被浏览器保存了下来并且无法被回收

接下来注释掉console.log,再来看一下结果:

<button>按钮</button> <script> document.querySelector('button').addEventListener('click', function() { let obj = new Array(1000000) // console.log(obj); }) </script> 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

performance如图所示:

img

可以看到没有打印以后,每次创建的obj都立马被销毁了,并且最终触发垃圾回收机制后跟初始的基准线同样高,说明已经不存在内存泄漏的现象了

其实同理,console.log也可以用Memory来进一步验证

  • 未注释console.log

img

  • 注释掉了console.log

img

最后简单总结一下:在开发环境下,可以使用控制台打印便于调试,但是在生产环境下,尽可能得不要在控制台打印数据。所以我们经常会在代码中看到类似如下的操作:

// 如果在开发环境下,打印变量obj if(isDev) { console.log(obj) } 
  • 1
  • 2
  • 3
  • 4

这样就避免了生产环境下无用的变量打印占用一定的内存空间,同样的除了console.log之外,console.error、console.info、console.dir等等都不要在生产环境下使用

5.遗忘的定时器

其实定时器也是平时很多人会忽略的一个问题,比如定义了定时器后就再也不去考虑清除定时器了,这样其实也会造成一定的内存泄漏。来看一个代码示例:

<button>开启定时器</button> <script> function fn1() { let largeObj = new Array(100000) setInterval(() => { let myObj = largeObj }, 1000) } document.querySelector('button').addEventListener('click', function() { fn1() }) </script> 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

这段代码是在点击按钮后执行fn1函数,fn1函数内创建了一个很大的数组对象largeObj,同时创建了一个setInterval定时器,定时器的回调函数只是简单的引用了一下变量largeObj,我们来看看其整体的内存分配情况吧:

img

按道理来说点击按钮执行fn1函数后会退出该函数的执行上下文,紧跟着函数体内的局部变量应该被清除,但图中performance的录制结果显示似乎是存在内存泄漏问题的,即最终曲线高度比基准线高度要高,那么再用Memory来确认一次:

img

在我们点击按钮后,从动态内存分配的图上看到出现一个蓝色柱形,说明浏览器为变量largeObj分配了一段内存,但是之后这段内存并没有被释放掉,说明的确存在内存泄漏的问题,原因其实就是因为setInterval的回调函数内对变量largeObj有一个引用关系,而定时器一直未被清除,所以变量largeObj的内存也自然不会被释放

那么我们如何来解决这个问题呢,假设我们只需要让定时器执行三次就可以了,那么我们可以改动一下代码:

<button>开启定时器</button> <script> function fn1() { let largeObj = new Array(100000) let index = 0 let timer = setInterval(() => { if(index === 3) clearInterval(timer); let myObj = largeObj
            index ++ }, 1000) } document.querySelector('button').addEventListener('click', function() { fn1() }) </script> 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

现在我们再通过performance和memory来看看还不会存在内存泄漏的问题

  • performance

img

这次的录制结果就能看出,最后的曲线高度和初始基准线的高度一样,说明并没有内存泄漏的情况

  • memory

img

这里做一个解释,图中刚开始出现的蓝色柱形是因为我在录制后刷新了页面,可以忽略;然后我们点击了按钮,看到又出现了一个蓝色柱形,此时就是为fn1函数中的变量largeObj分配了内存,3s后该内存又被释放了,即变成了灰色柱形。所以我们可以得出结论,这段代码不存在内存泄漏的问题

简单总结一下: 大家在平时用到了定时器,如果在用不到定时器后一定要清除掉,否则就会出现本例中的情况。除了setTimeout和setInterval,其实浏览器还提供了一个API也可能就存在这样的问题,那就是requestAnimationFrame

总结

在项目过程中,如果遇到了某些性能问题可能跟内存泄漏有关时,就可以参照本文列举的5种情况去排查,一定能找到问题所在并给到解决办法的。

虽然JavaScript的垃圾回收是自动的,但我们有时也是需要考虑要不要手动清除某些变量的内存占用的,例如你明确某个变量在一定条件下再也不需要,但是还会被外部变量引用导致内存无法得到释放时,你可以用null对该变量重新赋值就可以在后续垃圾回收阶段释放该变量的内存了。

转自:csdn博客 作者:「零一」

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

日历

链接

个人资料

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

存档