0%

问题

在 Flutter 中进行页面跳转需要访问到当前的 BuildContext 及目标页面的 Widget,不利于代码的组织管理。

onPressed: () {
  Navigator.push(
    context,
    MaterialPageRoute(builder: (context) => SecondRoute()),
  );
}

服务定位器(Service Locator)

The service locator pattern is a design pattern used in software development to encapsulate the processes involved in obtaining a service with a strong abstraction layer. This pattern uses a central registry known as the “service locator”, which on request returns the information necessary to perform a certain task. Proponents of the pattern say the approach simplifies component-based applications where all dependencies are cleanly listed at the beginning of the whole application design, consequently making traditional dependency injection a more complex way of connecting objects. Critics of the pattern argue that it is an anti-pattern which obscures dependencies and makes software harder to test.

Wikipedia - Service locator pattern

简单来说服务定位器模式可以将获得服务的过程封装在服务定位器中,应用程序的代码不需要管理维护服务的大量依赖,使应用程序的结构得到良好的分离。

阅读全文 »

拦截器

示例:axios

实现

// https://github.com/axios/axios/blob/master/lib/core/InterceptorManager.js
function InterceptorManager() {
  this.handlers = [];
}

InterceptorManager.prototype.use = function use(fulfilled, rejected) {
  this.handlers.push({
    fulfilled: fulfilled,
    rejected: rejected,
  });
  return this.handlers.length - 1;
};

InterceptorManager.prototype.eject = function eject(id) {
  if (this.handlers[id]) {
    this.handlers[id] = null;
  }
};

InterceptorManager.prototype.forEach = function forEach(fn) {
  utils.forEach(this.handlers, function forEachHandler(h) {
    if (h !== null) {
      fn(h);
    }
  });
};

// https://github.com/axios/axios/blob/master/lib/core/Axios.js
function Axios(instanceConfig) {
  this.interceptors = {
    request: new InterceptorManager(),
    response: new InterceptorManager(),
  };
}

Axios.prototype.request = function request(config) {
  // Hook up interceptors middleware
  var chain = [dispatchRequest, undefined];
  var promise = Promise.resolve(config);

  this.interceptors.request.forEach(function unshiftRequestInterceptors(
    interceptor
  ) {
    chain.unshift(interceptor.fulfilled, interceptor.rejected);
  });

  this.interceptors.response.forEach(function pushResponseInterceptors(
    interceptor
  ) {
    chain.push(interceptor.fulfilled, interceptor.rejected);
  });

  while (chain.length) {
    promise = promise.then(chain.shift(), chain.shift());
  }

  return promise;
};

拦截器的注册:在 InterceptorManager 实例中使用一个数组储存拦截器函数,use 拦截器时会将 handler 插入数组,返回插入的位置,以备 eject 时使用。

拦截器的调用:Axios 的实例会为 requestresponse 生成两个独立的 InterceptorManager。发起请求时,会生成一个 chain 的队列,将发起请求的 handler 放入其中,将 request 的拦截器插入队列前,将 response 的拦截器插入队列后,通过 Promise 链式调用的方式使这些 handler 可以依次被执行。

阅读全文 »

背景

在之前的介绍小程序性能优化的文章中曾提到一种使用 rich-text 来替代 web-view 展示 Web 页面的方法,由于实际操作中,源 DOM 树可能会非常复杂,导致生成的 node 树很庞大,不易拆分,直接执行 setData 会造成页面卡死,所以需要有能够分段拆分树型结构的方案。这种方案应该满足我们的两点需求:

  1. 能够被分批读取生成 node 树
  2. 每段大小可以控制

简单设计

由需求 1,拆分的结果需要可以被分批读取,我们可以做如下设计:

interface Node {
  children?: Node[];
}

interface Fragment {
  path: number[];
  nodes: Node[];
}

const splitTree = (tree: Node): Fragment[] => {};

const renderFragments = (frags: Fragment[]): void => {};

树的拆分

To be continued…

定义

同层渲染:通过一定技术把原生组件直接渲染在 WebView 层级中,原生组件直接挂载在 WebView 节点。

解决什么问题

  • 原生组件层级问题

微信小程序

iOS

原理

iOS 中,当在一个 DOM 节点设置 CSS 属性:

overflow: scroll;
-webkit-overflow-scrolling: touch;

时,WKWebView 会为其生成一个 WKChildScrollView,与 DOM 节点存在映射关系,这是一个 UIScrollView 的子类,WebView 里的滚动是由原生的滚动组件来承载的。

阅读全文 »

有如下可以正常运行的代码,功能很简单,加载 React, ReactDOM,然后在 app 中渲染 React 元素:

<div id="app"></div>
<script
  src="https://cdnjs.cloudflare.com/ajax/libs/systemjs/6.7.1/system.js"></script>
<script>
  (async () => {
    const React = await System.import(
      "//cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.development.js"
    );
    const ReactDOM = await System.import(
      "https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.development.min.js"
    );

    const { createElement: h } = React;
    const container = document.getElementById("app");

    ReactDOM.render(h("p", {}, "Hello world."), container);
  })();
</script>

一切看上去很正常,ちょっと待って,引入的两个模块都是 UMD 的,为什么 SystemJS 可以正常注册模块呢?

阅读全文 »

背景

用 Markdown 写文档时,经常遇到插入表格的情况(如旅行规划),但是直接使用 Markdown 的语法来写表格并不方便,且 Markdown 的表格不支持单元格合并。这时候可以先在 Excle 等工具中写表格,再转换成 HTML 的 Table 插入 Markdown 文档。一番搜索发现现有的表格转 Table 工具有很多,但支持单元格合并的并没有,需要自己开发一个。

依赖

  • React/ReactDOM
  • Ant Design
  • sheetjs(xlsx)

产出

spreadsheet2table

最近计划学习钢琴,有网友建议去看 Andrew Furmanczyk 的 How to play piano 系列视频进行学习,由于原视频是在 YouTube 上且字幕是由 YouTube 自动生成的,为了方便观看,需要下载下来。实际操作中,发现想要达到预期的效果并不容易,有很多细节需要注意,故作此文记录。

  • 下载工具
    youtube-dl
  • 代理配置
    --proxy
  • 下载自动生成的字幕文件
    --write-auto-sub
  • 字幕文件格式转换
    下载下来的字幕文件格式为 vtt,为了便于播放器读取,需转换为 srt 格式的文件 ffmpeg -i input.vtt output.srt
  • WARNING: Requested formats are incompatible for merge and will be merged into mkv.
    默认情况下,youtube-dl 将下载最佳画质和音质的流,然后将这些流打包到兼容的容器中。如果最佳的流不能存在 mp4 容器中时,youtube-dl 则会将它们打包到 mkv 容器中,这时就会看到这条警告。如果一定要将这些流打包到 mp4 容器中,则需要手动指定格式 -f 'bestvideo[ext=mp4]+bestaudio[ext=m4a]'
  • 以上这些配置可以直接写入 ~/.config/youtube-dl/config
    --no-playlist --write-auto-sub --proxy "socks5://localhost:80" -f 'bestvideo[ext=mp4]+bestaudio[ext=m4a]' --user-agent 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.111 Safari/537.36'
  • 批量转换 vtt 文件
    从 playlist 下载下来的会是多个 vtt 文件,可以使用 shell 脚本批量转换:
    #!/bin/bash
    for f in ./how_to_play_piano/*.vtt;
    do
        ffmpeg -i "$f" "${f%.vtt}".srt;
    done

计划以后每月都把当月的一些思考整理记录下来,期望锻炼自己的思维能力,同时能够深入的思考一些问题。(由于是第一次,便把以前的一些想法也记录下来)

做前端的三个阶段:前端框架各有各的不同;前端框架没有什么不同;不要考虑前端框架

  • 前端框架各自的实现不同
    • 数据变化检测
      • React setState
      • Angular 脏检查
      • Vue 属性访问器(Object.defineProperty/Proxy)
    • 视图层写法(DSL)
      • Vue 模板
      • React JSX
    • 由视图层写法的差异,导致 UI 表达能力的不同
    • 由 DSL 的不同,Vue 在编译时有更多的优化空间
  • 前端框架都是处理 State -> UI 的工具
  • 多关注价值(业务价值/技术价值)

做前端重构最好的时间是 React 16.8 发布时,其次是现在

从 React 16.8 开始,React Hook 稳定版本正式发布。

很多事情其实就是拍脑袋决定的,区别是拍谁的脑袋

这种情况是不是有做 A/B test 的空间呢?

偏见的形成和距离有关/了解的越多越可能产生偏见

主要交通

日期 时间 路线 交通方式 内容 金额 平台
2020/10/13 08:03-10:17 杭州 - 厦门 火车 G1651 杭州东站 - 武夷山北站 193.50 飞猪(12306)
2020/10/15 12:40-15:55 武夷山 - 厦门 火车 G1601 武夷山北站 - 厦门北站 209.00 飞猪(12306)
2020/10/18 14:40-16:15 厦门 - 杭州 飞机 山东航空 SC8891 厦门高崎 - 杭州萧山 487.00(437.00 + 50.00) 马蜂窝

住宿

时间 城市 酒店 房型 金额 地点 平台
2020/10/13-2020/10/15 福建武夷山 武夷山旧街五号云起时客栈 笙歌静思大床房(大窗) 478.00 三姑度假区幔亭峰路7号 携程
2020/10/15-2020/10/18 福建厦门 全季厦门中山路酒店 双床房 717.00 思明南路291号(近镇海路地铁口2号出口) 华住
阅读全文 »

上周去武夷山+厦门游玩了 6 日,日后会放出游记,本文仅记述在支付、门票、机票等角度的见闻。

支付

本次行程中有很多场景只能使用微信支付,如武夷山的公交、船夫的讲解、小店购物等;其他微信、支付宝都支持的场景,也有存在支付宝收款码不明显的情况。

我们平时都用微信,支付宝是大城市人用的。

某船夫
阅读全文 »