CMS

分类: 计算机

  • 【React】学习笔记-基础篇

    虚拟 DOM 的原理是什么?为什么比直接操作 DOM 高效?

    • 虚拟 DOM 是一个轻量的 JS 对象,用来描述真实 DOM 的层次和属性。
    • 通过 Diff 算法最小化 DOM 操作,减少重绘和回流。

    JSX 是什么?和普通 JavaScript 的区别?

    • JSX 是语法糖,最终会被 Babel 编译为 React.createElement() 调用。

    数据流

    • 单向数据流,自上而下传递。
    • 父组件通过 props 把数据传递给子组件;
    • 子组件可以通过回调函数通知父组件。

    Props

    • PropTypes 定义类型
    • defaultProps 定义默认值
    • this.props.children 表示组件的所有子节点
    • 不要把 props 复制到 state 中,要尽可能把 props 当作数据源(避免反模式)

    高阶组件(HOC)

    • 用于复用组件逻辑。本质上是一个函数,通过接收一个组件并返回一个新组件

    PureComponent 和 React.memo

    • PureComponent 会对 props 和 state 进行浅比较
    • memo 只对 props 进行浅比较

    `

    ` 和 “ (空标签)的区别 – 只有 ` ` 可以带 key 属性,这在列表渲染时非常重要 #### 类组件和函数组件的区别? – 语法与结构 “`js class MyComponent extends React.Component { render() { return
    Hello, {this.props.name}
    ; } } function MyComponent(props) { return
    Hello, {props.name}
    ; } “` – 状态管理 – 类组件通过 `this.state` 定义状态,`this.setState()` 更新状态 – 函数组件使用 Hooks 的 `useState` 管理状态 – 生命周期与副作用 – 类组件通过生命周期处理副作用 – 函数组件通过 `useEffect` 替代生命周期 – `this` 的绑定问题 – 类组件通过手动绑定事件函数的 `this` “`js class Button extends React.Component { handleClick() { console.log(this); } // this 可能为 undefined render() { return ; } } “` – 函数组件没有 this,直接访问 props 和状态 “`js function Button() { const handleClick = () => console.log(“No this!”); return ; } “` – 性能优化 – 类组件使用 `shouldComponentUpdate` 或 `PureComponent` 避免不必要的渲染 – 函数组件使用 `React.memo` 进行浅比较 ### 【状态管理】 #### React 中的状态提升(Lifting State Up)是什么? – 把状态提升到父组件进行管理,子组件通过 props 读取数据。 #### Context API 的作用?如何避免不必要的渲染? – Context(上下文) 是 React 提供的跨层级组件数据传递方案,用于解决多层组件嵌套时(逐层传递 props)的问题。 – 核心作用 – 跨组件共享数据(如主题、用户信息、全局配置等) – 避免中间组件透传 props(减少冗余代码) #### MobX / Redux – 全局状态管理,把状态抽离到组件之外。 **Redux** * 工作流程: 1. 定义 Store:集中管理全局状态(createStore)。 2. 派发 Action:组件通过 dispatch(action) 发出状态修改请求(action 是一个描述“发生了什么”的普通对象)。 3. 执行 Reducer:根据 action.type,纯函数 reducer 计算新状态(不可变更新)。 4. 更新视图:Store 通知订阅者(如 React 组件),触发重新渲染。 * 核心概念: – 单向数据流:View → Action → Reducer → Store → View – 三大原则:单一数据源、状态只读、纯函数修改。 * 总结: – 组件 dispatch → Reducer 处理 → Store 更新 → 视图同步 **MobX** * 工作流程: 1. 定义 Observable State:用 @observable 或 makeObservable 标记可变状态。 2. 修改 State:在 @action 函数中更新状态(直接赋值/修改对象属性)。 3. 自动追踪依赖:组件用 observer 包裹,自动订阅其依赖的状态。 4. 触发更新:状态变化时,依赖它的组件自动重渲染。 * 核心概念: – 响应式编程:像 Excel 公式一样自动更新(无需手动 dispatch/reducer)。 – 直接修改状态(但通过 action 规范变更)。 * 总结: – 响应式状态,自动追踪依赖,修改即更新 ### 【性能优化】 #### React 的渲染机制是怎样的?如何避免不必要的渲染? – 使用 React.memo、useMemo、useCallback、避免内联对象/函数。 #### React 的 key 属性有什么作用? – 帮助 React 识别元素变化,优化列表渲染。 #### 实现代码分割(Code Splitting) – 动态 import() 语法 – 动态 import() 是 ES2020 引入的语法,返回一个 Promise: “`js // 普通模块中使用 import(“./math”).then(math => { console.log(math.add(16, 26)); }); // React 组件中使用 import(“./OtherComponent”).then(OtherComponent => { // 使用加载的组件 }); “` – React.lazy + Suspense – React.lazy 和 Suspense 来实现组件的懒加载: “`js import React, { Suspense } from ‘react’; const OtherComponent = React.lazy(() => import(‘./OtherComponent’)); function MyComponent() { return (
    Loading…
    }> ); } “`
  • WEB前端-JS篇

    小数精度处理

    • 使用 toFixed 取后几位小数
    • 把小数转为整数再进行运算
    • 使用精度运算库,如:big.js

    for…infor…of 的区别

    • for-in 历遍对象,通常获取 key(类似 Object.keys)
    • for-of 历遍可迭代对象,如 ArrayMapSet

    任务队列(Task Queue)

    • 先执行同步再执行异步
    • 核心作用:处理异步代码的调度和执行
    • 宏任务:setTimeoutsetIntervalI/O
    • 微任务:Promise 回调(.then / .catch/ .finally)、MutationObserver 回调等
    • 微任务优先级高于宏任务
    • 调用 fetch 会同步执行并立即返回一个 promise

    call | apply | bind

    均用于改变函数内部 this 的指向

    const obj = { name: "Alice" };
    
    function greet() {
      console.log(`Hello, ${this.name}!`);
    }
    
    greet.call(obj);      // Hello, Alice!
    greet.apply(obj);     // Hello, Alice!
    const boundGreet = greet.bind(obj);
    boundGreet();         // Hello, Alice!

    ES 新特性:

    变量声明

    letconst

    箭头函数

    简化函数语法,自动绑定 this

    模板字符串

    `Hello ${name}`

    解构赋值

    const { age, name } = user;
    const [first, second] = [1, 2];

    默认参数

    扩展运算符

    const arr1 = [1, 2];
    const arr2 = [...arr1, 3]; // [1, 2, 3]; // 合并数组
    const objCopy = { ...originalObj }; // 合并对象

    Promise 和 异步处理

    Promise.all – 等待所有 Promise 对象都执行完成 Promise.race – 返回第一个完成的 Promise(无论成功失败) Promise.any – 返回第一个成功的 Promise

    模块化:importexport

    Set, Map

    let s = new Set(); s.add(‘a’).add(‘b’)
    let m = new Map(); m.set(’age’, 16)

    数组的 includes,检查是否包含某元素

    async / await

    底层实现机制: async/await 的核心是通过 Generator 函数和自动执行器实现的:

    1. async 函数本质上是一个返回 Promise 的 Generator 函数;
    2. await 相当于 yield,暂停函数执行;
    3. 有一个自动执行器负责处理 Promise 的 resolve/reject 并继续执行。

    Generator 函数的语法糖,省去手动 next() 的步骤

    Object.values() 和 Object.entries()

    Object.values({ a: 1, b: 2 }); // [1, 2]
    Object.entries({ a: 1, b: 2 }); // [["a", 1], ["b", 2]]

    字符串填充:padStart() 和 padEnd()

    "5".padStart(2, "0"); // "05"

    空值合并运算符

    const value = input ?? "default";

    动态导入模块

    const module = await import('./module.js')

    replaceAll 替换所有匹配的子串

    "a.b.c".replaceAll(".", "-"); // "a-b-c"

    require 和 import

    • 分别属于 CommonJS 和 ES 两种不同模块系统
    • require 动态加载,运行时解析
    • import 静态加载,编译时解析
    • Proxy
      • 数据绑定与响应式系统:通过 set 拦截属性赋值,触发副作用(如重新渲染)
      • 属性访问控制和校验:限制对象属性的访问或赋值

    微信小程序:

    OpenID & UnionID

    OpenID

    • 用户在当前小程序中的唯一标识
    • 用途:用于识别用户在当前小程序内的身份(如用户数据存储、业务逻辑处理)
    • 获取:通过 wx.login() 获取 code,后端用 code 调用微信接口换取 OpenID

    UnionID

    • 用户在微信的唯一标识
    • 用途:实现跨小程序、公众号用户身份统一
    • 获取:通过 wx.getUserInfo() 获取 encryptedData, iv,发送到后端解密,或者通过按钮触发授权

    性能优化:

    1. 文本压缩(css, js, html)
    2. 图片压缩
    3. 合并资源(合并文本、雪碧图)
    4. 图片懒加载
    5. 合并请求
    6. 缓存资源(设置浏览器头、设置 html head)
    7. 使用 CDN
    8. gzip 压缩
    9. 减少 DOM 操作
    10. CSS 样式置顶、JS 脚本置底
    11. 防抖和节流
      • 防抖:保证N秒内执行一次,如果N秒内触发将再执行并重新计算时间
      • 节流:保证N秒内只能触发一次
  • WEB前端-CSS篇

    CSS3 新增属性

    1. 选择器(Selectors)

    CSS3 新增了许多强大的选择器,可以更精确地匹配元素。

    选择器 示例 描述
    属性选择器 input[type="text"] 匹配具有特定属性的元素
    结构伪类 :nth-child(n) 匹配第 n 个子元素
    :first-of-type 匹配同类型的第一个元素
    :last-child 匹配最后一个子元素
    :not(selector) 排除匹配的元素
    UI 伪类 :checked 匹配被选中的表单元素(如复选框)
    :disabled 匹配禁用的表单元素
    :enabled 匹配可用的表单元素
    目标伪类 :target 匹配当前 URL 的锚点目标元素

    2. 盒模型(Box Model)

    CSS3 提供了更灵活的盒模型控制方式。

    属性 描述
    box-sizing: border-box 使 widthheight 包含 paddingborder
    resize 允许用户调整元素大小(bothhorizontalvertical
    outline-offset 设置轮廓(outline)与边框的偏移距离

    3. 背景与边框(Background & Border)

    CSS3 增强了背景和边框的样式控制。

    背景(Background)

    属性 描述
    background-size 控制背景图片大小(covercontain100% 100%
    background-clip 定义背景绘制区域(border-boxpadding-boxcontent-box
    background-origin 定义背景定位的基准区域
    background-attachment: local 背景随内容滚动
    background: url(img1.png), url(img2.png); 多背景

    边框(Border)

    属性 描述
    border-radius 圆角边框(border-radius: 10px;
    box-shadow 盒子阴影(box-shadow: 5px 5px 10px #888;
    border-image 使用图片作为边框(border-image: url(border.png) 30 round;

    4. 渐变(Gradients)

    CSS3 支持线性渐变和径向渐变。

    类型 示例 描述
    线性渐变 background: linear-gradient(to right, red, blue); 从左到右渐变
    径向渐变 background: radial-gradient(circle, red, yellow); 从中心向外渐变
    重复渐变 background: repeating-linear-gradient(45deg, red, red 10px, blue 10px, blue 20px); 重复渐变

    5. 过渡(Transitions)

    CSS3 过渡允许属性在一定时间内平滑变化。

    属性 描述
    transition-property 指定过渡的属性(如 widthopacity
    transition-duration 过渡持续时间(如 1s
    transition-timing-function 过渡速度曲线(easelinearcubic-bezier()
    transition-delay 过渡延迟时间
    简写 transition: width 1s ease 0.5s;

    6. 动画(Animations)

    CSS3 动画比过渡更强大,支持关键帧控制。

    属性 描述
    @keyframes 定义动画关键帧
    animation-name 指定动画名称
    animation-duration 动画持续时间
    animation-timing-function 动画速度曲线
    animation-delay 动画延迟时间
    animation-iteration-count 动画播放次数(infinite 表示无限循环)
    animation-direction 动画方向(normalreversealternate
    animation-fill-mode 动画结束后保持状态(forwardsbackwards
    简写 animation: move 2s ease infinite;

    示例:

    @keyframes move {
      0% { transform: translateX(0); }
      100% { transform: translateX(100px); }
    }
    div {
      animation: move 2s ease infinite;
    }

    7. 变形(Transforms)

    CSS3 变形允许对元素进行 2D/3D 变换。

    2D 变形:

    属性 描述
    transform: translate(x, y) 移动元素
    transform: rotate(45deg) 旋转元素
    transform: scale(1.5) 缩放元素
    transform: skew(30deg) 倾斜元素
    组合变换 transform: rotate(45deg) scale(1.2);

    3D 变形:

    属性 描述
    transform: translate3d(x, y, z) 3D 移动
    transform: rotateX(45deg) 绕 X 轴旋转
    transform: perspective(500px) 设置 3D 透视距离

    8. 弹性布局(Flexbox)

    CSS3 Flexbox 提供更灵活的布局方式。

    属性 描述
    display: flex 启用 Flex 布局
    flex-direction 主轴方向(rowcolumn
    justify-content 主轴对齐方式(centerspace-between
    align-items 交叉轴对齐方式(centerstretch
    flex-wrap 是否换行(wrapnowrap
    flex-grow 定义项目的放大比例
    flex-shrink 定义项目的缩小比例
    flex-basis 定义项目的初始大小

    9. 网格布局(Grid)

    CSS3 Grid 提供二维布局系统。

    属性 描述
    display: grid 启用 Grid 布局
    grid-template-columns 定义列宽(如 1fr 2fr
    grid-template-rows 定义行高
    grid-gap 定义行列间距
    grid-column / grid-row 定义项目占据的网格区域
    <!DOCTYPE html>
    <html>
    <head>
    
    <style>
            .grid-container {
                display: grid;
                grid-template-columns: 100px 200px auto; /* 三列:固定100px,固定200px,剩余空间 */
                gap: 10px;
                background-color: #2196F3;
                padding: 10px;
            }
    
            .grid-item {
                background-color: rgba(255, 255, 255, 0.8);
                border: 1px solid rgba(0, 0, 0, 0.8);
                padding: 20px;
                font-size: 30px;
                text-align: center;
            }
        </style>
    </head>
    <body>
        <div class="grid-container">
            <div class="grid-item">1</div>
            <div class="grid-item">2</div>
            <div class="grid-item">3</div>  
            <div class="grid-item">4</div>
            <div class="grid-item">5</div>
            <div class="grid-item">6</div>  
            <div class="grid-item">7</div>
            <div class="grid-item">8</div>
            <div class="grid-item">9</div>  
        </div>
    </body>
    </html>

    10. 媒体查询(Media Queries)

    CSS3 媒体查询允许针对不同设备应用不同样式。

    /* 屏幕宽度小于 600px 时生效 */
    @media (max-width: 600px) {
      body { background: lightblue; }
    }

    BEM 命名规范

    BEM(Block-Element-Modifier)是一种流行的 CSS 命名方法论,旨在提高代码可读性、可维护性和可复用性。其核心思想是通过模块化的方式组织 CSS 类名,避免样式冲突。

    1. BEM 基本结构

    BEM 将界面拆分为三个部分: 部分 描述 示例
    Block 独立的、可复用的组件(如按钮、卡片、导航栏)。 .btn.card
    Element 属于 Block 的子元素,不能单独使用(如按钮的图标、卡片的标题)。 .btn__icon.card__title
    Modifier 表示 Block 或 Element 的状态或变体(如禁用按钮、大号卡片)。 .btn--disabled.card--large

    2. 命名规则

    (1) Block(块)
    • 使用单一单词短横线连接-)的命名方式。
    • 代表一个独立的组件,不依赖其他元素。
    .header { ... }       /* 页眉 */
    .menu { ... }         /* 菜单 */
    .search-box { ... }   /* 搜索框 */
    (2) Element(元素)
    • 格式:Block名称__Element名称双下划线 __ 连接)。
    • 表示属于 Block 的一部分,不能脱离 Block 使用。
    .menu__item { ... }          /* 菜单项 */
    .search-box__input { ... }   /* 搜索框的输入框 */
    .card__title { ... }         /* 卡片的标题 */
    (3) Modifier(修饰符)
    • 格式:Block--ModifierBlock__Element--Modifier双短横线 -- 连接)。
    • 表示 Block 或 Element 的不同状态或样式变体。
    .button--primary { ... }     /* 主要按钮 */
    .button--disabled { ... }    /* 禁用按钮 */
    .menu__item--active { ... }  /* 当前选中的菜单项 */

    3. 实际代码示例

    HTML 结构

    <!-- Block: card -->
    <div class="card card--highlight">
      <!-- Element: card 的子元素 -->
      <h2 class="card__title">标题</h2>
      <p class="card__content">内容...</p>
      <!-- 带 Modifier 的 Element -->
      <button class="card__button card__button--large">点击</button>
    </div>

    CSS 样式

    /* Block */
    .card { 
      border: 1px solid #ccc;
      padding: 16px;
    }
    
    /* Modifier: 高亮卡片 */
    .card--highlight {
      background: #f5f5f5;
    }
    
    /* Element: 卡片标题 */
    .card__title {
      font-size: 18px;
      color: #333;
    }
    
    /* Element: 卡片按钮 */
    .card__button {
      padding: 8px 16px;
    }
    
    /* Modifier: 大号按钮 */
    .card__button--large {
      padding: 12px 24px;
    }
  • WEB前端-HTML篇

    新增标签元素

    • 语义化结构标签:
    ,
    ,
  • 【JS】判断登录设备是移动端还是PC端

    基于 navigator.userAgent 的 User Agent 检测

    function isMobileDevice () {
      const ua = navigator.userAgent || navigator.vendor || window.opera;
      const mobileRegex = /android|iphone|ipad|ipod|blackberry|iemobile|opera mini|mobile|windows phone|phone|webos|kindle|tablet/i;
      return mobileRegex.test(ua.toLowerCase());
    }

    通过 navigator.userAgent 获取浏览器标识字符串‌。正则表达式匹配移动端关键词(如 android、iphone、mobile 等)‌。返回 true 表示移动端,false 表示 PC 端。

    ‌基于 navigator.userAgent 的插件方案‌

    插件:mobile-detect.js

    插件通过解析 navigator.userAgent 中的设备标识符(如 Android、iPhone、Windows 等)实现设备判断‌。 支持更细分的设备类型检测(如平板、手机、PC)‌。

    import MobileDetect from 'mobile-detect';
    
    const md = new MobileDetect(navigator.userAgent);
    
    if (md.mobile()) {
      console.log("移动端登录设备(手机/平板)");
    } else if (md.tablet()) {
      console.log("平板设备");
    } else {
      console.log("PC 端设备");
    }
  • MIDI音符代码表

    MIDI 音符代码是用于表示音符的数字系统,范围从0到127,对应不同的音高和八度。具体如下:

    1. 音符编号:每个音符对应一个唯一的编号,例如:

      • C4(中央C)的编号为60。
      • C5的编号为72。
    2. 音高与编号

      • 编号0对应C-1(最低音)。
      • 编号127对应G9(最高音)。
    3. 八度划分

      • 每增加12,编号提高一个八度。例如,C4为60,C5为72。
    4. 半音关系

      • 相邻编号相差一个半音。例如,C4为60,C#4/Db4为61。
    5. 常用音符示例

      • C4: 60
      • D4: 62
      • E4: 64
      • F4: 65
      • G4: 67
      • A4: 69
      • B4: 71
      • C5: 72
    6. 应用

      • MIDI音符代码用于音乐软件、硬件和数字乐器中,控制音符的播放。

    附表:

    编号 音符代码(二进制码) 音符代码(十六进制码) 所在音阶 音调
    0 0000000 00 -1 C
    1 0000001 01 -1 C#
    2 0000010 02 -1 D
    3 0000011 03 -1 D#
    4 0000100 04 -1 E
    5 0000101 05 -1 F
    6 0000110 06 -1 F#
    7 0000111 07 -1 G
    8 0001000 08 -1 G#
    9 0001001 09 -1 A
    10 0001010 0A -1 A#
    11 0001011 0B -1 B
    12 0001100 0C 0 C
    13 0001101 0D 0 C#
    14 0001110 0E 0 D
    15 0001111 0F 0 D#
    16 0010000 10 0 E
    17 0010001 11 0 F
    18 0010010 12 0 F#
    19 0010011 13 0 G
    20 0010100 14 0 G#
    21 0010101 15 0 A
    22 0010110 16 0 A#
    23 0010111 17 0 B
    24 0011000 18 1 C
    25 0011001 19 1 C#
    26 0011010 1A 1 D
    27 0011011 1B 1 D#
    28 0011100 1C 1 E
    29 0011101 1D 1 F
    30 0011110 1E 1 F#
    31 0011111 1F 1 G
    32 0100000 20 1 G#
    33 0100001 21 1 A
    34 0100010 22 1 A#
    35 0100011 23 1 B
    36 0100100 24 2 C
    37 0100101 25 2 C#
    38 0100110 26 2 D
    39 0100111 27 2 D#
    40 0101000 28 2 E
    41 0101001 29 2 F
    42 0101010 2A 2 F#
    43 0101011 2B 2 G
    44 0101100 2C 2 G#
    45 0101101 2D 2 A
    46 0101110 2E 2 A#
    47 0101111 2F 2 B
    48 0110000 30 3 C
    49 0110001 31 3 C#
    50 0110010 32 3 D
    51 0110011 33 3 D#
    52 0110100 34 3 E
    53 0110101 35 3 F
    54 0110110 36 3 F#
    55 0110111 37 3 G
    56 0111000 38 3 G#
    57 0111001 39 3 A
    58 0111010 3A 3 A#
    59 0111011 3B 3 B
    60 0111100 3C 4 C
    61 0111101 3D 4 C#
    62 0111110 3E 4 D
    63 0111111 3F 4 D#
    64 1000000 40 4 E
    65 1000001 41 4 F
    66 1000010 42 4 F#
    67 1000011 43 4 G
    68 1000100 44 4 G#
    69 1000101 45 4 A
    70 1000110 46 4 A#
    71 1000111 47 4 B
    72 1001000 48 5 C
    73 1001001 49 5 C#
    74 1001010 4A 5 D
    75 1001011 4B 5 D#
    76 1001100 4C 5 E
    77 1001101 4D 5 F
    78 1001110 4E 5 F#
    79 1001111 4F 5 G
    80 1010000 50 5 G#
    81 1010001 51 5 A
    82 1010010 52 5 A#
    83 1010011 53 5 B
    84 1010100 54 6 C
    85 1010101 55 6 C#
    86 1010110 56 6 D
    87 1010111 57 6 D#
    88 1011000 58 6 E
    89 1011001 59 6 F
    90 1011010 5A 6 F#
    91 1011011 5B 6 G
    92 1011100 5C 6 G#
    93 1011101 5D 6 A
    94 1011110 5E 6 A#
    95 1011111 5F 6 B
    96 1100000 60 7 C
    97 1100001 61 7 C#
    98 1100010 62 7 D
    99 1100011 63 7 D#
    100 1100100 64 7 E
    101 1100101 65 7 F
    102 1100110 66 7 F#
    103 1100111 67 7 G
    104 1101000 68 7 G#
    105 1101001 69 7 A
    106 1101010 6A 7 A#
    107 1101011 6B 7 B
    108 1101100 6C 8 C
    109 1101101 6D 8 C#
    110 1101110 6E 8 D
    111 1101111 6F 8 D#
    112 1110000 70 8 E
    113 1110001 71 8 F
    114 1110010 72 8 F#
    115 1110011 73 8 G
    116 1110100 74 8 G#
    117 1110101 75 8 A
    118 1110110 76 8 A#
    119 1110111 77 8 B
    120 1111000 78 9 C
    121 1111001 79 9 C#
    122 1111010 7A 9 D
    123 1111011 7B 9 D#
    124 1111100 7C 9 E
    125 1111101 7D 9 F
    126 1111110 7E 9 F#
    127 1111111 7F 9 G
  • 关于音符一拍的时值计算

    音符一拍的时值计算是音乐理论中的基础概念,涉及音符的时值和拍号:

    1. 音符时值

    音符时值表示音符的持续时间,常见音符及其时值如下:

    • 全音符:4拍
    • 二分音符:2拍
    • 四分音符:1拍
    • 八分音符:0.5拍
    • 十六分音符:0.25拍

    2. 拍号

    拍号决定每小节的拍数和每拍的时值,通常表示为分数,如4/4、3/4等。

    • 分子:每小节的拍数
    • 分母:每拍的音符类型(4代表四分音符,8代表八分音符)

    3. 计算一拍的时值

    以4/4拍为例:

    • 拍号:4/4
    • 每拍时值:四分音符(分母为4)
    • 每拍时长:1拍

    4. 不同拍号的时值计算

    • 3/4拍:每拍为四分音符,每小节3拍。
    • 6/8拍:每拍为八分音符,每小节6拍。

    5. 示例

    • 4/4拍:四分音符=1拍,二分音符=2拍,全音符=4拍。
    • 6/8拍:八分音符=1拍,四分音符=2拍,附点四分音符=3拍。

    6. 实际应用

    • 4/4拍:四分音符=1拍,八分音符=0.5拍。
    • 3/4拍:四分音符=1拍,二分音符=2拍。

    总结

    音符一拍的时值由拍号决定,分母指定每拍的音符类型,分子决定每小节的拍数。

  • 【CSS】动画性能优化

    60fps 与设备刷新率

    目前大多数设备的屏幕刷新率为60fps(Frame per Second),即每秒60帧。因此,如果在页面中有一个动画或渐变效果,或者用户正在滚动页面,那么浏览器渲染动画或页面的每一帧的速率也需要跟设备屏幕的刷新率保持一致,即每一帧要在16毫秒(1S/60 = 16.66ms)之内完成。如果无法完成,由于帧率的下降会导致内容在屏幕上抖动。此现象通常称为卡顿,会对用户体验产生负面影响。

    浏览器渲染

    在讲性能之前,我们需要先对浏览器渲染页面有一个基础的理解。

    css 图层

    浏览器在渲染一个页面时,会将页面分为很多个图层,图层有大有小,每个图层上有一个或多个节点。需要注意的是,如果图层中某个元素需要重绘,那么整个图层都需要重绘(关于重绘下面会讲到)。

    渲染过程

    简单来说,浏览器的渲染过程其实就是将页面转换成像素显示到屏幕上,大致有如下几个步骤: 渲染过程流程图

    • JavaScript操作: 一般来说,我们会使用 JavaScript 来实现一些交互操作。比如用往页面里添加一些元素,切换显示隐藏等。
    • style 样式计算: 该过程根据 css 选择器,获取每个元素匹配的 css 样式并计算其最终应用样式。
    • Layout 布局:该过程计算元素要占据的空间大小及其在屏幕的位置。网页的布局模式意味着一个元素可能影响其他元素,例如 <body> 元素的宽度一般会影响其子元素的宽度以及树中各处的节点,因此对于浏览器来说,布局是经常发生的。
    • Paint 绘制:本质上就是填充像素的过程。包括绘制文字、颜色、图像、边框和阴影等。也就是绘制元素所有的可视效果。
    • Composite 渲染层合并:在每个层上完成绘制过程之后,浏览器会将所有层按照合理的顺序合并成一个图层,然后显示在屏幕上如果我们需要提高动画的性能,需要做的就是减少浏览器在动画运行时所需要做的工作。当 css 在进行动画时,其不同属性值引起的改变,重新渲染可能会有三种执行路径:
      1. layout -> paint -> composite
      2. paint -> composite
      3. composite 很明显,最短路径的 C 动画性能是最高的,所以我们在使用动画的时候就得考虑使用什么属性,以尽量减少执行路径。

    动画属性

    css 的属性大致分为三类:布局类(layout),绘制类(paint),合成类(composite)。

    重排(reflow)

    由元素的布局类属性改变所触发的行为过程,我们称为 reflow,也叫做 relayout(重新布局)。当某个节点 reflow 时会重新计算节点的尺寸和位置,还可能会引起其它节点的 reflow。

    该系列属性的改变,会执行路径 A 进行重新渲染,所以性能是最差的。(这充分说明,重排会引起重绘)

    触发重排的属性

    • 盒子模型相关属性会触发重布局:

      • width
      • height
      • padding
      • margin
      • display
      • border-width
      • border
      • min-height
    • 定位属性及浮动也会触发重布局:

      • top
      • bottom
      • left
      • right
      • position
      • float
      • clear
    • 改变节点内部文字结构也会触发重布局:

      • text-align
      • overflow-y
      • font-weight
      • overflow
      • font-family
      • line-height
      • vertival-align
      • white-space
      • font-size

    重绘(repaint)

    由绘制类属性改变触发节点重新绘制其可视效果的过程,我们称为 repaint。 该系列属性的改变,会执行路径 B,所以性能一般。

    修改时只触发重绘的属性有:

    • color
    • border-style
    • border-radius
    • visibility
    • text-decoration
    • background
    • background-image
    • background-position
    • background-repeat
    • background-size
    • outline-color
    • outline
    • outline-style
    • outline-width
    • box-shadow

    上面的属性由于不会修改节点的大小和位置,因此不会触发重排,其只是改变了节点内部的渲染效果,所以只会进行重绘以下的步骤。

    composite

    目前只有两个属性属于 composite 类:

    • transform
    • opactiy

    优化技巧

    减少动画元素

    减少动画元素是动画性能优化中首先需要完成的。通过审查页面动画 DOM 元素结构,去除不必要的动画元素,减少元素的数量,相应地会减少布页面局和绘制的时间。

    尽量使用 fixed、absolute 定位

    对于动画元素,尽量使用用 fixed、absolute 定位方式,避免影响到其他节点重排。

    尽量只改变 transform 和 opacity

    能用 transform、opacity 优先使用,其属性的改变不会发生重排和重绘。如位移操作的,可以使用translate 来实现,渐隐渐现效果可以使用 opacity 属性来实现。

    恰当开启硬件加速效果

    对动画元素应用

    transform: translate3d(0, 0, 0);
    /* transform: translateZ(0); */
    will-change: transform;

    等来开启硬件加速。通常开启硬件加速可以让动画变得更加流畅。但这里需注意,在不需要的时候需去掉避免过多的内存消耗。

  • 【Typescript】接口、类

    接口

    TypeScript的核心原则之一是对值所具有的结构进行类型检查。

    interface LabelledValue {
      label: string;
    }
    
    function printLabel(labelledObj: LabelledValue) {
      console.log(labelledObj.label);
    }
    
    let myObj = {size: 10, label: "Size 10 Object"};
    printLabel(myObj);

    可选属性

    interface SquareConfig {
        width: number
        height: number
        color?: string // 可选属性
    }

    只读属性

    只能在刚创建时修改其值

    interface Point {
        readonly x: number;
        readonly y: number;
    }

    额外的属性检查

    如果 SquareConfig带有上面定义的类型的color和width属性,并且还会带有任意数量的其它属性,可以这样定义:

    interface SquareConfig {
        width: number
        color?: string
        [propName: string]: any
    }

    函数类型

    interface SearchFunc {
      (source: string, subString: string): boolean
    }
    
    // 对于函数类型的类型检查来说,函数的参数名不需要与接口里定义的名字相匹配。 
    let mySearch: SearchFunc;
    mySearch = function(src: string, sub: string): boolean {
      let result = src.search(sub);
      return result > -1;
    }

    可索引的类型

    interface StringArray {
      [index: number]: string;
    }
    
    let myArray: StringArray;
    myArray = ["Bob", "Fred"];
    
    let myStr: string = myArray[0];

    类类型

    interface ClockInterface {
        currentTime: Date;
        setTime(d: Date);
    }
    
    // 通过 implements 描述
    class Clock implements ClockInterface {
        currentTime: Date;
        setTime(d: Date) {
            this.currentTime = d;
        }
        constructor(h: number, m: number) { }
    }

    继承接口

    interface Shape {
        color: string;
    }
    
    interface PenStroke {
        penWidth: number;
    }
    
    interface Square extends Shape, PenStroke {
        sideLength: number;
    }
    
    let square = 
    <Square>{};
    square.color = "blue"
    square.sideLength = 10
    square.penWidth = 5

    混合类型

    一个对象可以同时做为函数和对象使用,并带有额外的属性

    interface Counter {
        (start: number): string;
        interval: number;
        reset(): void;
    }
    
    function getCounter(): Counter {
        let counter = 
    <Counter>function (start: number) { };
        counter.interval = 123;
        counter.reset = function () { };
        return counter;
    }
    
    let c = getCounter();
    c(10);
    c.reset();
    c.interval = 5.0;

    class Animal {
        move(distanceInMeters: number = 0) {
            console.log(`Animal moved ${distanceInMeters}m.`);
        }
    }
    
    class Dog extends Animal {
        bark() {
            console.log('Woof! Woof!');
        }
    }
    
    const dog = new Dog();
    dog.bark();
    dog.move(10);
    dog.bark();

    继承

    通过 extends 关键字

    class Animal {
        name: string;
        constructor(theName: string) { this.name = theName; }
        move(distanceInMeters: number = 0) {
            console.log(`${this.name} moved ${distanceInMeters}m.`);
        }
    }
    
    class Snake extends Animal {
        constructor(name: string) { super(name); }
        move(distanceInMeters = 5) {
            console.log("Slithering...");
            super.move(distanceInMeters);
        }
    }

    公共,私有与受保护的修饰符

    public(默认)

    在TypeScript里,成员都默认为 public。

    class Animal {
        public name: string;
    }
    private

    当成员被标记成private时,它就不能在声明它的类的外部访问。

    class Animal {
        private name: string
        constructor(theName: string) { this.name = theName; }
    }
    
    new Animal('Cat').name // 错误: 'name' 是私有的.
    protected

    protected与private修饰符的行为很相似,但有一点不同, protected成员在派生类中仍然可以访问。

    class Person {
        protected name: string
        constructor(name: string) { this.name = name }
    }
    
    class Employee extends Person {
        private department: string
        public getElevatorPitch() {
            return `my name is ${this.name} and I work in ${this.department}.`
        }
    }
    
    let dick = new Employee("Howard", "Sales")
    console.log(dick.getElevatorPitch());
    console.log(dick.name); // 错误

    readonly修饰符

    readonly关键字将属性设置为只读的。 只读属性必须在声明时或构造函数里被初始化。

    class Octopus {
        readonly name: string;
        constructor (theName: string) {
            this.name = theName;
        }
    }
    let dad = new Octopus("Man with the 8 strong legs");
    dad.name = "Man with the 3-piece suit"; // 错误! name 是只读的.

    存取器

    TypeScript支持通过getters/setters来截取对对象成员的访问。

    let passcode = "secret passcode";
    
    class Employee {
        private _fullName: string;
    
        get fullName(): string {
            return this._fullName;
        }
    
        set fullName(newName: string) {
            if (passcode && passcode == "secret passcode") {
                this._fullName = newName;
            }
            else {
                console.log("Error: Unauthorized update of employee!");
            }
        }
    }
    
    let employee = new Employee();
    employee.fullName = "Bob Smith";
    if (employee.fullName) {
        alert(employee.fullName);
    }

    抽象类

    抽象类做为其它派生类的基类使用。 它们一般不会直接被实例化。

    abstract class Department { ... }
    class AccountingDepartment extends Department { ... }
    
    let department: Department // 允许创建一个对抽象类型的引用
    department = new Department() // 错误: 不能创建一个抽象类的实例
    department = new AccountingDepartment() // 允许对一个抽象子类进行实例化和赋值
  • 【JS】小数精度处理技巧

    JavaScript 使用 IEEE 754 双精度浮点数表示所有数字,这会导致一些常见的小数精度问题,例如 0.1 + 0.2 !== 0.3。以下是处理 JavaScript 小数精度的常用技巧:

    1. 四舍五入方法

    // toFixed方法(返回字符串)
    let num = 0.1 + 0.2; // 0.30000000000000004
    let fixed = num.toFixed(2); // "0.30"
    
    // 使用Math.round四舍五入
    function round(value, decimals) {
      return Number(Math.round(value + 'e' + decimals) + 'e-' + decimals);
    }
    round(1.005, 2); // 1.01

    2. 精度运算库

    使用专门的数学库处理精确计算:

    • decimal.js —— 功能相对简单,适合基本的高精度计算需求
    • big.js —— 支持三角函数、指数、对数等高级数学运算
    // 使用decimal.js示例
    import Decimal from 'decimal.js';
    let sum = new Decimal(0.1).add(new Decimal(0.2)); // 0.3

    3. 整数运算技巧

    将小数转换为整数进行计算,再转换回去:

    function add(num1, num2) {
      const multiplier = Math.pow(10, Math.max(getDecimalLength(num1), getDecimalLength(num2)));
      return (num1 * multiplier + num2 * multiplier) / multiplier;
    }
    
    function getDecimalLength(num) {
      const decimalStr = num.toString().split('.')[1];
      return decimalStr ? decimalStr.length : 0;
    }

    4. 比较数字时的容差方法

    function numbersEqual(a, b, tolerance = 1e-10) {
      return Math.abs(a - b) < tolerance;
    }

    5. 格式化显示

    // 使用Intl.NumberFormat
    const formatter = new Intl.NumberFormat('en-US', {
      minimumFractionDigits: 2,
      maximumFractionDigits: 2
    });
    formatter.format(0.1 + 0.2); // "0.30"

    6. 避免常见陷阱

    • 不要直接比较浮点数
    • 对于货币计算,通常使用整数表示分(如用100表示1.00元)
    • 大整数计算考虑使用BigInt类型

    对于简单的显示问题,toFixed可能足够;对于复杂的财务计算,建议使用专门的库。