不会化妆的写手
不是好程序员


  • Home

  • Categories

  • Archives

  • Tags

  • About

一个比较有趣的需求

Posted on 2019-10-18 | In 框架/库/工具 , React.js

因为比较少见,是难得比较有趣的需求,记录一下。

首先介绍一下需求。


大概就是要实现如图效果,需求点拆分大概如下:

  1. 给出一个句子,使用户可以在句子上划动,选中句子中的一部分。可以取消选中。
  2. 将选中部分的位置(下称 index)提交给后端。不能单单提交选中的内容,因为同一句中可能会出现重复的单词。
  3. 给出 index,可以将对应 index 转化为横线,划在句子上。句子中的下划线还可以再次叠加下划线,叠加的线条数目也是无上限的。

第2、3条合并起来,其实就是 UI 视图到 index,与 index 到视图的相互转化。

Read more »

全是代码——React 事件机制

Posted on 2019-06-21 | In 框架/库/工具 , React.js

起因其实是我用了 React Swiper 库。该库虽然挂着 React 的名头,但其实完全是由 Swiper.js 包装而成的,因而内里的事件机制仍旧是基于原生的。这导致我发现,我无法正常地将事件从 React 组件中阻止冒泡到 Swiper 的部分。

为了解决这个问题,很快,我从文档中查阅到,React 中的事件是由自己合成的“合成事件”,事件上的 e.nativeEvent 才是真正的原生事件。于是,我就尝试在事件处理函数中执行 e.nativeEvent.stopPropagation(),却仍旧无法阻止冒泡。

要搞清楚出现这个问题的原因,就需要了解 React 的事件机制。

React 的事件机制是基于事件委托的。所有 React 的事件监听实际都是被绑定在 document 上的。

例子

本文其实就是对 React 事件机制部分的代码按照执行顺序解读。使用的例子代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// App.js

import React from 'react'
import Child from './Child'
import './App.css'

class App extends React.Component {

clickHandler = (e) => {
console.log('click callback', e)
}

render() {
return (
// 使用 <article> 是为了调试时更方便地一眼看出是什么标签
// 其实该标签在语义化上不应被这样使用
<article onClick={this.clickHandler}>
<Child />
</article>
)
}
}

export default App
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// Child.js

import React from 'react'

class Child extends React.Component {
childClickHandler = (e) => {
console.log('child click callback', e)
}

render() {
return (
<div
className="box"
onClick={this.childClickHandler}
>请点击</div>
)
}
}

export default Child
Read more »

浏览器渲染的详细过程

Posted on 2019-03-29 | In 前端基础

这是一篇转载文章。

原作者博客已失效,留了一篇在掘金。很担心掘金的也会消失掉,赶快敲一遍,侵删。

主要还是我比较笨拙,看内容很好的文章还真是很喜欢一个字一个字敲一遍,基于理解稍微整理层级改变细节。感觉这个方式对我来说真的很利于领会和记忆,能够触及每一个细节。

就是实在太费时间了……这篇文章长达七千多字,边理解边抄弄了有十个小时……

另外,我真的非常喜欢这位作者。我和他很像,都是钻牛角尖的人,追求对细节的了解,认为流程中缺失了不确定的部分就不是真正理解。不同的是,这位是一名十分优秀的程序员,能够从规范级别的文档中找到自己需要的答案。而我往往周旋于良莠不齐的国内博客,看着大家东抄西抄的东西,不仅找不到答案,还频频被误导。

所以说,第一个目标:学好英语。

页面渲染的时机

从 event loop 开始

event loop 是整个渲染过程的关键,涉及浏览器进行渲染的时机。先看 HTML5 关于 event loop 的官方规范:

  • 从第 1 到第 5 步,从多个 task 队列中选出一个 task 队列(浏览器为了区分不同 task 的优先级,所以时常有多个 task 队列),从这个 task 队列中取出最老的一个 task,执行它,然后将它从队列中移除;
  • 第 6 步,执行一个 microtask 检查点(preform a microtask checkpoint)。只要 microtask 队列不为空,这一步会一直从 microtask 队列中取出 microtask,然后执行。如果 microtask 执行过程中又增加了 microtask,仍然会执行新添加的 microtask。(链接第 7 步的“return to the microtask queue handling step”很关键。)
  • 第 7 步,更新渲染(update the rendering)
    • 判断当前 document 是否需要渲染。因为只需要保持 60Hz 的刷新率即可,而每轮 event loop 都是非常快的,所以没必要每轮都进行渲染,而是差不多间隔 16ms 的时候渲染一次。同时,对于一些卡顿得已经不能保证 60Hz 的页面,若再在此时执行渲染会雪上加霜,所以浏览器可能会下调渲染频率为 30Hz。
    • run the resize steps
      • 若浏览器 resize 过,那么这里会在 window 上触发“resize”事件
    • run the scroll steps
      • 首先,每当我们在某个 target 上滚动时(target 可以是某个可滚动元素,也可以是 document),浏览器就会在 target 所属的 document 上的 pending scroll event targets 列表里存放这个发生滚动的 target
      • 现在,run the scroll steps 这一步会从 pending scroll event targets 列表里取出 target,然后在 target 上触发事件
    • 计算是否触发 media query
    • 执行 css animation,触发 animationstart 等 animation 相关事件
    • run the fullscreen rendering steps:如果在之前的 task 或 microtask 中执行过 requestFullscreen() 等 full screen 相关 api,此时会执行全屏操作
    • run the animation frame callbacks:执行 requestAnimationFrame 的回调
    • 执行 IntersectionObserver 的回调
    • 更新,渲染用户界面
  • 继续返回第一步
