首页

2020潘通年度流行色经典蓝出炉!附配色方案

ui设计分享达人

2020潘通流行色




一觉醒来,2020 年潘通年度色就这么发布了。

2020年年度潘通色:「经典蓝」

这款被命名为为「经典蓝」(Classic Blue)的色彩沉稳耐看,隽永优雅,按照潘通官方的说法,「经典蓝」仿佛暮色四合时的天空,宁神静心,让人驻足。「经典蓝」是一种稳固可靠的色彩,给人确信感,抚慰着每一个期待稳定者的心灵。

为什么会选择这样一种蓝色,潘通色彩研究所的执行董事 Leatrice Eiseman 在潘通官网上,予以详细的解释。

解读「经典蓝」

「我们生活在一个亟需信念和信任的时代。潘通 19-4052 经典蓝 ,这种色彩正是自信和恒久的外化体现,这种坚实可靠的蓝色调,更能与人产生深层的共鸣,令人心安。一望无际的蓝色能够唤起人类对于广袤无垠夜空的无限遐想,经典蓝鼓励着我们透过显而易见的事实,拓展思维,放飞想象。

经典蓝敦促着我们更加深入地思考,开阔视野,进行更加深入的交流。」

——Leatrice Eiseman 潘通色彩研究所执行董事

从色彩心理学的角度上来看,蓝色色相本身有着信任、信念的含义,也是的色相。在此之前,潘通曾经多次选择蓝色作为年度色, 2000 年的时候选择天蓝色(Cerulean),在 2005 年的时候则选择了蓝绿色(Blue Turquoise),2008 年的年度色叫鸢尾蓝(Blue Iris),2016 年的年度色则是静谧蓝(Serenity)。

相比于这4种蓝色,「经典蓝」更为沉静稳重,没有一点攻击性,也不显得沉重,轻松而易于互动。

「经典蓝」无疑是一个略显保守,但是非常贴合当下语境的色彩。它充满了确信感,让人觉得安全,屏蔽了焦虑。同 2019 年充满前进感的「活力珊瑚橙」相比,「经典蓝」更加内敛和笃定,悄无声息地增加每一个的信心。

除了发布这一年度色彩之外,潘通色彩研究所还专门制定了5套配色方案,这些配色方案应该会在接下来的 2020 年,得到更加广泛的应用。

「经典蓝」的5套官方配色方案

沉思

这是一套舒缓的配色方案,凉爽的蓝色调和同样柔和的暖色组合到一起,给人以沉静的感觉。


呼吸

这套名为呼吸的配色方案当中,加入了更为经典的黑白色,选择了对比更加强烈、更加富有活力的搭配方式。这样的色彩搭配更容易唤起观者的幸福感,让你的设计更加具有田园牧歌的气息。

沙漠暮色

「经典蓝」的灵感来源之一就是夜幕降临时的天空。而这套名为沙漠暮色的配色方案,就与此相关。充满金属质感的潘通色更能够凸显「沙漠」的闪亮质感,而恰为对比色的「经典蓝」让这套配色方案优雅无比。

异域风情

这套配色方案比起之前的配色更加丰富,更显混搭,仿佛加入了天然的调味料。这种健康而自我的色彩搭配方式,让人仿佛身在异域,不同于此地的文化,迥异与此时的感觉,独特却令人着迷。


非传统

打破常规的桎梏,选择异乎寻常的色彩搭配,而足够传统的「经典蓝」恰恰构成了这种搭配方式当中,作为基础的底色。这套配色方案指向时尚,突破规则,如同小品,有趣而俏皮。

「经典蓝」的9套精选配色方案

为了更加方便线上的设计师来使用这些配色方案,优设的小编从官方提供的这些配色方案当中,精心挑选了 9 套适合进行数字设计的配色方案,并且标注上了相应的 HEX 值便于精准取色,请尽情取用:


转自:站酷——hicatherine86 




Emscripten教程之emcc编译命令

seo达人

语法


emcc [options] file ...

这个输入文件file,既可以是clang可以编译的C/C++语言,也可以是二进制形式的llvm bitcode或者人类可读形式的llvm assembly文件。

参数

大部分clang或者gcc的选项(option)都是可以工作的,比如:

# 显示信息 emcc --help # 显示编译器版本信息 emcc --version

如果想看当前Emscripten中clang版本支持的全部选项列表,可以直接使用命令:
clang --help.

emcc修改的或者emcc中新的选项列在下面:

首先是一些编译优化flag,它们-O0,-O1,-O2,-Os,-Oz,-O3。

-O0:
不进行编译优化(这是默认情况)。当你刚开始移植项目是推荐使用它,因为它会包含许多断言。

-O1:
简单优化。推荐你在既想缩短编译时间又想编译优化时使用。它毕竟比-O2级别的优化编译起来快多了。它会进行asm.js和llvm的-O1进行优化,它会relooping,会删除运行时断言和C++异常捕获,它也会使得-s ALIASING_FUNCTION_POINTERS=1。

想要C++异常捕获重新可用,请设置:-s DISABLE_EXCEPTION_CATCHING=0。

-O2:
和-O1类似,不过多了JavaScript级别的优化以及一些llvm -O3的优化项。当你想发布项目的时候,推荐使用本级别优化。

-O3:
和-O2类似,不过比-O2又多了一些JavaScript优化,而且编译时间明显比-O2长。这个也推荐在发布版本的时候使用。

-Os:
和-O3类似,不过增加了额外的优化以减小生成的代码体积,代价是比-O3性能差一点。-Os优化会同时影响llvm bitcode 和JavaScript文件的生成。

-Oz:
和-Os类似,不过进一步减小了代码体积。

-s OPTION=VALUE
传给编译器的所有涉及到JavaScript代码生成的选项。选项列表,请见settings.js

对于某个选项的值,不仅可以直接在emcc命令行里面设定,也可以把他们写成json文件。比如下面,就是将DEAD_FUNCTIONS选项的值放到了path/to/file文件里,emcc里面传这个文件的路径。

-s DEAD_FUNCTIONS=@/path/to/file
note: 1、文件内容可以是:["_func1","_func2"]; 2、文件路径必须是绝对的,不能是相对的。

-g:
这是保留调试信息flag。

  • 如果只是编译到bitcode,那就和clang和gcc中的-g一样。
  • 如果是要编译到JavaScript,-g就等于-g3。

-g<level>
控制打印的调试信息数量,每一个level都是在前一个level的基础上编译的:

  • -g0:不保留调试信息。
  • -g1:保留空格,不压缩。
  • -g2:保留函数名。
  • -g3:保留变量名,与-g同。变量名一般不是必须编译后保留的,但是如果保留了,可以推断变量的目的,对吧。
  • -g4:保留llvm 调试信息,这是能调试的最高级别。
note:
优化级别越高,编译时间越长

--profiling:

--profiling-funcs:

--tracing:
启用Emscripten的tracing API。

--emit-symbol-map:

--js-opts<level>:
允许JavaScript优化,有两个值:
0:不允许JavaScript优化器允许;
1:使用JavaScript优化器。
通常用不到我们设置这一项, 因为设置-O后面的level的时候,这个项就能顺便取到一个合适的值。

note:
有些选项会重写这个flag的值,比如EMTERPRETIFY, DEAD_FUNCTIONS, OUTLINING_LIMIT, SAFE_HEAP 和 SPLIT_MEMORY会将js-opts=1,因为他们依赖js优化器。

--llvm-opts<level>:
启用llvm优化。它的取值有有:

  • 0:不使用llvm优化
  • 1:llvm -O1优化
  • 2:llvm -O2优化
  • 3:llvm -O3优化

和--js-opts<level>一样,通常用不到我们设置这一项, 因为设置-O后面的level的时候,这个项就能顺便取到一个合适的值。

--llvm-lto<level>:
启用llvm 连接时 优化。可以取值0,1,2,3。

--closure <on>:
运行压缩编译器(Closure Compiler),可能的取值有,0,1,2:

  • 0:是不启用压缩编译器。
  • 1:启用。
  • 2:启用。

--pre-js <file>
生成代码前,指定一个要把内容添加进来的文件。

--post-js <file>
生成代码后,指定一个要把内容添加进来的文件。

--embed-file <file>
指定一个带路径的文件嵌入到编译生成的js代码里。路径是相对于编译时的当前路径。如果传的是一个目录,则目录下所有文件的内容都会被嵌入到将来生成的js代码中。

--preload-file <name>
异步运行编译代码前,指定一个预加载的文件。路径是相对于编译时的当前路径。如果传的是一个目录,则目录下所有文件的内容都会被预加载到一个.data文件中。

--exclude-file <name>
从 –embed-file and –preload-file后面的目录中排除一些文件,支持使用通配符*。

--use-preload-plugins
告诉文件打包器当文件加载时,运行预加载插件。它用来执行诸如使用浏览器解码器解码图片和音频等。

