mobx中跟新了数据视图没变化_【第1781期】MobX 简明教程

前言

SEEConf,2020年1月4号杭州见,C位抢票见文章末尾。今日早读文章由腾讯@whinc投稿分享。

正文从这开始~~

导读:MobX 是一个优秀的响应式状态管理库,在流行的状态管理库 Redux 之外为我们提供了其他选择。如果你还没有尝试过 MobX,我强烈建议你继续阅读本文,并跟着示例动手实践体验一下。本文是 MobX 的入门教程,文章包含 MobX 的设计哲学、状态派生模型、核心API以及与 React 的集成。

MobX 是一个简单、可伸缩的响应式状态管理库。通过 MobX 你可以用最直观的方式修改状态,其他的一切 MobX 都会为你处理好(如自动更新UI),并且具有非常高的性能。

MobX 文档的概念和 API 比较多,初次接触时可能会感觉无从下手或者抓不住重点,本文尝试提供一份 MobX 核心的知识的简明教程,作为阅读官方文档之前的热身。

MobX 的设计哲学

在学习使用 MobX API 之前,我们首先要了解 MobX 的设计哲学,它是我们在思考 MobX 应用时的心智模型,可以帮助我们更好的使用 MobX API。

MobX 的设计哲学概括起来就一句话:Anything that can be derived from the application state, should be derived. Automatically. (任何可以从应用状态中派生的内容,都应当自动地被派生。)

理解这句话的关键是搞清楚【派生】的含义是什么?在 MobX 中【派生】的含义比较广泛,包括:

  • 用户接口(UI),如组件、页面、图表

  • 派生数据(computed data),如从数组中计算得到的数组长度

  • 副作用(side effect),如发送网络请求、设置定时任务、打印日志等

我们平时常看到的状态响应模型,其中的响应就可以看做是状态的一种派生,MobX 将这种模型进行泛化,形成更通用的状态派生模型,接下来会详细介绍。

MobX 状态响应模型

状态响应模型概括起来主要包含三个要素:定义状态、响应状态、修改状态(如下图所示)。

979a17f0d14d1ea640c4dd5925336c82.png

MobX 中通过observable来定义可观察状态, 它接受任意 JS 值(包括Object、Array、Map、Set),返回原始数据的代理对象,代理对象与原始数据具有相同的接口,你可以把代理对象当做原始数据使用。

// 定义状态

const store = observable({

count: 0

});

MobX 中通过autorun来定义状态变化时要执行的响应操作,它接受一个函数。此后,每当observable中定义的状态发生变化时,MobX 都会立即执行该函数。

// 响应状态

autorun(() => {

console.log("count:", store.count);

});

MobX 中修改状态和修改原始数据的方式没什么区别,这也是 MobX 的优点——符合直觉的操作方式。

// 修改状态

store.count += 1;

把上面的部分串起来就是一个最简单的 MobX 示例了(如下),示例中每次修改 count 的值时会自动打印一条日志,并且日志包含最新的 count 值。这个示例揭示了 MobX 中最核心的功能。

import { observable, autorun } from "mobx";

// 1. 定义状态

const store = observable({

count: 0

});

// 2. 响应状态

autorun(() => {

console.log("count:", store.count);

});

// count: 0

// 3. 修改状态

store.count += 1;

// count: 1

上面例子中,首先通过 MobX 提供的observable函数定义状态,例子的状态是{count: 0}。然后通过通过 MobX 提供的autorun函数定义状态响应函数,例子中是一条打印当前count值得的语句,当定义的状态中的任何值发生变化时,该响应函数会立即执行,并且是由 MobX 自动完成的。最后是修改状态,和操作对象属性一样,例子中是对count属性进行自增操作。

MobX 状态派生模型

从上一节中我们知道 MobX 的状态响应函数是在状态变化时执行某些操作。实际应用中这些操作可分为两类:带副作用(如打印日志、渲染 UI、请求网络等)和不带副作用(如计算数组的长度)。MobX 将这些操作统称为派生(Derivations),即可从应用状态中派生出来的任何内容。

其中不带副作用的操作是一个纯函数,一般是根据当前状态计算返回一个新的值。为了区分带副作用和不带副作用的两类情况,MobX 将 Derivations 概念细分成两个概念 Reactions 和 Computed values 来区分二者。此外,MobX 还提供了一个可选的概念 Action 来表示对 State 的修改操作,用于约束和预测应用状态的修改行为。这些概念汇总在一起如下图:

52763a66b195200cb7a709d0eec1490d.png

下面是一个简单的示例,完整展示了上图中涉及的所有概念。

index.html 文件

id="container">

index.js 文件

import { observable, autorun, computed, action } from "mobx"

const containerEl = document.querySelector("#container");

// State

const store = observable({

count: 0

});

// Actions

window.increaseCount = action(() => store.count++);

// Computed values

const doubleCount = computed(() => 2 * store.count);

// Reactions

autorun(() => {

containerEl.innerHTML = `

${store.count} * 2 = ${doubleCount.get()}

+1

`;

});

// 0 * 2 = 0

// (点击按钮)

// 1 * 2 = 2

// (点击按钮)

// 2 * 2 = 4

这个示例展示了一个简单的乘法,点击按钮后数据会自增,同时计算的结果也随之更新。相比上一个例子,它有几个值得注意的区别:

  • 通过computed定义计算值,并返回计算值对象doubleCount,通过其get/set方法访问内部计算值。computed接受一个返回计算值的函数,在函数内部可以使用observable定义的状态数据(如count),每当count的值发生变化时,doubleCount内部的计算值会自动更新。

  • 通过action定义状态修改操作,action接受一个函数,并返回一个签名相同的函数。在函数内部可直接修改obsevable定义的状态(如count)触发autorun和computed重新运行。

  • autorun中的响应操作替换成了渲染 UI,每当状态发生变化时重新渲染 UI。

MobX 核心 API 解析

MobX 的 API 可分为四类:定义状态(observable)、响应状态(autorun, computed)、修改状态(action)、辅助函数。下面挑出最核心的 API 进行重点介绍,但不会涉及 API 的详细用法(请参考MobX Api Reference)。

MobX 目前同时支持 v4 和 v5 两个版本,这两个版本的 API 是相同的(功能相同),他们的区别在于内部实现数据响应的方式不同,如果使用 v4 版本时需要注意文档中标注的注意情况。

observable

observable用于定义可观察状态,其类型定义如下(已简化):

extends Object>(value: T): T & IObservableObject;

= any>(value: T[]): IObservableArray;

= any, V = any>(value: Map, V>): ObservableMap, V>;

= any>(value: Set): ObservableSet;

它接受Object/Array/Map/Set类型的数据作为参数,并返回对应数据的代理对象,代理对象和原数据类型具有相同的接口,你可以像使用原始数据一样使用代理对象。例如:

import { observable } from "mobx";

const object = observable({a: 1})

console.log(object.a)

const array = observable([1, 2])

console.log(array[0])

const map = observable(new Map({a: 1}))

console.log(map.get('a'))

const set = observable(new Set([1, 2]))

console.log(set.has(1))

observable观察的数据可以嵌套,嵌套的数据也会被观察。例如:

import { observable, autorun } from "mobx";

const store = observable({

a: {

b: [1, 2]

}

})

autorun(() => console.log(store.a.b[0]))

// 1

store.a.b[0] += 1

// 2

observable支持动态添加可观察状态。例如:

import { observable, autorun } from "mobx";

const store = observable({});

autorun(() => {

console.log("a =", store.a);

});

// a = undefined

store.a = 1;

// a = 1

动态添加可观察状态,仅适用于 MobX v5+ 版本,MobX v4 及以下版本需要借助辅助函数,详见Direct Observable manipulation。

autorun

autorun用于定义响应函数,其类型定义如下(已简化):

autorun(reaction: () => any): IReactionDisposer;

autorun接受一个响应函数 reaction,并在定义时立即执行一次 reaction 函数, reaction 函数内部可以执行带有副作用的操作。以后,每当依赖状态发生变化时,autorun自动重新运行 reaction 函数。autorun第一次运行 reaction 函数是为了搜集依赖状态——运行 reaction 过程中实际使用的状态(通过obj.name或obj['name']解引用方式使用的状态)。

例如下面例子,autorun中使用了状态a,因此当状态a的值发生变化时,会执行响应函数。而状态b虽然被列为可观察状态,但由于在autorun中没有被实际使用,因此当状态b的值发生变化时,不会执行 响应函数 。这是 MobX 的“聪明”之处,它能根据状态的实际使用情况,细粒度地控制更新范围。由于减少了不必要的执行开销,从而提升了程序性能。

import { observable, autorun } from "mobx";

