首页

你可能不需要在 JavaScript 使用 switch 语句!

seo达人

没有 switch 就没有复杂的代码块

switch很方便:给定一个表达式,我们可以检查它是否与一堆case子句中的其他表达式匹配。 考虑以下示例:


const name = "Juliana";


switch (name) {

 case "Juliana":

   console.log("She's Juliana");

   break;

 case "Tom":

   console.log("She's not Juliana");

   break;

}

当 name 为“Juliana”时,我们将打印一条消息,并立即中断退出该块。 在switch函数内部时,直接在 case 块使用 return,就可以省略break。


当没有匹配项时,可以使用 default 选项:


const name = "Kris";


switch (name) {

 case "Juliana":

   console.log("She's Juliana");

   break;

 case "Tom":

   console.log("She's not Juliana");

   break;

 default:

   console.log("Sorry, no match");

}

switch在 Redux reducers 中也大量使用(尽管Redux Toolkit简化了样板),以避免产生大量的if。 考虑以下示例:


const LOGIN_SUCCESS = "LOGIN_SUCCESS";

const LOGIN_FAILED = "LOGIN_FAILED";


const authState = {

 token: "",

 error: "",

};


function authReducer(state = authState, action) {

 switch (action.type) {

   case LOGIN_SUCCESS:

     return { ...state, token: action.payload };

   case LOGIN_FAILED:

     return { ...state, error: action.payload };

   default:

     return state;

 }

}

这有什么问题吗?几乎没有。但是有没有更好的选择呢?


从 Python 获得的启示

来自 Telmo 的这条 Tweet引起了我的注意。 他展示了两种“switch”风格,其中一种非常接近Python中的模式。


Python 没有开关,它给我们一个更好的替代方法。 首先让我们将代码从 JavaScript 移植到Python:


LOGIN_SUCCESS = "LOGIN_SUCCESS"

LOGIN_FAILED = "LOGIN_FAILED"


auth_state = {"token": "", "error": ""}



def auth_reducer(state=auth_state, action={}):

   mapping = {

       LOGIN_SUCCESS: {**state, "token": action["payload"]},

       LOGIN_FAILED: {**state, "error": action["payload"]},

   }


   return mapping.get(action["type"], state)

在 Python 中,我们可以使用字典来模拟switch 。 dict.get() 可以用来表示 switch 的 default 语句。


当访问不存在的key时,Python 会触发一个 KeyError 错误:


>>> my_dict = {

   "name": "John",

   "city": "Rome",

   "age": 44

   }


>>> my_dict["not_here"]


# Output: KeyError: 'not_here'

.get()方法是一种更安全方法,因为它不会引发错误,并且可以为不存在的key指定默认值:


>>> my_dict = {

   "name": "John",

   "city": "Rome",

   "age": 44

   }


>>> my_dict.get("not_here", "not found")


# Output: 'not found'

因此,Pytho n中的这一行:


return mapping.get(action["type"], state)

等价于 JavaScript中的:


function authReducer(state = authState, action) {

 ...

   default:

     return state;

 ...

}

使用字典的方式替换 switch

再次思考前面的示例:


const LOGIN_SUCCESS = "LOGIN_SUCCESS";

const LOGIN_FAILED = "LOGIN_FAILED";


const authState = {

 token: "",

 error: "",

};


function authReducer(state = authState, action) {

 switch (action.type) {

   case LOGIN_SUCCESS:

     return { ...state, token: action.payload };

   case LOGIN_FAILED:

     return { ...state, error: action.payload };

   default:

     return state;

 }

}

如果不使用 switch 我们可以这样做:


function authReducer(state = authState, action) {

 const mapping = {

   [LOGIN_SUCCESS]: { ...state, token: action.payload },

   [LOGIN_FAILED]: { ...state, error: action.payload }

 };


 return mapping[action.type] || state;

}

这里我们使用 ES6 中的计算属性,此处,mapping的属性是根据两个常量即时计算的:LOGIN_SUCCESS 和 LOGIN_FAILED。

属性对应的值,我们这里使用的是对象解构,这里 ES9((ECMAScript 2018)) 出来的。


const mapping = {

 [LOGIN_SUCCESS]: { ...state, token: action.payload },

 [LOGIN_FAILED]: { ...state, error: action.payload }

}

你如何看待这种方法?它对 switch 来说可能还能一些限制,但对于 reducer 来说可能是一种更好的方案。


但是,此代码的性能如何?


性能怎么样?

switch 的性能优于字典的写法。我们可以使用下面的事例测试一下:


console.time("sample");

for (let i = 0; i < 2000000; i++) {

 const nextState = authReducer(authState, {

   type: LOGIN_SUCCESS,

   payload: "some_token"

 });

}

console.timeEnd("sample");

测量它们十次左右,


for t in {1..10}; do node switch.js >> switch.txt;done

for t in {1..10}; do node map.js >> map.txt;done

clipboard.png


人才们的 【三连】 就是小智不断分享的最大动力,如果本篇博客有任何错误和建议,欢迎人才们留言,最后,谢谢大家的观看。


原文:https://codeburst.io/alternat...


代码部署后可能存在的BUG没法实时知道,事后为了解决这些BUG,花了大量的时间进行log 调试,这边顺便给大家推荐一个好用的BUG监控工具 Fundebug。

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



vuex管理状态仓库详解

seo达人

一.什么是Vuex?

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。Vuex 也集成到 Vue 的官方调试工具 devtools extension,提供了诸如零配置的 time-travel 调试、状态快照导入导出等高级调试功能。采用了全局单例模式,将组件的共享状态抽离出来管理,使得组件树中每一个位置都可以获取共享的状态或者触发行为。
那么什么是状态呢?我把状态理解为在没有使用vuex时,在当前组件中data内需要共用的数据为状态。
vuex使得状态或行为成为了共享的状态,所共享的状态或行为可以在各个组件中都可以访问到,省去了子父或子子之间传递变量,提高了开发效率。

二.不使用vuex时与使用vuex时的差别

当我们不使用vuex时,对于组件之间传递信息会较为麻烦。

不使用vuex时

父子之间传递信息:

App.vue文件中:

<template>
  <div id="app">
      <Fruits :fruitList="fruitList"/>
  </div>
</template> 
<script> import Goods from './components/Goods'; export default { name: 'App',
  components:{
    Fruits,
    Goods
  }, data(){
    return{ goodList:[
      {
        name:'doll',
        price:12 },
      { name:'glass',
        price:10 }
    ],
    }
  }
}
</script>
<style>
</style>

Good.vue文件中:

<template>
  <div class="hello">
      <ul>
        <li v-for="(good,index) in goodList" :key="index"> name:{{good.name}} number: {{good.number}} {{index}}
        </li>
      </ul>
  </div>
</template>

<script> export default { props:['goodList'],
}
</script>
<style>

</style>

兄弟之间传递信息:

首先先创建一个js文件作为两兄弟之间传输的纽扣,这里起名为msg.js

//创建并暴露vue import Vue from 'vue';
export default new Vue

兄弟组件Goods:

<template>
  <div>
        <button @click="deliver">点击</button>
  </div>
</template>

<script> import MSG from '../msg';
export default {
  data(){ return{
      msg:'hahah' }
  },
  methods:{
    deliver() {
        MSG.$emit('showMsg',this.msg)
    }
  }

}
</script>
<style>

</style>

兄弟组件Fruits:

<template>
  <div>
      <div class="fruit">
          {{text}}
      </div>
  </div>
</template>
<script> import MSG from '../msg';
export default {
    data(){ return{
        text:'' }
    },
    created(){ this.getMsg()
    },
    methods:{
      getMsg(){
        MSG.$on('showMsg',(message)=>{ this.text = message
        })
      }
    }
}
</script>
<style>
</style>

在App组件中的代码:
在这里插入图片描述
点击按钮:


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

Tubik Studio 是怎么为华为定制整套 UI 图标的?

雪涛

一套 UI 界面当中,核心的 APP 图标是用户每天都要接触、经常使用的视觉组件和交互对象。设计图标的时候,要用到大家最熟悉的元素才能贴合用户认知,要醒目、统一,也要足够「隐形」,避免喧宾夺主。今天这篇文章,来自著名的 Tubik Studio 团队,他们为华为旗下的 EMUI 10 系统设计了核心的图标系统,来看看他们的设计过程吧。

Tubik Studio 是怎么为华为定制整套 UI 图标的?

我们总不会低估一个操作系统基础图标,对于产品的可用性和合意性的影响。基础图标虽然小巧,但是对于整个操作系统而言,总是极具影响力的,因为他们是用户界面的核心元素,帮助用户快速直观地在系统中定位、浏览、导航。但是,对于设计师而言,图标的设计始终是挑战,它看起来最为简单,但实则为最为艰难的工作。

图标需要,让人一目了然,但是也要具备良好的可识别性,在传统和创新之间达到平衡。这一次,Tubik Studio 设计团队将要给华为的 EMUI 10 来设计图标,而这篇文章将会为你揭示背后的设计过程。

这次的项目主要是由 Sergii Valiukh 、Arthur Avakyan 和 Polina Taran 来负责。这次的设计项目从最初的探索构思入手,逐渐开始绘制草图,探索样式,一直到最后打磨,完成设计。

Tubik Studio 是怎么为华为定制整套 UI 图标的?

项目内容

为华为 EMUI 10 系统的用户界面设计基础的图标

客户介绍

我们在 2019 年夏季,收到了来自华为的邀约,这次的项目要重新设计 EMUI 这套基于 Android 系统的用户界面基础图标,这套图标会用来适配华为旗下的旗舰手机,这些图标将会随着新版的系统部署到这些手机产品当中。我们的任务,是创建总计 54 款符合当下潮流趋势的图标,这些图标要能够贴合品牌和已有用户的偏好,并且能够吸引新的用户。

这个图标设计项目如今已经在当下的华为旗下手机产品中应用且部署好了,最早使用这套图标的智能手机型号为 Mate 30 ,紧接其后的是 P40。

Tubik Studio 是怎么为华为定制整套 UI 图标的?

设计过程

在整个操作系统中,这些图标是始终位于用户视野以内、最基础的最核心的交互元素,通常用户每天都会同这些核心的基础 APP 进行交互,有时候一天多达几十次,因此它们应当具备良好的功能性,还应该足够美观,给用户带来满足感。同时,这套图标的设计,也应当足够统一,以协调的体验切入到整个 EMUI 的设计系统当中,贴合整体的样式特征。

所以,我们使用了一整套图标网格系统,应对不同需求,在设计的过程中,这套网格有助于确保所有图标外部尺寸的一致性,也保证了内部元素的统一性。

Tubik Studio 是怎么为华为定制整套 UI 图标的?

为了发掘全新的视角,我们决定从传统的手绘图标开始,寻找重新塑造图标设计的方法。这些图标所涉及到的元素,早已为全球数百万用户所熟知,要重新设计图标外观,并且还要成套且统一,这本身就是一个巨大的挑战。

Tubik Studio 是怎么为华为定制整套 UI 图标的?

比如「电话」图标所对应的听筒元素、「信息」图标所对应的气泡对话框这样的元素,是不可能完全抛弃重新创造的,所以我们的真正的切入点是在形态和色彩上寻求解决方案。

越是简单的东西,重塑起来就越难。

在实际的设计过程中,我们尝试了数以百计的造型变体,从完全放飞、非常规的的外部造型,到极其常规,大家熟知的造型解决方案,我们都逐一试过。而在色彩上的尝试相对会显得更加具有实验性:我们尝试使用明亮的紫罗兰色、栗色和浅绿色来进行混搭。

当然,我们很清楚,这样的实验性的工作是探索过程中的一小步,但是它是必须的,是创造新设计的种子,是寻求正确答案的必经之路。

Tubik Studio 是怎么为华为定制整套 UI 图标的?

在设计过程在,有一件有趣的事情发生在设计「相机」图标的过程中。我们尝试过很多不同的图标造型,不同的元素,不同的样式,但是其中始终有一个细节是不变的,那就是右上角的小红点。这是为了暗示用户,华为的摄像头模组来自徕卡,这个红色的小点就是向其致敬的标识。

Tubik Studio 是怎么为华为定制整套 UI 图标的?

而下面的概念设计,则是强化了图标之间的几何轮廓的差异,从而提升图标在智能手机屏幕上的对比度和识别度。

Tubik Studio 是怎么为华为定制整套 UI 图标的?

