跳到主要内容

在 Clash 中添加全局规则

· 阅读需 3 分钟
1adybug
子虚伊人

有时候我们希望某些网站不走代理,这时候就需要添加自定义规则,右击订阅,选择扩展脚本:

// Define main function (script entry)

/**
* @param {object} config
* @param {string[]} config.rules
* @param {string} profileName
*/
function main(config, profileName) {
config.rules.unshift("DOMAIN-SUFFIX,axshare.com,DIRECT")
config.rules.unshift("DOMAIN-SUFFIX,bing.com,DIRECT")
config.rules.unshift("DOMAIN-SUFFIX,codesandbox.io,DIRECT")
config.rules.unshift("DOMAIN-SUFFIX,csbops.io,DIRECT")
config.rules.unshift("DOMAIN-SUFFIX,csb.app,DIRECT")
config.rules.unshift("DOMAIN-SUFFIX,gallerycdn.vsassets.io,节点选择")
config.rules.unshift("DOMAIN-SUFFIX,gallery.vsassets.io,节点选择")
config.rules.unshift("DOMAIN-SUFFIX,marketplace.visualstudio.com,节点选择")
return config
}

DOMAIN 是指定域名,DOMAIN-SUFFIX 是指定域名后缀,DIRECT节点选择 都是规则内的分组。

规则类型匹配逻辑匹配目标性能等级关键注意事项
DOMAIN精确域名匹配域名仅匹配完全相同的域名字符串。
DOMAIN-SUFFIX域名后缀匹配域名匹配指定后缀及其所有子域名。
DOMAIN-KEYWORD域名关键词匹配域名匹配域名中任何位置出现的关键词,可能产生误匹配。
IP-CIDR目标 IPv4 地址段目标 IPv4默认触发 DNS 解析。
IP-CIDR6目标 IPv6 地址段目标 IPv6默认触发 DNS 解析。
GEOIP目标 IP 地理位置目标 IP基于 MaxMind 数据库;默认触发 DNS 解析。
SRC-IP-CIDR源 IPv4 地址段源 IPv4主要用于网关模式,区分局域网内不同设备。
DST-PORT目标端口目标端口基于 TCP/UDP 目标端口进行匹配。
SRC-PORT源端口源端口基于 TCP/UDP 源端口进行匹配。
PROCESS-NAME进程名称进程名操作系统相关;适用于 Windows, macOS, Linux, FreeBSD。
PROCESS-PATH进程路径进程路径操作系统相关;比进程名更精确,但可移植性差。
IPSETLinux IP 集目标 IP低 (内核)仅 Linux 可用;需要 ipset 工具;性能极高。

有时候我们希望某些规则对于所有的订阅都生效,这时候我们可以右击 全局扩展脚本,选择编辑文件:

// Define main function (script entry)

/**
* @param {object} config
* @param {string[]} config.rules
* @param {string} profileName
*/
function main(config, profileName) {
/** @type string | undefined */
const name = config["proxy-groups"]?.at(0)?.name
config.rules.unshift(`DOMAIN-SUFFIX,neo4j.com,${name}`)
return config
}

这里的意思找到订阅的第一个分组(一般都是通用的代理规则),然后将这条规则添加到这个分组中。

如果需要对于脚本进行调试,可以添加 console.log,在保存脚本以后,会在 全局扩展脚本 下方出现一个日志按钮,点击以后会输出脚本的日志。

stopPropagation 和 stopImmediatePropagation 之间的区别

· 阅读需 2 分钟
1adybug
子虚伊人

JavaScript 中,stopPropagationstopImmediatePropagation 是两个用于控制事件传播的方法。它们之间有一些关键的区别。

stopPropagation

stopPropagation 方法用于阻止事件冒泡到下一个元素,但允许其他事件处理程序在当前元素上继续执行。例如,如果你在一个按钮的点击事件中调用 stopPropagation,点击事件将不会冒泡到按钮的父元素,但该按钮上的其他点击事件处理程序仍然会被执行。

