React设计模式:深入理解React&Redux原理套路

原文地址

本文从属于笔者的React入门与最佳实践系列,推荐阅读GUI应用程序架构的十年变迁:MVC,MVP,MVVM,Unidirectional,Clean

Communication

React组件一个很大的特性在于其拥有自己完整的生命周期,因此我们可以将React组件视作可自运行的小型系统,它拥有自己的内部状态、输入与输出。

Input

对于React组件而言,其输入的来源就是Props,我们会用如下方式向某个React组件传入数据:


// Title.jsx
class Title extends React.Component {
  render() {
    return 

{ this.props.text }

; } }; Title.propTypes = { text: React.PropTypes.string }; Title.defaultProps = { text: 'Hello world' }; // App.jsx class App extends React.Component { render() { return ; } };</code></pre> <p><code>text</code>是<code>Text</code>组件自己的输入域,父组件<code>App</code>在使用子组件<code>Title</code>时候应该提供<code>text</code>属性值。除了标准的属性名之外,我们还会用到如下两个设置:</p> <ul> <li><p>propTypes:用于定义Props的类型,这有助于追踪运行时误设置的Prop值。</p></li> <li><p>defaultProps:定义Props的默认值,这个在开发时很有帮助</p></li> </ul> <p>Props中还有一个特殊的属性<code>props.children</code>可以允许我们使用子组件:</p> <pre><code> class Title extends React.Component { render() { return ( <h1> { this.props.text } { this.props.children } </h1> ); } }; class App extends React.Component { render() { return ( <Title text='Hello React'> <span>community</span> ); } };

注意,如果我们不主动在Title组件的render函数中设置{this.props.children},那么span标签是不会被渲染出来的。除了Props之外,另一个隐性的组件的输入即是context,整个React组件树会拥有一个context对象,它可以被树中挂载的每个组件所访问到,关于此部分更多的内容请参考依赖注入这一章节。

Output

组件最明显的输出就是渲染后的HTML文本,即是React组件渲染结果的可视化展示。当然,部分包含了逻辑的组件也可能发送或者触发某些Action或者Event。