下一步,我们基于几何图形外观差异性,设计了多种造型不同但同样优雅的图标。在基于这种风格概念进行延伸的过程中,我们会优先考虑圆形的元素。而「天气」图标明显既不适合圆形也不适合方形来呈现,所以我们使用的是云和太阳两种元素的组合。「钱包」图标使用的是矩形,然后搭配卡片的组合。尽管造型整体上是相对自由的,但是所有的图标都是遵循图标网格,并且在视觉权重上尽可能统一。

Tubik Studio 是怎么为华为定制整套 UI 图标的?

在色彩和样式上,我们则更加倾向于渐变。没有色彩渐变,纯扁平的图标显得过于幼稚和「古早」,没有足够的品质感,所以,我们在新的图标设计上,开始加入渐变色彩,提升质感。

Tubik Studio 是怎么为华为定制整套 UI 图标的?

不过,在最终版本当中,我们还是保留了图标外部的圆角矩形的外轮廓,然后将图标元素的内径进行了适当地缩减,渐变和核心的简约几何特征依然是整套设计的最高优先级。

Tubik Studio 是怎么为华为定制整套 UI 图标的?

这套设计方案展现了在整套 UI 界面中,图标这个小元素的设计上所需要投入的精力和潜在的难度。想要图标足够协调、美观、易用还要贴合品牌特征、还要区别于以往,是一件相当不容易的事情。

Tubik Studio 是怎么为华为定制整套 UI 图标的?

细节里藏着魔鬼,任何细小的元素、线条轮廓、色彩的变化都可能会在屏幕上放大、被感知到。

文章来源:优设    作者:Tubik Studio

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

中央气象台近十年设计精品集

前端达人

今天整理网站类作品,看到给中央气象台近十年设计的诸多网页和系统。这几天每天关注鄱阳湖洪水状况,2018年改版时,有一版的设计方案,我设计了洪水到来时的专题,图片视频,动画提醒居民注意事项。同事们也花了许多精力时间做了大量工作。

作为公民,我们在用我们擅长的设计去服务社会,在这个过程中形成自己的积淀。这是不可用金钱来衡量的价值。

微信图片_20200721174610.png


微信图片_20200721174606.jpg


微信图片_20200721174603.png

微信图片_20200721174559.png

微信图片_20200721174555.png

微信图片_20200721174551.png

微信图片_20200721174547.png

微信图片_20200721174543.png

微信图片_20200721174539.png

微信图片_20200721174614.png







作者:蓝蓝

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

2020-2021 设计趋势ISUX报告 · 用户体验篇

分享达人

It is ultra experience



ISUX Design Trend Report

前言

——————————

身为用户体验设计师,无时无刻不被世界上的新事物冲刷着认知——互联网红利下降带来变化莫测的商业动向、循着摩尔定律野蛮生长日新月异的新技术、各类亚文化群体催生出多元复杂的圈层文化、脑洞口味越来越独特的年轻人,甚至眼下席卷全球的黑天鹅事件......


任何一个新事物的悄悄冒头,都有可能在未知的将来影响着用户体验设计师。我们能做的是,在起初感受到微微震幅时,便沿着震感逐步寻找源头,并思考未来的发展走向。赶在变化降临前先拥抱变化。


本文通过研究近一两年科技、社会文化以及自身用户体验领域的变化,从用户体验领域关键的用户、媒介(设备与应用)、交互行为、信息与场景的五个角度出发,探索用户体验设计未来的趋势,希望能带来启发。


随着人工时代到来,过去机械的单向交互方式逐渐被打破,机器渐渐演化成了会主动“观察”真实场景,“感受”用户情感,预判用户意图并自动完成任务的贴心小棉袄。机器如何为人们提供更智能便捷的服务,未来还有非常大的想象空间。 





随着人工时代到来,过去机械的单向交互方式逐渐被打破,机器渐渐演化成了会主动“观察”真实场景,“感受”用户情感,预判用户意图并自动完成任务的贴心小棉袄。机器如何为人们提供更智能便捷的服务,未来还有非常大的想象空间。



1-1

——————————

基于真实场景推理用户意图

随着AI技术的发展,智能设备可以越来越无缝地将数字世界和物理世界嫁接起来,主动感知用户所处情境并智能提供相应服务。


在2019的 Google I/O 大会上,Google Lens 展示的AR点菜功能可以智能识别用户扫描的菜单并将美食网站上的相关推荐直接呈现在屏幕上。


当用 Google Lens 识别到小票信息时,可快速提取小票上的金额,且可自动弹起计算器快速帮助用户计算人均消费,节省人工计算的时间成本。


随着信息入口从数字空间延伸到周围的物理空间中,未来万物皆可为用户体验的媒介,设计师未来在设计的时候需要注意:


寻找适合的打通真实世界的切入点:在陌生语言、信息复杂或者难以处理等苛刻的环境下,充分发挥智能设备对信息智能读取、批量识别与翻译等强大能力,帮助用户完成任务;


将用户旅程的上下游串联:根据生活常识和经验预判用户行为目的,前置推荐服务;


更加系统细心地考量干扰因素:真实场景是动态变化的,需要更全方位考虑光线的强弱、多源的噪音、实体的可视性、人员和事件的打断等因素。



1-2

——————————

任务自动化,简化用户旅程


为了完成一项任务,用户往往需要借助多个应用来回切换配合,使用起来琐碎麻烦。如今应用越做越强大也越复杂,过去仅仅解决单一场景的解决方案不再适应于用户对于完成任务的诉求。


Google Assistant 的新能力 Duplex on the web 可以通过自动跨应用任务处理来简化用户旅程。只需要用户发出语音指令“预定一辆去某地的车”,助手便可自动跨邮件、日历、付款等应用调取信息、自动根据使用习惯做选择,并自动填写信息,而用户全程需要的只是在关键节点轻敲“确认”即可。


2019年随着 iOS 13 的更新,“快捷指令”推出了“自动化”能力,用户通过“if...then...”语法便可为自己的App设计一套程序,实现如:“当我回到公司时提醒我打卡”、“每天早上10点给我的女朋友发送一条表白短信”等能力,将不相关的场景动作串联字一起自动化执行,大大节省人工操作成本。


提升使用效率是用户体验设计孜孜不倦努力的方向之一。在利用新技术进一步简化用户旅程时,设计师可以充分利用以下因素:


借助语音输入:比起界面触控操作,语音交互的直达性可以“穿透”复杂界面,让设备第一时间明确用户目标;


基于用户行为形成习惯记忆:对用户长期重复的行为做分析处理,构建用户习惯模型并主动提供服务;


适当考虑专家级用户:随着部分用户的智能设备使用水平越来越高,可以考虑为专家用户提供自定义操作脚本,满足其自身的独特需求。



1-3

——————————

基于情感感知,主动理解用户需求


随着人脸识别、表情识别、肢体跟踪等技术的提升,机器逐渐学会感性语言,主动感知用户内在情感和心理需求。


2019年1月的CES展上起亚亮相的互动式“情感驾驶空间”技术,可通过传感器读取用户的面部表情、心率等反应,调整驾驶空间内的灯光、影片类型、音乐风格等,舒缓舱内乘客心情,由此提供更人性化的出行体验。

用户总是会期待更贴心的服务,设计师未来对同理心的情感嗅觉更加敏锐:


利用感性线索定位用户情绪:需要通过面部表情、特殊时间节点或者识别到的关键词,对用户情绪进行理解和定位,判断用户情感理解用户内心诉求是自由探索、趣味娱乐、或者静谧修行并提供符合用户当下心境的服务。


综合使用感性元素进行设计:通过使用线条、色彩、声音和动作等传达并唤起相对应的情感,提供更加人性化的体验。


小结

更智能的服务提供方式会让人们生活拥有更多可能性,但一旦火候把握不得当,可能就会造成对人们生活的野蛮入侵。关于如何让科技更好造福于人们,早在上个世纪,施乐帕克研究中心提出了宁静技术(Calm Technology)的愿景,认为影响最深远的技术应该是隐匿不见的,它们如纤维般融入日常生活,丝丝入扣,直至不可分辨。


随着科技的发展,设计师对新技术不应是不加克制地应用,而应该润物细无声般地提供服务,帮助人们从繁杂喧嚣的数字世界中解脱出来,将宝贵的注意力资源投放在让生活更美好的事物上。



回顾人类和机器的交流语言,从命令行界面、图形用户界面到自然用户界面,人机交互方式越来越贴合人与人之间更自然的交流方式,其背后是心智模型与实现模型的高度拟合的趋势。


在自然用户界面中,为满足新形态智能硬件对新接口的需求,以及人们对更丰富强大的交互方式的自然诉求,越来越多的自然用户界面被开发出来。语音交互和隔空手势交互便是近几年迅速发展并落地的两种交互方式。



2-1

——————————

隔空手势交互:更自由、更灵动


为了让机器更好地读懂用户的身体语言,能够感知深度信息的摄像头走进了日常手机。2019年国内外手机厂商的发布大会上,LG 手机 G8 ThinQ 以及华为发布 Mate 30 系列推出的隔空手势,可实现一些简单的诸如滑动、切歌、截屏等效果。



除此以外,隔空手势支持更加细微的手势,如旋转、揉搓等,可以更直观、更灵活的方式操控界面,让用户获得一种像魔术师用意念控制事物运作的快感。



对于隔空手势操作网上的言论褒贬不一,其中争议性最大的就是隔空手势宛如“杀鸡用牛刀”,明明可以用更加精准的手势触控,为什么还要用看似很酷炫其实精准度更低的隔空手势操作?


隔空手势并不是要替代触控手势成为主流的人机交互方式,更多是对情境式障碍场景的补充。在某些场景下,用户使用设备的条件可能是充满干扰的。想想看当你边看手机食谱边炒菜的时候、边煲剧边剥小龙虾的时候、疫情期间出门佩戴橡胶手套无法正常触控手机屏幕时.....隔空手势是不是特别好用?


每个人在特殊的场景下都有可能面临感官障碍,未来的设计也应该更多地考虑情境式障碍的场景,让用户无论身处何时何地依旧能一如既往无障碍地使用设备。




2-2

——————————

语音交互:更精准、更好玩

语音交互作为更趋近于人与人之间最自然的交流方式,近些年有许多发展的突破点。


在发展主线上,语音交互趋向更自然、更人性化、更个性化。过去反人类的一些沟通方式慢慢被“调教”。此外,多人会话场景下的技术方案日渐增多。


2019的 Google I/O 大会展示了一个视频片段,视频中的两位嘉宾相继吐槽,经常出现针锋相对难以听清的时候,这时用户可以调节音源音量选择性增强自己关注的人物声音,让另一个人“静音”。


  滑动选择音源


此外,语音交互除了在智能音箱领域广泛应用以外,也逐渐应用在广告等更多的传播媒介中,刷新人们日常使用体验。2020年2月索尼提交了一项广告播放新专利。当用户在观看电视节目时,如果出现广告,只要站起来大喊广告中对应品牌的名字,便可直接跳过这个广告。


设计师在语音交互场景下,需要留意以下几个比较容易被忽视的因素:


用户语音交互习惯培养:如今还处于培养用户语音交互使用习惯阶段,设计师需要更多地考虑应用的语音交互规则如何才能更趋近于人们日常的沟通习惯,并进一步为人们的社会习俗所接纳。


真实场景下的多人音源:在现实情境中, 在多人对话场景下将面临音源不清、穿插停顿、噪音过多等影响体验的情况,由于计算机听觉分析能力开始从单人音源拓宽到了多人音源,多人对话解决方案上还有很大想象空间。


改变传统的视听体验:在使用场景上,语音交互接口也将逐渐运用到更多的媒介上,更全面地刷新用户体验。



小结

人类拥有双手、眼睛、耳朵和发声的嘴巴,但是并不总是在每个使用场景下都能自如地使用:在安静的自习室下声音收到限制,在驾驶场景下注意力受到限制,在双手拎着东西场景下双手受到限制......但目前许多产品设计都建立在用户能完整使用感官功能这一理想化的基础上。


未来的发展趋势倾向于将视、听、触、嗅等多通道信息完美整合起来,综合使用多种输入通道和输出通道,根据用户使用场景用最恰当的方式传递服务,满足用户多方位的需求。




尽管乔布斯曾断言3.5英寸是手机的黄金尺寸,但作为人们日常内容消费与娱乐的窗口,手机屏幕毫无疑问地变得越来越大,甚至超出传统物理限制。人们对大屏享受的追求与设备携带便捷性之间的矛盾由来已久,硬件形态的变化对旧有的用户体验设计思路带来的新的挑战。