--shell-file <path>
指定要生成HTML的模板文件。

--source-map-base <base-url>

--minify 0
等于-g1。

--js-transform <cmd>
优化之前,生成代码之后,设定这一条命令。这条命令可以让你修改JavaScript代码。之后,编译器会将修改的和未修改的一起进行编译优化。

--bind
启用bingdings编译源代码。bingings是Emscripten中连接C++和JavaScript代码的一类API。

--ignore-dynamic-linking
告诉编译器忽视动态链接,之后用户就得手动链接到共享库。

--js-library <lib>
定义除了核心库(src/library_*)以外的js库。

-v
打开详细输出。
这个设置为把-v传给clang,并且启用EMCC_DEBUG生成编译阶段的中间文件。它也会运行Emscripten关于工具链的内部的完整性检查。

tip: emcc -v是诊断错误的有用工具,不管你是否附加其他参数。

--cache

--clear-cache

--clear-ports

--show-ports

--save-bc PATH

--memory-init-file <on>
规定是否单独生成一个内存初始化文件。取值包括0和1.

  • 0:不单独生成.mem文件。
  • 1:单独生成.mem文件。

-Wwarn-absolute-paths
启用在-I和-L命令行指令中使用绝对路径的警告。这是用来警告无意中使用了绝对路径的。在引用非可移植的本地系统头文件时,使用绝对路径有时是很危险的。

--proxy-to-worker

--emrun
使生成的代码能够感知emrun命令行工具。当运行emran生成的应用程序时,这样设置就允许stdout、stderr和exit(returncode)被捕获。

--cpuprofiler
在生成的页面上嵌入一个简单的CPU分析器。使用这个来执行粗略的交互式性能分析。

--memoryprofiler
在生成的页面上嵌入内存分配跟踪器,使用它来分析应用程序Emscripten堆的使用情况。

--threadprofiler
在生成的页面上嵌入一个线程活动分析器。当进行多线程编译时,使用它来分析多线程应用程序。
--em-config

--default-obj-ext .ext

--valid-abspath path
设置一个绝对路径的白名单,以防止关于绝对路径的警告。

-o <target>
编译输出的文件格式。target可以取值为:

  • name.js:JavaScript文件;
  • name.html:HTML+js文件。把js单独生成是为了减小页面加载时间。
  • name.bc:llvm bitcode。这是默认值。
  • name.o:和上面一样。
note:
如果你用了--memory-init-file,则还会从js文件中再单独分出一部分代码为.mem文件。

-c
生成llvm bitcode代码,而不是JavaScript。

--separate-asm
把asm.js文件单独生成到一个文件中。这样可以减少启动时的内存加载。

--output_eol windows|linux
规定生成的文本文件的行尾,如果是–output_eol windows,就是windows rn行尾,如果是–output_eol linux,则生成Linux行尾的文本文件。

--cflags

环境变量


emcc会受到几个环境变量的影响,如下:

  • EMMAKEN_JUST_CONFIGURE
  • EMMAKEN_JUST_CONFIGURE_RECURSE
  • EMCONFIGURE_JS
  • EMCONFIGURE_CC
  • EMMAKEN_CXX
  • EMMAKEN_COMPILER
  • EMMAKEN_CFLAGS
  • EMCC_DEBUG

这几个里面比较有意思的是EMCC_DEBUG。比如,如果你在编译之前设置set EMCC_DEBUG=1,那么编译的时候会把编译过程的调试信息和编译各个阶段的中间文件输出到一个临时目录,这算是给开发者提供一些编译期间的帮助或者说调试信息吧。


Emscripten主题系列文章是emscripten中文站点的一部分内容。
第一个主题介绍代码可移植性与限制
第二个主题介绍Emscripten的运行时环境
第三个主题第一篇文章介绍连接C++和JavaScript
第三个主题第二篇文章介绍embind
第四个主题介绍文件和文件系统
第六个主题介绍Emscripten如何调试代码

界面该不该加弹窗?来看高级设计师的总结

资深UI设计者

这几天在工作中处理了一个历史遗留问题:弹窗适配,几经折腾终于落定发包。于是我也趁着这个机会把弹窗体系梳理研究了一遍。

我们常见的弹窗有对话框,提示框,有时候在需求沟通中经常会听到产品同学说,给这里加个弹窗,那里加个提示框吧。实际上,这个弹窗到底该不该加?这个弹窗承载的是提示说明文字内容,还是需要引导用户下一步操作?接下来我们从弹窗的属性来聊聊我们的弹窗到底在什么时候加比较合适?用什么形态展示体验会更好?

弹窗体系:模态与非模态

模态弹窗:是指在用户任务中,终止了用户的上一步行为。也就是说,这个模态弹窗必须要用户操作才可以进行下一步动作。所以在产品设计中,我们通常会用模态弹窗引导用户去做我们要他做的操作。

常见的模态弹窗有这几种:对话框、动作栏、浮层。

对话框:对话框主要是给用户提供选项、相关的操作。另外,在一些对话框中,也可以展现图片、头像、步骤图、其它输入项等。

动作栏:常见的动作栏一般会出现屏幕下方,比如选择某个内容时候,出现的选择。

浮层:弹出窗口,浮动于顶层窗口,气泡。

非模态弹窗:是指不强制用户操作,他的作用相当于内容信息提示,他的出现不打扰用户的当前操作,并且有时间限制,在一定时间里能自动消失。比如「xxx 功能已更新哦」,这种提示是不需要用户点击操作,让用户看到就可以了。

常见的非模态弹窗有这几种:toast/hud、snackbar、notice(通知)

toast/hud:iOS 用户更习惯于在中间感知反馈信息,通常在信息提示完 3 秒左右后会消失,安卓通常会出现在屏幕顶部或者下部,不会遮挡主体内容。需要注意的是 toast 只有文字,hud 是可以带有图标的。

snackbar;可以理解为加强版的 Toast

notice:系统消息、通知推送,也是不干扰用户行为,有消失时间,是非模态的弹窗。

什么场景下使用什么弹框

了解完弹窗体系后,我们就可以针对具体场景来看产品同学说加个弹窗到底合适不合适?我们可以从下面的几个使用场景来看用什么样的弹窗合适。

1. 重要打断用户操作

在一些重要操作,避免用户操作失误。不过目前很多人都觉得这类弹框有利有弊,有的地方出现得不合时宜,打断了用户的使用状态。所以在设计这类弹框时要非常谨慎,得通过多研究确认是否有必要出现。

对话框相对来说比动作栏更重要,因为在视觉中心,更能强烈引起用户的重视,对于十分重要的内容需要打断用户上一步任务的,采用对话框的弹窗,对于不是特别重要信息露出又需要终止用户上一步动作的情况下,一般采用动作栏弹框。

2. 定制化广告弹窗

如功能更新、升级、优惠券弹窗。这一类一般是强制用户看到的,要展示出与众不同的特色,在视觉上比较突出,会使用模态的弹窗浮层形式。

3. 给予一定提示

比如提示用户状态、信息、反馈,确保用户知晓自己所处的状态,并可以做出相应的措施。一般使用非模态的弹窗。

4. 用户操作反馈

出现在用户操作完之后的反馈,比如提醒用户页面正在加载中、保存成功、已删除、已刷新等等。

可以不用弹窗的反馈例子:完成页

比如已支付成功、下载完毕、签到成功,这一类是告诉你上个动作结束了,下一步不需要进行引导了,这种反馈大多数都不采用弹窗形式展现了。

需要注意事项

1. 层级关系

弹框是内容和导航的补充,用于通知、操作菜单、成功或加载状态的 toast,他是写在蒙层上面的一层内容。

2. 适配方式

下面我们需要了解的是这几种弹窗在开发那的实现形式。我们可以理解为 2 种形式:

一种是开发直接用系统的接口,缺点是具有不可定制,形式美观度不够好。

另一种是开发用代码会单独写出一个模态弹框系统,这套系统与整体设计语言具有一致性,可以复用在各个任务中,可以定制化设计。

需要注意的是,代码写出来的模态样式要考虑在不同机型的适配情况,考虑不同机型的边界。这个适配也有两种实现形式,一种是固定宽高尺寸,展示在不同机型尺寸中,另一种是常用的等比例缩放。这个就需要开发与设计进行密切的沟通,能尽量合理地在不同机型展现更加合适,避免出现极限的情况。

我们是这样操作的,为了避免尺寸比例混乱的情况,会设计一个弹窗的宽高尺寸范围,开发同学代码写出的这套弹窗的适配在各个机型中,是在一定缩放比例下,适用各个不同情况下的视觉展示。这套弹窗可以调用在首页引导、升级等各个页面的弹窗设计中。

文章来源:优设    作者:咏舍

设计师要懂的“产品导流”背后知识点。

ui设计分享达人

