Decorative image frame

redux 快速上手

redux 官方文档,实例的 todo 看的我脑壳疼,各种查找,搜索,发现这个真难理解,为什么呢?因为他们都把这三个拆开介绍,本人本来就对这个玩意陌生,结果还拆开。
瞬间懵逼。

事实上 redux 实际开发上手非常简单。我们走起~

首先介绍一下,只需三行就能完事~~。

state 这个就是跟 react 里面的 state 一样,跟 vue 的 data 一样,存储数据(不过跟 react 一个尿性只能手动更新)

store 因为 state 不能直接赋值,所有要调用 store 的函数去更新和获取值。

action 通过 action 去操作 state 从而达到改变 state 的一个具体实现 action 可以是 obj 也可以是 function。

我们需要做的很简单

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
//创建一个对象用来初始 state
let initStates = {
life: "bad",
};

function reducer(state = initStates, action) {
//在这里面可以各种操作从而返回改变好的state
if (action.type === "changeLife") {
state.life = action.life;
}

return state;
}
//创建一个store
let store = createStore(reducer);
store.getState().life; //这就拿到了初始的life
//现在redux 就完成最基本的初始化了,如果不理解,那不要紧
//我们写给action去改变一下state的值
let dispatchLife = {
type: "changeLife",
life: "good",
};
store.dispatch(dispatchLife);
//在dispatch那一刻life 从bad 变成 good 了
//获取也很简单,只需要
store.getState().life; //这就拿到了改变的life

那么现在你再去看官网的那些概念是不是就很好理解了呢~

像Element UI 一样管理自定组件

官方使用思路

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
MyPlugin.install = function (Vue, options) {
// 1. 添加全局方法或属性
Vue.myGlobalMethod = function () {
// 逻辑...
}

// 2. 添加全局资源
Vue.directive('my-directive', {
bind (el, binding, vnode, oldVnode) {
// 逻辑...
}
...
})

// 3. 注入组件选项
Vue.mixin({
created: function () {
// 逻辑...
}
...
})

// 4. 添加实例方法
Vue.prototype.$myMethod = function (methodOptions) {
// 逻辑...
}
}

如何像 Element ui 实现按需加载和全部加载?

我们只需 在每个组件中都加入 install 方法
并添加一个全局 install 即可。
类似下面这样:

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
const install = function (Vue, opts = {}) {
locale.use(opts.locale);
locale.i18n(opts.i18n);

components.forEach((component) => {
Vue.component(component.name, component);
});

Vue.use(InfiniteScroll);
Vue.use(Loading.directive);

Vue.prototype.$ELEMENT = {
size: opts.size || "",
zIndex: opts.zIndex || 2000,
};

Vue.prototype.$loading = Loading.service;
Vue.prototype.$msgbox = MessageBox;
Vue.prototype.$alert = MessageBox.alert;
Vue.prototype.$confirm = MessageBox.confirm;
Vue.prototype.$prompt = MessageBox.prompt;
Vue.prototype.$notify = Notification;
Vue.prototype.$message = Message;
};

/* istanbul ignore if */
if (typeof window !== "undefined" && window.Vue) {
install(window.Vue);
}

viewport适配思路(目前我没看懂)

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
//移动端版本兼容

var jsVer = 15;
var phoneWidth = parseInt(window.screen.width);
var phoneScale = phoneWidth / 640;

var ua = navigator.userAgent;
if (/Android (\d+\.\d+)/.test(ua)) {
var version = parseFloat(RegExp.$1);
// andriod 2.3
if (version > 2.3) {
document.write(
'<meta name="viewport" content="width=640, minimum-scale = ' +
phoneScale +
", maximum-scale = " +
phoneScale +
', target-densitydpi=device-dpi">'
);
// andriod 2.3以上
} else {
document.write(
'<meta name="viewport" content="width=640, target-densitydpi=device-dpi">'
);
}
// 其他系统
} else {
document.write(
'<meta name="viewport" content="width=640, user-scalable=no, target-densitydpi=device-dpi">'
);
}

滚轮事件兼容与研究

Facebook 兼容原文
鼠标事件(以及双指手势的触摸板) 在前端的支持糟透了奥. 这是一件复杂的事情, 因此这篇文档会很长并且 (希望)我讲的足够细致,能解决你的困惑。

如果你希望能让这个事件变得可控和可预测, 这份代码是你最好的朋友.拥抱~~