3-1

——————————

大屏幕:单手持握新挑战

屏幕横纵比越来越大,而人类的手部具有先天限制,曾经惯用的界面布局方式在高横纵比的屏幕上可能无法被大拇指无障碍全覆盖,使得越来越多的设计更加重视利用移动屏幕下半部分。


操作与信息进一步下移


高德地图、苹果地图的搜索框下移,方便单手操作用户快速激活输入框;


影视资讯平台IMDB强化底部标签栏功能,双击“搜索”tab即可激活输入框,无须艰难地触摸顶部。


即时战斗类手游皇室战争的说明卡片主要展示在下半部分,方便用户进行卡片上的相关操作。



底部导航被赋予更多能力

Pocket的底部标签栏现在兼任汉堡菜单功能,在激活状态下再次点击主页icon可选择主页上须展示的内容。


利用下滑手势代替点击

Snapchat的许多表示前后进退关系的页面都不是”返回“按钮,而是向下箭头,用户可下滑退出当前页面。







3-2

——————————

折叠屏:新形态的交互方式

为了解决设备形态和人类手部先天限制之间的矛盾,折叠屏诞生浏览并颠覆旧有的界面设计方式。


更灵活的信息布局


过去在单屏设计下,考虑到用户注意力由上到下纵向衰减,因此信息布局更多是按照优先级从上往下排序。而折叠屏中,屏幕展开后便可以开辟出更大的可利用空间,将次级页面或者较为重要的内容曝光在第二屏,对信息的布局将带来全新的变化。设计师为保证大小屏下顺畅的阅读体验,需要对信息模块在不同空间布局下的流动性有更强的把控能力。


更便捷的多任务操作


在过去的单屏体验中,用户只能将注意力完全集中在当前的界面中,一次只做一件事。但在实际生活中,用户面临的情景往往是主线任务和支线任务的频繁交错,并且根据会任务不同的性质自由调动自己的注意力重心,如边看视频边聊天、边看直播边逛街等等。在折叠屏中,设计师可以探索更多主线和支线交错进行的场景,利用折叠屏带来的更大的屏幕空间,可以让用户在不离开主线场景的基础上进行支线任务的处理,大大节约了在不同App上来回切换的操作成本。



更直观的拖拽交互


此外,随着多任务处理越来越广泛使用,拖拽交互将成为重要的交互模式之一。文本、表情包、图片、视频等交互对象,不再需要经过复杂的分享转发流程才能在不同App中流转,通过拖拽的方式可以更直观地进行交互。




双面屏互动玩法


外折叠屏在折叠状态下可转为双面屏,等于是给用户增加多一个观看视角。例如华为 Mate X 的镜像拍摄可以让被拍摄者即时获知自己的镜头影像是否满意,这一拍女友神器有望成为直男拍摄终结者。在未来更多的多人观看和互动玩法将被开拓出来。


 华为Mate X 的镜像拍摄


未来随着5G通讯技术的成长,越来越多的设备可以同时加入物联网,人们的生活将被各种智能设备围绕,设计师需要参与更多屏幕外的设计,让不同设备串联在一起协同合作,让用户能更加自在地享受科技的便利。



席卷全球的新冠疫情让数十亿用户乖乖待在家里。过去需要花费大量精力去教育的用户使用习惯因为疫情纷纷转变。云购物、云蹦迪、云赏樱、云监工......人们足不出户便可还原许多线下场景。随着用户线上和线下生活的界限进一步模糊,用户对于应用的效率和情感诉求也发生了变化。



4-1

——————————

更关注效率导向

疫情让远程办公学习需求剧增,多人协作场景越来越频繁,许多企业随之升级了电话、视频会议、文档制作等多人协作效率软件。过去仅仅考虑少人场景协作的方式不适用,设计师需要比以往更多地考虑多人协作场景下,如何对海量密集的信息进行分析处理和展示。


在学习方式上,由于线下学习转移至线上,学生群体对于娱乐向软件也有了效率诉求。为了顺应用户诉求变化,2020年5月QQ推出学习模式,屏蔽娱乐性的内容推送,让学生更专注在学习上。



4-2

————————————

更注重缓解社交疏离感


除了效率诉求急剧提升以外,随着长时间的线上学习与办公所产生社交疏离感和缺失感,人们对于线上学习工作的情感化诉求也进一步增强。


2020年推出的plagi远程办公软件支持设置每个人的avartar形象,让大家在远程办公时依旧能时刻感受到彼此的存在。在完成任务时还可以放鞭炮庆祝,让员工能感受到亲密无间的线上办公体验。


设计师需要更加关注如何让线上生活进一步与现实生活圈和时间线接轨,通过拓展真实社交下的更多伴生行为让线上也能还原线下的真实场景细节和互动体验,以弥补用户对真实社交的缺失感。



4-3

——————————

加速人和信息的强连接


疫情的发生加速了人与信息之间的连接。人们越来越习惯将自身的身体资料、心情状态等信息沉淀在智能设备上。


为了做好广大市民群众的健康监测服务,辅助疫情防控工作,微信和支付宝在2020年年初都上线了健康码服务,不同颜色的健康码代表人们不同的健康情况,市民出入特定场所都需初始健康码。



随着人的数据化越来越深入,个人身份信息的线上化在各平台上将成为更加通用的能力。设计师需要考虑如何更自然更低成本地将线下动态变化的资料信息线上化,更有效地对用户信息进行加工处理,以及记忆用户的使用习惯和行为,以便帮助用户更地完成任务。


疫情的出现加速了线下生活线上化,短短时间内我们看到日常习以为常的应用为响应疫情下的特殊需求纷纷出现改造,钉钉、QQ群被改造成上网课、批改作业的地方,医疗卫生公众号开辟了实时疫情播报与辟谣通道,无接触设计和服务需求异常突出......这也启发了设计师需要保持对突发事件的敏感力以及应急能力,在日常生活中留心思考,为日后突发事件提供充足的场景支撑。




在汹涌的资本语境下,互联网设计师裹挟在商业驱动的结果导向中狂奔,对设计的伦理和责任鲜有发声,但伴随着互联网红利退潮,发展放缓,狂奔之下的人本问题也逐渐浮出水面。在大趋势下,UX设计师需要培养自身设计对伦理和责任的敏感度,在满足商业目的外,重拾节操,为多群体,为大社会设计,更加注重“以人为本”。




5-1

——————————

更包容性的设计

包容性设计师指在做设计产品的时候,考虑到各类用户的诉求,输出具有包容性的设计方案。包容性设计依旧是2020年设计主题之一,伴随着互联网产品全球化,在通用性和包容性上也提出了新的要求。




为身障人士设计


三星在2019年针对东南亚市场推出了一款让聋盲人士和健全人实时交流的app:Good Vibes,盲聋人轻击屏幕输入摩斯电码,预先连线好的另一台手机就会显示从盲聋人发来的短信。健全人用普通的文字输入回复,在盲聋人这一端就会翻译成摩斯电码、以手机振动的方式读出短信内容。

   GOOD VIBES宣传视频


饿了么:在饿了么送货骑手中,约8%受色盲色弱的困扰(全国男性群体中红绿色盲色弱占比达8%-9%,饿了么骑手男性占比90%),为此饿了么设计团队在2019年对app的进行了重新设计,包括使用WCAG无障碍色彩对比度,以及无障碍色盘,以及调整字阶,使用辅助图形等设计手段来解决部分骑手在送货途中使用APP的痛点问题。


饿了么UED:《为骑士创造平等 — 配送 App 的包容性设计》



跨年龄段设计


谷歌助手礼貌功能 ( Google Pretty Please ) :开启谷歌助手礼貌功能后,如果使用者在下达指令的语句中包括“Please”,谷歌助手会对礼貌的请求表示感谢,以此培养孩子的礼貌言行。

Google Pretty Please功能宣传



Swift Playground:当10后小学生VITA君的编程课被“可敬的发量”刷满弹幕时,Swift playgrounds功不可没,这款为儿童新手学习编程的软件,用趣味的游戏方式为4岁以上低龄用户提供了一个学习编程的低门槛平台。



为性别平等而设计


苹果emoji回看历年苹果emoji的更新,从肤色平等,到性别、性向平等,再到为残疾人设计,2020年再为跨性别者增加新表情,性别平等依旧是包容性设计中重要一环。



Airbnb插画:爱彼迎在插画系统中,也为不同肤色,不同职业,不同性别,以及身障人士进行了人物的绘制。






5-2

——————————

关注用户隐私

2019是互联网科技隐私问题沉浮的一年,国外有Facebook因泄露隐私收到史上最大罚单,国内则打响了“人脸识别第一案”。笼罩在隐私信任危机下,个人信息和数据立法突飞猛进,美国推动《加州消费者隐私法案》,国内也将在2020年出台《个人信息保护法》和《数据安全法》。


MIUI12推出隐匿面具功能


Android开放生态导致的权限隐私问题一直被用户所诟病,某些APP存在用户不授权就无法使用情况,针对这一情况,MIUI12推出了隐匿面具功能。当用户在开启某些APP要求授权权限时,可以选择空白通行证进行授权,从而保护用户真实信息。


   在MIUI12的更新中,还推出了照明弹、拦截网两项隐私保护功能


iOS 14剪贴板提醒


在iOS 14的更新中,保护用户隐私方面进一步升级。

其中剪贴板提醒设计很贴心,当用户打开应用,如果该应用读取了你剪贴板的内容,会在系统顶部弹出提示,用户能在第一时间意识到剪贴板内容被读取,帮助用户更好的保护自己的隐私内容。




5-3

——————————

健康的数码生活方式


科技的发展是一把双刃剑,互联网产品的发展给用户带来便捷和沉浸体验的同时,也使得用户沉溺于科技所带来的惰性和投食之下,逐渐丧失了对真实生活的把控权,被科技绑架。


数字福祉(digital wellbeing)近年被频频提起,指科技产品需要权衡好数码产品和真实生活之间的平衡,防止数码产品过渡分散用户的注意力而影响生活质量。


Android Q 专注模式  Google Android Q Focus Mode


Android Q的更新加入了专注模式,用户在专注模式下,可以在系统层面快捷地关闭使你分心的应用,让你聚焦于更重要的事情。



防沉迷系统升级


推荐技术的进步,产品体验的升级,给用户带来了更合胃口的菜式和沉浸体验,但同时也被冠上了“电子海洛因”的称号。游戏或者内容产品的防沉迷系统依旧会是数字福祉下不可避免的趋势。


王者荣耀在2020年升级防沉迷系统,对青少年的娱乐时间和点券充值的限制进行了进一步升级。承接话。B站在2019年推出青少年模式,在该模式下,使用时长和内容推荐等做了定制化处理。






2020年的UI设计趋势,一方面是对往年风格的衍变和细化,另一方面,在扁平克制的界面风格盛行后,设计师们向往更自由、更突破的视觉表达。



6-1

——————————

深色模式


2019年iOS 13深色模式姗姗来迟,紧接着大厂APP相继推出此功能。在2020年,深色模式会继续普及外,也会在可视性和实现成本方面有更多细节打磨和研究。





6-2

——————————

新拟态

设计趋势的发展是螺旋式上升的,在扁平化设计流行之后,对物体的拟真再一次回归设计圈,新拟态以一种对旧拟物风格的再创新,重新流行起来。


新拟物风格(Neumorphism)缘起于设计师Alexander Plyuto发布在dribbble的一组作品,以投影重新对扁平界面进行了塑造,模仿出类似浮雕的视觉效果,感受耳目一新,引起大量设计师相尽模仿。


新拟态的实用性和可落地性有待商榷,但是作为一种新的风格受到设计师拥趸,也不失为下一波风潮到来前的灵感缪斯。



WWDC2020对mac OS的更新也重新定义了新拟态设计语言,在mac OS新系统Big Sur中,图标的设计增添了轻微的渐变、投影、高光,以此来营造图标内元素之间的纵深关系。




6-3

——————————

多彩配色


在扁平简洁UI风格盛行之后,丰富的色彩依旧是设计趋势之一,大面积色块,碰撞配色,带来更具冲击感的视觉体验。





6-4

——————————

字体装饰化


UI界面逐渐扁平,色块图标弱化,为突出页面重心和内容,iOS 11在界面标题上使用更大的字号,更粗的字重。近年在大标题的风格衍变下,文字在传达信息外,也开始有了装饰性作用,采用超大字体,成为页面排版美化的一部分。





6-5

————————

更大圆角

