HYPERS 前端团队博客


  • 首页

  • 分类

  • 归档

  • 标签
loop

从MVC到MVVM——数据与视图关系的演进

发表于 2017-07-31
| 字数统计 2,837

之前在网上看到过一位设计师这样说:现在绝大多数的网站做的其实只是把数据库里的内容展示到网页上,除此之外并没有做其他任何事情,所有的网站都是没有温度的。

他当然是在只是在批评当前网站的单一与乏味,但是我们仔细想想前半句话却觉得似乎很有道理,似乎整个web开发史就只是在反反复复的解决这一件事。

如何把数据展示到页面上,如何更快的把数据展示到页面上,如何更好的把数据展示到页面上,如何方便用户和展示到页面上的数据进行交互。web的发展史在重复的解决着这些问题,因此他们之间关系发展的历史也是web开发的发展史。

web的诞生时期

关于web的诞生网上的资料大致是这个样子:

  • 1989年3月: Tim Berners-Lee撰写了 “Information Management: A Proposal” 并在欧洲核子研究中心广泛征求意见。1990年10月,Tim Berners-Lee 开始进行使用NeXTStep开发环境开发超文本GUI浏览器及编辑器。他为这个项目命名为“万维网” 。
  • 1991年8月: 互联网上出现了通过FTP传送的万维网软件。
  • 1992年5月: Pei Wei的 “Viola” GUI 浏览器X测试版本诞生。
  • 1993年2月: 国家超级计算应用中心(National Center for Supercomputing Applications )发布了编写的“Mosaic for X” 的第一份alpha版本。
  • 1993年4月: 欧洲核子研究中心宣布万维网技术将可以被人们免费使用,欧洲核子研究中心将不收取和此项技术相关的费用。
  • 1994年5月: 第一节国际万维网大会在日内瓦的欧洲核子研究中心召开。
  • 1994年10月: 万维网联盟(World Wide Web Consortium ,即W3C)成立。

确切的时间并不重要,有人认为是1989年3月因为那个时候概念第一次被提出来,但是也有人认为是91年,因为那个时候这个概念才算第一次落地。

但是正真意义上的web开发的诞生就是浏览器诞生之后了。那个时候我们的web开发,其实就是内容开发,它与今天我们做PPT,写word文档没有什么本质不同。为什么说在当时web开发等同于内容开发呢?看看HTML的全称就知道了,Hypertext Markup Language ,超文本标记语言。

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
<header>
<title>The World Wide Web project</title>
<nextid n="55">
</header>
<body>
<h1>World Wide Web</h1>
The WorldWideWeb (W3) is a wide-area
<a name="0" href="WhatIs.html">
hypermedia
</a> information retrieval initiative aiming to give universal access to a large universe of documents.
<p>Everything there is online about W3 is linked directly or indirectly to this document, including an
<a name="24" href="Summary.html">executive summary</a> of the project,
<a name="29" href="Administration/Mailing/Overview.html">Mailing lists</a> ,
<a name="30" href="Policy.html">Policy</a> , November's
<a name="34" href="News/9211.html">W3 news</a> ,
<a name="41" href="FAQ/List.html">Frequently Asked Questions</a> .
<dl>
<dt><a name="44" href="../DataSources/Top.html">What's out there?</a>
<dd> Pointers to the world's online information,<a name="45" href="../DataSources/bySubject/Overview.html"> subjects</a>
,
<a name="z54" href="../DataSources/WWW/Servers.html">W3 servers</a>, etc.
<dt><a name="46" href="Help.html">Help</a>
<dd> on the browser you are using
<dt><a name="13" href="Status.html">Software Products</a>
<dd> A list of W3 project
...

我们看代码其实就是普通的HTML,所以那个时候的数据和视图的关系甚至说不上耦合,因为数据本身就是视图。

CGI时代

web诞生之初所有的页面几乎都是人肉编写,服务器接收到对特定文件的请求,然后找到对应文件,丢给客户端。出于对动态内容的需要,人们定义了 CGI 。