设计师不仅要低头画图,也要了解“图”背后的逻辑和需求本质 。嗯,下面用直白的语言跟大家聊一波相关知识点。




说到产品导流,大家都不陌生。



身为设计师,应该经常听到产品同学提需求:“在这里给XX功能加个入口吧”,“这个宣传新产品的banner可以再大一些吗”,“这个场景可以宣传下我们的新功能诶”…


很多设计师在不了解背景的情况下往往内心是排斥的,心想怎么老是在犄角旮旯里加这么多小广告啊,都不关心用户体验的嘛,balabala...



但是有谁在吐槽时,深究其背后的原因呢,举起小爪子让大牙康康。比如:什么是产品导流?为什么需要产品导流?它有哪些的形式?如何做效果好又能兼顾产品体验呢?



最近正好在搞相关的事情,所以撸一篇文章,分享一波相关思考。





什么是产品导流? 



“产品导流”指的是:一款产品采用某种形式,增加对另一款产品/功能的曝光,使自己的用户群体(流量)去使用或探索另一款产品/功能。



眉头一紧,感觉上面的描述有点拗口,善良的牙尝试着用大白话,把导流和被导流的关系,分为两种形式解释下:1.父子关系;2.兄弟关系。

1.父子关系


画风是:“爸爸,大腿借我一抱吧”,比如:“抖音”里增加“多闪”入口,“微信”里增加“微视”入口,通过自家体量大的产品(爸爸)给自己导流。



2.兄弟关系


画风是:“外面环境恶劣,是好兄弟,就互导一下吧”,比如:京东的会员可以享受爱奇艺的会员福利,同辈之间互相导流。



所以,这么说就好理解什么是产品导流,和它们之间的关系了吧。






产品为什么需要导流? 


产品之间导流的目的,大致分为两种:1.获得新增;2.企业生态。




1.获得新增



试想你费劲巴拉的搞了个新产品,没人知道,也没人来用,就算你产品做的再妖娆,是不是也白搭,更不用说后续的商业变现之类的。


所以,这个时候如果你有一个相对成熟的产品(爸爸)大腿,让它给你导流,实现一波冷启动,是顺利迈过第一个坎儿的手段。


当然,在目前激烈的竞争环境下,不仅是新产品需要导流,扩大规模和获得新增流量,是每一个互联网企业里产品或运营同学OKR中必不可少且另他们头秃的一项指标。



所以,不管是新产品,还是相对成熟的产品,都需要通过导流的手段,来获得新增用户。





2.企业生态


“产品导流”除了为了获得新增,还有就是为了企业生态的体验闭环。


做成一款产品的公司很牛逼,但是如果能够持续做出一系列牛逼产品的公司,一定有成功的基因,或者有一套做事儿的逻辑。


比如:亚马逊的飞轮效应,要想形成飞轮,打造自己的生态,业务上就得有自己的闭环,并且闭环上的每一个步骤都可以为其它步骤助力,其次就是以第一个飞轮作为根据地,拉动周边其它业务,形成第二个或者更大的飞轮。


这个时候,就需要各个业务线之间的互通及联动(互相导流)了。






比如,阿里也是在打造自己的生态,产品之间相互赋能和联动,一旦建立起来这些基础设施后,壁垒和护城河就非常坚固。



整体来看,“产品导流”不仅有助于新产品获得冷启动,还有助于相对成熟的产品扩大规模,同时从整个企业生态来看,矩阵产品的互相导流,协同发挥优势,也有利于打造体验闭环,建立企业壁垒。





产品导流的形式有哪些? 



目前,市面上导流形式大致有以下几种:1.场景化导流;2.会员制捆绑;3.固定入口扶持;4.广告位推荐。


1.场景化导流


场景化导流,翻译成大白话就是,让导流的过程更加无缝衔接。


这种做法,能兼顾用户体验,同时对导流量也更有利,一般用于“功能”层面的互通。比如,用户正好需要XX产品或功能,你见缝插针的增加导流入口,同时功能和设计元素也跟母产品保持一致,这样用户不会觉得干扰或反感。


类似这种做法的产品比如:QQ&微云,抖音&剪映;视频号&公众号...


QQ&微云


我们用QQ聊天时,好友之间总会有一些分享文件的行为,有“分享”就有“接收”,有“接收”就有“保存”的诉求,在这种场景下,QQ聊天页面里用户长按当前“文件”,就可以将文件保存到“微云”上。






除此之外,QQ首页点击左上角的侧边栏,能看到一列与“我”相关的功能入口,其中有一项是“我的文件”,点击进去后就能看到“我”在QQ里消费过的历史文件。


同时还有“微云”小程序的入口,点击直接跳转至微云小程序(做的很服帖,跟自体脂肪填充似的,导流过程不会让用户感到突兀)。




除此之外,微云作为基础的云存储能力,也在跟腾讯系的QQ音乐互通,满足用户将下载的歌曲保存到“微云”。





这么做,不仅满足了QQ音乐用户的存储诉求,还提升了QQ音乐的登录率(用户想要使用音乐网盘,必须先登录),同时增加了QQ音乐用户粘性(用户一旦在一款产品上存下自己的东西,就会存在迁移成本),最后还为微云带来了新增用户(更多QQ的用户使用微云进行存储),扩大其规模。


这样的互通/导流手段,就实现了产品间1+1大于2的效果。





抖音&剪映


刷抖音的兄弟们应该经常见很多视频下面有一个小标签,叫“剪映-抖音出品”(意味着博主的那条短视频是用剪映做的)。


很多人看到好玩好看新奇的短视频,都会觉得真香我也要剪同款,因此抖音就是这种恰到好处的投喂给你,点击标签,直接跳转到下载页,如果已经安装了的用户,可以直接进行剪同款。




抖音不仅送你出去,还负责接你回来,一条龙服务给你安排(导)的明明白白的。


比如,当你剪辑完成后,就引导你回抖音里分享,毕竟他们不希望你剪完之后保存到本地然后美滋滋去竞品(快手之类的)分享吧。





他们的目的就是,不管你怎么折腾,也逃不出我大字节的手掌。


包括,最近的视频号,发完视频号还能附带公众号的链接,高频带低频,通过公域流量,帮助作者往私域导流(顺便做个广告,快去关注我的视频号:大牙兄,哈哈哈)。





所以微信现在的导流方式是:视频号能够链公众号,公众号能够挂小程序,小程序又能开直播,直播又能去变现。


整体来看,场景化的进行导流,更适合“功能”层面的互通,满足用户和企业的诉求,还平衡了产品体验,相对更加丝滑。





2.会员制捆绑


会员制捆绑,已经成为互联网常见导流+变现的方式了,指的就是用户在你这买了会员,在别的合作产品里也享有它们的特权福利。利用用户“爱占小便宜”的心理,产品间互相导流。


比如我们常见的方式有:88会员、联合会员...

88会员


开过88会员的同学举个爪!好,放下吧,反正我也看不到。


88会员算是阿里生态体系的重要布局了,也就是你开通了88会员,阿里系的很多产品你可以劈着叉去用,而且很划算。


比如,饿了么会员每年108元,虾米每年128元,优酷每年也差不多180元,还不算其它的,这些价格在用户心中已经是锚定价格,加到一起怎么着也大1000了吧,跟88元一比,稳赚不赔啊!






我们来看看88会员里包含什么,各种阿里系的七大姑八大姨的产品都包含进去了,从吃饭、购物、娱乐、旅行再到看病,生活里的方方面面都包含了。





所以,我身边的人开了88会员后,画风是这样的:明明想用QQ音乐听歌呢,不!我是88会员!我要去用虾米!明明想用美团点个外卖呢,不!我要去饿了么!明明我想去爱奇艺看看视频呢,不!我要去优酷看!(不过他们如果想看“淡黄色长裙蓬松的头发”还是得去爱奇艺)。


可以看出,阿里用这套会员体系,把用户死死的框在(导流)自己的产品矩阵中了,产品群们被自己爸爸carry的明明白白的。





联合会员


联合会员跟上面说的88会员有点类似了,只是88是自己企业内产品的互相导流,而很多联合会员是跨公司,跨界整合资源。 

比如,京东的PLUS京典卡,就是跨公司跟别人合作,有和腾讯视频、酷狗音乐、喜马拉雅、携程、爱奇艺的联合会员。 





其实也能看出,京东和其它产品这么搞,也是对阿里系的反击,没有爸爸,只能兄弟之间互相导流,抱团取暖,一致对外了。



3.固定入口扶持


固定入口扶持,一般出现在规模较大的平台级产品上,它们利用自己的流量优势,在固定位置给自己的子孙/兄弟产品们导流。


扶持下一代,努力做到子又生孙,孙又生子,子子孙孙无穷尽也。



比如:淘宝、支付宝、美团,在首页金刚位给自己企业相关产品导流,同时也补足/丰富自己产品的其它场景。