大圆角的风格会继续延续,相较以往,卡片的处理圆角会更大,随之带来的是多的留白处理,结合大字号,带来更透气通透的视觉感受。



Mac OS Big Sur的界面相对旧版本采用了更大的圆角;系统图标的设计统一成圆角矩形。





6-6

——————————

更丰富的插图


UI插图的丰富体现在样式和内容上,样式上开始3D化,内容上更注重插图叙事的表达。


3D插图


3D图形往年更多运用在动态影像或运营类设计中,随着3D的普及运用,UI插图也会迎来3D化,给用户带来更立体,更新鲜的视觉感受。




讲求叙事表意


相较于往年追求形式的UI插图,新趋势下的插图更讲求功能性,每一副插图都承载一定的作用——传达功能信息或透传品牌情感;同时插图更讲求画面表意和情节,给用户叙事性的视觉体验,增进用户和产品之间的情感联系。




插图组件化


插画的流行,随之而来的是成本的水涨船高——一套系列插图为保持风格统一,往往由唯一设计师绘制,同时为兼容各类场景,设计师往往要绘制多张。


为解决插图的成本和效率,插图开始以组件化的方式进行绘制——插图设计师将插画进行拆分绘制——不同人物,不同场景,不同物件等,再通过组件化的拼接合成,使用组件的设计师可以根据需求场景自由组合,也避免了风格不统一问题。


设计师Pablo Stanley将日常绘制的插画制成一套矢量插图组件库,将人物分为:半身、全身和坐姿3大类。通过不同表情、发型和服装可自由搭配出近60万种组合。


Pablo Stanley人物插画系统




6-7

————————

多维度动画表现


新趋势下,动画一方面回溯复古线描手绘风格,另一方面追求更三维的体验,同时帧率进一步提升,追求更流畅的视觉感受。


手绘动画


手绘插图是往年的热门,其随性自然的笔触,能给用户带来亲切的感受,在新的趋势下,动画的加入赋予手绘插图一份灵性和趣味。


3D运动


Google Material Design通过卡片投影层级和二维动画规律,赋予扁平界面Z轴的纵深感。随着3D的普及流行,新趋势下的界面,界面的运动从二维走向三维,表现出3D场景下透视感。



高帧率动画


高帧率影视从线下电影院移步到线上流媒体,手机高帧率屏幕从90Hz到120Hz逐步升级,用户对画面流畅的定义一再刷新,UI动画的帧率升级也会是新的一轮趋势。


Telegram的表情采用了高帧率动画,给用户更流畅的视觉感受。








体验的持续升级,产品的高速迭代,对UX设计师的设计师效率提出了更高的要求。的设计方式是一个永恒的趋势。




7-1

——————————

从本地文件到云端协作


传统的文件交接方式效率低下,导致设计师之间信息不对称,最终影响产品的一致性体验。近些年在线设计协同工具发展迅速,从UI设计、 设计交付以及组件协同等环节上给设计师提供更加实时的协作体验,获得大量UX设计师的簇拥。在2019 uxtool的设计工具调研中,在线设计协同工具佼佼者figma以其协作和性能优势,大有追赶sketch之势。


随着团队对设计效率要求的提高,设计文档从本地走向云端协作是不可逆趋势。不过设计工具的迭代是需要成本的,尤其在大型设计团队,设计工具需要渡过阵痛期来完成迭代,进而提升设计效率和体验一致性。



7-2

——————————

科学有效的设计系统


UX的发展,从早期的静态规范到当下的动态设计系统,是为解决产品迭代增速后带来的设计效率和产品体验问题。商业驱动下的产品迭代速度有增无减,设计系统依旧会是未来几年的设计趋势之一。


这里说的设计系统不是广义上的设计系统,而是在互联网设计的发展中,对组件化设计逐步迭代升华的一套设计协作方法:


“设计系统(Design systems)是一组为了共同目标而服务的内在相互联系的设计模式和多人协同执行的方法。”(引自《Design systems》,Alla Kholmatova,C7210翻译)。


设计系统历程衍变


组件化的发展历经规范文档到UI组件,再到设计系统,形态从最初对设计一致性的指导规范,到对产品研发流程的规范,以及产品设计价值观的输出,当下的设计系统以集大成者形式影响整个产品的设计形态。


设计系统的结构见下图:



设计系统的求同存异


设计系统并非一成不变的,他是一个动态进化的系统,会根据团队性质、产品特性在内容上有所区分——比如大团队更应该大而全,小团队更倾向小而精;成熟产品的设计系统更倾向于打造完整闭环的合作流程机制,新产品的设计系统应该以小为始,快速迭代……


随着产品的垂直化,细分化,设计系统的趋势会是在趋势大同之下找到适合产品和团队自身的形态和节奏。


Material Design是一个包含了指导规范、组件,以及设计开发工具的自适应性设计系统。


它作为平台型性设计系统,更为大而全的规范了整个生态系统的设计风格,以及提供工具让研发者能快速产出符合规范的产品。


  Google生态庞大繁杂,Material Design更为全面


Ant Design作为一个为to B产品提供解决方案的平台,更多从设计可用性和完整性考虑设计系统的搭建。


Ant Design通过模块化解决方案,降低冗余的生产成本,让设计者专注于更好的用户体验


QQ作为一款面向95后的2C社交产品,其设计系统Q语言从风格调性上对设计进行规范,同时给予设计师一定的自由度;也考虑到QQ内兼顾多个产品,以及界面主题样式,对基础组件的使用场景和代码进行了规范,方便设计和开发敏捷开发。


Q语言,给予产品的自由调性之外,也针对主题和基础组件进行了规范


每个产品和团队都有自身的特征,设计系统的建设也应该有的放矢,没有可照搬的标准答案,在大方向下找到适合自身的解决方案才是的可行之道,将效率最大化。


科学有效的优化迭代


组件是设计系统中的重要组成部分,但是以往静态的、孤立的协作方式使得组件的更新迭代滞后和阻塞。随着设计系统的发展,设计师组件化思维的普及,组件的更新需要更科学的方式进行管理。


Figma在2019年推出的Design System Analytics功能,组件设计师可以借此查看组件的使用情况,包括引用次数,解组次数等,并可以生成组件使用情况的曲线趋势图,以数据的形式,科学地推动组件的优化迭代。


 1.选择分析的时间段;2.组件使用的次数曲线图;3.团队使用情况;4.所有组件使用情况



后记

未来的用户体验会出现什么新趋势?人工智能等算法的发展、5G技术普及、新的智能设备形态、新的信息处理技术、新一代用户的喜好和口味......这些往后或将影响用户体验发展的走向。未来用户对体验的要求只会越来越高。


用户体验设计师需要了解更多的技术动向,但安身立命之本还是让用户真正受益:立足于用户真实使用场景,在理性价值层面上,打造可用、易用、的设计;在感性需求层上赋予情感上的愉悦性,在反思层面赋予意义价值。




文章来源:站酷    作者:百度MEUX

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



UX TRENDS | 注重隐私的用户体验设计

分享达人

用户隐私安全在产品设计中是很重要的一个环节,本文从用户体验角度切入,从匿名模式、减少永久性和减少公开性三个方面展开分析。



我们先看⼀组来⾃⽪优的2019年6⽉的调研数据:70%的美国⼈认为,与5年前相⽐个⼈信息变得更不安全。尤其是⾼学历⾼收⼊群体。由此可⻅⽤户对个⼈信息数据的隐私担忧⽐以往更甚。



⽤户隐私安全很重要,涉及的范围和⻆度也很多。本次的分析从⽤户体验⻆度切⼊,涉及如下三个⽅⾯:

  1. Incognito Mode匿名模式;

  2. Reducing Permanence减少永久性;

  3. Reducing Publicity减少公开性。



01 匿名模式


下图是Google系App(Google AppChromeYouTubeandGoogle Map)匿名模式切换,从交互体验上来说有如下⼏个特点:

  1. 统⼀在右上⻆;

  2. 可以便捷切换⾄匿名模式,反过来也很容易切回登陆状态;

  3. 匿名模式的状态提示,例如YouTube 在匿名模式下在界⾯底部有⼩字提示“您 当前处于匿名模式”。


匿名模式不是最近才流⾏的功能,最早提供隐匿模式的是苹果safari浏览器,早在 2005年就⽀持了匿名模式。Chrome浏览器在2008年就开始⽀持此模式。虽然由来已久,为什么到了2020年,匿名模式依然是国外互联⽹⼀⼤趋势呢?


我们看⼀组数据:

这是来⾃DuckDuckGo 2019年9⽉的调研(DuckDuckGo是美国的⼀款不记录⽤户⾏为保护⽤户隐私的搜索引擎)。样本来⾃美国、英国、德国和澳⼤利亚的成年⼈⽤户,共计3,411⼈的调研得出。各国⽤户对使⽤搜索引擎的个⼈隐私安全⾮常在意(是否搜集个⼈的数据和记录搜索⾏为)。

2020年5⽉DuckDuckGo⽇均搜索次数为6200万。对⽐看2019年11⽉底⽇均搜索次数4900万,2018年10⽉是2900万。

最近⼏年的持续活跃度⾼幅增⻓证明了不记录个⼈隐私的搜素引擎越来越受到⽤户的⻘睐。

国内,头条、UC浏览器在搜索框输⼊状态也提供了“⽆痕浏览”⼊⼝。

不仅是搜索引擎领域,保护⽤户隐私也成为Facebook最重要的战略⽅向之⼀。Facebook CEO Mark Zuckerberg在2019年 F8开发者⼤会上喊出“THE FUTURE IS PRIVATE”。2019年3⽉Mark Zuckerberg发⽂,主题就是《聚焦于保护隐私的社交⽹络》。



02 减少永久性


我们先看国外社交媒体Stories(⼩故事)产品形态的流⾏。

⼈们总是对于所分享的内容永远记录在⽹上感到担忧。Stories24⼩时消失缓解了⼈们的隐私顾虑,这让⽤户更安⼼地⾃然分享。

Stories由Snapchat⾸创,由 Facebook发扬光⼤。早在2019年4⽉,Facebook+Messenger Stories, Instagram Stories⽇活⽤户数就突破5亿。 2020年2-3⽉LinkedIn,Twitter也先后宣布将上线类似功能。



03 减少公开性


来⾃⽪优的调研报告:41%的美国⼈经历过⽹络骚扰,最常⻅的就是在社交媒体上。23%的⽤户最近经历的⽹络骚扰来⾃评论区的评论内容。27%的⽤户经历⽹络骚扰后决定不再发布任何内容。

我们以限定评论互动的公开性为例:

2020年5⽉Twitter上线了新的评论功能,可以限定谁可以回复帖⼦的功能,提供了三种选项:谁都可以评论,只有被关注者可以评论,只有被提及者可以评论三种公开度的限定。

Instagram也在测试“评论限制”新功能,批量屏蔽/限制评论。⽬前已经上线的⼀个例⼦:⽤户(评论发布者)如果发布的评论含有攻击性敏感词,发布前伴有提示,提醒评论含有攻击性敏感词是否真的要发布。


注重隐私提供仅好友可⻅/仅⾃⼰可⻅/仅作者可⻅/等多重维度的隐私设定有助于⽤户更安⼼地参与互动。

另外⼀个例⼦是付费频道会员:付费频道会员-限定频道的公开性让内容创作者减轻隐私顾虑不仅能获得⼴告收⼊,也能得到来⾃会员、会费的收⼊,形成“忠实粉丝”社区,有助于内容⽣态的社区化建设。

我们主要看YouTube的频道会员案例:

YouTube有两种会员模式。⼀种是YouTube整个平台的付费会员,去⼴告,看原创美剧影视,消费⾳乐,可下载内容的模式。第⼆种模式是Youtuber个⼈频道付费会员,吸引忠实粉丝加⼊。我想说的就是第⼆种。


为什么⼤V⽹红有意愿开通频道会员?


除了获得忠实粉丝收⼊变现的商业价值以及付费频道会员可以为忠实粉丝提供各种专属功能,背后也和⽹红⼤V对个⼈隐私顾虑有关。

⽹红⼤V在完全公开的社交⽹络上需要始终保持⾜够克制谨慎,避免引起争议。但在忠实粉丝付费频道专属会员群中,⽹红⼤V会减轻隐私顾虑,更加回归⾃我。

⽐如在频道会员中发布更多与个⼈⽣活相关的内容,表达更多不便在完全公开的社交⽹络中的想法和感受等,因为忠实粉丝通常更具包容度,更不容易引起争议。