const store = observable({

a: 1,

b: 2

});

autorun(() => {

console.log("a =", store.a);

});

// a = 1

store.a += 1;

// a = 2

store.b += 1;

// (无输出)

让我们回顾一下 MobX 的使用,通过observable定义状态,在autorun中使用状态,当autorun中使用到的状态发生变化时,该autorun重新执行。绝大部分情况下它都工作的很好,如果你遇到修改了状态而autorun没有如你预期的那样运行,这时候你需要深入了解 MobX 是如何响应状态变化的,推荐阅读What does MobX react to?。

computed

computed用于定义计算值,其类型定义如下(已简化):

(func: () => T) => { get(): T, set(value: T): void}

computed与autorun相似,他们都会在依赖的状态发生变化时会重新运行,不同之处是computed接收的是纯函数并且返回一个计算值,这个计算值在状态变化时会自动更新,计算值可以在autorun中使用。

例如下面例子中,ca是一个计算值,它依赖状态a的值,当状态a的值发生变化时,ca会重新计算值。计算值ca是一个“装箱”对象,需要通过get/set访问内部值,只有这样才能保持计算值的引用不变而内部值又是可变的。

import { observable, autorun, computed } from "mobx";

const store = observable({

a: 1

});

const ca = computed(() => {

return 10 * store.a;

});

autorun(() => {

console.log(`${store.a} * 10 = ${ca.get()}`);

});

// 1 * 10 = 10

store.a += 1;

// 2 * 10 = 20

store.a += 1;

// 3 * 10 = 30

由于computed被视作是纯函数,MobX 提供了许多开箱即用的优化措施,例如对计算值的缓存和惰性计算。

computed值会被缓存

每当读取computed值时,如果其依赖的状态或其他computed值未发生变化,则使用上次的缓存结果,以减少计算开销,对于复杂的computed值,缓存可以大大提高性能。

例如下面例子中,computed值ca和cb分别依赖状态a和b,第一次执行autorun时,ca和cb都会重新计算,然后修改状态a的值,第二次执行autorun时,只有ca会重新计算,而cb则使用上次的缓存结果。

import { observable, autorun, computed } from "mobx";

const store = observable({

a: 1,

b: 1

});

const ca = computed(() => {

console.log("recomputed ca");

return 10 * store.a;

});

const cb = computed(() => {

console.log("recomputed cb");

return 10 * store.b;

});

autorun(() => {

console.log(

`a = ${store.a}, ca = ${ca.get()}, b = ${store.b}, cb = ${cb.get()}`

);

});

// recomputed ca

// recomputed cb

// a = 1, ca = 10, b = 1, cb = 10

store.a += 1;

// recomputed ca

// a = 2, ca = 20, b = 1, cb = 10

为了观察computed的计算过程,插入了打印日志的语句,这会带有副作用,实际中不要这样做

computed值会惰性计算

只有computed值被使用时才重新计算值。反言之,即使computed值依赖的状态发生了变化,但是它暂时没有被使用,那么它不会重新计算。

例如下面例子中,computed值ca依赖状态a。当状态a的值小于 3 时,autorun运行时只打印状态a的值,由于computed值ca未被使用,所以ca不会从新计算。当状态a的值增长到 3 以后,autorun运行时同时打印状态a和computed值ca,由于computed值ca被使用了,所以ca会重新计算。

import { observable, autorun, computed } from "mobx";

const store = observable({

a: 1

});

const ca = computed(() => {

console.log("recomputed ca");

return 10 * store.a;

});

autorun(() => {

if (store.a >= 3) {

console.log(`a = ${store.a}, ca = ${ca.get()}`);

} else {

console.log(`a = ${store.a}`);

}

});

// a = 1

store.a += 1;

// a = 2

store.a += 1;

// recomputed ca

// a = 3, ca = 30

action

action用于定义状态修改操作,其类型定义如下(已简化):

extends Function>(fn: T) => T

虽然没有action也可以直接修改状态,但是通过action显式地修改状态,使得状态的变化可预测(状态的变化能定位到是哪个action引起的)。此外,action函数是事务型的,通过action修改状态时,响应函数不会立即执行,而是等到action结束后才执行,这有助于提升性能。

例如下面例子中,展示了修改状态的两种方式,一种是直接修改状态a,另一种是通过调用预先定义的action修改状态。值得注意的是,在一次action中连续两次修改状态a的值,只会触发一次autorun的执行。

