072018.7

写代码的时候,经常会有几个概念容易混淆,即update, set, put, patch。这几个单词在我们编程过程中经常会出现,我们自己在给方法取名字的时候,也常常不知道该用哪一个好。据一个例子,当你想要将用户修改好的个人信息保存到数据库时,应该用哪一个单词呢?现在我就来具体解释一下它们的使用场景。

set:将对象塞入到集合中,无论这个对象是否在集合中已经存在。这里有一个隐藏的风险,如果这个对象在集合中已经存在了,那么当你set的对象比原来的对象少了一些属性的时候,这些属性就丢失了。在这种情况下,set相当于replace。而如果这个对象本身不存在于集合中,那么set就相当于add了。

update:对传入的对象进行更新。首先,它要更新的内容虽然是对象,但你不确定它的个数;其次,它在更新时并不像set一样进行直接替换,而是对传入update中参数中规定的属性进行更新,因此,参数中未规定的属性保持不变;最后,它只是更新,不会附带add操作。update是一个比较高度抽象的词,因此,你实在不知道应该选用哪个词作为更新操作的时候,用update就行了。

put:和set非常接近。从词语本身看,set更多倾向于replace,put更多倾向于add。put的前提是在集合中已经存在一个位置(空位),而put操作准确的将参数对象放入这个位置。但是实际中,大部分规则下,put被赋予replace的功能。因此,在编程时,可以说put和set没有区别。

patch:和update非常接近,但仅对单个对象进行update,而update可以对一组对象进行更新。在restful中patch和put一样也被定义为更新操作。实际上,我认为patch是对但对象的update,而put被大部分人误解。restful中,大部分人将put定义为更新操作,即对但对象的update操作。但我认为,put既不是update,也不是add,而是这两者的整合,也就是上面说的set的逻辑(repalce/add)。但是patch非常明确,就是对单个对象的更新,因此,在restful设计中应该考虑用patch,而非put。

 

写方法的时候,我们常常会有两种方式,一种是set(key, value)的形式,另一种是set({ key: value })的形式。显然,第一种只能一次更新一个属性,而第二种可以同时更新多个属性。但是很明显,这里如果我们把set理解为上文阐述的意义,那么只能选择第一种形式,它代表将一个对象的某个属性整体替换为(如果不存在则添加)新内容。而第二种形式更适合patch,即我在知道我要更新哪一个对象时(常常通过url确定),我只传入我想要更新的属性名和值。但是在不知道我要更新哪个对象时,则不应该使用patch,而应该用update,并且要求在传入的对象中,提供明确的主键值。

18:02:00 已有0条回复
062018.7

简单package的简单构建脚本

在前段时间,我把之前写的几个package重新命名为hello开头的包,例如hello-storage, hello-events, hello-worker,这些包非常基础,不依赖任何第三方包,就是用基础的api来实现的,因此,代码非常简单。面对这种随即编写,随即打包发布的package,我采用最小化的方式进行发布。

一个包,在发布的时候,其实最重要的是考虑用户(开发者)使用这个包的使用场景,如果不考虑,直接给源码就行了,干嘛还要进行构建发布呢?所以,对于我而言,主要考虑两点:

  • 模块化
  • 代码兼容性

-

模块化是指用户可能在不同的模块规范下使用你的包,目前而言,有amd, cmd, commonjs, es6 module这几种,另外,还要考虑在浏览器中直接使用<script>标签引用脚本(没有模块化环境)。针对这点,我采用umd的方案,但是代码是自己写的,我的源码是es6 module,因此在导出的时候使用export default,但是在其他模块化规范下,并不需要default这样的接口,因此要直接export整个模块,因此,我的方案就是,对于要直接引用es6模块的用户,建议他import源码文件,而其他模块化规范下,引用dist文件:

// ES6
import HelloEvents from 'hello-events/src/hello-events'

// CommonJS
const HelloEvents = require('hello-events')

// 由于webpack在打包时能正确处理export default的问题,因此如果使用webpack,可以
import HelloEvents from 'hello-events'

对于开发者而言,这种解释应该是非常容易理解的。使用习惯上也不会造成困扰。那么怎么来实现这套方案呢?我在代码中采用umd的方案实现:

!function(root) {

// ES6代码转码之后放在这里

if (typeof define === 'function' && (define.cmd || define.amd)) { // amd | cmd
  define(function(require, exports, module) {
    module.exports = HelloEvents;
  });
}
else if (typeof module !== 'undefined' && module.exports) {
  module.exports = HelloEvents;
}
else {
  root.HelloEvents = HelloEvents;
}

} (this || window);

这得益于我以前写过omd.js。那怎么才能最终输出上面这个结构呢?我使用了gulp来做,并用上了我写的gulp-bufferify包来实现文件内容的修改。这样,当我使用babel把es6的代码转化为es5的代码之后,再对文件内容进行修改,就能得到上面这个结构了。

-

代码兼容性是指要考虑在不同浏览器版本中代码都能跑,主要是指一些es6新特性的降级,例如async函数。因为我写的这些pacakge的功能非常弱,不会涉及generator函数,也不会涉及symbol,因此其他大部分功能都可以用es5语法代替,并且找到对应的babel插件即可。

对于我而言,主要考虑到两个点:1.class语法;2.async/await语法;3. ...语法。这三个语法已经是我日常写作的必须品,因此在我的构建脚本里面,首先需要将这三个语法转化为es5语法。1和3使用babel-preset-env就可以自动解决,非常好用,2的话我使用了bebel-plugin-async-to-promises,它可以将async/await语法转换为promise,而preset-env默认使用bebel-plugin-transform-async-to-generator(将async/await语法转换为generator语法)和bebel-plugin-transform-regenerator(使用一个特殊的变量regeneratorRuntime来支持generator语法,这个变量需要你引入babel-polyfill),这两个插件和bebel-plugin-async-to-promises有冲突,因此要禁用默认选项,在.babelrc里面这样写:

{
  "presets": [
    ["env", { "exclude": ["transform-async-to-generator", "transform-regenerator"] }]
  ],
  "plugins": [
    "async-to-promises"
  ]
}

但是,其实这里对于开发者而言,是需要进行权衡的,比如说,我会在代码中随意使用array.filter, Object.assign等,这些新接口是es6新加入的,对于老版本的浏览器是不支持的。但是这些接口不会被babel转译,因此,我也不会特意在自己的代码中去实现这一块。之所以不自己在代码中实现这块,是因为这种接口很大程度上其他包也会用。对于使用包的开发者而言,他需要自己进行权衡,是不是要引入一个polyfill,如果他自己不想支持低版本的浏览器,就不用引入了。这样,可以保证我自己的包里面的代码是绝对干净,但是在大部分情况下是可以运行的。

但是这里有一个问题,就是为什么不把async/await也留给上层的开发者自己去决定。这是因为这个语法没有办法用polyfill,你不可能通过一个polyfill让一个不支持async语法的浏览器变得支持。这就是权衡,如果我在自己的所有包里面都把这两个语法的转译代码写进去,那么最后,上层开发者的应用里面,就会有重复代码,这也是没有办法的,如果webpack等打包工具更加智能,就可以将这些重复代码提升作用域来实现减少重复的效果。

-

上面讲完了我所关心的两个点之后,下面要出代码了。不过别急。我们再讨论一个问题,就是有没有必要任何包都用webpack?答案显而易见,我现在开始不大喜欢webpack,我越来越觉得webpack应该是一个应用层面进行打包时才用的上的工具,对于一个package而言,不应该进行打包,而是以原始的module文件间引用关系对外使用,只不过需要做上述两个点的处理。

好了,上代码:

// 安装对应的npm包
$ npm i -D babel-core babel-plugin-async-to-promises babel-preset-env gulp gulp-babel gulp-bufferify
// 构建脚本 build.js

var gulp = require('gulp')
var bufferify = require('gulp-bufferify').default
var babel = require('gulp-babel')

var namespace = 'HelloWorker' // 修改为自己的ClassName,一定要跟源码里面的class名对应
var entryfile = 'src/hello-worker.js' // 修改为自己的入口文件

gulp.src(entryfile)
  .pipe(babel())
  .pipe(bufferify(function(content) {

    content = content.replace(/Object\.defineProperty\(exports,[\s\S]+?\);/gm, '')
    content = content.replace(`exports.default = ${namespace};`, '')
    content = `
!function(root) {

${content}

if (typeof define === 'function' && (define.cmd || define.amd)) { // amd | cmd
  define(function(require, exports, module) {
    module.exports = ${namespace};
  });
}
else if (typeof module !== 'undefined' && module.exports) {
  module.exports = ${namespace};
}
else {
  root.${namespace} = ${namespace};
}

} (this || window);
    `
    content = content.trim()
    content += "\n"

    return content
  }))
  .pipe(gulp.dest('dist'))
// 进行构建
node build.js

这段构建脚本非常简单,稍微花点时间就读懂了。根据自己的实际情况进行修改,比如路径问题,比如namespace那个地方。总之对于我自己而言,非常简单的包都会采用这样的模式去做。

19:03:52 已有0条回复
022018.7

HTML5 File API

http://www.cnblogs.com/zichi/p/html5-file-api.html

092018.6

react navigation tab navigator with examples

https://www.youtube.com/watch?v=xqvB3yLy9Uc

一印度哥们的教程,努力看完了,最后还是没有解答我最重要的点……不过对于入门react native tab navigation还是比价有用。

062018.6

jsconfeu2018

022018.6

Awesome React Native

今天在想要用自己以前写的npm包时,看github,发现自己写的源码竟然没有上传到github上,真是让人意外。更意外的是,这个包已经发布好久了,竟然没有人发现这个问题,想想也是难过,自己写的这么好的东西,怎么就没人用呢?

00:40:01 已有0条回复
302018.5

Wired Elements 手绘风格UI组件库

272018.5

React Native 项目常用第三方组件汇总

我最喜欢的小键盘

前面我做过一期罗技键盘的开箱,说了一下我对它键盘布局的看法。但是,我觉得我小键盘仍然是我最喜欢的,我想说下我最喜欢的键盘。

键程

长键程,但是不要太硬,否则会比较累手指。

键布局

每一个键不能太小,要足够大,不需要思考有没有按到。一些关键键要大,比如esc、ctrl。方向键绝对不能像苹果自带的那种。不喜欢巧克力键,双飞燕那种就好。

每个键的位置,如下图:

比较理想的键盘布局

这是对我来说最理想的位置安排,而且这样键盘也不会太长,可以放进包里,可以把键盘稍微做厚一点点,多放几节电池,保证续航。如果能够在调整下,就更爽了:1.esc和左边ctrl放长一点;2.标记出windows和mac两种心跳下的不同键名;3.键帽改成长键程的普通帽子;4.fn换到空格键右边那个位置,或者直接把右边的context键替换掉,给左边的ctrl腾出空间加长,fn的也可以稍微加长一点;5.print screen键可以不要,留出空间给esc和delete加长。

额……大约应该就长这样子:

连接

罗技那款可以蓝牙、USB连接的不错,而且可以同时连多台设备。但是罗技那款有个问题,USB连接器不能插在键盘里面,也就不能随身携带,比较不爽,可以在键盘两侧的某个地方设计一个USB插槽,用来存放USB连接器,这样就可以随身携带。

我就像问,哪里可以定制键盘……

15:34:26 已有0条回复