富文本编辑器调研

考察点

  • 开源协议
  • 框架依赖
  • 组件库大小
  • 开发维护及社区状态
  • 基本功能及插件功能
  • 浏览器兼容性
  • 现有的问题
  • demo地址

开源协议介绍

6种开源协议比较如下

编辑器列表

  • TinyMce(付费)
  • Medium Editor
  • Draft.js
  • Froala(付费)
  • Ckeditor
  • Quill
  • summernote
  • ueditor

TinyMce

开源协议

LGPL 2.1 license
允许商业化,但不允许闭源

框架依赖

使用方式,引入库,初始化即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
tinymce.init({
selector: 'textarea',
height: 500,
theme: 'modern',
plugins: '123',
toolbar1: 'formatselect | bold italic strikethrough forecolor backcolor | link | alignleft aligncenter alignright alignjustify | numlist bullist outdent indent | removeformat',
image_advtab: true,
templates: [
{ title: 'Test template 1', content: 'Test 1' },
{ title: 'Test template 2', content: 'Test 2' }
],
content_css: [
'//fonts.googleapis.com/css?family=Lato:300,300i,400,400i',
'//www.tinymce.com/css/codepen.min.css'
]
});

组件库大小

js:418k
js地址

开发维护及社区状态

title 图片
star
代码提交频率
issue修复比例

基本功能及插件功能

  • 基本功能:加粗、斜体、下划线、链接、有序无序列表、插入图片、插入链接、undo、redo
    插件:
  • Pro版本:$33/M (office张贴、链接检查、@功能)
  • Developer版本:$10/M
重要性 功能(付费版) 支持情况
10 加粗、斜体、删除、有序、无序列表、redo/undo、H1-H6、下划线、引用、对齐方式 支持
10 插入删除链接/链接操作 支持
9 粘贴链接 支持
10 插入图片/图片操作(左右对齐、删除、大小) 支持
9 图片粘贴 支持
10 插入表格/表格操作 支持
10 表格粘贴 支持
9 自动列表 支持
9 粘贴word 支持
8 mention# 支持
9 hashtag# 不支持
8 emoji 不支持
8 行内toolbar 不支持
8 区块拖拽 支持
6 快捷键 支持
4 特殊字符 支持
5 全屏 支持
3 源码编辑 支持
2 字体、颜色 支持
placeholder 不支持
多实例 支持
插入时间 不支持
批注 不支持

浏览器兼容性

  • chrome+
  • Firefox+
  • Edge+
  • IE6+
  • safari+
    参考

demo地址

codepen demo

demo

官网

Medium Editor

开源协议

开源

框架依赖

引入js、css

1
2
3
4
5
6
<link rel="stylesheet" href="bower_components/medium-editor/dist/css/medium-editor.css">
<script src="bower_components/medium-editor/dist/js/medium-editor.js"></script>
<div class="editable"></div>
<script type="text/javascript">
var editor = new MediumEditor('.editable');
</script>

组件库大小

js:338k css:7.6k
css
js

开发维护及社区状态

title 图片
star
代码提交频率
issue修复比例

基本功能及插件功能

通过选中出现
基本功能:
加粗、斜体、下划线、链接、h1-h6、#功能
插件功能:
图片上传、表格、markdown、自动表格、@、#、自定义toolbar

重要性 功能 支持情况
10 加粗、斜体、删除、有序、无序列表、redo/undo、H1-H6、下划线、引用、对齐方式 支持
10 插入删除链接/链接操作 不支持
9 粘贴链接 不支持
10 插入图片/图片操作(左右对齐、删除、大小) 不支持
9 图片粘贴 不支持
10 插入表格/表格操作 不支持
10 表格粘贴 不支持
9 自动列表 不支持
9 粘贴word 不支持
8 mention# 不支持
9 hashtag# 不支持
8 emoji 不支持
8 行内toolbar 支持
8 区块拖拽 不支持
6 快捷键 支持
4 特殊字符 不支持
5 全屏 不支持
3 源码编辑 不支持
2 字体、颜色 不支持
placeholder 支持
多实例 支持
插入时间 不支持
批注 不支持