Read more »

工作记录零碎

Posted on 2019-03-25 | In 前端基础

Charles mock 数据

Charles mock 数据,眼看着 Charles 抓到的包都是正常的,可是浏览器中间就是收不到响应。忽然回忆起,跨域实际上是浏览器本身做的一个限制,为用户的安全,及时收到数据也不返回。考虑到可能是跨域的问题,开了禁止跨域的浏览器,解决。

position: fixed 问题

fixed 的元素不一定相对视口定位。在祖先元素中出现某些特定属性时,会相对于该祖先元素定位。

相关文章:fixed 定位失效 | 不受控制的 position:fixed

写全局组件的方法

如常见的 Modal 弹窗,之前一直都会在组件中设置一个 modalShow 的值,用来在组件内控制 Modal 组件的显示/隐藏。最近忽然意识到,其实可以更加方便。直接 ReactDOM.render Modal 组件,然后将其插入某节点即可。不需要依赖组件内部的某个值来显示隐藏。详见:Modal 组件

Read more »

对 Fiber 的介绍

Posted on 2018-12-02 | In 框架/库/工具 , React.js

可能大多数人都不知道 Fiber 是什么。如果你不知道,不必担心,因为我会从最基本的开始。解释它是什么,以及它是如何工作的。

Fiber 的作用

这是一个 React app 的例子:

React app 的例子

左边是现在的 React 的效果,右边是使用了 Fiber 的 React 的效果。这是一个玩具性质的应用,它本身不是很有用处,但却可以很好地替代现实中的 app。我过会儿会解释原因。

很明显,Fiber 为像上图这样的复杂 React 应用提升了用户感知的性能和响应。

Read more »

你可能不需要 Derived State

Posted on 2018-11-13 | In 框架/库/工具 , React.js

React 16.4 为 getDerivedStateFromProps 修复了一个 bug,这个 bug 会导致一些在 React 组件中存在的 bug 不断重现。如果本次更新暴露了你的应用正在使用反模式(不推荐的做法)且在本次更新后可能出现问题,我们对此感到抱歉。在这篇博文中,我们会解释一些使用 derived state 时常见的反模式和我们更推荐的替代做法。

在很长一段时间里,如果想在 props 改变时更新 state,且不造成多余的渲染,我们只能依靠生命周期 componentWillReceiveProps。在 16.3 版本,我们介绍了一种可以替代 componentWillReceiveProps 的生命周期—— getDerivedStateFromProps ——来更安全地解决同样的问题。与此同时,我们发现大家在使用这两种生命周期时存在很多错误的观念,也发现了一些会造成微妙而令人困扰的 bug 的反模式。16.4 版本中对 getDerivedStateFromProps 的 bugfix 使 derived state 更加可预料,因而误用它所产生的结果也更加容易被注意到。

注意
本文描述的所有反模式都同时适用于旧的 componentWillReceiveProps 和新的 getDerivedStateFromProps。

Read more »

关于我的项目为什么使用又剥离了Redux

Posted on 2018-11-05 | In 框架/库/工具 , React.js

关于我的项目这么简单我为什么又折腾了。

关于我的需求为什么做不完我怎么在加班。

决定引入 Redux 的原因

当初,决定将 Redux 引入项目的原因其实非常简单。项目的嵌套层级太深了。以评论列表的一个分支为例,嵌套层级为 CommentZone(用于引用评论列表组件,即下文的“环境”)> CommentList(评论列表组件)> Comment(单条评论组件)> ReplyList(单条评论中的回复列表)> Reply(单条回复)。在最末端的 Reply 组件中,我需要删除单条回复,而删除单条回复,实际上本质是操作最顶层的评论列表。这意味着,我用于删除评论列表的方法需要从顶层层层向下传递,传递四层。

这让我义无反顾地选择了 Redux。