YouTube频道会员费⽤可以从三种会费(按⽉)区间选择,⽀持多选:

  1. 低阶 Low Levels $0.99~3.99;

  2. 中阶 Medium Levels $4.99~14.99;

  3. ⾼阶 High Levels $19.99~49.99;

频道会员功能在2018年开始测试,⾯向粉丝数过10万的YouTuber开放。



以上综述,我们可以说:


1.匿名模式:

虽然匿名模式由来已久,但仍然是当前的⼀⼤基本⽤户体验设计趋势,尤其是匿名模式的切换便捷性⾮常重要。


2.减少永久性:

Stories⼩故事24⼩时消失缓解了⼈们的隐私顾虑,这让⽤户更安⼼地⾃然分享,已经成为国外社交媒体平台的必备功能,Facebook, Instagram平台的最主要、最具影响⼒的功能之⼀。


3.减少公开性:

⽤户总是对在社交媒体平台发表评论有所顾忌,限定评论的公开性能够有助于促进⽤户发帖表达,其他⽤户也可以更安⼼地参与互动。


付费频道会员可以限定频道的公开性,让内容创作者减轻隐私顾虑不仅能获得⼴告收⼊,也能得到来⾃会员会费的收⼊,形成“忠实粉丝”社区,有助于内容⽣态的社区化建设。


从UE⻆度,我们可以为频道会员提供专属身份设计例如专属徽章,专属表情等。

THE FUTURE IS PRIVATE, 注重⽤户隐私的体验设计越来越重要!

文章来源:站酷    作者:百度MEUX

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



VUE-多文件断点续传、秒传、分片上传

seo达人

凡是要知其然知其所以然

文件上传相信很多朋友都有遇到过,那或许你也遇到过当上传大文件时,上传时间较长,且经常失败的困扰,并且失败后,又得重新上传很是烦人。那我们先了解下失败的原因吧!


据我了解大概有以下原因:


服务器配置:例如在PHP中默认的文件上传大小为8M【post_max_size = 8m】,若你在一个请求体中放入8M以上的内容时,便会出现异常

请求超时:当你设置了接口的超时时间为10s,那么上传大文件时,一个接口响应时间超过10s,那么便会被Faild掉。

网络波动:这个就属于不可控因素,也是较常见的问题。

基于以上原因,聪明的人们就想到了,将文件拆分多个小文件,依次上传,不就解决以上1,2问题嘛,这便是分片上传。 网络波动这个实在不可控,也许一阵大风刮来,就断网了呢。那这样好了,既然断网无法控制,那我可以控制只上传已经上传的文件内容,不就好了,这样大大加快了重新上传的速度。所以便有了“断点续传”一说。此时,人群中有人插了一嘴,有些文件我已经上传一遍了,为啥还要在上传,能不能不浪费我流量和时间。喔...这个嘛,简单,每次上传时判断下是否存在这个文件,若存在就不重新上传便可,于是又有了“秒传”一说。从此这"三兄弟" 便自行CP,统治了整个文件界。”

注意文中的代码并非实际代码,请移步至github查看代码

https://github.com/pseudo-god...


分片上传

HTML

原生INPUT样式较丑,这里通过样式叠加的方式,放一个Button.

 <div class="btns">

   <el-button-group>

     <el-button :disabled="changeDisabled">

       <i class="el-icon-upload2 el-icon--left" size="mini"></i>选择文件

       <input

         v-if="!changeDisabled"

         type="file"

         :multiple="multiple"

         class="select-file-input"

         :accept="accept"

         @change="handleFileChange"

       />

     </el-button>

     <el-button :disabled="uploadDisabled" @click="handleUpload()"><i class="el-icon-upload el-icon--left" size="mini"></i>上传</el-button>

     <el-button :disabled="pauseDisabled" @click="handlePause"><i class="el-icon-video-pause el-icon--left" size="mini"></i>暂停</el-button>

     <el-button :disabled="resumeDisabled" @click="handleResume"><i class="el-icon-video-play el-icon--left" size="mini"></i>恢复</el-button>

     <el-button :disabled="clearDisabled" @click="clearFiles"><i class="el-icon-video-play el-icon--left" size="mini"></i>清空</el-button>

   </el-button-group>

   <slot

   

//data 数据


var chunkSize = 10 * 1024 * 1024; // 切片大小

var fileIndex = 0; // 当前正在被遍历的文件下标


data: () => ({

   container: {

     files: null

   },

   tempFilesArr: [], // 存储files信息

   cancels: [], // 存储要取消的请求

   tempThreads: 3,

   // 默认状态

   status: Status.wait

 }),

   

一个稍微好看的UI就出来了。




选择文件

选择文件过程中,需要对外暴露出几个钩子,熟悉elementUi的同学应该很眼熟,这几个钩子基本与其一致。onExceed:文件超出个数限制时的钩子、beforeUpload:文件上传之前

fileIndex 这个很重要,因为是多文件上传,所以定位当前正在被上传的文件就很重要,基本都靠它


handleFileChange(e) {

 const files = e.target.files;

 if (!files) return;

 Object.assign(this.$data, this.$options.data()); // 重置data所有数据


 fileIndex = 0; // 重置文件下标

 this.container.files = files;

 // 判断文件选择的个数

 if (this.limit && this.container.files.length > this.limit) {

   this.onExceed && this.onExceed(files);

   return;

 }


 // 因filelist不可编辑,故拷贝filelist 对象

 var index = 0; // 所选文件的下标,主要用于剔除文件后,原文件list与临时文件list不对应的情况

 for (const key in this.container.files) {

   if (this.container.files.hasOwnProperty(key)) {

     const file = this.container.files[key];


     if (this.beforeUpload) {

       const before = this.beforeUpload(file);

       if (before) {

         this.pushTempFile(file, index);

       }

     }


     if (!this.beforeUpload) {

       this.pushTempFile(file, index);

     }


     index++;

   }

 }

},

// 存入 tempFilesArr,为了上面的钩子,所以将代码做了拆分

pushTempFile(file, index) {

 // 额外的初始值

 const obj = {

   status: fileStatus.wait,

   chunkList: [],

   uploadProgress: 0,

   hashProgress: 0,

   index

 };

 for (const k in file) {

   obj[k] = file[k];

 }

 console.log('pushTempFile -> obj', obj);

 this.tempFilesArr.push(obj);

}

分片上传

创建切片,循环分解文件即可


 createFileChunk(file, size = chunkSize) {

   const fileChunkList = [];

   var count = 0;

   while (count < file.size) {

     fileChunkList.push({

       file: file.slice(count, count + size)

     });

     count += size;

   }

   return fileChunkList;

 }

循环创建切片,既然咱们做的是多文件,所以这里就有循环去处理,依次创建文件切片,及切片的上传。

async handleUpload(resume) {

 if (!this.container.files) return;

 this.status = Status.uploading;

 const filesArr = this.container.files;

 var tempFilesArr = this.tempFilesArr;


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

   fileIndex = i;

   //创建切片

   const fileChunkList = this.createFileChunk(

     filesArr[tempFilesArr[i].index]

   );

     

   tempFilesArr[i].fileHash ='xxxx'; // 先不用看这个,后面会讲,占个位置

   tempFilesArr[i].chunkList = fileChunkList.map(({ file }, index) => ({

     fileHash: tempFilesArr[i].hash,

     fileName: tempFilesArr[i].name,

     index,

     hash: tempFilesArr[i].hash + '-' + index,

     chunk: file,

     size: file.size,

     uploaded: false,

     progress: 0, // 每个块的上传进度

     status: 'wait' // 上传状态,用作进度状态显示

   }));

   

   //上传切片

   await this.uploadChunks(this.tempFilesArr[i]);

 }

}

上传切片,这个里需要考虑的问题较多,也算是核心吧,uploadChunks方法只负责构造传递给后端的数据,核心上传功能放到sendRequest方法中

async uploadChunks(data) {

 var chunkData = data.chunkList;

 const requestDataList = chunkData

   .map(({ fileHash, chunk, fileName, index }) => {

     const formData = new FormData();

     formData.append('md5', fileHash);

     formData.append('file', chunk);

     formData.append('fileName', index); // 文件名使用切片的下标

     return { formData, index, fileName };

   });


 try {

   await this.sendRequest(requestDataList, chunkData);

 } catch (error) {

   // 上传有被reject的

   this.$message.error('亲 上传失败了,考虑重试下呦' + error);

   return;

 }


 // 合并切片

 const isUpload = chunkData.some(item => item.uploaded === false);

 console.log('created -> isUpload', isUpload);

 if (isUpload) {

   alert('存在失败的切片');

 } else {

   // 执行合并

   await this.mergeRequest(data);

 }

}

sendReques。上传这是最重要的地方,也是容易失败的地方,假设有10个分片,那我们若是直接发10个请求的话,很容易达到浏览器的瓶颈,所以需要对请求进行并发处理。


并发处理:这里我使用for循环控制并发的初始并发数,然后在 handler 函数里调用自己,这样就控制了并发。在handler中,通过数组API.shift模拟队列的效果,来上传切片。

重试: retryArr 数组存储每个切片文件请求的重试次数,做累加。比如[1,0,2],就是第0个文件切片报错1次,第2个报错2次。为保证能与文件做对应,const index = formInfo.index; 我们直接从数据中拿之前定义好的index。 若失败后,将失败的请求重新加入队列即可。


关于并发及重试我写了一个小Demo,若不理解可以自己在研究下,文件地址:https://github.com/pseudo-god... , 重试代码好像被我弄丢了,大家要是有需求,我再补吧!

   // 并发处理

sendRequest(forms, chunkData) {

 var finished = 0;

 const total = forms.length;

 const that = this;

 const retryArr = []; // 数组存储每个文件hash请求的重试次数,做累加 比如[1,0,2],就是第0个文件切片报错1次,第2个报错2次


 return new Promise((resolve, reject) => {

   const handler = () => {

     if (forms.length) {

       // 出栈

       const formInfo = forms.shift();


       const formData = formInfo.formData;

       const index = formInfo.index;

       

       instance.post('fileChunk', formData, {

         onUploadProgress: that.createProgresshandler(chunkData[index]),

         cancelToken: new CancelToken(c => this.cancels.push(c)),

         timeout: 0

       }).then(res => {

         console.log('handler -> res', res);

         // 更改状态

         chunkData[index].uploaded = true;

         chunkData[index].status = 'success';

         

         finished++;

         handler();

       })

         .catch(e => {

           // 若暂停,则禁止重试

           if (this.status === Status.pause) return;

           if (typeof retryArr[index] !== 'number') {

             retryArr[index] = 0;

           }


           // 更新状态

           chunkData[index].status = 'warning';


           // 累加错误次数

           retryArr[index]++;


           // 重试3次

           if (retryArr[index] >= this.chunkRetry) {

             return reject('重试失败', retryArr);

           }


           this.tempThreads++; // 释放当前占用的通道


           // 将失败的重新加入队列

           forms.push(formInfo);

           handler();

         });

     }


     if (finished >= total) {

       resolve('done');

     }

   };


   // 控制并发

   for (let i = 0; i < this.tempThreads; i++) {

     handler();

   }

 });

}

切片的上传进度,通过axios的onUploadProgress事件,结合createProgresshandler方法进行维护

// 切片上传进度

createProgresshandler(item) {

 return p => {

   item.progress = parseInt(String((p.loaded / p.total) * 100));

   this.fileProgress();

 };

}

Hash计算

其实就是算一个文件的MD5值,MD5在整个项目中用到的地方也就几点。

秒传,需要通过MD5值判断文件是否已存在。

续传:需要用到MD5作为key值,当唯一值使用。

本项目主要使用worker处理,性能及速度都会有很大提升.

由于是多文件,所以HASH的计算进度也要体现在每个文件上,所以这里使用全局变量fileIndex来定位当前正在被上传的文件

执行计算hash


正在上传文件


// 生成文件 hash(web-worker)

calculateHash(fileChunkList) {

 return new Promise(resolve => {

   this.container.worker = new Worker('./hash.js');

   this.container.worker.postMessage({ fileChunkList });

   this.container.worker.onmessage = e => {

     const { percentage, hash } = e.data;

     if (this.tempFilesArr[fileIndex]) {

       this.tempFilesArr[fileIndex].hashProgress = Number(

         percentage.toFixed(0)

       );

     }


     if (hash) {

       resolve(hash);

     }

   };

 });

}

因使用worker,所以我们不能直接使用NPM包方式使用MD5。需要单独去下载spark-md5.js文件,并引入


//hash.js