element.addEventListener("click", function (event) {
event.stopPropagation()
console.log("Button clicked")
})

stopImmediatePropagation

stopImmediatePropagation 方法不仅阻止事件冒泡,还阻止当前元素上所有后续的事件处理程序执行。这意味着一旦调用了 stopImmediatePropagation,当前元素上的其他事件处理程序将不会被触发。

element.addEventListener("click", function (event) {
event.stopImmediatePropagation()
console.log("Button clicked")
})

element.addEventListener("click", function (event) {
console.log("This will not be logged")
})

何时使用

  • 使用 stopPropagation 当你只想阻止事件冒泡但仍希望当前元素上的其他事件处理程序执行时。
  • 使用 stopImmediatePropagation 当你希望完全停止事件传播并阻止当前元素上的所有后续事件处理程序执行时。

了解这两个方法的区别可以帮助你更好地控制事件流,从而实现更复杂的用户交互逻辑。

修改函数的名称

· 阅读需 1 分钟
1adybug
子虚伊人

函数的名称在定义时已经被确定,比如:

function foo() {}

// 打印 "foo"
console.log(foo.name)

然而,匿名函数或者箭头函数的名称是空字符串:

const foo = (() => function () {})()

// 打印 ""
console.log(foo.name)

之所以要写成 (() => function () {})() 这么奇怪的形式,是应为使用 varletconst 定义的变量名会赋值给函数的名称:

const foo = function () {}

// 打印 "foo"
console.log(foo.name)

const bar = () => 0

// 打印 "bar"
console.log(bar.name)

变量定义时,属性名也会被赋值给函数的名称:

const obj = {
foo: function () {},
bar: () => 0,
}

// 打印 "foo"
console.log(obj.foo.name)

// 打印 "bar"
console.log(obj.bar.name)

然而,后续的赋值操作不会影响函数的名称:

const obj = {}

obj.foo = function () {}

// 打印 ""
console.log(obj.foo.name)

const name = "bar"
obj[name] = () => 0

// 打印 ""
console.log(obj.bar.name)

然而,更有趣的是,定义对象字面量时,动态的属性名会被赋值给属性名:

const name = "foo"

const obj = {
[name]: function () {},
}

// 打印 "foo"
console.log(obj[name].name)

最后,如果你想修改函数的名称,可以使用 Object.defineProperty

const foo = function () {}
Object.defineProperty(foo, "name", { value: "bar" })

// 打印 "bar"
console.log(foo.name)

在 Markdown 中使用 Prettier

· 阅读需 1 分钟
1adybug
子虚伊人

prettier 也支持 Markdown 文件的格式化,但是有以下注意点:

  1. 中文用户可以安装插件 prettier-plugin-lint-md
  2. JavaScript 代码块语言标识需要改为 javascript 或者 jsTypeScript 改为 typescript 或者 ts,驼峰命名的语言标识需要改为全小写,否则 prettier 无法识别
  3. tabWidth 要设置为 2,否则 Markdown 文件中的文本缩进会有问题

使用 Grid 布局实现的 Flow 组件

· 阅读需 1 分钟
1adybug
子虚伊人
.container {
display: grid;
grid-template-columns: repeat(auto-fill, 100px); /* 固定宽度为 100px */
grid-auto-rows: 100px; /* 固定高度为 100px */
gap: 10px; /* 网格间距 */
}

太牛了

selecto.js 配置项

· 阅读需 9 分钟
1adybug
子虚伊人

Selecto.js 是一个用于在网页上进行拖拽选择的 JavaScript 库,允许用户通过绘制选择框来选择页面上的元素。通过配置选项,您可以自定义 Selecto 的行为和功能。以下是每个配置选项的详细说明:

  1. container

    • 类型HTMLElement | string
    • 默认值document.body
    • 作用:指定选择区域的容器元素。Selecto 会在该容器内检测和选择元素。可以直接传入 DOM 元素或选择器字符串。
  2. dragContainer

    • 类型HTMLElement | string
    • 默认值container
    • 作用:指定触发拖拽事件的容器。通常用于在特定区域内启用或禁用拖拽选择。
  3. selectableTargets

    • 类型string[]
    • 默认值[]
    • 作用:定义可被选择的目标元素的选择器数组。只有匹配这些选择器的元素才会响应选择操作。
  4. hitRate

    • 类型number
    • 默认值100
    • 作用:设置元素被选择所需的覆盖率(百分比)。值为 0-100,表示选择框覆盖元素的比例达到该值时,元素即被选中。
  5. selectByClick

    • 类型boolean
    • 默认值true
    • 作用:是否允许通过单击来选择元素。设置为 true 时,用户可以直接点击元素进行选择。
  6. selectFromInside

    • 类型boolean
    • 默认值true
    • 作用:是否允许从被选择元素的内部开始拖拽选择。设置为 false 时,用户无法从目标元素内部开始绘制选择框。
  7. continueSelect

    • 类型boolean
    • 默认值false
    • 作用:是否在新的选择操作中保留之前选中的元素。设置为 true,新的选择结果会累加到已有的选择中。
  8. toggleContinueSelect

    • 类型string | string[]
    • 默认值null
    • 作用:指定用于切换 continueSelect 状态的按键。当按下指定的键时,continueSelect 状态会被激活或关闭。例如:'shift'['ctrl', 'meta']
  9. keyContainer

    • 类型HTMLElement | Document | Window
    • 默认值window
    • 作用:指定用于监听键盘事件的容器。当使用 toggleContinueSelect 功能时,需要监听键盘事件以切换选择模式。
  10. ratio

    • 类型number
    • 默认值0
    • 作用:设置选择框的固定宽高比。值为 0 时,不限制宽高比。非零值会使选择框按照指定比例缩放。
  11. scrollOptions

    • 类型object | null
    • 默认值null
    • 作用:配置自动滚动选项,当选择框到达容器边缘时,容器会自动滚动。包含以下属性:
      • container: 滚动的容器元素或选择器。
      • threshold: 触发滚动的距离阈值。
      • speed: 滚动速度。
      • getScrollPosition: 自定义获取滚动位置的方法。
  12. boundContainer

    • 类型HTMLElement | string | null
    • 默认值null
    • 作用:限制选择框的活动范围。选择框无法超出指定的容器边界。
  13. preventDefault

    • 类型boolean
    • 默认值false
    • 作用:是否在拖拽选择时调用 event.preventDefault(),以防止默认的浏览器行为(如文本选中、图片拖拽等)。
  14. cspNonce

    • 类型string
    • 默认值null
    • 作用:用于 Content Security Policy(内容安全策略)的 nonce 值,确保内联样式在启用了 CSP 的环境下被正确应用。
  15. checkInput

    • 类型boolean
    • 默认值false
    • 作用:是否在选择操作中检查输入元素(如 <input><textarea>)。设置为 true 时,选择操作不会影响这些输入元素的交互。
  16. preventDragFromInside

    • 类型boolean
    • 默认值true
    • 作用:是否防止从目标元素内部开始拖拽选择。设置为 true,可以避免与内部元素的拖拽操作冲突。
  17. getElementRect

    • 类型function
    • 默认值(el) => el.getBoundingClientRect()
    • 作用:自定义获取元素位置和尺寸的方法。可用于处理特殊情况或优化性能。
  18. dragCondition

    • 类型function
    • 默认值() => true
    • 作用:自定义拖拽开始的条件。返回 true 表示允许开始拖拽选择,false 则阻止拖拽。
  19. className

    • 类型string
    • 默认值''
    • 作用:为选择框元素添加自定义的 CSS 类名,方便进行样式定制。
  20. hoverClassName

    • 类型string
    • 默认值''
    • 作用:当元素被鼠标悬停或被选择时,添加到元素上的 CSS 类名。
  21. toggleContinueSelectWithoutDeselect

    • 类型boolean
    • 默认值false
    • 作用:在切换 continueSelect 状态时,是否保留已选中的元素而不取消选择。
  22. preventClickEvent

    • 类型boolean
    • 默认值true
    • 作用:是否在选择操作中阻止点击事件的触发,避免与其他点击交互冲突。
  23. appendTo

    • 类型HTMLElement | string
    • 默认值container
    • 作用:指定选择框元素添加到的容器。可用于调整选择框的层级关系。
  24. dragCondition

    • 类型function
    • 默认值null
    • 作用:自定义判断是否可以开始拖拽选择的条件函数。返回 true 允许拖拽,false 阻止拖拽。

示例代码:

const selecto = new Selecto({
container: document.querySelector(".selecto-area"),
dragContainer: window,
selectableTargets: [".selectable"],
hitRate: 50,
selectByClick: true,
selectFromInside: false,
continueSelect: false,
toggleContinueSelect: "shift",
keyContainer: window,
ratio: 0,
scrollOptions: {
container: document.querySelector(".scroll-container"),
threshold: 30,
speed: 10,
},
boundContainer: document.querySelector(".bound-area"),
preventDefault: true,
cspNonce: "your-csp-nonce",
checkInput: true,
preventDragFromInside: false,
getElementRect: el =>
// 自定义获取元素位置的方法
({
left: el.offsetLeft,
top: el.offsetTop,
width: el.offsetWidth,
height: el.offsetHeight,
}),
className: "custom-selecto",
hoverClassName: "selected",
toggleContinueSelectWithoutDeselect: true,
preventClickEvent: true,
appendTo: document.body,
dragCondition: e =>
// 仅当按下左键时允许拖拽选择
e.inputEvent.button === 0,
})

通过正确配置以上选项,您可以根据具体需求定制 Selecto.js 的功能,例如:

  • 多选功能:使用 continueSelect: true 实现多次选择累加。
  • 限制选择范围:通过 boundContainer 限制选择框的活动区域。
  • 自定义样式:使用 classNamehoverClassName 为选择框和被选中元素添加自定义样式。

注意事项:

  • 事件监听:Selecto.js 提供了丰富的事件回调,如 select, dragStart, dragEnd,可用于响应用户的选择操作。
  • 性能优化:在大量可选元素时,合理使用 selectableTargetsgetElementRect 以提升性能。

希望以上解释能帮助您全面了解 Selecto.js 的配置选项,从而更好地应用到项目中。

FAQ

dragContainercontainer 的区别

深入理解 selecto.js 中 containerdragContainer 的区别

selecto.js 中,containerdragContainer 都是用于配置选择行为的选项,但它们的作用不同。


1. container

  • 类型HTMLElement | string
  • 默认值document.body
  • 作用:定义选择操作的范围,以及可被选择的元素所在的区域。

container 是选择操作发生的区域,Selecto 会在这个容器内查找和选择元素。当您指定 selectableTargets 时,selecto.js 会在 container 内根据这些选择器查找目标元素。

container 的关键点:

  • 操作范围:它是 selecto.js 进行选择操作的主要区域。
  • 元素定位:所有可被选择的元素都应位于此容器内。
  • 坐标计算:选择框的位置和尺寸是基于该容器的坐标系计算的。
  • 默认容器:如果未指定,默认为 document.body

2. dragContainer

  • 类型HTMLElement | string
  • 默认值:如果未指定,则与 container 相同
  • 作用:指定监听鼠标或触摸事件以开始选择过程的区域或元素。

dragContainer 是用户可以开始拖拽以创建选择框的区域。换句话说,它是用户可以点击并拖动以开始选择的区域。

dragContainer 的关键点:

  • 事件监听区域:它是用于监听鼠标或触摸事件的区域。
  • 选择启动:用户只能在此区域内开始点击并拖动以进行选择。
  • 可独立设置:可以与 container 不同,允许在一个区域启动选择,但选择另一个区域内的元素。
  • 用途多样:可用于限制选择操作的启动区域,防止干扰其他交互。

区别和使用场景

区别总结:

  • container:定义选择操作的目标区域和元素所在位置。
  • dragContainer:定义用户可以开始拖拽选择的区域。

使用场景:

  1. 默认行为(containerdragContainer 相同)

    • 用户可以在容器内的任何位置开始拖拽选择。
    • 适用于简单的选择场景,没有特殊的交互限制。
  2. 不同的 containerdragContainer

    • 场景一:防止干扰元素的正常交互
      • 示例:在一个图片库中,您希望用户在图片上点击时查看大图,而不是开始选择。
      • 解决:将 dragContainer 设置为不包含图片的区域,如空白背景。
    • 场景二:从特定区域启动选择
      • 示例:只有在按住某个按钮或在特定面板上拖拽时才开始选择。
      • 解决:将 dragContainer 设置为该按钮或面板的元素选择器。

示例代码:

const selecto = new Selecto({
// 定义可被选择的元素所在的容器
container: ".items-container",
// 定义用户可以开始拖拽选择的区域
dragContainer: ".selection-area",
// 可被选择的元素
selectableTargets: [".items-container .item"],
// 其他配置...
})

解释:

  • .items-container:包含了所有可被选择的项目。
  • .selection-area:用户只能在此区域内开始拖拽选择。
  • 结果:避免了用户在与项目交互时(如点击、拖动项目)意外启动选择操作。

总结

  • container(容器):选择操作的范围,决定哪些元素可以被选择,以及选择框的位置计算。
  • dragContainer(拖拽容器):用户可以开始拖拽以进行选择的区域,决定了选择操作的触发区域。

通过正确配置这两个选项,您可以:

  • 控制选择行为:防止选择操作干扰其他交互(如点击、拖动元素本身)。
  • 优化用户体验:使选择操作更加直观,符合应用的交互逻辑。
  • 增强功能:实现复杂的交互需求,如在特定区域或条件下才允许选择。

建议:

  • 根据需求设置:如果不需要特殊的选择启动区域,保持默认配置即可。
  • 测试交互效果:在应用中测试不同配置,确保选择操作符合用户预期。

希望以上解释能帮助您充分理解 containerdragContainer 的区别,并在项目中灵活应用。

https 详解

· 阅读需 7 分钟
1adybug
子虚伊人

对称加密

对称加密是一种加密算法,其中加密和解密使用同一个密钥。它的工作原理是,发送方用密钥对数据进行加密,接收方使用相同的密钥来解密数据。由于加密和解密过程中使用的密钥是相同的,因此被称为“对称”。

对称加密的基本流程

  1. 密钥生成:系统生成一个共享的秘密密钥。
  2. 加密:发送方使用密钥和对称加密算法对原始数据进行加密,生成密文。
  3. 传输密文:将密文传输给接收方。
  4. 解密:接收方使用相同的密钥解密密文,恢复原始数据。

常见的对称加密算法

  • DES(数据加密标准):一种较早的对称加密算法,密钥长度为 56 位,安全性较低。
  • AES(高级加密标准):一种现代的对称加密算法,密钥长度可以是 128 位、192 位或 256 位,具有较高的安全性和效率。

对称加密的优点

  • 加密速度快:对称加密比非对称加密在处理大数据时速度更快。
  • 算法简单:实现相对简单,资源消耗较低。

对称加密的缺点

  • 密钥管理困难:双方必须安全地共享和管理密钥。如果密钥被泄露,数据就不再安全。客户端是不可信的,所以客户端的密钥泄露后果很严重。
  • 不适用于大量用户的环境:因为每对用户需要单独的一套密钥,管理多个密钥对会非常复杂。

非对称加密

非对称加密是一种加密算法,它使用一对密钥:公钥私钥。这对密钥中的一个用于加密,另一个则用于解密。公钥可以公开给任何人,而私钥必须由所有者保密。非对称加密解决了对称加密中“密钥分发”的难题,因为加密和解密使用不同的密钥。

非对称加密的基本流程

  1. 密钥对生成:系统生成一对密钥,包含一个公钥和一个私钥。
  2. 公钥加密:发送方使用接收方的公钥对消息进行加密。
  3. 私钥解密:接收方使用自己的私钥解密密文,恢复原始数据。