比如,各家的小程序,也是相对固定的入口给自己的产品或第三方产品进行导流,完善自己的产品生态。






4.广告位推荐


广告位推荐的导流方式应该是大家非常熟悉的了,它区别于上面的导流方式的核心点在于一般是产品/活动的介绍,引导安装之类的,形式大致分为:闪屏、 banner、角标、feed流...




闪屏

闪屏指的是用户在使用产品时,打开的第一个启动页。


用它进行导流的优点就是全屏沉浸展示,用户的目光就聚焦到这里了,但缺点是:时间短,如果在短时间内传达不清晰的话,很容易一闪而过,钱就白花了。



所以,闪屏的导流,最好找重叠用户较高的产品,比如,在汽车之家闪屏投个刮胡刀啊,在亲宝宝闪屏投奶粉啊之类的,用户在短时间内好接受好理解。


投放越精准,对产品体验和对广告主的导流效果越好。





角标

角标导流这种形式,在电商类平台搞活动的时候经常见,各种小角标纷纷出来拉客,角标的着陆页一般都是活动H5类型的居多。




这种形式的优点在于能够一直常驻,不像闪屏,闪一下就没了,它只要用户不手动关闭就一直在这杵着。


但对于设计师的挑战就是,如何在小小的区域里,把被导流的产品/活动核心利益点传递清楚,吸引用户点击。

而且,这种形式也要谨慎使用,如果乱八七糟的飚小角标也挺伤害体验的。





Banner


Banner位的导流大家更常见了,一般都是自己家产品/业务,夹杂着第三方广告推广,无限轮播着进行导流,大家都太熟悉了,我就不啰嗦的说了。





Feed流


一般资讯类或者社区类产品,喜欢用这种方式进行导流,因为用户在其产品上的核心操作就是撸Feed。


所以,撸着撸着给用户投喂一个通过算法推荐的广告,然后再把广告包装成跟Feed内容很相近的设计,吸引(pian)用户点进去,从而进行导流。


比如: 知乎、最右、百度、头条 ...




不得不说第二张“最右”的推荐内容,让我不禁抚摸了下自己的胡子。

不过除了最右给我推“大胸妹子”,百度给我推“游戏”,头条给我推“汽车”,难道在他们的算法中,我是个油腻中年男???


看来他们的人工还不够智能,机器还得再学习学习,大牙对你萌hin失望。





总结  


总的来说,“产品导流”不仅有助于新产品获得冷启动,还有助于相对成熟的产品扩大规模,同时从整个企业生态来看,矩阵产品的互相导流,协同发挥优势,也有利于打造体验闭环,建立企业壁垒。


形式大致分为:1.场景化导流,无缝衔接式的体验,让用户在产品功能间丝滑的流转;2.会员制捆绑,低价获得跨产品福利,利用用户占小便宜的心理跨界导流;3.固定入口扶持,平台型产品常用手段,丰富自己产品场景又能给自己小弟带流量;4.广告位推荐,灵活可配形式多变的导流方式(闪屏、 banner、角标、feed流)但搞不好很伤用户体验。



但是,不管什么导流形式,导进来的用户,只有看到满足他们诉求和预期的着陆页才是最关键的,不然就算给你再大的流量入口,用户搞不明白该走还是会走。


同时,也不能为了导流而不分场景的尬导,这样很容易伤害原有产品的用户体验,捡了芝麻,丢了西瓜。

转自:站酷-苏大牙


交互问答 | 产品设计的稀缺性 & 截图功能 & 分页与无限滚动加载差异

ui设计分享达人

通过交互思维,从常见功能模式梳理产品设计的底层逻辑。

通过读者提出的三个问题,我们从交互设计的角度来聊聊它们的底层逻辑。


稀缺性的正反价值


读者提问:

呆总,有什么心理学或经济学的概念是在产品设计上也被沿用的?


当然有,比如稀缺性。


让我们从你的生活小故事开始讲起。


在某个饿着肚子的下午,你走进一家超市,想要买桶泡面充饥,但是各式品牌与类型的泡面让你应接不暇,于是纠结起来,正犹豫不决中,一款所剩无几的泡面在茫茫商品中跳进了你的视野,不禁心中窃喜,“没错,就是它了!卖得这么好,味道一定不错~”。


回到家里,你一边吃着泡面一边刷着手机,突然看到了节假日商品促销的广告,于是打开了购物车,果然每件商品上都赫然标着今日满减的红字,让你蠢蠢欲动,之前嫌贵的商品突然在今天有了剁手的理由。不仅如此,其中你的最爱竟然显示库存紧张,如何能忍,赶紧结账下单,在付完款的最后一刻,你终于松了口气并开始暗自庆幸自己的英明决策。


这时,你的舍友突然推门而入,迫不及待地告诉了你她偶然得知的一个八卦消息,之前从未听闻的你感到激动不已,能够获得鲜见的信息让你感到无比地满足,两人就此事展开了激烈地讨论....


以上。


就像故事中描绘的那样,我们的生活就是由这些琐碎的片段重复构成,在无数个场景中,我们做着自觉或不自觉的思考和判断,它们影响着我们的感知,主导着我们的行为。其中,稀缺性就是我们估值的必要条件和决策的重要基础,那些热销的商品、限时的促销和不为人知的秘密唤起了我们内心的需求与渴望,催促着我们去行动。在设计产品功能时,产品人员也总是会刻意营造给用户一种稀缺的感知来突出其价值,以引导用户进行点击、浏览、购买等一系列行为。


稀缺的分类


总的来说,我们对稀缺性的利用大致体现在三个方面:时间、产品、信息。


时间的紧迫感。


这一点经常应用在电商产品中,比如特定节日内的商品限时优惠、抢购活动等等。对时间的限制能够传递给用户一种紧迫感,暗示他们尽快购买,从而减少他们的决策时间,达到促销的目的。

产品的缺失。


我们经常能在电商产品中看到商品库存的数量,特别是当库存较少时,能起到刺激用户的购买欲望。谷歌早前在发布线上邮箱的时候也充分利用了类似的手段,因为个人测试版的邮箱受技术的限制,不能为每个人开放足够大的储藏空间,于是他们采用了「邀请制」来推动这项服务,结果非常有效。


当你成为了其中一员,就能够再邀请 2-3 个朋友,这项服务的「供应不足」在推荐系统的支持下得到了病毒式的传播。类似的,知乎早期也利用了邀请的方式去帮助自己获得初期的忠实用户,产品的不易得性反而让人更看重它,间接提高了产品的价值,达到了意想不到的效果。


再比如部分商品「限购 2 件」,会让用户产生再买一件的念头 —— 其实原本就只想买一件。


信息的限制。


因为我们总是假定少的东西更具价值,所以对于审查和限制我们获得信息的动作尤为敏感,我们更渴望得到那些被禁的隐秘信息而不是唾手可得的信息。这种稀缺性所起的作用甚至比法律还要强,我们会本能地认为它更有说服力。这也是一些新奇罕见的消息更容易传播的原因之一,得到不常见的信息能够使人享受到额外资源的优越感。一些媒体恰恰利用了这一点,常通过夸张的新闻标题去抓住人们的注意,试图扩大其影响力。


或者一些特权(虚拟)商品,譬如 VIP。就是提供给用户部分信息,再告知用户 VIP 权限更高,能获得多有「价值」信息,诱导购买。


稀缺作用的原理


天然的,出于原始的不断追逐猎物、获取资源的本能,我们总是格外珍惜稀缺资源,并认为它们更具价值。稀缺性在一定程度上鼓动了我们内心贪婪的欲望,除此之外,我们还有「喜欢走捷径」的弱点,同时「厌恶失去」。


一方面,当事物很难被获取的时候,我们通过易得性进行快速的价值判断,遵循这样的方式我们总能地做出决定。

另一方面,我们讨厌失去选择的自由,所以常倾向于快速地决策来留住一些东西,甚至想要得更多。

虽然我们的这些本能,在帮助我们快速行动,但却不一定会做出最适合自己的选择。毕竟人的判断大多偏向于主观,一些用心不良的企业,可能会利用它让一些人做出错误的决定。


稀缺作用的条件


相比于一些错误所导致的稀缺,比如最近由于猪瘟造成的猪肉供应量的减少,在有限资源的竞争中由社会需求导致的稀缺能够发挥更强的效应,零售商早已充分洞悉了我们的这种倾向,所以常告诉我们产品正在热销当中,应尽快购买。这不仅是社会认同在发挥作用,即我们认为其他人认同的产品是好的,而且我们正参与到产品的竞争当中。

稀缺原则的应用焦点在于利用紧张的时间或者强调某些东西未来的不可得性,防止用户花很多的时间做出决策,推动用户马上做出对商家有利的决定。如果给人们这个世界上的所有时间去做出决定,他们要不花上所有的时间,要不根本不做决定。