self.importScripts("/spark-md5.min.js"); // 导入脚本

// 生成文件 hash

self.onmessage = e => {

 const { fileChunkList } = e.data;

 const spark = new self.SparkMD5.ArrayBuffer();

 let percentage = 0;

 let count = 0;

 const loadNext = index => {

   const reader = new FileReader();

   reader.readAsArrayBuffer(fileChunkList[index].file);

   reader.onload = e => {

     count++;

     spark.append(e.target.result);

     if (count === fileChunkList.length) {

       self.postMessage({

         percentage: 100,

         hash: spark.end()

       });

       self.close();

     } else {

       percentage += 100 / fileChunkList.length;

       self.postMessage({

         percentage

       });

       loadNext(count);

     }

   };

 };

 loadNext(0);

};

文件合并

当我们的切片全部上传完毕后,就需要进行文件的合并,这里我们只需要请求接口即可

mergeRequest(data) {

  const obj = {

    md5: data.fileHash,

    fileName: data.name,

    fileChunkNum: data.chunkList.length

  };


  instance.post('fileChunk/merge', obj,

    {

      timeout: 0

    })

    .then((res) => {

      this.$message.success('上传成功');

    });

}

Done: 至此一个分片上传的功能便已完成

断点续传

顾名思义,就是从那断的就从那开始,明确思路就很简单了。一般有2种方式,一种为服务器端返回,告知我从那开始,还有一种是浏览器端自行处理。2种方案各有优缺点。本项目使用第二种。

思路:已文件HASH为key值,每个切片上传成功后,记录下来便可。若需要续传时,直接跳过记录中已存在的便可。本项目将使用Localstorage进行存储,这里我已提前封装好addChunkStorage、getChunkStorage方法。


存储在Stroage的数据




缓存处理

在切片上传的axios成功回调中,存储已上传成功的切片


instance.post('fileChunk', formData, )

 .then(res => {

   // 存储已上传的切片下标

+ this.addChunkStorage(chunkData[index].fileHash, index);

   handler();

 })

在切片上传前,先看下localstorage中是否存在已上传的切片,并修改uploaded


   async handleUpload(resume) {

+      const getChunkStorage = this.getChunkStorage(tempFilesArr[i].hash);

     tempFilesArr[i].chunkList = fileChunkList.map(({ file }, index) => ({

+        uploaded: getChunkStorage && getChunkStorage.includes(index), // 标识:是否已完成上传

+        progress: getChunkStorage && getChunkStorage.includes(index) ? 100 : 0,

+        status: getChunkStorage && getChunkStorage.includes(index)? 'success'

+              : 'wait' // 上传状态,用作进度状态显示

     }));


   }

构造切片数据时,过滤掉uploaded为true的


async uploadChunks(data) {

 var chunkData = data.chunkList;

 const requestDataList = chunkData

+    .filter(({ uploaded }) => !uploaded)

   .map(({ fileHash, chunk, fileName, index }) => {

     const formData = new FormData();

     formData.append('md5', fileHash);

     formData.append('file', chunk);

     formData.append('fileName', index); // 文件名使用切片的下标

     return { formData, index, fileName };

   })

}

垃圾文件清理

随着上传文件的增多,相应的垃圾文件也会增多,比如有些时候上传一半就不再继续,或上传失败,碎片文件就会增多。解决方案我目前想了2种

前端在localstorage设置缓存时间,超过时间就发送请求通知后端清理碎片文件,同时前端也要清理缓存。

前后端都约定好,每个缓存从生成开始,只能存储12小时,12小时后自动清理

以上2中方案似乎都有点问题,极有可能造成前后端因时间差,引发切片上传异常的问题,后面想到合适的解决方案再来更新吧。

Done: 续传到这里也就完成了。


秒传

这算是最简单的,只是听起来很厉害的样子。原理:计算整个文件的HASH,在执行上传操作前,向服务端发送请求,传递MD5值,后端进行文件检索。若服务器中已存在该文件,便不进行后续的任何操作,上传也便直接结束。大家一看就明白

async handleUpload(resume) {

   if (!this.container.files) return;

   const filesArr = this.container.files;

   var tempFilesArr = this.tempFilesArr;


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

     const fileChunkList = this.createFileChunk(

       filesArr[tempFilesArr[i].index]

     );


     // hash校验,是否为秒传

+      tempFilesArr[i].hash = await this.calculateHash(fileChunkList);

+      const verifyRes = await this.verifyUpload(

+        tempFilesArr[i].name,

+        tempFilesArr[i].hash

+      );

+      if (verifyRes.data.presence) {

+       tempFilesArr[i].status = fileStatus.secondPass;

+       tempFilesArr[i].uploadProgress = 100;

+      } else {

       console.log('开始上传切片文件----》', tempFilesArr[i].name);

       await this.uploadChunks(this.tempFilesArr[i]);

     }

   }

 }

 // 文件上传之前的校验: 校验文件是否已存在

 verifyUpload(fileName, fileHash) {

   return new Promise(resolve => {

     const obj = {

       md5: fileHash,

       fileName,

       ...this.uploadArguments //传递其他参数

     };

     instance

       .post('fileChunk/presence', obj)

       .then(res => {

         resolve(res.data);

       })

       .catch(err => {

         console.log('verifyUpload -> err', err);

       });

   });

 }

Done: 秒传到这里也就完成了。

后端处理

文章好像有点长了,具体代码逻辑就先不贴了,除非有人留言要求,嘻嘻,有时间再更新

Node版

请前往 https://github.com/pseudo-god... 查看

JAVA版

下周应该会更新处理

PHP版

1年多没写PHP了,抽空我会慢慢补上来

待完善

切片的大小:这个后面会做出动态计算的。需要根据当前所上传文件的大小,自动计算合适的切片大小。避免出现切片过多的情况。

文件追加:目前上传文件过程中,不能继续选择文件加入队列。(这个没想好应该怎么处理。)

更新记录

组件已经运行一段时间了,期间也测试出几个问题,本来以为没BUG的,看起来BUG都挺严重

BUG-1:当同时上传多个内容相同但是文件名称不同的文件时,出现上传失败的问题。


预期结果:第一个上传成功后,后面相同的问文件应该直接秒传


实际结果:第一个上传成功后,其余相同的文件都失败,错误信息,块数不对。


原因:当第一个文件块上传完毕后,便立即进行了下一个文件的循环,导致无法及时获取文件是否已秒传的状态,从而导致失败。


解决方案:在当前文件分片上传完毕并且请求合并接口完毕后,再进行下一次循环。


将子方法都改为同步方式,mergeRequest 和 uploadChunks 方法





BUG-2: 当每次选择相同的文件并触发beforeUpload方法时,若第二次也选择了相同的文件,beforeUpload方法失效,从而导致整个流程失效。

原因:之前每次选择文件时,没有清空上次所选input文件的数据,相同数据的情况下,是不会触发input的change事件。


解决方案:每次点击input时,清空数据即可。我顺带优化了下其他的代码,具体看提交记录吧。


<input

 v-if="!changeDisabled"

 type="file"

 :multiple="multiple"

 class="select-file-input"

 :accept="accept"

+  οnclick="f.outerHTML=f.outerHTML"

 @change="handleFileChange"/>

重写了暂停和恢复的功能,实际上,主要是增加了暂停和恢复的状态





之前的处理逻辑太简单粗暴,存在诸多问题。现在将状态定位在每一个文件之上,这样恢复上传时,直接跳过即可





封装组件

写了一大堆,其实以上代码你直接复制也无法使用,这里我将此封装了一个组件。大家可以去github下载文件,里面有使用案例 ,若有用记得随手给个star,谢谢!

偷个懒,具体封装组件的代码就不列出来了,大家直接去下载文件查看,若有不明白的,可留言。


组件文档

Attribute

参数 类型 说明 默认 备注

headers Object 设置请求头

before-upload Function 上传文件前的钩子,返回false则停止上传

accept String 接受上传的文件类型

upload-arguments Object 上传文件时携带的参数

with-credentials Boolean 是否传递Cookie false

limit Number 最大允许上传个数 0 0为不限制

on-exceed Function 文件超出个数限制时的钩子

multiple Boolean 是否为多选模式 true

base-url String 由于本组件为内置的AXIOS,若你需要走代理,可以直接在这里配置你的基础路径

chunk-size Number 每个切片的大小 10M

threads Number 请求的并发数 3 并发数越高,对服务器的性能要求越高,尽可能用默认值即可

chunk-retry Number 错误重试次数 3 分片请求的错误重试次数

Slot

方法名 说明 参数 备注

header 按钮区域 无

tip 提示说明文字 无

后端接口文档:按文档实现即可

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






今天影院复工,带你看2020电影海报大合集!

雪涛

千呼万唤始出来,犹戴口罩半遮面

要说上周什么消息最激动人心,那绝对是全国7月20日低风险地区电影院重开无疑了

今天影院复工,带你看2020电影海报大合集!

消息一出,蛰伏数月的电影爱好者们,纷纷倾巢出动在央妈的评论区里为电影提前打Call。

今天影院复工,带你看2020电影海报大合集!

各种网络段子也是应声齐飞:什么待业电影院员工急售餐车抢复工;上座率不超30%,情侣们也得分开坐

今天影院复工,带你看2020电影海报大合集!

想必各位鸭脖听闻也是激动难耐特别是海报设计师们,终于不用因为没活儿继续挨饿了

趁此机会,先来看看电影海报,一来可解相思之苦,顺便排一排雷,还能边挑边学设计技巧,三全其美,岂不快哉:

7月上映电影

1. 误杀

重映时间—7月20日

近年来难得一见的国产剧情好电影,单从海报上就可见一斑。

「看1000部电影」不能助你飞黄腾达,却能让你将世人的眼睛玩弄于股掌,既然生活是一部电影,那就没有什么是不能人为去剪辑的。

今天影院复工,带你看2020电影海报大合集!

表面上束手就擒的一家人,实则齐心协力,暗度陈仓。

今天影院复工,带你看2020电影海报大合集!

如果你认为孩子画的案发现场,将成为如山铁证,那么恭喜你,人都还没进影院,就已经陷入了导演的叙事诡计。

今天影院复工,带你看2020电影海报大合集!

2. 第一次的离别

上映时间—7月20日

发生在新疆男孩与母亲间的温情故事,母亲患病无法言语,更时常离家出走,两小无猜的男孩女孩,毅然穿越漫漫黄沙,踏上寻母的道路。

今天影院复工,带你看2020电影海报大合集!

沙漠中,只要三个人影在孤独地行走路的尽头,究竟是与人别离,还是要跟童年别离,或许,二者都是。

今天影院复工,带你看2020电影海报大合集!

3. 璀璨薪火

上映时间—7月20日

作为非物质文化遗产纪录片的海报一只饱经风霜的粗糙的手,谈不上精妙创意,甚至颇有些质朴。

今天影院复工,带你看2020电影海报大合集!

但别忘了,正是这一双双沉稳的手,日复一日,年复一年守护着国人傲立世界的文化的骄傲。

今天影院复工,带你看2020电影海报大合集!

4. 妙先生

上映时间—7月31日

年度话题大作《大护法》的姊妹篇有关「善良」的深刻讨论,如果每次拯救恶人,都要牺牲好人,那这种所谓的拯救,究竟还值不值。

今天影院复工,带你看2020电影海报大合集!

温馨提示,内有大汉「聚众赏菊」是成年人画给成年人看的动画片。

今天影院复工,带你看2020电影海报大合集!

5. 呆瓜兄弟

上映时间—7月31日

从1976年开播的第一集就让观众捧腹大笑的木偶短片。

今天影院复工,带你看2020电影海报大合集!

40年后重聚的不光是这两只呆瓜更是荧幕前的观众与自己的童年。

今天影院复工,带你看2020电影海报大合集!

8月至12月上映电影

1. 我想静静

上映时间—8月07日

号称「国内首部动物喜剧电影」海报却疑似「宠物店」素材打底。

今天影院复工,带你看2020电影海报大合集!

以上动物皆不会出现在电影中怕不光是「首部宠物喜剧」更是「首部魔幻现实主义作品」

今天影院复工,带你看2020电影海报大合集!

2. 荞麦疯长

上映时间—8月25日

神似一年一度感动中国的构图展现着上世纪90年代,三个相互交织串联的青年人生。

今天影院复工,带你看2020电影海报大合集!

在人们热衷讨论00后、05后的当下导演将镜头对准70、80,在建的东方明珠,既是时代的挽歌也是对一代青年人美好未来的赞美。