由于使用不同的密钥,加密和解密过程更加安全,尤其是在公钥公开的情况下,只有持有对应私钥的人才能解密密文。

公钥私钥是非对称加密算法中的两种密钥,它们共同构成一对密钥,用于加密和解密数据。这两个密钥具有特殊的数学关系:使用一个密钥加密的数据,必须用另一个密钥来解密。

公钥和私钥

公钥(Public Key)

  • 定义:公钥是非对称加密中的一个密钥,可以公开给任何人使用。
  • 用途:主要用于加密数据和验证数字签名。任何人都可以使用接收方的公钥对消息进行加密。
  • 特点:即使公钥是公开的,没有对应的私钥,外部的人也无法解密数据,因此公钥的公开不会带来风险。

私钥(Private Key)

  • 定义:私钥是与公钥成对的密钥,必须严格保密,仅由密钥所有者掌握。
  • 用途:用于解密通过公钥加密的数据,或者用来生成数字签名,证明消息的真实性。
  • 特点:只有持有私钥的人才能解密用公钥加密的消息,或者验证私钥签署的数字签名的真实性。

公钥和私钥的工作原理

  1. 加密和解密

    • 发送方使用接收方的公钥加密数据。
    • 接收方使用自己的私钥解密数据。

    由于公钥可以公开,任何人都能加密消息,但只有持有私钥的人能解密。

  2. 数字签名

    • 发送方使用自己的私钥对消息进行签名,证明消息是由自己发出的。
    • 接收方使用发送方的公钥验证签名,确保消息的真实性。

    这种机制确保了消息的完整性和发送者的身份,防止了数据篡改和伪造。

公钥和私钥的应用场景

  • 加密通信:使用接收方的公钥加密数据,确保只有接收方能使用私钥解密。
  • 数字签名和认证:使用私钥进行签名,确保接收方能够验证消息的来源和真实性。
  • 密钥交换:通过非对称加密安全地交换对称加密所用的密钥。

关键点总结

  • 公钥可以公开,主要用于加密和验证签名。
  • 私钥必须保密,主要用于解密和生成签名。
  • 公钥和私钥是互补的,一个加密的数据只能通过另一个来解密。

常见的非对称加密算法

  • RSA(Rivest-Shamir-Adleman):一种广泛使用的非对称加密算法,常用于数字签名和密钥交换。
  • ECC(椭圆曲线加密):一种效率更高的非对称加密算法,提供相同安全性所需的密钥长度更短,计算更快。

非对称加密的优点

  • 安全性更高:由于公钥可以公开,私钥可以保持私密,非对称加密解决了对称加密中的密钥分发问题。
  • 支持数字签名:可以验证消息发送者的身份,确保消息的完整性和真实性。

非对称加密的缺点

  • 加密速度较慢:相比对称加密,非对称加密在处理大量数据时速度较慢,计算复杂度高。
  • 密钥长度较长:为了提供相同的安全性,非对称加密所需的密钥通常比对称加密的密钥长得多。

HTTPS

HTTPS(超文本传输安全协议)使用了对称加密、非对称加密和数字证书来保证通信的安全性。这个过程大致可以分为以下几个步骤:

  1. 建立安全连接:

    当浏览器连接到 HTTPS 网站时,服务器会发送其 SSL 证书给浏览器。这个证书包含了服务器的公钥,以及由可信任的证书颁发机构(CA)签名的证书信息。

  2. 验证证书:

    浏览器会验证证书的有效性,包括检查证书是否过期、是否由可信的 CA 签发等。这一步骤可以确保浏览器连接到的确实是预期的服务器,而不是某个冒充的恶意服务器。

  3. 生成会话密钥:

    验证通过后,浏览器会生成一个随机的对称密钥(会话密钥)。然后使用服务器的公钥对这个会话密钥进行加密。

  4. 密钥交换:

    浏览器将加密后的会话密钥发送给服务器。服务器使用自己的私钥解密,获得会话密钥。

  5. 安全通信:

    此后,浏览器和服务器就可以使用这个共享的会话密钥,通过对称加密算法来加密它们之间的所有通信内容。