通过应用稀缺性的技巧,可以从根本上迫使人们采取对策。人们在稀缺性原则的压力下反应更快,因为他们害怕永远失去机会。稀缺性使得他们优先做出决定,所以这些决定相比无限制的决策变得更重要,更紧急,更被需要。


但是仅仅告诉人们他们将要获得的利益是不够的,如果他们选择你的产品或者服务,你还需要指出其特别之处以及被放弃时的成本。

月活超10亿的微信,是如何做好用户体验的

资深UI设计者

微信是一种生活方式。作为月活超 10 亿的国民级产品,它有着独特的设计之道。

同时,微信也是互联网界的一个异类,张小龙曾在微信公开课上回应道:「我们只是守住了做一个好产品的底线,居然就与众不同了」。

好产品自然是体验和价值至上。下面,我就为大家解读微信的用户体验设计。

二次确认的微创新

先从最简单的二次确认讲起。

微信针对首页消息和收藏列表的删除操作,做了二次确认的微创新。像同类 IM 聊天工具,如 iOS 短信、Facebook Messenger、飞聊等等,二次确认都是采用底部系统弹窗。这样做,从程序架构的角度来看兼容性和通用性更强。

而从体验设计的角度来看,则刚好相反,因为从第一次删除操作,到第二次确认系统弹窗。之间的目标距离太长,耗时也就变长了。根据菲茨定律(Fitts’ Law),获取目标的时间取决于目标的距离和大小。这意味着提升交互的效率,不仅需要减少距离,同时还要增加目标大小。

△ 收藏列表

回过头看来微信,就是这样设计的。二次确认是在第一次的基础上延展,距离几乎为 0,同时,目标按钮的宽度也增加了几倍,大大地提升了交互效率。

互动体验广告

其实,商业和用户体验往往是有冲突的。而微信广告很好的平衡了这一点。

通过丰富有趣的互动体验式创意,或画圆、或画方、或画爱心,吸引大家主动参与互动。

1. 开放首条评论

另外,首条评论功能让品牌像朋友一样与大家对话,利用明星效应,从而带动更多人参与评论,有效提升评论区活跃度和广告点击率。

△ 朋友圈刘雯广告

以刘雯发布的朋友圈广告为例,大表姐把款的 vivo X30 系列手机交到你手中,并在首条评论中邀请你帮她拍照。数十万用户积极回复刘雯,评论率高于历史均值 40 倍+,「你这么漂亮怎么拍都好看」、「天天给你拍」,大表姐的魅力折服了众多用户,有效提升了品牌的亲和力与好感度。

2. 打开仪式 · 最强震动级别

在交互方面,如果你是 iPhone 用户,可以体验到 Taptic Engine 线性震动马达,通常力度由轻到重分别是 Light、Medium、Heavy。在打开广告的那一刻,它给你的是最强震动级别,满满的仪式感!整个微信应该找不到第二个这样级别的震动了。

提供反馈信息

再举一个震动的例子,当你的好友拍摄了时刻视频后,可以看到 TA 的头像右上角多了一个蓝色的小圈圈,双击它就能看到好友的时刻视频了。

当然,你双击没有拍摄时刻视频的好友,TA 的头像会左右晃动,并且会有 Failure 的震动反馈,动画和震动节奏完美匹配,这个体验就像你解锁 iPhone 输错密码时的震动是一样的。

△ 没有时刻视频时的反馈

我们做产品设计时也一样,对于用户的操作,应当给予清晰明了的反馈,帮助用户更好地理解信息。

跨平台能力

微信的起步阶段是基于手机来做 App,不基于 PC 来做,PC 端只是辅助,而现在,它的跨平台能力也逐渐增强。

一周前,微信 PC 版「微信测试版 for Windows」发布了 2.9.0 的内测,同步了移动端的新功能,主要有两点:

支持打开小程序,也可以玩「贪吃蛇」「跳一跳」等小游戏了。

△ Windows 微信客户端

另外,此前的微信 PC 端只支持引用文字消息,也没有达到手机上引用消息的视觉效果。此次更新中,还新增了很多支持的应用类型,包括但不限于图片/视频、表情包、公众号链接、小程序、文件等。

如此看来,Mac 端的更新也不远了,可以期待一下。

语音实时翻译

最近的微信更新了,除了引入深色模式之外。值得一提的是,语音消息的交互体验得到了优化,上滑转文字更方便了。

此前的方式是按住说话,滑到转文字按钮,说完松开手指后,才把语音解析成文字。

现在交互则少了一步操作,达到了实时边说边转文字的功能。别小看这一步界面上的优化,它背后代表的是微信语音识别能力上的技术突破。

△ 语音实时转文字

锚点定位

微信有很多隐性和显性的锚点,隐性锚点就比如你打开的这篇文章,关闭后,再重新点进来,还是显示上次阅读的位置。

△ 订阅号列表

显性的锚点就比如上面这个:当你刷公众号列表时候,如果有新文章更新,标题栏会出现一个锚点按钮,点击它让你快速回到顶部,方便查看文章。

△ 朋友圈「跳到还没看的位置」

基于此,在新版微信朋友圈中,增加了一个「跳到还没看的位置」。很多信息流产品是往下刷内容,直到给你一个分界线提示:下面内容已经看过了。而微信这是一个逆向操作,我认为这个功能还是很有必要的,因为经常会有刷朋友圈刷到一半聊天退出去,当回来查看朋友圈时,需要重新拉到底部,费时费力。

自然的语音听筒播放

《在你身边,为你设计》一书中有提到语音听筒播放的优化。大家都知道,手机带有距离感应器,在感应到耳边贴近时,屏幕会关闭以节省电力,并且避免由于耳朵与屏幕的触碰导致的误操作。

微信在聊天界面中,也启用了距离感应,以实现手机贴近耳边时,自动将语音从扬声器切换到听筒进行播放,这样你可以用最自然的姿势来听语音,这是一个很好的体验。

但要完美地实现这个体验,就需要解决距离感应器的时延问题。播放语音时,如果你非常迅速地将手机移至耳边,这时候距离感应器并不会同样迅速地对这个动作产生反馈。大约在延迟了 300 毫秒后,感应器发出信号,微信将 iPhone 的屏幕关闭。而在这个时间内,你的耳廓极有可能已经触碰到了 iPhone 的屏幕上。触碰的位置大部分时候是左上角的返回按钮区域。于是很容易出现手机移至耳边,语音戛然而止。

△ 延时响应判断流程图

为了解决这个问题,微信设计了一个解决办法:在响应返回操作时,先等待 500 毫秒,这时候如果侦听到距离感应器有发出信号,则认为是贴耳的动作,此情况下不执行返回操作,如上图所示。而 500 毫秒的延时大部分时候你是不会感知到的。这一解决办法极大降低了贴耳时候的误操作。

总结

在微信的产品设计中,我们看到了交互的细微迭代和背后的技术突破,我们看到了商业创意与用户体验的平衡。给用户带来希望,让创造者体现价值,这就是微信的设计之道。

文章来源:优设    作者:洋爷

vue + vuex + koa2开发环境搭建及示例开发

seo达人

写在前面

这篇文章的主要目的是学会使用koa框架搭建web服务,从而提供一些后端接口,供前端调用。
搭建这个环境的目的是: 前端工程师在跟后台工程师商定了接口但还未联调之前,涉及到向后端请求数据的功能能够走前端工程师自己搭建的http路径,而不是直接在前端写几个死数据。即,模拟后端接口。

当然在这整个过程(搭建环境 + 开发示例demo)中,涉及到以下几点知识点。
包括:

  • koa2的知识点
  • node的知识点
  • 跨域问题
  • fetch的使用
  • axios的使用
  • promise的涉及
  • vuex -> state、mutations、actions的使用

第一部分:环境搭建

vue + vuex环境

首先是vue + vue-router + vuex的环境。我们用vue-cli脚手架生成项目,会用vue的同学对这块应该很熟了。

// 全局安装脚手架工具 npm i vue-cli -g // 验证脚手架工具安装成功与否 vue --version // 构建项目 vue init webpack 项目名 // 测试vue项目是否运行成功 npm run dev

因为脚手架生成的vue项目不包含vuex,所以再安装vuex。

// 安装vuex npm i vuex --save

koa2环境

前端项目构建好了,就开始构建我们的后端服务。

首先在你的开发工具(不管是webstorm还是sublime)里新建一个目录,用来搭建基于koa的web服务。

在这里,我们不妨给这个目录起名为koa-demo。

然后执行:

// 进入目录 cd koa-demo // 生成package.json npm init -y // 安装以下依赖项 npm i koa npm i koa-router npm i koa-cors

安装好koa和两个中间件,环境就算搭建完成了。

第二部分:示例开发

