困

~~~


  • 首页

  • 关于

  • 标签

  • 分类

  • 归档

  • 搜索

正则表达式

发表于 2019-05-29 | 分类于 regx

正则表达式

\0,\1,\2 回溯引用

\0 表示整个表达式 \1 代表括号里的子表达式

1
2
3
const str = 'catcat';
str.match(/(cat)\1/);
// ["catcat", "cat", index: 0, input: "catcat", groups: undefined]

(?: ) 非捕获

1
2
3
const s = 'happy';
s.match(/h(?:a)p(py)/)
// ["happy", "py", index: 0, input: "happy", groups: undefined]

(?= ) 前向查找

1
2
3
4
5
const s = 'happy';
s.match(/p(?=p)/)
// ["p", index: 2, input: "happy", groups: undefined]
s.match(/p(?=y)/)
// ["p", index: 3, input: "happy", groups: undefined]

(?! ) 前向负查找

1
2
3
4
5
const s = 'happy';
s.match(/p(?!p)/)
// ["p", index: 3, input: "happy", groups: undefined]
s.match(/p(?!y)/)
// ["p", index: 2, input: "happy", groups: undefined]

(?<= ) 后向查找

1
2
3
4
5
const s = 'happy';
s.match(/(?<=p)p/)
// ["p", index: 3, input: "happy", groups: undefined]
s.match(/(?<=a)p/)
// ["p", index: 2, input: "happy", groups: undefined]

(?<! ) 后向负查找

1
2
3
4
5
const s = 'happy';
s.match(/(?<!a)p/)
// ["p", index: 3, input: "happy", groups: undefined]
s.match(/(?<!p)p/)
// ["p", index: 2, input: "happy", groups: undefined]

react-native 坑点总结

发表于 2019-05-12 | 分类于 react

react-native 坑点总结


  1. 安卓:子元素设置-的margin,或者定位超出父容器的部分将不会展示出来。甚至给父元素设置overflow: ‘visible’也是不可以的。

  2. 安卓:TextInput的背景色更改不了

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    // 给容器增加背景色
    <View style={styles.inputContainer}>
    <TextInput
    style={styles.textInput}
    underlineColorAndroid="transparent" // try
    onChangeText={this.onSearchChange}
    value={this.props.searchValue}
    placeholder="xxxx"
    onFocus={this.onFocus}
    onBlur={this.onBlur}
    />
    </View>
  3. iOS和安卓的盒模型不一致,iOS :content 安卓 : padding + content + border

interview

发表于 2019-05-01 | 分类于 interview

interview

html

  1. h5

CSS

  1. 选择器优先级
  2. bfc
  3. 布局
  4. 盒模型
  5. css3

js

  1. Dom事件的三个步骤
  2. 事件循环机制
  3. promise
  4. es6
  5. 继承
  6. 原型链
  7. 事件委托,事件冒泡,事件捕获
  8. 闭包

框架

  1. diff算法
  2. 虚拟dom树
  3. 性能是否比原生好
  4. Dom.render之后发生了什么
  5. 生命周期
  6. 新增api 钩子函数

计算机网络

  1. 7层协议
  2. 302,303,307
  3. 跨域
  4. https

浏览器

  1. 执行顺序
  2. session cookie
  3. defer async

设计模式

  1. 观察者模式

算法题

  1. 快排
  2. 二分查找
  3. 冒泡
  4. dp
  5. 回文字符串

安全

  1. xss
  2. csrf

其他

  1. 防抖截流
  2. 手写promise
  3. 深浅拷贝

深度

  1. react diff算法
  2. react 对比vue
  3. redux 的原理 解决什么问题
  4. redux 的优缺点
  5. redux-saga的原理 解决什么问题
  6. redux-saga的优缺点

  7. mobx 的原理 解决什么问题

  8. mobx的优缺点
  9. immutable 的原理 解决什么问题
  10. immutable 的优缺点

  11. mobx redux对比

nginx学习笔记

发表于 2018-12-17 | 分类于 nginx

nginx学习笔记

安装

mac os:

1
brew install nginx

ubuntu:

1
sudo apt-get install nginx

启动

1
nginx

http://localhost 检测是否访问成功

1
2
3
nginx -s stop // 强制停掉
nginx -s quit // 优雅停掉
nginx -s reload // 重启
1
ps -ef|grep nginx

