Bootstrap

mobx学习

mobx与对象和数组学习

一、mobx常用api概念,基本用法

1.observable不同数据类型(对象,数组,map)被包装之后的特性

observable定义一个存储 state 的可追踪字段。
1.对象
observable.deep
任何被赋值给 observable 的字段都会基于它自己的类型被(深度)转化为observable、autoAction 或 flow。只有 plain object、array、Map、Set、function、generator function 可以转换,类实例和其他实例不会被影响。

observable.ref
类似于 observable,但只有重新赋值才会被追踪。在你打算将不可变数据存储在可观察字段中时,可以使用这个注解。

observable.shallow
类似于 observable.ref 但是是用于集合的。任何所赋的集合都会被转化为可观察值,但是其内部的值并不会变为可观察值。

observable.struct
类似于 observable,但是会忽略所赋的值中所有在结构上与当前值相同的值。

2.数组
clear()
删除数组中所有现存的元素。

replace(newItems)
用新元素替换数组中所有现存的元素。

remove(value)
从数组中删除一个值为 value 的元素,在找到并删除该元素后返回 true

3.Map
toJSON()
返回该 Map 的浅层普通对象表示(使用 toJS 进行深拷贝)。

merge(values)
将所提供的 values (普通的对象、数组或以字符串为键的 ES6 Map )的所有条目复制到该 Map 中。

replace(values)
用所提供的 values 替换该 Map 的全部内容。

2.computed用法

1.声明式创建

import {observable, computed} from "mobx";
 
class Money {
    @observable price = 0;
    @observable amount = 2;
 
    constructor(price = 1) {
        this.price = price;
    }
 
    @computed get total() {
        return this.price * this.amount;
    }
}
let m = new Money()
 
console.log(m.total) // 2
m.price = 10;
console.log(m.total) // 20

2.使用 decorate 引入

import {decorate, observable, computed} from "mobx";
 
class Money {
    price = 0;
    amount = 2;
    constructor(price = 1) {
        this.price = price;
    }
 
    get total() {
        return this.price * this.amount;
    }
}
decorate(Money, {
    price: observable,
    amount: observable,
    total: computed
})
 
let m = new Money()
 
console.log(m.total) // 2
m.price = 10;
console.log(m.total) // 20

3.使用 observable.object 创建

import {observable} from "mobx";
 
const Money = observable.object({
    price: 0,
    amount: 1,
    get total() {
        return this.price * this.amount
    }
})
 
console.log(Money.total) // 0
Money.price = 10;
console.log(Money.total) // 10

3.action使用场景

Action 就是任何一段修改状态的代码。只执行查找,过滤器等函数不应该被标记为动作,以允许 MobX 跟踪它们的调用。

二.了解mobx的store的使用方式

1.Provider - inject 方式

将提供类似react-redux的Provider操作,使得不用在每个需要mobx的组件中引入mobx仓库

1、安装
	cnpm install mobx-react --save
	
2、在index.js引入Provider和mobx仓库

	import {Provider} from 'mobx-react'
	import xx from './store/index'  mobx仓库
	
	将Provider包裹在最外层
	
	ReactDOM.render(
	  <Provider  xxx={xx}>
	    <React.Fragment>
	      <App/>
	    </React.Fragment>
	  </Provider>,
	  document.getElementById('root')
	);
	
3、组件使用mobx仓库
	import {inject,observer,PropTypes as ob} from 'mobx-react'
	static propTypes={
		xx:ob.observableArray,     observable数组类型的参数验证
		xxx:ob.observableObject    observable对象类型
	}

	方式一:直接导入仓库
	
		@inject('xxx')
		@observer 类组件
		
		获取:this.props.xxx.状态/方法名
	
	方式二:按需导入
		@inject((x)=>({
			键名:x.Provider的xxx.状态/方法名
				其中:x.Provider的xxx获取到xxx的内容
		}))
		@observer 类组件
		
		获取:this.props.键名
		
4、多个mobx仓库
	(1)创建好多个仓库的实例后,集合导出
	(2)在index.js中将,多个仓库的集合对象,一起传入Provider中
	
			方式一:直接导入仓库
	
		@inject('xxx')
		@observer 类组件
		
		获取:this.props.xxx.导出的仓库名.状态/方法名		
		
	方式二:按需导入
		@inject((x)=>({
			键名:x.Provider的xxx.导出的仓库名.状态/方法名		
		}))
		@observer 类组件
		
		获取:this.props.键名
		

2.store 问题