浏览器兼容性

  • chrome+
  • Firefox+
  • Edge latest
  • IE9+
  • safari 8+

参考

demo地址

demo

官网

Draft.js

开源协议

BSD License 最开放的协议

框架依赖

React

组件库大小

js:481kb
js

开发维护及社区状态

最近发布plugin2.0

title 图片
star
代码提交频率
issue解决比率

基本功能及插件功能

官方插件比较少,只有简单的@、Emoji、Image、Video、Sticker、#、行内操作栏、侧边操作栏、固定操作栏、undo、计数、链接、对齐、drag、resize插件
社区插件较多

社区很强大,插件很丰富 插件

重要性 功能 支持情况
10 加粗、斜体、删除、有序、无序列表、redo/undo、H1-H6、下划线、引用、对齐方式 支持
10 插入删除链接/链接操作 插件支持
9 粘贴链接 不支持
10 插入图片/图片操作(左右对齐、删除、大小) 插件支持
9 图片粘贴 不支持
10 插入表格/表格操作 插件支持(弱)
10 表格粘贴 不支持
9 自动列表 插件支持
9 粘贴word 不支持
8 mention# 插件支持
9 hashtag# 插件支持
8 emoji 插件支持
8 行内toolbar 插件支持
8 区块拖拽 插件支持
6 快捷键 支持
4 特殊字符 不支持
5 全屏 插件支持
3 源码编辑 不支持
2 字体、颜色 插件支持
placeholder 支持
多实例 支持
插入时间 不支持
批注 不支持

浏览器兼容性

手机端基本功能能用
正在向手机端支持靠近,但是没有官方公布支持手机

  • chrome+
  • Firefox+
  • Edge latest+
  • IE9+
  • safari latest+
  • ios safari not fully supported
  • chrome for android

现有的问题

  • 状态更新有延迟
  • 用户自定义的osx键绑定
  • 浏览器插件,例如语法检查器可能会改变默认的样式,这会导致状态不同步
  • IME 和IE11 bug
  • 现有的一些高优bug

完整版本参考:
https://github.com/facebook/draft-js/blob/master/docs/Advanced-Topics-Issues-and-Pitfalls.md

demo地址

demo

image

Froala

开源协议

非开源、需购买

框架依赖

vue版本插件

组件库大小

基本js:185kb
还有一堆插件
js

开发维护及社区状态

title 图片
star
代码更新频率
issue解决比率

基本功能及插件功能

插件地址

重要性 功能 支持情况
10 加粗、斜体、删除、有序、无序列表、redo/undo、H1-H6、下划线、引用、对齐方式 支持
10 插入删除链接/链接操作 支持
9 粘贴链接 支持
10 插入图片/图片操作(左右对齐、删除、大小) 支持
9 图片粘贴 支持
10 插入表格/表格操作 支持
10 表格粘贴 支持
9 自动列表 不支持
9 粘贴word 支持
8 mention# 不支持
9 hashtag# 不支持
8 emoji 支持
8 行内toolbar 不支持
8 区块拖拽 支持
6 快捷键 支持
4 特殊字符 支持
5 全屏 支持
3 源码编辑 不支持
2 字体、颜色 支持
placeholder 支持
多实例 支持
插入时间 不支持
批注 不支持

浏览器兼容性

  • Chrome
  • Edge
  • Firefox
  • Safari
  • Opera
  • Internet Explorer 10+
  • Safari iOS
  • Chrome, Firefox and Default Browser Android

现有的问题

  • 删除卡
  • 连续输入响应慢
  • 失焦

demo地址

demo

Ckeditor 4.0和5.0

开源协议

MIT license

框架依赖

可直接使用

1
2
3
4
5
6
CKEDITOR.editorConfig = function( config ) {
config.language = 'es';
config.uiColor = '#F7B42C';
config.height = 300;
config.toolbarCanCollapse = true;
};

组件库大小

js大小:549kb
js

开发维护及社区状态

title 图片
star
代码提交频率
issue解决比率

基本功能及插件功能

基本功能:

重要性 功能 支持情况
10 加粗、斜体、删除、有序、无序列表、redo/undo、H1-H6、下划线、引用、对齐方式 支持
10 插入删除链接/链接操作 支持
9 粘贴链接 支持
10 插入图片/图片操作(左右对齐、删除、大小) 支持
9 图片粘贴 不支持
10 插入表格/表格操作 支持
10 表格粘贴 支持
9 自动列表 不支持
9 粘贴word 支持,图片没有自动上传
8 mention# 不支持
9 hashtag# 不支持
6 快捷键 支持
4 特殊字符 支持
5 全屏 支持
3 源码编辑 支持
2 字体、颜色 不支持
placeholder 不支持
多实例 支持
插入时间 不支持
批注 不支持

浏览器兼容性

详细参考

  • chrome window/mac/linux最新版
  • Firefox window/mac/linux最新版
  • Edge window 最新版
  • IE11+
  • safari mac最新版
  • 最新版ios safari
  • 最新版android chrome
  • 最新版windows surface IE

demo地址

demo

Quill

开源协议

BSD 3-clause

框架依赖

1
2
3
4
5
6
7
8
9
10
<!-- Include the Quill library -->
<script src="https://cdn.quilljs.com/1.0.0/quill.js"></script>
<!-- Initialize Quill editor -->
<script>
var editor = new Quill('#editor', {
modules: { toolbar: '#toolbar' },
theme: 'snow'
});
</script>

组件库大小

js:401kb

开发维护及社区状态

title 图片
star
提交频率
issue解决比率

基本功能及插件功能

重要性 功能 支持情况
10 加粗、斜体、删除、有序、无序列表、redo/undo、H1-H6、下划线、引用、对齐方式 支持
10 插入删除链接/链接操作 支持
9 粘贴链接 支持
10 插入图片/图片操作(左右对齐、删除、大小) 支持
9 图片粘贴 不支持
10 插入表格/表格操作 不支持
10 表格粘贴 不支持
9 自动列表 支持
9 粘贴word 图片不显示
8 mention# 不支持
9 hashtag# 不支持
6 快捷键 支持
4 特殊字符 不支持
5 全屏 不支持
3 源码编辑 不支持
2 字体、颜色 字体支持、颜色不支持
placeholder 不支持
多实例 支持
插入时间 不支持
批注 不支持

浏览器兼容性

  • android 5.1/6.0
  • FF 44+
  • chrome 47 +
  • IE 11+
  • iphone 10.10
  • IE Edge
  • Safari 10.11

demo地址

demo

summernote

开源协议

MIT License

框架依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
<!-- include libraries(jQuery, bootstrap) -->
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.5/css/bootstrap.min.css" />
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.5/js/bootstrap.min.js"></script>
<!-- include summernote css/js-->
<link href="summernote.css" rel="stylesheet">
<script src="summernote.js"></script>
<script type="text/javascript">
$(document).ready(function() {
$('#summernote').summernote();
});
</script>

组件库大小

js : 232kb

开发维护及社区状态

title 图片
star
提交频率
issue解决比率

基本功能及插件功能

重要性 功能 支持情况
10 加粗、斜体、删除、有序、无序列表、redo/undo、H1-H6、下划线、引用、对齐方式 支持
10 插入删除链接/链接操作 支持
9 粘贴链接 支持
10 插入图片/图片操作(左右对齐、删除、大小) 支持
9 图片粘贴 支持
10 插入表格/表格操作 支持
10 表格粘贴 支持
9 自动列表 不支持
9 粘贴word 支持,图片无法粘贴
8 mention# 不支持
9 hashtag# 不支持
6 快捷键 支持
4 特殊字符 不支持
5 全屏 支持
3 源码编辑 支持
2 字体、颜色 支持
placeholder 支持
多实例 支持
插入时间 不支持
批注 不支持

浏览器兼容性

  • FF 56+
  • chrome 62+
  • IE 9+
  • Edge 15
  • Safari 8+

demo地址

demo

Ueditor

开源协议

MIT license

开发维护及社区状态

title 图片
star
提交频率
issue解决比率