nginx进程在启动的时候,会附带一个守护进程,用于保护正式进程不被异常终止;如果守护进程一旦发现nginx进程被终止了,会自动重启该进程。

守护进程一般会称为master进程,业务进程被称为worker进程

nginx.conf

mac下的nginx.conf位置: /usr/local/etc/nginx/nginx.conf

可以命令搜索

1
sudo find / -name nginx.conf

嫌麻烦打开,配置别名

1
2
// .zshrc code是vscode命令快捷打开功能
alias ngc="code /usr/local/etc/nginx/nginx.conf"
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
... #全局块
events { #events块
...
}

http #http块
{
... #http全局块

server #server块
{
... #server全局块
location [PATTERN] #location块
{
...
}
location [PATTERN]
{
...
}
}

server
{
...
}

... #http全局块
}

默认root路径

1
2
root html => /usr/local/Cellar/nginx/[version]/html
root /var/www => /usr/local/var/www

规则

1
2
3
4
server {
listen 8080;
server_name localhost;
location ...

我们访问http://xxx.com/proxy/index.html

case 1:

1
2
3
location /proxy/ {
proxy_pass http://127.0.0.1/;
}

代理到 http://127.0.0.1/index.html

case 2:

1
2
3
location /proxy/ {
proxy_pass http://127.0.0.1;
}

代理到 http://127.0.0.1/proxy/index.html

case 3:

1
2
3
location /proxy/ {
proxy_pass http://127.0.0.1/api/;
}

代理到 http://127.0.0.1/api/index.html

case 4:

1
2
3
location /proxy/ {
proxy_pass http://127.0.0.1/api;
}

代理到 http://127.0.0.1/apiindex.html

看case 2 和 4是比较难理解的,为什么都是没有后面的/差别为什么这么大,个人理解,如果proxy_pass只是域名则只替换原地址域名,否则全部替换掉。

用到的文件路径

ng配置: nginx.conf => /usr/local/etc/nginx

访问日志: access.log => /usr/local/var/log/nginx

优先级

方式 意义
=PATH 精确匹配路径
^~PATH 使用正则表达式匹配URI的前半段
~PATH 使用正则表达式匹配URI,区分大小写
~*PATH 使用正则表达式匹配URI,不区分大小写
PATH 直接使用PATH匹配,表示在PATH路径下的资源
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
location = / {                                  # 仅当URI为"/"时,使用A配置
[ configuration A ]
}

location / { # URI为"/"下包含的路径时,使用B配置
[ configuration B ]
}

location /documents/ { # URI为"/documents/"下包含的路径时,使用C配置
[ configuration C ]
}

location ^~ /images/ { # URI靠前部分为"/images/",使用D配置
[ configuration D ]
}

location ~* \.(gif|jpg|jpeg)$ { # URI结尾是gif、jpg或jpeg时,使用E配置
[ configuration E ]
}

按上述定义,和优先级规则。比如:

“www.test.com/”匹配A配置;
“www.test.com/test”、”www.test.com/example”匹配B配置,因为URI都在”/”下;
“www.test.com/documents/test.html”匹配C配置,虽然它也符合B配置,但能匹配到的越长优先级越高;
“www.test.com/images/test.html”匹配D配置,虽然它也符合B配置,但正则表达式匹配前端字符优先级高;
“www.test.com/documents/test.jpg”匹配E配置,虽然它也符合C配置,但正则表达式匹配高于普通字符串。

配置https本地开发环境

在开发qtalk-web版本时遇到这样一个需求

images

实现谷歌推送消息,经过调研发现这个需要在https协议下,api才会生效,当时搜出的方法就是nginx,可是由于时间紧迫并没有时间研究,使用的比较快捷有效的方法,恰巧beta环境有https的支持,那我本地用charles劫持了js代码到本地,利用beta环境变成本地的调试环境。

我是通过这篇博客学习了https本地搭建

https://www.jianshu.com/p/fe0fadb38600

全局搜索配置文件:

1
sudo find / -name nginx.conf

证书及https配置

1
2
3
4
5
6
cd /usr/local/etc/nginx # 进入希望生成证书和私钥的目录,这里我们选择nginx.conf所在目录
openssl genrsa -des3 -out server.key 1024 # 创建服务器私钥,该命令会让你输入一个口令
openssl req -new -key server.key -out server.csr # 创建签名请求的证书(CSR)
cp server.key server.key.org
openssl rsa -in server.key.org -out server.key # 在加载SSL支持的Nginx并使用上述私钥时除去必须的口令
openssl x509 -req -days 365 -in server.csr -signkey server.key -out server.crt # 最后标记证书使用上述私钥和CSR

配置 nginx.config

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# HTTPS server
#
server {
listen 443 ssl;
server_name localhost;

# ssl_certificate cert.pem;
# ssl_certificate_key cert.key;
ssl_certificate server.crt;
ssl_certificate_key server.key;

# ssl_session_cache shared:SSL:1m;
# ssl_session_timeout 5m;

# ssl_ciphers HIGH:!aNULL:!MD5;
# ssl_prefer_server_ciphers on;

location / {
root /www/;
index index.html index.htm;
}
}

那我这里想在我的项目里用到https协议,这样还不能满足,我需要更改下location

1
2
3
location / {
proxy_pass http://my.corp.qunar.com:3002;
}

还是会报一个错误

1
GET https://my.corp.qunar.com/index.js net::ERR_CONTENT_LENGTH_MISMATCH 200 (OK)

google后发现是因为nginx会从缓存中拿文件,是这个目录proxy_temp,而没有权限,所以网上一些方法是

1
chown -R _www:admin proxy_temp

不过更改文件夹权限我是莫名地抵触的,我选择了一个我认为还不错的方法

1
2
3
4
location / {
proxy_pass http://my.corp.qunar.com:3002;
proxy_buffering off;
}

就是把缓存禁掉

总结

ng访问地址404 自己会根据优先级逐级查找,如果都404,则按照原地址访问。这样就有可能导致形成内循环,相互跳转,产生错误。

redux源码随便读了读

发表于 2018-12-07 | 分类于 react

redux源码浅析

image


祭出我多年珍藏的redux flow 你看这个redux又小又牛逼 respect skr~

如果你之前接触过redux应该看过无数次这个图了吧,组件触发一个动作,ActiconCreators生成一个action通过dipatch方法经过reducers处理返回新的state到store再触发重新render。


而今天讲的不光包括工作流程,还有redux初始化过程,以及react和redux的配合使用。


看源码先提几个问题,然后带着问题看源码是怎么解决的?

  1. 为什么使用redux?
  2. redux怎么管理state?
  3. redux是怎么触发重新render的?

为什么使用redux?

在我另一篇对比redux和mobx对比并没有讲到~(皮一下狠开心)。redux的产生必然他的原因,写个轮子自然有他要跑的道路,如果你不知道redux是来干什么,说明你还没遇到ta解决的问题,或者你遇到了并不知道redux可以拿来解决。

记得我刚开始学这个的时候纯靠记忆来写,不知所以然,再加上有同事把模版代码写好,那我更不用动脑,无脑虚区~。然而真正遇到问题时,解决起来是非常慢速和困难的。

下面我画一张图来解释redux的好处
image

如果你遇到这种场景,组件套组件层级很深的话,想与旁边的同级元素进行交互时,想象一下没有redux,纯react来做的话是不是需要一层层通过props传递,不能做跨级操作,让人感到疲惫。那么redux来了,建立一个共有的state,大家需要什么都可以从这里拿,更改了,还会通知拿了的人需要更新了,再也不需要口口相传,直接互联网。

其他两个问题将在源码中一一阐述

源码开始

下面来看一下源码

image

主要就5个文件 代码总共大概20多k(未压缩) 源码不是很多

首先看下index

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
import createStore from './createStore'
import combineReducers from './combineReducers'
import bindActionCreators from './bindActionCreators'
import applyMiddleware from './applyMiddleware'
import compose from './compose'
import warning from './utils/warning'
import __DO_NOT_USE__ActionTypes from './utils/actionTypes'

/*
* This is a dummy function to check if the function name has been altered by minification.
* If the function has been minified and NODE_ENV !== 'production', warn the user.
*/
function isCrushed() {}

if (
process.env.NODE_ENV !== 'production' &&
typeof isCrushed.name === 'string' &&
isCrushed.name !== 'isCrushed'
) {
warning(
'You are currently using minified code outside of NODE_ENV === "production". ' +
'This means that you are running a slower development build of Redux. ' +
'You can use loose-envify (https://github.com/zertosh/loose-envify) for browserify ' +
'or setting mode to production in webpack (https://webpack.js.org/concepts/mode/) ' +
'to ensure you have the correct code for your production build.'
)
}

export {
createStore,
combineReducers,
bindActionCreators,
applyMiddleware,
compose,
__DO_NOT_USE__ActionTypes
}

主要是把其他文件整合导出,这里没什么可讲。其他文件我也不想一个个讲,因为串不起来就记不住。

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
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { createStore, applyMiddleware } from 'redux';
import createSagaMiddleware from 'redux-saga';
import reducer from './reducer';
import saga from './saga';
import Page from './';

const root = document.getElementById('app');
const sagaMiddleware = createSagaMiddleware();
const store = createStore(reducer, applyMiddleware(sagaMiddleware));
sagaMiddleware.run(saga);

ReactDOM.render(
<Provider store={store}>
<Page />
</Provider>,
root
);
if (module.hot) {
module.hot.accept(['.'], () => {
// eslint-disable-next-line
render(require('.'));
});
}

上面是随便粘了一段业务中的代码,因为讲redux 中间件saga可以忽略掉,缘起缘灭就是entry的代码。

缘起缘灭

最主要的一行是这一行

1
const store = createStore(reducer, applyMiddleware(sagaMiddleware));

还记得我前面说的主要的五个文件createStore,combineReducers,bindActionCreators,applyMiddleware,compose
这就用到了俩。其实是三个
因为reducer这个参数其实是

1
2
3
4
5
// reducer
...
export default combineReducers({
reducer
});

那么combineReducers也用上了
其他两个一个会在中间价处用到(compose),其实是一个柯里化函数,跟redux本身没什么关系。另一个是为action和dispach方便调用的语法糖函数(bindActionCreators)
这样一看是不是觉得redux文件很少十分的简单呢,其实不然,而且如果要配合react使用,还有高效的发送action,或者在复杂的项目里进行命名空间划分的时候就还要增加很多东西。

好 看下这段代码的执行

image

按照步骤分析

步骤1:combineReducers是个高阶函数,这里看下代码

image

做了一些检测,返回的函数combination是每次发action时,reducer执行处理的地方,现在初始化还没用到这个combination ,等下跑起来的时候再细讲。

步骤2:applyMiddleware这个函数还是很有意思的,他也是一个高阶函数,但不同于combineReducers的是,层级深了一层,也就是说

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

```js
import compose from './compose'

export default function applyMiddleware(...middlewares) {
return createStore => (...args) => {
const store = createStore(...args)
let dispatch = () => {
throw new Error(
`Dispatching while constructing your middleware is not allowed. ` +
`Other middleware would not be applied to this dispatch.`
)
}

const middlewareAPI = {
getState: store.getState,
dispatch: (...args) => dispatch(...args)
}
const chain = middlewares.map(middleware => middleware(middlewareAPI))
dispatch = compose(...chain)(store.dispatch)

return {
...store,
dispatch
}
}
}

applyMiddleware这个高阶函数作为参数,执行之后返回了又一个高阶函数作为参数,而这个高阶函数还需要createStore作为参数,目的是拿到store的getState方法,还有dispatch这两个作为参数传给每一个中间件,让他们各自完成自己想要的封装。

可是他的设计者为什么要搞的那么花了呼哨呢?如果我面对这个需求的话,我可能不需要applyMiddleware这个高阶函数,把middlewares直接传给createStore,这样createStore中,getState方法还容易拿到,这些中间件的捆绑操作交给createStore,岂不是更容易让人理解吗?

这里不难发现,如果你知道函数式编程的话,或者这里是什么设计模式,我还未了解。他用了函数式编程的方式,让代码更容易测试,更加健壮。关键在于,如果按照我之前说的方式来执行middlewares,首先你的中间件是不确定有多少个的,那么对于传参的时候,redux的用户是非常不爽的,createStore要传多种多个参数,比如createStore(reducer, initstate, middleware1, middleware2)这种设计上是不可取的,那你说middleware可以是个一个数组啊,比如createStore(reducer, initstate, [middleware1, middleware2])。emm这块还没想到怎么反驳。。。。或许根本反驳不了,就是多种实现方式,各有取舍,相比之下的选择而已。

我其实想学会redux在设计applyMiddleware这个的实现方式,想在下一次我遇到这种场景去使用到,但是我或许还没领悟其中的精髓,就算知道怎么实现了,下一次我想我不会活学活用,还是会使用我熟悉的方式,打破固有思维还是很难得,但是打破了就是成长。我的理解只到他是函数式编程,只不过复杂了一些,createStore调用了自己而已。

到这里,createStore里的参数都执行了一遍,又返回了各自的函数,接下来才是要执行createStore,让我们揭开createStore神秘的面纱。

image

经过一系列判断, enhancer就是之前的applyMiddleware返回的函数,ta译为增强函数,把自己传进去,第二次调用再传进reducer和preloadedState(undefined)。然后applyMiddleware返回的函数再执行createStore。通过了前面判断了,接着执行createStore里定义的dispatch,dispatch才调用了一遍reducer。是不是很绕,那么让我来梳理一个图,你大概就能明白。

总结步骤

image

而combination在每次发送action都会调用。主要在最后两个步骤里,一个dispatch方法里。

image

遗留问题

这里就是之前遗留的两个问题

  1. redux怎么管理state?
  2. redux是怎么触发重新render的?

当前的state都是通过reducer执行处理来的,而listener这个函数里其实做的是this.setState操作,就可以达到利用react自己触发重新render,也比较好解释react-redux这个库中提供的provider和connect做了些什么,provider其实就是一个react组件,做了提供listener,为listener放入了this.setState().我们经常看到provider放在组件的最上层,也是这个原因,一旦state改变,所有connect的子组件都会判断刷新,如果connect的子组件里的props更新了,那么就会告知刷新,否则不会刷新。这是因为connect是个高阶组件,他会在shouldComponentUpdate生命周期判断是否需要刷新。
那么为什么redux是怎样直接传递props给深层级的组件呢,其实redux自己是做不到的。而是provider接收Redux的store作为props,通过context对象传递给子孙组件上的connect。

当然你也可以直接用creatStore的subscribe来监听组件,react-redux只不过是redux配合react的非常好用的工具,subscribe这里redux用了观察者模式,这是js领域经常用到的设计模式。这里就不多讲了。

compose和bindActionCreator这里也不讲了,我觉得我上面简单提到的足以解释两个方法,并不影响你理解redux的机制。有兴趣你可以参考其他的博文了解这两个方法,比你想象的要简单。

redux-mobx

发表于 2018-10-04 | 分类于 react

redux&mobx对比

image

前言

redux和mobx都是javascript的状态管理库,我们团队一直都在用它与react一起使用,我一直都在用redux,各种项目,各种用法。redux + thunk, redux + saga,或是引入immutable。我最近在一个后台内部使用的项目上尝试了mobx,由于有redux的使用基础,询问了下用过的同事,传授了几句话,甚至没看mobx的文档就开始使用了,可见redux和mobx从使用上这方面讲,存在很多的相同点。可能你看过很多的文章有关于这方面的,我写的也与他们别无二至,我的观点就是mobx也好,redux也好,他们各有优缺点,用哪一个都没有错,即使你的项目越来越大,mobx也完全可以胜任。

主要内容

  • 函数式编程,面向对象编程和响应式编程
  • 简单讲下redux,着重讲下mobx
  • 对比redux&mobx优缺点
  • 使用mobx时的心得体会

函数式编程,面向对象编程和响应式编程

之所以讲这个,是因为redux是遵循函数式编程思想,例如reducer通过一个个的纯函数把旧的state生成新的state,而不是改变旧的state,这就存在函数式编程的思想。又引出了一个概念纯函数,所谓纯函数就是同样的输入,同样的输出。举个例子:

1
2
3
4
5
function add (a, b) {
return a + b;
}

add(1, 2); // 3

最简单的一个例子,add函数在输入1,2的参数情况下,始终会输出3。不会出现任何所谓的副作用,也就是影响到其他的地方,例如全局变量。
ok,有一个问题,

redux为什么要用纯函数来处理state?

reducer将旧的状态(prev)和要修改的数据一起传进去,然后返回一个新的(next)状态,prev和next相比较来确定storge数据是否改变。如果我们用不纯的函数,prev和next将一致,就算数据改变,hasChanged也会是false。

面向对象编程

你可能常常听到面向对象有三大特性:封装,继承,多态。这里不做过多阐述。

说下面向对象编程产生的原因:由于面向过程在构造系统时,无法解决
重用,维护,扩展
的问题,代码逻辑过于复杂,代码晦涩难懂,因此人们开始想能不能让计算机直接模拟现实的环境,以人类解决问题的方法,思路,习惯和步骤来设计相应的应用程序。于是,面向对象的编程思想就产生了。

面向对象编程最重要的就是类,类是创建对象的模版,一个类可以创建多个对象。mobx的store就是一个个类,这里的类只创造了一个对象,多个类的实例化也就是多个store。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import { observable, action } from 'mobx';

class IndexStore {
@observable searchField = 1;
...
}

class otherStore {
@observable searchField = 1;
...
}

export default {
index: new IndexStore(),
other: new otherStore()
};

响应式编程

这里我看到一个很好的比喻

老张还喝水,那么煮开水
有两把水壶,一个是普通水壶,一个是响水壶.

  1. 老张把水壶放到火上,在旁边等待着水开。(同步阻塞)
    老张觉得自己有点傻
  2. 于是老张把水壶放到火上,去客厅看电视,时不时去厨房看看水开没开(同步非阻塞)
    老张还是觉得有点傻,于是乎换了水壶
  3. 这回用的是响水壶,老张继续在旁边等着响。(异步阻塞)
    老张觉得这样不用等啊,听声就好了
  4. 于是老张去客厅看电视了,响了再去看水壶(异步非阻塞)
    老张心满意足,很是傲娇

回归到本质回答这个问题:响应式编程,本质上是对数据流或某种变化所作出的反应,但是这个变化什么时候发生是未知的,所以他是一种基于异步、回调的方式在处理问题。

那么mobx在哪里用到了这种思想呢?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var globalID = 0
function observable(obj) {
var oID = ++globalID
return new Proxy(obj, {
get: function (target, key, receiver) {
collect.startCollect(oID + '' +key)
return Reflect.get(target, key, receiver)
},
set: function (target, key, value, receiver) {
Reflect.set(target, key, value, receiver)
collection[oID + '' + key] && collection[oID + '' + key].forEach(c => {
c()
});
}
})
}

Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。Proxy 这个词的原意是代理,用在这里表示由它来“代理”某些操作,可以译为“代理器”。
这里用Proxy是因为当你在可观察对象上加入新的属性时,就不会像Object.defineProperty那样新的属性没法监听了。

redux (react)

image
这里不讲怎么使用react-redux,谈谈redux的优点和缺点

优点

  • redux的优点在SPA应用中最有体现,当然分页也可以用,redux解决兄弟组件间通信,只要connect一个组件,这个组件无论在任何地方都能拿到全局的state(redux的state)
  • redux让应用的状态变化变得更加可预测,必须通过action改变state
  • redux与很多中间件结合,搭配immutable使应用的性能提升,开发效率提高。

缺点

  • store给我的感觉是在写全局变量,当一个复杂大型的项目多人一起开发时,命名就有可能出现重复,本来想更改这个模块的state,存在相互影响的问题,所以一般大家会对自己的模块增加命名空间解决此问题
  • 为了配合函数式编程,不可变思想,导致存在大量的模版代码,这个虽然说不上是缺点,但是开发起来就很头痛,所以也衍生出很多代码来自动生成模版。

这些缺点也让作者出现’打脸式’回答:’You might not need Redux’,’try mobx’,其实就是应用场景比较单一,不能覆盖多种场景导致的。正所谓你之蜜糖,我之砒霜。

mobx(react)

image

从图中看出和redux对比,就是不用redux更改store,而是actions直接修改sotre了,因为mobx的原理是通过观察者模式对数据做出追踪处理,在对可观察属性的作出变更或者引用的时候,触发其依赖的监听函数。
什么意思呢?像redux的数据是不可变的,如果你理解不可变的话。它需要更改数据的引用来感知数据的变化,而mobx不会更改数据的引用,是通过更改值,监测值的变化进而重新渲染等工作。

store

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
import {
observable,
action,
configure,
computed,
runInAction
} from 'mobx';

configure({ enforceActions: true });
// useStrict(true); mobx4之前

class Store {
@observable id = 1;
@observable data = [];

@action.bound
@action('find data')
async findData() {
try {
const res = await axios({
method: 'POST',
url: '/metadata/api/data',
data: {
id: this.id
}
});
runInAction(() => {
this.data = res.data || [];
});
} catch (e) {
console.log(e);
}
@computed
get pageCount() {
return Math.ceil(this.totalCount / this.pageSize);
}
}
}

export default {
store: new Store()
};

entry

1
2
3
4
5
6
7
8
ReactDOM.render(
<Provider {...store}>
<Frame {...options}>
<Entry />
</Frame>
</Provider>,
document.getElementById('app')
);

component

1
2
3
4
5
@inject('store')
@observer
export default class Page extends Component {
....
}

pou出了一段mobx我自己的用法,可以看出mobx没有redux中的Actions、Action Creator、 Action Types、Reducer、Global Store,一个Store把所有事都干了,在 Mobx 中可以使用 inject 获得 store 依赖。然后 store 可以传递 substate 和 actions 给组件。Mobx 的 observer 确保组件在 store 中 observable 的属性变化时更新。

mobx对比redux

学习难度

有人说mobx简单,redux较难,是因为redux延伸出的东西太多,mobx把这些封装到一起,单说redux这个抽象的理念比较难理解,再言之,我认为两种语言的学习难度都不能作为是否选择这个状态管理库的先决条件,作为一个优秀的开发者,两者还是都要学习了解的。

性能

在写redux的action的时候,总是需要用到扩展语句或者Object.assign()的方式来得到一个新的state,这一点对于JavaScript而言是对象的浅拷贝,它对内存的开销肯定是大于mobX中那样直接操作对象属性的方式大得多。
但是对于现在的浏览器和硬件的性能来说一般的应用都不会遇到这种性能的问题。

适用场景

redux适用大型复杂多人开发的项目,是因为redux的特性是单向的数据流,任何操作和行为的结果都是可预测的,易测试的。而mobx也是适用较复杂的,如果太简单的话直接setState的就好了,mobx相对redux显得有些灵活,所以如果要在大型项目用的化,还是团队内部约定规范,使项目变得易维护。

开发体验

我个人认为mobx的开发体验简直不要太爽,不需要构思reducer如何把一个旧的state换成新的state,可能一个不小心就把一个key的值覆盖掉,mobx而是直接赋值。没有冗余的actions模版代码,更不存在命名空间问题,相信无论哪个开发者用过mobx和redux之后都会认为mobx的开发舒适度更高一些。

redux更像是手动挡的车,mobx更像是自动挡的。

心得&&最佳实践

最近做的项目基于mobx + react系写的,回头看了一下项目,一些地方并没有遵循最佳实践的写法,而是有种redux的思想写mobx的感觉。
官方介绍建议新建两个store(实际上应该是两种),一个UI state一个domain state

  • UI state是指当前UI的状态,比如:窗口尺寸、当前展示的页面、渲染状态、网络状态等等
  • Domain state则主要包含页面所需的各种数据(一般是需要从后端获取的)。例如:
    • 文章详情(id为索引的数据表)
    • 首页feed(只有一个,不需要列表)
    • 推荐列表(推荐id索引的数据表,每一项的内容又是一个文章id的列表)

redux + PureComponent + immutable是提升性能的最佳实践
而mobx自己做了这些事情。
也导致了mobx在双向绑定的同时禁掉了react自身的刷新

PureComponent 有很多坑,这里就不一一列举了,网上有很多关于PureComponent的最佳用法。

redux改变值的方式是通过拷贝原来的对象生成新的对象,从而触发组件的componentWillReceiveProps,而MobX改变值只是在原始值的基础上改变,所以值的引用是没有改变的,这也就导致使MobX不会触发componentWillReceiveProps。

基于这种原因所以mobx-react提供了componentWillReact来触发MobX值的改变,但是它不只是监听MobX值的改变,同时包含componentReceiveProps的功能,所以在使用MobX之后,并不需要componentWillReceiveProps方法了。

first

发表于 2018-06-13 | 分类于 other

Hello World

发表于 2018-06-13 | 分类于 other

Welcome to Hexo! This is your very first post. Check documentation for more info. If you get any problems when using Hexo, you can find the answer in troubleshooting or you can ask me on GitHub.

Quick Start

Create a new post

1
$ hexo new "My New Post"

More info: Writing

Run server

1
$ hexo server

More info: Server

Generate static files

1
$ hexo generate

More info: Generating

Deploy to remote sites

1
$ hexo deploy

More info: Deployment

pagnkelly

每当下午三点过一刻

8 日志
5 分类
7 标签
RSS
GitHub E-Mail
© 2019 pagnkelly
由 Hexo 强力驱动
|
主题 — NexT.Pisces v5.1.4
本站总访问量:  |   本站访客数: