nia3y / blog Goto Github PK
View Code? Open in Web Editor NEWniayyy`s blog
niayyy`s blog
移动端项目中公司框架默认引入了 fastclick.js。因为业务需要,同时引入了 ant-design 中的 select 组件,导致 select 组件需要双击才能弹出选项。
通过对这个问题进行深入研究,发现是 fastclick 导致的问题。
首选需要了解一下 DOM 中 click
,鼠标事件 mouse
以及触摸事件 touch
的触发顺序:
onTouchStart => (onTouchmove) => onTouchEnd => mousedown => (mousemove) => mouseup => click
通过查看 fastclick 源码得知:
onTouchEnd
中调用了 event.preventDefault()
阻止默认事件(会阻止后续的 mouse
、click
事件的触发)。click
事件(对于原生的 select
元素则触发 mousedown
事件)对于
onTouchStart
及onTouchEnd
中调用event.preventDefault()
阻止的默认事件,可参考:Touch event -- mdn
通过查看 ant-design 使用到的 rc-select
得知是模拟了原生的 select
的 mousedown
事件触发弹出选项事件的,但内部并没有使用 select
元素:
const onInternalMouseDown: React.MouseEventHandler<HTMLDivElement> = (event, ...restArgs) => {
// xxxx
if (onMouseDown) {
onMouseDown(event, ...restArgs);
}
};
// dom 结构
return (
<div
className={mergedClassName}
{...domProps}
ref={containerRef}
onMouseDown={onInternalMouseDown}
onKeyDown={onInternalKeyDown}
onKeyUp={onInternalKeyUp}
onFocus={onContainerFocus}
onBlur={onContainerBlur}
>
{mockFocused && !mergedOpen && (
<span
style={{
width: 0,
height: 0,
display: 'flex',
overflow: 'hidden',
opacity: 0,
}}
aria-live="polite"
>
{/* Merge into one string to make screen reader work as expect */}
{`${mergedRawValue.join(', ')}`}
</span>
)}
{selectorNode}
{arrowNode}
{clearNode}
</div>
)
组件的实现位于
react-component/select
中,文件地址:https://github.com/react-component/select/blob/master/src/generate.tsx#L906
fastclick 不能识别 ant-design 的 select
,导致 mousedown
事件不会触发,进而单击无反应。
通过进一步查看源码,了解到 fastclick 对双击事件进行了特殊处理,当两次点击低于延迟 250ms
(fastclick 默认是否为双击判断时间),当双击后会触发 fastclick 对双击事件进行处理、首先,在 onTouchStart
中:
FastClick.prototype.onTouchStart = function(event) {
// xxx
if ((event.timeStamp - this.lastClickTime) < this.tapDelay) {
event.preventDefault();
}
}
虽然 onTouchStart
中调用了 event.preventDefault()
,但是并不能阻止后续事件的触发。移动端为了让滚动能够更快的响应,所以浏览器对于 onTouchStart
事件默认设置 passive: true
,即调用 event.preventDefault()
会被忽略(chrome 56+)。
在后续的 onTouchEnd
中,也对双击进行了判断:
FastClick.prototype.onTouchEnd = function(event) {
if ((event.timeStamp - this.lastClickTime) < this.tapDelay) {
this.cancelNextClick = true;
return true;
}
// xxx
}
onTouchEnd
通过 return true
阻止了后续自定义事件的触发,导致后续原生的 mousedown
事件能够触发,进而 ant-design 的触发 select
的 onMouseDown
事件。
因为项目不需要兼容老旧的浏览器,并且 <header>
中已经设置了:
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1">
项目中不需要 fastclick 来进行兼容,所以最后直接干掉了 fastclick
更多移动端 300ms 解决方案:5 way prevent 300ms click delay mobile devices
[Modules: CommonJS modules | Node.js v22.2.0 Documentation]
commonJS 是一种前端模块化规范
每个文件有自己单独的作用域,在其中定义的所有变量、函数、类都是私有的
通过 module.exports 来对外暴露值、通过 require 来加载 module.exports 暴露的值
如果想要多文件共享,可以把功能定义到全局变量上(不推荐的🙅)
每一个模块都有一个 module 对象,有以下属性:
const module {
id: '.',
path: 'D:\\work\\commonjs\\tests',
exports: {}, // 模块对外输出的值
filename: 'D:\\work\\commonjs\\tests\\index.js',
loaded: false, // 该模块是否加载完成
children: [], // require 的模块信息
parent: [] // 调用该模块的模块信息
paths: [ // 模块的搜索路径
'D:\\work\\commonjs\\tests\\node_modules',
'D:\\work\\commonjs\\node_modules',
'D:\\work\\node_modules',
'D:\\node_modules'
]
}
exports 对象是对 module.exports 的引用
var exports = module.exports
模块中的 this 也指向这个 exports 对象
require 用来读取一个 JS 文件,并返回 exports 对象:
const bar = require('./foo.js');
// 读取 foo.js 并把 exports 赋值给 bar
文件解析规则:
如果路径以 /
开头,则加载绝对路径的模块文件
如果以 ./
开头,则加载相对路径文件
否则加载内置的模块 或者 根据当前路径,递归查找 node_modules
下的模块文件并结合 package.json 进行解析:
如果指定的模块文件没有发现,Node 会依次添加 .js
、.json
、.node
后缀并再次查找
具体的算法:
[Modules: CommonJS modules | Node.js v22.2.0 Documentation]
代码示例加载逻辑:
Moldule._load = function(request) {
// 1. 判断是否命中 Module.cache
// 2. 没命中的话执行 module._load 加载模块
// 3. 执行 module._compile 执行文件
// 4. 缓存执行结果,并返回 module.exports
}
Module.prototype._compile = function(content) {
const module = {
exports: {},
...
}
// 执行文件
// (function (exports, require, module, __filename, __dirname) {
// content
// })();
return module.exports
}
const require = [Function: require] { // 既是函数也是对象
resolve: [Function: resolve] // 返回加载的绝对地址
{
paths: [Function: paths] // 获取 path 的查找路径
},
main: { // 入口模块
id: '.',
path: 'D:\\work\\commonjs\\tests',
exports: {},
filename: 'D:\\work\\commonjs\\tests\\index.js',
loaded: false,
children: [ [Object] ],
paths: [
'D:\\work\\commonjs\\tests\\node_modules',
'D:\\work\\commonjs\\node_modules',
'D:\\work\\node_modules',
'D:\\node_modules'
]
},
extensions: [Object: null prototype] { // 不同后缀执行的加载方法
'.js': [Function (anonymous)],
'.json': [Function (anonymous)],
'.node': [Function (anonymous)]
},
cache: [Object: null prototype] { // 缓存的模块信息
'D:\\work\\commonjs\\tests\\index.js': {
id: '.',
path: 'D:\\work\\commonjs\\tests',
exports: {},
filename: 'D:\\work\\commonjs\\tests\\index.js',
loaded: false,
children: [Array],
paths: [Array]
},
'D:\\work\\commonjs\\tests\\import.js': {
id: 'D:\\work\\commonjs\\tests\\import.js',
path: 'D:\\work\\commonjs\\tests',
exports: [Object],
filename: 'D:\\work\\commonjs\\tests\\import.js',
loaded: true,
children: [],
paths: [Array]
}
}
}
// a.js
exports.x = 'a1';
console.log('a.js ', require('./b.js').x);
exports.x = 'a2';
// b.js
exports.x = 'b1';
console.log('b.js ', require('./a.js').x);
exports.x = 'b2';
// main.js
console.log('main.js ', require('./a.js').x);
console.log('main.js ', require('./b.js').x);
// output
// b.js a1
// a.js b2
// main.js a2
// main.js b2
设置 display: flex
会创建 flex 容器:
flex 布局计算是基于文档流的方向(flow directions)来进行的。主轴在文档流的方向,交叉轴和主轴垂直。
在 flex 容器元素中,一些属性不会生效:
clear
& float
:first-line
& :first-letter
设置为 inline-flex
在特殊情况下作为 flex
对待,详情:https://www.w3.org/TR/CSS2/visuren.html#dis-pos-flo
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.