然而,随着业务的不断更新,愚蠢而稚嫩的我开始意识到了自己的错误。那就是,一个打算要复用的东西,本身就是不应该和 Redux 有所牵连的。

Read more »

React 和 Redux 中的不可变性:全面指引

Posted on 2018-10-16 | In 框架/库/工具 , React.js

不可变性(immutability)可能是个令人困惑的话题,它总会在 React、Redux 和 JavaScript 中到处出现。

你可能碰到过已经更改了 props,但 React 组件不重新渲染的 bug。有人说:“你应该做不可改变状态(immutable state)的更新。”可能你或者你的同事经常写改变状态(mutate state)的 Redux reducers,且你不得不不断纠正这些(reducers,或者你的同事😄)。

纠正这个很棘手。它真的很微妙,特别是如果你不确定为什么要纠正。况且说真的,如果你不确定为什么这很重要,那就很难去在意它。

这份导航会解释“不可变性”是什么,以及如果在你自己的应用中写不可变性的代码。

不可变性是什么

首先,“不可变”是“可变”的反义词——“可变”意味着可以变化,可能会出问题。

所以某个东西是不可变的,就是说,这个东西不能够被改变。

极端的讲,这意味着比起传统地直接改变值,你应该始终创建新的值去取代旧的值。JavaScript 没有这么极端,但是一些语言完全不允许“可变”(Elixir、Erlang、ML 等等)。

尽管 JavaScript 不是纯粹的函数式语言,但它有时候可以假装是。在 JS 中,某些数组操作是不可变的(就是返回一个新数值,而不是修改原本的)。字符串操作总是不可变的(变化会创建一个新字符串)。并且,你也可以自己写不可变的函数。你只需要意识到一些规则。

Read more »

rem布局在webview中页面错乱

Posted on 2018-07-20 | In CSS

查到原因之后,感觉是一个很简单的事,解决方法也很方便很简单,本来没想写什么博客的。可是仔细看了看查到的两篇文章,一篇解决方法写得超级复杂,另一篇就特别……一言难尽。感觉我写这篇博客最主要的原因就是被第二篇文章的方法之差劲给震惊了……

啊,写博客好麻烦好麻烦的……

现象及原因

在安卓webview中发现了利用rem布局的样式错乱的问题,以下是正常样式和错乱样式的对比:

左为正常情况,右为错误情况

可以很容易发现,“错乱样式”实际上是rem的基准,即根结点字号,变大导致的。实际上,利用Chrome://inspect调试,也能够清楚地看到这一点,设置在根结点上的字号为font-size: 48px,而实际调用getComputedStyle(document.documentElement).fontSize的字号却是55.1px,这样,会产生样式的错乱就一点也不奇怪了。

查询了一下,发现这个问题产生的原因实际上是因为用户设置了系统的字体,影响范围似乎只有安卓app内的webview,测试ios同样的操作并没有产生同样的问题。

Read more »

babel-polyfill & babel-runtime

Posted on 2018-03-28 | In 框架/库/工具 , webpack

之前明明知道,不知道为什么又变得不确定……“好记性不如烂笔头”系列。
为了验证一下,又找不到火狐旧版本入口,还专门去了类似“太平洋下载站”的地方下了个旧版的火狐哈哈哈哈哈哈哈有十年多没进过这类网站了吧。

.babelrc

1
2
3
4
5
6
7
8
{
"present": [
"es2015",
"react",
"stage-2"
],
"plugins": []
}

Present是按照ECMAScript草案来组织的,通常可以分为以下三大类:

  1. 已经被写入ECMAScript标准中的特性,由于之前每年都有新特性被加入到标准中,又可以细分为:
    • es2015包含在2015里加入的新特性;
    • es2016包含在2016里加入的新特性;
    • es2017包含在2017里加入的新特性;
    • env包含当前所有ECMAScript标准里的最新特性。

env包含了es2015、es2016和es2017。

  1. 被社区提出,但还没有写入ECMAScript标准里的特性,这其中又分为以下四种:
    • stage0 只是一个美好激进的想法,有 Babel 插件实现了对这些特性的支持,但是不确定是否会被定为标准;
    • stage1 值得被纳入标准的特性;
    • stage2 该特性规范已经被起草,将会被纳入标准里;
    • stage3 该特性规范已经定稿,各大浏览器厂商和 Node.js 社区开始着手实现。

stage0包含stage1,stage1包含stage2,stage2包含stage3。

  1. 为了支持一些特定应用场景下的语法,和ECMAScript标准没有关系。例如babel-preset-react是为了支持React开发中的JSX语法。
Read more »
123…6
泉先

泉先

今天我有变得更厉害一点吗!>v<

51 posts
14 categories
23 tags
© 2017 - 2022 泉先
Powered by Hexo
Theme - NexT.Gemini