#2022#2021

css3 动画相关

--- title: css3 动画相关 tags: - css3 - animation author: powerfulyang date: 2021-11-23 --- ## animation-timing-function 有如下可选值: ```css /* Keyword values */ animation-timing-function: ease; animation-timing-function: ease-in; animation-timing-function: ease-out; animation-timing-function: ease-in-out; animation-timing-function: linear; animation-timing-function: step-start; animation-timing-function: step-end; /* Function values */ animation-timing-function: cubic-bezier(0.1, 0.7, 1.0, 0.1); animation-timing-function: steps(4, end); /* Steps Function keywords */ animation-timing-function: steps(4, jump-start); animation-timing-function: steps(10, jump-end); animation-timing-function: steps(20, jump-none); animation-timing-function: steps(5, jump-both); animation-timing-function: steps(6, start); animation-timing-function: steps(8, end); /* Multiple animations */ animation-timing-function: ease, step-start, cubic-bezier(0.1, 0.7, 1.0, 0.1); /* Global values */ animation-timing-function: inherit; animation-timing-function: initial; animation-timing-function: revert; animation-timing-function: unset; ``` ### steps() > Relate to [张鑫旭的 blog](https://www.zhangxinxu.com/wordpress/2018/06/css3-animation-steps-step-start-end/) 可以用于实现不连续的动画 > Relate to [stackoverflow](https://stackoverflow.com/questions/4503195/css3-sprite-animation-without-tweening) 一些 demo 仅供参考 [demo1](https://codepen.io/atelierbram/pen/gMOypj) [demo2](https://codepen.io/whqet/pen/qgtzH) ## prefers-reduced-motion 媒体查询 prefers-reduced-motion 用于检测用户的系统是否被开启了动画减弱功能。 ```css .animation { animation: vibrate 0.3s linear infinite both; } @media (prefers-reduced-motion: reduce) { .animation { animation: none; } } ```
powerfulyangThu, Dec 23, 2021 1:53 PM

一场 position: sticky 无效引发的思考

--- title: '一场 position: sticky 无效引发的思考' author: powerfulyang date: 2021-12-1 tags: - css --- > Relate to https://stackoverflow.com/questions/43707076/how-does-the-position-sticky-property-work ## 一些属性会使 position: sticky 无效 + You must specify a threshold with at least one of `top`, `right`, `bottom`, or `left` for sticky positioning to behave as expected + 父元素的 `display: flex` / `overflow` 属性,等会使其无效 ### 没了 display: flex 还怎么活 使用 float: left 浮动元素 又导致了一个问题 **子元素无法把父元素撑开** #### 解决使用 float: left 出现的塌陷 > Relate to https://blog.csdn.net/xiaobing_hope/article/details/50296009 在最后一个浮动子元素后面添加一个空的div,并清除其浮动 ```html <div style="float: left" class="div0"> <div class="div1" style="float: left;"></div> <div class="div2" style="float: left;"></div> <div style="clear:both;"></div> </div> ``` ## 2022-01-06 更新 > Relate to https://ishadeed.com/article/position-sticky-css-grid/ ### align-self: start; demo: https://codepen.io/shadeed/pen/VwzGpRa 其实之前链接已经有较好的解决方案了 https://stackoverflow.com/a/66966273/12583084, 我却没看到, 并不需要那么多 hack 。
powerfulyangWed, Dec 15, 2021 8:42 PM

什么是 QPS, TPS, PV, UV, GMV...

--- title: 什么是 QPS, TPS, PV, UV, GMV... tags: - 十万个全知道 author: powerfulyang posterId: 46050 date: 2021-09-07 --- ## QPS `Queries Per Second`,每秒查询数。每秒能够响应的查询次数。 QPS 是对一个特定的查询服务器在规定时间内所处理流量多少的衡量标准,在因特网上,作为域名系统服务器的机器的性能经常用每秒查询率来衡量。每秒的响应请求数,也即是最大吞吐能力。 ## TPS `Transactions Per Second` 的缩写,每秒处理的事务数目。一个事务是指一个客户机向服务器发送请求然后服务器做出反应的过程。客户机在发送请求时开始计时,收到服务器响应后结束计时,以此来计算使用的时间和完成的事务个数,最终利用这些信息作出的评估分。 TPS 的过程包括:客户端请求服务端、服务端内部处理、服务端返回客户端。 例如,访问一个 Index 页面会请求服务器 3 次,包括一次 html,一次 css,一次 js,那么访问这一个页面就会产生一个 “T”,产生三个 “Q”。 ## PV `Page View` 即页面浏览量,通常是衡量一个网络新闻频道或网站甚至一条网络新闻的主要指标。用户每一次对网站中的每个页面访问均被记录 1 次。用户对同一页面的多次刷新,访问量累计。 根据这个特性,刷网站的 PV 就很好刷了。 与 PV 相关的还有 RV,即重复访问者数量 `Repeat Visitors`。 ## UV 访问数 `Unique Visitor` 指独立访客访问数,统计 1 天内访问某站点的用户数 (以 cookie 为依据),一台电脑终端为一个访客。 ## GMV `Gross Merchandise Volume` 的简称。只要是订单,不管消费者是否付款、卖家是否发货、是否退货,都可放进 GMV 。 ## RPS 代表吞吐率,即 `Requests Per Second` 的缩写。吞吐率是服务器并发处理能力的量化描述,单位是 reqs/s,指的是某个并发用户数下单位时间内处理的请求数。 某个并发用户数下单位时间内能处理的最大的请求数,称之为最大吞吐率。
powerfulyangTue, Dec 14, 2021 4:20 PM

关闭页面时怎么向后台发送消息?

--- title: 关闭页面时怎么向后台发送消息? author: powerfulyang date: 2021-08-25 tags: - broswer - navigate --- ## 使用 navigate api 这个方法主要用于满足统计和诊断代码的需要,这些代码通常尝试在卸载(unload)文档之前向web服务器发送数据。 __参数__ + url 表明 data 将要被发送到的网络地址。 + data 参数是将要发送的 ArrayBufferView 或 Blob 、DOMString 或者 FormData 类型的数据 ```js navigator.sendBeacon(url, data); ``` __返回值__ + 当用户代理成功把数据加入传输队列时,sendBeacon() 方法将会返回 true,否则返回 false。
powerfulyangTue, Dec 14, 2021 4:18 PM

不常用的Hooks