基本功能及插件功能

重要性 功能 支持情况
10 加粗、斜体、删除、有序、无序列表、redo/undo、H1-H6、下划线、引用、对齐方式 支持
10 插入删除链接/链接操作 支持
9 粘贴链接 支持
10 插入图片/图片操作(左右对齐、删除、大小) 支持
9 图片粘贴 支持
10 插入表格/表格操作 支持
10 表格粘贴 支持
9 自动列表 支持
9 粘贴word 支持,图片没有自动上传
8 mention# 不支持
9 hashtag# 不支持
6 快捷键 支持
4 特殊字符 支持
5 全屏 支持
3 源码编辑 支持
2 字体、颜色 字体支持
placeholder 不支持
多实例 支持
插入时间 不支持
批注 不支持

浏览器兼容性

  • FF
  • chrome
  • IE 8+
  • Edge
  • Safari

demo地址

demo

自己造一个editor

本地demo
hi话题功能造编辑器经验
通过contenteditable、execCommand
问题列表
问题的冰山一角:

  • 如果加粗想要用strong替换b
    • 使用observers,当修改后将html规范化后放入,这种情况要考虑选区的保存、还有undo操作
  • 回车创建一个新的行,而不是上一次的默认样式
    • 重新实现execCommand(算法不难,但是浏览器表现都不一样,例如FF有多个选区,实现undo操作)

实际可能:

  • delay 几个月
  • 不兼容其他浏览器,有些bug可能是浏览器8年的老bug
  • 有太多的情况需要考虑到
  • 需要支持enter键,同时出来地狱般的粘贴
  • 需要处理(Blink/Webkit)回退键、删除键
  • 图片支持开发
  • selection 具有方向性
  • 处理selection系统
  • 处理打字、键盘事件、插入特点字符到编辑器
  • 处理上下左右导航时的光标

结论

name 总结 细节
TinyMce 功能比较强大,要花钱 IE6+
Medium Editor 功能太过简单,需要花大量时间扩展 IE9+
Draft.js 2016年React推出,社区很丰富,插件很多,还支持手机端,但是基于React, IE11+,支持手机端
Froala 不开源,要花钱,IE10+ ,支持手机端
Ckeditor 功能算开源中比较强大的,star比较少,IE11+,支持手机端
Quill star多,功能在开源中还算可以的,IE11+,支持手机端
summernote 功能算开源中比较多的,比Quill多点,IE9+,star比Quill少些
ueditor 功能比较强大,bug不少,浏览器兼容性不错
  • 如果现成框架为reactjs,用draftjs,社区比较强大
  • 如果现成框架为vue,建议用Quill、summernote、Ckeditor,如果需要支持到IE9+,那么只有summernote和medium Editor是官方支持的
  • 如果是需要支持手机端:可以用draftjs、Quill

Vue响应式

首先看端代码如何将一个普通对象转为可以监控到变化的对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
function touch(parent, key, obj) {
if (typeof obj === 'object') {
for (let i in obj) {
let result = touch(obj, i, obj[i]);
if (typeof obj[i] !== 'object') {} else {
let curVal = obj[i];
Object.defineProperty(obj, i, {
get: function() {
console.log('get', i);
return curVal;
},
set: function(value) {
console.log('set', i, value);
curVal = value;
}
});
}
}
} else {
let currentValue = obj;
Object.defineProperty(parent, key, {
get: function() {
console.log('get', key);
return currentValue;
},
set: function(value) {
console.log('set', key, value);
currentValue = value;
}
});
return parent;
}
}
var objtest = {
a: 1,
b: {
c: 2,
d: 3,
e:{
f:4,
g:5
}
}
};
touch(window, 'getObj', objtest);
objtest.b.c='new';

CORS

原文
Cross-Origin Resource Sharing (CORS)
让user agent 获取不同源的资源,通过附加在http头来实现的机制。

cross-origin请求如下

1
2
<!-- page in http://domain-a.com -->
<img src="http://domain-b.com/image.jpg">

现如今很多网页从CDN加载css、imgs、script