class Title extends React.Component {
  render() {
    return (
      

); } }; class App extends React.Component { render() { return ; } logoClicked() { console.log('logo clicked'); } };</code></pre> <p>在<code>App</code>组件中我们向<code>Title</code>组件传入了可以从<code>Title</code>调用的回调函数,在<code>logoClicked</code>函数中我们可以设置或者修改需要传回父组件的数据。需要注意的是,React并没有提供可以访问子组件状态的API,换言之,我们不能使用<code>this.props.children[0].state</code>或者类似的方法。正确的从子组件中获取数据的方法应该是在Props中传入回调函数,而这种隔离也有助于我们定义更加清晰的API并且促进了所谓单向数据流。</p> <h1>Composition</h1> <p>React最大的特性之一即是其强大的组件的可组合性,实际上除了React之外,笔者并不知道还有哪个框架能够提供如此简单易用的方式来创建与组合各式各样的组件。本章我们会一起讨论些常用的组合技巧,我们以一个简单的例子来进行讲解。假设在我们的应用中有一个页首栏目,并且其中放置了导航栏。我们创建了三个独立的React组件:<code>App</code>,<code>Header</code>以及<code>Navigation</code>。将这三个组件依次嵌套组合,可以得到以下的代码:</p> <pre><code> <App> <Header> <Navigation> ... </Navigation> </Header> </App></code></pre> <p>而在JSX中组合这些组件的方式就是在需要的时候引用它们:</p> <pre><code> // app.jsx import Header from './Header.jsx'; export default class App extends React.Component { render() { return <Header />; } } // Header.jsx import Navigation from './Navigation.jsx'; export default class Header extends React.Component { render() { return <header><Navigation /></header>; } } // Navigation.jsx export default class Navigation extends React.Component { render() { return (<nav> ... </nav>); } }</code></pre> <p>不过这种方式却可能存在以下的问题:</p> <ul> <li><p>我们将<code>App</code>当做各个组件间的连接线,也是整个应用的入口,因此在<code>App</code>中进行各个独立组件的组合是个不错的方法。不过<code>Header</code>元素中可能包含像图标、搜索栏或者Slogan这样的元素。而如果我们需要另一个不包含<code>Navigation</code>功能的<code>Header</code>组件时,像上面这种直接将<code>Navigation</code>组件硬编码进入<code>Header</code>的方式就会难于修改。</p></li> <li><p>这种硬编码的方式会难以测试,如果我们在<code>Header</code>中加入一些自定义的业务逻辑代码,那么在测试的时候当我们要创建<code>Header</code>实例时,因为其依赖于其他组件而导致了这种依赖层次过深(这里不包含Shallow Rendering这种仅渲染父组件而不渲染嵌套的子组件方式)。</p></li> </ul> <h2>使用React的<code>children</code>API</h2> <p>React为我们提供了<code>this.props.children</code>来允许父组件访问其子组件,这种方式有助于保证我们的<code>Header</code>独立并且不需要与其他组件解耦合。</p> <pre><code> // App.jsx export default class App extends React.Component { render() { return ( <Header> <Navigation /> </Header> ); } } // Header.jsx export default class Header extends React.Component { render() { return <header>{ this.props.children }</header>; } };</code></pre> <p>这种方式也有助于测试,我们可以选择输入空白的<code>div</code>元素,从而将要测试的目标元素隔离开来而专注于我们需要测试的部分。</p> <h2>将子组件以属性方式传入</h2> <p>React组件可以接受Props作为输入,我们也可以选择将需要封装的组件以Props方式传入:</p> <pre><code> // App.jsx class App extends React.Component { render() { var title = <h1>Hello there!</h1>; return ( <Header title={ title }> <Navigation /> </Header> ); } }; // Header.jsx export default class Header extends React.Component { render() { return ( <header> { this.props.title } <hr /> { this.props.children } </header> ); } };</code></pre> <p>这种方式在我们需要对传入的待组合组件进行一些修正时非常适用。</p> <h1>Higher-order components</h1> <p>Higher-Order Components模式看上去非常类似于装饰器模式,它会用于包裹某个组件然后为其添加一些新的功能。这里展示一个简单的用于构造Higher-Order Component的函数:</p> <pre><code> var enhanceComponent = (Component) => class Enhance extends React.Component { render() { return ( <Component {...this.state} {...this.props} /> ) } }; export default enhanceComponent;</code></pre> <p>通常情况下我们会构建一个工厂函数,接收原始的组件然后返回一个所谓的增强或者包裹后的版本,譬如:</p> <pre><code> var OriginalComponent = () => <p>Hello world.</p>; class App extends React.Component { render() { return React.createElement(enhanceComponent(OriginalComponent)); } };</code></pre> <p>一般来说,高阶组件的首要工作就是渲染原始的组件,我们经常也会将Props与State传递进去,将这两个属性传递进去会有助于我们建立一个数据代理。HOC模式允许我们控制组件的输入,即将需要传入的数据以Props传递进去。譬如我们需要为原始组件添加一些配置:</p> <pre><code> var config = require('path/to/configuration'); var enhanceComponent = (Component) => class Enhance extends React.Component { render() { return ( <Component {...this.state} {...this.props} title={ config.appTitle } /> ) } };</code></pre> <p>这里对于<code>configuration</code>的细节实现会被隐藏到高阶组件中,原始组件只需要了解从Props中获取到<code>title</code>变量然后渲染到界面上。原始组件并不会关心变量存于何地,从何而来,这种模式最大的优势在于我们能够以独立的模式对该组件进行测试,并且可以非常方便地对该组件进行Mocking。在HOC模式下我们的原始组件会变成这样子:</p> <pre><code> var OriginalComponent = (props) => <p>{ props.title }</p>;</code></pre> <h1>Dependency injection</h1> <p>我们写的大部分组件与模块都会包含一些依赖,合适的依赖管理有助于创建良好可维护的项目结构。而所谓的依赖注入技术正是解决这个问题的常用技巧,无论是在Java还是其他应用程序中,依赖注入都受到了广泛的使用。而React中对于依赖注入的需要也是显而易见的,让我们假设有如下的应用树结构:</p> <pre><code> // Title.jsx export default function Title(props) { return <h1>{ props.title }</h1>; } // Header.jsx import Title from './Title.jsx'; export default function Header() { return ( <header> <Title /> </header> ); } // App.jsx import Header from './Header.jsx'; class App extends React.Component { constructor(props) { super(props); this.state = { title: 'React in patterns' }; } render() { return <Header />; } };</code></pre> <p><code>title</code>这个变量的值是在<code>App</code>组件中被定义好的,我们需要将其传入到<code>Title</code>组件中。最直接的方法就是将其从<code>App</code>组件传入到<code>Header</code>组件,然后再由<code>Header</code>组件传入到<code>Title</code>组件中。这种方法在这里描述的简单的仅有三个组件的应用中还是非常清晰可维护的,不过随着项目功能与复杂度的增加,这种层次化的传值方式会导致很多的组件要去考虑它们并不需要的属性。在上文所讲的HOC模式中我们已经使用了数据注入的方式,这里我们使用同样的技术来注入<code>title</code>变量:</p> <pre><code> // enhance.jsx var title = 'React in patterns'; var enhanceComponent = (Component) => class Enhance extends React.Component { render() { return ( <Component {...this.state} {...this.props} title={ title } /> ) } }; export default enhanceComponent; // Header.jsx import enhance from './enhance.jsx'; import Title from './Title.jsx'; var EnhancedTitle = enhance(Title); export default function Header() { return ( <header> <EnhancedTitle /> </header> ); }</code></pre> <p>在上文这种HOC模式中,<code>title</code>变量被包含在了一个隐藏的中间层中,我们将其作为Props值传入到原始的<code>Title</code>变量中并且得到一个新的组件。这种方式思想是不错,不过还是只解决了部分问题。现在我们可以不去显式地将<code>title</code>变量传递到<code>Title</code>组件中即可以达到同样的<code>enhance.jsx</code>效果。</p> <p>React为我们提供了<code>context</code>的概念,<code>context</code>是贯穿于整个React组件树允许每个组件访问的对象。有点像所谓的Event Bus,一个简单的例子如下所示:</p> <pre><code> // a place where we'll define the context var context = { title: 'React in patterns' }; class App extends React.Component { getChildContext() { return context; } ... }; App.childContextTypes = { title: React.PropTypes.string }; // a place where we need data class Inject extends React.Component { render() { var title = this.context.title; ... } } Inject.contextTypes = { title: React.PropTypes.string };</code></pre> <p>注意,我们要使用context对象必须要通过<code>childContextTypes</code>与<code>contextTypes</code>指明其构成。如果在<code>context</code>对象中未指明这些那么<code>context</code>会被设置为空,这可能会添加些额外的代码。因此我们最好不要将<code>context</code>当做一个简单的object对象而为其设置一些封装方法:</p> <pre><code> // dependencies.js export default { data: {}, get(key) { return this.data[key]; }, register(key, value) { this.data[key] = value; } }</code></pre> <p>这样,我们的<code>App</code>组件会被改造成这样子:</p> <pre><code> import dependencies from './dependencies'; dependencies.register('title', 'React in patterns'); class App extends React.Component { getChildContext() { return dependencies; } render() { return <Header />; } }; App.childContextTypes = { data: React.PropTypes.object, get: React.PropTypes.func, register: React.PropTypes.func };</code></pre> <p>而在<code>Title</code>组件中,我们需要进行如下设置:</p> <pre><code> // Title.jsx export default class Title extends React.Component { render() { return <h1>{ this.context.get('title') }</h1> } } Title.contextTypes = { data: React.PropTypes.object, get: React.PropTypes.func, register: React.PropTypes.func };</code></pre> <p>当然我们不希望在每次要使用<code>contextTypes</code>的时候都需要显式地声明一下,我们可以将这些声明细节包含在一个高阶组件中。</p> <pre><code> // Title.jsx import wire from './wire'; function Title(props) { return <h1>{ props.title }</h1>; } export default wire(Title, ['title'], function resolve(title) { return { title }; });</code></pre> <p>这里的<code>wire</code>函数的第一个参数是React组件对象,第二个参数是一系列需要注入的依赖值,注意,这些依赖值务必已经调用过<code>register</code>函数。最后一个参数则是所谓的映射函数,它接收存储在<code>context</code>中的某个原始值然后返回React Props中需要的值。因为在这个例子里<code>context</code>中存储的值与<code>Title</code>组件中需要的值都是<code>title</code>变量,因此我们直接返回即可。不过在真实的应用中可能是一个数据集合、配置等等。</p> <pre><code> export default function wire(Component, dependencies, mapper) { class Inject extends React.Component { render() { var resolved = dependencies.map(this.context.get.bind(this.context)); var props = mapper(...resolved); return React.createElement(Component, props); } } Inject.contextTypes = { data: React.PropTypes.object, get: React.PropTypes.func, register: React.PropTypes.func }; return Inject; };</code></pre> <p>这里的Inject就是某个可以访问<code>context</code>的高阶组件,而<code>mapper</code>就是用于接收<code>context</code>中的数据并将其转化为组件所需要的Props的函数。实际上现在大部分的依赖注入的解决方案都是基于<code>context</code>,我觉得了解这种方式的底层原理还是很有意义的。譬如现在流行的<code>Redux</code>,其核心的<code>connect</code>函数与<code>Provider</code>组件都是基于<code>context</code>。</p> <h1>One direction data flow</h1> <p>单向数据流是React中主要的数据驱动模式,其核心概念在于组件并不会修改它们接收到的数据,它们只是负责接收新的数据而后重新渲染到界面上或者发出某些Action以触发某些专门的业务代码来修改数据存储中的数据。我们先设置一个包含一个按钮的<code>Switcher</code>组件,当我们点击该按钮时会触发某个<code>flag</code>变量的改变:</p> <pre><code> class Switcher extends React.Component { constructor(props) { super(props); this.state = { flag: false }; this._onButtonClick = e => this.setState({ flag: !this.state.flag }); } render() { return ( <button onClick={ this._onButtonClick }> { this.state.flag ? 'lights on' : 'lights off' } </button> ); } }; // ... and we render it class App extends React.Component { render() { return <Switcher />; } };</code></pre> <p>此时我们将所有的数据放置到组件内,换言之,<code>Switcher</code>是唯一的包含我们<code>flag</code>变量的地方,我们来尝试下将这些数据托管于专门的Store中:</p> <pre><code> var Store = { _flag: false, set: function(value) { this._flag = value; }, get: function() { return this._flag; } }; class Switcher extends React.Component { constructor(props) { super(props); this.state = { flag: false }; this._onButtonClick = e => { this.setState({ flag: !this.state.flag }, () => { this.props.onChange(this.state.flag); }); } } render() { return ( <button onClick={ this._onButtonClick }> { this.state.flag ? 'lights on' : 'lights off' } </button> ); } }; class App extends React.Component { render() { return <Switcher onChange={ Store.set.bind(Store) } />; } };</code></pre> <p>这里的<code>Store</code>对象是一个简单的单例对象,可以帮助我们设置与获取<code>_flag</code>属性值。而通过将<code>getter</code>函数传递到组件内,可以允许我们在<code>Store</code>外部修改这些变量,此时我们的应用工作流大概是这样的:</p> <pre><code> User's input | Switcher -------> Store</code></pre> <p>假设我们已经将<code>flag</code>值保存到某个后端服务中,我们需要为该组件设置一个合适的初始状态。此时就会存在一个问题在于同一份数据保存在了两个地方,对于UI与<code>Store</code>分别保存了各自独立的关于<code>flag</code>的数据状态,我们等于在<code>Store</code>与<code>Switcher</code>之间建立了双向的数据流:<code>Store ---> Switcher</code>与<code>Switcher ---> Store</code></p> <pre><code> // ... in App component <Switcher value={ Store.get() } onChange={ Store.set.bind(Store) } /> // ... in Switcher component constructor(props) { super(props); this.state = { flag: this.props.value }; ...</code></pre> <p>此时我们的数据流向变成了:</p> <pre><code> User's input | Switcher <-------> Store ^ | | | | | | v Service communicating with our backend</code></pre> <p>在这种双向数据流下,如果我们在外部改变了<code>Store</code>中的状态之后,我们需要将改变之后的最新值更新到<code>Switcher</code>中,这样也在无形之间增加了应用的复杂度。而单向数据流则是解决了这个问题,它强制在全局只保留一个状态存储,通常是存放在Store中。在单向数据流下,我们需要添加一些订阅Store中状态改变的响应函数:</p> <pre><code> var Store = { _handlers: [], _flag: '', onChange: function(handler) { this._handlers.push(handler); }, set: function(value) { this._flag = value; this._handlers.forEach(handler => handler()) }, get: function() { return this._flag; } };</code></pre> <p>然后我们在<code>App</code>组件中设置了钩子函数,这样每次<code>Store</code>改变其值的时候我们都会强制重新渲染:</p> <pre><code> class App extends React.Component { constructor(props) { super(props); Store.onChange(this.forceUpdate.bind(this)); } render() { return ( <div> <Switcher value={ Store.get() } onChange={ Store.set.bind(Store) } /> </div> ); } };</code></pre> <p>注意,这里使用的<code>forceUpdate</code>并不是一个推荐的用法,我们通常会使用HOC模式来进行重渲染,这里使用<code>forceUpdate</code>只是用于演示说明。在基于上述的改造,我们就不需要在组件中继续保留内部状态:</p> <pre><code> class Switcher extends React.Component { constructor(props) { super(props); this._onButtonClick = e => { this.props.onChange(!this.props.value); } } render() { return ( <button onClick={ this._onButtonClick }> { this.props.value ? 'lights on' : 'lights off' } </button> ); } };</code></pre> <p>这种模式的优势在于会将我们的组件改造为简单的<code>Store</code>中数据的呈现,此时才是真正无状态的View。我们可以以完全声明式的方式来编写组件,而将应用中复杂的业务逻辑放置到单独的地方。此时我们应用程序的流图变成了:</p> <pre><code> Service communicating with our backend ^ | v Store <----- | | v | Switcher ----> ^ | | User input</code></pre> <p>在这种单向数据流中我们不再需要同步系统中的多个部分,这种单向数据流的概念并不仅仅适用于基于React的应用。</p> <h1>Flux</h1> <blockquote> <p>关于Flux的简单了解可以参考笔者的GUI应用程序架构的十年变迁:MVC,MVP,MVVM,Unidirectional,Clean</p> </blockquote> <p>Flux是用于构建用户交互界面的架构模式,最早由Facebook在f8大会上提出,自此之后,很多的公司开始尝试这种概念并且貌似这是个很不错的构建前端应用的模式。Flux经常和React一起搭配使用,笔者本身在日常的工作中也是使用React+Flux的搭配,给自己带来了很大的遍历。</p> <p><span class="img-wrap"></span></p> <p>Flux中最主要的角色为Dispatcher,它是整个系统中所有的Events的中转站。Dispatcher负责接收我们称之为Actions的消息通知并且将其转发给所有的Stores。每个Store实例本身来决定是否对该Action感兴趣并且是否相应地改变其内部的状态。当我们将Flux与熟知的MVC相比较,你就会发现Store在某些意义上很类似于Model,二者都是用于存放状态与状态中的改变。而在系统中,除了View层的用户交互可能触发Actions之外,其他的类似于Service层也可能触发Actions,譬如在某个HTTP请求完成之后,请求模块也会发出相应类型的Action来触发Store中对于状态的变更。</p> <p>而在Flux中有个最大的陷阱就是对于数据流的破坏,我们可以在Views中访问Store中的数据,但是我们不应该在Views中修改任何Store的内部状态,所有对于状态的修改都应该通过Actions进行。作者在这里介绍了其维护的某个Flux变种的项目fluxiny。</p> <h2>Dispatcher</h2> <p><span class="img-wrap"></span></p> <p>大部分情况下我们在系统中只需要单个的Dispatcher,它是类似于粘合剂的角色将系统的其他部分有机结合在一起。Dispatcher一般而言有两个输入:Actions与Stores。其中Actions需要被直接转发给Stores,因此我们并不需要记录Actions的对象,而Stores的引用则需要保存在Dispatcher中。基于这个考虑,我们可以编写一个简单的Dispatcher:</p> <pre><code> var Dispatcher = function () { return { _stores: [], register: function (store) { this._stores.push({ store: store }); }, dispatch: function (action) { if (this._stores.length > 0) { this._stores.forEach(function (entry) { entry.store.update(action); }); } } } };</code></pre> <p>在上述实现中我们会发现,每个传入的<code>Store</code>对象都应该拥有一个<code>update</code>方法,因此我们在进行Store的注册时也要来检测该方法是否存在:</p> <pre><code> register: function (store) { if (!store || !store.update) { throw new Error('You should provide a store that has an `update` method.'); } else { this._stores.push({ store: store }); } }</code></pre> <p>在完成了对于Store的注册之后,下一步我们就是需要将View与Store关联起来,从而在Store发生改变的时候能够触发View的重渲染:</p> <p><span class="img-wrap"><a href="http://img.e-com-net.com/image/info8/974ca9e3b62f4161a57891032a210cca.jpg" target="_blank"><img src="http://img.e-com-net.com/image/info8/974ca9e3b62f4161a57891032a210cca.jpg" alt="React设计模式:深入理解React&Redux原理套路_第1张图片" title="" width="620" height="245" style="border:1px solid black;"></a></span></p> <p>很多flux的实现中都会使用如下的辅助函数:</p> <pre><code> Framework.attachToStore(view, store);</code></pre> <p>不过作者并不是很喜欢这种方式,这样这样会要求View中需要调用某个具体的API,换言之,在View中就需要了解到Store的实现细节,而使得View与Store又陷入了紧耦合的境地。当开发者打算切换到其他的Flux框架时就不得不修改每个View中的相对应的API,那又会增加项目的复杂度。另一种可选的方式就是使用<code>React mixins</code>:</p> <pre><code> var View = React.createClass({ mixins: [Framework.attachToStore(store)] ... });</code></pre> <p>使用<code>mixin</code>是个不错的修改现有的React 组件而不影响其原有代码的方式,不过这种方式的缺陷在于它不能够以一种Predictable的方式去修改组件,用户的可控性较低。还有一种方式就是使用<code>React context</code>,这种方式允许我们将值跨层次地传递给React组件树中的组件而不需要了解它们处于组件树中的哪个层级。这种方式和mixins可能有相同的问题,开发者并不知道该数据从何而来。</p> <p>作者最终选用的方式即是上面提及到的Higher-Order Components模式,它建立了一个包裹函数来对现有组件进行重新打包处理:</p> <pre><code> function attachToStore(Component, store, consumer) { const Wrapper = React.createClass({ getInitialState() { return consumer(this.props, store); }, componentDidMount() { store.onChangeEvent(this._handleStoreChange); }, componentWillUnmount() { store.offChangeEvent(this._handleStoreChange); }, _handleStoreChange() { if (this.isMounted()) { this.setState(consumer(this.props, store)); } }, render() { return <Component {...this.props} {...this.state} />; } }); return Wrapper; };</code></pre> <p>其中<code>Component</code>代指我们需要附着到<code>Store</code>中的View,而<code>consumer</code>则是应该被传递给View的Store中的部分的状态,简单的用法为:</p> <pre><code> class MyView extends React.Component { ... } ProfilePage = connectToStores(MyView, store, (props, store) => ({ data: store.get('key') }));</code></pre> <p>这种模式的优势在于其有效地分割了各个模块间的职责,在该模式中Store并不需要主动地推送消息给View,而主需要简单地修改数据然后广播说我的状态已经更新了,然后由HOC去主动地抓取数据。那么在作者具体的实现中,就是选用了HOC模式:</p> <pre><code> register: function (store) { if (!store || !store.update) { throw new Error('You should provide a store that has an `update` method.'); } else { var consumers = []; var change = function () { consumers.forEach(function (l) { l(store); }); }; var subscribe = function (consumer) { consumers.push(consumer); }; this._stores.push({ store: store, change: change }); return subscribe; } return false; }, dispatch: function (action) { if (this._stores.length > 0) { this._stores.forEach(function (entry) { entry.store.update(action, entry.change); }); } }</code></pre> <p><span class="img-wrap"><a href="http://img.e-com-net.com/image/info8/572fc31fdbc647e3ba44983263cf5f6d.jpg" target="_blank"><img src="http://img.e-com-net.com/image/info8/572fc31fdbc647e3ba44983263cf5f6d.jpg" alt="React设计模式:深入理解React&Redux原理套路_第2张图片" title="" width="620" height="212" style="border:1px solid black;"></a></span></p> <p>另一个常见的用户场景就是我们需要为界面提供一些默认的状态,换言之当每个<code>consumer</code>注册的时候需要提供一些初始化的默认数据:</p> <pre><code> var subscribe = function (consumer, noInit) { consumers.push(consumer); !noInit ? consumer(store) : null; };</code></pre> <p>综上所述,最终的Dispatcher函数如下所示:</p> <pre><code> var Dispatcher = function () { return { _stores: [], register: function (store) { if (!store || !store.update) { throw new Error('You should provide a store that has an `update` method.'); } else { var consumers = []; var change = function () { consumers.forEach(function (l) { l(store); }); }; var subscribe = function (consumer, noInit) { consumers.push(consumer); !noInit ? consumer(store) : null; }; this._stores.push({ store: store, change: change }); return subscribe; } return false; }, dispatch: function (action) { if (this._stores.length > 0) { this._stores.forEach(function (entry) { entry.store.update(action, entry.change); }); } } } };</code></pre> <h2>Actions</h2> <p>Actions就是在系统中各个模块之间传递的消息载体,作者觉得应该使用标准的Flux Action模式:</p> <pre><code> { type: 'USER_LOGIN_REQUEST', payload: { username: '...', password: '...' } }</code></pre> <p>其中的<code>type</code>属性表明该Action所代表的操作而<code>payload</code>中包含了相关的数据。另外,在某些情况下Action中没有带有Payload,因此可以使用Partial Application方式来创建标准的Action请求:</p> <pre><code> var createAction = function (type) { if (!type) { throw new Error('Please, provide action\'s type.'); } else { return function (payload) { return dispatcher.dispatch({ type: type, payload: payload }); } } }</code></pre> <p><span class="img-wrap"><a href="http://img.e-com-net.com/image/info8/5cb55e30ad7a4ca3b56ea27d60c7770f.jpg" target="_blank"><img src="http://img.e-com-net.com/image/info8/5cb55e30ad7a4ca3b56ea27d60c7770f.jpg" alt="React设计模式:深入理解React&Redux原理套路_第3张图片" title="" width="620" height="204" style="border:1px solid black;"></a></span></p> <h2>Final Code</h2> <p>上文我们已经了解了核心的Dispatcher与Action的构造过程,那么在这里我们将这二者组合起来:</p> <pre><code> var createSubscriber = function (store) { return dispatcher.register(store); }</code></pre> <p>并且为了不直接暴露dispatcher对象,我们可以允许用户使用<code>createAction</code>与<code>createSubscriber</code>这两个函数:</p> <pre><code> var Dispatcher = function () { return { _stores: [], register: function (store) { if (!store || !store.update) { throw new Error('You should provide a store that has an `update` method.'); } else { var consumers = []; var change = function () { consumers.forEach(function (l) { l(store); }); }; var subscribe = function (consumer, noInit) { consumers.push(consumer); !noInit ? consumer(store) : null; }; this._stores.push({ store: store, change: change }); return subscribe; } return false; }, dispatch: function (action) { if (this._stores.length > 0) { this._stores.forEach(function (entry) { entry.store.update(action, entry.change); }); } } } }; module.exports = { create: function () { var dispatcher = Dispatcher(); return { createAction: function (type) { if (!type) { throw new Error('Please, provide action\'s type.'); } else { return function (payload) { return dispatcher.dispatch({ type: type, payload: payload }); } } }, createSubscriber: function (store) { return dispatcher.register(store); } } } };</code></pre> <p><span class="img-wrap"><img src="http://img.e-com-net.com/image/info8/29df9eb1055a42fe88db86f41996d206.jpg" alt="" title="" width="0" height="0"></span></p> </div> </div> </div> </div> </div> </div> <!--PC和WAP自适应版--> <div id="SOHUCS" sid="1305169394015440896"></div> <script type="text/javascript" src="/views/front/js/chanyan.js"></script> <!-- 文章页-底部 动态广告位 --> <div class="youdao-fixed-ad" id="detail_ad_bottom"></div> </div> <div class="col-md-3"> <div class="row" id="ad"> <!-- 文章页-右侧1 动态广告位 --> <div id="right-1" class="col-lg-12 col-md-12 col-sm-4 col-xs-4 ad"> <div class="youdao-fixed-ad" id="detail_ad_1"> </div> </div> <!-- 文章页-右侧2 动态广告位 --> <div id="right-2" class="col-lg-12 col-md-12 col-sm-4 col-xs-4 ad"> <div class="youdao-fixed-ad" id="detail_ad_2"></div> </div> <!-- 文章页-右侧3 动态广告位 --> <div id="right-3" class="col-lg-12 col-md-12 col-sm-4 col-xs-4 ad"> <div class="youdao-fixed-ad" id="detail_ad_3"></div> </div> </div> </div> </div> </div> </div> <div class="container"> <h4 class="pt20 mb15 mt0 border-top">你可能感兴趣的:(React设计模式:深入理解React&Redux原理套路)</h4> <div id="paradigm-article-related"> <div class="recommend-post mb30"> <ul class="widget-links"> <li><a href="/article/1899478376649912320.htm" title="C++上机实验|继承与派生编程练习" target="_blank">C++上机实验|继承与派生编程练习</a> <span class="text-muted">皖山文武</span> <a class="tag" taget="_blank" href="/search/C%2B%2B%E8%AF%AD%E8%A8%80%E7%A8%8B%E5%BA%8F%E8%AE%BE%E8%AE%A1%E6%95%99%E7%A8%8B/1.htm">C++语言程序设计教程</a><a class="tag" taget="_blank" href="/search/c%2B%2B/1.htm">c++</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a> <div>1.实验目的(1)掌握派生与继承的概念与使用方法(2)运用继承机制对现有的类进行重用。(3)掌握继承中的构造函数与析构函数的调用顺序,(4)为派生类设计合适的构造函数初始化派生类。(5)深入理解继承与组合的区别。2.实验内容设计一个人员类person和一个日期类date,由人员类派生出学生类student和教师类professor,学生类和教师类的数据成员birthday为日期类。3.参考代码#i</div> </li> <li><a href="/article/1899473454697082880.htm" title="JAVA PTA 7-2 声明图书类,记录图书总册数,利用静态变量赋值。分数 10 作者 强彦 单位 太原理工大学" target="_blank">JAVA PTA 7-2 声明图书类,记录图书总册数,利用静态变量赋值。分数 10 作者 强彦 单位 太原理工大学</a> <span class="text-muted">pta pass</span> <a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a> <div>声明一个图书类,其数据成员为书名、编号(利用静态变量实现自动编号)、书价,并拥有静态数据成员册数,记录图书的总册数;在构造方法中,利用静态变量为对象的编号赋值,在主方法中定义对象数组,并求出总册数。输出格式:请输出每本图书对应的书名,书号,书价以及总图书数。输出样例:书名:Java程序设计,书号:1,书价:34.5书名:数据结构,书号:2,书价:44.8书名:C++程序设计,书号:3,书价:35.</div> </li> <li><a href="/article/1899468416859828224.htm" title="MPPT与PWM充电原理及区别详解" target="_blank">MPPT与PWM充电原理及区别详解</a> <span class="text-muted">皖山文武</span> <a class="tag" taget="_blank" href="/search/%E6%99%BA%E8%83%BD%E5%88%B6%E9%80%A0/1.htm">智能制造</a><a class="tag" taget="_blank" href="/search/%E6%9C%BA%E7%94%B5%E4%B8%80%E4%BD%93%E5%8C%96%E6%8A%80%E6%9C%AF/1.htm">机电一体化技术</a><a class="tag" taget="_blank" href="/search/%E6%9C%BA%E7%94%B5%E4%B8%80%E4%BD%93%E5%8C%96/1.htm">机电一体化</a> <div>MPPT(最大功率点跟踪)和PWM(脉宽调制)是太阳能充电控制器中常用的两种技术,它们在原理、效率和适用场景上有显著区别。以下是两者的详细对比:1.工作原理PWM(脉宽调制)核心机制:通过快速开关(MOSFET等)调节太阳能板与电池之间的连接,使太阳能板电压被强制拉低至电池电压水平。充电过程:初期以大电流快速充电(电池电压较低)。当电池接近充满时,通过调节脉冲宽度(占空比)减少电流,防止过充。电压</div> </li> <li><a href="/article/1899462870781980672.htm" title="基于MATLAB串级控制系统仿真设计,基于MATLAB的精馏塔控制系统设计.doc" target="_blank">基于MATLAB串级控制系统仿真设计,基于MATLAB的精馏塔控制系统设计.doc</a> <span class="text-muted">阳光泉</span> <div>摘要:精馏技术是一种应用非常普遍的物料分离的化工装置,在工业中使用极其广泛。它的原理是根据物料的挥发程度的不同来实现物料的分离,以供制造不同的产品。随着工业的迅速发展,生产规模的不断扩大,化工产品种类也在飞速增多。为了促进能源的充分利用,使得精馏在工业过程控制领域中发挥了越来越重要的作用。不同的控制设计过程中使用不同的工业生产工艺,在现实的工业过程中应根据产品的质量要求、产品的工作环境以及能量耗散</div> </li> <li><a href="/article/1899453669699547136.htm" title="STM32实时时钟(RTC)代码深度解析 | 零基础入门STM32第三十九步" target="_blank">STM32实时时钟(RTC)代码深度解析 | 零基础入门STM32第三十九步</a> <span class="text-muted">触角01010001</span> <a class="tag" taget="_blank" href="/search/STM32/1.htm">STM32</a><a class="tag" taget="_blank" href="/search/stm32/1.htm">stm32</a><a class="tag" taget="_blank" href="/search/%E5%8D%95%E7%89%87%E6%9C%BA/1.htm">单片机</a><a class="tag" taget="_blank" href="/search/%E5%B5%8C%E5%85%A5%E5%BC%8F%E7%A1%AC%E4%BB%B6/1.htm">嵌入式硬件</a> <div>主题内容教学目的/扩展视频RTC时钟的使用重点课程RTC时钟的原理,电路原理分析,固件库分析,驱动程序分析。在超级终端上显示时钟。做可修改的超级终端显示RTC的项目。师从洋桃电子,杜洋老师文章目录一、RTC初始化流程分析1.1时钟与备份域配置1.2初始化检测机制二、时间处理核心算法2.1闰年判断算法2.2时间戳转换(Unix时间)三、时间读取与转换3.1读取计数器值3.2星期计算算法四、中断处理机</div> </li> <li><a href="/article/1899453543249670144.htm" title="STM32固件库文件调用原理详解 | 零基础入门STM32第二十二步" target="_blank">STM32固件库文件调用原理详解 | 零基础入门STM32第二十二步</a> <span class="text-muted">触角01010001</span> <a class="tag" taget="_blank" href="/search/STM32/1.htm">STM32</a><a class="tag" taget="_blank" href="/search/stm32/1.htm">stm32</a><a class="tag" taget="_blank" href="/search/%E5%B5%8C%E5%85%A5%E5%BC%8F%E7%A1%AC%E4%BB%B6/1.htm">嵌入式硬件</a><a class="tag" taget="_blank" href="/search/%E5%8D%95%E7%89%87%E6%9C%BA/1.htm">单片机</a> <div>主题内容教学目的/扩展视频固件库介绍什么是固件库,固件库下载,各文件夹介绍对固件库有基础的印象即可。师从洋桃电子,杜洋老师文章目录一、固件库的核心组成1.启动代码(StartupFiles)2.内核相关程序(CMSIS层)3.外设驱动库(StdPeriph_Driver)二、文件调用原理剖析1.启动流程全景图2.外设驱动调用实例3.文档手册的关键作用三、工程文件结构解析四、头文件的桥梁作用五、结语</div> </li> <li><a href="/article/1899443585930031104.htm" title="Vue-前端发展史" target="_blank">Vue-前端发展史</a> <span class="text-muted">lengzher_5601</span> <a class="tag" taget="_blank" href="/search/Vue/1.htm">Vue</a><a class="tag" taget="_blank" href="/search/vue.js/1.htm">vue.js</a><a class="tag" taget="_blank" href="/search/html/1.htm">html</a><a class="tag" taget="_blank" href="/search/css/1.htm">css</a><a class="tag" taget="_blank" href="/search/js/1.htm">js</a><a class="tag" taget="_blank" href="/search/jsp/1.htm">jsp</a> <div>文章目录Vue-前端发展史二、前端发展史1、UI框架2、JavaScript构建工具3、三端同一4、后端技术5、主流前端框架混合开发微信小程序Vue-前端发展史二、前端发展史1、UI框架Ant-Design:阿里巴巴出品,基于React的UI框架ElementUI、iview、ice:饿了么出品,基于Vue的UI框架BootStrap:Teitter推出的一个用于前端开发的开源工具包AmazeUI</div> </li> <li><a href="/article/1899442702886432768.htm" title="Poe AI推出Previews预览功能!对标Claude Artifacts!" target="_blank">Poe AI推出Previews预览功能!对标Claude Artifacts!</a> <span class="text-muted">AI信息Gap</span> <a class="tag" taget="_blank" href="/search/%E4%BA%BA%E5%B7%A5%E6%99%BA%E8%83%BD/1.htm">人工智能</a><a class="tag" taget="_blank" href="/search/ai/1.htm">ai</a><a class="tag" taget="_blank" href="/search/gpt/1.htm">gpt</a><a class="tag" taget="_blank" href="/search/OpenAI/1.htm">OpenAI</a><a class="tag" taget="_blank" href="/search/chatgpt/1.htm">chatgpt</a> <div>Anthropic在发布最新模型Claude3.5Sonnet时,同时官宣了一个针对ClaudeAI重要的更新,那就是Artifacts。新功能Artifacts允许Claude用户在与聊天机器人的对话之外,通过一个专门的窗口分享、编辑和构建重要的独立内容。这些内容通常是超过15行的文本、代码片段、HTML网页、SVG图像、图表和交互式React组件等。用户可以在专用窗口中查看、复制和编辑这些内容</div> </li> <li><a href="/article/1899440425450663936.htm" title="jvm的gc过程" target="_blank">jvm的gc过程</a> <span class="text-muted">18你磊哥</span> <a class="tag" taget="_blank" href="/search/jvm/1.htm">jvm</a> <div>先回忆一下JVM的内存结构和GC的基本原理。JVM的内存主要分为堆、栈、方法区等,而GC主要发生在堆内存里。堆又分为新生代和老年代,新生代包括Eden区、Survivor区(From和To),老年代则是长期存活的对象所在。首先,用户可能想了解GC是如何工作的,不同的垃圾收集器有什么不同,比如Serial、Parallel、CMS、G1、ZGC等。但可能他们需要的是基础流程,比如标记-清除、复制、标</div> </li> <li><a href="/article/1899437273754169344.htm" title="探索Coco-Web:一款强大的H5创作工具" target="_blank">探索Coco-Web:一款强大的H5创作工具</a> <span class="text-muted">岑晔含Dora</span> <div>探索Coco-Web:一款强大的H5创作工具去发现同类优质开源项目:https://gitcode.com/是一个开源的、基于Web的H5(HTML5)创作平台,旨在让开发者和设计师能够轻松地创建互动式的内容和应用。通过其直观的界面和丰富的功能,无论你是编程高手还是初学者,都能够利用Coco-Web制作出富有吸引力的数字内容。技术分析Coco-Web基于现代Web技术构建,包括:React.js:</div> </li> <li><a href="/article/1899436425804312576.htm" title="如何实现和调试REST API中的摘要认证(Digest Authentication)" target="_blank">如何实现和调试REST API中的摘要认证(Digest Authentication)</a> <span class="text-muted"></span> <div>如何实现和调试RESTAPI中的摘要认证(DigestAuthentication)在保护RESTAPI时,开发者通常会在多种认证机制之间进行选择,其中摘要认证(DigestAuthentication)是一种常见的选择。本文探讨了使用摘要认证的原因,解释了其原理,提供了Java和Go语言的实现示例,并提供了测试该认证的工具和方法。为什么使用摘要认证来保护RESTAPI?摘要认证是一种安全的用户验</div> </li> <li><a href="/article/1899435506161217536.htm" title="【排序算法】选择排序" target="_blank">【排序算法】选择排序</a> <span class="text-muted">啥也不会干的小码</span> <a class="tag" taget="_blank" href="/search/%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95/1.htm">排序算法</a><a class="tag" taget="_blank" href="/search/%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95/1.htm">排序算法</a><a class="tag" taget="_blank" href="/search/%E7%AE%97%E6%B3%95/1.htm">算法</a><a class="tag" taget="_blank" href="/search/c%E8%AF%AD%E8%A8%80/1.htm">c语言</a> <div>一、定义:选择排序(Selectionsort)是一种简单直观的排序算法。第一次从待排序的数据(元素)中选出最小(或最大)的一个元素,存放在数组的起始位置,然后再从剩余的没有排序的元素中寻找到最小(大)元素,然后放到已排序的数组的末尾。以此类推,直到全部待排序的数据元素的个数为零。对于数据量大的排序就没啥用了,排的比较慢。二、原理:1、对于待排序的数组,我们从首元素开始,将首元素的下标用min记住</div> </li> <li><a href="/article/1899432227637358592.htm" title="Java 三路快排" target="_blank">Java 三路快排</a> <span class="text-muted">18你磊哥</span> <a class="tag" taget="_blank" href="/search/java%E5%9F%BA%E7%A1%80%E5%AD%A6%E4%B9%A0/1.htm">java基础学习</a><a class="tag" taget="_blank" href="/search/java/1.htm">java</a> <div>三路快速排序(3-WayQuickSort)是快速排序的优化版本,特别适用于处理包含大量重复元素的数组。其核心思想是将数组划分为三个区域:小于基准值、等于基准值和大于基准值,从而减少不必要的递归和交换三路快排原理分区逻辑:使用三个指针lt(lessthan)、current(当前遍历位置)、gt(greaterthan)将数组划分为三部分:[low,lt-1]:小于基准值的元素[lt,gt]:等于</div> </li> <li><a href="/article/1899421510704099328.htm" title="不同存储器组织和交叉编址技术的工作方式及举例说明" target="_blank">不同存储器组织和交叉编址技术的工作方式及举例说明</a> <span class="text-muted">海大超级无敌暴龙战士</span> <a class="tag" taget="_blank" href="/search/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BB%84%E6%88%90%E5%8E%9F%E7%90%86/1.htm">计算机组成原理</a><a class="tag" taget="_blank" href="/search/%E5%AD%A6%E4%B9%A0%E6%96%B9%E6%B3%95/1.htm">学习方法</a> <div>不同存储器组织和交叉编址技术的工作方式及举例说明本节介绍内存系统中常见的组织方式以及如何通过交叉编址来提高存储器性能,主要内容包括:多模块存储器单体多字存储器多体并行存储器高位交叉编址(顺序方式)低位交叉编址(交叉方式)下面依次说明这些结构的工作原理和实际应用中的举例。1.多模块存储器原理简介:多模块存储器将整个存储系统划分为多个独立模块(或称“块”),每个模块具有一定的存储容量和独立的控制电路。</div> </li> <li><a href="/article/1899420124008476672.htm" title="在 cmake_modules 目录下编写 FindG2O.cmake 以集成 G2O" target="_blank">在 cmake_modules 目录下编写 FindG2O.cmake 以集成 G2O</a> <span class="text-muted">XU磊260</span> <a class="tag" taget="_blank" href="/search/SLAM/1.htm">SLAM</a><a class="tag" taget="_blank" href="/search/c%2B%2B/1.htm">c++</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a> <div>1.简介在使用G2O(GeneralGraphOptimization)库进行优化问题求解时,通常需要在CMake项目中正确配置G2O的头文件和库文件路径。由于G2O并未提供官方的CMake配置文件,因此需要手动编写FindG2O.cmake以确保CMake能够正确找到G2O的依赖项。本文将详细解析FindG2O.cmake的编写方式,并介绍其工作原理。2.CMake中的FindG2O.cmake</div> </li> <li><a href="/article/1899411170629054464.htm" title="如何有效管理 JavaScript 中的内存:垃圾回收与最佳实践" target="_blank">如何有效管理 JavaScript 中的内存:垃圾回收与最佳实践</a> <span class="text-muted">名之以父</span> <a class="tag" taget="_blank" href="/search/JavaScript/1.htm">JavaScript</a><a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a><a class="tag" taget="_blank" href="/search/%E5%AE%89%E5%85%A8/1.htm">安全</a><a class="tag" taget="_blank" href="/search/javascript/1.htm">javascript</a><a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF%E6%A1%86%E6%9E%B6/1.htm">前端框架</a><a class="tag" taget="_blank" href="/search/react.js/1.htm">react.js</a><a class="tag" taget="_blank" href="/search/vue.js/1.htm">vue.js</a><a class="tag" taget="_blank" href="/search/%E7%BD%91%E7%BB%9C/1.htm">网络</a> <div>“垃圾回收是现代编程语言的核心特性之一,它使得开发者可以专注于功能实现,而无需担心内存管理的细节。”——在JavaScript中,垃圾回收(GC)是一个自动化的内存管理过程,它帮助我们确保不再使用的内存得到释放。尽管JavaScript的垃圾回收机制非常强大,但如果对其原理和工作方式不够了解,也可能导致一些性能问题和内存泄漏。本文将深入探讨JavaScript中的垃圾回收机制、算法以及如何优化垃圾</div> </li> <li><a href="/article/1899411172239667200.htm" title="【JavaScript 】垃圾回收机制进阶解析:提高性能的终极指南" target="_blank">【JavaScript 】垃圾回收机制进阶解析:提高性能的终极指南</a> <span class="text-muted">名之以父</span> <a class="tag" taget="_blank" href="/search/JavaScript/1.htm">JavaScript</a><a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/jvm/1.htm">jvm</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a><a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a><a class="tag" taget="_blank" href="/search/%E5%AE%89%E5%85%A8/1.htm">安全</a><a class="tag" taget="_blank" href="/search/%E7%BD%91%E7%BB%9C/1.htm">网络</a><a class="tag" taget="_blank" href="/search/vue.js/1.htm">vue.js</a> <div>“垃圾回收机制不仅是内存管理的基石,更是高效Web开发的保障。在JavaScript中,理解其工作原理至关重要。”在JavaScript中,垃圾回收(GarbageCollection,GC)是一个自动化的内存管理过程,能够有效防止内存泄漏虽然这看似是一个简单的机制,但背后却包含着丰富的理论与实现细节。理解这些原理,不仅能够帮助我们写出更高效的代码,还能避免一些性能问题和内存泄漏。本文将带你深入探</div> </li> <li><a href="/article/1899406507481559040.htm" title="K-means 算法核心原理" target="_blank">K-means 算法核心原理</a> <span class="text-muted">code 旭</span> <a class="tag" taget="_blank" href="/search/AI%E4%BA%BA%E5%B7%A5%E6%99%BA%E8%83%BD%E5%AD%A6%E4%B9%A0/1.htm">AI人工智能学习</a><a class="tag" taget="_blank" href="/search/%E7%AE%97%E6%B3%95/1.htm">算法</a><a class="tag" taget="_blank" href="/search/kmeans/1.htm">kmeans</a><a class="tag" taget="_blank" href="/search/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0/1.htm">机器学习</a> <div>一、K-means算法核心原理1.算法目标将n个样本划分到k个簇中,使得每个样本到所属簇中心的距离平方和最小。2.数学公式目标函数(SSE,簇内平方误差):J=∑i=1k∑x∈Ci∥x−μi∥2J=\sum_{i=1}^k\sum_{x\inC_i}\|x-\mu_i\|^2J=i=1∑kx∈Ci∑∥x−μi∥2其中:CiC_iCi表示第iii个簇μi\mu_iμi表示第iii个簇的质心二、算法步</div> </li> <li><a href="/article/1899403353994096640.htm" title="Spring WebFlux:响应式编程" target="_blank">Spring WebFlux:响应式编程</a> <span class="text-muted">m0_74825223</span> <a class="tag" taget="_blank" href="/search/%E9%9D%A2%E8%AF%95/1.htm">面试</a><a class="tag" taget="_blank" href="/search/%E5%AD%A6%E4%B9%A0%E8%B7%AF%E7%BA%BF/1.htm">学习路线</a><a class="tag" taget="_blank" href="/search/%E9%98%BF%E9%87%8C%E5%B7%B4%E5%B7%B4/1.htm">阿里巴巴</a><a class="tag" taget="_blank" href="/search/spring/1.htm">spring</a><a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/%E5%90%8E%E7%AB%AF/1.htm">后端</a> <div>在软件开发领域,随着互联网应用的规模和复杂性不断增加,传统的编程模型逐渐暴露出一些局限性,尤其是在面对高并发、大规模数据流处理等场景时。为了应对这些挑战,响应式编程(ReactiveProgramming)应运而生,它提供了一种更为高效、灵活的编程范式,以适应不断变化的系统需求。1.SpringWebFlux简介WebFlux提供了一个非阻塞、异步的Web框架,允许开发者构建高性能、可伸缩的Web</div> </li> <li><a href="/article/1899402468391972864.htm" title="常用图像增强算法原理及 OpenCV C++ 实现" target="_blank">常用图像增强算法原理及 OpenCV C++ 实现</a> <span class="text-muted">埃菲尔铁塔_CV算法</span> <a class="tag" taget="_blank" href="/search/opencv/1.htm">opencv</a><a class="tag" taget="_blank" href="/search/%E8%AE%A1%E7%AE%97%E6%9C%BA%E8%A7%86%E8%A7%89/1.htm">计算机视觉</a><a class="tag" taget="_blank" href="/search/%E4%BA%BA%E5%B7%A5%E6%99%BA%E8%83%BD/1.htm">人工智能</a><a class="tag" taget="_blank" href="/search/c%2B%2B/1.htm">c++</a><a class="tag" taget="_blank" href="/search/%E7%AE%97%E6%B3%95/1.htm">算法</a><a class="tag" taget="_blank" href="/search/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0/1.htm">机器学习</a> <div>一、引言图像增强是数字图像处理中的一个重要分支,其目的是改善图像的视觉效果,突出图像中的重要信息,或者将图像转换为更适合人或机器分析处理的形式。在实际应用中,图像增强技术广泛应用于医学影像、遥感图像、安防监控等领域。本文将详细介绍常用的图像增强算法原理,并给出基于OpenCVC++库的实现代码。二、图像增强算法分类图像增强算法可以分为空间域增强和频域增强两大类。空间域增强是直接对图像的像素值进行操</div> </li> <li><a href="/article/1899401205357015040.htm" title="ESP32 小智 AI 机器人入门教程从原理到实现(自己云端部署)" target="_blank">ESP32 小智 AI 机器人入门教程从原理到实现(自己云端部署)</a> <span class="text-muted">与光同尘 大道至简</span> <a class="tag" taget="_blank" href="/search/%E4%BA%BA%E5%B7%A5%E6%99%BA%E8%83%BD/1.htm">人工智能</a><a class="tag" taget="_blank" href="/search/%E6%9C%BA%E5%99%A8%E4%BA%BA/1.htm">机器人</a><a class="tag" taget="_blank" href="/search/python/1.htm">python</a><a class="tag" taget="_blank" href="/search/%E4%BA%BA%E6%9C%BA%E4%BA%A4%E4%BA%92/1.htm">人机交互</a><a class="tag" taget="_blank" href="/search/github/1.htm">github</a><a class="tag" taget="_blank" href="/search/visual/1.htm">visual</a><a class="tag" taget="_blank" href="/search/studio/1.htm">studio</a><a class="tag" taget="_blank" href="/search/%E5%8D%95%E7%89%87%E6%9C%BA/1.htm">单片机</a> <div>此博客为一篇针对初学者的详细教程,涵盖小智AI机器人的原理、硬件准备、软件环境搭建、代码实现、云端部署以及优化扩展。文章结合了现有的网络资源,取长补短,确保内容易于理解和操作。简介:本教程将指导初学者使用ESP32微控制器开发一个简单的语音对话机器人“小智”。我们将介绍所需的基础原理、硬件准备、软件环境搭建,以及如何编写代码实现语音唤醒和与云端大模型的对接。通过本教程,即使没有深厚的AI或嵌入式经</div> </li> <li><a href="/article/1899400197226688512.htm" title="我与DeepSeek读《大型网站技术架构》(3)" target="_blank">我与DeepSeek读《大型网站技术架构》(3)</a> <span class="text-muted">诺亚凹凸曼</span> <a class="tag" taget="_blank" href="/search/%E6%9E%B6%E6%9E%84/1.htm">架构</a> <div>大型网站架构的核心要素《大型网站技术架构:核心原理与案例分析》第三章聚焦于大型网站架构的核心要素,从技术维度剖析了构建高可用、高性能、可扩展系统的关键设计方向。1.五大核心架构要素(1)性能(Performance)目标:快速响应用户请求,优化用户体验。关键策略:前端优化:CDN加速静态资源、合并压缩JS/CSS、浏览器缓存。服务端优化:缓存(Redis/Memcached)、异步处理(消息队列)</div> </li> <li><a href="/article/1899398305947906048.htm" title="【Servlet】深入解析 Servlet 启动过程 —— 原理分析、代码实战及在 JDK 和 Spring 中的应用" target="_blank">【Servlet】深入解析 Servlet 启动过程 —— 原理分析、代码实战及在 JDK 和 Spring 中的应用</a> <span class="text-muted">工一木子</span> <a class="tag" taget="_blank" href="/search/%E5%8E%9F%E7%90%86%E5%88%86%E6%9E%90/1.htm">原理分析</a><a class="tag" taget="_blank" href="/search/Servlet/1.htm">Servlet</a><a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/servlet/1.htm">servlet</a> <div>深入解析Servlet启动过程——原理分析、代码实战及在JDK和Spring中的应用在JavaWeb开发中,Servlet是一种用于创建动态Web应用程序的核心技术。作为JavaEE(现在是JakartaEE)的基础,Servlet在处理客户端请求、生成响应等方面发挥着重要作用。理解Servlet的启动过程,不仅能帮助我们调试和优化应用,还能为深入掌握JavaWeb开发的核心原理打下坚实基础。本文</div> </li> <li><a href="/article/1899396792693354496.htm" title="垃圾收集算法与收集器" target="_blank">垃圾收集算法与收集器</a> <span class="text-muted">HBryce24</span> <a class="tag" taget="_blank" href="/search/JVM/1.htm">JVM</a><a class="tag" taget="_blank" href="/search/jvm/1.htm">jvm</a> <div>在JVM中,垃圾收集(GarbageCollection,GC)算法的核心目标是自动回收无用对象的内存,同时尽量减少对应用性能的影响。以下是JVM中主要垃圾收集算法的原理、流程及实际应用场景的详细介绍:一、标记-清除算法(Mark-Sweep)原理标记阶段:从GCRoots(如栈引用、静态变量)出发,遍历对象图,标记所有存活对象。清除阶段:扫描堆内存,回收未被标记的对象所占用的内存(直接释放,不整</div> </li> <li><a href="/article/1899394645624942592.htm" title="【二分算法】-- 三种二分模板总结" target="_blank">【二分算法】-- 三种二分模板总结</a> <span class="text-muted">雨雨雨雨点子</span> <a class="tag" taget="_blank" href="/search/%E7%AE%97%E6%B3%95/1.htm">算法</a><a class="tag" taget="_blank" href="/search/%E7%AE%97%E6%B3%95/1.htm">算法</a><a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a><a class="tag" taget="_blank" href="/search/leetcode/1.htm">leetcode</a> <div>文章目录1.特点2.学习中的侧重点2.1算法原理2.2模板2.2.1朴素二分模板(easy-->有局限)2.2.2查找左边界的二分模板2.2.3查找右边界的二分模板1.特点二分算法是最恶心,细节最多,最容易写出死循环的算法====但是,一旦掌握了之后,二分算法就是最简单的算法。其实并不是一定要二分,三分,四分也都可以,但是根据概率学中的求期望数学中可知,二分是效率最高的。如果是三分的话,我们就像是</div> </li> <li><a href="/article/1899385694812434432.htm" title="转基因大豆检测仪:快速精准识别,确保大豆安全品质" target="_blank">转基因大豆检测仪:快速精准识别,确保大豆安全品质</a> <span class="text-muted">tianhe8888_</span> <a class="tag" taget="_blank" href="/search/%E8%BD%AC%E5%9F%BA%E5%9B%A0%E6%A3%80%E6%B5%8B%E4%BB%AA/1.htm">转基因检测仪</a><a class="tag" taget="_blank" href="/search/%E8%BD%AC%E5%9F%BA%E5%9B%A0%E6%A3%80%E6%B5%8B%E8%AE%BE%E5%A4%87/1.htm">转基因检测设备</a> <div>【TH-ZJY1】在现代农业与食品工业中,转基因作物的安全性一直是公众关注的焦点。为了确保大豆及其制品的安全品质,转基因大豆检测仪应运而生。这种高科技设备以其快速、精准的检测能力,为大豆产业链的安全监管提供了有力支持。一、工作原理基因检测技术转基因大豆检测仪主要依赖于先进的基因检测技术,如聚合酶链反应(PCR)、荧光原位杂交(FISH)或基因芯片等。这些技术能够特异性地识别大豆DNA中的转基因片段</div> </li> <li><a href="/article/1899385441812017152.htm" title="雨滴谱仪:准确掌握降水情况,助力道路维护" target="_blank">雨滴谱仪:准确掌握降水情况,助力道路维护</a> <span class="text-muted">tianhe8888_</span> <a class="tag" taget="_blank" href="/search/%E9%9B%A8%E6%BB%B4%E8%B0%B1%E4%BB%AA/1.htm">雨滴谱仪</a><a class="tag" taget="_blank" href="/search/%E9%99%8D%E6%B0%B4%E5%A4%A9%E6%B0%94%E7%8E%B0%E8%B1%A1%E4%BB%AA/1.htm">降水天气现象仪</a><a class="tag" taget="_blank" href="/search/%E9%9B%A8%E6%BB%B4/1.htm">雨滴</a> <div>【TH-YD1】在气象监测与交通管理中,对道路降水情况的实时监测是至关重要的。雨滴谱仪作为一种高精度、智能化的降水天气现象监测设备,凭借其独特的工作原理和卓越的性能,在实时监测道路降水情况方面发挥着重要作用。一、引言降水是自然界中一种常见的天气现象,对道路交通、农业生产、城市排水等多个领域都有着重要影响。为了准确掌握降水情况,及时采取应对措施,科学家们研发了雨滴谱仪这一先进的气象监测设备。雨滴谱仪</div> </li> <li><a href="/article/1899381533098176512.htm" title="使用css画三角形" target="_blank">使用css画三角形</a> <span class="text-muted">伊小小小凡</span> <a class="tag" taget="_blank" href="/search/css/1.htm">css</a><a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a> <div>使用css画三角形在CSS中,可以通过利用border属性来创建三角形。其原理是通过设置一个元素的宽高为0,然后给其设置不同方向的边框,并将不需要的边框颜色设置为透明,从而形成三角形的形状。以下是使用CSS创建三角形的示例代码:基本三角形.triangle{width:0;height:0;border-left:50pxsolidtransparent;/*左边框*/border-right:5</div> </li> <li><a href="/article/1899379260221616128.htm" title="DeepSeek 助力 Vue3 开发:打造丝滑的表格(Table)之功能优化,添加列宽调整功能Table12" target="_blank">DeepSeek 助力 Vue3 开发:打造丝滑的表格(Table)之功能优化,添加列宽调整功能Table12</a> <span class="text-muted">宝码香车</span> <a class="tag" taget="_blank" href="/search/%23/1.htm">#</a><a class="tag" taget="_blank" href="/search/DeepSeek/1.htm">DeepSeek</a><a class="tag" taget="_blank" href="/search/javascript/1.htm">javascript</a><a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a><a class="tag" taget="_blank" href="/search/vue.js/1.htm">vue.js</a><a class="tag" taget="_blank" href="/search/ecmascript/1.htm">ecmascript</a><a class="tag" taget="_blank" href="/search/DeepSeek/1.htm">DeepSeek</a> <div>前言:哈喽,大家好,今天给大家分享一篇文章!并提供具体代码帮助大家深入理解,彻底掌握!创作不易,如果能帮助到大家或者给大家一些灵感和启发,欢迎收藏+关注哦目录DeepSeek助力Vue3开发:打造丝滑的表格(Table)之功能优化,添加列宽调整功能Table12页面效果指令输入think组件代码功能增强说明:注意事项:代码测试测试代码正常跑通,附其他基本代码编写路由src\router\index</div> </li> <li><a href="/article/1899378629184385024.htm" title="Android Glide 的显示与回调模块原理源码级深度剖析" target="_blank">Android Glide 的显示与回调模块原理源码级深度剖析</a> <span class="text-muted">&有梦想的咸鱼&</span> <a class="tag" taget="_blank" href="/search/Android/1.htm">Android</a><a class="tag" taget="_blank" href="/search/Glide%E5%8E%9F%E7%90%86/1.htm">Glide原理</a><a class="tag" taget="_blank" href="/search/Android%E5%BC%80%E5%8F%91%E5%A4%A7%E5%85%A8/1.htm">Android开发大全</a><a class="tag" taget="_blank" href="/search/android/1.htm">android</a><a class="tag" taget="_blank" href="/search/glide/1.htm">glide</a> <div>一、引言在当今的Android应用开发中,图片处理是一个至关重要的环节。从应用的图标展示到复杂的图片画廊,图片的加载和显示直接影响着用户体验。Glide作为一款功能强大且广泛使用的图片加载库,凭借其高效的性能、丰富的功能和简洁的API,成为了开发者的首选。其中,显示与回调模块更是Glide的核心部分,它负责将加载好的图片资源准确无误地显示在目标视图上,并在整个过程中提供各种回调机制,让开发者能够实</div> </li> <li><a href="/article/34.htm" title="Java 并发包之线程池和原子计数" target="_blank">Java 并发包之线程池和原子计数</a> <span class="text-muted">lijingyao8206</span> <a class="tag" taget="_blank" href="/search/Java%E8%AE%A1%E6%95%B0/1.htm">Java计数</a><a class="tag" taget="_blank" href="/search/ThreadPool/1.htm">ThreadPool</a><a class="tag" taget="_blank" href="/search/%E5%B9%B6%E5%8F%91%E5%8C%85/1.htm">并发包</a><a class="tag" taget="_blank" href="/search/java%E7%BA%BF%E7%A8%8B%E6%B1%A0/1.htm">java线程池</a> <div>对于大数据量关联的业务处理逻辑,比较直接的想法就是用JDK提供的并发包去解决多线程情况下的业务数据处理。线程池可以提供很好的管理线程的方式,并且可以提高线程利用率,并发包中的原子计数在多线程的情况下可以让我们避免去写一些同步代码。     这里就先把jdk并发包中的线程池处理器ThreadPoolExecutor 以原子计数类AomicInteger 和倒数计时锁C</div> </li> <li><a href="/article/161.htm" title="java编程思想 抽象类和接口" target="_blank">java编程思想 抽象类和接口</a> <span class="text-muted">百合不是茶</span> <a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/%E6%8A%BD%E8%B1%A1%E7%B1%BB/1.htm">抽象类</a><a class="tag" taget="_blank" href="/search/%E6%8E%A5%E5%8F%A3/1.htm">接口</a> <div>接口c++对接口和内部类只有简介的支持,但在java中有队这些类的直接支持   1 ,抽象类 :  如果一个类包含一个或多个抽象方法,该类必须限定为抽象类(否者编译器报错)   抽象方法 : 在方法中仅有声明而没有方法体    package com.wj.Interface; </div> </li> <li><a href="/article/288.htm" title="[房地产与大数据]房地产数据挖掘系统" target="_blank">[房地产与大数据]房地产数据挖掘系统</a> <span class="text-muted">comsci</span> <a class="tag" taget="_blank" href="/search/%E6%95%B0%E6%8D%AE%E6%8C%96%E6%8E%98/1.htm">数据挖掘</a> <div>        随着一个关键核心技术的突破,我们已经是独立自主的开发某些先进模块,但是要完全实现,还需要一定的时间...        所以,除了代码工作以外,我们还需要关心一下非技术领域的事件..比如说房地产     &nb</div> </li> <li><a href="/article/415.htm" title="数组队列总结" target="_blank">数组队列总结</a> <span class="text-muted">沐刃青蛟</span> <a class="tag" taget="_blank" href="/search/%E6%95%B0%E7%BB%84%E9%98%9F%E5%88%97/1.htm">数组队列</a> <div>      数组队列是一种大小可以改变,类型没有定死的类似数组的工具。不过与数组相比,它更具有灵活性。因为它不但不用担心越界问题,而且因为泛型(类似c++中模板的东西)的存在而支持各种类型。      以下是数组队列的功能实现代码:   import List.Student; public class</div> </li> <li><a href="/article/542.htm" title="Oracle存储过程无法编译的解决方法" target="_blank">Oracle存储过程无法编译的解决方法</a> <span class="text-muted">IT独行者</span> <a class="tag" taget="_blank" href="/search/oracle/1.htm">oracle</a><a class="tag" taget="_blank" href="/search/%E5%AD%98%E5%82%A8%E8%BF%87%E7%A8%8B%E3%80%80/1.htm">存储过程 </a> <div>今天同事修改Oracle存储过程又导致2个过程无法被编译,流程规范上的东西,Dave 这里不多说,看看怎么解决问题。   1.     查看无效对象 XEZF@xezf(qs-xezf-db1)> select object_name,object_type,status from all_objects where status='IN</div> </li> <li><a href="/article/669.htm" title="重装系统之后oracle恢复" target="_blank">重装系统之后oracle恢复</a> <span class="text-muted">文强chu</span> <a class="tag" taget="_blank" href="/search/oracle/1.htm">oracle</a> <div>前几天正在使用电脑,没有暂停oracle的各种服务。 突然win8.1系统奔溃,无法修复,开机时系统 提示正在搜集错误信息,然后再开机,再提示的无限循环中。 无耐我拿出系统u盘 准备重装系统,没想到竟然无法从u盘引导成功。 晚上到外面早了一家修电脑店,让人家给装了个系统,并且那哥们在我没反应过来的时候, 直接把我的c盘给格式化了 并且清理了注册表,再装系统。 然后的结果就是我的oracl</div> </li> <li><a href="/article/796.htm" title="python学习二( 一些基础语法)" target="_blank">python学习二( 一些基础语法)</a> <span class="text-muted">小桔子</span> <a class="tag" taget="_blank" href="/search/pthon/1.htm">pthon</a><a class="tag" taget="_blank" href="/search/%E5%9F%BA%E7%A1%80%E8%AF%AD%E6%B3%95/1.htm">基础语法</a> <div>紧接着把!昨天没看继续看django 官方教程,学了下python的基本语法 与c类语言还是有些小差别: 1.ptyhon的源文件以UTF-8编码格式 2. /   除 结果浮点型 //  除 结果整形 %   除 取余数 *   乘 **  乘方 eg 5**2 结果是5的2次方25 _&</div> </li> <li><a href="/article/923.htm" title="svn 常用命令" target="_blank">svn 常用命令</a> <span class="text-muted">aichenglong</span> <a class="tag" taget="_blank" href="/search/SVN/1.htm">SVN</a><a class="tag" taget="_blank" href="/search/%E7%89%88%E6%9C%AC%E5%9B%9E%E9%80%80/1.htm">版本回退</a> <div>1 svn回退版本   1)在window中选择log,根据想要回退的内容,选择revert this version或revert chanages from this version 两者的区别:   revert this version:表示回退到当前版本(该版本后的版本全部作废)   revert chanages from this versio</div> </li> <li><a href="/article/1050.htm" title="某小公司面试归来" target="_blank">某小公司面试归来</a> <span class="text-muted">alafqq</span> <a class="tag" taget="_blank" href="/search/%E9%9D%A2%E8%AF%95/1.htm">面试</a> <div>先填单子,还要写笔试题,我以时间为急,拒绝了它。。时间宝贵。 老拿这些对付毕业生的东东来吓唬我。。 面试官很刁难,问了几个问题,记录下; 1,包的范围。。。public,private,protect. --悲剧了 2,hashcode方法和equals方法的区别。谁覆盖谁.结果,他说我说反了。 3,最恶心的一道题,抽象类继承抽象类吗?(察,一般它都是被继承的啊) 4,stru</div> </li> <li><a href="/article/1177.htm" title="动态数组的存储速度比较 集合框架" target="_blank">动态数组的存储速度比较 集合框架</a> <span class="text-muted">百合不是茶</span> <a class="tag" taget="_blank" href="/search/%E9%9B%86%E5%90%88%E6%A1%86%E6%9E%B6/1.htm">集合框架</a> <div>集合框架: 自定义数据结构(增删改查等) package 数组; /** * 创建动态数组 * @author 百合 * */ public class ArrayDemo{ //定义一个数组来存放数据 String[] src = new String[0]; /** * 增加元素加入容器 * @param s要加入容器</div> </li> <li><a href="/article/1304.htm" title="用JS实现一个JS对象,对象里有两个属性一个方法" target="_blank">用JS实现一个JS对象,对象里有两个属性一个方法</a> <span class="text-muted">bijian1013</span> <a class="tag" taget="_blank" href="/search/js%E5%AF%B9%E8%B1%A1/1.htm">js对象</a> <div><html> <head> </head> <body> 用js代码实现一个js对象,对象里有两个属性,一个方法 </body> <script> var obj={a:'1234567',b:'bbbbbbbbbb',c:function(x){ </div> </li> <li><a href="/article/1431.htm" title="探索JUnit4扩展:使用Rule" target="_blank">探索JUnit4扩展:使用Rule</a> <span class="text-muted">bijian1013</span> <a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/%E5%8D%95%E5%85%83%E6%B5%8B%E8%AF%95/1.htm">单元测试</a><a class="tag" taget="_blank" href="/search/JUnit/1.htm">JUnit</a><a class="tag" taget="_blank" href="/search/Rule/1.htm">Rule</a> <div>        在上一篇文章中,讨论了使用Runner扩展JUnit4的方式,即直接修改Test Runner的实现(BlockJUnit4ClassRunner)。但这种方法显然不便于灵活地添加或删除扩展功能。下面将使用JUnit4.7才开始引入的扩展方式——Rule来实现相同的扩展功能。 1. Rule       &n</div> </li> <li><a href="/article/1558.htm" title="[Gson一]非泛型POJO对象的反序列化" target="_blank">[Gson一]非泛型POJO对象的反序列化</a> <span class="text-muted">bit1129</span> <a class="tag" taget="_blank" href="/search/POJO/1.htm">POJO</a> <div>当要将JSON数据串反序列化自身为非泛型的POJO时,使用Gson.fromJson(String, Class)方法。自身为非泛型的POJO的包括两种: 1. POJO对象不包含任何泛型的字段 2. POJO对象包含泛型字段,例如泛型集合或者泛型类 Data类 a.不是泛型类, b.Data中的集合List和Map都是泛型的 c.Data中不包含其它的POJO    </div> </li> <li><a href="/article/1685.htm" title="【Kakfa五】Kafka Producer和Consumer基本使用" target="_blank">【Kakfa五】Kafka Producer和Consumer基本使用</a> <span class="text-muted">bit1129</span> <a class="tag" taget="_blank" href="/search/kafka/1.htm">kafka</a> <div>0.Kafka服务器的配置 一个Broker, 一个Topic Topic中只有一个Partition()   1. Producer: package kafka.examples.producers; import kafka.producer.KeyedMessage; import kafka.javaapi.producer.Producer; impor</div> </li> <li><a href="/article/1812.htm" title="lsyncd实时同步搭建指南——取代rsync+inotify" target="_blank">lsyncd实时同步搭建指南——取代rsync+inotify</a> <span class="text-muted">ronin47</span> <div>1. 几大实时同步工具比较 1.1 inotify + rsync 最近一直在寻求生产服务服务器上的同步替代方案,原先使用的是 inotify + rsync,但随着文件数量的增大到100W+,目录下的文件列表就达20M,在网络状况不佳或者限速的情况下,变更的文件可能10来个才几M,却因此要发送的文件列表就达20M,严重减低的带宽的使用效率以及同步效率;更为要紧的是,加入inotify</div> </li> <li><a href="/article/1939.htm" title="java-9. 判断整数序列是不是二元查找树的后序遍历结果" target="_blank">java-9. 判断整数序列是不是二元查找树的后序遍历结果</a> <span class="text-muted">bylijinnan</span> <a class="tag" taget="_blank" href="/search/java/1.htm">java</a> <div> public class IsBinTreePostTraverse{ static boolean isBSTPostOrder(int[] a){ if(a==null){ return false; } /*1.只有一个结点时,肯定是查找树 *2.只有两个结点时,肯定是查找树。例如{5,6}对应的BST是 6 {6,5}对应的BST是</div> </li> <li><a href="/article/2066.htm" title="MySQL的sum函数返回的类型" target="_blank">MySQL的sum函数返回的类型</a> <span class="text-muted">bylijinnan</span> <a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/spring/1.htm">spring</a><a class="tag" taget="_blank" href="/search/sql/1.htm">sql</a><a class="tag" taget="_blank" href="/search/mysql/1.htm">mysql</a><a class="tag" taget="_blank" href="/search/jdbc/1.htm">jdbc</a> <div>今天项目切换数据库时,出错 访问数据库的代码大概是这样: String sql = "select sum(number) as sumNumberOfOneDay from tableName"; List<Map> rows = getJdbcTemplate().queryForList(sql); for (Map row : rows</div> </li> <li><a href="/article/2193.htm" title="java设计模式之单例模式" target="_blank">java设计模式之单例模式</a> <span class="text-muted">chicony</span> <a class="tag" taget="_blank" href="/search/java%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/1.htm">java设计模式</a> <div> 在阎宏博士的《JAVA与模式》一书中开头是这样描述单例模式的:   作为对象的创建模式,单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。这个类称为单例类。 单例模式的结构   单例模式的特点: 单例类只能有一个实例。 单例类必须自己创建自己的唯一实例。 单例类必须给所有其他对象提供这一实例。   饿汉式单例类   publ</div> </li> <li><a href="/article/2320.htm" title="javascript取当月最后一天" target="_blank">javascript取当月最后一天</a> <span class="text-muted">ctrain</span> <a class="tag" taget="_blank" href="/search/JavaScript/1.htm">JavaScript</a> <div> <!--javascript取当月最后一天--> <script language=javascript> var current = new Date(); var year = current.getYear(); var month = current.getMonth(); showMonthLastDay(year, mont</div> </li> <li><a href="/article/2447.htm" title="linux tune2fs命令详解" target="_blank">linux tune2fs命令详解</a> <span class="text-muted">daizj</span> <a class="tag" taget="_blank" href="/search/linux/1.htm">linux</a><a class="tag" taget="_blank" href="/search/tune2fs/1.htm">tune2fs</a><a class="tag" taget="_blank" href="/search/%E6%9F%A5%E7%9C%8B%E7%B3%BB%E7%BB%9F%E6%96%87%E4%BB%B6%E5%9D%97%E4%BF%A1%E6%81%AF/1.htm">查看系统文件块信息</a> <div>一.简介: tune2fs是调整和查看ext2/ext3文件系统的文件系统参数,Windows下面如果出现意外断电死机情况,下次开机一般都会出现系统自检。Linux系统下面也有文件系统自检,而且是可以通过tune2fs命令,自行定义自检周期及方式。 二.用法: Usage: tune2fs [-c max_mounts_count] [-e errors_behavior] [-g grou</div> </li> <li><a href="/article/2574.htm" title="做有中国特色的程序员" target="_blank">做有中国特色的程序员</a> <span class="text-muted">dcj3sjt126com</span> <a class="tag" taget="_blank" href="/search/%E7%A8%8B%E5%BA%8F%E5%91%98/1.htm">程序员</a> <div>  从出版业说起 网络作品排到靠前的,都不会太难看,一般人不爱看某部作品也是因为不喜欢这个类型,而此人也不会全不喜欢这些网络作品。究其原因,是因为网络作品都是让人先白看的,看的好了才出了头。而纸质作品就不一定了,排行榜靠前的,有好作品,也有垃圾。 许多大牛都是写了博客,后来出了书。这些书也都不次,可能有人让为不好,是因为技术书不像小说,小说在读故事,技术书是在学知识或温习知识,有</div> </li> <li><a href="/article/2701.htm" title="Android:TextView属性大全" target="_blank">Android:TextView属性大全</a> <span class="text-muted">dcj3sjt126com</span> <a class="tag" taget="_blank" href="/search/textview/1.htm">textview</a> <div>android:autoLink    设置是否当文本为URL链接/email/电话号码/map时,文本显示为可点击的链接。可选值(none/web/email/phone/map/all)  android:autoText    如果设置,将自动执行输入值的拼写纠正。此处无效果,在显示输入法并输</div> </li> <li><a href="/article/2828.htm" title="tomcat虚拟目录安装及其配置" target="_blank">tomcat虚拟目录安装及其配置</a> <span class="text-muted">eksliang</span> <a class="tag" taget="_blank" href="/search/tomcat%E9%85%8D%E7%BD%AE%E8%AF%B4%E6%98%8E/1.htm">tomcat配置说明</a><a class="tag" taget="_blank" href="/search/tomca%E9%83%A8%E7%BD%B2web%E5%BA%94%E7%94%A8/1.htm">tomca部署web应用</a><a class="tag" taget="_blank" href="/search/tomcat%E8%99%9A%E6%8B%9F%E7%9B%AE%E5%BD%95%E5%AE%89%E8%A3%85/1.htm">tomcat虚拟目录安装</a> <div>转载请出自出处:http://eksliang.iteye.com/blog/2097184 1.-------------------------------------------tomcat  目录结构 config:存放tomcat的配置文件 temp  :存放tomcat跑起来后存放临时文件用的 work   : 当第一次访问应用中的jsp</div> </li> <li><a href="/article/2955.htm" title="浅谈:APP有哪些常被黑客利用的安全漏洞" target="_blank">浅谈:APP有哪些常被黑客利用的安全漏洞</a> <span class="text-muted">gg163</span> <a class="tag" taget="_blank" href="/search/APP/1.htm">APP</a> <div>首先,说到APP的安全漏洞,身为程序猿的大家应该不陌生;如果抛开安卓自身开源的问题的话,其主要产生的原因就是开发过程中疏忽或者代码不严谨引起的。但这些责任也不能怪在程序猿头上,有时会因为BOSS时间催得紧等很多可观原因。由国内移动应用安全检测团队爱内测(ineice.com)的CTO给我们浅谈关于Android 系统的开源设计以及生态环境。 1. 应用反编译漏洞:APK 包非常容易被反编译成可读</div> </li> <li><a href="/article/3082.htm" title="C#根据网址生成静态页面" target="_blank">C#根据网址生成静态页面</a> <span class="text-muted">hvt</span> <a class="tag" taget="_blank" href="/search/Web/1.htm">Web</a><a class="tag" taget="_blank" href="/search/.net/1.htm">.net</a><a class="tag" taget="_blank" href="/search/C%23/1.htm">C#</a><a class="tag" taget="_blank" href="/search/asp.net/1.htm">asp.net</a><a class="tag" taget="_blank" href="/search/hovertree/1.htm">hovertree</a> <div>HoverTree开源项目中HoverTreeWeb.HVTPanel的Index.aspx文件是后台管理的首页。包含生成留言板首页,以及显示用户名,退出等功能。根据网址生成页面的方法:   bool CreateHtmlFile(string url, string path) { //http://keleyi.com/a/bjae/3d10wfax.htm stri</div> </li> <li><a href="/article/3209.htm" title="SVG 教程 (一)" target="_blank">SVG 教程 (一)</a> <span class="text-muted">天梯梦</span> <a class="tag" taget="_blank" href="/search/svg/1.htm">svg</a> <div>SVG 简介 SVG 是使用 XML 来描述二维图形和绘图程序的语言。 学习之前应具备的基础知识: 继续学习之前,你应该对以下内容有基本的了解: HTML XML 基础 如果希望首先学习这些内容,请在本站的首页选择相应的教程。 什么是SVG? SVG 指可伸缩矢量图形 (Scalable Vector Graphics) SVG 用来定义用于网络的基于矢量</div> </li> <li><a href="/article/3336.htm" title="一个简单的java栈" target="_blank">一个简单的java栈</a> <span class="text-muted">luyulong</span> <a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/1.htm">数据结构</a><a class="tag" taget="_blank" href="/search/%E6%A0%88/1.htm">栈</a> <div> public class MyStack { private long[] arr; private int top; public MyStack() { arr = new long[10]; top = -1; } public MyStack(int maxsize) { arr = new long[maxsize]; top </div> </li> <li><a href="/article/3463.htm" title="基础数据结构和算法八:Binary search" target="_blank">基础数据结构和算法八:Binary search</a> <span class="text-muted">sunwinner</span> <a class="tag" taget="_blank" href="/search/Algorithm/1.htm">Algorithm</a><a class="tag" taget="_blank" href="/search/Binary+search/1.htm">Binary search</a> <div>Binary search needs an ordered array so that it can use array indexing to dramatically reduce the number of compares required for each search, using the classic and venerable binary search algori</div> </li> <li><a href="/article/3590.htm" title="12个C语言面试题,涉及指针、进程、运算、结构体、函数、内存,看看你能做出几个!" target="_blank">12个C语言面试题,涉及指针、进程、运算、结构体、函数、内存,看看你能做出几个!</a> <span class="text-muted">刘星宇</span> <a class="tag" taget="_blank" href="/search/c/1.htm">c</a><a class="tag" taget="_blank" href="/search/%E9%9D%A2%E8%AF%95/1.htm">面试</a> <div>12个C语言面试题,涉及指针、进程、运算、结构体、函数、内存,看看你能做出几个! 1.gets()函数 问:请找出下面代码里的问题: #include<stdio.h> int main(void) {     char buff[10];     memset(buff,0,sizeof(buff)); </div> </li> <li><a href="/article/3717.htm" title="ITeye 7月技术图书有奖试读获奖名单公布" target="_blank">ITeye 7月技术图书有奖试读获奖名单公布</a> <span class="text-muted">ITeye管理员</span> <a class="tag" taget="_blank" href="/search/%E6%B4%BB%E5%8A%A8/1.htm">活动</a><a class="tag" taget="_blank" href="/search/ITeye/1.htm">ITeye</a><a class="tag" taget="_blank" href="/search/%E8%AF%95%E8%AF%BB/1.htm">试读</a> <div>ITeye携手人民邮电出版社图灵教育共同举办的7月技术图书有奖试读活动已圆满结束,非常感谢广大用户对本次活动的关注与参与。 7月试读活动回顾: http://webmaster.iteye.com/blog/2092746 本次技术图书试读活动的优秀奖获奖名单及相应作品如下(优秀文章有很多,但名额有限,没获奖并不代表不优秀): 《Java性能优化权威指南》 </div> </li> </ul> </div> </div> </div> <div> <div class="container"> <div class="indexes"> <strong>按字母分类:</strong> <a href="/tags/A/1.htm" target="_blank">A</a><a href="/tags/B/1.htm" target="_blank">B</a><a href="/tags/C/1.htm" target="_blank">C</a><a href="/tags/D/1.htm" target="_blank">D</a><a href="/tags/E/1.htm" target="_blank">E</a><a href="/tags/F/1.htm" target="_blank">F</a><a href="/tags/G/1.htm" target="_blank">G</a><a href="/tags/H/1.htm" target="_blank">H</a><a href="/tags/I/1.htm" target="_blank">I</a><a href="/tags/J/1.htm" target="_blank">J</a><a href="/tags/K/1.htm" target="_blank">K</a><a href="/tags/L/1.htm" target="_blank">L</a><a href="/tags/M/1.htm" target="_blank">M</a><a href="/tags/N/1.htm" target="_blank">N</a><a href="/tags/O/1.htm" target="_blank">O</a><a href="/tags/P/1.htm" target="_blank">P</a><a href="/tags/Q/1.htm" target="_blank">Q</a><a href="/tags/R/1.htm" target="_blank">R</a><a href="/tags/S/1.htm" target="_blank">S</a><a href="/tags/T/1.htm" target="_blank">T</a><a href="/tags/U/1.htm" target="_blank">U</a><a href="/tags/V/1.htm" target="_blank">V</a><a href="/tags/W/1.htm" target="_blank">W</a><a href="/tags/X/1.htm" target="_blank">X</a><a href="/tags/Y/1.htm" target="_blank">Y</a><a href="/tags/Z/1.htm" target="_blank">Z</a><a href="/tags/0/1.htm" target="_blank">其他</a> </div> </div> </div> <footer id="footer" class="mb30 mt30"> <div class="container"> <div class="footBglm"> <a target="_blank" href="/">首页</a> - <a target="_blank" href="/custom/about.htm">关于我们</a> - <a target="_blank" href="/search/Java/1.htm">站内搜索</a> - <a target="_blank" href="/sitemap.txt">Sitemap</a> - <a target="_blank" href="/custom/delete.htm">侵权投诉</a> </div> <div class="copyright">版权所有 IT知识库 CopyRight © 2000-2050 E-COM-NET.COM , All Rights Reserved. <!-- <a href="https://beian.miit.gov.cn/" rel="nofollow" target="_blank">京ICP备09083238号</a><br>--> </div> </div> </footer> <!-- 代码高亮 --> <script type="text/javascript" src="/static/syntaxhighlighter/scripts/shCore.js"></script> <script type="text/javascript" src="/static/syntaxhighlighter/scripts/shLegacy.js"></script> <script type="text/javascript" src="/static/syntaxhighlighter/scripts/shAutoloader.js"></script> <link type="text/css" rel="stylesheet" href="/static/syntaxhighlighter/styles/shCoreDefault.css"/> <script type="text/javascript" src="/static/syntaxhighlighter/src/my_start_1.js"></script> </body> </html>