至今, 已经有 4 种 DOM event 你能监听到:
‘wheel’ – Chrome(31+), FF(17+), IE(9+)
‘mousewheel’ – Chrome, IE(6+), Opera, Safari
‘MozMousePixelScroll’ – FF(3.5 only!) (2010-2013) – don’t bother!
‘DOMMouseScroll’ – FF(0.9.7+) since 2003

那我 tm 应该怎么做呢? 最好的方法是:

normalizeWheel.getEventType();

在你的回调中调用上面这段代码,用他去智能的屏蔽这些琐碎的细节. 这段代码将返回一个对象内含四个值:

spinX – normalized spin speed (use for zoom) - x plane
spinY – “ - y plane
pixelX – normalized distance (to pixels) - x plane
pixelY – “ - y plane

在你用鼠标滚动浏览器页面,或者触摸板,触摸屏挪动像素的时候,上述的所有的数值由浏览器提供。数值是会变化的
显而,在不同的浏览器和平台中, 你可能滑动的速度不同. 一些设备 (like trackpads) 会反馈出更小的增量在触摸板上, 还有一些这些数值会变化的更加平滑或者更强烈.

这份代码会做更通常的方式去让你使用:

===以下兼容不翻译====。

  • spin is trying to normalize how far the wheel was spun (or trackpad
    dragged). This is super useful for zoom support where you want to
    throw away the chunky scroll steps on the PC and make those equal to
    the slow and smooth tiny steps on the Mac. Key data: This code tries to
    resolve a single slow step on a wheel to 1.
  • pixel is normalizing the desired scroll delta in pixel units. You’ll
    get the crazy differences between browsers, but at least it’ll be in
    pixels!
  • positive value indicates scrolling DOWN/RIGHT, negative UP/LEFT. This
    should translate to positive value zooming IN, negative zooming OUT.
    This matches the newer ‘wheel’ event.

Why are there spinX, spinY (or pixels)?

  • spinX is a 2-finger side drag on the trackpad, and a shift + wheel turn
    with a mouse. It results in side-scrolling in the browser by default.
  • spinY is what you expect – it’s the classic axis of a mouse wheel.
  • I dropped spinZ/pixelZ. It is supported by the DOM 3 ‘wheel’ event and
    probably is by browsers in conjunction with fancy 3D controllers .. but
    you know.

Implementation info:

Examples of ‘wheel’ event if you scroll slowly (down) by one step with an
average mouse:

OS X + Chrome (mouse) - 4 pixel delta (wheelDelta -120)
OS X + Safari (mouse) - N/A pixel delta (wheelDelta -12)
OS X + Firefox (mouse) - 0.1 line delta (wheelDelta N/A)
Win8 + Chrome (mouse) - 100 pixel delta (wheelDelta -120)
Win8 + Firefox (mouse) - 3 line delta (wheelDelta -120)

On the trackpad:

OS X + Chrome (trackpad) - 2 pixel delta (wheelDelta -6)
OS X + Firefox (trackpad) - 1 pixel delta (wheelDelta N/A)

On other/older browsers.. it’s more complicated as there can be multiple and
also missing delta values.

The ‘wheel’ event is more standard:

http://www.w3.org/TR/DOM-Level-3-Events/#events-wheelevents

The basics is that it includes a unit, deltaMode (pixels, lines, pages), and
deltaX, deltaY and deltaZ. Some browsers provide other values to maintain
backward compatibility with older events. Those other values help us
better normalize spin speed. Example of what the browsers provide:

| event.wheelDelta | event.detail
——————+——————+————–
Safari v5/OS X | -120 | 0
Safari v5/Win7 | -120 | 0
Chrome v17/OS X | -120 | 0
Chrome v17/Win7 | -120 | 0
IE9/Win7 | -120 | undefined
Firefox v4/OS X | undefined | 1
Firefox v4/Win7 | undefined | 3

之前我一直使用 wheel 事件,现在发现并不严谨在读了 Element 的兼容写法后,我找到了上面的这个文章。

Element ui 在此基础上的封装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import normalizeWheel from "normalize-wheel";
const isFirefox =
typeof navigator !== "undefined" &&
navigator.userAgent.toLowerCase().indexOf("firefox") > -1;
const mousewheel = function (element, callback) {
if (element && element.addEventListener) {
element.addEventListener(
isFirefox ? "DOMMouseScroll" : "mousewheel",
function (event) {
const normalized = normalizeWheel(event);
callback && callback.apply(this, [event, normalized]);
}
);
}
};
export default {
bind(el, binding) {
mousewheel(el, binding.value);
},
};

