阅读 222

前端Rollup+RxJs响应式编程实践

响应式编程

以下概念介绍,基于 node v10.16.3 版本进行。不满足条件的,请移步 google or 度娘。

概念介绍

const a = 1,b = 2;
const c = a+ b;
复制代码

响应式编程,当a,b再次变化时,c会再次做出修改。

背景

  • RxJS是一个用于使用Observables进行响应式编程的库
  • 可简化编写异步或基于回调的代码

本地环境搭建

  • node
  • npm script
  • rollup
npm install --global rollup
复制代码
  • rxjs
npm install rxjs
复制代码
  • es6 语法,利用tree-shaking ,缩减代码

使用

hello-world 之123

import { of } from 'rxjs';
import { map } from 'rxjs/operators';
of(1,2,3).subscribe(res => {
    console.log(res);
});
复制代码

rollup打包→ bundle.js → node bundle.js,连续输出123

rollup main.js --file bundle.js --format cjs
复制代码
'use strict';

var rxjs = require('rxjs');
require('rxjs/operators');

rxjs.of(1,2,3).subscribe(res => {
    console.log(res);
});
复制代码

优化rollup配置

创建rollup.config.js

export default {
  input: 'main.js',
  output: {
    file: 'bundle.js',
    format: 'cjs'
  }
};
复制代码
rollup -c rollup.config.js 
复制代码

npm script支持

package.json

 {
 "name": "rxjs-demo",
  "version": "1.0.0",
  "description": "",
  "main": "test.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build":"rollup -c", // 默认配置 rollup --config  rollup.config.js
    "start":"npm run build && node bundle.js"   // && 是继发,& 是并发
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "rxjs": "^6.5.3"
  }
}
复制代码

最终的运行如下:

npm start 
复制代码

Rxjs读取package.json字段version

// install plugin
npm install --save-dev rollup-plugin-json
复制代码
// rollup.config.js
import json from 'rollup-plugin-json';
export default {
	input: 'main.js',
	output: {
		file: 'bundle.js',
		format: 'cjs'
	},
	plugins:[json()]
};
复制代码
// main.js
import { of } from 'rxjs';
import { map } from 'rxjs/operators';
import { version } from './package.json';

of(1,2,3,version).subscribe(res => {
    console.log(res);
});
复制代码

pipe管道在输出前修改数据

import { of } from 'rxjs';
import { map } from 'rxjs/operators';
import { version } from './package.json';

of(1,2,3,version).pipe(map(item => item + ' through pipe use map'))
.subscribe(res => {
    console.log(res);
});
复制代码

用Observable改写of

其中,可以看到observable属于异步执行。

