huangsy / blog Goto Github PK
View Code? Open in Web Editor NEWBlog
Blog
git config —global push.default current
git branch —set-upstream-to=origin/daily/0.0.3 daily/0.0.3
插件的定义是一种遵循一定规范的应用程序接口编写出来的程序。
tween: {
easeInQuad: function(pos){
return Math.pow(pos, 2);
},
easeOutQuad: function(pos){
return -(Math.pow((pos-1), 2) -1);
},
easeInOutQuad: function(pos){
if ((pos/=0.5) < 1) return 0.5*Math.pow(pos,2);
return -0.5 * ((pos-=2)*pos - 2);
},
easeInCubic: function(pos){
return Math.pow(pos, 3);
},
easeOutCubic: function(pos){
return (Math.pow((pos-1), 3) +1);
},
easeInOutCubic: function(pos){
if ((pos/=0.5) < 1) return 0.5*Math.pow(pos,3);
return 0.5 * (Math.pow((pos-2),3) + 2);
},
easeInQuart: function(pos){
return Math.pow(pos, 4);
},
easeOutQuart: function(pos){
return -(Math.pow((pos-1), 4) -1)
},
easeInOutQuart: function(pos){
if ((pos/=0.5) < 1) return 0.5*Math.pow(pos,4);
return -0.5 * ((pos-=2)*Math.pow(pos,3) - 2);
},
easeInQuint: function(pos){
return Math.pow(pos, 5);
},
easeOutQuint: function(pos){
return (Math.pow((pos-1), 5) +1);
},
easeInOutQuint: function(pos){
if ((pos/=0.5) < 1) return 0.5*Math.pow(pos,5);
return 0.5 * (Math.pow((pos-2),5) + 2);
},
easeInSine: function(pos){
return -Math.cos(pos * (Math.PI/2)) + 1;
},
easeOutSine: function(pos){
return Math.sin(pos * (Math.PI/2));
},
easeInOutSine: function(pos){
return (-.5 * (Math.cos(Math.PI*pos) -1));
},
easeInExpo: function(pos){
return (pos==0) ? 0 : Math.pow(2, 10 * (pos - 1));
},
easeOutExpo: function(pos){
return (pos==1) ? 1 : -Math.pow(2, -10 * pos) + 1;
},
easeInOutExpo: function(pos){
if(pos==0) return 0;
if(pos==1) return 1;
if((pos/=0.5) < 1) return 0.5 * Math.pow(2,10 * (pos-1));
return 0.5 * (-Math.pow(2, -10 * --pos) + 2);
},
easeInCirc: function(pos){
return -(Math.sqrt(1 - (pos*pos)) - 1);
},
easeOutCirc: function(pos){
return Math.sqrt(1 - Math.pow((pos-1), 2))
},
easeInOutCirc: function(pos){
if((pos/=0.5) < 1) return -0.5 * (Math.sqrt(1 - pos*pos) - 1);
return 0.5 * (Math.sqrt(1 - (pos-=2)*pos) + 1);
},
easeOutBounce: function(pos){
if ((pos) < (1/2.75)) {
return (7.5625*pos*pos);
} else if (pos < (2/2.75)) {
return (7.5625*(pos-=(1.5/2.75))*pos + .75);
} else if (pos < (2.5/2.75)) {
return (7.5625*(pos-=(2.25/2.75))*pos + .9375);
} else {
return (7.5625*(pos-=(2.625/2.75))*pos + .984375);
}
},
easeInBack: function(pos){
var s = 1.70158;
return (pos)*pos*((s+1)*pos - s);
},
easeOutBack: function(pos){
var s = 1.70158;
return (pos=pos-1)*pos*((s+1)*pos + s) + 1;
},
easeInOutBack: function(pos){
var s = 1.70158;
if((pos/=0.5) < 1) return 0.5*(pos*pos*(((s*=(1.525))+1)*pos -s));
return 0.5*((pos-=2)*pos*(((s*=(1.525))+1)*pos +s) +2);
},
elastic: function(pos) {
return -1 * Math.pow(4,-8*pos) * Math.sin((pos*6-1)*(2*Math.PI)/2) + 1;
},
swingFromTo: function(pos) {
var s = 1.70158;
return ((pos/=0.5) < 1) ? 0.5*(pos*pos*(((s*=(1.525))+1)*pos - s)) :
0.5*((pos-=2)*pos*(((s*=(1.525))+1)*pos + s) + 2);
},
swingFrom: function(pos) {
var s = 1.70158;
return pos*pos*((s+1)*pos - s);
},
swingTo: function(pos) {
var s = 1.70158;
return (pos-=1)*pos*((s+1)*pos + s) + 1;
},
bounce: function(pos) {
if (pos < (1/2.75)) {
return (7.5625*pos*pos);
} else if (pos < (2/2.75)) {
return (7.5625*(pos-=(1.5/2.75))*pos + .75);
} else if (pos < (2.5/2.75)) {
return (7.5625*(pos-=(2.25/2.75))*pos + .9375);
} else {
return (7.5625*(pos-=(2.625/2.75))*pos + .984375);
}
},
bouncePast: function(pos) {
if (pos < (1/2.75)) {
return (7.5625*pos*pos);
} else if (pos < (2/2.75)) {
return 2 - (7.5625*(pos-=(1.5/2.75))*pos + .75);
} else if (pos < (2.5/2.75)) {
return 2 - (7.5625*(pos-=(2.25/2.75))*pos + .9375);
} else {
return 2 - (7.5625*(pos-=(2.625/2.75))*pos + .984375);
}
},
easeFromTo: function(pos) {
if ((pos/=0.5) < 1) return 0.5*Math.pow(pos,4);
return -0.5 * ((pos-=2)*Math.pow(pos,3) - 2);
},
easeFrom: function(pos) {
return Math.pow(pos,4);
},
easeTo: function(pos) {
return Math.pow(pos,0.25);
},
linear: function(pos) {
return pos
},
sinusoidal: function(pos) {
return (-Math.cos(pos*Math.PI)/2) + 0.5;
},
reverse: function(pos) {
return 1 - pos;
},
mirror: function(pos, transition) {
transition = transition || tween.sinusoidal;
if(pos<0.5)
return transition(pos*2);
else
return transition(1-(pos-0.5)*2);
},
flicker: function(pos) {
var pos = pos + (Math.random()-0.5)/5;
return tween.sinusoidal(pos < 0 ? 0 : pos > 1 ? 1 : pos);
},
wobble: function(pos) {
return (-Math.cos(pos*Math.PI*(9*pos))/2) + 0.5;
},
pulse: function(pos, pulses) {
return (-Math.cos((pos*((pulses||5)-.5)*2)*Math.PI)/2) + .5;
},
blink: function(pos, blinks) {
return Math.round(pos*(blinks||5)) % 2;
},
spring: function(pos) {
return 1 - (Math.cos(pos * 4.5 * Math.PI) * Math.exp(-pos * 6));
},
none: function(pos){
return 0
},
full: function(pos){
return 1
}
}
IE8下不支持window.onmousewheel,但document.onmousewheel是支持的,document的dom event比window多。从定义来看,window指的是整个浏览器窗口,document是window的一个属性,指的是整个页面文档,所以一般对文档绑定事件,只要绑在document下即可。
How WebKit Works:
https://docs.google.com/presentation/d/1ZRIQbUKw9Tf077odCh66OrrwRIVNLvI_nhLm2Gi__F0/embed?start=false&loop=false&delayms=3000#slide=id.p
The WebKit Open Source Project:
http://www.webkit.org/coding/technical-articles.html
pod install --verbose --no-repo-update
pod update --verbose --no-repo-update
http://zhuanlan.zhihu.com/FrontendMagazine/19996445
DOM diff
http://calendar.perfplanet.com/2013/diff/
基于Flux的前端系统实现
http://www.react-china.org/t/flux/615
webpack 提供了丰富的插件系统,并且支持自定义插件,当 webpack 提供的插件无法满足需求的时候,就需要自定义一个 webpack 插件。要想自定义一个 webpack 插件,首先需要了解 webpack 的生命周期。
将 webpack.config.js 中的配置项保存到 webpack 的编译器 compiler 中。
根据配置项初始化部分内置插件,按照 webpack.config.js 中定义的 plugins 数组的先后顺序初始化插件。
创建编译器实例 compilation,对代码进行编译。
编译结束后,将编译后的代码生成到配置项中设置的目录中。
为什么要了解生命周期呢?插件的实质就是在 webpack 编译的不同阶段绑定一些事件。
什么是钩子?钩子其实就是生命周期中的一个个里程点,比如编译开始会触发 "before-compile" 的钩子,编译结束会触发 "after-compile" 的钩子。webpack 的钩子非常多,所有的钩子类型及执行顺序可以参考 Event Hook 的文档。下面举几个简单的例子:
名称 | 描述 | 参数 | 类型 |
---|---|---|---|
before-compile | 编译开始前,生成编译器需要的参数 | compilation 的参数 | 异步 |
compilation | 编译器实例创建成功 | compilation | 同步 |
make | 开始编译 | compilation | 并行 |
after-compile | 编译结束 | compilation | 异步 |
emit | 准备生成文件到输出的目录 | compilation | 异步 |
after-emit | 输出目录结束 | compilation | 异步 |
钩子的类型大致可以分为同步、瀑布流和异步三种。
顾名思义,相同的钩子按照绑定的顺序依次执行。
瀑布流类型是同步的变种,前一个钩子的返回值会作为后一个钩子的参数。
异步类型则是按照钩子的绑定顺序同时执行。
以上是在自定义 webpack 插件前必须了解的基础知识,下面就可以开始自定义 webpack 插件了。
class MyCustomPlugin {
apply(compiler) {
...
}
}
插件的结构很简单,只需要实现一个 apply 方法即可,apply 方法会在初始化插件的时候执行,自定义插件中需要做的事情就是对钩子进行绑定事件,比如我要统计编译过程总共花了多长时间:
class MyCustomPlugin {
apply(compiler) {
var start = 0;
compiler.plugin('before-compile', (compilation, callback) => {
// 编译开始前记录时间
start = +new Date
// callback 必须调用,否则不会往下执行
callback();
});
compiler.plugin('after-compile', (compilation, callback) => {
var time = +new Date - start;
console.log(`编译过程总共用时${time}`);
});
}
}
compiler.plugin 可以理解为事件绑定中的 on,钩子则是在不同的阶段 trigger 对应的事件。最后只要将插件导出,在 webpack 的配置文件中就能使用了。
// MyCustomPlugin.js
class MyCustomPlugin {
...
}
module.exports = MyCustomPlugin
// webpack.config.js
var MuCustomPlugin = require('./plugins/MyCustomPlugin');
module.exports = {
...
plugins: [
new MyCustomPlugin();
]
}
直接看代码:
var obj = {
arr: [],
get: function(){ console.log(this.arr); },
set: function(arr){ this.arr = arr; }
}
var arr = [1,2,3];
obj.set(arr);
obj.get(); // output: [1,2,3]
// Attention now!
obj.arr.pop();
obj.get(); // output: [1,2]
console.log(arr); // what is the output?
输出结果是[1,2,3]?错!
正确的结果是:[1,2]
原因:因为js数组是引用传递,this.arr = arr这行代码导致this.arr的值改变的时候,arr也会跟着改变!
解决方案只要对数组拷贝一下即可,吧set方法改成function(arr){ this.arr = arr.slice(0); }即可。
纠结了我一个下午的bug = =!活到老,学到老……
实现方式:
var iframe = document.createElement('iframe');
iframe.style.cssText = 'display:none';
window.document.body.appendChild(iframe);
iframe.setAttribute('src', url);
可以参考:iOS 9 safari iframe src with custom url scheme not working
两者的区别在于:
chrome在18以前支持iframe的跳转,但25以后,就不再支持了。可以参考:Android Intents with Chrome
intent的格式:
intent:
HOST/URI-path // Optional host
#Intent;
package=[string];
action=[string];
category=[string];
component=[string];
scheme=[string];
end;
可能因为intent存在一些漏洞,chrome把自动触发的intent都屏蔽了。
盗取opera下的cookie
<script>
location.href = "intent:#Intent;S.url=file:///data/data/com.opera.browser/app_opera/cookies;c
omponent=com.opera.browser/com.admarvel.android.ads.AdMarvelActivity;end";
</script>
qq浏览器下设置绕过Pin码
<a href="intent:#Intent;action=android.settings.SETTINGS;S.:android:show_fragment=com.android.settings.ChooseLockPassword$ChooseLockPasswordFragment;B.confirm_credentials=false;end">
设置绕过Pin码(android 3.0-4.3)
</a>
因为存在很多漏洞,qq浏览器把所有的intent的用法都去掉了。
更多可以参考:
天猫 | 手淘 | |
---|---|---|
ios9.1 safari | location.href = 'tmall://' | anchor = 'taobao://' |
ios9.1 chrome | location.href = 'tmall://' | iframe = 'taobao://' |
ios8.2 safari | iframe.src = 'tmall://' | iframe.src = 'taobao://' |
ios8.2 chrome | iframe.src = 'tmall://' | iframe.src = 'taobao://' |
安卓4.4.2 chrome | location.href = 'intent://' | location.href = 'intent://' |
安卓4.4.2 native | iframe.src = 'tmall://' | iframe.src = 'taobao://' |
安卓6.0 chrome | location.href = 'intent://' | location.href = 'intent://' |
A. anchor
B. location.href
C. window.open
D. iframe
总结一些做项目过程中的思考方式
做项目之前,需要对产品背景进行分析,想清楚为什么要做这个产品。首先要知道目标用户的特点,这个产品能给用户带来什么价值,如果不清楚或者没有什么价值就不值得做。
每个项目都需要投入很多资源,为了不白干,在投入前期,需要对项目树立目标。首先要清楚产品的目标,用户量大概是多少,确认这些目标数据的统计方式(埋点等)。然后制定自己的目标,是否可以沉淀通用的、可以产品化的工具、组件等。
做项目最忌没构思清楚就开始写代码,可能一开始会比较顺,当代码量越来越大的时候,就会慢慢变得难以维护,可能需要花时间做重构,整理代码。所以在写代码之前需要把整体架构搭好。首先要选择好工具,react 还是 vue,要有能选择合适的工具,需要对他们的特点做深入的研究,比如 react 的 fiber 架构,适合 react 的单数据流还是 vue 的双向绑定,分析两者的优缺点,结合项目来选择。然后分析使用单页还是多页,单页适合模块之间需要频繁通信,缺点是不利于seo优化,容易资源不释放导致内存泄漏。如果选择单页需要考虑路由如何管理,当页面不展示如何释放资源,页面是否需要seo优化,如果需要如何能让爬虫顺利对页面进行抓取。接下来需要考虑代码容错性,尽量不要让一个模块的报错影响其他模块,对页面报错进行日志收集。最后分析页面需要哪些数据,分别谁来提供,数据适合采用同步还是异步的方式,核心数据可以采用同步方式,非核心数据尽量使用异步,防止不重要的后端接口挂了影响到整个页面的使用。
当我们需要的工具没法满足需求,可以尝试对工具进行重写。例如 react 的**是把一个组件当成一次函数调用,数据作为函数的入参,返回值就是由数据组装成的 DOM 节点。js 本身就在一个事件循环中执行,哪些方法需要优先执行哪些需要延后执行没办法控制,所以 react 通过浏览器提供的 requestAnimationFrame(执行优先级高的逻辑)和 requestIdleCallback(浏览器空闲时候执行)方法来重新定义调用栈,可以保证整个渲染过程是可控的。好的产品对细节把控都需要有追求极致的态度,当别人提供的资源没法满足需求时,就只能自己重新做。
没什么好说的,写代码不需要过多的思考,只要按照确定的设计方案来做,如果需要思考,说明前期的准备工作做的不够好。
项目上线后需要对整个项目过程进行复盘和总结,产出一些后面项目需要优化的点。通过埋点、用户调查来看是否能达到产品的预期,如果不能则分析原因。
有一个 Timer 组件,Timer 组件包含 Hours、Minutes 和 Seconds 三个子组件,分别控制小时、分钟和秒的计数。满足以下需求:
我们很容易写出整个 Timer 组件的结构:
<Timer>
<Hours />
<Minutes />
<Seconds />
</Timer>
首先我们可以考虑使用 props:
Hours、Minutes、Seconds分别有一个属性time,用来存当前计数
<Timer>
<Hours time="0" />
<Minutes time="0" />
<Seconds time="0" />
</Timer>
可以将当前时间存在 Timer 的 state 中
var Timer = React.createClass({
getInitialState: function () {
var now = new Date();
return {
hours: now.getHours(),
minutes: now.getMinutes(),
seconds: now.getSeconds()
};
},
...
});
<Timer>
<Hours time={this.state.hours} />:
<Minutes time={this.state.minutes} />:
<Seconds time={this.state.seconds} />
</Timer>
这样就把当前时间显示出来了
Seconds 组件实现一个简单的计时器
var Seconds = React.createClass({
// 将初始化的time属性的值存到state中
getInitialState: function () {
return {
time: this.props.time
}
},
componentDidMount: function () {
var time;
// 每秒钟更新一次
setInterval(function () {
time = this.state.time;
this.setState({
time: time < 59 ? time + 1 : 0
});
// TODO: 此处当计数到60时,需要向外部发送一条消息
}.bind(this), 1000);
},
render: function () {
var time = this.state.time;
time = time < 10 ? '0' + time : time
return <span>{time}</span>
}
});
我们很容易想到加入一个全局的事件机制,React文档 Communicate Between Components 中也提到:
For communication between two components that don't have a parent-child relationship, you can set up your own global event system.
我们也可以使用 Reflux 中的 ListenerMethods,如 The Reflux data flow model 这篇文章所提到的方式。
既然 time 可以实现 Timer 传入 Seconds 中,那是否可以使用 props 呢?
我们可以尝试在 props 上定义一个 notify 属性,并在 Timer 中定义相应的方法
...
notifyMinutes: function () {
},
notifyHours: function () {
},
...
<Timer>
<Hours time={this.state.hours} />:
<Minutes time={this.state.minutes} notify={this.notifyHours} />:
<Seconds time={this.state.seconds} notify={this.notifyMinutes} />
</Timer>
当 Seconds 组件计数达到60时,发送一条消息
this.props.notify({});
在 Timer 中就能接收到 Seconds 的消息了
Timer 如何通知 Minutes 和 Seconds 呢?
很简单,只要重新渲染,更新 time 的值即可
notifyMinutes: function () {
var minutes = this.state.minutes;
this.setState({
minutes: minutes + 1
});
},
notifyHours: function () {
var hours = this.state.hours;
this.setState({
hours: hours + 1
});
}
至此,即可通过 props 实现组件间通信
Seconds 和 Minutes 同时发 notify 的时候会多次执行 setState,会导致一个错误
Uncaught Error: Invariant Violation: setState(...): Cannot update during an existing state transition (such as within
render
). Render methods should be a pure function of props and state.
未完待续...
原文地址:http://www.html5rocks.com/zh/tutorials/internals/howbrowserswork/
流程:文档 -> 词法分析 -> 语法分析 -> 解析树
HTML标签的容错机制:
比较复杂,采用一些优化方案:
CSS属性优先级:
P0:style属性,比重为1,0,0,0
P1:ID选择器,比重为0,1,0,0
P2:类、伪类选择器,比重为0,0,1,0
P3:标签名称,比重为0,0,0,1
P4:*,比重为0,0,0,0
布局调整:
整体布局会带来性能问题,采用dirty位系统的方式进行标记需要调整布局的元素
布局处理:
- 放置子元素
- 计算子元素的高度
千分逗号正则:/(\d(?=(\d{3})+$))/g
<meta content="initial-scale=1,maximum-scale=1,user-scalable=no,width=device-width" name="viewport" />
<link href="http://img01.taobaocdn.com/tps/i1/T1zo51XxXfXXXeOHro-144-144.png" rel="apple-touch-icon-precomposed" />
<meta content="yes" name="apple-mobile-web-app-capable" />
<meta content="black-translucent" name="apple-mobile-web-app-status-bar-style" />
<meta content="telephone=no" name="format-detection" />
优秀的组件库离不开好的文档、测试用例,以及文档搜索。搜索是文档的常用入口,当使用者忘记组件的 API 如何使用,一般都会通过文档搜索来获取 API 的具体使用方法。
最基本的文档搜索,都可以提供对接口名进行搜索,如 backbone、lodash:
这种搜索实现方式比较简单,只要记住接口名,就能轻易找到对应的 API,这种方式更适合工具库的文档,但可能有些库如提供图标功能的 antv。
要实现搜索,首先需要一个索引文件。索引文件如何维护?手工维护索引文件内容更精确,但成本很高,每次 API 接口变更都需要修改索引文件,因此不建议手工维护。而自动维护索引文件是可以通过组件文档来生成:
antv 采用的就是这种方式。因此,antv 可以实现搜素文档标题来查找 API,但会存在一个问题:
md 文件可能会有多个重复的标题,如何区分?
重复的标题可以通过展示内容来区分,同时也可以提供文档内容搜索。如 ice(飞冰)。
索引文件该如何构建?
如果还是采用生成 html 的方式,比较难区分内容和标签,ice 提供了一种解决方案:生成 jsonml 格式的内容。jsonml 的结构如下:
[
"p",
"我们希望中后台应用的开发能变得更高效。面向",
[
"strong",
"设计者"
],
"端,我们提供了 ICE Design 设计语言,来给我们的 UI 界面提供专业的视觉指导。面向",
[
"strong",
"开发者"
]
]
数组第一项是标签名,第二项是内容,这样就将标签和内容区分开了。用 mark-twain 可以实现。通过解析 h1-h6 标签,可以生成标题,内容如何拼接?
常见标签如 p、strong 等可以进行拼接,而 li 标签则可以通过循环解析进行拼接。简单实现方式如下:
getJsonmlString(jsonml) {
for (var result = '', index = 1; r < jsonml.length; r++) {
if (Array.isArray(jsonml[index])) {
result += this.getJsonmlString(jsonml[r])
} else if (typeof jsonml[index] === 'string') {
result += jsonml[index] + ' ';
}
}
return result;
}
虽然实现了对文章内容进行搜索,但搜索结果还是有些凌乱,可以进行进一步内容分类,如分成 API、demo、F&Q 等,使用者可以根据需求方便找到对应文档。要实现这个功能可以根据给索引文件中的标题增加类型,这也是我们后面准备做的。
var xx,yy,XX,YY,swipeX,swipeY ;
div.addEventListener('touchstart',function(event){
xx = event.targetTouches[0].screenX ;
yy = event.targetTouches[0].screenY ;
swipeX = true;
swipeY = true ;
})
div.addEventListener('touchmove',function(event){
XX = event.targetTouches[0].screenX ;
YY = event.targetTouches[0].screenY ;
if(swipeX && Math.abs(XX-xx)-Math.abs(YY-yy)>0) //左右滑动
{
event.stopPropagation();//组织冒泡
event.preventDefault();//阻止浏览器默认事件
swipeY = false ;
//左右滑动
}
else if(swipeY && Math.abs(XX-xx)-Math.abs(YY-yy)<0){ //上下滑动
swipeX = false ;
//上下滑动,使用浏览器默认的上下滑动
}
})
今天跟一个实习生讨论了一下大学学过的算法,他让我实现一下汉诺塔的算法,这个不算难,也正好可以回顾下,代码如下
function hanoi(from, to, tmp, count){
if (count === 1) {
console.log('move from ' + from + ' to ' + to);
} else {
hanoi(from, tmp, to, count - 1);
hanoi(from, to, tmp, 1);
hanoi(tmp, to, from, count - 1);
}
}
hanoi('a', 'b', 'c', 4);
Flux是Facebook用来构建客户端web应用的一个单向数据流的应用架构,它更像是一个开发模式。主要包含三个部分:分发器(Dispatcher)、存储(Stores)、视图(Views,即React组件),其结构和数据流如下图所示:
具体示例代码可以参考官方的TodoMVC demo
Reflux比Flux优势的地方是:
Flux创建事件的方式是:
var TodoActions = {
create: function () {},
update: function () {},
remove: function () {}
};
而Reflux只需要这样定义:
var TodoActions = Reflux.createActions([
'create',
'update',
'remove'
]);
Flux Dispatcher的实现方式更类似于配置的形式:
switch(action.actionType) {
case TodoConstants.TODO_CREATE:
...
break;
case TodoConstants.TODO_UPDATE:
...
break;
case TodoConstants.TODO_REMOVE:
...
break;
}
而Reflux利用约定优于配置原则,将这些swtich语句全部去除
约定优于配置(convention over configuration),也称作按约定编程,是一种软件设计范式,旨在减少软件开发人员需做决定的数量,获得简单的好处,而又不失灵活性。更多信息
Reflux的实现方式是通过约定TodoActions.create会触发TodoStore.onCreate,可以在Reflux的utils模块中看到:
function capitalize(string) {
return string.charAt(0).toUpperCase() + string.slice(1);
}
function callbackName(string, prefix) {
prefix = prefix || "on";
return prefix + exports.capitalize(string);
}
Flux只实现最简单的事件推送,类似于Reflux中的trigger:
// Dispatcher
case TodoConstants.TODO_CREATE:
TodoStore.emitChange();
// TodoStore
emitChange: function() {
this.emit(CHANGE_EVENT);
}
而Reflux有三种事件推送方式: trigger、triggerAsync、triggerPromise,区别在于:
trigger
直接推送数据triggerAsync
加入nextTick,在下一个事件循环中推送数据triggerPromise
与triggerAsync类似,多返回一个promise给actionpreEmit、shouldEmit都在事件推送(emit)前调用,调用顺序为
trigger -> preEmit -> shouldEmit -> emit
join这是一个很有意思的模块,Reflux定义了4种关联action的方式:
例如定义了3个关联action:
var TodoActions = Reflux.createActions([
'create',
'update',
'remove'
]);
然后在TodoStore中进行其中一种方式的关联:
var TodoStore = Reflux.createStore({
init: function () {
this.joinTrailing(TodoActions.create, TodoActions.update, TodoActions.remove, this.trigger);
}
});
TodoStore.listen(function (a, b, c) {
console.log('trigger: ', a, b, c);
});
然后触发其中某个action:
TodoActions.create('create once');
由于joinTrailing关联了create、update、remove三个action,所以需要3个action调用完才回触发trigger:
TodoActions.create('create once');
TodoActions.update('update once');
TodoActions.remove('remove once');
// trigger: ["create once"] ["update once"] ["remove once"]
这四种方式有什么区别?
TodoActions.create('create once');
TodoActions.update('update once');
TodoActions.remove('remove once');
TodoActions.create('create twice');
// trigger: ["update once"] ["remove once"] ["create twice"]
第一个["create once"]丢失了,所以joinTrailing只会存最后一个数据
TodoActions.create('create once');
TodoActions.update('update once');
TodoActions.remove('remove once');
TodoActions.create('create twice');
// trigger: ["create once"] ["update once"] ["remove once"]
除了第一个["create once"],后面的都丢失了,所以joinLeading只会存第一个数据
TodoActions.create('create once');
TodoActions.update('update once');
TodoActions.create('create twice');
TodoActions.remove('remove once');
// Error
// trigger: ["create once"] ["update once"] ["remove once"]
如果是joinStrict的方式,在一个关联中的任何action只要触发两次就回报错,只会保留第一个数据
TodoActions.create('create once');
TodoActions.update('update once');
TodoActions.create('create twice');
TodoActions.remove('remove once');
// trigger: ["create once"] ["update once"] ["create twice"] ["remove once"]
joinConcat能把关联中的所有数据都保留下来
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.