ddvdd

ddvdd 查看完整档案

上海编辑  |  填写毕业院校  |  填写所在公司/组织 github.com/ddvdd008 编辑
编辑

戎码一生

个人动态

ddvdd 关注了专栏 · 2018-11-02

前端的bigboom

个人博客: https://www.villainhr.com 公众号:前端小吉米

关注 1065

ddvdd 回答了问题 · 2018-10-18

FormattedMessage在属性中使用

placeholder接受的是字符串,FormattedMessage这个应该是个组件吧,如果你要实现动态显示placeholder的内容,建议使用一个变量或者函数去实现

关注 5 回答 3

ddvdd 收藏了文章 · 2018-05-21

前端网络知识目录(持续更新)

这里收录了前端新手进阶学习网络相关的资源链接,按照一定的先后顺序排列。如果你是新手,可以按照目录中的排序一篇一篇的看。如果你是有经验的老手,也可以选择性的浏览。

1.TCP与UDP

文章原始内容来自《计算机网络》(第六版),基础权威,详细的讲解了UDP、TCP、拥塞控制的概念性知识。知识内容很基础,所以也比较浅显。如果想了解拥塞控制的具体知识还需要参考其他书籍,《http权威指南》是个不错的选择。
详细的介绍了TCP的创建连接与断开连接的过程,图文描述,嘻清晰易懂。如果想深入理解关于SYN和ACK的知识还需要参考别的文章。

2.HTTP基础篇

详细的介绍了http报文中非常重要的字段content-type的知识,主要讲了常见常用的,省去了不常见或者被废弃的,不啰嗦,直接背下来。

3.HTTP跨域相关

很全面很具体的介绍前端跨域知识,从什么是跨域到跨域实现,读完之后真是恍然大悟。
从跨域请求时出现了OPTIONS 请求开始分析,介绍了预检机制(preflight request)、简单请求与复杂请求和一些跨域请求的相关知识。文章中包括了很多我们忽略掉的但是很重要的知识,如果要系统掌握CORS,这篇文章必读。

4.XSS与CSRF攻击

读完了跨域的相关知识,你知道为什么浏览器要有“同源策略”了嘛?这篇文章就讲了“同源策略”的目的--为了安全。这里很详细的讲了前端常见的两种攻击策略:XSS与CSRF攻击。对于丰富你对前端安全知识领域有很大帮助,记得深刻理解这些危险是怎么发生的,思考现在我们又是如何应对的。
由浅入讲解XSS与CSRF攻击,全面具体,适合新手快速入门,了解前端安全强烈推荐此篇。

5.HTTP缓存

这是一篇详尽介绍浏览器缓存的文章,仔细阅读,浏览器缓存掌握这一篇就够了。
查看原文

ddvdd 收藏了文章 · 2018-05-21

前端面试题积累

创建对象的方式

1、工厂模式

在函数里,new 一个 Object,然后根据传入的参数给该对象添加属性,最后返回该对象。问题:无法知道一个对象的类型。

2、构造函数模式

问题:每个方法都要在每个实例上重新创建一遍。解决:在全局作用域中定义全局函数。当然,这会导致封装性很差。

3、原型模式

每个函数都有一个 prototype(原型)属性,这个属性是一个指针,指向一个对象(指向该函数的原型对象),而这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法。如果按照字面意思来理解,那么 prototype 就是通过调用构造函数而创建的那个对象实例的原型对象。使用原型对象的好处是可以让所有对象实例共享它所包含的属性和方法。

缺点:原型中所有属性是被很多实例共享的,这种共享对于函数非常合适。但是对于包含引用类型值的属性问题就突出了

4、组合使用构造函数模式和原型模式

构造函数模式用于定义实例属性,而原型模式用于定义方法和共享的属性。
结果,每个实例都会有自己的一份实例属性的副本,但同时又共享着对方法的引用,最大限度地节省了内存。另外,这种混成模式还支持向构造函数传递参数;可谓是集两种模式之长。

5、动态原型模式

在构造函数中这么写共享的方法和属性:

// 方法
if (typeof this.sayName != 'function') {
  Person.prototype.sayName = function() {
    alert(this.name);
  };
}

6、寄生构造函数模式

基本思想:创建一个函数,该函数的作用仅仅是封装创建对象的代码,然后再返回新创建的对象。