今天影院复工,带你看2020电影海报大合集!

3. 我在时间尽头等你

上映时间—8月25日

偶然间获得的超能力让男主能够回到过去,挽救爱情却被命运捉弄,始终都不得圆满

今天影院复工,带你看2020电影海报大合集!

与《时空恋旅人》同样的科幻题材海报当中,也有十分相似的一场雨,希望如同《流浪地球》,国人能再次拍出不输经典的高水准。

今天影院复工,带你看2020电影海报大合集!

4. 小妇人

上映时间—8月待定

女性可以柔弱,也可以拥有高傲的头颅南北战争中的四姐妹,在清苦生活中活出了不一样的坚强人生。

今天影院复工,带你看2020电影海报大合集!

5. 急先锋

上映时间—8月待定

角落里熊熊燃烧的美国核动力航母身后高耸入云的迪拜塔,没有一处不在透露着这是部大制作。

今天影院复工,带你看2020电影海报大合集!

从沙漠到瀑布,剧情搭不搭且不说至少光是海报就搭了不少真金白银。

今天影院复工,带你看2020电影海报大合集!

邻里美好的一天

上映时间—8月待定

一心想揭露社会阴暗丑恶的新闻记者处处与人为善的老好人,矛盾的二者相遇,会是场何样的闹剧。

今天影院复工,带你看2020电影海报大合集!

上一秒促膝长谈,下一秒皮鞋横飞「邻里间的美好」或许本身就是伪命题。

今天影院复工,带你看2020电影海报大合集!

6. 许愿神龙

上映时间—8月待定

上海少年误打误撞唤醒沉睡神龙却发自己与龙有着上千年的代沟,西式画风,中式温情,一人一龙的冒险故事,值得一看。

今天影院复工,带你看2020电影海报大合集!

7. 无名狂

上映时间—9月25日

一部诞生于摩点众筹的众筹电影片名叫《无名狂》,海报却写满了赞助的网友的名字。

今天影院复工,带你看2020电影海报大合集!

围绕万历年间两大刺客门派所展开的一场场腥风血雨,平静海报之下,山呼海啸,风雨欲来。

今天影院复工,带你看2020电影海报大合集!

尚未定档的电影

除此之外,还有不少电影尚未排期,全年随时可能上映。其中自然也不缺乏优秀的海报创意,比如从路人视角,见证抗战历史的《八佰》。

今天影院复工,带你看2020电影海报大合集!

克味十足,容易联想到哈利波特系列的《刺杀小说家》

今天影院复工,带你看2020电影海报大合集!

单靠海报配色就能让人陷入迷惑的《抵达之谜》

今天影院复工,带你看2020电影海报大合集!

颇有08年阿迪达斯海报味道的《夺冠》

今天影院复工,带你看2020电影海报大合集!

今天影院复工,带你看2020电影海报大合集!

用风卷云涌展现中式诸神之战的《封神三部曲》

今天影院复工,带你看2020电影海报大合集!

居然还能拍出第二部的《爵迹2》

今天影院复工,带你看2020电影海报大合集!

正看是海浪,倒看是山峦的《一直游到海水变蓝》

今天影院复工,带你看2020电影海报大合集!

近看是悬崖,远看是枪口的《悬崖之上》

今天影院复工,带你看2020电影海报大合集!

六小龄童老师参与动捕制作今年下半年…可能会上映的《真假美猴王》

今天影院复工,带你看2020电影海报大合集!

希望别播完片头就播staff名单的《一秒钟》

今天影院复工,带你看2020电影海报大合集!

能把牛顿从棺材里吓活过来的《神秘访客》

今天影院复工,带你看2020电影海报大合集!

进退两男、男上加男、满身大汉的《唐人街探案3》

今天影院复工,带你看2020电影海报大合集!

生死一线间淡定到面带微笑的《紧急救援》

今天影院复工,带你看2020电影海报大合集!

以及万众期待的封神宇宙大片《姜子牙》

今天影院复工,带你看2020电影海报大合集!

好了,以上就是2020年将要上映以及可能上映的电影的海报大合集。看完了这些精彩的海报焦急的等待之情是否缓解了一些?

倒也正应了那句老话,只要熬过低谷,后面都是更好的一天。

文章来源:优设    作者:你丫才美工

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

初学者应该看的 Webpack 完整指南(2020)

seo达人

我们应该学习 webpack 吗 ?

如今,CLI工具(如create-react-app或Vue -cli)已经为我们抽象了大部分配置,并提供了合理的默认设置。


即使那样,了解幕后工作原理还是有好处的,因为我们迟早需要对默认值进行一些调整。


在本文中中,我们会知道 webpack可以做什么,以及如何配置它以满足我们的日常需求。


什么是 webpack?

作为前端开发人员,我们应该熟悉 module 概念。 你可能听说过 AMD模块,UMD,Common JS还有ES模块。


webpack是一个模块绑定器,它对模块有一个更广泛的定义,对于webpack来说,模块是:


Common JS modules

AMD modules

CSS import

Images url

ES modules

webpack 还可以从这些模块中获取依赖关系。


webpack 的最终目标是将所有这些不同的源和模块类型统一起来,从而将所有内容导入JavaScript代码,并最生成可以运行的代码。


entry

Webpack的 entry(入口点)是收集前端项目的所有依赖项的起点。 实际上,这是一个简单的 JavaScript 文件。


这些依赖关系形成一个依赖关系图。


Webpack 的默认入口点(从版本4开始)是src/index.js,它是可配置的。 webpack 可以有多个入口点。


Output

output是生成的JavaScript和静态文件的地方。


Loaders

Loaders 是第三方扩展程序,可帮助webpack处理各种文件扩展名。 例如,CSS,图像或txt文件。


Loaders的目标是在模块中转换文件(JavaScript以外的文件)。 文件成为模块后,webpack可以将其用作项目中的依赖项。


Plugins

插件是第三方扩展,可以更改webpack的工作方式。 例如,有一些用于提取HTML,CSS或设置环境变量的插件。


Mode

webpack 有两种操作模式:开发(development)和生产(production)。 它们之间的主要区别是生产模式自动生成一些优化后的代码。


Code splitting

代码拆分或延迟加载是一种避免生成较大包的优化技术。


通过代码拆分,开发人员可以决定仅在响应某些用户交互时加载整个JavaScript块,比如单击或路由更改(或其他条件)。


被拆分的一段代码称为 chunk。


Webpack入门

开始使用webpack时,先创建一个新文件夹,然后进入该文件中,初始化一个NPM项目,如下所示:


mkdir webpack-tutorial && cd $_


npm init -y

接着安装 webpack,webpack-cli和 webpack-dev-server:


npm i webpack webpack-cli webpack-dev-server --save-dev

要运行 webpack,只需要在 package.json 配置如下命令即可:


 "scripts": {

   "dev": "webpack --mode development"

 },

通过这个脚本,我们指导webpack在开发模式下工作,方便在本地工作。


Webpack 的第一步

在开发模式下运行 webpack:


npm run dev

运行完后会看到如下错误:


ERROR in Entry module not found: Error: Can't resolve './src'

webpack 在这里寻找默认入口点src/index.js,所以我们需要手动创建一下,并输入一些内容:


mkdir src


echo 'console.log("Hello webpack!")' > src/index.js

现在再次运行npm run dev,错误就没有了。 运行的结果生成了一个名为dist/的新文件夹,其中包含一个名为main.js的 JS 文件:


dist

└── main.js

这是我们的第一个webpack包,也称为output。


配置 Webpack

对于简单的任务,webpack无需配置即可工作,但是很快我们就会遇到问题,一些文件如果没有指定的 loader 是没法打包的。所以,我们需要对 webpack进行配置,对于 webpack 的配置是在 webpack.config.js 进行的,所以我们需要创建该文件:


touch webpack.config.js

Webpack 用 JavaScript 编写,并在无头 JS 环境(例如Node.js)上运行。 在此文件中,至少需要一个module.exports,这是的 Common JS 导出方式:


module.exports = {

 //

};

在webpack.config.js中,我们可以通过添加或修改来改变webpack的行为方式


entry point

output

loaders

plugins

code splitting

例如,要更改入口路径,我们可以这样做


const path = require("path");


module.exports = {

 entry: { index: path.resolve(__dirname, "source", "index.js") }

};

现在,webpack 将在source/index.js中查找要加载的第一个文件。 要更改包的输出路径,我们可以这样做:


const path = require("path");


module.exports = {

 output: {

   path: path.resolve(__dirname, "build")

 }

}

这样,webpack将把最终生成包放在build中,而不是dist.(为了简单起见,在本文中,我们使用默认配置)。


打包 HTML

没有HTML页面的Web应用程序几乎没有用。 要在webpack中使用 HTML,我们需要安装一个插件html-webpack-plugin:


npm i html-webpack-plugin --save-dev

一旦插件安装好,我们就可以对其进行配置:


const HtmlWebpackPlugin = require("html-webpack-plugin");

const path = require("path");


module.exports = {

 plugins: [

   new HtmlWebpackPlugin({

     template: path.resolve(__dirname, "src", "index.html")

   })

 ]

};

这里的意思是让 webpack,从 src/index.html 加载 HTML 模板。


html-webpack-plugin的最终目标有两个:


加载 html 文件

它将bundle注入到同一个文件中

接着,我们需要在 src/index.html 中创建一个简单的 HTML 文件:


<!DOCTYPE html>

<html lang="en">

<head>

   <meta charset="UTF-8">

   <title>Webpack tutorial</title>

</head>

<body>


</body>

</html>

稍后,我们会运行这个程序。


webpack development server

在本文第一部分中,我们安装了webpack-dev-server。如果你忘记安装了,现在可以运行下面命令安装一下:


npm i webpack-dev-server --save-dev

webpack-dev-server 可以让开发更方便,不需要改动了文件就去手动刷新文件。 配置完成后,我们可以启动本地服务器来提供文件。


要配置webpack-dev-server,请打开package.json并添加一个 “start” 命令:


"scripts": {

 "dev": "webpack --mode development",

 "start": "webpack-dev-server --mode development --open",

},

有了 start 命令,我们来跑一下:


npm start

运行后,默认浏览器应打开。 在浏览器的控制台中,还应该看到一个 script 标签,引入的是我们的 main.js。


clipboard.png


使用 webpack loader

Loader是第三方扩展程序,可帮助webpack处理各种文件扩展名。 例如,有用于 CSS,图像或 txt 文件的加载程序。


下面是一些 loader 配置介绍:


module.exports = {

 module: {

   rules: [

     {

       test: /\.filename$/,

       use: ["loader-b", "loader-a"]

     }

   ]

 },

 //

};

相关配置以module 关键字开始。 在module内,我们在rules内配置每个加载程序组或单个加载程序。


对于我们想要作为模块处理的每个文件,我们用test和use配置一个对象


{

   test: /\.filename$/,

   use: ["loader-b", "loader-a"]

}

test 告诉 webpack “嘿,将此文件名视为一个模块”。 use 定义将哪些 loaders 应用于些打包的文件。


打包 CSS

要 在webpack 中打包CSS,我们需要至少安装两个 loader。Loader 对于帮助 webpack 了解如何处理.css文件是必不可少的。


要在 webpack 中测试 CSS,我们需要在 src 下创建一个style.css文件:


h1 {

   color: orange;

}

另外在 src/index.html 添加 h1 标签


<!DOCTYPE html>

<html lang="en">

<head>

   <meta charset="UTF-8">

   <title>Webpack tutorial</title>

</head>

<body>

<h1>Hello webpack!</h1>

</body>

</html>

最后,在src/index.js 中加载 CSS:


在测试之前,我们需要安装两个 loader:


css-loader: 解析 css 代码中的 url、@import语法像import和require一样去处理css里面引入的模块

style-loader:帮我们直接将css-loader解析后的内容挂载到html页面当中

安装 loader:


npm i css-loader style-loader --save-dev

然后在webpack.config.js中配置它们


const HtmlWebpackPlugin = require("html-webpack-plugin");

const path = require("path");


module.exports = {

 module: {

   rules: [

     {

       test: /\.css$/,

       use: ["style-loader", "css-loader"]

     }

   ]

 },

 plugins: [

   new HtmlWebpackPlugin({

     template: path.resolve(__dirname, "src", "index.html")

   })

 ]

};

现在,如果你运行npm start,会看到样式表加载在HTML的头部:


clipboard.png


一旦CSS Loader 就位,我们还可以使用MiniCssExtractPlugin提取CSS文件