由于安全原因,浏览器限制js脚本里面的cross-origin的http请求,例如XMLHttpRequest和Fetch API遵循同源策略,这意味着网页使用这些APIS只能访问同源的资源,除非使用CORS的header

哪些请求要使用CORS

  • 使用跨域方式调用XMLHttpRequest或者Fetch APIS
  • Web Fonts(对于@font-face使用跨域资源de )

    1
    2
    3
    4
    5
    @font-face {
    font-family: "Open Sans";
    src: url("/fonts/OpenSans-Regular-webfont.woff2") format("woff2"),
    url("/fonts/OpenSans-Regular-webfont.woff") format("woff");
    }
  • WebGL textures

  • Canvas 使用drawImage来draw Images/Video frames
  • stylesheets(for CSSOM access)
  • scripts(for unmuted exceptions)

简单请求

简单请求不会触发preflighted request
简要介绍下preflight request

ECMA 5规范

[ECMA5原文]
(https://www.ecma-international.org/ecma-262/5.1/#sec-8.12.8)
ECMA6原文

第8章类型

第9章类型转换相关

ToNumber抽象操作 ToNumber(input)

  • Undefined 返回NaN
  • Null 返回+0
  • Boolean true是1,false是+0
  • Number 不变
  • String 如果input不能使用StringNumbericLiteral解析,则返回NaN
    • StringNumbericLiteral包含以下合法字符
    • 十六进制表示 0x HexDigit或者0X hexDigit,hexDigit包含(0123456789abcdefABCDEF)
    • 有标识的整数 “+” “-“
    • 指数 “e”、”E”
    • 小数点 “.”
  • Object 返回ToNumber(ToPrimitive(input,Number))

ToString抽象操作 ToString(input)

  • Undefined 返回”undefined”
  • Null 返回’null’
  • Boolean 返回’true’或则’false’
  • Number 如下
    • NaN 返回’NaN’
    • +0或者-0 返回’0’
    • 如果input小于0, 返回’-‘+ToString(-input)
    • 如果input是无限的,则返回’Infinity’
    • 否则转实际的值为String
  • Object ToString(ToPrimitve(input,String))

ToBoolean抽象操作 ToBoolean(input)

  • Undefined 返回false
  • Null 返回 false
  • Boolean 不变
  • Number +0,-0,NaN返回false,其他返回true
  • String 空字符串返回false,否则返回true
  • Object 返回true

ToPrimitive操作(隐式装箱)

  • Undefined 不变
  • Null 不变
  • Boolean 不变
  • Number 不变
  • String 不变
  • Object 通过调用内部的DefaultValue方法得到,可以传递PreferredType参数(默认是default,还有string、number),具体如下,
    • 如果有toString方法,并且toString返回的是一个primitive值,则直接返回这个值
    • 如果有valueOf方法,并且valueOf返回的是一个值,则直接返回这个值
    • 报TypeError错误
      注意根据hint值不一样,下面的toString方法和valueOf方法顺序相反,如果没有hint,则hint默认是Number,另外如果数组对象实现了自己的DefaultValue的方法,则必须保证return的是primitive值
      另外注意Object可以自行定义toPrimitive方法(ES6补充),如下demo

ToNumber,ToString对对象转换时都会调用ToPrimitve抽象操作,ToBoolean不会调用,直接返回true

举个例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// An object with Symbol.toPrimitive property.
var obj2 = {
[Symbol.toPrimitive](hint) {
if (hint == 'number') {
return 10;
}
if (hint == 'string') {
return 'hello';
}
return true;
}
};
console.log(+obj2); // 10 -- hint is "number"
console.log(`${obj2}`); // "hello" -- hint is "string"
console.log(obj2 + ''); // "true" -- hint is "default"

ToObject抽象操作

  • Undefined 返回TypeError
  • Null 返回TypeError
  • Boolean 创建一个Boolean对象,并设置内部属性PrimitiveValue为该值
  • Number 创建一个Number对象,并设置内部属性PrimitiveValue为改值
  • String 创建一个String对象,并设置内部属性PrimitiveValue为改值
  • Object 不变

第11章 表达式

11.4 一元操作符delete、void、typeof、++、–、+、-、~、!

typeof操作

  • Undefined 返回’undefined’
  • Null 返回’object’
  • Boolean 返回 ‘boolean’
  • Number 返回’number’
  • String 返回’string’
  • Object (native and does not implement [[Call]]) 返回’object’
  • Object (native or host and does implement [[Call]]) 返回’function’
  • Object (host and does not implement [[Call]]) 看实现

名词解释

  • native object(ECMAScript定义实现的,例如Object、Date、Math、parseInt、eval等)
  • host object(宿主环境实现的,例如window、document、location、history、setTimeout)
1
2
3
4
console.log(typeof Function); // function
console.log(Object.constructor);
console.log(Function.constructor);
console.log(typeof Function.prototype); // Function

11.5 多元操作符*、/、%

11.6 加法操作符 +、-

11.7 位操作 <<、>>、>>>

11.8 关系操作 <、>、<=、>=

11.9 相等操作 ==、!=、===、!==

抽象相等比较算法(x == y)

  • 如果类型相等

    • 如果x是undefined return true
    • 如果x是null return true
    • 如果是Number
      • NaN != NaN
      • x和y的value相等,return true
      • +0 == -0
      • return false
    • 如果是String
      • 如果x和y是一样的长度和一样的字符串,return true,否则返回false
    • 如果是Boolean
      • 值一样true,否则false
    • 如果Oobject
      • 同一个引用return true,否则返回false
  • x或者y分别是null和undefined,return true

  • x、y分别是number和string类型,将string类型转为number类型
  • x、y中有一个boolean类型,则首先将boolean转为字符串再递归执行==,ToNumber(x) == y
  • 如果x是String、Number,y是object,则执行x == ToPrimitive(y)
  • 反之亦然
  • return false

抽象不等操作 x != y

  • 使r = x==y,如果r是true返回false,否则返回true

严格相等操作 x===y

  • 如果类型不等直接返回false
  • 如果是Undefined、Null类型,返回true
  • 如果是Number类型,如果有NaN 返回false,+0===-0,相等返回true,其他返回false
  • 如果是String类型,x和y完全相等返回true,否则返回false
  • 如果是Boolean,x和y一样返回true,否则返回false
  • 如果是引用类型,x和y引用同一个object返回true,否则返回false

严格不相等操作 x!==y

  • 同上操作后,最后执行相反操作!(x===y)

redux源码分析

主要分为以下几块

  • createStore
  • combineReducers
  • bindActionCreators
  • applyMiddleware
  • compose
  • utils
createStore

createStore 接受3个参数reducer、preloadedState(初始状态)、enhancer(store增强器,只能用applyMiddleWare方法来生成)

默认执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// ...
// createStore构建reducer的
enhancer(createStore)(reducer, preloadedState)
// ...
dispatch({
type: '@@redux/INIT'
})
// ...
return {
dispatch, /* 派发action,执行reducer,执行subscribe的所有listener */
subscribe, /* 订阅事件,返回unsubscribe函数,取消当前的订阅 */
getState, /* 获取当前状态 */
replaceReducer, /* 替换reducer,dispatch init */
}
applyMiddleWare

返回一个函数,封装middleware中间件。
这个函数接受createStore参数,返回一个新store,主要是改写dispatch方法
源码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
export default function applyMiddleware(...middlewares) {
return (createStore) => (reducer, preloadedState, enhancer) => {
var store = createStore(reducer, preloadedState, enhancer)
var dispatch = store.dispatch
var chain = []
// 暴露两个方法给外部,例如redux-thunk就是接受这两个参数
var middlewareAPI = {
getState: store.getState,
dispatch: (action) => dispatch(action)
}
chain = middlewares.map(middleware => middleware(middlewareAPI))
dispatch = compose(...chain)(store.dispatch)
return {
...store,
dispatch
}
}
}

redux-thunk
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function createThunkMiddleware(extraArgument) {
return function(dispatch, getState){
return function(next){
return function(action){
if (typeof action === 'function') {
return action(dispatch, getState, extraArgument);
}
return next(action);
}
}
}
}
const thunk = createThunkMiddleware();
thunk.withExtraArgument = createThunkMiddleware;
export default thunk;
combineReducers

combineReducers接受一个参数:reducers,数组类型
返回一个新的reducer

新的reducer执行时会依次取之前定义的每个reducer并将返回值付给分别的相应的state

bindActionCreators

将action creater转为dispatch action(react-redux中的connect就使用了该函数)
bindActionCreators接受两个参数:actionCreators,dispatch

compose(…functions)

源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
function compose() {
for (var _len = arguments.length, funcs = Array(_len), _key = 0; _key < _len; _key++) {
funcs[_key] = arguments[_key];
}
if (funcs.length === 0) {
return function (arg) {
return arg;
};
}
if (funcs.length === 1) {
return funcs[0];
}
var last = funcs[funcs.length - 1];
var rest = funcs.slice(0, -1);
return function () {
// composed表示上一次执行的结果,f表示当前值
return rest.reduceRight(function (composed, f) {
return f(composed);
}, last.apply(undefined, arguments));
};
}

从右到左组合多个函数,还有一个与之相对的方法pipe,在Lodash中叫做flow,他们的区别在于flow是从左到右执行

1
2
3
4
// compose简化写法
const compose = (...fns) => x => fns.reduceRight((v, f) => f(v), x);
// pipe简化写法
const pipe = (...fns) => x => fns.reduce((v, f) => f(v), x);

关于compose的一个实用技巧,可以用来trace函数运行的过程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const trace = curry((label, x) => {
console.log(`== ${ label }: ${ x }`);
return x;
});
const toSlug = pipe(
trace('input'),
split(' '),
map(toLowerCase),
trace('after map'),
join('-'),
encodeURIComponent
);
console.log(toSlug('JS Cheerleader'));
// '== input: JS Cheerleader'
// '== after map: js,cheerleader'
// 'js-cheerleader'

函数式编程中的方法,当需要吧多个store增强器依次执行时,会用到它

如下

1
2
3
4
5
6
7
8
9
// compose 对一个参数,依次从右向左执行函数,并且讲上一个函数的输出作为下一个函数的输入,有点类似于shell里面的管道
var compose = (fn1, fn2) => (arg) => fn1(fn2(arg));
var fn1 = arr => arr.concat(5);
var fn2 = arr => arr.concat(6);
var a=[1,2,3]
var b = compose(fn1,fn2);
var c=b(a);
console.log(c); //[1,2,3,6,5]

一般使用如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import makeRootReducer from './reducers';
import thunk from 'redux-thunk'
const middleware = [thunk/*,loggerMiddleware*/]
const enhancers = []
// ...
const store = createStore(
makeRootReducer(), // reducer
initialState, // 初始状态
compose( //enhancer
applyMiddleware(...middleware),
...enhancers
)
)
// ... createStore中
enhancer(createStore)(reducer, preloadedState)

资源检索

(译文)如何实现virtual dom

创建虚拟dom需要知道两件事。你不需要看深入研究React的源码。或者其它虚拟dom的源码。他们都非常繁琐也复杂。实际上虚拟dom只需要50行以内的代码就可以实现!!!

这里有两个概念:

  • 虚拟dom是真实dom的一种呈现方式
  • 当我们改虚拟dom树,我们得到一个新的dom树,有一个算法会查找新旧dom树的差别,并且在实际dom上做最小的改变

    Read More

资源检索

问题 地址
outlook对css和html支持情况 https://www.campaignmonitor.com/css/
outlook对css和html支持情况 https://msdn.microsoft.com/en-us/library/aa338201(v=office.12).aspx
ES5/ES6/ES7兼容性 http://kangax.github.io/compat-table/es5/
fildler的各种状态 http://docs.telerik.com/fiddler/KnowledgeBase/UIGuide
内存泄漏 https://developers.google.com/web/tools/chrome-devtools/memory-problems/?hl=zh-cn
html规范 https://html.spec.whatwg.org/
链接数限制 http://www.browserscope.org/?category=network&v=top
eventLoop模型 https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop

Read More