CrazyCodes

CrazyCodes 查看完整档案

北京编辑北京邮电大学  |  计算机信息技术 编辑学两招  |  PHP开发工程师 编辑 blog.fastrun.cn 编辑
编辑

I am CrazyCodes,生命不息,编码不止。

GitHub : CrazyCodes

CSDN : CrazyCodes

掘金 : GraceDevelopment

不止于技术的微信公众号 : phpznb

与我一起传递技术正能量!

个人动态

CrazyCodes 赞了文章 · 3月24日

从头写一个 React-like 框架:工程搭建

最近在网上看到了 Build your own React 这篇文章,作者从零开始实现了一个简易类 React 框架,虽然没有过多的优化,但 React 中的核心思想 Concurrent ModeFiber Reconciler 等都有实现,看完后对理解 React 有很大帮助,因此我想在 Build your own React 的基础上,对代码进行拆分,搭建起自己的框架工程,然后完善教程中没完成的其他功能,代码在 rac 中。

工程搭建

技术栈上我选择用 TypeScript 开发,Rollup 打包, 都是平时用的不多的技术,顺带一起练练手,而且相比 webpack, rollup 配置更简单一些。在工程中创建一个 tsconfig.json 和一个 rollup.config.js, 然后安装一下需要的 rollup 插件,比如 rollup-plugin-typescript2rollup-plugin-terser。另外准备一个 examples 文件夹,创建一个小型的 demo 工程,使用 tsx 开发

支持 jsx

如果想让 TypeScript 支持 jsx,需要在 tsconfig 中开启 jsx TypeScript 自带了三种模式: preservereact,和 react-native,我们设置为 react, TypeScript 就会将代码中的 jsx 翻译成 React.createElement,这也是在使用 jsx 时,React 必须要在作用域中的原因。

但是我们要自己实现一个 React-like 框架,完全可以给 React.createElement 换个名字。在 Build your own React 中,作者通过 /** @jsx Didact.createElement */ 注释,告诉编译器将 jsx 的输出函数改为 Didact.createElement,这个方法只对当前文件生效,如果是在工程中使用为每个文件都加一行注释就麻烦了。我们通过另一种办法,在 tsconfig 中通过 jsxFactory 属性指定,我们这里叫 h,除了 React.createEmenent,还有个特殊元素 - Fragment,TypeScript 默认会翻译成 React.Fragment,我们通过 jsxFragmentFactory 直接改为 Fragment

tsconfig.json:

{
  "compilerOptions": {
    "target": "esnext",
    "module": "commonjs",
    "moduleResolution": "node",
    "jsx": "react", // enable jsx
    "jsxFactory": "h", // React.createElement => h
    "jsxFragmentFactory": "Fragment", // React.Fragment => Fragment
    "rootDir": "./src",
    "lib": ["dom", "es2015"]
  }
}

Rollup 配置

Rollup 的配置比较简单,除了 input,output,再额外加一些插件就可以了:

const path = require('path')
const typescript = require('rollup-plugin-typescript2')
const { terser } = require('rollup-plugin-terser')
const eslint = require('@rollup/plugin-eslint')

export default {
  input: 'src/index.ts',
  output: [
    { file: 'dist/rac.umd.js', format: 'umd', name: 'rac' }
  ],
  plugins: [
    terser(),
    eslint({
      throwOnError: true,
      include: ['src/**/*.ts']
    }),
    typescript({
      verbosity: 0,
      tsconfig: path.resolve(__dirname, 'tsconfig.json'),
      useTsconfigDeclarationDir: true
    })
  ]
}

Eslint in TypeScript

为了能让 Eslint 支持 TypeScript,需要给 Eslint 一些额外配置:

module.exports = {
  parser: '@typescript-eslint/parser',
  env: {
    es6: true,
    browser: true
  },
  plugins: [
    '@typescript-eslint'
  ],
  extends: [
    'eslint:recommended',
  ],
  parserOptions: {
    sourceType: 'module'
  },
  rules: {
    ...
  }
}

项目结构
React 新的 Fiber 架构有几个核心概念,在 Build your own React 中,作者依照

  • Step I: The createElement Function
  • Step II: The render Function
  • Step III: Concurrent Mode
  • Step IV: Fibers
  • Step V: Render and Commit Phases
  • Step VI: Reconciliation
  • Step VII: Function Components
  • Step VIII: Hooks

这几步逐步实现了一个 mini React,为了提高代码可读性和可维护性,会把这些功能划分到不同的文件中:

.
├── README.md
├── examples  // demo目录
├── package.json
├── rollup.config.js
├── src
│   ├── dom.ts
│   ├── h.ts
│   ├── hooks.ts
│   ├── index.ts
│   ├── reconciler.ts
│   ├── scheduler.ts
│   └── type.ts
└── tsconfig.json
  • dom.ts 中处理 DOM 相关工作
  • h.ts 中是对 jsxFactory, jsxFragmentFactory 的实现
  • hooks.ts 中是对 hooks 的实现
  • reconciler.ts 是 reconcile 阶段和 commit 阶段的实现
  • shceduler.ts 是任务调度器的实现
  • type.ts 是一些类型定义

到这工程就搭建起来了,整个工程的结构和一些代码实现上借鉴了 fre 这个框架。

查看原文

赞 10 收藏 4 评论 0

CrazyCodes 关注了用户 · 3月17日

阿遂_Asui @asui_83x0

SegmentFault 思否社区老编辑

饮水机の守护神,艾泽拉斯的勇士,朝阳区埃米纳姆,我爸我妈的独生子,深夜撰稿者,统领一猫一狗的国王,Glory to the Sin'dorei!

关注 176

CrazyCodes 关注了用户 · 3月4日

三掌柜 @sanzhanggui

一分耕耘,不一定有一分收获,但十分耕耘,一定会有一分收获!

关注 1828

CrazyCodes 关注了用户 · 3月2日

芒果果 @wangying_5ea4fb9de961c

一路走走看看,顺便留下点什么。

关注 61

CrazyCodes 赞了文章 · 2月24日

PHPMQTT v1.3.0 版本发布,MQTT 协议解析 & 协程客户端

v1.3.0 版本新增了一个 Message 类簇,主要方便用于在 Server 中回复对端 ACK。


use Simps\MQTT\Protocol\Types;
use Simps\MQTT\Protocol\V3;
use Simps\MQTT\Message\ConnAck;
use Simps\MQTT\Message\PingResp;

$server = new Swoole\Server('127.0.0.1', 1883, SWOOLE_BASE);

$server->set(
    [
        'open_mqtt_protocol' => true,
        'package_max_length' => 2 * 1024 * 1024,
    ]
);

$server->on('connect', function ($server, $fd) {
    echo "Client #{$fd}: Connect.\n";
});

$server->on('receive', function (Swoole\Server $server, $fd, $from_id, $data) {
    $data = V3::unpack($data);
    if (is_array($data) && isset($data['type'])) {
        switch ($data['type']) {
            case Types::CONNECT:
                if ($data['protocol_name'] != 'MQTT') {
                    $server->close($fd);

                    return false;
                }
                $server->send(
                    $fd,
                    (new ConnAck())->setCode(0)
                        ->setSessionPresent(0)
                );
                break;
            case Types::PINGREQ:
                $server->send($fd, (new PingResp()));
                break;
        }
    } else {
        $server->close($fd);
    }
});

$server->on('close', function ($server, $fd) {
    echo "Client #{$fd}: Close.\n";
});

$server->start();

在收到CONNECT包之后,需要回复CONNACK报文,之前的版本需要用户手动调用pack类来生成send_data

而现在只需要实例化对应的 Message 类,设置所需要的值即可,如 ConnAck :

  • MQTT3
use Simps\MQTT\Message\ConnAck;

$ack = new ConnAck();
$ack->setCode(0)->setSessionPresent(0);

$server->send($fd, $ack->getContents());
$server->send($fd, $ack);
  • MQTT5
use Simps\MQTT\Message\ConnAck;
use Simps\MQTT\Protocol\ProtocolInterface;

$ack = new ConnAck();
$ack->setProtocolLevel(ProtocolInterface::MQTT_PROTOCOL_LEVEL_5_0)
    ->setCode(0)
    ->setSessionPresent(0)
    ->setProperties([]);

$server->send($fd, $ack->getContents());
$server->send($fd, $ack);

其他的可以查看 具体的类 或者 example 文件

更新日志

向下不兼容

  • SUBACK 的payload应该是返回码,修改键名payloadcodes (9e72ce2) (283ff41)

增强

  • 优化 Client recv (#38) (99a85bf)
  • 添加 CONNACK & PUBLISH & PINGRESP Message (700a6c9)
  • 添加 SubAck Message 和更新 getMessageId (09f6334)
  • 添加 DISCONNECT、PUBACK、PUBREC、PUBREL、UNSUBACK (20a78c7)
  • 修改 AbstractConfig (dff6283)
  • 添加 Message 使用示例 (58d5b4a)
  • 添加 getContents 方便在__toString中调用 (a7ba577)
  • 添加 Message 使用文档 (bab2297)

关于 PHPMQTT

  • MQTT 协议解析 & 协程客户端
  • 适用于 PHP 的 MQTT 协议解析和协程客户端
  • 支持 MQTT 协议 3.1、3.1.1 和 5.0 版本,支持 QoS 0、QoS 1、QoS 2
  • 首个支持 MQTT v5.0 协议的 PHP library

文档:https://mqtt.simps.io
GitHub:https://github.com/simps/mqtt
Gitee:https://gitee.com/phpiot/mqtt

支持记得点个 Star~

沈唁志公众号

查看原文

赞 3 收藏 1 评论 0

CrazyCodes 赞了文章 · 2月22日

我身边的高T,问了Java面试者这样的问题......

大家好,我是来自JDL京东物流技术发展部邢焕杰, 今天来分享一个京东面试真题,这也是我前阵子听我工位旁边高T(高,实在是高)面试候选人的时候问的一个问题,他问,你能说说 MySQL的事务吗?MVCC有了解吗?话不多说,本文就深度解析一下MySQL事务及MVCC的实现原理。

事务定义及四大特性

  • 事务是什么?
  • 事务的四大特性(简称ACID):
  • 原子性(Atomicity):一个事务是一个不可分割的工作单位,事务中包括的操作要么都做,要么都不做。
  • 一致性(Consistency):事务必须是使数据库从一个一致性状态变到另一个一致性状态。一致性与原子性是密切相关的。
  • 隔离性(Isolation):一个事务的执行不能被其他事务干扰。即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰.
  • 持久性(Durability):指一个事务一旦提交,它对数据库中数据的改变就应该是永久性的,接下来的其他操作或故障不应该对其有任何影响。

事务中常见问题

  • 脏读(dirty read):就是一个A事务即便没有提交,它对数据的修改也可以被其他事务B事务看到,B事务读到了A事务还未提交的数据,这个数据有可能是错的,有可能A不想提交这个数据,这只是A事务修改数据过程中的一个中间数据,但是被B事务读到了,这种行为被称作脏读,这个数据被称为脏数据
  • 不可重复读(non-repeatable read):在A事务内,多次读取同一个数据,但是读取的过程中,B事务对这个数据进行了修改,导致此数据变化了,那么A事务再次读取的时候,数据就和第一次读取的时候不一样了,这就叫做不可重复读
  • 幻读(phantom read):A事务多次查询数据库,结果发现查询的数据条数不一样,A事务多次查询的间隔中,B事务又写入了一些符合查询条件的多条数据(这里的写入可以是update,insert,delete),A事务再查的话,就像发生了幻觉一样,怎么突然改变了这么多,这种现象这就叫做幻读

隔离级别——产生问题的原因

多个事务互相影响,并没有隔离好,就是我们刚才提到的事务的四大特性中的 隔离性(Isolation) 出现了问题 事务的隔离级别并没有设置好,下面我们来看下事务究竟有哪几种隔离级别

  • 隔离级别
  • 读未提交(read uncommitted RU): 一个事务还没提交时,它做的变更就能被别的事务看到
  • 读提交(read committed RC): 一个事务提交之后,它做的变更才会被其他事务看到。
  • 可重复读(repeatable read RR): 一个事务执行过程中看到的数据,总是跟这个事务在启动时看到的数据是一致的。当然在可重复读隔离级别下,未提交变更对其他事务也是不可见的。
  • 串行化(serializable ): 顾名思义是对于同一行记录,“写”会加“写锁”,“读”会加“读锁”。当出现读写锁冲突的时候,后访问的事务必须等前一个事务执行完成,才能继续执行。

我们来看个例子,更加直观的了解这四种隔离级别和上述问题脏读,不可重复读,幻读的关系

下面我们讨论下当事务处于不同隔离级别情况时,V1,V2,V3分别是什么不同的值吧

  • 读未提交 (RU): A事务可以读取到B事务修改的值,即便B事务没有提交。所以V1就是200
  • V1 : 200
  • V2 : 200
  • V3 : 200
  • 读提交(RC): 当B事务没有提交的时候,A事务不可以看到B事务修改的值,只有提交以后才可以看到
  • V1 : 100
  • V2 : 200
  • V3 : 200
  • 可重复读(RR): A事务多次读取数据,数据总和第一次读取的一样,
  • V1 : 100
  • V2 : 100
  • V3 : 200
  • 串行化(S): 事务A在执行的时候,事务B会被锁住,等事务A执行结束后,事务B才可以继续执行
  • V1 : 100
  • V2 : 100
  • V3 : 200

MVCC原理

MVCC(Multi-Version Concurrency Control)多版本并发控制,是数据库控制并发访问的一种手段。

  • 特别要注意MVCC只在 读已提交(RC) 和 可重复度(RR) 这两种事务隔离级别下才有效
  • 是 数据库引擎(InnoDB) 层面实现的,用来处理读写冲突的手段(不用加锁),提高访问性能

MVCC是怎么实现的呢?它靠的就是版本链一致性视图

1. 版本链

  • 版本链是一条链表,链接的是每条数据曾经的修改记录

那么这个版本链又是如何形成的呢,每条数据又是靠什么链接起来的呢?

其实是这样的,对于InnoDB存储引擎的表来说,它的聚簇索引记录包含两个隐藏字段

  • trx_id: 存储修改此数据的事务id,只有这个事务操作了某些表的数据后当更改操作发生的时候(update,delete,insert),才会分配唯一的事务id,并且此事务id是递增的
  • roll_pointer: 指针,指向上一次修改的记录
  • row_id(非必须): 当有主键或者有不允许为null的unique键时,不包含此字段

假如说当前数据库有一条这样的数据,假设是事务ID为100的事务插入的这条数据,那么此条数据的结构如下

后来,事务200,事务300,分别来修改此数据

所以此时的版本链如下

我们每更改一次数据,就会插入一条undo日志,并且记录的roll_pointer指针会指向上一条记录,如图所示

  1. 第一条数据是小杰,事务ID为100
  2. 事务ID为200的事务将名称从小杰改为了A
  3. 事务ID为200的事务将名称从A又改为了B
  4. 事务ID为300的事务将名称从B又改为了C

所以串成的链表就是 C -> B -> A -> 小杰 (从最新的数据到最老的数据)

2. 一致性视图(ReadView)

需要判断版本链中的哪个版本是是当前事务可见的,因此有了一致性视图的概念。其中有四个属性比较重要

  • m_ids: 在生成ReadView时,当前活跃的读写事务的事务id列表
  • min_trx_id: m_ids的最小值
  • max_trx_id: m_ids的最大值+1
  • creator_trx_id: 生成该事务的事务id,单纯开启事务是没有事务id的,默认为0,creator_trx_id是0。

版本链中的当前版本是否可以被当前事务可见的要根据这四个属性按照以下几种情况来判断

  • 当 trx_id = creator_trx_id 时:当前事务可以看见自己所修改的数据, 可见
  • 当 trx_id < min_trx_id 时   : 生成此数据的事务已经在生成readView前提交了, 可见
  • 当  trx_id >= max_trx_id 时   :表明生成该数据的事务是在生成ReadView后才开启的, 不可见
  • 当  min_trx_id <= trx_id < max_trx_id 时
  • trx_id 在 m_ids 列表里面 :生成ReadView时,活跃事务还未提交,不可见
  • trx_id 不在 m_ids 列表里面 :事务在生成readView前已经提交了,可见

如果某个版本数据对当前事务不可见,那么则要顺着版本链继续向前寻找下个版本,继续这样判断,以此类推。

注:RR和RC生成一致性视图的时机不一样 (这也是两种隔离级别实现的主要区别)

  • 读提交(read committed RC) 是在每一次select的时候生成ReadView的
  • 可重复读(repeatable read RR)是在第一次select的时候生成ReadView的

下面咱们一起来举个例子实战一下。

RR与RC和MVCC的例子实战

假如说,我们有多个事务如下执行,我们通过这个例子来分析当数据库隔离级别为RC和RR的情况下,当时读数据的一致性视图版本链,也就是MVCC,分别是怎么样的。

  • 假设数据库中有一条初始数据  姓名是java小杰要加油,id是1 (id,姓名,trx_id,roll_point),插入此数据的事务id是1
  • 尤其要指出的是,只有这个事务操作了某些表的数据后当更改操作发生的时候(update,delete,insert),才会分配唯一的事务id,并且此事务id是递增的,单纯开启事务是没有事务id的,默认为0,creator_trx_id是0。
  • 以下例子中的A,B,C的意思是将姓名更改为A,B,C 读也是读取当前时刻的姓名,默认全都开启事务,并且此事务都经历过某些操作产生了事务id

读已提交(RC)与MVCC

  • 一个事务提交之后,它做的变更才会被其他事务看到
每次读的时候,ReadView(一致性视图)都会重新生成
  1. 当T1时刻时,事务100修改名字为A
  2. 当T2时刻时,事务100修改名字为B
  3. 当T3时刻时,事务200修改名字为C
  4. 当T4时刻时,事务300开始读取名字
  • 此时这条数据的版本链如下

同颜色代表是同一事务内的操作

  • 来我们静下心来好好分析一下此时T4时刻事务300要读了,究竟会读到什么数据?

当前最近的一条数据是,C,事务200修改的,还记得我们前文说的一致性视图的几个属性吗,和按照什么规则判断这个数据能不能被当前事务读。我们就分析这个例子。

此时  (生成一致性视图ReadView

  • m_ids 是[100,200]: 当前活跃的读写事务的事务id列表
  • min_trx_id 是 100:  m_ids的最小值
  • max_trx_id 是 201: m_ids的最大值+1

当前数据的trx_id(事务id)是 200,符合min_trx_id<=trx_id<max_trx_id 此时需要判断 trx_id 是否在m_ids活跃事务列表里面,一看,活跃事务列表里面是【100,200】,只有两个事务活跃,而此时的trx_id是200,则trx_id在活跃事务列表里面,活跃事务列表代表还未提交的事务,所以该版本数据不可见,就要根据roll_point指针指向上一个版本,继续这样的判断,上一个版本事务id是100,数据是B,发现100也在活跃事务列表里面,所以不可见,继续找到上个版本,事务是100,数据是A,发现是同样的情况,继续找到上个版本,发现事务是1,数据是小杰,1小于100,trx_id<min_trx_id,代表生成这个数据的事务已经在生成ReadView前提交了,此数据可以被读到。所以读取的数据就是小杰。

分析完第一个读,我们继续向下分析

  1. 当T5时刻时,事务100提交
  2. 当T6时刻时,事务300将名字改为D
  3. 当T7时刻时,事务400读取当前数据
  • 此时这条数据的版本链如下

此时 (重新生成一致性视图ReadView

  • m_ids 是[200,300]: 当前活跃的读写事务的事务id列表
  • min_trx_id 是 200:  m_ids的最小值
  • max_trx_id 是 301: m_ids的最大值+1

当前数据事务id是300,数据为D,符合min_trx_id<=trx_id<max_trx_id 此时需要判断数据是否在活跃事务列表里,300在这里面,所以就是还未提交的事务就是不可见,所以就去查看上个版本的数据,上个版本事务id是200,数据是C,也在活跃事务列表里面,也不可见,继续向上个版本找,上个版本事务id是100,数据是B,100小于min_trx_id,就代表,代表生成这个数据的事务已经在生成ReadView前提交了,此数据可见,所以读取出来的数据就是B。

分析完第二个读,我们继续向下分析

  1. 当T8时刻时,事务200将名字改为E
  2. 当T9时刻时,事务200提交
  3. 当T10时刻时,事务300读取当前数据
  • 此时这条数据的版本链如下

此时 (重新生成一致性视图ReadView

  • m_ids 是[300]: 当前活跃的读写事务的事务id列表
  • min_trx_id 是 300:  m_ids的最小值
  • max_trx_id 是 301: m_ids的最大值+1

当前事务id是200,200<min_trx_id ,代表生成这个数据的事务已经在生成ReadView前提交了,此数据可见,所以读出的数据就是E。

当隔离级别是读已提交RC的情况下,每次读都会重新生成 一致性视图(ReadView)

  • T4时刻 事务300读取到的数据是小杰
  • T7时刻 事务400读取到的数据是B
  • T10时刻 事务300读取到的数据是E

可重复读(RR)与MVCC

  • 一个事务执行过程中看到的数据,总是跟这个事务在启动时看到的数据是一致的
所以对于事务300来讲,它分别在T4和T10的时候,读取数据,但是它的一致性视图,用的永远都是第一次读取时的视图,就是T3时刻产生的一致性视图

RR和RC的版本链是一样的,但是判断当前数据可见与否用到的一致性视图不一样

在此可重复读RR隔离级别下,

  1. T4时刻时事务300第一次读时的分析和结果与RC都一样,可以见上文分析与结果
  2. T7时刻时事务400第一次读时的分析和结果与RC都一样,可以见上文分析与结果
  3. T10时刻时事务300第二次读时的一致性视图和第一次读时的一样,所以此时到底读取到什么数据就要重新分析了

此时  (用的是第一次读时生成的一致性视图ReadView

  • m_ids 是[100,200]: 当前活跃的读写事务的事务id列表
  • min_trx_id 是 100:  m_ids的最小值
  • max_trx_id 是 201: m_ids的最大值+1

此时的版本链是

当前数据的事务id是200,数据是E,在当前事务活跃列表里面,所以数据不可见,根据回滚指针找到上个版本,发现事务id是300,当前事务也是300,可见,所以读取的数据是D

  • 我们可以自己思考下,要是没有事务300这条更改的这条记录,又该怎么继续向下分析呢?

当隔离级别是可重复读RR的情况下,每次读都会用第一次读取数据时生成的一致性视图(ReadView)

  • T4时刻 事务300读取到的数据是小杰
  • T7时刻 事务400读取到的数据是B
  • T10时刻 事务300读取到的数据是D

欢迎点击【京东科技】,了解开发者社区

更多精彩技术实践与独家干货解析

欢迎关注【京东科技开发者】公众号

查看原文

赞 13 收藏 9 评论 4

CrazyCodes 发布了文章 · 2月21日

从MySQL开始聊聊“树”结构 (上)

image.png

前言

嗨喽,大家好,我是CrazyCodes, 近一年写的文章,都是一些广度方面的思考,新的一年,在技术深度上也需要有更多的探索,感谢各位的持续支持!

MySQL

image.png

先聊聊大家熟知的MySQL,我们使用MySQL肯定是有数据存储的需求。

我们从基础开始看,首先我们创建一张用户表

CREATE TABLE `user` (
  `id` int unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(32) COLLATE utf8mb4_general_ci DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;

这是一张简单的用户表,包含id和name两个字段,id为主键自增,name上没有索引。想必大家对索引都有一定的认识,索引是一种数据结构,即本篇的核心议题“树”结构,索引可以达到快速命中某条记录,“快速”一词代表着在时间和空间相结合的最优解,当然也并不代表索引越多,数据表设计越合理,要根据实际业务去选择。

我们随机向表中插入几条记录

INSERT INTO `user` VALUES(1,"Join");
INSERT INTO `user` VALUES(2,"Tom");
INSERT INTO `user` VALUES(3,"Jeein");

按照预期,数据库内出现三条对应的记录,这里我使用语句

SELECT * FROM `user`;

对数据进行查询
image.png
看到这里,你可能说了,这是在讲什么,增删改查都是常识,有什么好讲的,那我们现在就正式进入正题,首先从插入记录开始说起,MySQL会使用B+树来存储数据记录,B+树是B树的延伸,而B树又是平衡二叉树的延伸,除了平衡二叉树还有二叉查找树,依次基于其概念延伸的排序,大概是如下这样

B+树 -> B树 -> 平衡二叉树 -> 二叉查找树 -> 二叉树 -> 树

当然也不仅仅是所有跟树有关的数据结构,这里为了提高学习树这种数据结构的兴趣,所以以MySQL讲起,纠其根源,再结合MySQL的数据记录将这一串知识链接起来,那我们把这个顺序倒过来依次讲起

树 -> 二叉树 -> 二叉查找树 -> 平衡二叉树 -> B树 -> B+树

我尽量不把这些数据结构讲成教科书的样子。

树🌲

image.png
你要不知道树是什么玩意,就别继续看下去了😂,就是大街上我们看到的树,大街的树也就是我们常识认知的树,是通过“树根”、“树枝”和“树叶”组成,缺一样,这树也不是一颗完整的树。

那么数据结构中的树指的什么,你可以把手机或者电脑倒过来看上图,数据结构中的树,就是一个倒过来的树,并不是他非要倒过来才能说,而是根据人类正常的视觉与思维,就例如你看书总不能从最后一页的最后一个字开始从后往前读把,倒过来只是便于理解和阅读它罢了。

在数据结构中倒过来的树大概是张下面这个样子

倒树
image.png

正树
image.png

正树、倒树,都贴出来了,你可以尝试对比这两张图,看看那张图更容易理解,接下来的案例只会以倒树展示。

树是其他树型数据结构的基础,这里你必须要熟记一些基本概念

  • 树根,树的根节点
  • 子节点,树枝一、二、三是树根的子节点
  • 兄弟节点,树枝一、二、三互为兄弟节点
  • 父节点,树根是树枝一的父节点,树枝一是树枝1-1的父节点

相信根据上图多看几遍,这很容易理解

那我们生搬硬套,把MySQL的数据放到这个最简单的树中
image.png
这是我随意摆放的,当然你想怎么摆就怎么摆,例如
image.png
这看起来像跟棍,或者
image.png
无论怎样,我们创建了一个树,那根据SQL语句

SELECT * FROM `user`;

我们需要查到根节点,这很简单,然后去判断根节点的左子节点是否有值,有的话取出来,右子节点也是一样的道理,这就是所谓的树的遍历。那么我们现在在SQL上加些条件

SELECT * FROM `user` WHERE `id` = 1;

image.png

那这个条件,在树结构中如何查呢,到这里,你会发现一个问题

无论条件是什么,我们都需要遍历整棵树,遍历树的深度,完全取决于要查询的数据所在的位置,位置靠前,就少遍历些,位置靠后就多遍历下。

这里讲一个更基础的知识,时间复杂度,时间复杂度用于表示某个算法所计算的时间长度,那我们根据上面这个例子,可以分析出其平均时间复杂度为 O(n) , 当然最好的情况是 O(1) , 这种情况就是根节点就是我们要找的数据。

数据的不断增加,才使得我们无法继续使用最原始的数据结构,哎,都是数据逼得呀。

假设我们不仅仅有3条记录,而是在3000万条查找Join,恰巧还是第3000万条,那么如果使用最基本的树结构去查询的话,那么我们将要进行3000万去查询才可以获得最终结果。是不是感觉不切实际了?

其实这样的话,是不是树结构都不重要了,他可以是一个数组、链表或者队列等等,可以用任意数据结构去存储,当然每种数据结构的出现都有其存在的原因。那么我们使用原始树的结构感觉不靠谱,那么跟随我的文字来到二叉树的世界!

二叉树

image.png

二叉树,顾名思义是只有两个叉的树,每个节点只有两个叉,这是它的特性,这时候你就疑问了,为啥不能是三叉树,四叉树,N叉树呢?当然可以有,那么我们举一个例子,我们再往user表内插几行数据

INSERT INTO `user` VALUES(4,"Jorr");
INSERT INTO `user` VALUES(5,"Queie");
INSERT INTO `user` VALUES(6,"Kioa");

现在我们库里有6条记录
image.png
我们拿三叉树举例
image.png
我把每个节点需要查询的次数列个表,这里我使用前序遍历树的方式,让你简单的清楚树有几叉意味着什么

tips :树有三种遍历方式,分别为 前序、中序、后序 ,根据具体需求使用不同的遍历方式
节点次数
1,join1
3,jeein2
5,queie3
6,kioa4
2,tom5
4,jorr6

如果是二叉树呢?
image.png

节点次数
1,join1
3,jeein2
5,queie3
6,kioa4
2,tom5
4,jorr6

有没有很奇怪?无论几叉,查询4,jorr数据都需要6次,感觉用哪个都一样是不?并不是这样,你可以这样考虑一个问题,在使用程序去遍历这两棵树时,二叉树需要判断左右子节点,而三叉树则需要判断左中右三个节点,他们所在的内存空间不同,专业点说,他们的空间复杂度不同。

解答了你的一点点小疑惑后,我们回到正题,还是聊聊二叉树的事。

二叉树分为完全二叉树和不完全二叉树

  • 完全二叉树代表每个节点都有2个树枝(就是不缺胳膊少腿)
  • 不完全二叉树反之就是(缺胳膊少腿),某个树叉上可能只有左子树,没有右子树

上述展示了MySQL的数据生搬硬套放到二叉树中玩法,但你发现没?我们依旧无法避免树被全部遍历的问题。

思考

我将在下一篇文章讲述剩余的“几颗树”,这里我给你留一道思考题。

问题一:树和二叉树(N叉树)都无法避免遍历整棵树,那下一篇要讲的平衡二叉树为什么可以做到只需要遍历“半棵树”?

问题二:时间复杂度的表示方式 O(1) O(n) O(log2^n) 分别代表什么?

致谢

感谢你看到这里,2021 我会在思否发布自己电商设计的录播课,也是我首个录播课。

希望本篇文章可以帮助到你,谢谢。

查看原文

赞 28 收藏 16 评论 5

CrazyCodes 赞了文章 · 2月1日

IntelliJ IDEA 20 岁了!20 年前的第 1 版曝光…

IntelliJ IDEA 最近发布了 20 周年庆典:

https://www.jetbrains.com/lp/...

IntelliJ IDEA 是目前最受欢迎、最智能的 IDE,没有之一,它诞生于 2001 年,其诞生的愿景就是:使开发变得更加高效、有趣

IntelliJ IDEA 有商业版本和社区版本,来看下功能对比:

商业版本功能明显更强大,不过社区版本也能满足日常开发需要。

另外一个很头疼的问题就是,商业版本对一般开发人员来说确实不便宜:

一年大几千,能有多少程序员舍得掏这个钱?

IntelliJ IDEA 确实贵,但贵有贵的道理,即使如此,官方也释放出了许多正规途径来免费获取正版激活码,关注公众号Java技术栈,回复:IDEA,可以阅读我分享过的获取正版 IDEA 激活码的教程,很多粉丝都反馈说轻松得到了,感兴趣的都可以去申请,不能太容易了。

所以说,IntelliJ IDEA 对开发人员还是很厚道的。。

截止到今年 2021 年,全世界有超过 400+ 万的开发人员使用 IntelliJ IDEA 进行编程,在过去 20 年发布了 40+ 个主要版本。

来看下 2001 年的 IntelliJ IDEA 的第一个版本:

虽然现在看起来它很简陋,但在那个年代,它是首批具有高级代码导航,以及集成代码重构功能的 Java IDE。

2020 年的 IntelliJ IDEA:

JetBrains 推出了 Mono 字体,这是一种专门为开发人员设计的新字体,它成为 IntelliJ IDEA 和其他 JetBrains IDEs 中的默认字体。

2020 发布的 IntelliJ IDEA 2020.1 支持 Java 14,支持直接从 IDE 中下载和设置 JDK,支持调试器中的数据流分析,以及新增了 LightEdit 模式等等!

2020 年最后的一个版本:2020.3.2

IntelliJ IDEA 2021.1 正在开发中,想抢先体验的可以从这里下载:

https://www.jetbrains.com/ide...

全世界都在使用 IntelliJ IDEA:

IntelliJ IDEA 的开发者已超过:400 万+,2020 年下载量超过:1,200 万+。

Top 5 用户数最多的国家:

中国程序员最多。。。

其他 4 个依次是:美国、印度、德国、俄罗斯。

Top 5 使用最多的特性:

  • Сode completion(代码完成)
  • Show Intention Actions(显示意向动作)
  • Run(运行)
  • Go to Declaration(前往申明处)
  • Toggle Line Breakpoint(打行断点)

IntelliJ 平台团队规模:

JetBrains 现已使用 IntelliJ 平台针对特定语言和技术构建专用的 IDE 产品,以提供最佳的编程体验,这些产品主要包括:

你用过几个?

有几个还是眼熟的,比如数据库端工具:DataGrip,以及针对各种开发语言定制的开发工具, JetBrains 已然已成为开发工具全家桶,真香!

如果你还没用过 IntelliJ IDEA,这里推荐几篇历史教程:

更多请关注公众号Java技术栈,在后台回复:idea,可以获取我整理的完整版教程。

一起来期待未来的 20 年及以后……

版权申明:本文系公众号 "Java技术栈" 原创,原创实属不易,转载、引用本文内容请注明出处,禁止抄袭、洗稿,请自重,尊重他人劳动成果和知识产权。

近期热文推荐:

1.Java 15 正式发布, 14 个新特性,刷新你的认知!!

2.终于靠开源项目弄到 IntelliJ IDEA 激活码了,真香!

3.我用 Java 8 写了一段逻辑,同事直呼看不懂,你试试看。。

4.吊打 Tomcat ,Undertow 性能很炸!!

5.《Java开发手册(嵩山版)》最新发布,速速下载!

觉得不错,别忘了随手点赞+转发哦!

查看原文

赞 5 收藏 2 评论 1

CrazyCodes 关注了用户 · 1月26日

huangzhhui @huangzhhui

Creator of Hyperf

关注 550

CrazyCodes 关注了用户 · 1月25日

fenbox @fenbox

主业设计,副业写代码

          =,    (\_/)    ,=
           /`-'--(")--'-'\
      woo /     (___)     \
         /.-.-./ " " \.-.-.\

08 年参与 Typecho 开源博客项目
11 年参与 SegmentFault 开发者问答项目
12 年创业,与 joyqi、sunny 驻扎莲花街

关注 492

CrazyCodes 赞了文章 · 1月25日

2021.01.25 更新:新版文章详情

SegmentFault 技术小队历经几个月的改造,实现网站容器化和部分页面前后端分离。你当前看到的这个页面就是前后端分离的版本。

秉承 SegmentFault 历来的作风,我们的技术栈在原有 PHP 的基础上引入了 SwooleReact,大幅提高了性能和现有服务器资源的利用率。接下来还将逐步改造其他页面。

新功能

  • 文章详情前后端分离
  • 撰写/编辑页面前后端分离
  • 文章封面图功能
  • 文章支持结构化数据
  • 记住上次发布的专栏和版权信息
  • 新版本 404 等错误页面

优化更新

  • 移除格子广告、减少广告位数量
  • 移除复制需要登录的右倾错误
  • 优化文章详情页布局

    • 提高文章作者的曝光率
    • 文章作者的会显示第一行个人简介,可以利用这个功能一句话描述自己
    • 优化文章点赞效果
  • 评论交互改进
  • 优化转载、翻译的“阅读原文”链接及版权信息
  • 改进搜索引擎友好度

缺陷修复

  • 修复验证码登录注册失败的问题
  • 修复定时发布文章的草稿时间问题
查看原文

赞 10 收藏 1 评论 20

CrazyCodes 赞了文章 · 1月24日

mysql中InnoDB引擎中页的概念

Innodb中页的概念

基础结构

Page是Innodb存储的最基本结构,也是Innodb磁盘管理的最小单位,与数据库相关的所有内容都存储在Page结构里。Page分为几种类型:数据页(B-Tree Node)Undo页(Undo Log Page)系统页(System Page)事务数据页(Transaction System Page)等;每个数据页的大小为16kb,每个Page使用一个32位(一位表示的就是0或1)的int值来表示,正好对应Innodb最大64TB的存储容量(16kb * 2^32=64tib)
一个Page的基本结构如下:

clipboard.png

头部数据

每个page都有通用的头和尾,但是中部的内容根据page的类型不同而发生变化,头部的数据如下:

clipboard.png

page头部保存了两个指针,分别指向前一个Page和后一个Page,头部还有Page的类型信息和用来唯一标识Page的编号。根据这个指针分布可以想象到Page链接起来就是一个双向链表

clipboard.png

主体数据

在Page的主体部分,主要关注数据和索引的存储,他们都位于User Records部分,User Records占据Page的大部分空间,User Records由一条条的Record组成,每条记录代表索引树上的一个节点(非叶子节点和叶子节点);在一个单链表的内部,单链表的头尾由两条记录来表示,字符串形式的“ Infimum”代表开头,“Supremum”表示结尾;System Record 和 User Record是两个平行的段;
Innodb中存在四种不同的Record,分别是

  1. 主键索引树非叶子节点

  2. 主键索引树叶子节点

  3. 辅助键索引树非叶子节点

  4. 辅助键索引树叶子节点

这四种节点Record格式上有差异,但是内部都存储着Next指针指向下一个Record

clipboard.png

User Record

User Record在Page内以单链表的形式存在,最初数据是按照插入的先后顺序排列的,但是随着新数据的插入和旧数据的删除,数据物理顺序发生改变,但是他们依然保持着逻辑上的先后顺序

clipboard.png

把User Record组织形式和若干Page组织起来,就得到了稍微完整的形式:

clipboard.png

如何定位一个Record:

  1. 通过根节点开始遍历一个索引的B+树,通过各层非叶子节点达到底层的叶子节点的数据页(Page),这个Page内部存放的都是叶子节点

  2. 在Page内部从“Infimum”节点开始遍历单链表(遍历一般会被优化),如果找到键则返回。如果遍历到了“Supremum”,说明当前Page里没有合适的键,这时借助Page页内部的next page指针,跳转到下一个page继续从“Infmum”开始逐个查找

clipboard.png

User Record内部的数据

User Record内部存储了四种格式的数据:

  1. 主索引树非叶子节点(绿色)

    • 子节点存储的主键里最小的值,这时B+树必须的,作用是在一个Page里定位到具体的记录的位置

    • 最小的值所在的Page的编号,作用是定位到对应的Record所在的Page

  2. 主索引树叶子节点(黄色)

    • 主键,B+树所必须的,也是数据行的一部分

    • 除去主键以外的所有列,这时数据行的除去主键的其他所有列的集合

  3. 辅助索引树非叶子节点(蓝色)

    • 子节点里存储的辅助键值里的最小值,这时B+Tree必须的,作用是在一个Page里定位到具体记录的位置

  4. 辅助索引树叶子节点(红色)

    • 辅助索引键值,是B+树必须的

    • 主键值,用来在主索引树里在做一次B+树检索来找到整条记录

clipboard.png

整体的查找过程

clipboard.png

简介的树形查找示意图

clipboard.png

Page和B+树之间并没有一一对应的关系,Page只是作为一个Record的保存容器,它存在的目的是便于对磁盘空间进行批量管理。

查看原文

赞 25 收藏 20 评论 4

CrazyCodes 赞了文章 · 1月21日

有道云笔记新版编辑器架构设计(上)

在开发有道云笔记的新版编辑器的过程中,我们遇到很多实际问题,愈发感觉到这是一个非常有深度的前端技术领域,所以我们将新版编辑器的技术选型、架构和部分实现细节拿出来分享给大家,希望对大家开发富文本编辑器、做复杂系统的架构设计有一定参考意义。

作者/ 金鑫

编辑/ Ryan

来源/ 有道技术团队(ID: youdaotech)

1. 富文本编辑器背景

1.1 什么是编辑器

编辑器在前端开发领域是指可以提供给用户编辑纯文本、富文本、代码、多媒体内容等的功能模块,例如以云笔记为例,编辑器指下图中绿色的区域。

有道云笔记编辑器界面

编辑器一般由编辑区域、光标、工具栏、右键菜单等功能模块组成,一般都包含编辑文字、设置文字样式、设置段落样式、插入多媒体内容、撤销重做、复制剪切粘贴等功能。

1.2 编辑器发展简史

编辑器的由来可以追溯到打字机时代,下图是一个常见的打字机。

我们可以将打字机的构造与编辑器进行类比,打字机的纸张对应于编辑器的编辑,打字机的游标对应于编辑器的光标,甚至敲击键盘的表现,编辑器也与打字机一脉相承:

  1. 当敲击字母时,在光标后输入该字符;
  2. 当敲击空格键时,在游标之后插入空格;
  3. 当敲击回格键时,删除游标之前的字符;
  4. 当敲击换行键时,游标换到下一行开始;

而在计算机中,出现的最早的是文本编辑器,例如我们在 Linux 系统中常用的 vi,vim,emacs 等,它们可以对纯文本数据进行编辑,并引入了撤销重做、复制剪切粘贴、查找替换等编辑器的核心功能。

随着用户图形界面的兴起,人们对于文本的编辑不止满足于纯文本了,还需要给文本段落加上各种格式和排版信息。

同时,人们对于在文档中插入图片、图形、表格等更丰富格式的需求也越来越多。为了满足这些需求,富文本编辑器就出现了,其中的集大成者就是微软的 Word 和金山的 WPS。

Word 和 WPS 可以说将桌面客户端中的富文本编辑器做到了极致,至今也是功能强大的富文本编辑器。

但是它们的设计初衷就是做一款单机的文字处理软件,自然会遇到不支持互联网上的音视频格式、存储备份依靠本地计算机的文件系统、多人协作依靠文件拷贝等问题。

在互联网遍及千家万户的今日,人们反而不太需要 Word 提供的交互复杂的各种强大功能,而是需要支持更多互联网数据格式、存储备份更加方便、能够提供多人协同编辑功能的轻量级富文本编辑器。

基于浏览器的富文本编辑器就是在这样的设计思路下产生的,其中的代表产品有 Google Docs、有道云笔记、印象笔记、石墨文档等。

这些基于浏览器的富文本编辑器都有以下特点:

  1. 利用 Web 技术开发,需要在浏览器环境中使用;
  2. 功能相对 Word 更加简单,只保留了最常用的富文本编辑功能;
  3. 支持图片、附件、视频、音频、地图等多种互联网资源;
  4. 可以将文档备份在网盘中,实现多端同步;
  5. 文档可以分享查看,可以进行多人实时协同编辑。

当然基于浏览器的富文本编辑器,也是经过了几轮的技术迭代和创新,才到了今天这种百花齐放的局面。

1.3 基于浏览器的富文本编辑器四要素

在现代的浏览器框架下,利用 Web 技术开发一款富文本编辑器,一般采用经典 MVC 模型,根据数据模型渲染视图,视图操作通过控制器修改数据模型。具体要解决以下四个问题:

模型:

模型包含内存模型和存储模型。存储模型是数据存储、同步和备份时的模型,需要考虑带宽、存储体积、模型序列化效率、模型正确性验证效率等因素。内存模型则是数据渲染时的模型,结构一般比存储模型复杂,会在存储模型的基础上添加其他渲染时需要用到的属性。

渲染:

渲染指如何将内存模型渲染成 Web 页面。所有的基于浏览器富文本编辑器都将内存模型渲染成为了 HTML 页面。但是它们在排版上的策略略有不同,大多数编辑器都采用了基于 HTML 和 CSS 的排版方式,也有少数编辑器自己实现了排版引擎,例如 Google Docs。

编辑:

编辑指如何提供编辑区域让用户在编辑区域编辑文档,以及如何感知用户编辑区域的编辑动作通知控制器以修改数据模型。浏览器提供了 contentEditable 的属性可以把元素变为可编辑状态,大部分编辑器都是以这个思路进行编辑的,并且它们可以拦截 contentEditable 元素的事件,将事件通知给控制器。也有少数编辑器自己实现了编辑区域和事件系统,例如 Google Docs。

指令:

指控制器根据收到的编辑区域的编辑动作,生成对应指令修改内存模型,内存模型得以更新完成循环。这部分与数据模型相关,如果数据模型是 HTML,编辑器可以通过 execCommand 直接修改 HTML 数据,如果是自定义数据,监听或拦截编辑区域上的事件,也可以推测意图生成出修改数据模型的指令。通过指令修改数据,可以更方便的实现撤销重做、历史版本恢复、协同编辑等功能。

1.4 基于浏览器的富文本编辑器技术演进

基于以上四个问题,可以将基于浏览器的富文本编辑器划分为四代:

第一代:

完全基于浏览器 API 设计,数据模型直接采用 HTML 数据,渲染用原生 HTML,编辑区域用 contentEditable 生成,通过 execCommand 执行浏览器自带的修改 HTML 数据的指令。

这类编辑器一般都出现在各类《XX 行代码教你实现富文本编辑器》的博客里,基本没有成熟的开源编辑器或者商用编辑器采用这一种设计方式。它们的主要问题处在 execCommand 接口上:

  • 只提供了有限的几个命令,例如 execCommand 就没有办法支持插入待办列表。
  • 提供的命令有些有功能受限,例如 'fontSize' 命令只能支持 1-7,导致不能自定义字体的大小。
  • 修改的结果与浏览器有关,例如 'bold' 命令在一些浏览器上会给选区中的文字添加标签,而在另一些浏览器上则会添加标签。

第二代:

由于 execCommand 功能上的限制,第二代的编辑器普遍抛弃了用浏览器的 execCommand 接口直接修改 HTML 文档的办法,而是用自己实现的 execCommand 和指令修改 HTML 文档,这样做就可以实现更加灵活多样的功能。

这类编辑器的主要问题是:不同的 HTML 结构可能表示的含义一样。例如下面两行 HTML 都表示一段既加粗又斜体的文字,但是他们的 HTML 结构却不一样,这样对比较数据是否一样就变得非常困难。

第三代:

针对 HTML 含义不一致的问题,第三代编辑器则抛弃了既用 HTML 做文档模型,又用 HTML 做渲染的策略,而是采用自定义的数据模型,例如 XML 数据模型或者 JSON 数据模型。同样的数据模型渲染生成的 HTML 一样,自定义的操作则可以保证同样的操作修改之后的文档模型也是一样的。

目前常见的编辑器产品例如:有道云笔记、石墨文档等,以及开源的编辑器库例如 Slate、Draft、Quill 等,都是第三代编辑器,它们已经能够满足大多数应用场景。但是由于渲染出的页面中,可编辑区域还是基于 contentEditable,需要根据拦截的事件判断用户行为,生成对应的指令修改数据模型。一旦有用户数据没有拦截,或者处理的行为不对,用户的行为就可能直接修改了 contentEditable 的元素,导致数据和视图的不一致。因此产生的 bug 定位和修复都比较难,在编辑器的移动端适配中经常出现。

第四代:

为了解决 contentEditable 引起的不可控事件,以 Google Docs 为代表的第四代编辑器则彻底抛弃的 contentEditable,自己实现排版引擎。排版引擎控制了文档的页面和布局,将数据渲染成为页面上的HTML。同时由于抛弃了contentEditable,还需要解决实现光标和选区的绘制、监听文字输入事件等技术问题,才可以做到和浏览器类似的编辑体验。

第四代编辑器相对于第三代,优点是彻底解决了contentEditable 引起的 bug,有更好的可扩展性。对应的代价是开发难度更高、体验不如原生、可能会遇到性能问题。目前只有 Google Docs 等采用了这种架构开发编辑器。

2. 云笔记新版编辑器技术选型

根据上节所述的实现富文本编辑器的四要素,我们总结了四代编辑器的技术选型,如下表所示:

有道云笔记新版编辑器综合了项目的可扩展性和实现难度,作出了以下的技术选型:

  • 模型:自定义的 JSON 数据格式作为内存模型,它的压缩版本作为存储模型;
  • 渲染:借助浏览器排版,用 React 框架渲染视图;
  • 编辑:不依赖 contenteditable,拦截浏览器事件判断用户交互,自己实现了光标和选区;
  • 指令:实现了丰富的自定义的富文本编辑指令,重新实现了 execCommand 执行指令。

2.1 模型

有道云笔记新版编辑器采用了自定义的 JSON 数据格式作为内存模型。存储模型与内存模型基本对应,是内存模型的压缩版本,这样可以减少在数据序列化和反序列化过程中出现的错误。

文档模型:

新版编辑器的内存模型采用了文档-段落-文本的三层模型,顶层对象是文档(下图黄色区域),一篇文档包含多个段落(下图蓝色区域),每个段落中有至少一个文本(下图红色区域)。

对于三层的文档模型,我们可以很自然的想到用树的结构来表示它,如下图所示:

由于 JSON 格式天然的就可以表示嵌套的树状结构,所以我们的三层文档模型可以表示为以下的 JSON 结构:

富文本表示:

对于富文本编辑器的数据模型,需要考虑文本的行内样式和段落样式:

行内样式是作用于文字的样式,每个文字都可能有不同的行内样式,例如文字的粗体、斜体、文字颜色、背景色、字体、字号等

段落样式是用作与段落的样式,整段文字只会有一个段落样式,例如对齐方式、行高、段落缩进等。

由于我们三层文档模型中段落是单独一层,有对应的段落节点,所以对于段落样式只需要在段落节点上添加表示段落样式的字段,我们是在 paragraph 添加了 data 字段,其中添加了 style 属性表示该段落的段落样式,如下图所示:

如果要表示行内样式,目前文本节点中只保存一个 content 字段就没有办法胜任了,需要将它拆分为一个一个字符在每个字符上添加表示行内样式的字段,例如我们可以用一个 chars 数组,里面的每个元素表示一个字符,text 字段表示字符的内容,marks 数组表示字符上的行内样式,如下图表示 a rich text。

叶子节点:

上述的富文本行内样式的表示方法中,我们可以看到rich的样式粗体、斜体、红色背景色保存在了 r, i, c, h 四个字符上,存在着冗余数据。我们参考了HTML渲染结果和部分开源编辑器的实现定义了合并规则。

如果连续的字符节点,具有完全相同的行内样式,则它们可以合并成一个叶子节点。

例如上面的例子里,r, i, c, h 四个字符连续且具有完全相同的行内样式,它们是可以合并成为一个叶子节点的。类似的 “a ”、“ text” 也分别可以合并成叶子节点,所以我们可以将文本节点简化表示为:

综上, 简化后支持行内样式的文本节点也是一个树状结构,它包含一个或多个叶子节点,每个叶子节点包含文本的内容和这些内容共同的行内样式,且相邻的叶子节点的行内样式必须不完全一致,如下图所示:

2.2 渲染

云笔记新版编辑器依旧采用了浏览器进行排版渲染,没有像 Google Docs 那样自研排版引擎,原因是我们认为浏览器的排版引擎已经足够强大,基本满足日常的文本编辑需求,只有诸如图片的文字环绕、分页、分栏等比较高级的功能无法实现,而自研排版引擎需要大量的开发和测试工作量,还可能会产生性能问题,所以我们暂时还是用来浏览器进行排版和渲染。

我们采用了 React 框架,采用了组件化的方式渲染数据模型。针对文档-段落-文本的三层数据模型,以及文本节点的叶子模型,我们设计了对应 React 组件嵌套进行渲染,如下图所示:

  • Document 组件渲染文档数据。
  • Paragraph 组件渲染段落数据,它是 Document 组件的子组件。
  • Text 组件渲染文本数据,它是 Paragraph 组件的子组件。
  • Leaf 组件渲染叶子节点,它是 Text 组件的子组件。

对于叶子节点包含的文本片段和行内样式,只需要渲染成一个带 style 属性的<span>标签就可以了,这也印证了我们设计的富文本模型,简化了富文本的渲染逻辑,使得富文本渲染代码变得非常的轻量化。

以上是本期文章的全部内容。
下周三,我们将推送《有道云笔记新版编辑器架构设计(下)》,继续分享关于编辑指令的内容,并进一步讲解新编辑器的分层架构。
敬请关注有道技术团队。

- END -

查看原文

赞 32 收藏 14 评论 8

CrazyCodes 赞了文章 · 1月20日

[讨论]青春饭的程序员都在害怕 35 岁,那你之前在干什么?

社会一般人对程序员有几个固有认知,而且大部分程序员看上去也是这般:

[x] 工作累加班多,身体差,熬夜容易死;

[x] 不修边幅甚至有点邋遢,榆木脑袋;

[x] 中年危机严重,35 岁将被公司淘汰;

[x] 单身,找女朋友是一件困难的事;

为什么这么说呢,但凡我们细心留意一下,就会发现网络上关于中年危机35岁被辞退单身程序员相关的关键字几乎都是连在一起的。我们来看几个具体的例子:

•程序员到了 35 岁就要被裁员?•35 岁的码农年后第一天被辞退;•程序员的中年危机:拿什么拯救你,我的三十五岁;•程序员工作职能做到 35 岁吗?之后的路是怎么走的呢?•程序员单身真的是有理由的吗?•程序员年薪高到 40 万,为什么还有很多程序员单身?

似乎人人都在担心 35 岁,甚至是 30 岁这个而立年龄。30 岁本因该当打之年,为什么就要被优化掉呢。说到这里,你可能会说这是社会整体趋势,是大环境造就的,谁都不想。

不错,目前社会大环境确实如此,连正在写文章的 30 岁的我,也努力地做打工人。不过对于 35 岁的中年危机,我自己是不担心的,这么说并不是因为我心态好或者不寻求在大城市生根发芽,而是因为我提前做了一些预备

一般程序员的工作和生活

在开始聊预备话题之前,我们先来捋一捋一般程序员的工作和生活,就拿我自己和身边的人作为引子吧。

时间分配

我自己在北京互联网类的公司做研发工作,认识的大部分人都是程序员,有职级比较高的、也有比较普通的。从以往在技术群里面的交流来看,大部分人的生活都分为:工作休闲生活放松玩乐。休闲生活和放松玩乐指的是人类正常生活,例如做饭、陪伴家庭、逛街购物、看电影玩游戏之类,偶尔会有一些学习的时间。相信现在正在看文章的你也是这样。当然,部分人会有一些爱好,例如跑步、骑车、打篮球,这些就划到放松玩乐里面。

不过并不是所有人都按照这个时间比例进行分配,也有人熬夜玩乐,第二天照样能够划水上班的。

经济分配

挣钱当然是要用的,家庭开支生活吃穿学习投入玩乐开销,基本上能够涵盖大部分人的经济分配,这里也不排除一些炒股或者购买基金这类理财产品的情况,那么理财投资也算是一种。

收入渠道

今天的重点,其实是围绕着收入展开的,前面的铺垫也是为了好好聊聊这部分内容。相信大多数人的收入渠道都只有一个:工作收入。上面提到了理财投资,这个也是收入渠道里面的一种。作为程序员,偶尔会遇到一些公司愿意支付费用邀请你帮忙处理一些事,这是工作以外的收入,也是收入渠道里面的一种。

看到这儿,想必聪明的你已经知道关注点在哪里了。没错,支出项多,收入项少。如果你的收入抵不上支出,那么就会陷入泥潭,会觉得工作压力逐渐变大,进而影响到你的心理状况,很容易造成焦虑。

如果这时候正好是 30+ 岁,工作体能和加班能力比不上 二十六七的小伙子是很正常的。假设公司业绩不好,你被优化掉了,那么你的收入项里面比重最大的那部分就没有了,只剩下比较少的那部分,甚至什么都不剩下。入不敷出,这就是程序员们都害怕 35 岁的根源。

基本概念,资产与负债

一般人要摆脱 35 岁困顿,首先要明白一个基本概念:资产负债,这是我当初阅读《穷爸爸和富爸爸》中获得的唯一知识点。这里不咬文嚼字,我们朴实无华地聊。资产大体指的就是能够为你持续创造收益或者大幅度减少资金流失的东西,例如房子、你买的理财产品等。负债大体指的是一次性消耗或者持续不断消耗你的资源或资金的东西,例如你为了追求时尚买的 iPhone 最新款 Pro Max。

当然,资产和负债并不是固定或者绝对的,例如汽车。对于一个做生意或者跑业务的人来说,如果汽车能够直接/间接地创造收益,那么汽车就是他的资产。如果跟你我一般,只是为了不挤公交、不坐地铁而用来代步的汽车(实际上上班开车也不太现实,很多年轻人买车就是为了周末去玩或者放假回家),那它这时候就是一个负债,因为你需要为它支付保险、保养费用、油费等等费用。

要注意的是,除了跟钱有关的物品之外,跟时间有关的东西也可以这么划分。例如你非常喜欢玩一款游戏,每天下班必定玩几局,将几个小时都分配给了这个游戏,那么它就是你的负债。

斩断情丝,减少负债

在你弄明白资产和负债后,可以现场梳理一下手头的资产和背负的负债。将家中的物品罗列一个清单,在清单上给物品打上资产或者负债的分类标记,然后计算它们对应的金额。

考虑一下,要不要把那些负债跟你的情丝斩断。

围水造田,开源节流

上面的斩断情丝,其实就是在节流。除了负债之外,思考一下有哪些是不必要/可以优化的花销,把这些花销降低或者消除。例如你固定每周都会上一次饭店,是不是可以调整为两周去一次?

下面聊聊开源,即开辟更多的收入项。相信看完上面文章的时候你自己心里已经算过了,自己的收入渠道也就那两三项,如果真的有一天被优化掉,是不是意味着生活堪忧了呢?

程序员这个群体能够围水造田的事其实很多,我们来捋一捋:

[x] 技术付费,比较直接的说法是接外包,也就是凭借技术能力获得别人支付费用;

[x] 经验付费,例如独立咨询或顾问,凭借多年工作经验为企业或单位提供有价值的指导或服务;

[x] 跨界合作,例如餐饮连锁软件、营销工具,与其他行业的公司合作,对他们的同行进行降维打击;

[x] 知识付费,在线教育这么火热,无论是视频专栏、图文专栏还是训练营,都是很不错的选择;

[x] 产品付费,根据自己对多年工作所在领域的理解,提供一整套解决方案或者可用的产品;

[x] 作品付费,书籍的分红虽然不高,但是销量可观的话,收入不菲;

[x] 自媒体,短视频爆发的年代,很多人都通过自媒体实现了自己的财富自由,你何不尝试呢?

上面这些都是比较容易接触到,且可落地实施的事。虽然你还没有开始,也不知道从哪里开始,不过通过这篇文章总算是有一个清晰的认知了,可以提前往这些方向上靠拢。例如提前聚集人气、熟悉市场等,为自己后续的动作打下基础。

从现在开始做,慢慢增加自己的收入项,这样就算那天真的被优化掉,也能够不慌不忙。如果围水造田做得好,说不定早早就能够退休了呢。我自己的计划已经确定了,35 岁之前能够自己做主退休,不知道你有何计划?

这里是微信公众号 Python 编程参考,如果你觉得这篇文章对你有帮助,请来关注我哦。点击前往作者专栏 https://www.weishidong.com,看更多有用知识。

说明:部分图片截取自网络,如有侵权请联系 vansenb@foxmail.com[1]

References

[1] vansenb@foxmail.com: mailto:vansenb@foxmail.com

查看原文

赞 1 收藏 0 评论 0

CrazyCodes 赞了文章 · 1月19日

漫画 | 遇上个傻屌领导是什么体验??

今天这篇漫画的灵感来源于知乎的万赞回答,也有一部分原因是因为最近工作状态的真实写照吧, 它讲述的是程序员工作中的bug是如何产生的,以及作为一名优秀的程序员,如何把握好你跑路的时间呢?

一起来看看发生了什么吧……

情节纯属虚构,如有雷同,纯属巧合..

1
2
3456789101112

编后

俗话说:千里之堤,溃于蚁穴,作为一名程序员,或多或少都有类似的经历吧,一个小小的bug,即能把整个系统搞挂。

而bug的产生,大多源于遇上不懂技术的领导或老板,到处跟业务部门、客户等吹牛逼,保证项目开发能有多快、多好、多稳,但当真正需求出来后,经过开发人员评估,时间远远超出他吹牛皮的范围,然后就开启了压迫模式,逼程序员加班加点,缩短项目周期来补全他的面子,导致项目基础构架不到位,测试用例范围不够广,就像漫画中的情节一样,地基不牢,摇摇晃晃勉强上线了,后期无法维护,改一个地方有十个地方会受到影响,用户的行为超出测试用例范围外,系统立刻崩溃~,然后老板知道后,颜面尽失,反过来又指责程序员.

到最后程序员无法忍受,只好跳槽跑路,新来的接盘侠对项目完全不了解,在迷宫一样的系统里垂死挣扎,改好一个bug引发了另外100个问题,如此反复循环……,而老板/领导完全自我良好,认识不到自己的问题…

程序员真的是太难了…

参考资料

  • Bug是如何产生的
  • www.zhihu.com/question/365343579/answer/967299388

文中部分素材来源网络,如有侵权,请联系删除

话说王大拿跑路之后,
去了哪里?
发生了什么意想不到的事呢?
敬请期待下一篇哦~...

本文系 “ 前端布道师 ” 原创
转载请先微信联系苏南 (su-south)授权,并标明出处!

撰稿:苏南、哒柏
插画 / 排版:苏南

往期回顾

image
image
小公司卧薪尝胆三年,意外拿到美团offer
人到中年,一地鸡毛
前端发展史的江湖恩怨情仇
揭密微信是如何诞生的?

前端布道师-苏南著作

更多精彩,欢迎关注我们

本文首发于公众号:前端布道师

链接:https://mp.weixin.qq.com/s/um1mYo3k0i8OAtdnTr_a7w

转载请联系微信:su-south 授权

用漫画解读前端技术,执笔演绎程序人生,愿吾手中笔,能博君(卿)一笑
Github地址:更多有趣漫画https://github.com/meibin08/comics-program-life欢迎 Star、watch

查看原文

赞 12 收藏 3 评论 5

CrazyCodes 关注了用户 · 1月13日

沈唁 @sy_records

欢迎关注我的个人公众号:沈唁志

关注 102

CrazyCodes 发布了文章 · 1月1日

2021 PHP程序员修炼秘籍

image

前言

嗨喽,大家好,我是CrazyCodes,祝思否的小伙伴新年快乐,依照惯例,在2021年的第一天,为大家献上2021年PHP程序员修炼秘籍

2020

回顾2020年,新冠病毒肆虐全球,不易的一年,就这样一晃而过,还记得2020年的春节,全国洋溢着春节的气息,当疫情爆发的那一刻,举国上下齐心协力,抵御疫情,致敬奋战在一线的白衣天使、抗疫工作者!

WechatIMG61.png

语言

在编程语言社区 TIOBE 发布的 12 月份的编程语言排行榜。根据表中显示,12 月 C 语言排名第一,Java 降至第二位,Python 排列第三,与去年同期相同。C++、C# 分别位列第四、第五
image.png
我们PHP开发语言在第八位,我说这些想表达什么呢?是想要告诉广大的PHP开发工程师,不要被社会焦虑所控制,这一年来在知乎、简书、CSDN等等各渠道看到的很多文章,标题大概是这样的《PHP还能活多久》《PHP为什么会成为冷门》等等一系列的描述,前两年文章中我会列出一些数据来说明PHP还在不断发展或者说明PHP的市场占有率,这次不在做相关的阐述,以下说几点

  • 语言只是工具,当你做一个WEB应用你会选择PHP或JAVA,如果你需要处理大量数据,我会推荐你使用Python
  • 存在即合理,每门语言都有它的历史发展,为推动社会所作出的卓越贡献。
  • 不同语言所涉及的领域不同,如果你边用PHP搬砖,边看着人家开发桌面应用,恐怕你只有看着的份。
  • 根据不同需求选择不同语言,不必再纠结哪个会活哪个会死
  • 社区的不断发展壮大是开发语言发展的助推器

如果你真的喜欢PHP这门开发语言,那就去PHP相关社区多做贡献,以体现你真的热爱它。

基础

我是一名北邮计算机专业的自考生,在自考的过程中,深有体会计算机基础对一名程序员的重要性,我见过很多程序员吐槽很多公司面试要出一些算法题,数据结构题,要是面大厂,就说大厂苛刻,面中厂小厂,就说人家学大厂。其实我以前也是这么想的,但自从开启了基础知识学习的那一刻,我开始蔑视自己以往的想法。

可以这么说吧,如果你没有看过C,没有学习过数据结构,操作系统,你不会写出好的代码,更别提成为一个牛X的程序员。这个为什么不能,一言两语说不清楚,如果你感觉我在吹牛皮,不妨去学习下,并且带着你的认知来反驳我

奉上我的自考科目,自考科目与统招学习的科目不同,难度会降低不少,你可以先从这里开始

  • 《数据结构导论》
  • 《操作系统概论》
  • 《软件开发工具》
  • 《数据库系统原理》
  • 《C++程序设计》
  • 《计算机网络原理》
  • 《高级语言程序设计(一)》

无论你是做什么语言开发,在什么行业,C语言是你必须精通的一门语言,可能你会说“什么?要精通?”,在你跟我抬杠前,先至少熟悉了在来。

框架

image.png

2020年Laravel框架热度不减,生态圈也在不断壮大,我也是Laravel框架的重度使用者。但今年我不会强烈推荐你去学习它。

这要看你所处的环境与你需要做的应用,如果你身处一家小公司,其要求是快速迭代,那么Laravel框架会是你的首选,当然如果不是一家创业公司,其已经有了稳定的用户及一定量级的数据,那你也没得选,公司用什么你用什么。

不同场景,不同量级,选择不同的框架,这是一名理智的程序员可以做出的选择。

我强烈推荐你去学习使用 Swoole 与 Hyperf , 就算你不知道协程是个啥。

框架是语言更上层的东西,这里就不做过多阐述了。

如果你是一名新人,在研究到底选择怎样的一个框架去开启你的程序之路,那么我的建议是随便选一个就好。

PHP 8

image.png
PHP8带我们进入了一个全新的世界,是PHP历史发展过程中重要的里程碑。

你在实际工作中可能无法使用它,但我强烈强烈强烈建议你在本地安装并尝试使用它。

PHP官方关于PHP8的介绍,PHP官方还从来没有因为新版本单独拿出一个页面展示,可见官方对齐的重视

我之前有写过一篇相关文章,放到了下方。

社区

技术社区与技术讨论群在这里我一并讲下,学习的方式分两种,一种是主动学习,一种的被动学习,主动学习不多说,就是你主动去学习了解一些事物,被动学习这里指的是浏览技术社区和技术讨论群,保持持续学习的热情是必须的,但也要允许自己堕落一会,大脑长时间保持高速运转,人是受不了的。当你堕落的时候,不知道学点什么的时候,不妨溜溜技术社区,找些感兴趣的文章读一读,就当是课外读物了。

技术的深度和广度是同时存在的,深度代表你对某个领域的专业程度,广度则是对整个行业的宏观理解与认识。做一个有深度,有广度,有态度的程序员吧。

技术社区我经常逛这些,不要贪多,有几个就行

  • Segmentfault
  • Csdn
  • 掘金

关于技术讨论群,找一些话题讨论有深度的加入,这里的深度并不仅仅指的技术层。

如果在你的群列表内,经常出现有人问这段代码为什么报错,这个基础东西该怎么搞,或者天天有人让你砍一刀,建议你趁早退出,俗话说得好,物以类聚人以群分

English

英语,众所周知,是一门沟通语言,在计算机发展长河里,英语占有重要地位,看不懂英文,在技术这个行业里,就像你在中国不识字是一样的。

当然学习英语是一个保持持续热情的事情,如果你坚持不下来,但在日常开发中还躲不掉,这里我教你一个退而求其次的招数,你是不是经常打开github或者某个英文网站,英文文档,右键翻译该网站?我建议你在翻译前,先把要翻译的这段英文copy出来,对比着翻译后的结果,你可以大体知道哪个英文对应的中文是什么,久而久之,你看得懂的英文就会不断增加。

当然,这是一个最最差劲的办法,如果你还是想系统的学习,我这里还有个狠招,“消费你的人民币”,人都是有惰性的,这是人的天性,如果不用上班就可以有无穷无尽的财富,我想上班的人不会太多,报一个英语学习班,我指的是现场授课的那种。并不是某个英语学习APP,花个99元就想通读各大英文书籍文章。

当你花了钱,并且坐在教室里,会有一种莫名的驱动力驱动你这样去做。不妨试试?

创业

image.png

当你工作很多年后,肯定会多多少少有一些创业的想法,作为一名失败者,我劝你三思而后行,但也鼓励你激进做事。

拉卡拉董事长孙陶然先生对于创业者有这样一句忠告 “创业就是,做一个有人用的产品,并且把这个产品卖出去”

这是作为一名创业者首先要做的事情,而非所谓为了梦想。这也是一个很现实的事情,不是每个人都能做到像滴滴、抖音这些明星企业一样。

创业成功本身就是一件小概率事件,当你迈上这条路,首先你的技术就不是你的核心竞争力了。

每每写到创业,我都想多描述些,但碍于我也是一名失败者,作为一名失败者去教你如何创业,恐怕你失败的概率比我还高,就跟那些天天演讲成功学的“专家”,如果他成功了,恐怕不会去演讲这个东西。一个失败人去讲成功学,会把成功学讲的更失败。

如果你希望了解我的创业故事,可以私信我。

我很乐意把我的经历描述跟你听。

最后致敬所有的创业者,这个时代的逆行者。

致谢

如果你没细看我上面啰嗦的那些,那你可以看看这里的总结

  • 不要被贩卖焦虑
  • 保持持续的学习热情
  • 允许自己一时的堕落
  • 拥有扎实的基础
  • 要熟练使用C语言
  • 英语不能忽视
  • 创业要做足准备

感谢你看到这里,2021 我会在思否发布自己电商设计的录播课,也是我首个录播课。

希望本篇文章可以帮助到你,谢谢。

查看原文

赞 20 收藏 8 评论 6

CrazyCodes 赞了文章 · 1月1日

思否年度有奖征文丨你的 2020 留下了哪些印记?

中奖名单来咯~

2020年思否有奖征文获奖名单公布,快来领奖!
戳链接领取奖:https://segmentfault.com/a/11...

image.png

2020年已经结束,这一年过的太快,让人产生了一种虚幻而错乱的穿越感,仿佛上一秒还在年初,下一秒就临近年尾。

但实际上这一年我们经历了太多,其中有恐慌、焦虑、辛酸和艰难,当然也有收获、快乐与感动。

回顾 2020 年,你最大的感受是什么?

作为一名开发者,你在 2020 又留下了哪些印记?

年底了,让我们一起回顾 2020,展望 2021,参加 SegmentFault 思否的年度有奖征文活动,来对自己的 2020年 做个总结吧~

如何参与?

一、活动规则

在思否社区写下你的「2020总结文章」,添加「2020总结」标签,并在文章注明超链接:

本文参与了 SegmentFault 思否征文「2020 总结」,欢迎正在阅读的你也加入。
二、文章内容

写下属于你的 2020 年 故事:标题不限,文体不限,语言不限,字数不限,自由发挥。

三、活动时间

自本篇文章发布始到 2021 年 1 月 31 日

有什么奖励 ?

image

实物奖品大合影

感谢融云RongCloud、青云QingCloud、京东智联云开发者社区、AWS、ARM中国 等对本次活动的赞助

编程课程8折劵(会在现有的折扣价上再打 8 折):

视频课程
图文课程

如何获奖?

当然,这些丰富礼品,要拿下以下这四个大奖才能得到~

  • 2020 人气顶流奖

    评选规则:社区文章互动数多的(基于点赞、评论等)
    评选人数:【3人】
    💗奖励: 融云鼠标垫 + ARM 蓝牙音箱 + 京东玩偶 + 编程课程 8 折劵 各一份

  • 2020 凡尔赛文学奖

    评选规则:文笔超赞/分享硬核干货内容最多的
    评选人数:【3人】
    💗奖励: 青云定制包 + AWS 玩偶 + 技术书籍 + 编程课程 8 折劵 各一份

  • 2020 搞笑担当奖

    评选规则:内容最有趣的
    评选人数:【3人】
    💗奖励: 京东鼠标垫 + AWS 玩偶 + ARM 蓝牙音箱 + 编程课程 8 折劵 各一份

  • 2020 人间真爱奖

    评选规则:前【50人】(自本篇文章发布至1月31日)参与活动且文章符合要求被收录的征文作者即可获得奖励
    💗 奖励:1-20 可获得 SegmentFault 思否定制马克杯+ 编程课程 8 折劵, 21-50 可获得重启世界限量版手办 + 编程课程 8 折劵


2020 已经结束,2021 欣然赴约;凛冬过后,春天还会远吗?我们努力把 2021 过成梦想的样子...

温馨提示:

发布后在此帖下面留言,小姐姐会更快发现你的文章,获奖机会更大。
② 思否小姐姐会把优质文章汇总收录并发出,参与征文活动的优质文章内容会被推荐到社区首页技术交流群,分享给更多的开发者。
③ 除了以上的「四大奖」以外,还有神秘礼品,大家要积极参与哦~


扫码下方二维码,添加 SF 思否小姐姐 了解更多活动信息

思否小姐姐微信号

最终解释权归 SegmentFault 思否所有

参选文章汇总

  1. axuebin:一个小小前端的 2020 年流水账
  2. 欧雷属于我的三年·第一年
  3. Kevinwan一个20年技术老兵的 2020 年度技术总结
  4. 小傅哥2020总结 | 作为技术号主的一年!
  5. pingan87872020 总结 | 21 张图总结我的 2020 年
  6. 民工哥我的 2020 年是这样的。。你的呢?
  7. 沈唁今天,告别 2020 年。
  8. masonli“措手不及”的2020 | 2020总结
  9. 敲键盘的猫我的2020,在非常时期的非常努力
  10. 皮小蛋2020 年终总结
  11. Why技术2020,我这一年。
  12. xuexiangjys年终总结 | 在开源、博客和搬砖的路上砥砺前行「2020」
  13. codecraft2020年终总结
  14. 公丕昊再见,2020
  15. robin2020,再见
  16. 死月死月的二零二零总结
  17. linong2020总结😊我想混个奖品
  18. YourbatmanYourBatman 2020年感悟关键词:科比、裁员、管理层、活着
  19. 边城边城客栈的 2020
  20. Fw恶龙2020 年终盘点
  21. CrazyCodes2021 PHP程序员修炼秘籍
  22. 波波Nadia我的 2020 个人成长回顾:花足够长的时间,有智慧地做“无用功”
  23. 折腾不止的追梦人我的2020,啪啪打脸
  24. Kense2020 年终总结
  25. 一个优秀的废人来年,做个俗人!
  26. formulahendry我的 2020:出书、办签售会、发展 VS Code 中文社区、成为开源先锋、全网 10 万粉丝、10 场演讲、内推 21 人
  27. 程序员哆啦A梦2020回顾-个人web分享JavaScript面试题附加回答
  28. Shenfq:普普通通打工人的2020
  29. HLQ_Struggle静心,未来可期 | 告别 2020
  30. 蒋鹏飞工作都是公司的,技术才是自己的!| 底层技术人的2020年度总结
  31. Raymond懒人的2020
  32. xindooXINDOO的2020总结
  33. tina6662020 疫情下湖北人的生活(只谈生活)
  34. 张喜硕度光阴的人
  35. JerryWang_汪子熙一个SAP成都研究院开发工程师的2020年度总结:未知生,焉知死
  36. 夜尽天明前端工程师的 2020 年终总结 - 成长不及预期的 3 年之痒
  37. 张晋涛2020 小回顾 | 新晋程序员奶爸的云原生之路
  38. JamesGoodbye 2020 wassup 2021 | 2020 总结
  39. howie梦想开始的地方
  40. 运维汪回顾2020,展望202,确定三个关键字:自我成长、打地基、过男人关
  41. zangecizangeci的2020年度报告
  42. Meathill2020 年知识分享与学习总结
  43. 寒雁2021,一个长期主义者的年度计划
  44. 程序员cxuan:坚持并活下去!cxuan 在思否的 2020 年终总结。
  45. 芋头芒果小龙虾再见2020,再见口罩,再见所有的不愉快
  46. thinkwei全面建设小康社会的2020年
  47. Java中文社群大龄程序员10年编程生涯的转型之旅,2020年新的开始
  48. kumfo写在2020年结尾——2020总结
  49. qqxx6661阔别2020 | 我的年度总结
  50. 沉默王二2020总结文章|乘风破浪的一年
  51. joking_zhang2020 总结 | 张兜兜 - 神奇的一年
  52. Chor Chor 的 2020 年度总结 | 2020,再见;2021,你好
  53. 0xBoo二零二零年终总结
  54. 风中有php做的云洪光光的2020年
  55. 陆陆通通「2020总结」一个老程序员的 2020 年总结回顾,2021 年如何变的更牛逼
  56. By无邪2020年度总结
  57. 三掌柜2020年年度关于前端学习与工作的心得体会
  58. TianSong2020感谢每一天的陪伴
  59. Gopal:Gopal 的 2020 年度总结
  60. Mondo平凡的 2020 年
  61. 我是开发者FTD2020,再见;2021,我来了!
  62. 敖丙一年40W粉,小小程序员的2020年终总结
  63. 木易杨:我的 2020 总结,我在蚂蚁成长的这一年
  64. 蔚蓝2020 一个少年大厂梦的陨落与wlui的诞生
  65. 良许万万没想到,良许也开挂了
  66. mar112020年,得与失,汗水和泪水
  67. 依然饭特稀西2020年总结!翻过这座山,他们就会听到你的故事!
  68. 卡颂:大佬那么多,为什么不能是我 | 卡颂2020年终总结
  69. 起风了Zzzz24岁, 接触前端的这三年
  70. dreamapplehappy:2020年,我第一次很正式地写年终总结
  71. 书旅:2020年终总结:回顾、反思、期待
  72. phodal2020 结点:平凡 & 重新出发
  73. BWH_Steven二十一岁的九局下半,希望能将结局逆转 | 年度总结
  74. 暖暖日记🍂文艺逗比的2020
  75. 画星星高手我的2020回顾——技术篇
  76. Lpyexplore 迟来的2020年度总结,顺带附上被鸽了很久的自我介绍「2020总结文章」
  77. 澹台宇鹏平凡而精彩的2020,期待的2021
  78. 提莫找蘑菇我去前面探路了,2020!

划重点

不要忘记在你的文章下添加「2020总结」标签,并在文章末尾处注明超链接 ——

本文参与了 SegmentFault 思否征文「2020 总结」,欢迎正在阅读的你也加入。
查看原文

赞 29 收藏 3 评论 69

CrazyCodes 发布了文章 · 2020-12-19

API调试工具大汇总

image.png

前言

大家好,我是CrazyCodes,今天总结下业界常见的API调试工具,众所周知,API调试工具即简单化的完成API调试流程,将Header、Cookie等其他频繁使用的参数进行复用,对不计其数的接口进行分类,方便查询管理。

Postman

image.png

Postman是一款强大网页调试工具的客户端,你能想到的,Postman几乎都提供了。Postman兼容几乎所有的操作系统,并且提供了WEB版本,可以让你随时随地加班,不错吧。言归正传,先看下postman的控制面板
image.png
面板分为三部分

顶部

image.png

  • 创建面板
  • 批量导入接口,我们可以将同事的接口进行批量导入
  • 接口批量测试
  • 切换工作空间,与git概念相似,不同的工作区(分支),进行不同的工作
  • 同步、登录等等其他操作,postman有自己的云存储,注册账号可以将本机的调试接口同步到云上,这样就可以随时随地加班喽,不过免费用户是有存储上限的,具体请查看官网

左侧

image.png

  • 历史请求,会记录所有的请求调试
  • 接口集合目录,将接口合理分类,是提升工作效率的最佳实践

右侧

image.png

调试内容区,我们可以在这里进行任何方式的接口调试,请求方式支持rest风格,put,get,patch,post等等,并且可以设置认证方式,设置自定义的header头,设置前置脚本,后置脚本,并且设定了环境变量的功能,我们可以根据自己的开发、测试、生产地址,配置不同的链接,实现地址复用

postman应该是开发人员众所周知的最佳api调试工具了,postman不仅仅是一个调试工作,也配备了项目管理,协作办公等功能,不过好像是收费的,具体可以在官网查询,官网地址我贴在下面了

https://www.postman.com/

Swagger

image.png

Swagger是一款强大的api文档工具,其生成的文档具备调试功能,它可以通过读取注释的方式,自动生成对应的api文档,并且支持将文档导出成其他格式,简直不要太好,官网还特意开发了一项展示文档的ui页面,开源课随意修改的,其细节比较多,是基于编码层次的,就不细细讲解了。感兴趣的小伙伴可以点击下方链接

具体的玩法请见github https://github.com/swagger-api

PhpStorm Http Client

image.png

我是一名php程序员,所以就以phpstorm为例来讲解下,当然jetbrains内所有的产品都具备此项功能,PhpStorm 为接口调试提供了专属的工具,我们在菜单Tools->Http Client 可以找到它, jetbrains 提供的此项接口调试功能是专门为程序员准备的,没有任何界面,接口调试什么的全靠码,不过我们可以以git最简单的方式去维护接口文档。

httpclient 通过创建demo.http文件来进行调试的,http是文件后缀
image.png
上图是官方给到的demo,可以通过点击Run All Requests In File 对接口进行访问,也可以点击每个接口地址前方的箭头进行单个接口调试,整体来说还是比较方便的,接口的解释与正常代码注释一样

http client还支持将curl请求直接导入到http文件中
image.png

当然依旧支持环境变量,支持方式是由一个单独的json配置文件控制
image.png
点击后会自动在根目录创建配置文件

{
 "dev": {
 "url": "http://dev.baidu.com"
 },
 "pro": {
 "url": "http://pro.baidu.com"
 }
}

我们可以在http文件内通过{{url}}调用该变量,dev和pro分别代表不同的环境,我们可以在此处选择不同的环境
image.png

总体来说,简单简约,可扩展性强,感兴趣的小伙伴可以试试

ApiPost

image.png

apipost是国人开发的一款api调试工具,功能与postman类型,但是全部中文,其使用文档也非常详细
https://doc.apipost.cn/

冲着这句话,也得支持下,强烈推荐!
image.png
如果你英文不太好,使用apipost你会get到很多postman的丰富功能
image.png
其没有web版本,但有linux,mac,windows客户端,做纯工具的公司不多了,让我们守住他们。

Apizza

image.png

功能与apipost、postman相似,同一类产品,但只有web端,感兴趣的小伙伴可以点击下方链接进入

https://www.apizza.net/

看云文档

看云是thinkphp官网的文档管理工具,该文档内可以创建api调试
image.png

感兴趣的可以点击下方链接了解
https://www.kancloud.cn/

Teambition

一款协作项目管理工具,阿里巴巴搞的,该工作内文档功能可以创建api文档,可进行请求调试,感兴趣的可以点击下方链接

http://teambition.com/

致谢

工具永远只是工具,排序无好坏之分,我们按需使用。

感谢你看到这里,谢谢。

查看原文

赞 12 收藏 6 评论 2

CrazyCodes 回答了问题 · 2020-12-08

昆 明 办 假 护 照/签 证

耗子尾汁........

关注 4 回答 3