import { observable, autorun, action } from "mobx";

const store = observable({

a: 1

});

autorun(() => {

console.log(`a = ${store.a}`);

});

// a = 1

store.a += 1;

// a = 2

store.a += 1;

// a = 3

const increaseA = action(() => {

store.a += 1;

store.a += 1;

});

increaseA();

// a = 5

对 MobX 进行一些配置后,可以使action成为修改状态的唯一方式,这可以避免不受约束的修改状态行为发生,有利于提升项目的可维护性。

例如下面例子中,配置 enforceActions 为"always"后,就只能通过action修改状态了,如果尝试直接修改状态将会触发异常。

import { observable, autorun, action, configure } from "mobx";

// 强制只能通过 action 修改状态

configure({

enforceActions: "always"

});

const store = observable({

a: 1

});

autorun(() => {

console.log(`a = ${store.a}`);

});

// a = 1

// store.a += 1;

// 直接修改状态,将会抛出如下异常

// Error: [mobx] Since strict-mode is enabled, changing observed

// observable values outside actions is not allowed.

// Please wrap the code in an `action` if this change is intended.

const increaseA = action(() => {

store.a += 1;

});

increaseA();

// a = 2

MobX 与 React 集成

MobX 是框架无关的,你可以单独使用它,也可以与任何流行的 UI 框架进行一起使用,MobX 官方提供了 React/Vue/Angular 等流行框架的绑定实现。MobX 最常见的是与 React 一起使用,他们的绑定实现是 mobx-react(或 mobx-react-lite)。

mobx-react 提供了一个observer方法, 它是一个高阶组件,它接收 React 组件并返回一个新的 React 组件,返回的新组件能响应(通过observable定义的)状态的变化,即组件能在可观察状态变化时自动更新。observer方法是对 MobX 提供的autorun方法和 React 组件更新机制的封装,以便于在 React 中使用,你依然可以在 React 中使用autorun来更新组件。下面是observer方法的类型声明,它支持组件类和函数组件。

function observer<T extends React.ComponentClass | React.FunctionComponent>(target: T): T

下面是组件类使用 MobX 的示例,通过observable定义可观察状态,并通过observer包裹组件,之后组件事件处理方法中修改状态后,组件会自动更新,无需手动调用 React 的setState()来更新组件。

import React from "react";

import { observable } from "mobx";

import { observer } from "mobx-react";

class Counter extends React.Component {

constructor(props) {

super(props);

this.store = observable({

count: 0

});

}

render() {

return (

<button onClick={() => store.count++}>

{store.count}

button>

)

}

}

export default observer(Counter);

下面是函数组件使用 MobX 的示例,与上面类组件类似,区别是使用 mobx-react 提供的useLocalStore定义客观察状态,useLocalStore内部也是使用observable定义可观察状态。

import React, { useMemo } from "react";

import { observable } from "mobx";

import { observer, useLocalStore } from "mobx-react";

const Counter = () => {

const store = useLocalStore(() => ({

count: 0

}));

// 等价于下面

// const store = useMemo(() => observable({ count: 0 }), []);

return (

<button onClick={() => store.count++}>

{store.count}

button>

)

};

export default observer(Counter);

小结

MobX 的设计哲学是“可从应用状态中派生的任何内容都应当自动的被派生”,后半句有两个关键字:自动和派生。心中秉持这一设计哲学,再来看 MobX 的派生状态模型就比较清晰了,Computed values 和 Reactions 都可以视作是从 State 中派生出的,State 变化时触发 Computed values 的重新计算和 Reations 的重新运行。为了让派生能自动的进行,MobX 通过Object.definePropery或Proxy方式拦截对象的读写操作,从而允许用户以自然的方式来修改状态,MobX 负责更新派生的内容。

MobX 提供了几个核心 API 来帮助定义状态(observable)、响应状态(autorun, computed)和修改状态(action),通过这些 API 可以让程序立即具备响应式能力。这些 API 接口并不复杂,但要熟练使用,需要深入理解 MobX 响应机制,文中通过一些简单的示例来辅助理解这些 API 的行为。

MobX 可以单独使用,也可以与任何流行的 UI 框架一起使用,Github 上可以找到 MobX 与流行框架的绑定实现。不过 MobX 最常见的是与 React 一起使用,mobx-react 是流行的 MobX 和 React 的绑定实现库,本文介绍了它在组件类和函数组件上的一些基本用法。