为什么要使用这些技术:

  1. 非对称加密(公钥加密):

    • 用于安全地交换对称密钥
    • 解决了密钥分发的问题
    • 但是计算速度较慢
  2. 对称加密:

    • 用于加密实际的通信内容
    • 计算速度快,适合大量数据的加密
  3. CA 证书:

    • 用于验证服务器的身份
    • 防止中间人攻击

通过结合使用这些技术,HTTPS 能够:

  • 确保通信的机密性(防止被窃听)
  • 保证数据的完整性(防止被篡改)
  • 验证通信双方的身份(防止身份伪造)

这种方式既保证了安全性,又兼顾了效率,是目前互联网上广泛使用的安全通信方式。

document 和 document.documentElement 之间的区别

· 阅读需 1 分钟
1adybug
子虚伊人

documentdocument.documentElement 确实有一些重要区别:

  1. 定义:

    • document 是整个 HTML 文档的根节点。
    • document.documentElementHTML 文档的根元素,通常是 <html> 标签。
  2. 层级:

    • documentDOM 树的顶层对象。
    • document.documentElementdocument 的直接子节点。
  3. 属性和方法:

    • document 包含许多特有的属性和方法,如 createElement(), getElementById() 等。
    • document.documentElement 主要继承自 Element 接口,拥有元素通用的属性和方法。
  4. 尺寸获取:

    • 获取视口大小时,通常使用 document.documentElement.clientWidth/clientHeight
    • document 本身没有这些尺寸属性。
  5. 滚动相关:

    • 处理页面滚动时,常用 document.documentElement.scrollTop/scrollLeft
    • 某些浏览器可能需要使用 document.body 而非 document.documentElement
  6. DOCTYPE:

    • document 包含 DOCTYPE 声明。
    • document.documentElement 不包含 DOCTYPE,仅从 <html> 开始。

React Native 和 Expo 开发中的问题汇总

· 阅读需 1 分钟
1adybug
子虚伊人

无法连接到开发服务器

注意

could not connect to the development server

解决方案:

  1. 在虚拟机中连接 WiFi
  2. 在 Android Studio 中将 GradleJava 位置设置为系统位置

项目编译失败

解决方案:

在 Android Studio 中打开 android 目录

动画暂停之后速度变慢

原因:

动画暂停之后再开始,会以设置的全部时间来完成剩余的进度

解决方案:

/** 初始值 */
const fromValue = 0

/** 重点值 */
const toValue = 100

/** 时间 */
const duration = 10000

/** 速度 */
const speed = (toValue - fromValue) / duration

/** 动画值 */
const translateX = useRef(new Animated.Value(fromValue)).current

/** 暂停值 */
const stopValue = useRef(fromValue)

/** 动画状态 */
const status = useRef(false)

function onClick() {
// 如果暂停值已经达到目标值,说明动画已经完成
if (stopValue.current === toValue) return

// 如果动画处于播放状态,暂停动画,并且将当前值赋予给暂停值
if (status.current) translateX.stopAnimation(value => (stopValue.current = value))
// 否则播放动画
else {
Animated.timing(translateX, {
toValue,
duration: (toValue - stopValue.current) / speed,
useNativeDriver: true,
easing: Easing.linear,
}).start(({ finished }) => finished && (stopValue.current = toValue))
}

status.current = !status.current
}

useForm 警告

· 阅读需 1 分钟
1adybug
子虚伊人

在使用 Ant DesignForm 表单时,经常会遇到这个警告:

注意

Warning: Instance created by `useForm` is not connected to any Form element. Forget to pass `form` prop?

如果确定传递了 form 属性,那么一般是由于调用 form 实例的方法时,Form 表单还没有被渲染的原因,常见于 Modal 中嵌套的 Form,解决办法为监听弹窗是否被打开:

useEffect(() => {
if (!open) return
form.setFieldsValue({})
}, [open, form])