我们发现,Element UI 没注意到其实 normaliz-wheel 做了这件事情。
由于没看源码,没注意到:
这个 normalizeWheel.getEventType(); 这个 api
只看见了 readme。。。

1
2
3
4
5
import normalizeWheel from "normalize-wheel";
document.addEventListener("mousewheel", function (event) {
const normalized = normalizeWheel(event);
console.log(normalized.pixelX, normalized.pixelY);
});

那我们应该如何改进呢?

1
2
3
4
5
6
7
8
9
10
11
12
13
const mousewheel = function(element, callback) {
const mouseEvent = normalizeWheel.getEventType();
element.addEventListener(mouseEvent, function(event) {
const normalized = normalizeWheel(event);
callback && callback.apply(this, [event, normalized]);
});
}
};
export default {
bind(el, binding) {
mousewheel(el, binding.value);
}
};

为什么要这样做?
请阅读源码。

表情过滤

1
2
3
4
5
6
const REGEXP_CONFIG = {
// emoji表情过滤
emoji: /(?:[\u00A9\u00AE\u203C\u2049\u2122\u2139\u2194-\u2199\u21A9-\u21AA\u231A-\u231B\u2328\u23CF\u23E9-\u23F3\u23F8-\u23FA\u24C2\u25AA-\u25AB\u25B6\u25C0\u25FB-\u25FE\u2600-\u2604\u260E\u2611\u2614-\u2615\u2618\u261D\u2620\u2622-\u2623\u2626\u262A\u262E-\u262F\u2638-\u263A\u2640\u2642\u2648-\u2653\u2660\u2663\u2665-\u2666\u2668\u267B\u267F\u2692-\u2697\u2699\u269B-\u269C\u26A0-\u26A1\u26AA-\u26AB\u26B0-\u26B1\u26BD-\u26BE\u26C4-\u26C5\u26C8\u26CE-\u26CF\u26D1\u26D3-\u26D4\u26E9-\u26EA\u26F0-\u26F5\u26F7-\u26FA\u26FD\u2702\u2705\u2708-\u270D\u270F\u2712\u2714\u2716\u271D\u2721\u2728\u2733-\u2734\u2744\u2747\u274C\u274E\u2753-\u2755\u2757\u2763-\u2764\u2795-\u2797\u27A1\u27B0\u27BF\u2934-\u2935\u2B05-\u2B07\u2B1B-\u2B1C\u2B50\u2B55\u3030\u303D\u3297\u3299]|(?:\uD83C[\uDC04\uDCCF\uDD70-\uDD71\uDD7E-\uDD7F\uDD8E\uDD91-\uDD9A\uDDE6-\uDDFF\uDE01-\uDE02\uDE1A\uDE2F\uDE32-\uDE3A\uDE50-\uDE51\uDF00-\uDF21\uDF24-\uDF93\uDF96-\uDF97\uDF99-\uDF9B\uDF9E-\uDFF0\uDFF3-\uDFF5\uDFF7-\uDFFF]|\uD83D[\uDC00-\uDCFD\uDCFF-\uDD3D\uDD49-\uDD4E\uDD50-\uDD67\uDD6F-\uDD70\uDD73-\uDD7A\uDD87\uDD8A-\uDD8D\uDD90\uDD95-\uDD96\uDDA4-\uDDA5\uDDA8\uDDB1-\uDDB2\uDDBC\uDDC2-\uDDC4\uDDD1-\uDDD3\uDDDC-\uDDDE\uDDE1\uDDE3\uDDE8\uDDEF\uDDF3\uDDFA-\uDE4F\uDE80-\uDEC5\uDECB-\uDED2\uDEE0-\uDEE5\uDEE9\uDEEB-\uDEEC\uDEF0\uDEF3-\uDEF6]|\uD83E[\uDD10-\uDD1E\uDD20-\uDD27\uDD30\uDD33-\uDD3A\uDD3C-\uDD3E\uDD40-\uDD45\uDD47-\uDD4B\uDD50-\uDD5E\uDD80-\uDD91\uDDC0]))/gim,
// 表情解析
// mdFace: /([\\\.\+\*\?\[\^\]\$\(\)\{\}\=\!<>\|\:])/g
};