参考

  • MobX Documentations

  • mobx-react

关于本文 作者:@whinc 原文:https://github.com/whinc/blog/issues/16

b206cab1309267f6939f122375d024c9.png

他曾分享过

【第1611期】前端路由原理解析和实现

为你推荐

【第1586期】基于Redux/Vuex/MobX等库的通用化状态OOP

【第1302期】基于 MobX 构建视图框架无关的数据层-与 Vue 的结合

【第1736期】现代 JavaScript 教程 - 代码风格

d7671a9bac7d1d9017026fd642c54838.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/355727.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

excel导航窗格_Excel小技巧16:在每天的某个时刻自动打开特定工作簿

学习Excel技术&#xff0c;关注微信公众号&#xff1a;excelperfect我想要在每天下班前&#xff0c;将自已一天的工作进行整理并简短记录在一个Excel工作簿中。然而&#xff0c;有时候忙&#xff0c;可能会忘记&#xff1b;有时候到下班时间了&#xff0c;急于下班&#xff0c;…

pdf secured_使您的Spring Security @Secured注释更干燥

pdf secured最近&#xff0c;Grails用户邮件列表中的一个用户想知道在定义Secured注释时如何减少重复 。 在Java批注中指定属性的规则非常严格&#xff0c;因此我看不到直接执行他所要求的方法的方法。 使用Groovy并没有真正的帮助&#xff0c;因为Groovy类中的注释大部分与Ja…

51单片机按键控制数码管0~9_51单片机外部中断

前面为大家介绍的点亮LED灯、数码管、按键只用到了51单片机的IO资源&#xff0c;我们要是仅仅用单片机点灯、操作数码管&#xff0c;那可真是大才小用了。这些都只是51单片机资源的冰山一角&#xff0c;51单片机还有好多的功能&#xff0c;我后面将为大家一一介绍。今天为大家介…

IIS7开启gZip动态压缩

1.安装动态压缩模块&#xff1a; 安装过程可能的报错&#xff1a;This application has requested the Runtime to terminate it in an unusual way. 解决办法>> 报错&#xff1a;错误: 尝试安装 动态内容压缩 失败&#xff0c;错误代码为 0x8007000E。 存储空间不足&am…

mysql查询不重复记录数_mysql查询不重复的行内容,不重复的记录数.count,distinct

有这么一个表 记录了id, p_id, p_name , p_content , p_time 1 343 aaa aaaaaa 2012-09-01 2 344 bbb bbbbbb 2012-09-02 3 321 ccc cccccccc 2012-09-03 4 343 aaa aaaaaa 2012-09-04 想查询不重复的行的内容,并且输出 p_sum ( 产品p_id出现重复的次数) sele有这么一个表记录了…

使用Java中的FileChannel和ByteBuffer在文件中读取/写入文件

过去&#xff0c;我讨论过RandomAccessFile以及如何将其用于在Java中进行更快的IO&#xff0c;在本Java NIO教程中&#xff0c;我们将了解如何通过使用FileChannel和ByteBuffer来使用读/写数据。 Channel提供了一种从文件读取数据的替代方法&#xff0c;它提供了比InputStream…

字符,字符串,int之间互相转换

字符转换成字符串&#xff1a;String str String.valueOf(ch); 字符转换成int&#xff1a; int a ch; 字符串转换成字符&#xff1a;char ch str.charAt(0); 字符串转换成Int&#xff1a;只包含数字的字符串可以通过Integer.parseInt(str)转换为int&#xff0c;但是包含字母…

mysql 导入百万级数据 几种 java_Java 修行第034天--执行计划及其使用--Oracle数据导入导出--第三章MySQL使用...

执行计划中牢记几句话:-- 尽量避免是*代替所有列,编写查询语句时使用具体列名代替*,可以防止全表扫描-- 尽可能少的使用like关键字进行模糊查询-- 建立适当的索引可以提高查询效率十三. 执行计划--通过PL/SQL Developer查看查询的1 执行计划是一条查询语句在Oracle中的执行过程…

10g添加用户 oracle_oracle 10g中如何创建用户

登录身份 说明sys/change_on_install SYSDBA或SYSOPER 不能以NORMAL登录&#xff0c;可作为默认的系统管理员system/manager SYSDBA或NORMAL 不能以SYSOPER登录&#xff0c;可作为默认的系统管理员sysman/oem_temp sysman 为oms的用户名scott/tiger NORMAL 普通用户aqadm /aqad…

