首页 web前端 html教程 前端响应式编程的方案及其缺点的详细介绍(附代码)

前端响应式编程的方案及其缺点的详细介绍(附代码)

Aug 14, 2018 pm 03:14 PM

本篇文章给大家带来的内容是关于前端响应式编程的方案及其缺点的详细介绍(附代码),有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。

现实世界有很多是以响应式的方式运作的,例如我们会在收到他人的提问,然后做出响应,给出相应的回答。在开发过程中我也应用了大量的响应式设计,积累了一些经验,希望能抛砖引玉。

响应式编程(Reactive Programming)和普通的编程思路的主要区别在于,响应式以推(push)的方式运作,而非响应式的编程思路以拉(pull)的方式运作。例如,事件就是一个很常见的响应式编程,我们通常会这么做:

button.on('click', () => {  
    // ...})
登录后复制

而非响应式方式下,就会变成这样:

while (true) {  
    if (button.clicked) {        // ...
    }
}
登录后复制

显然,无论在是代码的优雅度还是执行效率上,非响应式的方式都不如响应式的设计。

Event Emitter

Event Emitter是大多数人都很熟悉的事件实现,它很简单也很实用,我们可以利用Event Emitter实现简单的响应式设计,例如下面这个异步搜索:

class Input extends Component {  
    state = {        value: ''
    }
    onChange = e => {        this.props.events.emit('onChange', e.target.value)
    }
    afterChange = value => {        this.setState({
            value
        })
    }
    componentDidMount() {        this.props.events.on('onChange', this.afterChange)
    }
    componentWillUnmount() {        this.props.events.off('onChange', this.afterChange)
    }
    render() {        
    const { value } = this.state        
    return (            <input value={value} onChange={this.onChange} />
        )
    }
}
class Search extends Component {  
    doSearch = (value) => {
        ajax(/* ... */).then(list => this.setState({
            list
        }))
    }
    componentDidMount() {
        this.props.events.on(&#39;onChange&#39;, this.doSearch)
    }
    componentWillUnmount() {
        this.props.events.off(&#39;onChange&#39;, this.doSearch)
    }
    render() {
        const { list } = this.state
        return (            <ul>
                {list.map(item => <li key={item.id}>{item.value}</li>)}            </ul>
        )
    }
}
登录后复制

这里我们会发现用Event Emitter的实现有很多缺点,需要我们手动在componentWillUnmount里进行资源的释放。它的表达能力不足,例如我们在搜索的时候需要聚合多个数据源的时候:

class Search extends Component {  
    foo = &#39;&#39;
    bar = &#39;&#39;
    doSearch = () => {
        ajax({
            foo,
            bar
        }).then(list => this.setState({
            list
        }))
    }
    fooChange = value => {        this.foo = value        this.doSearch()
    }
    barChange = value => {        this.bar = value        this.doSearch()
    }
    componentDidMount() {        this.props.events.on(&#39;fooChange&#39;, this.fooChange)        this.props.events.on(&#39;barChange&#39;, this.barChange)
    }
    componentWillUnmount() {        this.props.events.off(&#39;fooChange&#39;, this.fooChange)        this.props.events.off(&#39;barChange&#39;, this.barChange)
    }
    render() {        // ...
    }
}
登录后复制

显然开发效率很低。

Redux

Redux采用了一个事件流的方式实现响应式,在Redux中由于reducer必须是纯函数,因此要实现响应式的方式只有订阅中或者是在中间件中。

如果通过订阅store的方式,由于Redux不能准确拿到哪一个数据放生了变化,因此只能通过脏检查的方式。例如:

function createWatcher(mapState, callback) {  
    let previousValue = null
    return (store) => {
        store.subscribe(() => {            const value = mapState(store.getState())            if (value !== previousValue) {
                callback(value)
            }
            previousValue = value
        })
    }
}const watcher = createWatcher(state => {  
    // ...}, () => {    // ...})
登录后复制

watcher(store)

这个方法有两个缺点,一是在数据很复杂且数据量比较大的时候会有效率上的问题;二是,如果mapState函数依赖上下文的话,就很难办了。在react-redux中,connect函数中mapStateToProps的第二个参数是props,可以通过上层组件传入props来获得需要的上下文,但是这样监听者就变成了React的组件,会随着组件的挂载和卸载被创建和销毁,如果我们希望这个响应式和组件无关的话就有问题了。

另一种方式就是在中间件中监听数据变化。得益于Redux的设计,我们通过监听特定的事件(Action)就可以得到对应的数据变化。

const search = () => (dispatch, getState) => {  
    // ...}const middleware = ({ dispatch }) => next => action => {  
    switch action.type {        case &#39;FOO_CHANGE&#39;:        case &#39;BAR_CHANGE&#39;: {            const nextState = next(action)            // 在本次dispatch完成以后再去进行新的dispatch
            setTimeout(() => dispatch(search()), 0)            return nextState
        }        default:            return next(action)
    }
}
登录后复制

这个方法能解决大多数的问题,但是在Redux中,中间件和reducer实际上隐式订阅了所有的事件(Action),这显然是有些不合理的,虽然在没有性能问题的前提下是完全可以接受的。

面向对象的响应式

ECMASCRIPT 5.1引入了getter和setter,我们可以通过getter和setter实现一种响应式。

class Model {  
    _foo = &#39;&#39;
    get foo() {        return this._foo
    }
    set foo(value) {        this._foo = value        this.search()
    }
    search() {        // ...
    }
}// 当然如果没有getter和setter的话也可以通过这种方式实现class Model {  
    foo = &#39;&#39;
    getFoo() {        return this.foo
    }
    setFoo(value) {        this.foo = value        this.search()
    }
    search() {        // ...
    }
}
登录后复制

Mobx和Vue就使用了这样的方式实现响应式。当然,如果不考虑兼容性的话我们还可以使用Proxy。

当我们需要响应若干个值然后得到一个新值的话,在Mobx中我们可以这么做:

class Model {  
    @observable hour = &#39;00&#39;
    @observable minute = &#39;00&#39;
    @computed get time() {        return `${this.hour}:${this.minute}`
    }
}
登录后复制

Mobx会在运行时收集time依赖了哪些值,并在这些值发生改变(触发setter)的时候重新计算time的值,显然要比EventEmitter的做法方便高效得多,相对Redux的middleware更直观。

但是这里也有一个缺点,基于getter的computed属性只能描述y = f(x)的情形,但是现实中很多情况f是一个异步函数,那么就会变成y = await f(x),对于这种情形getter就无法描述了。

对于这种情形,我们可以通过Mobx提供的autorun来实现:

class Model {  
    @observable keyword = &#39;&#39;
    @observable searchResult = []    constructor() {
        autorun(() => {            // ajax ...
        })
    }
}
登录后复制

由于运行时的依赖收集过程完全是隐式的,这里经常会遇到一个问题就是收集到意外的依赖:

class Model {  
    @observable loading = false
    @observable keyword = &#39;&#39;
    @observable searchResult = []    constructor() {
        autorun(() => {            if (this.loading) {                return
            }            // ajax ...
        })
    }
}
登录后复制

显然这里loading不应该被搜索的autorun收集到,为了处理这个问题就会多出一些额外的代码,而多余的代码容易带来犯错的机会。 或者,我们也可以手动指定需要的字段,但是这种方式就不得不多出一些额外的操作:

class Model {  
    @observable loading = false
    @observable keyword = &#39;&#39;
    @observable searchResult = []
    disposers = []
    fetch = () => {        // ...
    }
    dispose() {        this.disposers.forEach(disposer => disposer())
    }    constructor() {        this.disposers.push(
            observe(this, &#39;loading&#39;, this.fetch),
            observe(this, &#39;keyword&#39;, this.fetch)
        )
    }
}class FooComponent extends Component {  
    this.mode = new Model()
    componentWillUnmount() {        this.state.model.dispose()
    }    // ...}
登录后复制

而当我们需要对时间轴做一些描述时,Mobx就有些力不从心了,例如需要延迟5秒再进行搜索。

相关推荐:

拼图响应式前端框架版响应式后台正式发布_html/css_WEB-ITnose

使用很简单的响应式前端开发框架_html/css_WEB-ITnose


以上是前端响应式编程的方案及其缺点的详细介绍(附代码)的详细内容。更多信息请关注PHP中文网其他相关文章!

本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

Video Face Swap

Video Face Swap

使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

HTML容易为初学者学习吗? HTML容易为初学者学习吗? Apr 07, 2025 am 12:11 AM

HTML适合初学者学习,因为它简单易学且能快速看到成果。1)HTML的学习曲线平缓,易于上手。2)只需掌握基本标签即可开始创建网页。3)灵活性高,可与CSS和JavaScript结合使用。4)丰富的学习资源和现代工具支持学习过程。

HTML,CSS和JavaScript的角色:核心职责 HTML,CSS和JavaScript的角色:核心职责 Apr 08, 2025 pm 07:05 PM

HTML定义网页结构,CSS负责样式和布局,JavaScript赋予动态交互。三者在网页开发中各司其职,共同构建丰富多彩的网站。

了解HTML,CSS和JavaScript:初学者指南 了解HTML,CSS和JavaScript:初学者指南 Apr 12, 2025 am 12:02 AM

WebDevelovermentReliesonHtml,CSS和JavaScript:1)HTMLStructuresContent,2)CSSStyleSIT和3)JavaScriptAddSstractivity,形成thebasisofmodernWebemodernWebExexperiences。

HTML中起始标签的示例是什么? HTML中起始标签的示例是什么? Apr 06, 2025 am 12:04 AM

AnexampleOfAstartingTaginHtmlis,beginSaparagraph.startingTagSareEssentialInhtmlastheyInitiateEllements,defiteTheeTheErtypes,andarecrucialforsstructuringwebpages wepages webpages andConstructingthedom。

Gitee Pages静态网站部署失败:单个文件404错误如何排查和解决? Gitee Pages静态网站部署失败:单个文件404错误如何排查和解决? Apr 04, 2025 pm 11:54 PM

GiteePages静态网站部署失败:404错误排查与解决在使用Gitee...

如何用CSS3和JavaScript实现图片点击后周围图片散开并放大效果? 如何用CSS3和JavaScript实现图片点击后周围图片散开并放大效果? Apr 05, 2025 am 06:15 AM

实现图片点击后周围图片散开并放大效果许多网页设计中,需要实现一种交互效果:点击某张图片,使其周围的...

网页批注如何实现Y轴位置的自适应布局? 网页批注如何实现Y轴位置的自适应布局? Apr 04, 2025 pm 11:30 PM

网页批注功能的Y轴位置自适应算法本文将探讨如何实现类似Word文档的批注功能,特别是如何处理批注之间的间�...

HTML,CSS和JavaScript:Web开发人员的基本工具 HTML,CSS和JavaScript:Web开发人员的基本工具 Apr 09, 2025 am 12:12 AM

HTML、CSS和JavaScript是Web开发的三大支柱。1.HTML定义网页结构,使用标签如、等。2.CSS控制网页样式,使用选择器和属性如color、font-size等。3.JavaScript实现动态效果和交互,通过事件监听和DOM操作。

See all articles