Webpack Loader 顺序很重要!

在webpack中,Loader 在配置中出现的顺序非常重要。以下配置无效:


//


module.exports = {

 module: {

   rules: [

     {

       test: /\.css$/,

       use: ["css-loader", "style-loader"]

     }

   ]

 },

 //

};

此处,“style-loader”出现在 “css-loader” 之前。 但是style-loader用于在页面中注入样式,而不是用于加载实际的CSS文件。


相反,以下配置有效:


module.exports = {

 module: {

   rules: [

     {

       test: /\.css$/,

       use: ["style-loader", "css-loader"]

     }

   ]

 },

 //

};

webpack loaders 是从右到左执行的。


打包 sass

要在 webpack 中测试sass,同样,我们需要在 src 目录下创建一个 style.scss 文件:


@import url("https://fonts.googleapis.com/css?family=Karla:weight@400;700&display=swap");


$font: "Karla", sans-serif;

$primary-color: #3e6f9e;


body {

 font-family: $font;

 color: $primary-color;

}

另外,在src/index.html中添加一些 Dom 元素:


<!DOCTYPE html>

<html lang="en">

<head>

   <meta charset="UTF-8">

   <title>Webpack tutorial</title>

</head>

<body>

 <h1>Hello webpack!</h1>

 <p>Hello sass!</p>

</body>

</html>

最后,将 sass 文件加载到src/index.js中:


import "./style.scss";

console.log("Hello webpack!");

在测试之前,我们需要安装几个 loader:


sass-loader:加载 SASS / SCSS 文件并将其编译为 CSS

css-loader: 解析 css 代码中的 url、@import语法像import和require一样去处理css里面引入的模块

style-loader:帮我们直接将css-loader解析后的内容挂载到html页面当中

安装 loader:


npm i css-loader style-loader sass-loader sass --save-dev

然后在webpack.config.js中配置它们:


const HtmlWebpackPlugin = require("html-webpack-plugin");

const path = require("path");


module.exports = {

 module: {

   rules: [

     {

       test: /\.scss$/,

       use: ["style-loader", "css-loader", "sass-loader"]

     }

   ]

 },

 plugins: [

   new HtmlWebpackPlugin({

     template: path.resolve(__dirname, "src", "index.html")

   })

 ]

};

注意loader的出现顺序:首先是sass-loader,然后是css-loader,最后是style-loader。


现在,运行npm start,你应该会在HTML的头部看到加载的样式表:


clipboard.png


打包现代 JavaScrip

webpack 本身并不知道如何转换JavaScript代码。 该任务已外包给babel的第三方 loader,特别是babel-loader。


babel是一个JavaScript编译器和“编译器”。 babel 可以将现代JS(es6, es7...)转换为可以在(几乎)任何浏览器中运行的兼容代码。


同样,要使用它,我们需要安装一些 Loader:


babel-core :把 js 代码分析成 ast ,方便各个插件分析语法进行相应的处理

babel-preset-env:将现代 JS 编译为ES5

babel-loader :用于 webpack

引入依赖关系


npm i @babel/core babel-loader @babel/preset-env --save-dev

接着,创建一个新文件babel.config.json配置babel,内容如下:


{

 "presets": [

   "@babel/preset-env"

 ]

}

最后在配置一下 webpack :


const HtmlWebpackPlugin = require("html-webpack-plugin");

const path = require("path");


module.exports = {

 module: {

   rules: [

     {

       test: /\.scss$/,

       use: ["style-loader", "css-loader", "sass-loader"]

     },

     {

       test: /\.js$/,

       exclude: /node_modules/,

       use: ["babel-loader"]

     }

   ]

 },

 plugins: [

   new HtmlWebpackPlugin({

     template: path.resolve(__dirname, "src", "index.html")

   })

 ]

};

要测试转换,可以在 src/index.js中编写一些现代语法:


import "./style.scss";

console.log("Hello webpack!");


const fancyFunc = () => {

 return [1, 2];

};


const [a, b] = fancyFunc();

现在运行npm run dev来查看dist中转换后的代码。 打开 dist/main.js并搜索“fancyFunc”:


\n\nvar fancyFunc = function fancyFunc() {\n return [1, 2];\n};\n\nvar _fancyFunc = fancyFunc(),\n _fancyFunc2 = _slicedToArray(_fancyFunc, 2),\n a = _fancyFunc2[0],\n b = _fancyFunc2[1];\n\n//# sourceURL=webpack:///./src/index.js?"

没有babel,代码将不会被转译:


\n\nconsole.log(\"Hello webpack!\");\n\nconst fancyFunc = () => {\n return [1, 2];\n};\n\nconst [a, b] = fancyFunc();\n\n\n//# sourceURL=webpack:///./src/index.js?");

注意:即使没有babel,webpack也可以正常工作。 仅在执行 ES5 代码时才需要进行代码转换过程。


在 Webpack 中使用 JS 的模块

webpack 将整个文件视为模块。 但是,请不要忘记它的主要目的:加载ES模块。


ECMAScript模块(简称ES模块)是一种JavaScript代码重用的机制,于2015年推出,一经推出就受到前端开发者的喜爱。在2015之年,JavaScript 还没有一个代码重用的标准机制。多年来,人们对这方面的规范进行了很多尝试,导致现在有多种模块化的方式。


你可能听说过AMD模块,UMD,或CommonJS,这些没有孰优孰劣。最后,在ECMAScript 2015中,ES 模块出现了。


我们现在有了一个“正式的”模块系统。


要在 webpack 使用 ES module ,首先创建 src/common/usersAPI.js 文件:


const ENDPOINT = "https://jsonplaceholder.typicode.com/users/";


export function getUsers() {

 return fetch(ENDPOINT)

   .then(response => {

     if (!response.ok) throw Error(response.statusText);

     return response.json();

   })

   .then(json => json);

}

在 src/index.js中,引入上面的模块:


import { getUsers } from "./common/usersAPI";

import "./style.scss";

console.log("Hello webpack!");


getUsers().then(json => console.log(json));

生产方式

如前所述,webpack有两种操作模式:开发(development )和(production)。 到目前为止,我们仅在开发模式下工作。


在开发模式中,为了便于代码调试方便我们快速定位错误,不会压缩混淆源代码。相反,在生产模式下,webpac k进行了许多优化:


使用 TerserWebpackPlugin 进行缩小以减小 bundle 的大小

使用ModuleConcatenationPlugin提升作用域

在生产模式下配 置webpack,请打开 package.json 并添加一个“ build” 命令:


现在运行 npm run build,webpack 会生成一个压缩的包。


Code splitting

代码拆分(Code splitting)是指针对以下方面的优化技术:


避免出现一个很大的 bundle

避免重复的依赖关系

webpack 社区考虑到应用程序的初始 bundle 的最大大小有一个限制:200KB。


在 webpack 中有三种激活 code splitting 的主要方法:


有多个入口点

使用 optimization.splitChunks 选项

动态导入

第一种基于多个入口点的技术适用于较小的项目,但是从长远来看它是不可扩展的。这里我们只关注第二和第三种方式。


Code splitting 与 optimization.splitChunks

考虑一个使用Moment.js 的 JS 应用程序,Moment.js是流行的时间和日期JS库。


在项目文件夹中安装该库:


npm i moment

现在清除src/index.js的内容,并引入 moment 库:


import moment from "moment";

运行 npm run build 并查看控制的输出内容:


main.js 350 KiB 0 [emitted] [big] main

整个 moment 库都绑定到了 main.js 中这样是不好的。借助optimization.splitChunks,我们可以从主包中移出moment.js。


要使用它,需要在 webpack.config.js 添加 optimization 选项:


const HtmlWebpackPlugin = require("html-webpack-plugin");

const path = require("path");


module.exports = {

 module: {

 // ...

 },

 optimization: {

   splitChunks: { chunks: "all" }

 },

 // ...

};

运行npm run build 并查看运行结果:


       main.js   5.05 KiB       0  [emitted]         main

vendors~main.js    346 KiB       1  [emitted]  [big]  vendors~main

现在,我们有了一个带有moment.js 的vendors〜main.js,而主入口点的大小更合理。


注意:即使进行代码拆分,moment.js仍然是一个体积较大的库。 有更好的选择,如使用luxon或date-fns。


Code splitting 与 动态导入

Code splitting的一种更强大的技术使用动态导入来有条件地加载代码。 在ECMAScript 2020中提供此功能之前,webpack 提供了动态导入。


这种方法在 Vue 和 React 之类的现代前端库中得到了广泛使用(React有其自己的方式,但是概念是相同的)。


Code splitting 可用于:


模块级别

路由级别

例如,你可以有条件地加载一些 JavaScript 模块,以响应用户的交互(例如单击或鼠标移动)。 或者,可以在响应路由更改时加载代码的相关部分。


要使用动态导入,我们先清除src/index.html,并写入下面的内容:


<!DOCTYPE html>

<html lang="en">

<head>

   <meta charset="UTF-8">

   <title>Dynamic imports</title>

</head>

<body>

<button id="btn">Load!</button>

</body>

</html>

在 src/common/usersAPI.js中:


const ENDPOINT = "https://jsonplaceholder.typicode.com/users/";


export function getUsers() {

 return fetch(ENDPOINT)

   .then(response => {

     if (!response.ok) throw Error(response.statusText);

     return response.json();

   })

   .then(json => json);

}

在 src/index.js 中


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


btn.addEventListener("click", () => {

 //

});

如果运行npm run start查看并单击界面中的按钮,什么也不会发生。


现在想象一下,我们想在某人单击按钮后加载用户列表。 “原生”的方法可以使用静态导入从src/common /usersAPI.js加载函数:


import { getUsers } from "./common/usersAPI";


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


btn.addEventListener("click", () => {

 getUsers().then(json => console.log(json));

});

问题在于ES模块是静态的,这意味着我们无法在运行时更改导入的内容。


通过动态导入,我们可以选择何时加载代码


const getUserModule = () => import("./common/usersAPI");


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


btn.addEventListener("click", () => {

 getUserModule().then(({ getUsers }) => {

   getUsers().then(json => console.log(json));

 });

});

这里我们创建一个函数来动态加载模块


const getUserModule = () => import("./common/usersAPI");

现在,当你第一次使用npm run start加载页面时,会看到控制台中已加载 js 包:


clipboard.png


现在,仅在单击按钮时才加载/common/usersAPI:


clipboard.png


对应的 chunk 是 0.js


通过在导入路径前面加上魔法注释/ * webpackChunkName:“ name_here” * /,可以更改块名称:


const getUserModule = () =>

 import(/* webpackChunkName: "usersAPI" */ "./common/usersAPI");


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


btn.addEventListener("click", () => {

 getUserModule().then(({ getUsers }) => {

   getUsers().then(json => console.log(json));

 });

});

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

iview按需引入,ie11不兼容,报无效字符问题解决

seo达人

准备工作

//借助插件

npm install babel-plugin-import --save-dev


// .babelrc

{

 "plugins": [["import", {

   "libraryName": "view-design",

   "libraryDirectory": "src/components"

 }]]

}

在main.js中引入

import "view-design/dist/styles/iview.css";

import { Button, Table } from "view-design";

const viewDesign = {

Button: Button,

Table: Table

};

Object.keys(viewDesign).forEach(element => {

Vue.component(element, viewDesign[element]);

});

先用google浏览器打开正常,以上操作猛如虎,IE浏览器打开250,好了不废话,下面是解决方案


解决方案

//vue.config.js中配置

chainWebpack: config => {

   //解决iview 按需引入babel转换问题

  config.module

     .rule("view-design")  //  我目前用的是新版本的iview ,旧版本的iview,用iview代替view-design

     .test(/view-design.src.*?js$/)

     .use("babel")

     .loader("babel-loader")

     .end();

}

问题原因

为什么会有如上问题呢? 这个就和babel转换问题有关了,按需引入时,那些组件里js文件未进行babel转换或转换不彻底就被引入了,ie11对es6+的语法支持是很差的,所以以上方法就是让引入文件前就对view-design的src下的所有js文件进行babel转换,举一反三,当按需引入第三方框架时出现这个问题,都可用这方法解决了,只要把规则和正则中view-design进行替换。


延伸扩展

//全局引入

import ViewUI from "view-design";

Vue.use(ViewUI);

import "view-design/dist/styles/iview.css";

tips:在全局引入时,一定要记住不要在.babelrc文件里配置按需导入,会导致冲突

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

日历

链接

blogger

蓝蓝 http://www.lanlanwork.com

存档