摄像头分辨率怎么调整_亿联CAM50 - 智能话机专属高清摄像头

亿联CAM50是通过USB连接的高清视频摄像机&#xff0c;适用于亿联桌面高端智能话机&#xff08;SIP-T58V和SIP-T58A&#xff09;&#xff0c;致力于提供一流的个人视频通信体验&#xff0c;优化视频协作。CAM50高清摄像机即插即用&#xff0c;无需额外安装驱动软件&#xff0c;也…

C orm mysql_Simple MySQL-C ORM

当你需要在纯C语言的应用程序中访问 MySQL 表中的数据时&#xff0c;是非常繁琐的事情&#xff0c;而该框架可以帮你大量的简化编码的工作&#xff0c;该框架采用 Python 开发&#xff0c;适用于 C 语言程序。示例代码&#xff1a;#include #include #include #include int mai…

jdbc:log4jdbc_使用Log4jdbc记录JDBC操作

jdbc:log4jdbc当我们开发任何应用程序&#xff0c;完成它或结束其任何模块时&#xff0c;我们都会开始优化过程。 大多数应用程序都包含数据库访问权限&#xff0c;如果您使用的是ORM &#xff0c;则可能会使用hibernate 。 优化Hibernate持久层&#xff0c;要求准备阅读&#…

Mac 编译安装 Redis-3.2.3

Redis官方下载地址&#xff1a;http://redis.io/download Redis安装 cd /usr/local/src/redis-3.2.3 sudo make sudo make installcp ./src/redis-benchmark /usr/local/redis/redis-3.2 cp ./src/redis-check-aof /usr/local/redis/redis-3.2 cp ./src/redis-check-rdb /usr/l…

linux c mysql教程_linux下c操作mysql之增删改查

书接上文,继续进行linux 下c操作mysql。1.创建表/插入数据mysql> desc children-> ;----------------------------------------------------------| Field | Type| Null | Key | Default | Extra |----------------------------------------------------------| childno …

ios系统python编译器_MacBook如何安装Python编译器-百度经验

编程是一门需要动手实践的技能&#xff0c;由于Python的性能&#xff0c;许多人都将其作为学习编程的入门语言。而要想学好Python&#xff0c;首先要在电脑上安装Python&#xff0c;并安装一个可以解释Python的文本编辑器。在此以在MacBook上安装Sublime Text为例。 工具/原料 …

jvm需要多长时间进行转义分析? 可能比您想象的要长。

这篇文章着眼于转义分析&#xff0c;特别是jvm在运行的程序中执行转义分析需要多长时间。 我做了一些观察&#xff0c;但目前还没有全部解释。 作为介绍&#xff0c;让我们绕道看看jvm -Xcomp中一个鲜为人知且使用更少的标志&#xff08;这将是一件好事&#xff09;。 该标志…

Java补漏(一)

&#xfeff;&#xfeff;第一章前言 在学长的建议下&#xff0c;为了弥补之前学Java漏下的或者不是非常清楚的知识点&#xff0c;买了本蛮好的教科书-《Java学习笔记&#xff08;JDK6&#xff09;》&#xff0c;正式又一次学习。为了记下一些让我恍然大悟的知识。写了本文档。…

子集和问题 算法_子集问题 主要是去重算法

给定一个可能包含重复元素的整数数组 nums&#xff0c;返回该数组所有可能的子集&#xff08;幂集&#xff09;。说明&#xff1a;解集不能包含重复的子集。示例:输入: [1,2,2]输出:[[2],[1],[1,2,2],[2,2],[1,2],[]]

mysql存储引擎使用教程_mysql教程:如何写MySQL存储引擎

在MySQL 5.1中开发一个存储引擎已经是比较方便了。所谓存储引擎实际上是按照MySQL的约定&#xff0c;提供某些接口的实现而已&#xff0c;如MySQL插入一条记录时将调用write_row方法&#xff0c;通过索引检索时将调用index_read和index_next方法等。MySQL与存储引擎之间的接口主…

express中connect-flash中间件的使用

在学习node的时候&#xff0c;flash是困扰我最久的一个中间件&#xff0c;之前一直都没有很好的理解&#xff0c;这里做一个总结。 参考自&#xff1a;http://yunkus.com/connect-flash-usage/ 什么是flash&#xff1f; The flash is a special area of the session used for s…