function Person(name, age, job) {
  var o = new Object();
  o.name = name;
  o.age = age;
  o.job = job;
  o.sayName = function() {
    alert(this.name);
  };
  return o;
}

var friend = new Person('Amy', 18, 'student');
friend.sayName();    // Amy

构造函数在不返回值的情况下,默认会返回新对象实例。而通过在构造函数的末尾添加一个 return 语句,可以重写调用构造函数时返回的值。

用法:这个模式可以在特殊的情况下用来为对象创建构造函数。假设我们想创建一个具有额外方法的特殊数组。由于不能直接修改 Array 构造函数,因此可以使用这个模式。

function SpecialArray() {
  // 创建数组
  var values = new Array();
  // 添加值
  values.push.apply(values, arguments);
  // 添加方法
  values.toPipedString = function() {
    return this.join('|');
  };
  // 返回数组
  return values;
}

var colors = new SpecialArray('red', 'green', 'blue');
alert(colors.toPipedString());    // red|green|blue

关于寄生构造函数模式,有一点需要说明:
首先,返回的对象与构造函数或者与构造函数的原型属性之间没有关系;也就是说,构造函数返回的对象与在构造函数外部创建的对象没有什么不同。为此,不能依赖 instanceof 操作符来确定对象类型。

用 new 调用构造函数实际经历了4个步骤

  • 1 创建一个新对象;
  • 2 将构造函数的作用域赋给新对象(因此 this 就指向了这个新对象);
  • 3 执行构造函数中的代码(为这个新对象添加属性);
  • 4 返回新对象。

理解原型对象

所有函数都有一个 prototype 属性,这个属性指向函数的原型对象

在默认情况下,所有原型对象都会自动获得一个 constructor (构造函数)属性,这个属性是一个指向 prototype 属性所在函数的指针
eg. Person.prototype.constructor => Person。

当调用构造函数创建一个新实例后,该实例的内部将包含一个指针(内部属性[[Prototype]]),指向构造函数的原型对象。注意:这个连接存在于实例与构造函数的原型对象之间,而不是存在于实例与构造函数之间。实例 => 构造函数的原型对象

原型对象

判断某个实例的原型指针是否指向某个函数的原型对象:

Person.prototype.isPrototypeOf(person1)    // true
Object.getPrototypeOf(person1) === Person.protype    // true

hasOwnProperty() 方法:检测一个属性是存在于实例中,还是存在于原型中。只有存在于实例中时,才返回 true。
in 操作符:实例和原型中的属性都能访问到。
同时使用 hasOwnProperty() 方法和 in 操作符,就可以确定该属性到底是存在于对象中,还是存在于原型中。

Object.keys() 方法:取得对象上所有可枚举的实例属性。
Object.getOwnPropertyNames() 方法:得到所有实例属性(包括不可枚举属性)。

重写原型会怎么样?

Person.prototype = {…}:
我们将 Person.prototype 设置为等于一个以对象字面量形式创建的新对象。 最终结果相同,但有一个例外:constructor 属性不再指向 Person 了。前面曾经介绍过,每创建一个函数,就会同时创建它的 prototype 对象,这个对象也会自动获得 constructor 属性。而我们在这里使用的语法,本质上完全重写了默认的 prototype 对象,因此 constructor 属性也就变成了新对象的 constructor 属性(指向 Object 构造函数),不再指向 Person 函数。此时,尽管 instanceof 操作符还能返回正确的结果,但通过 constructor 已经无法确定对象的类型了。

重写原型对象

实现继承的方式

1、原型链继承

查看原文

ddvdd 赞了文章 · 2018-03-02

LeanCloud 与阿里云到底有什么区别?

不是很了解 LeanCloud 的开发者经常会问「LeanCloud 与已有的很多云服务有什么区别呢?」下面我们就以国内比较有代表性的阿里云为例,跟我们做下对比。

产品的区别

进入阿里云网站可以看到阿里云的产品介绍。产品列表有弹性计算、数据库、存储与 CDN、网络、大规模计算、云盾、管理与监控、应用服务、互联网中间件、移动服务、域名与网站等,每个选项下面又有非常多的子产品列表,提供的服务种类繁多。个人感觉几乎开发中需要使用的服务器产品,阿里云应该都提供了。这些产品更偏向于较底层的服务,用户要想使用起来需要具备一定的能力。