CGI ( Common Gateway Interface / 通用网关接口 )它定义了web服务器与应用程序之间通讯的接口。有了CGI,web从之间返回给浏览器静态内容变成了,拿到URL解析出其中的参数,判断是否请求的是静态资源然后如果是动态资源,那么把解析出的参数丢给CGI程序,CGI调用外部数据库或者直接返回输出的内容给服务器。然后服务器把输出之间返回给浏览器。

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
/*
* parse_uri - parse URI into filename and CGI args
* return 0 if dynamic content, 1 if static
*/
/* $begin parse_uri */
int parse_uri(char *uri, char *filename, char *cgiargs)
{
char *ptr;

if (!strstr(uri, "cgi-bin")) { /* Static content */
strcpy(cgiargs, "");
strcpy(filename, ".");
strcat(filename, uri);
if (uri[strlen(uri)-1] == '/')
strcat(filename, "home.html");
return 1;
}
else { /* Dynamic content */
ptr = index(uri, '?');
if (ptr) {
strcpy(cgiargs, ptr+1);
*ptr = '\0';
}
else {
strcpy(cgiargs, "");
strcpy(filename, ".");
strcat(filename, uri);
return 0;
}
}
/* $end parse_uri */

这是《深入理解计算机系统》里一个服务器实现的一段代码,作用呢就是判断URL是直接请求静态资源还是动态内容。全部内容可以在原书里找到。

Web编程语言时代

CGI程序一般返回的都是HTML内容,用当时的 C/C++ ,或者Perl语言直接来处理字符串,想想就会非常的酸爽。所以需要一些库或者之间在语言层面把这部分的工作给处理掉 。

最好是这样,返回结果里永远不会变化的那部分的就放在一个固定的地方,然后动态的内容来填充这部分内容。

基于这个想法,伟大的PHP诞生了。

PHP( Hypertext Preprocessor / 超文本预处理器 )最早由 Rasmus Lerdorf 在1995年发明。它自带了对于模板的支持,可以把动态内容写在模板里,直接输出模板。

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
if (strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE') !== FALSE) {
?>
<h3>strpos() 肯定没有返回假 (FALSE)</h3>
<p>正在使用 Internet Explorer</p>
<?php
} else {
?>
<h3>strpos() 肯定返回假 (FALSE)</h3>
<center><b>没有使用 Internet Explorer</b></center>
<?php
}
?>

PHP的一个小例子,通过UA来判断当前浏览器是否为IE,因为混合了html模板,可以在代码里直观的看到输出的结果。

随着web的发展,逐渐出现了其他的web脚本语言,比如ASP和JSP脚本语言。这个时期的数据可以说是耦合在视图里的,开发者会直接在脚本里编写sql语句来查询数据,吐给服务器。

这个时期 java 语言的jsp规范里提出了一个JSP Model 2 模型,并且将其定义为一种架构模式。这个东西最大的意义是把MVC 这种GUI编程架构带到了Web。

这个时期 JAVA web的各种基础应用的发展使得java提出了j2ee的web架构,而微软则提出了.net 开发平台。

MVC开发框架时代

2004年,DHH(David Heinemeier Hansson)在开发Basecamp的时候发现无论是php还是java来开发web应用都不怎么满意,然后在好友的建议下使用了ruby 并且一见钟情,于是在产品开发完毕后抽出了其中的框架,并且命名为Ruby on Rails。

结合这张图片我们看一下rails的具体运作流程,

首先用户发送一个请求:

http://localhost:3000/articles/index

这个请求被router拦截:

1
2
3
4
Rails::Application.routes.draw do
get "articles/index" => "articles#index"
# ...
end

并且分发给welcom controller的say action

1
2
3
4
5
6
7
8
9
10
11
12
13

class ArticlesController < ApplicationController
def index
@articles = Article.all
end

def show
...
end

def new
...
end

controller把数据丢给view

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<h1>Listing articles</h1>

<table>
<tr>
<th>Title</th>
<th>Text</th>
</tr>

<% @articles.each do |article| %>
<tr>
<td><%= article.title %></td>
<td><%= article.text %></td>
</tr>
<% end %>
</table>

最后输出HTML返回给用户。

SAP(Single Page Application / 单页应用)

2005年出现的AJAX这个概念使得 JavaScript 催生了现代意义上的前端。AJAX即“Asynchronous JavaScript and XML”(异步的JavaScript与XML技术)。使得页面可以进行局部刷新,而不是每次都重新请求新的页面,Google的一系列产品比如Gmail,google map的成功使得 AJAX 的应用变得越来越广泛。

于是出现了SAP这个概念,SAP的出现没有改变后端web框架的模式,但是却将View也就是视图层的复杂度全部挪到了前端。于是,直接使用脚本方式的js越来越无法满足web开发的需要,催生出了前端的MVC框架。

前端MVC与传统MVC的区别

首先是前端的Controller的定义很模糊,因为很多控件的存在,它往往即是View又是Controller。这直接到了MVC的单一职责原则。

其次,在后端的MVC框架里,每个请求作为一个Action,然而SAP应用里,Action的来源则有可能会有很多,既有可能是来自于用户的输入,也有可能来自于URL的改变,甚至是web程序本身。

还有就是Model,前端MVC的Model不光要保存应用相关的状态(即从Server端拿到的数据),同时还要维护UI状态,比如Dropdown,Modal等组件的显示或者是隐藏。

因此和后端MVC框架相比,前端框架的架构更像这个样子:

区别在于Model和View是可以直接通讯的,但是View和Model直接通讯会带来诸多维护上的问题,最大的问题数据和界面同步的问题,

解决这个问题的关键在于阻止View和Model进行直接通讯,于是我们可以将上图中一部分可以和View进行通讯的Model拿出来作为View-model,同时把Controller里面用于同步View和Model关系的部分也放到View-model里,然后他们之间的关系就变成这样。

Vue作为一个视图框架实现了的视图和数据的双向绑定功能

1
2
3
4
<div id="app-6">
<p>{{ message }}</p>
<input v-model="message">
</div>
1
2
3
4
5
6
var app6 = new Vue({
el: '#app-6',
data: {
message: 'Hello Vue!'
}
})

传统的MV*框架存在着数据流不清晰的问题,因为View和Model直接的通信往往都是双向的。

因此为了让数据流更加清楚,Facebook提出了单向数据流的Flux框架。

用户发出一个Action Dispatcher收到Action 分发给Store,Store根据Action的类型来改变数据,然后通知View,View收到通知更新界面。

如果需要从View来更新数据的话就添加一个Action,这样即使在应用很复杂的情况下,数据的流向也会变得很清楚了。

总结一下,事实上纵观web发展史,框架直接的架构本质上来看在传统web开发框架Rails出现之后久没发生太大变化了,后面的几乎都是这种模式的变体,web还在发展,也肯定会有旧的框架死掉,新的框架出来,抓住其中的核心才是最关键的。

最后本文内容参考了众多资料:

这里只列出最主要的几篇,感谢他们用心写下的文章。

https://www.zhihu.com/question/22689579/answer/87879505

http://draveness.me/mvx.html

http://www.cnblogs.com/winter-cn/p/4285171.html

本文作者: Xiaoxiong

IE 常见问题

发表于 2017-07-24
| 字数统计 751

万恶的 IE,总是存在奇怪特有的问题,本文用于收集整理开发中遇到的与 IE 相关的问题

阅读全文 »

React 项目中的权限控制需要做哪些事情?

发表于 2017-07-22
| 字数统计 2,159

背景

公司的产品在切换到 SPA 架构时候前,主要采用的 Spring MVC 架构,还是用的 jsp 多页面进行管理,基本上前端不需要管理权限,也不需要控制路由,都由后端 Java filter 层进行处理。当决定在产品线中用 React 技术栈以后,权限一部分的控制就交给了前端进行控制,后端只需要控制 API 以及 API 返回的数据权限。

很多 SPA 架构的项目都采用是 JWT 安全认证,是基于 Token 的一种方式。在本文中所讲的权限认证是基于 CAS (这里采用 cookie + session 身份验证) 的安全认证,所以接下来所讲的内容不一定适用于所有项目,也不是最佳实践,只是在某个环境中合适的。

首先,分析一下权限控制,在产品中我们要考虑那几个方面:

  • 登录授权,用户没有登录只能访问登录页面,如果处于登录状态则跳转到当前用户的默认首页;
  • 路由授权,当前登录用户的角色,如果对一个 URL 没有权限访问,则跳转到 403 页面;
  • 数据授权,当访问一个没有权限的API,则跳转到 403 页面;
  • 操作授权,当页面中某个按钮或者区域没有权限访问则在页面中隐藏。

针对这4个方面,具体看一下在 React 项目中我们怎么控制?

登录授权

用户只有在登录以后才能访问一些页面,这部分的权限控制需要交给路由来控制, React 项目中的路由,我们用的是 react-router 。为了考虑到按需加载等问题,我们采用的是动态路由。

  • 首先,在路由设计的时候,会把路由分为两大类,一类是在非登录状态下可以访问,一类是必须在登录以后才能访问的。
  • 然后,在必须要登录后的这类路由设置一个父路由,这个路由没有可以没有 path, 它需要做的一件事件就是在 onEnter 函数中判断当前用户是否处于登录状态,如果未登录则跳转到登录页面。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
export default {
onEnter: (nextState, replace) => {
// 验证是否已经登录,如果没有登录则跳转到登录页面
if (!checkLogin()) {
replace('/login');
}
},
childRoutes: [
// 登录后可以访问的子路由
require('./users').default,
require('./userGroups').default,
...
]
};
  • 最后,在 login 路由中 onEnter 函数判断是否已经处于登录状态,如果已经登录则跳转到登录用户的默认首页。
1
2
3
4
5
6
7
8
9
export default {
path: 'login',
onEnter: (nextState, replace) => {
if (checkLogin()) {
goHomePage();
}
},
component: PageLogin
};

路由授权

用户登录以后,不同的角色所具有访问那些路由的权限是不一样的,比如:一个管理员登录以后可以用户管理的路由,但是一个普通用户则不能访问。 一个角色到底具有那些路由的权限,这个权限信息我们是存储在数据库中,后台通过一个 API 返回给前端,用户在登录以后就可以拿到登录用户具有那些路由权限的一个配置。有了这个配置就好办了,我们只需监听路由的变化是否发生变化,如果发生变化,判断当前路由是否在配置的路由中存在,如果不存在就跳转到指定的错误页面。

监听路由的变化我们放在了一个 components 层的基类 componentWillReceiveProps 中处理。

1
2
3
4
5
6
7
componentWillReceiveProps(nextProps) {
const { location } = nextProps;
// URL 发生改变的时候检查是否有权限访问
if (location.pathname === '/' || location.pathname !== this.props.location.pathname) {
checkUrlByAllowRouters(routers);
}
}

数据授权

不同的用户登录以后,对数据范围的权限是有限制的,那些能够访问,那些不能访问在产品设计的是就已经定义好,当访问一个当前登录用户无权访问的 API 或者数据的时候,API 响应中会返回对应的 code, 这个 code 是提前就前后的约定好的值。

这部分的处理逻辑应该放在 fetch api 访问层处理 , 定义一个 checkResponseCode 函数,根据约定的 code 做出相应的处理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function checkResponseCode(code){
...
}

fetch(url, options)
.then(checkHttpStatus)
.then(parseJSON)
.then((data) => {
checkResponseCode(data.code);
...
...
return data;
})
.catch((err) => {
errorCallback(err, dispatch);
...
});

操作授权

当用户进入到一个可以访问的路由以后,页面上的按钮不是所有角色都可以操作,有些角色具有查看的功能,有些角色具有新建和删除的功能,不同的角色进入到这个页面,有些按钮需要禁用或者直接不显示这是一个很普遍的业务功能。

这部分的逻辑就只能在每个业务模块的 components 中单独处理,根据不同的业务做个性化处理。为了能够方便的拿到用户的角色相关的信息,在用户登录以后,可以把用户信息放在 React 组件最外层的 context 中, 这样在各个子组件中都可以很方便的获取到用户信息,进行判断验证并对组件进行对应的控制处理。

另外,很多产品系统中的菜单是动态的,所以菜单的信息可以保存在数据库中,不同的角色会有不同的配置,当用户登录以后,会获取菜单资源的信息,并进行菜单初始化。

其他方面的问题

那是不是做到以上几点就可以呢? 其实还存在一些问题:

  • 当用户登录以后,在地址栏你们直接输入一个 URL 地址,访问一个未授权的路由,这样是否还能做到路由授权的验证? 路由授权那里讲到,在 componentWillReceiveProps 判断路由发生变化才会触发校验,在这种情况下是不会触发校验的。
  • 当用户刷新当前页面的时候,或者直接访问一个没有权限的页面, 会出现页面会闪一下再进行跳转。出现这种情况的原因是,整个项目都是走 redux 单向数据流,获取用户的信息 fetch... 方法, 被绑定到 components 上,也就是说,要等组件渲染的时候才会去取用户信息,然后再通过返回的用户信息进行校验,这个时候组件已经显示出来,再进行页面跳转,就出现了这种闪动的问题。

针对这种情况,我们必须要在组件渲染前就必须拿到用户信息,然后再做对应正确的组件渲染。

  • 首先,我们定义了一个 ready 函数,这个函数用来处理所有的预加载,会主动去那一次用户信息检验当是否处于等于状态,同时在这一步我们还做了一些其他时间,比如主题的预加载,环境信息的预加载。
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 filter=[];
/**
* 获取用户信息,检验是否处于登录装
*/
filter.push(new Promise((resolve, reject) => {

...
// 异步获取用户信息,如果拿到用信息后再检验一下,当前访问的路由是否有权限访问
checkUrlByAllowRouters(view.routers);
}));

/**
* 获取环境信息 , 主题加载并初始化
*/
filter.push(new Promise((resolve, reject) => {
...
}));


export default function ready(callback) {
Promise.all(filter)
.then(values => {
...
callback();
})
.catch((error) => {
console.error('ready=>', error);
});
}
  • 然后,等 ready 所有事情处理完成以后,在回调函数中调用 react-dom 的 render 函数,渲染组件。 在 ready 处理是需要一点时间,这个这个时间内为了体验,最好是叫一个默认的 Loading... 。
1
2
3
4
5
ready((values) => {
render(<App />,
document.getElementById('mount')
);
});

总结

处理到这里,应该说能想到的要点都考虑到了。总结一下,其实前端在做权限控制的时候,依赖于后端 API 返回的配置信息,所以在权限设计,路由设计,数据结构设计的时候,前后端一定要约定好。

另外,路由授权这部分的逻辑是可以改进,可以把这部分路径放到 router 中处理,后续如果改进后,再更新本文档。

本文作者:郭小铭

React 项目开发中的常见问题

发表于 2017-07-21
| 字数统计 1,668

React 中, 在 Controlled(受控制)的文本框中输入中文 onChange 会触发多次

Issues

通过输入法输入中文,日文,不管哪种一种用拼音的、笔划的,只要按下键盘的动作都会触发文本框的 change 事件。 但是 React 中的受控组件都通过触发 change 事件后得到的值再去更新组件的value, 这样不停的触发 change,导致 value 一直在更新,组件就一直在 render, 可能会造成组件运行逻辑问题,输入框内出现很多字符,而且中文输入不进去。

在 DOM events 中另外有 3 个事件可以辅助监听一段文字的输入:

  • compositionstart : 事件触发于一段文字的输入之前;
  • compositionupdate : 事件触发于字符被输入到一段文字的时候;
  • compositionend : 事件触发于一段文字的输入完成。

我们可以通过这 3 个事件判断当前输入是否完整的输入,再触发 onChange 事件,这样就能解决多次触发 onChange 的问题。 还有一个重要的问题,虽然 onChange 要等一次完整输入后才触发,但是文本框上的显示需要是实时的,所以还要考虑在 state 中维护一个 innerValue 做实时更新,这样就没有问题了。

另外这 3 个事件在各个浏览器上的兼容性不一样,所以还要考虑兼容性的问题。 具体详细的实现可以参考 form-lib。

Warning: It looks like you’re using a minified copy of the development build of React. When deploying React apps to production, make sure to use the production build which skips development warnings and is faster.

当 React 升级到 v15.* 以后,可能会见到这个警告,它是意思说你在生产环境中使用的是一个开发环境 minified 的代码。

那 React 它是怎么办判断你正在访问的是生产环境,而不是开发环境呢? 它是通过 webpack 知道的,因为最终发布生产环境的代码都是通过 webpack 命令打包。如果你是在开发环境,那肯定是用 webpack-dev-server。 在生产环境中不应该包括开发中使用的所有额外代码,所以在 webpack 部署环境中需要添加以下配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
module.exports = {
//...
plugins:[
new webpack.DefinePlugin({
'process.env':{
'NODE_ENV': JSON.stringify('production')
}
}),
new webpack.optimize.UglifyJsPlugin({
compress:{
warnings: true
}
})
]
//...
}

参考Make your own React production version with webpack

setState(…): Can only update a mounted or mounting component. This usually means you called setState() on an unmounted component. This is a no-op.

这个警告是说,在一个已经卸载的组件上调用 setState 是无效的。出现这种情况一般都是异步操作造成的,比如一个异步数据请求,响应后执行 setState 去更新组件,但是如果在异步请求未完成前,路由发生变化,页面当前组件被卸载,这个时候异步请求没有被中止,导致等请求完成后就会执行 setState 试图去更新组件,就会报当前错误。

解决办法很简单,一般都是在执行 setState 之前判断一下当前组件是否被卸载,如果没有被卸载,才执行 setState。在 ES6 以前可以通过 this.isMounted() 来判断当前组件是否装载,如下:

1
2
3
if(this.isMounted()) { // This is bad.
this.setState({...});
}

但是在 ES6 以后不能用这种方式,这是一种反模式,参考isMounted is an Antipattern

可以利用组件的生命周期自己维护一个 isMounted 状态, 在 componentDidMount 中把 isMounted 设置为 true,
在 componentWillUnmount 再将 isMounted 设置为 false。 通过 isMounted 变量来判断组件是否装载。

Each child in an array or iterator should have a unique key prop.

这个警告很明确,在遍历子元素的时候,每一个子元素都应该有一个唯一的 key。

参考官网相关说明 lists and keys

但是这里需要注意,避免使用数组的 index 来作为属性 key 的值,推荐使用唯一 ID。
参考 Index as a key is an anti-pattern

1
2
3
4
5
const todoItems = todos.map((todo) =>
<li key={todo.id}>
{todo.text}
</li>
);

在 IE 11 控制台报错:Objects are not valid as a React child

错误的全文: error-decoder

这问题一般会在开发环境中遇到,在使用 React 15.4 以后,如果使用了 react-hot-loader 则必须在热加载之前加载 babel-polyfill, 在你的 webpack.config.js 中参考如下配置:

1
2
3
4
5
6
7
entry: [
'babel-polyfill',
'react-hot-loader/patch',
'webpack-dev-server/client?http://127.0.0.1:3000',
'webpack/hot/only-dev-server',
path.resolve(__dirname, './scr/index')
]

在 IE 11 控制台报错:Minified exception occurred; use the non-minified dev environment for the full error message and additional helpful warnings.

如果出现这个错误是提示,在本地环境配置 NODE_ENV=development ,再看一下控制台是否输出了一些更详细的错误信息,参考 Introducing React’s Error Code System 。

如果这个错误只在 IE 11 浏览器环境出现,一般都是 ES6+ 的语法在IE存在兼容问题,一个比较通用的解决办法就是在项目中引入一个 babel-polyfill

1
npm i —save babel-polyfill

在项目入口,引入 babel-polyfill

1
import 'babel-polyfill'

在 webpack 构建的时候会兼容性处理

在 IE 11 控制台报错: Promise is undefined

IE 11 不支持 Promise 对象,解决办法

1
npm i —save es6-promise

在项目入口,引入es6-promise

1
import 'es6-promise/auto';

在 IE 11 浏览器上 FontIcon 图标不显示

这个问题和 React 没有关系,但是这里也记录一下。
在 IE11 会下载 .ttf/.woff 字体文件, 通过 Network 我们可以看到字体文件 response headers 中有一个 Pragma:no-cache,由于 IE 似乎有缓存和字体的问题,所有导致图标不能正常显示。所以删除 WEB 服务(Nginx..)中的 Pragma:no-cache 和 Cache-Control:no-store 就能正常访问。

参考 IE and Cache-Control

在 React 中使用 debounce

如果需要在 React 组件中使用 debounce 方法可以参考下面代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class SearchBox extends React.Component {
constructor(props) {
super(props);
this.method = debounce(this.method,300);
}
method() {
//...
}
render(){
return(
<input onKeyUp={e => {
e.persist();
this.handleKeyDown(e);
}}/>
)
}
}

因为 React 中 SyntheticEvent 是共用的,也就是说 SyntheticEvent对象将会循环使用。而每次执行完事件后,它所有的属性都将会失效(所有属性的值都被置为了 null)。所以在调用 debounce 方法前,一定要先调用 e.persist()来移除 SyntheticEvent ,并允许 event对象被保留下来以用于你自己的代码。
参考文章:

  • Perform debounce in React.js
  • React Document SyntheticEvent

本文作者:郭小铭、Godfery

HyperS 前端团队博客创建了

发表于 2017-07-20
| 字数统计 486

千呼万唤始出来,HyperS 的前端团队博客终于在今天搭建好了。欢迎各位小伙伴的踊跃投稿。

阅读全文 »

123
Hypers

Hypers

25 日志
2 分类
45 标签
© 2019 Hypers - 全站共58.9k字
由 Hexo 强力驱动
主题 - NexT.Muse