搭建环境是为了使用,所以我们立马来写一个demo出来。
demo开发既是一个练习如何在开发环境中写代码的过程,反过来,也是一个验证环境搭建的对不对、好不好用的过程。

后端接口开发

本例中,后端我们只提供一个服务,就是给前端提供一个返回json数据的接口。代码中包含注释,所以直接上代码。

server.js文件

 // server.js文件 let Koa = require('koa'); let Router = require('koa-router'); let cors = require('koa-cors'); // 引入modejs的文件系统API let fs = require('fs'); const app = new Koa(); const router = new Router(); // 提供一个/getJson接口 router
    .get('/getJson', async ctx => { // 后端允许cors跨域请求 await cors(); // 返回给前端的数据 ctx.body = JSON.parse(fs.readFileSync( './static/material.json'));

    }); // 将koa和两个中间件连起来 app.use(router.routes()).use(router.allowedMethods()); // 监听3000端口 app.listen(3000);

这里面用到了一个json文件,在'./static/material.json'路径,该json文件的代码是:

// material.json文件 [{ "id": 1, "date": "2016-05-02", "name": "张三", "address": "北京 清华大学",
}, { "id": 2, "date": "2016-05-04", "name": "李四", "address": "上海 复旦大学",
}, { "id": 3, "date": "2016-05-01", "name": "王五", "address": "广东 中山大学",
}, { "id": 4, "date": "2016-05-03", "name": "赵六", "address": "广东 深圳大学",
}, { "id": 5, "date": "2016-05-05", "name": "韩梅梅", "address": "四川 四川大学",
}, { "id": 6, "date": "2016-05-11", "name": "刘小律", "address": "湖南 中南大学",
}, { "id": 7, "date": "2016-04-13", "name": "曾坦", "address": "江苏 南京大学",
}] 

然后我们是用以下命令将服务启动

node server.js

测试接口是否良好

打开浏览器,输入http://127.0.0.1:3000/getJson。看一看页面上是否将json文件中的json数据显示出来,如果能够显示出来,则说明这个提供json数据的服务,我们已经搭建好了。

前端调用后端接口示例

为突出重点,排除干扰,方便理解。我们的前端就写一个组件,组件有两部分:首先是一个按钮,用来调用web服务的getJson接口;然后是一个内容展示区域,拿到后端返回的数据以后,将其在组件的这块区域显示出来

首先我们看组件文件

<template> <div class="test"> <button type="button" @click="getJson">从后端取json</button> <div class="showJson">{{json}}</div> </div> </template> <script> import {store} from '../vuex' export default { computed: {
          json(){ return store.state.json;
          }
        }, methods: {
          getJson(){
              store.dispatch("getJson");
          }
        }
    } </script> <style scoped> .showJson{ width:500px; margin:10px auto; min-height:500px; background-color: palegreen;
  } </style> 

非常简单,就不多解释了。
然后看我们的vuex文件

import Vue from 'vue' import Vuex from 'vuex';

Vue.use(Vuex) const state = { json: [],
}; const mutations = {
  setJson(state, db){
    state.json = db;
  }
} const actions = {
  getJson(context){ // 调用我们的后端getJson接口 fetch('http://127.0.0.1:3000/json', { method: 'GET', // mode:'cors', headers: { 'Accept': 'application/json', 'Content-Type': 'application/json',
      },
    }).then(function (res) { if(res.status === 200){ return res.json()
      }
    }).then(function (json) { //console.log(typeof Array.from(json), Array.from(json)); context.commit('setJson', Array.from(json));
    })
  }
}; export const store = new Vuex.Store({ state: state, mutations: mutations, actions: actions,
})

ok, 代码撸完了,获取后端数据之前是这样的。

获取后端数据之后是这样的。

说说axios

想要把本demo的fetch改为axios方式,要做的工作有以下几处:
1、安装axios、在vuex文件引用axios

npm i axios import axios from 'axios'

2、将fetch部分代码替换为:

const actions = {
  getJson(context){
    axios.get('/json', { method: 'GET', // mode:'cors', headers: { 'Accept': 'application/json', 'Content-Type': 'application/json',
      },
    }).then(function (res) { if(res.status === 200){ return res.data
      }
    }).then(function (json) { //console.log(typeof Array.from(json), Array.from(json)); context.commit('setJson', Array.from(json));
    })
  }
};

3、又会遇到跨域,在webpack中修改,路径config/index.js文件中添加proxyTable项的配置:

proxyTable: { '/json': { target: 'http://127.0.0.1:3000', changeOrigin: true, pathRewrite: { '^/json': '/json' }
      }
    },

后记

基于vue脚手架搭建的项目,模拟异步取数据,也可以直接在脚手架生成的static文件夹下放置数据,假装是后台拿过来的数据。

不过搭建一个基于express或者koa的web服务,确实也该是一个前端工程师应该掌握的。

OK,以上就是全文了。
如果这篇文章使你有所收获,不胜荣幸。
欢迎点赞,以期能帮助更多同学!

GitHub如何配置SSH Key

前端达人

文章目录

    • 步骤


    • https://github.com/xiangshuo1992/preload.git
      git@github.com:xiangshuo1992/preload.git
      这两个地址展示的是同一个项目,但是这两个地址之间有什么联系呢?
      前者是https url 直接有效网址打开,但是用户每次通过git提交的时候都要输入用户名和密码,有没有简单的一点的办法,一次配置,永久使用呢?当然,所以有了第二种地址,也就是SSH URL,那如何配置就是本文要分享的内容。
      GitHub配置SSH Key的目的是为了帮助我们在通过git提交代码是,不需要繁琐的验证过程,简化操作流程。
      
      步骤
              

      一、设置git的user name和email

      如果你是第一次使用,或者还没有配置过的话需要操作一下命令,自行替换相应字段。
      git config --global user.name "Luke.Deng"
      git config --global user.email  "xiangshuo1992@gmail.com"
            
              

      二、检查是否存在SSH Key

      cd ~/.ssh
      ls
      或者
      ll
      //看是否存在 id_rsa 和 id_rsa.pub文件,如果存在,说明已经有SSH Key
      如果没有SSH Key,则需要先生成一下
      
      
      ssh-keygen -t rsa -C "xiangshuo1992@gmail.com"
            
              

      三、获取SSH Key

      cat id_rsa.pub
      //拷贝秘钥 ssh-rsa开头
            
              

      四、GitHub添加SSH Key

      GitHub点击用户头像,选择setting
       
      新建一个SSH Key 
      取个名字,把之前拷贝的秘钥复制进去,添加就好啦。
            
              

      五、验证和修改

      测试是否成功配置SSH Key
      
      
      ssh -T git@github.com
      //运行结果出现类似如下
      Hi xiangshuo1992! You've successfully authenticated, but GitHub does not provide shell access.
      之前已经是https的链接,现在想要用SSH提交怎么办?
      直接修改项目目录下 .git文件夹下的config文件,将地址修改一下就好了。
            
              


      ———————————————— 版权声明:本文为CSDN博主「前端向朔」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/u013778905/java/article/details/83501204



深入理解vue中的slot与slot-scope

seo达人

写在前面

vue中关于插槽的文档说明很短,语言又写的很凝练,再加上其和methods,data,computed等常用选项使用频率、使用先后上的差别,这就有可能造成初次接触插槽的开发者容易产生“算了吧,回头再学,反正已经可以写基础组件了”,于是就关闭了vue说明文档。

实际上,插槽的概念很简单,下面通过分三部分来讲。这个部分也是按照vue说明文档的顺序来写的。

进入三部分之前,先让还没接触过插槽的同学对什么是插槽有一个简单的概念:插槽,也就是slot,是组件的一块HTML模板,这块模板显示不显示、以及怎样显示由父组件来决定。 实际上,一个slot最核心的两个问题这里就点出来了,是显示不显示怎样显示

由于插槽是一块模板,所以,对于任何一个组件,从模板种类的角度来分,其实都可以分为非插槽模板插槽模板两大类。
非插槽模板指的是html模板,指的是‘div、span、ul、table’这些,非插槽模板的显示与隐藏以及怎样显示由插件自身控制;插槽模板是slot,它是一个空壳子,因为它显示与隐藏以及最后用什么样的html模板显示由父组件控制。但是插槽显示的位置确由子组件自身决定,slot写在组件template的哪块,父组件传过来的模板将来就显示在哪块

单个插槽 | 默认插槽 | 匿名插槽

首先是单个插槽,单个插槽是vue的官方叫法,但是其实也可以叫它默认插槽,或者与具名插槽相对,我们可以叫它匿名插槽。因为它不用设置name属性。

单个插槽可以放置在组件的任意位置,但是就像它的名字一样,一个组件中只能有一个该类插槽。相对应的,具名插槽就可以有很多个,只要名字(name属性)不同就可以了。

下面通过一个例子来展示。

父组件:


    
  1. <template>
  2. <div class="father">
  3. <h3>这里是父组件</h3>
  4. <child>
  5. <div class="tmpl">
  6. <span>菜单1</span>
  7. <span>菜单2</span>
  8. <span>菜单3</span>
  9. <span>菜单4</span>
  10. <span>菜单5</span>
  11. <span>菜单6</span>
  12. </div>
  13. </child>
  14. </div>
  15. </template>

子组件:


    
  1. <template>
  2. <div class="child">
  3. <h3>这里是子组件</h3>
  4. <slot></slot>
  5. </div>
  6. </template>

在这个例子里,因为父组件在<child></child>里面写了html模板,那么子组件的匿名插槽这块模板就是下面这样。也就是说,子组件的匿名插槽被使用了,是被下面这块模板使用了。


    
  1. <div class="tmpl">
  2. <span>菜单1</span>
  3. <span>菜单2</span>
  4. <span>菜单3</span>
  5. <span>菜单4</span>
  6. <span>菜单5</span>
  7. <span>菜单6</span>
  8. </div>

最终的渲染结果如图所示:



    
  1. 注:所有demo都加了样式,以方便观察。其中,父组件以灰色背景填充,子组件都以浅蓝色填充。

具名插槽

匿名插槽没有name属性,所以是匿名插槽,那么,插槽加了name属性,就变成了具名插槽。具名插槽可以在一个组件中出现N次。出现在不同的位置。下面的例子,就是一个有两个具名插槽单个插槽的组件,这三个插槽被父组件用同一套css样式显示了出来,不同的是内容上略有区别。

父组件:


    
  1. <template>
  2. <div class="father">
  3. <h3>这里是父组件</h3>
  4. <child>
  5. <div class="tmpl" slot="up">
  6. <span>菜单1</span>
  7. <span>菜单2</span>
  8. <span>菜单3</span>
  9. <span>菜单4</span>
  10. <span>菜单5</span>
  11. <span>菜单6</span>
  12. </div>
  13. <div class="tmpl" slot="down">
  14. <span>菜单-1</span>
  15. <span>菜单-2</span>
  16. <span>菜单-3</span>
  17. <span>菜单-4</span>
  18. <span>菜单-5</span>
  19. <span>菜单-6</span>
  20. </div>
  21. <div class="tmpl">
  22. <span>菜单->1</span>
  23. <span>菜单->2</span>
  24. <span>菜单->3</span>
  25. <span>菜单->4</span>
  26. <span>菜单->5</span>
  27. <span>菜单->6</span>
  28. </div>
  29. </child>
  30. </div>
  31. </template>

子组件:


    
  1. <template>
  2. <div class="child">
  3. // 具名插槽
  4. <slot name="up"></slot>
  5. <h3>这里是子组件</h3>
  6. // 具名插槽
  7. <slot name="down"></slot>
  8. // 匿名插槽
  9. <slot></slot>
  10. </div>
  11. </template>

显示结果如图:


可以看到,父组件通过html模板上的slot属性关联具名插槽。没有slot属性的html模板默认关联匿名插槽。

作用域插槽 | 带数据的插槽

最后,就是我们的作用域插槽。这个稍微难理解一点。官方叫它作用域插槽,实际上,对比前面两种插槽,我们可以叫它带数据的插槽。什么意思呢,就是前面两种,都是在组件的template里面写


    
  1. 匿名插槽
  2. <slot></slot>
  3. 具名插槽
  4. <slot name="up"></slot>

但是作用域插槽要求,在slot上面绑定数据。也就是你得写成大概下面这个样子。


    
  1. <slot name="up" :data="data"></slot>
  2. export default {
  3. data: function(){
  4. return {
  5. data: ['zhangsan','lisi','wanwu','zhaoliu','tianqi','xiaoba']
  6. }
  7. },
  8. }

我们前面说了,插槽最后显示不显示是看父组件有没有在child下面写模板,像下面那样。


    
  1. <child>
  2. html模板
  3. </child>

写了,插槽就总得在浏览器上显示点东西,东西就是html该有的模样,没写,插槽就是空壳子,啥都没有。
OK,我们说有html模板的情况,就是父组件会往子组件插模板的情况,那到底插一套什么样的样式呢,这由父组件的html+css共同决定,但是这套样式里面的内容呢?

正因为作用域插槽绑定了一套数据,父组件可以拿来用。于是,情况就变成了这样:样式父组件说了算,但内容可以显示子组件插槽绑定的。

我们再来对比,作用域插槽和单个插槽和具名插槽的区别,因为单个插槽和具名插槽不绑定数据,所以父组件是提供的模板要既包括样式由包括内容的,上面的例子中,你看到的文字,“菜单1”,“菜单2”都是父组件自己提供的内容;而作用域插槽,父组件只需要提供一套样式(在确实用作用域插槽绑定的数据的前提下)。

下面的例子,你就能看到,父组件提供了三种样式(分别是flex、ul、直接显示),都没有提供数据,数据使用的都是子组件插槽自己绑定的那个人名数组。

父组件:


    
  1. <template>
  2. <div class="father">
  3. <h3>这里是父组件</h3>
  4. <!--第一次使用:用flex展示数据-->
  5. <child>
  6. <template slot-scope="user">
  7. <div class="tmpl">
  8. <span v-for="item in user.data">{{item}}</span>
  9. </div>
  10. </template>
  11. </child>
  12. <!--第二次使用:用列表展示数据-->
  13. <child>
  14. <template slot-scope="user">
  15. <ul>
  16. <li v-for="item in user.data">{{item}}</li>
  17. </ul>
  18. </template>
  19. </child>
  20. <!--第三次使用:直接显示数据-->
  21. <child>
  22. <template slot-scope="user">
  23. {{user.data}}
  24. </template>
  25. </child>
  26. <!--第四次使用:不使用其提供的数据, 作用域插槽退变成匿名插槽-->
  27. <child>
  28. 我就是模板
  29. </child>
  30. </div>
  31. </template>

子组件:


    
  1. <template>
  2. <div class="child">
  3. <h3>这里是子组件</h3>
  4. // 作用域插槽
  5. <slot :data="data"></slot>
  6. </div>
  7. </template>
  8. export default {
  9. data: function(){
  10. return {
  11. data: ['zhangsan','lisi','wanwu','zhaoliu','tianqi','xiaoba']
  12. }
  13. }
  14. }

结果如图所示:

github

以上三个demo就放在GitHub了,有需要的可以去取。使用非常方便,是基于vue-cli搭建工程。

https://github.com/cunzaizhuyi/vue-slot-demo

你真的了解重排和重绘吗?

前端达人

做过前端开发的小伙伴就算不是非常理解重排与重绘,但是肯定都听过这两个词。那为什么这两个东西这么重要?因为他与我们的页面性能息息相关,今天,我们就来好好研究一下这两个东西。



浏览器的渲染流程

在讲解重排和重绘之前,我们有必要说一下浏览器的渲染流程。下面是浏览器渲染过程中最关键的几个部分。如果想了解完整的浏览器渲染流程,推荐大家去阅读李兵老师的浏览器工作原理实践,需要付费阅读。后期我也会整理一下再出一篇博客详细介绍浏览器的渲染过程。


点击查看原图


JavaScript:一般来说,我们会使用 JavaScript 来实现一些视觉变化的效果。比如用 jQuery 的 animate 函数做一个动画、对一个数据集进行排序或者往页面里添加一些 DOM 元素等。当然,除了 JavaScript,还有其他一些常用方法也可以实现视觉变化效果,比如:CSS Animations、Transitions 和 Web Animation API。
样式计算:此过程是根据匹配选择器(例如 .headline 或 .nav > .nav__item)计算出哪些元素应用哪些 CSS 规则的过程。从中知道规则之后,将应用规则并计算每个元素的最终样式。
布局:在知道对一个元素应用哪些规则之后,浏览器即可开始计算它要占据的空间大小及其在屏幕的位置。网页的布局模式意味着一个元素可能影响其他元素,例如 元素的宽度一般会影响其子元素的宽度以及树中各处的节点,因此对于浏览器来说,布局过程是经常发生的。
绘制:绘制是填充像素的过程。它涉及绘出文本、颜色、图像、边框和阴影,基本上包括元素的每个可视部分。绘制一般是在多个表面(通常称为层)上完成的。
合成:由于页面的各部分可能被绘制到多层,由此它们需要按正确顺序绘制到屏幕上,以便正确渲染页面。对于与另一元素重叠的元素来说,这点特别重要,因为一个错误可能使一个元素错误地出现在另一个元素的上层。
其中,重排和重绘影响的就是其中的布局和绘制过程。

什么是重排和重绘制
重排:当DOM的变化引发了元素几何属性的变化,比如改变元素的宽高,元素的位置,导致浏览器不得不重新计算元素的几何属性,并重新构建渲染树,这个过程称为“重排”。
重绘:完成重排后,要将重新构建的渲染树渲染到屏幕上,这个过程就是“重绘”。
简单来说,涉及元素的几何更新时,叫重排。而只涉及样式更新而不涉及几何更新时,叫重绘。对于两者来说,重排必定引起重绘,但是重绘并不一定引起重排。所以,当涉及重排时,浏览器会将上述的步骤再次执行一遍。当只涉及重绘时,浏览器会跳过Layout步骤,即:


点击查看原图


而如果既不需要重排,也不需要重绘,那么就是下面这样:


点击查看原图



浏览器会直接跳到合成阶段。显然,对于页面性能来说,不重排也不重绘 > 重绘 > 重排。

什么操作会引起重排和重绘
显然,触发重排的一般都是几何因素,这是比较好理解的:

页面第一次渲染 在页面发生首次渲染的时候,所有组件都要进行首次布局,这是开销最大的一次重排
浏览器窗口尺寸改变
元素位置和尺寸发生改变的时候
新增和删除可见元素
内容发生改变(文字数量或图片大小等等)
元素字体大小变化
还有其他一些操作也可能引发重排

查询某些属性或调用某些方法
offset(Top|Left|Width|Height)
scroll(Top|Left|Width|Height)
client(Top|Left|Width|Height)
getComputedStyle()
我们可能不太理解为什么这些操作也能引起重排,这里我先简单解释一下。因为现在的浏览器已经非常完善了,会自动帮我们做一些优化。当我们用js操作DOM的时候,浏览器并不是立马执行的,而是将操作存储在一个队列中。当达到一定数量或者经过一定时间以后浏览器再统一的去执行队列中的操作。那么回到我们刚才的问题,为什么查询这些属性也会导致重排?因为当你查询这些属性时,浏览器就会强制刷新队列,因为如果不立马执行队列中的操作,有可能得到的结果就是错误的。所以相当于你强制打断了浏览器的优化流程,引发了重排。下面我们通过一些小例子来进一步理解这段话:

首先我们来一个显然会引发重排的操作

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    #test {
      width: 100px;
      height: 100px;
      background-color: red;
      position: relative;
    }
  </style>
</head>
<body>
  <div id="test">

  </div>
  <button onclick="reflow()">click</button>
  <script>
    function reflow() {
      var div = document.querySelector("#test");
      div.style.left = '200px';
    }
  </script>
</body>
</html>



把时间轴往后拉,可以看到这几个过程,先简单介绍一些这些名词代表的含义:

Recalculate Style:这个过程就是生成CSSOM的过程
Layout:这就是布局阶段,即重排的过程
Update Layer Tree:这个阶段是更新层树的过程
Paint:该阶段是为每一层准备绘制列表的过程
Composite Layers:该阶段是利用绘制列表来生成相应图层的位图了,还涉及到合成线程和光栅化,performence面板中的Raster就是光栅化线程池 。
这里只做一个简单的介绍,对其中内容不太明白的同学可以参考李兵老师的文章或者在我的下一篇介绍浏览器渲染过程的文章中会详细解释。

那通过这个图我们可以看到,我们改变了div的left之后就触发了Layout,即重排的过程。下面我们仅改变div的背景颜色,给大家一个对比。


即不重排也不重绘
说完了重排和重绘,不要忘记我们最开始提到的,最的方式就是跳过重排和重绘阶段。你可能会想,什么情况下可以做到这一点?其实这就是我们平时说的GPU加速,具体是如何实现呢?在开发过程中,如果我们使用了某些属性,浏览器会帮助我们将使用了该属性的div提升到一个单独的合成层,而在后面的渲染中,提升到该层的div将跳过重排和重绘的操作,直接到合成阶段。在stack overflow上有问题提到了这块内容。我们翻译一下就是:
下面几个属性能让浏览器帮助我们将div提升到一个单独的合成层:

图层具有3D或透视变换CSS属性
使用加速视频解码的 video 元素
拥有 3D(WebGL) 上下文或者加速 2D 上下文的 canvas 元素
混合插件(Flash)
对自己的 opacity 做 CSS 动画或使用一个动画 webkit 变换的元素
图层使用加速的CSS过滤器
层具有作为合成层的后代
图层具有较低z索引的同级元素,该同级元素具有合成层(换句话说,该层在合成层的顶部渲染)
css will-change属性
最后一点是我加上去的,同时根据文中的内容我们可以知道,css3硬件加速是浏览器的行为,所以在不同浏览器下可能会有不同的表现形式。下面我们用一个例子来理解一下。这是李兵老师在他的专栏中提出的一个例子,我拿过来借用一下,注意box中的will-change属性:

<html>

  <head>
      <title>观察will-change</title>
      <style>
          .box {
              will-change: transform, opacity;
              display: block;
              float: left;
              width: 40px;
              height: 40px;
              margin: 15px;
              padding: 10px;
              border: 1px solid rgb(136, 136, 136);
              background: rgb(187, 177, 37);
              border-radius: 30px;
              transition: border-radius 1s ease-out;
          }

          body {
              font-family: Arial;
          }
      </style>
  </head>

  <body>
      <div id="controls">
          <button id="start">start</button>
          <button id="stop">stop</button>
      </div>
      <div>
          <div class="box">旋转盒子</div>
          <div class="box">旋转盒子</div>
          <div class="box">旋转盒子</div>
          <div class="box">旋转盒子</div>
          <div class="box">旋转盒子</div>
          <div class="box">旋转盒子</div>
          <div class="box">旋转盒子</div>
          <div class="box">旋转盒子</div>
          <div class="box">旋转盒子</div>
          <div class="box">旋转盒子</div>
          <div class="box">旋转盒子</div>
          <div class="box">旋转盒子</div>
          <div class="box">旋转盒子</div>
          <div class="box">旋转盒子</div>
          <div class="box">旋转盒子</div>
          <div class="box">旋转盒子</div>
          <div class="box">旋转盒子</div>
          <div class="box">旋转盒子</div>
          <div class="box">旋转盒子</div>
          <div class="box">旋转盒子</div>
          <div class="box">旋转盒子</div>
          <div class="box">旋转盒子</div>
          <div class="box">旋转盒子</div>
          <div class="box">旋转盒子</div>
          <div class="box">旋转盒子</div>
          <div class="box">旋转盒子</div>
          <div class="box">旋转盒子</div>
          <div class="box">旋转盒子</div>
          <div class="box">旋转盒子</div>
          <div class="box">旋转盒子</div>
          <div class="box">旋转盒子</div>
          <div class="box">旋转盒子</div>
          <div class="box">旋转盒子</div>
          <div class="box">旋转盒子</div>
          <div class="box">旋转盒子</div>
          <div class="box">旋转盒子</div>
          <div class="box">旋转盒子</div>
          <div class="box">旋转盒子</div>
          <div class="box">旋转盒子</div>
          <div class="box">旋转盒子</div>
          <div class="box">旋转盒子</div>
          <div class="box">旋转盒子</div>
          <div class="box">旋转盒子</div>
          <div class="box">旋转盒子</div>
          <div class="box">旋转盒子</div>
          <div class="box">旋转盒子</div>
          <div class="box">旋转盒子</div>
          <div class="box">旋转盒子</div>
          <div class="box">旋转盒子</div>
          <div class="box">旋转盒子</div>
          <div class="box">旋转盒子</div>
          <div class="box">旋转盒子</div>
          <div class="box">旋转盒子</div>
          <div class="box">旋转盒子</div>
          <div class="box">旋转盒子</div>
          <div class="box">旋转盒子</div>
          <div class="box">旋转盒子</div>
          <div class="box">旋转盒子</div>
          <div class="box">旋转盒子</div>
          <div class="box">旋转盒子</div>
      </div>
      <script>

          let boxes = document.querySelectorAll('.box');
          let boxes1 = document.querySelectorAll('.box1');
          let start = document.getElementById('start');
          let stop = document.getElementById('stop');
          let stop_flag = false

          start.addEventListener('click', function () {
              stop_flag = false
              requestAnimationFrame(render);
          })

          stop.addEventListener('click', function () {
              stop_flag = true
          })

          let rotate_ = 0
          let opacity_ = 0
          function render() {
              if (stop_flag)
                  return 0
              rotate_ = rotate_ + 6
              if (opacity_ > 1)
                  opacity_ = 0
              opacity_ = opacity_ + 0.01
              let command = 'rotate(' + rotate_ + 'deg)';
              for (let index = 0; index < boxes.length; index++) {
                  boxes[index].style.transform = command
                  boxes[index].style.opacity = opacity_
              }
              requestAnimationFrame(render);
          }
      </script>
  </body>

  </html>



————————————————
版权声明:本文为CSDN博主「溪宁」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_38164763/article/details/105406580

日历

链接

个人资料

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

存档