阿里云官网部分截图

LeanCloud 则完全不同。它提供了四项产品,分别是 LeanStorage(数据、文件存储及云引擎)、LeanMessage(短信、推送及实时通讯服务)、LeanAnalytics(统计分析服务)、LeanModules(各种其他通用组件)。看起来很精简却有些抽象,那这些产品具体又能满足什么需求呢?

概念上的区别

阿里云提供的是类似于 AWS(亚马逊的云服务)一样的传统云服务。使用了阿里云你就不用再去操心那些与硬件和底层运维相关的事情,比如硬盘损坏、主机托管、服务器配置网络等等。

但如果想要开发一个自己的 App,你仍然需要在阿里云上购买机器,选择部署到哪个机房,还要购买数据库,选择数据具体是怎样的规格,然后还要对这台机器进行完整的配置。虽然比没有云服务的日子已经轻松了许多,但这些操作仍然需要一个专业的工程师才能很好地完成。

而使用 LeanCloud 用户却不需要操心这些事情,可以说基本上不用考虑服务器的细节。

LeanCloud 提供的是 BaaS 服务(Backend as a Service 后端即服务),又被称为云服务 2.0。简而言之,云服务 1.0 解决的是不再让你担心服务器,而 BaaS 的目标是帮你解决全部服务器运维,甚至是部分后端业务逻辑。那 LeanCloud 究竟是怎么做到的呢?回答这个问题之前,我们看下一个 App 一般都是什么样子。


以 LeanCloud 的用户「懂球帝」为参考,不论什么产品基本上都需要一套账号系统,目前较通用的做法是使用手机号码注册,发送短信验证;基于这个账号还要存储一些数据项,如昵称、头像等信息,再到真正的主业务逻辑,需要通过服务器基于某个逻辑运算出结果交给客户端做展示。

那么我们再考虑一个问题,为什么我们每次做一个产品都要反反复复地开发这些差不多一样的逻辑呢?比如账号系统、数据存储、短信验证、邮件验证、推送服务甚至是即时聊天,有没有办法让这些东西拿来就用,让自己能够最快速地投入开发呢?当然有办法,这就是 LeanCloud 所做的事情。

具体使用 LeanCloud

来举个具体例子。在 LeanCloud 中想要实现一套账号系统共分为三步:注册账号、创建一个应用、下载对应的 SDK。就这三步?难道不用写代码吗?是的,不用写代码你其实已经拥有了一套支持 ACL(访问权限控制)、支持短信验证注册、支持邮件注册这样具备完整安全体系的账号系统,客户端工程师只需直接使用即可。例如,Web 前端通过 JavaScript SDK 在浏览器使用账户系统,具体代码如下:

// 创建一个实例
const user = new AV.User();
// 设置用户名
user.set('username', 'wangxiao);
// 设置密码
user.set('password', 123456789);
// 注册
user.signUp().then(user => {
  // 注册成功
}, error => {
  // 注册失败
});

再如经常被使用的短信验证功能,你不需要去找服务端工程师去开发一个专用接口,而是直接在浏览器中调用 JavaScript SDK 的方法(支持模板来定制短信内容),具体代码如下:

// 发送手机验证码
AV.Cloud.requestSmsCode({
  mobilePhoneNumber: '182xxxx5548'
}).then(() => {
  // 发送成功
}, error => {
  // 发送失败
});

// 校验验证码
AV.Cloud.verifySmsCode('1234', '182xxxx5548')
.then(() => {
  // 验证成功
}, error => {
  // 验证失败
});

// 短信模板
AV.Cloud.requestSmsCode({
  mobilePhoneNumber: '182xxxx5548',
  template: 'Template_Name',
  ttttName: '自定义模板变量名'
}).then(() => {
  // 发送成功
}, error => {
  // 发送失败
});

短信验证仅仅是我们所开放的众多功能中的一项,你还可以使用 SDK 轻松实现数据存储、文件存储(CDN)、推送、即时聊天等实用功能。如此以来你的开发效率会大幅提升,服务器端对于你来说完全是透明的,这样就能把所有精力集中到研发核心产品上去,而后续的数据运营和管理工作可以直接在 LeanCloud 的控制台中进行,甚至在初期你都不需要给运营人员编写一个对应的管理后台。

控制台中的数据管理界面

成本的区别

选择传统的云服务,你可能需要更多地去了解服务端的结构,要综合考虑在云服务上搭建出一套自己的系统所付出的成本,还需要找到合适的工程师去维护这些服务,找到后端工程师来开发服务端很多通用的业务逻辑。

如果使用 LeanCloud 这些事情都不用去考虑,直接使用相应的服务即可。同时我们的云服务按照使用量计费,并提供了一定额度的免费使用量,在初期用户量少的时候基本不会产生什么费用,只有当用户量增长到一定量级时才会产生相应的费用。总之使用 LeanCloud 不仅仅省去了后期运维的成本,还减少了后端工程师的工作量,加速产品迭代。

查看原文

赞 5 收藏 6 评论 0

ddvdd 回答了问题 · 2018-02-24

font-size一样,汉字跟数字显示不一样大小?

跟字体类型有关,例如你字体设置幼圆,数字和汉字在相同字体大小下,显示大小不一样。
解决方案:对数字加上特定类名的标签p或者span,重新定义数字样式

关注 4 回答 4

ddvdd 回答了问题 · 2018-02-23

反爬虫技术如何实现

举一个我这边正在应对反爬的策略:
1.给需要增加反爬的接口增加一个参数,该参数的值是这个接口入参所有参数对象转化为json串后,再通过一个固定2
的加密规则生成一串秘文。
2.后台服务器根据这个参数,通过约定的秘钥进行解密,与传过来的参数进行比较,如果匹配,就认为是正常请求,如果不匹配,就认为是爬虫。

因为加密解密策略只有自己知道,所以爬虫不可能模仿,创造这个参数。

关注 10 回答 6

ddvdd 关注了用户 · 2018-02-18

姜立 @jiangli

关注 10

ddvdd 赞了文章 · 2018-02-05

教你从零开始搭建一款前端脚手架工具

本文系原创,转载请附带作者信息:Jrain Lau
项目地址:https://github.com/jrainlau/s...

前言

在实际的开发过程中,从零开始建立项目的结构是一件让人头疼的事情,所以各种各样的脚手架工具应运而生。笔者使用较多的yoemanexpress-generatorvue-cli便是当中之一。它们功能丰富,但最核心的功能都是能够快速搭建一个完整的项目的结构,开发者只需要在生成的项目结构的基础上进行开发即可,非常简单高效。

作为一个不折腾会死星人,在熟悉了使用方法以后就开始琢磨起它们的原理来了。经过仔细研究文档和源码,终于算是摸清了其核心的原理,并且依据这个原理自己搭建了一款叫做SCION的脚手架。

现在让我们就以SCION为例,从零开始搭建一款属于我们自己的脚手架工具吧!

核心原理

yoeman搭建项目需要提供yoeman-generatoryoeman-generator本质上就是一个具备完整文件结构的项目样板,用户需要手动地把这些generator下载到本地,然后yoeman就会根据这些generator自动生成各种不同的项目。

vue-cli提供了相当丰富的选项和设定功能,但是其本质也是从远程仓库把不同的模版拉取到本地,而并非是什么“本地生成”的黑科技。

这样看来,思路也就有了——首先建立不同的样板项目,然后脚手架根据用户的指令引用样板项目生成实际项目。样板项目可以内置在脚手架当中,也可以部署在远程仓库。为了更广的适用范围,SCION采用的是第二种方式。

技术选型

  • node.js:整个脚手架工具的根本组成部分,推荐使用最新的版本。

  • es6:新版本的node.js对于es6的支持度已经非常高,使用es6能够极大地提升开发效率和开发感受。

  • commander:TJ大神开发的工具,能够更好地组织和处理命令行的输入。

  • co:TJ大神开发的异步流程控制工具,用更舒服的方式写异步代码。

  • co-prompt:还是TJ大神的作品……传统的命令行只能单行一次性地输入所有参数和选项,使用这个工具可以自动提供提示信息,并且分步接收用户的输入,体验类似npm init时的一步一步输入参数的过程。

整体架构

国际惯例,着手开发之前得先弄明白整体架构,看图:
图片描述

首先明白模版的概念。一个模版就是一个项目的样板,包含项目的完整结构和信息。
模版的信息都存放在一个叫做templates.json的文件当中。
用户可以通过命令行对templates.json进行添加、删除、罗列的操作。
通过选择不同的模版,SCION会自动从远程仓库把相应的模板拉取到本地,完成项目的搭建。

最终整个脚手架的文件结构如下:

=================
  |__ bin
    |__ scion
  |__ command
    |__ add.js
    |__ delete.js
    |__ init.js
    |__ list.js
  |__ node_modules
  |__ package.json
  |__ templates.json

入口文件

首先建立项目,在package.json里面写入依赖并执行npm install

"dependencies": {
    "chalk": "^1.1.3",
    "co": "^4.6.0",
    "co-prompt": "^1.0.0",
    "commander": "^2.9.0"
  }

在根目录下建立\bin文件夹,在里面建立一个无后缀名的scion文件。这个bin\scion文件是整个脚手架的入口文件,所以我们首先对它进行编写。

首先是一些初始化的代码:

#!/usr/bin/env node --harmony
'use strict'
 // 定义脚手架的文件路径
process.env.NODE_PATH = __dirname + '/../node_modules/'

const program = require('commander')

 // 定义当前版本
program
    .version(require('../package').version )

// 定义使用方法
program
    .usage('<command>')

从前文的架构图中可以知道,脚手架支持用户输入4种不同的命令。现在我们来写处理这4种命令的方法:

program
    .command('add')
    .description('Add a new template')
  .alias('a')
  .action(() => {
    require('../command/add')()
  })

program
    .command('list')
    .description('List all the templates')
    .alias('l')
    .action(() => {
        require('../command/list')()
    })

program
    .command('init')
    .description('Generate a new project')
  .alias('i')
  .action(() => {
    require('../command/init')()
  })

program
    .command('delete')
    .description('Delete a template')
    .alias('d')
    .action(() => {
        require('../command/delete')()
    })

commander的具体使用方法在这里就不展开了,可以直接到官网去看详细的文档。
最后别忘了处理参数和提供帮助信息:

program.parse(process.argv)

if(!program.args.length){
  program.help()
}

完整的代码请看这里
使用node运行这个文件,看到输出如下,证明入口文件已经编写完成了。

Usage: scion <command>


  Commands:

    add|a      Add a new template
    list|l     List all the templates
    init|i     Generate a new project
    delete|d   Delete a template

  Options:

    -h, --help     output usage information
    -V, --version  output the version number

处理用户输入

在项目根目录下建立\command文件夹,专门用来存放命令处理文件。
在根目录下建立templates.json文件并写入如下内容,用来存放模版信息:

{"tpl":{}}

添加模板

进入\command并新建add.js文件:

'use strict'
const co = require('co')
const prompt = require('co-prompt')
const config = require('../templates')
const chalk = require('chalk')
const fs = require('fs')

module.exports = () => {
 co(function *() {

   // 分步接收用户输入的参数
   let tplName = yield prompt('Template name: ')
   let gitUrl = yield prompt('Git https link: ')
   let branch = yield prompt('Branch: ')
    
   // 避免重复添加
   if (!config.tpl[tplName]) {
     config.tpl[tplName] = {}
     config.tpl[tplName]['url'] = gitUrl.replace(/[\u0000-\u0019]/g, '') // 过滤unicode字符
     config.tpl[tplName]['branch'] = branch
   } else {
     console.log(chalk.red('Template has already existed!'))
     process.exit()
   }
   
   // 把模板信息写入templates.json
   fs.writeFile(__dirname + '/../templates.json', JSON.stringify(config), 'utf-8', (err) => {
     if (err) console.log(err)
     console.log(chalk.green('New template added!\n'))
     console.log(chalk.grey('The last template list is: \n'))
     console.log(config)
     console.log('\n')
     process.exit()
    })
 })
}

删除模板

同样的,在\command文件夹下建立delete.js文件:

'use strict'
const co = require('co')
const prompt = require('co-prompt')
const config = require('../templates')
const chalk = require('chalk')
const fs = require('fs')

module.exports = () => {
    co(function *() {
        // 接收用户输入的参数
        let tplName = yield prompt('Template name: ')

        // 删除对应的模板
        if (config.tpl[tplName]) {
            config.tpl[tplName] = undefined
        } else {
            console.log(chalk.red('Template does not exist!'))
            process.exit()
        }
        
        // 写入template.json
        fs.writeFile(__dirname + '/../templates.json', JSON.stringify(config),     'utf-8', (err) => {
            if (err) console.log(err)
            console.log(chalk.green('Template deleted!'))
            console.log(chalk.grey('The last template list is: \n'))
            console.log(config)
            console.log('\n')
            process.exit()
        })
    })
}

罗列模板

建立list.js文件:

'use strict'
const config = require('../templates')

module.exports = () => {
     console.log(config.tpl)
     process.exit()
}

构建项目

现在来到我们最重要的部分——构建项目。同样的,在\command目录下新建一个叫做init.js的文件:

'use strict'
const exec = require('child_process').exec
const co = require('co')
const prompt = require('co-prompt')
const config = require('../templates')
const chalk = require('chalk')

module.exports = () => {
 co(function *() {
    // 处理用户输入
      let tplName = yield prompt('Template name: ')
      let projectName = yield prompt('Project name: ')
      let gitUrl
      let branch

    if (!config.tpl[tplName]) {
        console.log(chalk.red('\n × Template does not exit!'))
        process.exit()
    }
    gitUrl = config.tpl[tplName].url
    branch = config.tpl[tplName].branch

    // git命令,远程拉取项目并自定义项目名
    let cmdStr = `git clone ${gitUrl} ${projectName} && cd ${projectName} && git checkout ${branch}`

    console.log(chalk.white('\n Start generating...'))

    exec(cmdStr, (error, stdout, stderr) => {
      if (error) {
        console.log(error)
        process.exit()
      }
      console.log(chalk.green('\n √ Generation completed!'))
      console.log(`\n cd ${projectName} && npm install \n`)
      process.exit()
    })
  })
}

可以看到,这一部分代码也非常简单,关键的一句话是

let cmdStr = `git clone ${gitUrl} ${projectName} && cd ${projectName} && git checkout ${branch}`

它的作用正是从远程仓库克隆到自定义目录,并切换到对应的分支。熟悉git命令的同学应该明白,不熟悉的同学是时候补补课啦!

全局使用

为了可以全局使用,我们需要在package.json里面设置一下:

"bin": {
    "scion": "bin/scion"
  },

本地调试的时候,在根目录下执行

npm link

即可把scion命令绑定到全局,以后就可以直接以scion作为命令开头而无需敲入长长的node scion之类的命令了。

现在我们的脚手架工具已经搭建好了,一起来尝试一下吧!

使用测试

  • add | a 添加模版命令
    图片描述

  • init | i 生成项目命令
    图片描述

  • delete | d 删除模版命令 和 list | l 罗列模版命令
    图片描述

大功告成啦!现在我们的整个脚手架工具已经搭建完成了,以后只需要知道模板的git https地址和branch就可以不断地往SCION上面添加,团队协作的话只需要分享SCION的templates.json文件就可以了。

后记

看起来并不复杂的东西,实际从零开始搭建也是颇费了一番心思。最大的难题是在开始的时候并不懂得如何像npm init那样可以一步一步地处理用户输入,只懂得一条命令行把所有的参数都带上,这样的用户体验真的很不好。研究了vue-cliyoeman也没有找到相应的代码,只好不断地google,最后总算找到了一篇文章,可以用coco-prompt这两个工具实现,再一次膜拜无所不能的TJ大神,也希望能够有小伙伴告诉我vue-cli它们是怎么实现的。

这个脚手架只具备最基本的功能,还远远没有达到市面上同类产品的高度,在日后再慢慢填补吧,不管怎么说,完成SCION的过程中真的学习到了很多东西。

感谢你的阅读。我是Jrain,欢迎关注我的专栏,将不定期分享自己的学习体验,开发心得,搬运墙外的干货。下次见啦!

查看原文

赞 150 收藏 331 评论 22

ddvdd 关注了用户 · 2018-02-05

Xeira @xeira

关注 1359

认证与成就

  • 获得 290 次点赞
  • 获得 2 枚徽章 获得 0 枚金徽章, 获得 0 枚银徽章, 获得 2 枚铜徽章

擅长技能
编辑

开源项目 & 著作
编辑

(゚∀゚ )
暂时没有

注册于 2017-02-07
个人主页被 1.7k 人浏览