import { of,Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { version } from './package.json';

//through pipe 
// of(1,2,3,version).pipe(map(item => item + ' through pipe use map'))
// .subscribe(res => {
//     console.log(res);
// });

console.log("before observable....");
const publisher$ = new  Observable(suber => {
    console.log("in observable....");
    suber.next(1);
    suber.next(2);
    suber.next(3);
    suber.next(version);
});
console.log("after observable....");
publisher$.pipe(map(item => item + ' through pipe use map')).subscribe(res => {
    console.log(res);
});
复制代码

subscribe订阅几次,通知执行几次,而不是仅消费一次

console.log("before observable....");
const publisher$ = new  Observable(suber => {
    console.log("in observable....");
    suber.next(1);
    suber.next(2);
    suber.next(3);
    suber.next(version);
});
console.log("after observable....");
publisher$.pipe(map(item => item + ' through pipe use map')).subscribe(res => {
    console.log(res);
});
publisher$.subscribe(res => {
    console.log("subscribe second..." + res);
});
复制代码

hot subscribe - 与时间先后有关,订阅内容差

import { of,Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { version } from './package.json';

let count = 0;
console.log("before observable....");
const publisher$ = new  Observable(suber => {
    console.log("in observable....");
    setInterval(() => {
        count++;
        suber.next(count);
    }, 100);
});
console.log("after observable....");
publisher$.pipe(map(item => 'first ...'+ item)).subscribe(res => {
    console.log(res);
});

setTimeout(() => {
    const publisher2$ = new Observable(suber => {
        setInterval(() => {
            suber.next(count);
        }, 100);
    });
    publisher2$.subscribe(res => {
        console.log("second ..." + res);
    }); 
}, 1000);

复制代码

cold subscribe -- 时间先后无关,内容无差异

import { of,Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { version } from './package.json';


console.log("before observable....");
const publisher$ = new  Observable(suber => {
    console.log("in observable....");
    setInterval(() => {
        let count = 0;
        count++;
        suber.next(count);
    }, 100);
});
console.log("after observable....");
publisher$.pipe(map(item => 'first ...'+ item)).subscribe(res => {
    console.log(res);
});

setTimeout(() => {
    const publisher2$ = new Observable(suber => {
        setInterval(() => {
            let count = 0;
            count++;
            suber.next(count);
        }, 100);
    });
    publisher2$.subscribe(res => {
        console.log("second ..." + res);
    }); 
}, 1000);

复制代码

observable状态终止

import { of,Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { version } from './package.json';


const publiser$ = new Observable(suber => {
    suber.next(1);
    suber.next(2);
    suber.next(3);
    suber.next(version);
    // suber.complete();
    suber.error();
});
publiser$.subscribe({
    next:res => {
        console.log(`next ...${res}`); 
    },
    complete:() => {
        console.log("complete done....");
    },
    error: () => {
        console.log("error occur....");
    }
});
复制代码

next中异常,不会进入error

import { of,Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { version } from './package.json';


const publiser$ = new Observable(suber => {
    suber.next(1);
    suber.next(2);
    suber.next(3);
    suber.next(version);
    // suber.complete();
    suber.error();
});
publiser$.subscribe({
    next:res => {
        console.log(`next ...${res}`);  # 此处发生异常,会抛出,不会进入error.
    },
    complete:() => {
        console.log("complete done...."); 
    },
    error: () => {
        console.log("error occur....");# error的发生由于publiser$处,触发的。
    }
});
复制代码

error推荐添加

# 缺少error处理,会出现如下异常提示:
Uncaught TypeError: Cannot read property 'ngOriginalError' of undefined
at getOriginalError (vendor.js?v=yC_pUFx9rDW_jX8REnKHb_rOOg7g5ohUM-Y5-vW8rlQ:1811)
at ErrorHandler._findOriginalError (vendor.js?v=yC_pUFx9rDW_jX8REnKHb_rOOg7g5ohUM-Y5-vW8rlQ:1916)
at ErrorHandler.handleError (vendor.js?v=yC_pUFx9rDW_jX8REnKHb_rOOg7g5ohUM-Y5-vW8rlQ:1885)
at Object.next (vendor.js?v=yC_pUFx9rDW_jX8REnKHb_rOOg7g5ohUM-Y5-vW8rlQ:5372)
at SafeSubscriber.schedulerFn [as _next] (vendor.js?v=yC_pUFx9rDW_jX8REnKHb_rOOg7g5ohUM-Y5-vW8rlQ:4445)
at SafeSubscriber.__tryOrUnsub (vendor.js?v=yC_pUFx9rDW_jX8REnKHb_rOOg7g5ohUM-Y5-vW8rlQ:574)
at SafeSubscriber.next (vendor.js?v=yC_pUFx9rDW_jX8REnKHb_rOOg7g5ohUM-Y5-vW8rlQ:521)
at Subscriber._next (vendor.js?v=yC_pUFx9rDW_jX8REnKHb_rOOg7g5ohUM-Y5-vW8rlQ:461)
at Subscriber.next (vendor.js?v=yC_pUFx9rDW_jX8REnKHb_rOOg7g5ohUM-Y5-vW8rlQ:425)
at EventEmitter.Subject.next (vendor.js?v=yC_pUFx9rDW_jX8REnKHb_rOOg7g5ohUM-Y5-vW8rlQ:20402)
复制代码

停止订阅,释放资源,但状态没有终止

import { of,Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { version } from './package.json';


const publiser$ = new Observable(suber => {
    let count = 0;
    const timer = setInterval(() => {
       suber.next(count);
			 count++;
    },400);
    //suber.next(1);
    // suber.complete();
    // suber.error();
    return {
        unsubscribe:() => {
            console.log("stop push data....");
            clearInterval(timer);
        }
    }
});
const unsuber = publiser$.subscribe({
    next:res => {
        console.log(`next ...${res}`);
    },
    complete:() => {
        console.log("complete done....");
    },
    error: () => {
        console.log("error occur....");
    }
});

setTimeout(() => {
    unsuber.unsubscribe();
},2000);
复制代码

利用rxjs自带操作符优化上述的取消订阅

import { of,Observable, interval } from 'rxjs';
import { map } from 'rxjs/operators';
import { version } from './package.json';


const publiser$ = interval(400);
const unsuber = publiser$.subscribe({
    next:res => {
        console.log(`next ...${res}`);
    },
    complete:() => {
        console.log("complete done....");
    },
    error: () => {
        console.log("error occur....");
    }
});

setTimeout(() => {
    unsuber.unsubscribe();
},2000);
复制代码

双重身份的SubJect

import { of,Observable, interval,Subject } from 'rxjs';
import { map } from 'rxjs/operators';
import { version } from './package.json';

const sub = new Subject();
sub.pipe(map(res => 'first ...'+res)).subscribe((res) => {
    console.log("sub one...",res);
});
sub.subscribe({
    next:(res) => {
        console.log(`second...${res}`);
    }
});
sub.next(1);
sub.next(2);
复制代码
import { of,Observable, interval,Subject ,from} from 'rxjs';
import { map } from 'rxjs/operators';
import { version } from './package.json';

const sub = new Subject();
sub.pipe(map(res => 'first ...'+res)).subscribe((res) => {
    console.log("sub one...",res);
});
sub.subscribe({
    next:(res) => {
        console.log(`second...${res}`);
    }
});
// sub.next(1);
// sub.next(2);
const publisher$ = from([1,2,3]);
publisher$.subscribe(sub);
复制代码

参考文献

RxJS

RxJS 操作符

阮一峰的网络日志 npm scripts 使用指南

本文作者:前端首席体验师(CheongHu)

联系邮箱:simple2012hcz@126.com

关注下面的标签,发现更多相似文章
评论