--- title: 不常用的Hooks author: powerfulyang date: 2021-01-12 tags: - react hooks --- ## useEffect 在渲染结束之后执行 ## useLayoutEffect 在DOM更新之后执行 ### useEffect 和 useLayoutEffect 差别 > Respect to [when-to-use-useimperativehandle-uselayouteffect-and-usedebugvalue](https://stackoverflow.com/questions/57005663/when-to-use-useimperativehandle-uselayouteffect-and-usedebugvalue) useLayoutEffect 在 DOM 更新之后执行。 useEffect 在 render 渲染结束后执行。 执行示例代码会发现 useLayoutEffect 永远比 useEffect 先执行,这是因为 DOM 更新之后,渲染才结束或者渲染还会结束。 Used in rare cases when you need to calculate the distance between elements after an update or do other post-update calculations / side-effects. ## useDebugValue I feel like the [docs](https://reactjs.org/docs/hooks-reference.html#usedebugvalue) do a pretty good example of explaining this one. If you have a custom hook, and you'd like to label it within React DevTools, then this is what you use.
powerfulyangTue, Dec 14, 2021 4:17 PM

React合成事件和DOM原生事件

--- title: React合成事件和DOM原生事件 author: powerfulyang date: 2021-01-12 tags: - react --- > Respect to [React合成事件和DOM原生事件混用须知](https://juejin.cn/post/6844903502729183239) React 的事件处理机制可以分为两个阶段:初始化渲染时在 root 节点上注册原生事件;原生事件触发时模拟捕获、目标和冒泡阶段派发合成事件。通过这种机制,冒泡的原生事件类型最多在 root 节点上注册一次,节省内存开销。且 React 为不同类型的事件定义了不同的处理优先级,从而让用户代码及时响应高优先级的用户交互,提升用户体验。 React 的事件机制中依赖合成事件这个核心概念。合成事件在符合 W3C 规范定义的前提下,抹平浏览器之间的差异化表现。并且简化事件逻辑,对关联事件进行合成。如每当表单类型组件的值发生改变时,都会触发 onChange 事件,而 onChange 事件由 change、click、input、keydown、keyup 等原生事件组成。 ## SyntheticEvent React17版本开始,对事件系统的两个重要变更: * React17以前将事件(包括捕获及冒泡)委托到document的冒泡阶段触发。React17开始将冒泡事件委托到容器root的冒泡阶段触发,将捕获事件委托到容器root的捕获阶段触发 * [移除事件池](https://zh-hans.reactjs.org/blog/2020/08/10/react-v17-rc.html#no-event-pooling) 注意: `react`的事件体系, 不是全部都通过`事件委托`来实现的. 有一些[特殊情况](https://github.com/facebook/react/blob/v17.0.2/packages/react-dom/src/client/ReactDOMComponent.js#L530-L616), 是直接绑定到对应 DOM 元素上的(如:`scroll`, `load`), 它们都通过[listenToNonDelegatedEvent](https://github.com/facebook/react/blob/v17.0.2/packages/react-dom/src/events/DOMPluginEventSystem.js#L295-L314)函数进行绑定. ```jsx const handleClick = (e) => { setState(e.target.value) // 在 react 16 中, 因为事件复用 setState(异步) 时 e 的引用被清空。 // react 17 中 no event pooling。 可以直接在异步中使用 event. // 从 v17 开始,e.persist() 将不再生效,因为 SyntheticEvent 不再放入事件池中。 } <button onClick={handleClick}> Activate Lasers </button> ``` **响应顺序上合成事件晚于原生事件** NativeEvent stopPropagation 会阻止 SyntheticEvent. SyntheticEvent stopPropagation 不会阻止 NativeEvent. ## What are passive event listeners? ```javascript element.addEventListener(event, handler[, options]); ``` **event** 事件名,例如:`"click"`。 **handler** 处理程序。 **options** 具有以下属性的附加可选对象: * `once`:如果为 `true`,那么会在被触发后自动删除监听器。 * `capture`:事件处理的阶段,由于历史原因,`options` 也可以是 `false/true`,它与 `{capture: false/true}` 相同。 * `passive`:如果为 `true`,那么处理程序将不会调用 `preventDefault()`。 > https://stackoverflow.com/a/37721906/12583084 **Problem:** All modern browsers have a threaded scrolling feature to permit scrolling to run smoothly even when expensive JavaScript is running, but this optimization is partially defeated by the need to wait for the results of any `touchstart` and `touchmove` handlers, which may prevent the scroll entirely by calling `preventDefault()` on the event. **Solution: `{passive: true}`** By marking a touch or wheel listener as passive, the developer is promising the handler won't call `preventDefault` to disable scrolling. This frees the browser up to respond to scrolling immediately without waiting for JavaScript, thus ensuring a reliably smooth scrolling experience for the user. ```js document.addEventListener("touchstart", function(e) { console.log(e.defaultPrevented); // will be false e.preventDefault(); // does nothing since the listener is passive console.log(e.defaultPrevented); // still false }, Modernizr.passiveeventlisteners ? {passive: true} : false); ``` ## 事件委托 捕获和冒泡允许我们实现最强大的事件处理模式之一,即 **事件委托** 模式。 这个想法是,如果我们有许多以类似方式处理的元素,那么就不必为每个元素分配一个处理程序 —— 而是将单个处理程序放在它们的共同祖先上。 在处理程序中,我们获取 `event.target` 以查看事件实际发生的位置并进行处理。 好处: * 简化初始化并节省内存:无需添加许多处理程序。 * 更少的代码:添加或移除元素时,无需添加/移除处理程序。 * DOM 修改 :我们可以使用 `innerHTML` 等,来批量添加/移除元素。 事件委托也有其局限性: * 首先,事件必须冒泡。而有些事件不会冒泡。此外,低级别的处理程序不应该使用 `event.stopPropagation()`。 * 其次,委托可能会增加 CPU 负载,因为容器级别的处理程序会对容器中任意位置的事件做出反应,而不管我们是否对该事件感兴趣。但是,通常负载可以忽略不计,所以我们不考虑它。 ## 自定义事件 ### 事件构造器 内建事件类形成一个层次结构(hierarchy),类似于 DOM 元素类。根是内建的 [Event](http://www.w3.org/TR/dom/#event) 类。 ```js let event = new Event(type[, options]); ``` 参数: * **type** —— 事件类型,可以是像这样 `"click"` 的字符串,或者我们自己的像这样 `"my-event"` 的参数。 * **options** —— 具有两个可选属性的对象: * `bubbles: true/false` —— 如果为 `true`,那么事件会冒泡。 * `cancelable: true/false` —— 如果为 `true`,那么“默认行为”就会被阻止。稍后我们会看到对于自定义事件,它意味着什么。 默认情况下,以上两者都为 false:`{bubbles: false, cancelable: false}`。 ### dispatchEvent 事件对象被创建后,我们应该使用 `elem.dispatchEvent(event)` 调用在元素上“运行”它。 然后,处理程序会对它做出反应,就好像它是一个常规的浏览器事件一样。如果事件是用 `bubbles` 标志创建的,那么它会冒泡。 在下面这个示例中,`click` 事件是用 JavaScript 初始化创建的。处理程序工作方式和点击按钮的方式相同: ```html <button id="elem" onclick="alert('Click!');">Autoclick</button> <script> let event = new Event("click"); elem.dispatchEvent(event); </script> ``` **event.isTrusted** 有一种方法可以区分“真实”用户事件和通过脚本生成的事件。 对于来自真实用户操作的事件,`event.isTrusted` 属性为 `true`,对于脚本生成的事件,`event.isTrusted` 属性为 `false`。 **我们应该对我们的自定义事件使用 `addEventListener`,因为 `on<event>` 仅存在于内建事件中,`document.onhello` 则无法运行。** ### event.preventDefault() 通过调用 `event.preventDefault()`,事件处理程序可以发出一个信号,指出这些行为应该被取消。 在这种情况下,`elem.dispatchEvent(event)` 的调用会返回 `false`。那么分派(dispatch)该事件的代码就会知道不应该再继续。 ```html <pre id="rabbit"> |\ /| \|_|/ /. .\ =\_Y_/= {>o<} </pre> <button onclick="hide()">Hide()</button> <script> function hide() { let event = new CustomEvent("hide", { cancelable: true // 没有这个标志,preventDefault 将不起作用 }); if (!rabbit.dispatchEvent(event)) { alert('The action was prevented by a handler'); } else { rabbit.hidden = true; } } rabbit.addEventListener('hide', function(event) { if (confirm("Call preventDefault?")) { event.preventDefault(); } }); </script> ``` 请注意:该事件必须具有 `cancelable: true` 标志,否则 `event.preventDefault()` 调用将会被忽略。 ### 事件中的事件是同步的 addEventListener => handler 是同步执行的。 ## 浏览器默认行为 许多事件会自动触发浏览器执行某些行为。 例如: * 点击一个链接 —— 触发导航(navigation)到该 URL。 * 点击表单的提交按钮 —— 触发提交到服务器的行为。 * 在文本上按下鼠标按钮并移动 —— 选中文本。 ### 阻止浏览器行为 有两种方式来告诉浏览器我们不希望它执行默认行为: * 主流的方式是使用 `event` 对象。有一个 `event.preventDefault()` 方法。 * 如果处理程序是使用 `on<event>`(而不是 `addEventListener`)分配的,那返回 `false` 也同样有效。 ```html <a href="/" onclick="return false">Click here</a> or <a href="/" onclick="event.preventDefault()">here</a> ``` 想要阻止默认行为 —— 可以使用 `event.preventDefault()` 或 `return false`。第二个方法只适用于通过 `on<event>` 分配的处理程序。 `addEventListener` 的 `passive: true` 选项告诉浏览器该行为不会被阻止。这对于某些移动端的事件(像 `touchstart` 和 `touchmove`)很有用,用以告诉浏览器在滚动之前不应等待所有处理程序完成。 如果默认行为被阻止,`event.defaultPrevented` 的值会变成 `true`,否则为 `false`。
powerfulyangTue, Dec 14, 2021 4:15 PM

Linux SWAP 交换分区

--- title: Linux SWAP 交换分区 author: powerfulyang date: 2020-12-26 posterId: 46055 tags: - linux swap --- > Linux 系统中的 Swap 分区,即交换分区,类似于 Windows 的虚拟内存, 其作用可简单的描述为:当系统的物理内存不够用的时候,将暂时不用的数据存放到交换空间所在的硬盘上,从而可以腾出内存来让别的程序运行。 下面将介绍通过添加 Swap 文件来设置交换分区的方式。 ## 添加swap分区 随便进入一个目录用于后续存放 Swap 文件,也可以直接放在根目录,这里放在 /var 目录下。 ```shell cd /var ``` 使用 dd 命令生成一个文件块,大小为自己想设置的 Swap 分区大小,这里生成一个名为 swapfile 的文件,大小设为 1G。 ```shell dd if=/dev/zero of=swapfile bs=1M count=1024 ``` 将该文件设为 Swap 文件(格式化)。 ```shell mkswap swapfile ``` 激活 Swap 文件(启用虚拟内存)。 ```shell swapon swapfile ``` 检查 Swap 是否正确。 ```shell swapon -s ``` 另外为了安全建议将交换分区文件权限设为 0600 或 0644 ,执行以下命令。 ```shell chmod 0600 swapfile ``` 此时已经成功添加了交换分区,可以使用 free 之类的命令查看验证。 但这样重启后 Swap 分区会丢失,需要重新激活才行,所以我们一般在 fstab 文件为其添加开机自动挂载设置。 ```shell vi /etc/fstab ``` 在最后增加以下内容: ```shell /var/swapfile swap swap defaults 0 0 ``` 也可以直接执行如下命令来添加: ```shell echo "/var/swapfile swap swap defaults 0 0" >> /etc/fstab ``` ## 删除 Swap 文件 首先将 Swap 文件取消激活: ```shell swapoff /var/swapfile ``` 然后删除我们设置的 Swap 文件: ```shell rm /var/swapfile ``` 最后再编辑 /fstab 文件删除掉自动挂载 Swap 的设置即可: ```shell vi /etc/fstab ```
powerfulyangTue, Dec 14, 2021 4:14 PM

How Nginx Decides Which Server Block Will Handle a Request

--- title: How Nginx Decides Which Server Block Will Handle a Request author: powerfulyang date: 2020-12-24 tags: - nginx --- > Respect to [Understanding Nginx Server and Location Block Selection Algorithms](https://www.digitalocean.com/community/tutorials/understanding-nginx-server-and-location-block-selection-algorithms#:~:text=First%2C%20Nginx%20looks%20at%20the,server%20block%20will%20respond%20to.) ## Parsing the “server_name” Directive to Choose a Match Next, to further evaluate requests that have equally specific listen directives, Nginx checks the request’s “Host” header. This value holds the domain or IP address that the client was actually trying to reach. ## Matching Location Blocks Similar to the process that Nginx uses to select the server block that will process a request, Nginx also has an established algorithm for deciding which location block within the server to use for handling requests. ### Location Block Syntax Location blocks generally take the following form: ```nginx location optional_modifier location_match { . . . } ``` The modifiers below will cause the associated location block to be interpreted as follows: + `(none)` : If no modifiers are present, the location is interpreted as a prefix match. This means that the location given will be matched against the beginning of the request URI to determine a match. + `=` : If an equal sign is used, this block will be considered a match if the request URI exactly matches the location given. = 开头表示精确匹配,只有完全匹配上才能生效 + `~` : If a tilde modifier is present, this location will be interpreted as a case-sensitive regular expression match. ~ 开头表示区分大小写的正则匹配 + `~*` : If a tilde and asterisk modifier is used, the location block will be interpreted as a case-insensitive regular expression match. ~* 开头表示不区分大小写的正则匹配 + `^~` : If a carat and tilde modifier is present, and if this block is selected as the best non-regular expression match, regular expression matching will not take place. ^~ 开头对URL路径进行前缀匹配,并且在正则之前。 + `/uri` : 不带任何修饰符,也表示前缀匹配,但是在正则匹配之后 + `/` : 通用匹配,任何未匹配到其它location的请求都会匹配到,相当于switch中的default ### When Does Location Block Evaluation Jump to Other Locations? Another instance where the processing location may be reevaluated is with the try_files directive. This directive tells Nginx to check for the existence of a named set of files or directories. The last parameter can be a URI that Nginx will make an internal redirect to. Consider the following configuration: ```nginx root /var/www/main; location / { try_files $uri $uri.html $uri/ /fallback/index.html; } location /fallback { root /var/www/another; } ``` ### proxy_pass 反向代理配置中 url 后面加不加 / 的说明 #### path 后面加 / ```nginx location /proxy/ { proxy_pass http://192.168.1.5:8090/; } ``` 这样,访问 http://192.168.1.23/proxy/ 就会被代理到 http://192.168.1.5:8090/ 匹配的proxy目录不需要存在根目录 /var/www/html 里面。 **注意** + 终端里如果访问 http://192.168.1.23/proxy (即后面不带 "/"),则会访问失败!因为 proxy_pass 配置的 url 后面加了 "/" + 浏览器访问 http://103.110.186.23/proxy 的时候,会自动加上 "/"(同理是由于 proxy_pass 配置的 url 后面加了 "/"),并反代到 http://103.110.186.5:8090 的结果。 ```nginx location /proxy/ { proxy_pass http://192.168.1.5:8090; } ``` 那么访问 http://192.168.1.23/proxy或http://192.168.1.23/proxy/,都会失败! 这样配置后,访问 http://192.168.1.23/proxy/ 就会被反向代理到 http://192.168.1.5:8090/proxy/ ```nginx location /proxy/ { proxy_pass http://192.168.1.5:8090/haha/; } ``` 这样配置的话,访问 http://103.110.186.23/proxy/ 代理到 http://192.168.1.5:8090/haha/ ```nginx location /proxy/ { proxy_pass http://192.168.1.5:8090/haha; } ``` 上面配置后,访问 http://192.168.1.23/proxy/index.html 就会被代理到 http://192.168.1.5:8090/hahaindex.html 同理,访问 http://192.168.1.23/proxy/test.html 就会被代理到 http://192.168.1.5:8090/hahatest.html 注意,这种情况下,不能直接访问 http://192.168.1.23/proxy/ 后面就算是默认的 index.html 文件也要跟上,否则访问失败! #### path路径后面不带 / ```nginx location /proxy { proxy_pass http://192.168.1.5:8090/; } ``` ```nginx location /proxy { proxy_pass http://192.168.1.5:8090; } ``` 这样配置的话,浏览器访问 http://103.110.186.23/proxy 会自动加上 "/"( 即变成 http://103.110.186.23/proxy/ ),代理到 http://192.168.1.5:8090/proxy/ ```nginx location /proxy { proxy_pass http://192.168.1.5:8090/haha/; } ``` 这样配置的话,浏览器访问 http://103.110.186.23/proxy 会自动加上 "/"( 即变成 http://103.110.186.23/proxy/ ),代理到 http://192.168.1.5:8090/haha/ ```nginx location /proxy { proxy_pass http://192.168.1.5:8090/haha; } ``` 这样配置的话,浏览器访问 http://103.110.186.23/proxy ,和第三种结果一样,同样被代理到 http://192.168.1.5:8090/haha/
powerfulyangTue, Dec 14, 2021 4:11 PM

bubbling and capturing

--- title: bubbling and capturing author: powerfulyang date: 2020-12-22 tags: - javascript --- ## bubbling + `stopPropagation` prevents further propagation of the current event in the capturing and bubbling phases. 如果一个元素在一个事件上有多个处理程序,即使其中一个停止冒泡,其他处理程序仍会执行。 换句话说,`event.stopPropagation()` 停止向上移动,但是当前元素上的其他处理程序都会继续运行。 有一个 `event.stopImmediatePropagation()` 方法,可以用于停止冒泡,并阻止当前元素上的处理程序运行。使用该方法之后,其他处理程序就不会被执行。 + `preventDefault` prevents the default action the browser makes on that event. + 使用 css `pointer-events: none` 实现点击穿透 ```html <div class='parent' onclick='alert(`parent`)'> <div class='child' onclick='clickChild(event)'>child</div> </div> <script> function clickChild(e) { alert('child'); e.stopPropagation(); } </script> ``` + `stopImmediatePropagation` 如果多个事件监听器被附加到相同元素的相同事件类型上,当此事件触发时,它们会按其被添加的顺序被调用。 如果在其中一个事件监听器中执行 `stopImmediatePropagation()` ,那么剩下的事件监听器都不会被调用,还会阻止冒泡。 ### 不必要的 stopPropagation 不要在没有需要的情况下停止冒泡! 冒泡很方便。不要在没有真实需求时阻止它:除非是显而易见的,并且在架构上经过深思熟虑的。 有时 `event.stopPropagation()` 会产生隐藏的陷阱,以后可能会成为问题。 例如: 1. 我们创建了一个嵌套菜单,每个子菜单各自处理对自己的元素的点击事件,并调用 `stopPropagation`,以便不会触发外部菜单。 2. 之后,我们决定捕获在整个窗口上的点击,以追踪用户的行为(用户点击的位置)。有些分析系统会这样做。通常,代码会使用 `document.addEventListener('click'…)` 来捕获所有的点击。 3. 我们的分析不适用于被 `stopPropagation` 所阻止点击的区域。太伤心了,我们有一个“死区”。 通常,没有真正的必要去阻止冒泡。一项看似需要阻止冒泡的任务,可以通过其他方法解决。其中之一就是使用自定义事件,稍后我们会介绍它们此外,我们还可以将我们的数据写入一个处理程序中的 `event` 对象,并在另一个处理程序中读取该数据,这样我们就可以向父处理程序传递有关下层处理程序的信息。 ## capturing [DOM 事件](http://www.w3.org/TR/DOM-Level-3-Events/)标准描述了事件传播的 3 个阶段: 1. 捕获阶段(Capturing phase)—— 事件(从 Window)向下走近元素。 2. 目标阶段(Target phase)—— 事件到达目标元素。 3. 冒泡阶段(Bubbling phase)—— 事件从元素上开始冒泡。 使用 `on<event>` 属性或使用 HTML 特性(attribute)或使用两个参数的 `addEventListener(event, handler)` 添加的处理程序,对捕获一无所知,它们仅在第二阶段和第三阶段运行。 为了在捕获阶段捕获事件,我们需要将处理程序的 `capture` 选项设置为 `true`: ```js elem.addEventListener(..., {capture: true}) // 或者,用 {capture: true} 的别名 "true" elem.addEventListener(..., true) ``` ### bubbling vs capturing ```html <style> body * { margin: 10px; border: 1px solid blue; } </style> <form>FORM <div>DIV <p>P</p> </div> </form> <script> for(let elem of document.querySelectorAll('*')) { elem.addEventListener("click", e => alert(`Capturing: ${elem.tagName}`), true); elem.addEventListener("click", e => alert(`Bubbling: ${elem.tagName}`)); } </script> ``` 上面这段代码为文档中的 **每个** 元素都设置了点击处理程序,以查看哪些元素上的点击事件处理程序生效了。 如果你点击了 `<p>`,那么顺序是: 1. `HTML` → `BODY` → `FORM` → `DIV`(捕获阶段第一个监听器): 2. `P`(目标阶段,触发两次,因为我们设置了两个监听器:捕获和冒泡) 3. `DIV` → `FORM` → `BODY` → `HTML`(冒泡阶段,第二个监听器)。 每个处理程序都可以访问 `event` 对象的属性: * `event.target` —— 引发事件的层级最深的元素。 * `event.currentTarget`(=`this`)—— 处理事件的当前元素(具有处理程序的元素) * `event.eventPhase` —— 当前阶段(capturing=1,target=2,bubbling=3)。
powerfulyangTue, Dec 14, 2021 4:10 PM

HTTP STATUS

--- title: HTTP STATUS author: powerfulyang date: 2020-12-22 posterId: 46041 tags: - HTTP STATUS --- ## 30x + 301 redirect 变成 GET请求, Moved Permanently + 302 redirect 变成 GET请求, Temporary Redirect + 303 redirect 变成 GET请求, 被 302 包含 + 307 redirect request method 不变, Temporary Redirect + 308 redirect request method 不变, Permanent Redirect
powerfulyangTue, Dec 14, 2021 4:03 PM

Numerical representation in computer

--- title: Numerical representation in computer author: powerfulyang date: 2020-12-16 posterId: 46044 tags: - 计算机基础 --- >Respect to [On the Primitive Code, Inverse Code, Complement Code and Shift Code of Operating System](https://developpaper.com/on-the-primitive-code-inverse-code-complement-code-and-shift-code-of-operating-system/#:~:text=The%20positive%20complement%20is%20the,code\)%20%2B1%20%3D%2011111111110.) and [原码, 反码, 补码 详解(博客园)](https://www.cnblogs.com/zhangziqiu/archive/2011/03/30/computercode.html) and [原码、反码、补码及位操作符,C语言位操作详解](http://c.biancheng.net/view/290.html) ## Binary machine code Any data exists in the form of binary machine code in a computer. The first number is used to represent the positive and negative values: 0 is positive and 1 is negative. > in the form of: 以...形式 ### Numerical representation in computer + Original Code 原码 + Inverse Code 反码 + Complement Code 补码 + ~~Frameshift 移码~~ (是一种操作,非数值表示法 移码是为了不同的特定目的而存在的,手段是移动部分或全部数位。例如上面有人提到的浮点数表示法使用的阶码,阶码是用补码表示的,但由于补码存在符号位,不利于比较,就把负数部分整个往上移动,使得最小的数是0,这样一来比较阶码的时候就不需要考虑符号位了。另一个常见的例子是补码,通过移位把反码的负0变成最小的负数。) ### Original Code The value represented by machine code is called the original code. ### Inverse Code Inverse code is generally the least useful representation of computer. The inverse code of positive numbers is the original code itself. The inverse code of negative numbers: the number of the sign bits is the first bit remains unchanged **(符号位保持不变)**, and the rest of the bits are reversed. For example, if the original code of + 2 is 00000010, then the inverse code of + 2 is 00000010. The original code of – 2 is 10000010, and the inverse code is 1111101. ### Complement Code Complement is one of the most frequently used encoding methods in computer numerical operations. The positive complement is the original code itself, which is the same as the inverse code. The complement of negative number is the result of it's inverse code + 1. For example, the complement of + 2 is 00000010. The complement of – 2: 11111111101 (counter code) +1 = 11111111110. ### Frameshift Shift code is to add a bias constant to the value. When the number of coding bits is n, it usually shifts to the N-1 power of 2. For example, the shift code of – 2 = the 7th power of the original code + 2 = 00000010 + 10000000 = 10000010, which can be used as the complement of – 2 11111110 `Sign bits remain unchanged and the remaining bits are reversed + 1`。 ## Why are there complements and countercodes? 首先, 因为人脑可以知道第一位是符号位, 在计算的时候我们会根据符号位, 选择对真值区域的加减. (真值的概念在本文最开头). 但是对于计算机, 加减乘数已经是最基础的运算, 要设计的尽量简单. 计算机辨别"符号位"显然会让计算机的基础电路设计变得十分复杂! 于是人们想出了将符号位也参与运算的方法. 我们知道, 根据运算法则减去一个正数等于加上一个负数, 即: 1-1 = 1 + (-1) = 0 , 所以机器可以只有加法而没有减法, 这样计算机运算的设计就更简单了. 于是人们开始探索 将符号位参与运算, 并且只保留加法的方法. 首先来看原码: 计算十进制的表达式: 1-1=0 1 - 1 = 1 + (-1) = [00000001]原 + [10000001]原 = [10000010]原 = -2 如果用原码表示, 让符号位也参与计算, 显然对于减法来说, 结果是不正确的.这也就是为何计算机内部不使用原码表示一个数. 为了解决原码做减法的问题, 出现了反码: 计算十进制的表达式: 1-1=0 1 - 1 = 1 + (-1) = [0000 0001]原 + [1000 0001]原= [0000 0001]反 + [1111 1110]反 = [1111 1111]反 = [1000 0000]原 = -0 发现用反码计算减法, 结果的真值部分是正确的. 而唯一的问题其实就出现在"0"这个特殊的数值上. 虽然人们理解上+0和-0是一样的, 但是0带符号是没有任何意义的. 而且会有[0000 0000]原和[1000 0000]原两个编码表示0. 于是补码的出现, 解决了0的符号以及两个编码的问题: 1-1 = 1 + (-1) = [0000 0001]原 + [1000 0001]原 = [0000 0001]补 + [1111 1111]补 = [0000 0000]补=[0000 0000]原 这样0用[0000 0000]表示, 而以前出现问题的-0则不存在了.而且可以用[1000 0000]表示-128: (-1) + (-127) = [1000 0001]原 + [1111 1111]原 = [1111 1111]补 + [1000 0001]补 = [1000 0000]补 -1-127的结果应该是-128, 在用补码运算的结果中, [1000 0000]补 就是-128. 但是注意因为实际上是使用以前的-0的补码来表示-128, 所以-128并没有原码和反码表示.(对-128的补码表示[1000 0000]补算出来的原码是[0000 0000]原, 这是不正确的) 使用补码, 不仅仅修复了0的符号以及存在两个编码的问题, 而且还能够多表示一个最低数. 这就是为什么8位二进制, 使用原码或反码表示的范围为[-127, +127], 而使用补码表示的范围为[-128, 127]. 因为机器使用补码, 所以对于编程中常用到的32位int类型, 可以表示范围是: [-2^31, 2^31-1] 因为第一位表示的是符号位.而使用补码表示时又可以多保存一个最小值. ## 位运算 **因为位运算的操作值要求是整数,其结果也是整数,所以经过位运算的都会自动变成整数** 在JavaScript中可以通过两次取反 `~~`去除小数部分,~~1.1 === 1, ~~(-.1.1) === 1 |运算符| 功 能 | 运算规则| |--------|---------|-------------| |& |按位与 | 对应位均为 1 时,结果才为 1| |\| |按位或 | 两位中只要有一位为 1,结果为 1。只有两位同时为 0 时,结果为才为 0。| |^ |按位异或 | 两位相异时,结果为 1;两位相同时,结果为 0。| |<< |左移 | 将运算数的各二进制位均左移若干位,高位丢弃(不包括 1),低位补 0,每左移一位,相当于该数乘以 2。| |>> |右移 | 将运算数的各二进制位均右移若干位,正数补左补 0,负数左补 1,右边移出的位丢弃。| |~ |按位取反 | 0 变 1,1 变 0。| ### 二进制 十六进制表示法 |十进制| 二进制| 十六进制| |-|-|- |0| 0b0| 0x00| |1| 0b1| 0x01| |2| 0b10| 0x02| |3| 0b11| 0x03|
powerfulyangTue, Dec 14, 2021 4:02 PM

浏览器的重排和重绘

--- title: 浏览器的重排和重绘 author: powerfulyang date: 2020-11-22 tags: - reflow - repaint --- ## 浏览器渲染 浏览器在渲染页面的时候,大致是以下几个步骤: 1. 解析 html 生成 DOM 树,解析 css,生成 CSSOM 树,将 DOM 树和 CSSOM 树结合,生成渲染树; 2. 根据渲染树,浏览器可以计算出网页中有哪些节点,各节点的CSS以及从属关系 - 回流 3. 根据渲染树以及回流得到的节点信息,计算出每个节点在屏幕中的位置 - 重绘 4. 最后将得到的节点位置信息交给浏览器的图形处理程序,让浏览器中显示页面 ## reflow 回流或重排 或者 layout 回流:英文叫reflow,指的是当渲染树中的节点信息发生了大小、边距等问题,需要重新计算各节点和css具体的大小和位置。 layout(reflow)一般被称为布局,这个操作是用来计算文档中元素的位置和大小,是渲染前重要的一步。在HTML第一次被加载的时候,会有一次layout之外,js脚本的执行和样式的改变同样会导致浏览器执行layout。 ## repaint 重绘 或者 paint 重绘:英文叫repaint,当节点的部分属性发生变化,但不影响布局,只需要重新计算节点在屏幕中的绝对位置并渲染的过程,就叫重绘。比如:改变元素的背景颜色、字体颜色等操作会造成重绘。 回流的过程在重绘的过程前面,所以回流一定会重绘,但重绘不一定会引起回流。 每次回流都会对浏览器造成额外的计算消耗,所以浏览器对于回流和重绘有一定的优化机制。浏览器通常都会将多次回流操作放入一个队列中,等过了一段时间或操作达到了一定的临界值,然后才会挨个执行,这样能节省一些计算消耗。但是在获取布局信息操作的时候,会强制将队列清空,也就是强制回流,比如访问或操作以下或方法时: * offsetTop * offsetLeft * offsetWidth * offsetHeight * scrollTop * scrollLeft * scrollWidth * scrollHeight * clientTop * clientLeft * clientWidth * clientHeight * getComputedStyle() 这些属性或方法都需要得到最新的布局信息,所以浏览器必须去回流执行。因此,在项目中,尽量避免使用上述属性或方法,如果非要使用的时候,也尽量将值缓存起来,而不是一直获取。 ## 减少回流和重绘 ### 合并样式修改 减少造成回流的次数,如果要给一个节点操作多个css属性,而每一个都会造成回流的话,尽量将多次操作合并成一个 ```js var oDiv = document.querySelector('.box'); oDiv.style.padding = '5px'; oDiv.style.border = '1px solid #000'; oDiv.style.margin = '5px'; ``` + 使用 style 的 cssText ```js oDiv.style.cssText = 'padding:5px; border:1px solid #000; margin:5px;'; ``` + 使用 class ### 批量操作元素 当对DOM有多次操作的时候,需要使用一些特殊处理减少触发回流,其实就是对 DOM 的多次操作,在脱离标准流后,对元素进行的多次操作,不会触发回流,等操作完成后,再将元素放回标准流。 **脱离标准流的操作有以下3种:** 1. 隐藏元素 `display: none` 2. 使用文档碎片 `createDocumentFragment` 3. ~~拷贝节点 有大病~~ ## HTMLCollection 问题 缓存选择器的结果,减少DOM查询。这里要特别提下 HTMLCollection。HTMLCollection 是通过 `document.getElementByTagName` 得到的对象类型,和数组类型很类似但是每次获取这个对象的一个属性,都相当于进行一次DOM查询: ```js var divs = document.getElementsByTagName("div"); for (var i = 0; i < divs.length; i++){ //infinite loop document.body.appendChild(document.createElement("div")); } ``` 比如上面的这段代码会导致无限循环,所以处理HTMLCollection对象的时候要做些缓存。 所有的 "getElementsBy*" 方法都会返回一个 实时的(live) 集合。这样的集合始终反映的是文档的当前状态,并且在文档发生更改时会“自动更新”。 在下面的例子中,有两个脚本。 第一个创建了对 <div> 的集合的引用。截至目前,它的长度是 1。 第二个脚本在浏览器再遇到一个 <div> 时运行,所以它的长度是 2。 ```html <div>First div</div> <script> let divs = document.getElementsByTagName('div'); alert(divs.length); // 1 </script> <div>Second div</div> <script> alert(divs.length); // 2 </script> ``` 相反,`querySelectorAll` 返回的是一个 **静态的** 集合。就像元素的固定数组。 如果我们使用它,那么两个脚本都会输出 `1`: ```html <div>First div</div> <script> let divs = document.querySelectorAll('div'); alert(divs.length); // 1 </script> <div>Second div</div> <script> alert(divs.length); // 1 </script> ``` ## DOM 操作很慢,主要是读操作很慢 一般情况下,浏览器的layout是lazy的,也就是说:在js脚本执行时,是不会去更新DOM的,任何对DOM的修改都会被暂存在一个队列中,在当前js的执行上下文完成执行后,会根据这个队列中的修改,进行一次layout。 然而有时希望在js代码中立刻获取最新的DOM节点信息,浏览器就不得不提前执行layout,这是导致DOM性能问题的主因。 如下的操作会打破常规,并触发浏览器执行layout * 通过js获取需要计算的DOM属性 * 添加或删除DOM元素 * resize浏览器窗口大小 * 改变字体 * css伪类的激活,比如:hover * 通过js修改DOM元素样式且该样式涉及到尺寸的改变 **部分读操作,导致 lazy reflow 失效这是导致 DOM 性能的主要原因**
powerfulyangTue, Dec 14, 2021 1:32 PM

Use acme.sh to get certificate

--- title: Use acme.sh to get certificate author: powerfulyang date: 2021-12-13 posterId: 45896 tags: - acme.sh --- ## Install from web ```bash curl https://get.acme.sh | sh ``` ## Example for Tencent Cloud (DNSPod) 1. click to [generate secret key](https://console.dnspod.cn/account/token) 2. use this script to get cert and add hook when certificate has renewed to reload your web server ```bash export DP_Id="1234" // get from https://console.dnspod.cn/account/token export DP_Key="sADDsdasdgdsf" acme.sh --issue --dns dns_dp -d example.com \n -d *.example.com --renew-hook "command to reload your web server" ``` ## 一些相关的小事情 + `acme.sh 默认 CA 切换到 ZoreSSL` 改回到 `Let's Encrypt` ```bash acme.sh --set-default-ca --server letsencrypt ``` + `有时候 corncob 会失败` ```bash acme.sh --cron --home ~/.acme.sh ``` 查看日志, 类似如下内容。 ```bash [2021年 12月 14日 星期二 10:56:23 CST] ===Starting cron=== [2021年 12月 14日 星期二 10:56:23 CST] Renew: 'powerfulyang.com' [2021年 12月 14日 星期二 10:56:23 CST] Skip, Next renewal time is: 2021年 12月 24日 星期五 10:23:57 UTC [2021年 12月 14日 星期二 10:56:23 CST] Add '--force' to force to renew. [2021年 12月 14日 星期二 10:56:23 CST] Skipped powerfulyang.com [2021年 12月 14日 星期二 10:56:23 CST] ===End cron=== ```
powerfulyangMon, Dec 13, 2021 11:10 PM

安装oh my zsh

--- title: 安装oh my zsh posterId: 46029 tags: - zsh - terminal --- > 官网 https://ohmyz.sh/ ## Install oh-my-zsh ```shell sh -c "$(curl -fsSL https://raw.github.com/ohmyzsh/ohmyzsh/master/tools/install.sh)" ```` ## Mac 安装 FiraCode 字体并修改 Iterm 字体 + 从 github 下载 `git clone https://github.com/tonsky/FiraCode.git` + 进入 /distr/ttf 目录全选字体 右键 open with font book + 可以顺便修改 Iterm 的字体为 FiraCode ## 换一个好看的 Iterm 配色 下载 [Solarized Dark Patched](https://raw.githubusercontent.com/mbadolato/iTerm2-Color-Schemes/master/schemes/Solarized%20Dark%20-%20Patched.itermcolors) 保存为itermcolors后缀 ## 换一个好看的 oh-my-zsh 主题 `vim ~/.zshrc` 修改 ZSH_THEME 为 `ZSH_THEME="agnoster"` ## Install useful plugins click [plugins](https://github.com/ohmyzsh/ohmyzsh/tree/master/plugins) to see. `vim ~/.zshrc` 修改 plugins 为 `plugins=(git zsh-autosuggestions zsh-syntax-highlighting sudo)` ### 安装 zsh-syntax-highlighting ```shell git clone https://github.com/zsh-users/zsh-syntax-highlighting.git echo "source ${(q-)PWD}/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh" >> ${ZDOTDIR:-$HOME}/.zshrc git clone https://github.com/zsh-users/zsh-syntax-highlighting.git ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-syntax-highlighting ``` ### 安装 zsh-autosuggestions ```shell git clone https://github.com/zsh-users/zsh-autosuggestions ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-autosuggestions ```
powerfulyangFri, Oct 29, 2021 7:35 PM

Git: How do I force "git pull" to overwrite local files?

--- title: 'Git: How do I force "git pull" to overwrite local files?' --- ## How to overwrite First, run a fetch to update all origin/<branch> refs to latest: `git fetch --all` Backup your current branch: `git branch backup-master` Then, you have two options: `git reset --hard origin/master` OR If you are on some other branch: `git reset --hard origin/<branch_name>` ## Explanation `git fetch` downloads the latest from remote without trying to merge or rebase anything. Then the git reset resets the master branch to what you just fetched. The `--hard` option changes all the files in your working tree to match the files in origin/master
powerfulyangFri, Oct 29, 2021 5:37 PM

vertical-align

--- title: vertical-align posterId: 46026 tags: - 只是一个笔记 - css - 我以前居然没发现 --- > https://developer.mozilla.org/en-US/docs/Web/CSS/vertical-align ## Values for inline elements + `vertical-align` 的默认值是 `baseline` + inherited: no, 不从父元素继承 ### Parent-relative values These values vertically align the element relative to its parent element: + `baseline` Aligns the baseline of the element with the baseline of its parent. The baseline of some replaced elements, like <textarea>, is not specified by the HTML specification, meaning that their behavior with this keyword may vary between browsers. + `sub` Aligns the baseline of the element with the subscript-baseline of its parent. + `super` Aligns the baseline of the element with the superscript-baseline of its parent. + `text-top` Aligns the top of the element with the top of the parent element's font. + `text-bottom` Aligns the bottom of the element with the bottom of the parent element's font. + `middle` Aligns the middle of the element with the baseline plus half the x-height of the parent. + `<length>` Aligns the baseline of the element to the given length above the baseline of its parent. A negative value is allowed. + `<percentage>` Aligns the baseline of the element to the given percentage above the baseline of its parent, with the value being a percentage of the line-height property. A negative value is allowed. 百分比值不是相对于字体大小或者其他什么属性计算的,而是相对于 `line-height` 计算的。 举个简单的例子,如下 CSS 代码: ```css { line-height: 30px; vertical-align: -10%; } ``` 实际上,等同于: ```css { line-height: 30px; vertical-align: -3px; /* = 30px * -10% */ } ``` ### Line-relative values The following values vertically align the element relative to the entire line: + `top` Aligns the top of the element and its descendants with the top of the entire line. + `bottom` Aligns the bottom of the element and its descendants with the bottom of the entire line. For elements that do not have a baseline, the bottom margin edge is used instead. ### Values for table cells + `baseline` (and `sub`, `super`, `text-top`, `text-bottom`, `<length>`, and `<percentage>`) Aligns the baseline of the cell with the baseline of all other cells in the row that are baseline-aligned. + `top` Aligns the top padding edge of the cell with the top of the row. + `middle` Centers the padding box of the cell within the row. + `bottom` Aligns the bottom padding edge of the cell with the bottom of the row. --- `The vertical-align CSS property sets vertical alignment of an inline, inline-block or table-cell box.` ~我终于懂了~ `vertical-align` 的场景: + 使行内元素盒模型与其行内元素容器垂直对齐 + 垂直对齐表格单元内容 注意 `vertical-align` 只对行内元素、行内块元素和表格单元格元素生效:不能用它垂直对齐[块级元素](https://developer.mozilla.org/zh-CN/docs/Web/HTML/Block-level_elements)。 ### baseline 是怎么样的? 下图`基线`所指向的就是 baseline, 默认元素的基线同**基准元素**(取行高最高的作为基准)的基线对齐。 ![source protected](47809) ### 关于图片默认间隙的问题? **下面 demo 的第四个例子**,因为 vertical-align 默认值是 baseline, 图片的最下边源被认为是基线所在的位置 #### 一些问题记录 **第二、四、七、九个例子**,`Line-relative values` top, bottom 等值相对整行垂直对齐,不会使接下来的元素向他们的基线对齐,即不会修改基线。 **第一、三、六、八个例子**,`Parent-relative values` 会使接下来的元素向他们的基线对齐,即会修改基线。 ```codepen demo: <iframe height="900" style="width: 100%;" scrolling="no" title="vertical-align demo" src="https://codepen.io/powerfulyang/embed/vYjbLbY?default-tab=result" frameborder="no" loading="lazy" allowtransparency="true" allowfullscreen="true"> See the Pen <a href="https://codepen.io/powerfulyang/pen/vYjbLbY"> vertical-align demo</a> by powerfulyang (<a href="https://codepen.io/powerfulyang">@powerfulyang</a>) on <a href="https://codepen.io">CodePen</a>. </iframe> <br/> ``` ## line-height line-height的默认值是normal,同时还支持数值、百分比值、长度值、继承。请看下面的表格: | 值 | 描述 | | --- | --- | | normal | 默认。设置合理的行间距。 | | number | 设置数字,此数字会与当前的字体尺寸相乘来设置行间距,即number为当前font-size的倍数。 | | length | 设置固定的行间距。 | | % | 基于当前字体尺寸的百分比行间距。 | | inherit | 规定应该从父元素继承 line-height 属性的值。 | ### line-height 继承情况下 不同属性下的 `line-height` 最终的计算方式比较如下。 | 设置方式 | line-height | 计算后的line-height | 子元素继承的line-height | | --- | --- | --- | --- | | inherit | 父元素的line-height值 | 不用计算 | 父元素的line-height值 | | length | 20px | 不用计算 | 20px | | % | 150% | 自身font-size (14px) \* 150% = 21px | 继承父元素计算后的line-height值 21px,而不是150% | | normal | 假如为1.2 | 自身font-size (16px) \* 1.2 = 19.2px | 继承1.2,line-height = 自身font-size(32px) \* 1.2 = 38.4px | | 纯数字 | 1.5 | 自身font-size (14px) \* 1.5 = 21px | 继承1.5,line-height = 自身font-size(32px) \* 1.5 = 48px | 所以,在实际开发中, 我们一般设置行高的值为 `纯数字是最推荐的方式,因为其会随着对应的 font-size 而缩放,排版效果良好。 ### line-height 与内联元素垂直居中 **行高等于容器高,外加 vertical-align: middle 可以设置单行文本的垂直居中**
powerfulyangSat, Oct 9, 2021 11:30 AM