1、单页面私有store
建一个store文件,使用时引入
2、多页面共享store
建立根store,用到的页面引入根store就行
3、如果多个store需要相互使用数据该如何处理
构建根store,使用时相互引用,可以通过inject方式引用

三.mobx的observer被包装组件拥有了哪些能力

可以用来将 React 组件转变成响应式组件。 它用 mobx.autorun 包装了组件的 render 函数以确保任何组件渲染中使用的数据变化时都可以强制刷新组件。

import {observer} from "mobx-react";

var timerData = observable({
    secondsPassed: 0
});

setInterval(() => {
    timerData.secondsPassed++;
}, 1000);

@observer class Timer extends React.Component {
    render() {
        return (<span>Seconds passed: { this.props.timerData.secondsPassed } </span> )
    }
};

ReactDOM.render(<Timer timerData={timerData} />, document.body);

1.陷阱: 组件中的间接引用值

MobX 无法使原始数据类型值转变成可观察的(尽管它可以用对象来包装它们)。 所以值是不可观察的,但是对象的属性可以。这意味着 @observer 实际上是对间接引用(dereference)值的反应。

React.render(<Timer timerData={timerData.secondsPassed} />, document.body)

2.ES5 支持

在ES5环境中,可以简单地使用 observer(React.createClass({ … 来定义观察者组件。还可以参见语法指南。

3.无状态函数组件

可以通过使用 observer 传递的无状态函数组件来编写

import {observer} from "mobx-react";

const Timer = observer(({ timerData }) =>
    <span>Seconds passed: { timerData.secondsPassed } </span>
);

4.可观察的局部组件状态

就像普通类一样,你可以通过使用 @observable 装饰器在React组件上引入可观察属性。 这意味着你可以在组件中拥有功能同样强大的本地状态(local state),而不需要通过 React 的冗长和强制性的 setState 机制来管理。 响应式状态会被 render 提取调用,但不会调用其它 React 的生命周期方法,除了 componentWillUpdate 和 componentDidUpdate 。 如果你需要用到其他 React 生命周期方法 ,只需使用基于 state 的常规 React API 即可。

import {observer} from "mobx-react"
import {observable} from "mobx"

@observer class Timer extends React.Component {
    @observable secondsPassed = 0

    componentWillMount() {
        setInterval(() => {
            this.secondsPassed++
        }, 1000)
    }

    render() {
        return (<span>Seconds passed: { this.secondsPassed } </span> )
    }
}

ReactDOM.render(<Timer />, document.body)

5.使用 inject 将组件连接到提供的 stores

mobx-react 包还提供了 Provider 组件,它使用了 React 的上下文(context)机制,可以用来向下传递 stores。 要连接到这些 stores,需要传递一个 stores 名称的列表给 inject,这使得 stores 可以作为组件的 props 使用。

const colors = observable({
   foreground: '#000',
   background: '#fff'
});

const App = () =>
  <Provider colors={colors}>
     <app stuff... />
  </Provider>;

const Button = inject("colors")(observer(({ colors, label, onClick }) =>
  <button style={{
      color: colors.foreground,
      backgroundColor: colors.background
    }}
    onClick={onClick}
  >{label}</button>
));

// 稍后..
colors.foreground = 'blue';
// 所有button都会更新

6.componentWillReact (生命周期钩子)

当使用 mobx-react 时可以定义一个新的生命周期钩子函数 componentWillReact(一语双关)。当组件因为它观察的数据发生了改变,它会安排重新渲染,这个时候 componentWillReact 会被触发。这使得它很容易追溯渲染并找到导致渲染的操作(action)。

四.对象常用方法

1.Object.values()

返回值为数组,返回的是对象的键值对中属性的值value.它返回的数组顺序,如果对象的key值是数字,则返回值会对key值进行排序,返回的是排序后的结果

 console.log(Object.values({one:2,two:1}))  //[2,1]
 console.log(Object.values({3:'a',4:'b',1:'c'}))  //["c","a","b"]

若键名是symbol,编译时会被自动忽略

 console.log(Object.values({[Symbol()]:1,"a":2}))  //[2]

2.Object.keys()

返回值也是数组,但是它返回的是对象的键值对中的键名key.并且返回的数组顺序,如果对象的key值是数字,则返回值会对key值进行排序,返回的是排序后的结果

console.log(Object.keys({3:'a',4:'b',1:'c'})); //["1","3","4"]

若键名是symbol,编译时会被自动忽略

 console.log(Object.keys({[Symbol()]:1,"a":2}))  //["a"]

当参数为数组时,返回值为索引组成的数组

 console.log(Object.keys([1,2,3,4]))  //["0","1","2","3"]

当参数为字符串时,返回值也是索引组成的数组

console.log(Object.keys('中国新青年')) //["0","1","2","3","4"]

两者比较如下

 var obj = { f1: 'abc', f2: 2 };
 console.log(Object.keys(obj)) //["f1", "f2"]
 console.log(Object.values(obj)) //["abc", 2]

3.Object.entries()

作用:将一个对象中可枚举属性的键名和键值按照二维数组的方法返回.若对象是数组,则会将数组的下标作为键值返回

 let result=Object.entries({one:1,two:2});
 console.log(result);  //[["one",1],["two",2]]
 console.log(Object.entries([1,3])); //[["0",1],["1",3]]

若键名是symbol,编译时会被自动忽略

console.log(Object.entries({[Symbol()]:1,two:2})) //["two",2]

entries()返回的数组顺序和Object.values(), Object.keys()一样,即如果对象的key值是数字,则返回值会对key值进行排序,返回的是排序后的结果

console.log(Object.entries({3:'a',4:'b',1:'c'})) //[["1","c"],["3","a"],["4","b"]]

4.Object.assign()

Object.assign()方法用于对象的合并,将源对象(source)的所有可枚举属性,复制到目标对象(target)。Object.assign()方法的第一个参数是目标对象,后面的参数都是源对象。

const target = { a: 1 };
const source1 = { b: 2 };
const source2 = { c: 3 };
 
Object.assign(target, source1, source2);
target // {a:1, b:2, c:3}
// 1.目标对象和源对象具有同属性,那么后面的会覆盖前面的值
const target = { a: 1, b: 1 };
const source1 = { b: 2, c: 2 };
const source2 = { c: 3 };
Object.assign(target, source1, source2);
target // {a:1, b:2, c:3}
 
// 2.如果参数只有一个,那么直接返回该参数对象
const obj = {a: 1};
Object.assign(obj) === obj // true
 
// 3.如果参数不是对象,那么会先转化成对象
typeof Object.assign(2) // "object"
 
// 4.如果参数只有一个,并且该参数是null或者undefined,那么会报错。因为无法转成对象
Object.assign(undefined) // 报错
Object.assign(null) // 报错
 
// 5.如果参数有多个,那么出现在源对象位置的参数如果无法转成对象那么会跳过,不会报错。
let obj = {a: 1};
Object.assign(obj, undefined) === obj // true
Object.assign(obj, null) === obj // true
 
// 6.其他类型的值(数值、字符串、布尔值),除了字符串会以数组形式,考入目标对象,其他值都不会产生效果。
const v1 = 'abc';
const v2 = true;
const v3 = 10;
const obj = Object.assign({}, v1, v2, v3);
console.log(obj); // { "0": "a", "1": "b", "2": "c" }
 
//7. 只拷贝源对象的自身属性(不拷贝继承属性),也不拷贝不可枚举属性(enumerable:false)
Object.assign({b: 'c'},
  Object.defineProperty({}, 'invisible', {
    enumerable: false,
    value: 'hello'
  })
)  // { b: 'c' }
 
// 8.Object.assign()会拷贝属性名为 Symbol 值的属性。
Object.assign({ a: 'b' }, { [Symbol('c')]: 'd' })  // { a: 'b', Symbol(c): 'd' }
 
// 10.同名属性的替换。是替换,而不是添加。
const target = { a: { b: 'c', d: 'e' } }
const source = { a: { b: 'hello' } }
Object.assign(target, source)  // { a: { b: 'hello' } }
 
// 11.数组的处理,将数组视为属性名从0开始的对象,所以属性名相同会覆盖(替换)
Object.assign([1, 2, 3], [4, 5])  // [4, 5, 3]
 
// 12.取值函数的处理。Object.assign()不会复制这个取值函数,只会拿到值以后,将这个值复制过去。
const source = {
  get foo() { return 1 }
};
const target = {};
Object.assign(target, source)  // { foo: 1 }

五.数组方法

1.map/forEach

1.相同点

  • 都是循环遍历数组中的每一项
  • forEach和map方法里每次执行匿名函数都支持3个参数,参数分别是item(当前每一项), - index(索引值),arr(原数组)
  • 匿名函数中的this都是指向window
  • 只能遍历数组
  • 都不会改变原数组

2.不同点

  • map方法返回一个新的数组,数组中的元素为原始数组调用函数处理后的值。
  • map方法不会对空数组进行检测,map方法不会改变原始数组。
  • forEach() 被调用时,不会改变原数组,也就是调用它的数组(尽管 callback 函数在被调用时可能会改变原数组)
  • forEach()会跳过空位。
  • map()会跳过空位,但会保留这个值

2.some/every

1.every()
一假即假。必须所有都返回true才会返回true,哪怕有一个false,就会返回false;

2.some()
一真即真。只要其中一个为true 就会返回true。

let list = [
    {name:"aaa",age:3},
    {name:"bbb",age:4},
    {name:"ccc",age:5},
];
 var eve= list.every(function(item){
   return item.age > 4
})
console.log(eve)//false;
var some = list.some(function(item){
   return item.age > 4
})
console.log(some)//true

3.shift/unshift

1.shift()
在数组的头部删除一个元素,并返回这个元素。

2.unshift()
在数组的头部加入一个元素,并返回原有length+1的长度。

4.pop/push

3.push()
在数组的尾部加入一个元素,并返回原有length+1的长度。

4.pop()
删除数组尾部第一个元素,并返回这个元素。

5.reduce

1.reduce()
reduce() 方法接收一个函数作为累加器,数组中的每个值(从左到右)开始缩减,最终计算为一个值。

arr.reduce(callback,[initialValue])

reduce 为数组中的每一个元素依次执行回调函数,不包括数组中被删除或从未被赋值的元素,接受四个参数:初始值(或者上一次回调函数的返回值),当前元素值,当前索引,调用 reduce 的数组。

callback (执行数组中每个值的函数,包含四个参数)

    1、previousValue (上一次调用回调返回的值,或者是提供的初始值(initialValue))
    2、currentValue (数组中当前被处理的元素)
    3、index (当前元素在数组中的索引)
    4、array (调用 reduce 的数组)

initialValue (作为第一次调用 callback 的第一个参数。)

普通用法

var  arr = [1, 2, 3, 4];
var sum = arr.reduce((x,y)=>x+y)
var mul = arr.reduce((x,y)=>x*y)
console.log( sum ); //求和,10
console.log( mul ); //求乘积,24

复杂用法

(1)计算数组中每个元素出现的次数

计算数组中每个元素出现的次数

let names = ['Alice', 'Bob', 'Tiff', 'Bruce', 'Alice'];

let nameNum = names.reduce((pre,cur)=>{
  if(cur in pre){
    pre[cur]++
  }else{
    pre[cur] = 1 
  }
  return pre
},{})
console.log(nameNum); //{Alice: 2, Bob: 1, Tiff: 1, Bruce: 1}

(2)数组去重

let arr = [1,2,3,4,4,1]
let newArr = arr.reduce((pre,cur)=>{
    if(!pre.includes(cur)){
      return pre.concat(cur)
    }else{
      return pre
    }
},[])
console.log(newArr);// [1, 2, 3, 4]

(3)将二维数组转化为一维

let arr = [[0, 1], [2, 3], [4, 5]]
let newArr = arr.reduce((pre,cur)=>{
    return pre.concat(cur)
},[])
console.log(newArr); // [0, 1, 2, 3, 4, 5]3)将多维数组转化为一维

let arr = [[0, 1], [2, 3], [4,[5,6,7]]]
const newArr = function(arr){
   return arr.reduce((pre,cur)=>pre.concat(Array.isArray(cur)?newArr(cur):cur),[])
}
console.log(newArr(arr)); //[0, 1, 2, 3, 4, 5, 6, 7]

(4)、对象里的属性求和

var result = [
    {
        subject: 'math',
        score: 10
    },
    {
        subject: 'chinese',
        score: 20
    },
    {
        subject: 'english',
        score: 30
    }
];

var sum = result.reduce(function(prev, cur) {
    return cur.score + prev;
}, 0);
console.log(sum) //60

6.includes/find

1.find()
find() 方法返回数组中满足提供的测试函数的第一个元素的值。否则返回 undefined。

let arr01 = [5,9,6,23,65,123]
let fd = arr01.find(element => element > 10)
console.log(fd); // 23

2.includes()
includes() 方法用来判断一个字符串是否包含在另一个字符串中,根据情况返回 true 或 false。

var str = "  你好 你你好,红红 小红!  "
console.log(str.includes('你'));  // true
console.log(str.includes('红红'));   // true
console.log(str.includes('王小红'));  // false
console.log(str.includes('你好, 1'));  // false
console.log(str.includes('abc'));  // false

3.findindex()
findIndex() 方法用来返回数组中满足提供的测试函数的第一个元素的索引。若没有找到对应元素则返回-1。

const array1 = [5, 12, 8, 130, 44];
const isLargeNumber = (element) => element > 13;
console.log(array1.findIndex(isLargeNumber)); // 3
;