我眼中的javascript函数式编程理解(三)

不可或缺的 curry

(译者注:原标题是“Can’t live if livin’ is without you”,为英国乐队 Badfinger 歌曲 Without You 中歌词。)
我父亲以前跟我说过,有些事物在你得到之前是无足轻重的,得到之后就不可或缺了。微波炉是这样,智能手机是这样,互联网也是这样——老人们在没有互联网的时候过得也很充实。对我来说,函数的柯里化(curry)也是这样。
curry 的概念很简单:只传递给函数一部分参数来调用它,让它返回一个函数去处理剩下的参数。
你可以一次性地调用 curry 函数,也可以每次只传一个参数分多次调用。

emmm 这里举个例子奥
正常我们写的话:

1
2
3
4
function a(val1,callback){
console.log(val1);
callback();
}

为了 curry 而 curry 的写法。。。

1
2
3
4
5
6
7
8
9
10
11
12
function a(val1) {
return function (callback) {
console.log(val1);
callback();
};
}
//函数式一点的写法
const a = (val1) => (callback) => {
console.log(val1);
callback();
};
a(1111)(fn);

那么更好用的写法:

1
const fn = (a) => (b) => fn2(a, b);

done!

我眼中的javascript函数式编程理解(二)

具体内容来自:

八年级数学

根据 mathisfun.com:

函数是不同数值之间的特殊关系:每一个输入值返回且只返回一个输出值。

换句话说,函数只是两种数值之间的关系:输入和输出。尽管每个输入都只会有一个输出,但不同的输入却可以有相同的输出。下图展示了一个合法的从 xy 的函数关系;
)(http://www.mathsisfun.com/sets/function.html)
相反,下面这张图表展示的就不是一种函数关系,因为输入值 5 指向了多个输出:
)(http://www.mathsisfun.com/sets/function.html)
函数可以描述为一个集合,这个集合里的内容是 (输入, 输出) 对:[(1,2), (3,6), (5,10)](看起来这个函数是把输入值加倍)。
或者一张表:

输入 输出
1 2
2 4
3 6

甚至一个以 x 为输入 y 为输出的函数曲线图:

如果输入直接指明了输出,那么就没有必要再实现具体的细节了。因为函数仅仅只是输入到输出的映射而已,所以简单地写一个对象就能“运行”它,使用 [] 代替 () 即可。

1
2
3
4
5
6
var toLowerCase = { A: "a", B: "b", C: "c", D: "d", E: "e", D: "d" };
toLowerCase["C"];
//=> "c"
var isPrime = { 1: false, 2: true, 3: true, 4: false, 5: true, 6: false };
isPrime[3];
//=> true

当然了,实际情况中你可能需要进行一些计算而不是手动指定各项值;不过上例倒是表明了另外一种思考函数的方式。(你可能会想“要是函数有多个参数呢?”。的确,这种情况表明了以数学方式思考问题的一点点不便。暂时我们可以把它们打包放到数组里,或者把 arguments 对象看成是输入。等学习 curry 的概念之后,你就知道如何直接为函数在数学上的定义建模了。)
戏剧性的是:纯函数就是数学上的函数,而且是函数式编程的全部。使用这些纯函数编程能够带来大量的好处,让我们来看一下为何要不遗余力地保留函数的纯粹性的原因。

追求“纯”的理由

可缓存性(Cacheable)

首先,纯函数总能够根据输入来做缓存。实现缓存的一种典型方式是 memoize 技术:

1
2
3
4
5
6
7
8
9
10
11
var squareNumber = memoize(function (x) {
return x * x;
});
squareNumber(4);
//=> 16
squareNumber(4); // 从缓存中读取输入值为 4 的结果
//=> 16
squareNumber(5);
//=> 25
squareNumber(5); // 从缓存中读取输入值为 5 的结果
//=> 25

下面的代码是一个简单的实现,尽管它不太健壮。

1
2
3
4
5
6
7
8
var memoize = function (f) {
var cache = {};
return function () {
var arg_str = JSON.stringify(arguments);
cache[arg_str] = cache[arg_str] || f.apply(f, arguments);
return cache[arg_str];
};
};

值得注意的一点是,可以通过延迟执行的方式把不纯的函数转换为纯函数:

1
2
3
4
5
var pureHttpCall = memoize(function (url, params) {
return function () {
return $.getJSON(url, params);
};
});