博客

  • VSCode + GitHub Copilot 实战使用

    过去我习惯通过 GPT、豆包等网页工具获取 AI 协助,但自从改用 VSCode 搭配 GitHub Copilot 后,切身感受到了 AI 辅助开发的强大。它不仅能省去大量重复编码的工作,更能在遇到技术卡点时快速响应,甚至独立追踪分析并解决问题。

    一、环境搭建与基础配置

    1.1 插件安装流程

    1. 核心插件安装:在 VSCode 扩展市场搜索 GitHub Copilot 和 GitHub Copilot Chat,完成安装并重启编辑器;二者需搭配使用,前者负责内联代码补全,后者提供交互式对话调试。

    2. 账号授权:登录已开通 Copilot 订阅的 GitHub 账号,完成编辑器与 GitHub 的权限绑定,确保网络环境可正常访问 GitHub 服务。

    3. 基础启用检查:右下角出现 Copilot 图标即代表激活成功,可通过图标快速开启/关闭补全、查看用量、进入设置面板。

    1.2 关键配置优化

    为兼顾效率与代码质量,针对性调整以下设置(文件 → 首选项 → 设置,搜索 Copilot 关键词):

    • 代码补全延迟:调低响应延迟,避免频繁手动触发补全,提升流畅度

    • 启用记忆功能:开启 github.copilot.chat.copilotMemory.enabled,让 Copilot 记住项目编码规范、命名风格,减少重复调教

    • 过滤无关建议:关闭非当前项目语言的冗余补全,降低干扰

    • 快捷键自定义:绑定常用快捷键(接受建议、切换下一条、手动触发补全),提升操作速度

    二、核心功能实战用法

    2.1 内联代码补全(高频场景)

    这是 Copilot 最基础也最实用的能力,贴合手写代码节奏,无需打断思路:

    • 单行/多行补全:输入函数名、注释或关键逻辑,Copilot 自动预判后续代码,按 Tab 接受建议,Alt/Option + ]/[ 切换备选方案。

    • 批量代码生成:编写清晰的单行注释(如 // 封装 axios 请求拦截器,统一处理 token 和报错),Copilot 可直接生成完整可运行的函数块。

    • 语法适配:支持主流编程语言(JavaScript/TypeScript、Python、Java、Go等),自动适配项目的代码风格、缩进规则、变量命名。

    2.2 Copilot Chat 交互式开发(进阶效率)

    通过侧边栏聊天窗口,实现代码解读、调试、重构一站式操作,核心用法:

    • 代码解释:选中晦涩代码,右键选择「Ask Copilot」,快速理清逻辑、梳理参数含义,适合接手旧项目。

    • BUG 定位与修复:粘贴报错信息 + 异常代码片段,Copilot 自动分析报错原因,给出修复方案并标注修改点。

    • 代码重构:指令式提问(如「将这段回调函数改写为 Promise/async await」「优化这段代码的可读性,添加注释并拆分函数」),批量优化冗余代码。

    • 文档与注释生成:一键生成 JSDoc、函数注释、接口说明,规范代码文档,减少手动编写工作量。

    2.3 高阶场景拓展

    • 测试用例生成:针对业务函数,自动生成单元测试代码,覆盖正常、异常、边界场景,提升测试效率。

    • Git 提交信息/PR 描述:基于代码变更,自动生成规范的 commit 信息,省去文案梳理时间。

    • 终端命令辅助:直接询问常用命令(如 Docker 部署、npm 脚本、Linux 操作),快速获取可复制执行的指令。

    三、使用心得与效率提升技巧

    核心原则:清晰的指令 = 高质量的结果

    • 精准描述需求:避免模糊提问,用「做什么+语言+规范」的句式提问,例如「用 TypeScript 编写一个防抖函数,支持立即执行和取消功能,符合 ES6+ 规范」。

    • 上下文投喂:涉及项目全局逻辑时,先粘贴依赖代码或配置项,让 Copilot 基于项目上下文生成适配代码,避免通用化模板。

    • 分步调教:复杂功能分步骤生成,先搭框架,再补逻辑,最后优化细节,降低出错率。

    • 代码必校验:Copilot 生成代码需逐行审核,重点校验逻辑漏洞、边界情况、依赖引入,杜绝直接照搬上线。

    • 快捷键固化:养成快捷键操作习惯,减少鼠标点击,最大化提升编码速度。

    四、常见问题与避坑总结

    4.1 常见问题排查

    • 补全不生效/无响应:检查 GitHub 登录状态、网络连通性、插件是否启用,重启 VSCode 或重新授权即可解决。

    • 建议质量差/不符合规范:补充项目上下文、细化指令,开启记忆功能让 Copilot 适配项目风格,避免过度依赖通用建议。

    • 卡顿/延迟高:关闭无关扩展,调低补全延迟,限制 Copilot 同时分析的文件数量。

    4.2 避坑要点

    • 严禁直接使用未审核的生成代码上线,尤其是涉及数据处理、权限校验、安全逻辑的代码。

    • 注意代码版权与合规性,商业项目需甄别生成代码的来源,避免侵权风险。

    • 不要过度依赖 Copilot,核心业务逻辑需自主设计,AI 仅作为效率工具辅助编码。

    五、总结与后续规划

    VSCode + Copilot 组合极大缩短了重复代码编写时间,降低了技术卡点的解决成本,日均编码效率提升约 40%,尤其适合快速迭代、原型开发、代码规范化场景。

    后续将进一步探索 Copilot 智能体模式、MCP 工具集成、团队共享规范等高阶能力,把 AI 辅助融入全开发流程,同时强化代码审核机制,兼顾效率与质量。

  • Python 学习笔记:常用核心库简介

    我近期在使用影刀开发各类自动化流程,发现即便借助影刀的指令体系,深入掌握 Python 第三方库依然至关重要。这些库覆盖了数据处理、可视化、网络请求、自动化等几乎所有编程场景,因此我整理了这份新手入门级的 Python 常用库笔记,包含核心功能、安装方式和极简示例,助力快速上手。

    一、基础必备库

    1. NumPy(数值计算基础)

    核心功能

    • 提供高性能的多维数组(ndarray) 数据结构,是科学计算的基础。
    • 支持向量化运算、矩阵操作、线性代数、傅里叶变换等数值计算。
    • 比 Python 原生列表快得多,是 Pandas、Matplotlib 等库的底层依赖。

    安装

    pip install numpy

    极简示例

    import numpy as np
    
    # 创建一维数组
    arr1 = np.array([1, 2, 3, 4])
    # 创建二维数组(矩阵)
    arr2 = np.array([[1, 2], [3, 4]])
    
    # 数组运算(向量化,无需循环)
    print(arr1 * 2)  # 输出:[2 4 6 8]
    print(arr2 + arr2)  # 输出:[[2 4], [6 8]]
    # 矩阵乘法
    print(arr2 @ arr2)  # 输出:[[7 10], [15 22]]

    2. Pandas(数据处理与分析)

    核心功能

    • 提供 DataFrame(表格型数据结构) 和 Series(一维序列),完美适配表格数据(如 Excel/CSV)。
    • 支持数据清洗(缺失值、重复值处理)、筛选、分组、合并、透视表等。
    • 是数据分析、数据挖掘的核心工具,能轻松处理几十万甚至百万行数据。

    安装

    pip install pandas

    极简示例

    import pandas as pd
    
    # 创建DataFrame(模拟表格数据)
    df = pd.DataFrame({
        "姓名": ["张三", "李四", "王五"],
        "年龄": [20, 25, 30],
        "城市": ["北京", "上海", "广州"]
    })
    
    # 数据查看
    print(df.head())  # 查看前n行(默认5行)
    # 数据筛选:筛选年龄>22的行
    print(df[df["年龄"] > 22])
    # 保存为CSV文件
    df.to_csv("user_info.csv", index=False)
    # 读取CSV文件
    df_read = pd.read_csv("user_info.csv")

    二、数据可视化库

    1. Matplotlib(基础可视化库)

    核心功能

    • Python 最基础的绘图库,支持绘制折线图、柱状图、散点图、直方图等几乎所有基础图表。
    • 高度可定制(标题、坐标轴、颜色、样式等),是其他可视化库(如 Seaborn)的底层。

    安装

    pip install matplotlib

    极简示例

    import matplotlib.pyplot as plt
    import numpy as np
    
    # 生成数据
    x = np.linspace(0, 10, 100)  # 0到10之间生成100个均匀分布的数
    y = np.sin(x)
    
    # 绘制折线图
    plt.plot(x, y, color="red", label="sin(x)")
    plt.xlabel("X轴")  # X轴标签
    plt.ylabel("Y轴")  # Y轴标签
    plt.title("正弦函数曲线")  # 标题
    plt.legend()  # 显示图例
    plt.show()  # 展示图表

    2. Seaborn(高级可视化库)

    核心功能

    • 基于 Matplotlib 封装,语法更简洁,默认样式更美观。
    • 专注于统计可视化(如箱线图、热力图、小提琴图、配对图),适配 Pandas 的 DataFrame。

    安装

    pip install seaborn

    极简示例

    import seaborn as sns
    import pandas as pd
    
    # 使用内置数据集
    tips = sns.load_dataset("tips")  # 餐饮小费数据集
    
    # 绘制柱状图(按性别分组,展示平均消费)
    sns.barplot(x="sex", y="total_bill", data=tips)
    # 绘制热力图(展示变量相关性)
    sns.heatmap(tips.corr(numeric_only=True), annot=True, cmap="coolwarm")

    三、网络请求与爬虫库

    1. Requests(HTTP 请求库)

    核心功能

    • 替代 Python 原生的 urllib,语法极简,支持 GET/POST 请求、Cookie、Headers、代理等。
    • 是爬虫、调用 API 接口的首选库。

    安装

    pip install requests

    极简示例

    import requests
    
    # GET请求(获取网页内容)
    response = requests.get("https://www.baidu.com")
    response.encoding = "utf-8"  # 设置编码
    print(response.text[:100])  # 打印前100个字符
    
    # POST请求(模拟表单提交)
    data = {"username": "test", "password": "123456"}
    response = requests.post("https://httpbin.org/post", data=data)
    print(response.json())  # 以JSON格式返回响应

    2. BeautifulSoup4(网页解析库)

    核心功能

    • 解析 HTML/XML 页面,提取标签、属性、文本等内容,搭配 Requests 实现爬虫。
    • 支持 CSS 选择器、正则表达式匹配,上手简单。

    安装

    pip install beautifulsoup4

    极简示例

    import requests
    from bs4 import BeautifulSoup
    
    # 获取网页内容
    response = requests.get("https://www.baidu.com")
    soup = BeautifulSoup(response.text, "html.parser")  # 解析HTML
    
    # 提取指定标签(比如所有a标签的href属性)
    a_tags = soup.find_all("a")  # 找到所有a标签
    for tag in a_tags[:5]:  # 打印前5个
        print(tag.get("href"), tag.text)

    四、自动化与实用工具库

    1. os & sys(系统交互库,内置)

    核心功能

    • os:操作文件/目录(创建、删除、遍历、路径拼接)、执行系统命令。
    • sys:获取系统参数(如命令行参数、Python 版本)、控制程序退出等。
    • 无需安装,Python 原生内置。

    极简示例

    import os
    import sys
    
    # os库示例:遍历指定目录下的文件
    print(os.listdir("."))  # 列出当前目录所有文件/文件夹
    os.makedirs("test_dir", exist_ok=True)  # 创建目录(已存在则不报错)
    
    # sys库示例:获取命令行参数
    print("Python版本:", sys.version)
    print("命令行参数:", sys.argv)  # 运行时传入的参数,如python test.py 123,则sys.argv=[test.py, 123]

    2. datetime(时间处理库,内置)

    核心功能

    • 处理日期、时间的解析、格式化、加减运算,替代原生 time 库的复杂操作。
    • 无需安装,Python 原生内置。

    极简示例

    from datetime import datetime, timedelta
    
    # 获取当前时间
    now = datetime.now()
    print(now.strftime("%Y-%m-%d %H:%M:%S"))  # 格式化输出:2026-03-11 10:00:00
    
    # 时间加减(比如3天后)
    after_3_days = now + timedelta(days=3)
    print(after_3_days.strftime("%Y-%m-%d"))
    
    # 解析字符串为时间
    time_str = "2026-01-01"
    time_obj = datetime.strptime(time_str, "%Y-%m-%d")
    print(time_obj)

    3. OpenPyXL(Excel 操作库)

    核心功能

    • 读写 Excel 文件(.xlsx 格式),支持单元格样式、公式、合并单元格等。
    • 替代老旧的 xlrd/xlwt,功能更全面。

    安装

    pip install openpyxl

    极简示例

    from openpyxl import Workbook, load_workbook
    
    # 创建新Excel并写入数据
    wb = Workbook()
    ws = wb.active  # 获取活跃工作表
    ws["A1"] = "姓名"
    ws["B1"] = "年龄"
    ws["A2"] = "张三"
    ws["B2"] = 20
    wb.save("test.xlsx")  # 保存文件
    
    # 读取Excel数据
    wb = load_workbook("test.xlsx")
    ws = wb.active
    print(ws["A2"].value)  # 输出:张三
    print(ws["B2"].value)  # 输出:20

    4. Playwright(现代自动化测试/爬虫库)

    核心功能

    • 由微软开发,支持无头浏览器自动化(Chrome、Firefox、Safari 全兼容)。
    • 相比 Selenium 更轻量、速度更快,API 设计更简洁,内置等待机制(无需手动加 sleep)。
    • 可用于网页自动化操作、动态渲染页面爬虫(如 JavaScript 加载的内容)、UI 自动化测试。
    • 支持同步/异步两种调用方式,新手优先学同步版即可。

    安装

    # 安装库
    pip install playwright
    # 安装浏览器驱动(首次使用必须执行,会下载Chrome/Firefox/Safari驱动)
    playwright install

    极简示例

    from playwright.sync_api import sync_playwright
    
    # 启动浏览器(headless=False 显示浏览器窗口,True 为无头模式)
    with sync_playwright() as p:
        # 选择浏览器:chromium/firefox/webkit
        browser = p.chromium.launch(headless=False)
        # 新建页面
        page = browser.new_page()
        # 访问网页
        page.goto("https://www.baidu.com")
    
        # 1. 输入并搜索(定位输入框,输入内容)
        page.locator("#kw").fill("Python Playwright")
        # 点击搜索按钮
        page.locator("#su").click()
    
        # 2. 等待页面加载并提取数据(等待标题出现)
        page.wait_for_selector("h3")
        # 提取所有搜索结果标题
        titles = page.locator("h3").all_text_contents()
        print("前5个搜索结果:")
        for title in titles[:5]:
            print(title)
    
        # 关闭浏览器
        browser.close()

    核心优势(对比传统爬虫/自动化工具)

    1. 能爬取动态渲染页面(如需要登录、滚动加载、JS 渲染的内容),解决 Requests 无法处理的场景。
    2. 内置自动等待:无需手动设置 time.sleep(),会等待元素加载完成后再操作,稳定性更高。
    3. 支持截图/录屏page.screenshot(path="baidu.png") 可保存页面截图,便于调试。

    五、其他常用库(快速了解)

    库名 核心用途 安装命令
    Scikit-learn 机器学习(分类、回归、聚类) pip install scikit-learn
    TensorFlow/PyTorch 深度学习(神经网络) pip install tensorflow / pip install torch
    Flask/Django Web 开发(轻量/全栈框架) pip install flask / pip install django
    Pillow 图片处理(裁剪、滤镜、格式转换) pip install pillow
    PyAutoGUI 键鼠自动化(模拟点击、输入) pip install pyautogui

    总结

    1. 基础核心库:NumPy 是数值计算基石,Pandas 是数据处理核心,二者是数据分析的必备组合。
    2. 可视化库:Matplotlib 是基础,Seaborn 更适合统计可视化,优先掌握折线图、柱状图、热力图等常用图表。
    3. 实用工具库:基础爬虫用 Requests+BeautifulSoup4 处理静态页面,Playwright 适配动态页面/网页自动化;os/datetime/OpenPyXL 覆盖日常文件、系统、时间和 Excel 操作场景。
  • Vue 3.0 核心特性技术日志

    作为从 Vue 1.0 一路使用过来的开发者,Vue 3.0 并非简单的版本迭代,而是在核心架构、性能、开发体验上的全面升级,同时兼容了 2.x 的大部分核心思想,既保留了我们熟悉的开发范式,又解决了 2.x 长期存在的痛点。以下从核心架构、语法特性、性能优化、工程化等维度,结合 2.x 对比梳理关键变化。

    一、核心架构:从 Options API 到 Composition API

    1. Vue 2.x:Options API 为主

    Vue 2.x 采用「选项式 API」,将代码按 datamethodscomputedwatch生命周期钩子 等选项划分,优点是上手简单,但存在明显痛点:

    • 逻辑碎片化:一个业务逻辑(如「用户登录状态管理」)的代码会分散在 data(状态)、methods(方法)、watch(监听)、mounted(初始化)等多个选项中,组件越大,逻辑越难追踪;
    • 代码复用受限:mixins 是主要复用方式,但存在命名冲突、来源不清晰、逻辑依赖隐晦的问题;
    • TypeScript 支持薄弱:Options API 天生不契合 TS 的类型推导,需要额外的类型声明,开发体验差。

    2. Vue 3.0:Composition API(组合式 API)+ 保留 Options API

    Vue 3.0 推出「组合式 API」,核心是 setup 函数(组件的入口),允许按业务逻辑维度组织代码,而非按选项类型:

    
    <template>
    
    <div>{{ count }}</div>
      <button @click="increment">+1</button>
    </template>
    
    <script setup>
    // Vue 3.0 组合式 API(<script setup> 语法糖)
    import { ref, computed, onMounted } from 'vue'
    
    // 状态定义(替代 2.x 的 data)
    const count = ref(0)
    
    // 方法定义(替代 2.x 的 methods)
    const increment = () => {
      count.value++
    }
    
    // 计算属性(替代 2.x 的 computed)
    const doubleCount = computed(() => count.value * 2)
    
    // 生命周期(替代 2.x 的 mounted)
    onMounted(() => {
      console.log('组件挂载完成,count 初始值:', count.value)
    })
    </script>

    核心优势

    • 逻辑聚合:一个业务逻辑的所有代码(状态、方法、监听、生命周期)可集中写在一处,组件结构更清晰;
    • 灵活复用:通过「组合函数(Composables)」复用逻辑(如 useUser()useRequest()),替代 mixins,解决命名冲突和依赖问题;
    • 完美支持 TS:基于函数式的写法天然契合 TS 类型推导,无需额外声明即可获得完整的类型提示。

    兼容说明:Vue 3.0 完全保留 Options API,2.x 的代码可直接迁移(仅需少量适配),支持「渐进式升级」—— 既可以在老组件中继续用 Options API,也可以在新组件中用 Composition API。

    二、响应式系统:从 Object.defineProperty 到 Proxy

    1. Vue 2.x:响应式的痛点

    Vue 2.x 基于 Object.defineProperty 实现响应式,存在天然限制:

    • 无法监听数组下标修改(如 arr[0] = 1)和数组长度修改(如 arr.length = 0),需通过 Vue.set/this.$set 或数组变异方法(push/splice)触发更新;
    • 无法监听对象新增属性/删除属性(如 this.obj.newKey = 1),同样需要 Vue.set/this.$delete
    • 响应式初始化时需要递归遍历对象,性能随对象层级加深下降。

    2. Vue 3.0:Proxy 重构响应式

    Vue 3.0 改用 ES6 的 Proxy 实现响应式,彻底解决上述问题:

    • 原生支持监听数组下标/长度修改、对象新增/删除属性,无需 $set/$delete
    • 非侵入式:无需修改原对象,而是代理对象,初始化时无需递归遍历(懒代理,访问属性时才递归),性能大幅提升;
    • 支持 RefReactive 两种响应式方式:
      • ref:用于基础类型(字符串、数字等),通过 .value 访问(模板中自动解包,无需 .value);
      • reactive:用于对象/数组,返回代理对象,直接访问属性即可。

    示例对比

    // Vue 2.x
    export default {
      data() {
        return {
          arr: [1, 2, 3],
          obj: { name: 'Vue2' }
        }
      },
      methods: {
        updateData() {
          // 无效:无法监听数组下标修改
          this.arr[0] = 100
          // 有效:需用 $set
          this.$set(this.arr, 0, 100)
    
          // 无效:无法监听对象新增属性
          this.obj.age = 3
          // 有效:需用 $set
          this.$set(this.obj, 'age', 3)
        }
      }
    }
    
    // Vue 3.0(Composition API)
    import { ref, reactive } from 'vue'
    const arr = ref([1, 2, 3])
    const obj = reactive({ name: 'Vue3' })
    
    const updateData = () => {
      // 有效:原生支持数组下标修改
      arr.value[0] = 100
      // 有效:原生支持对象新增属性
      obj.age = 3
    }

    三、性能优化:体积、渲染、编译全方位提升

    1. 体积更小

    • Vue 2.x 运行时体积约 22KB(gzip);
    • Vue 3.0 通过「树形摇树(Tree-shaking)」重构核心代码,仅引入使用的功能(如仅用响应式、不用编译器),最小体积约 10KB(gzip),体积减少 50% 以上。

    2. 渲染更快

    • 虚拟 DOM 重构:Vue 3.0 优化了虚拟 DOM 的生成和对比算法,引入「静态标记」—— 对模板中不变的节点(如纯文本、静态标签)做标记,对比时直接跳过,渲染性能提升约 55%;
    • 编译优化:Vue 3.0 的模板编译器会提前分析模板结构,生成更高效的渲染函数,更新性能提升约 133%。

    3. 内存占用更低

    Vue 3.0 优化了组件实例的内存占用,同场景下内存使用减少约 50%。

    四、其他关键变化(对比 Vue 2.x)

    1. 生命周期钩子

    Vue 3.0 保留了 2.x 的生命周期概念,但命名略有调整(Composition API 中),且新增 setup 替代 beforeCreatecreated Vue 2.x Vue 3.0(Composition API) 说明
    beforeCreate setup(开始) setup 执行时,组件实例未创建
    created setup(结束) setup 执行完,组件实例已创建
    beforeMount onBeforeMount 挂载前
    mounted onMounted 挂载后
    beforeUpdate onBeforeUpdate 更新前
    updated onUpdated 更新后
    beforeDestroy onBeforeUnmount 卸载前(命名更语义化)
    destroyed onUnmounted 卸载后(命名更语义化)

    2. 模板语法

    • 支持多根节点:Vue 2.x 模板要求唯一根节点,3.0 支持多根节点(Fragment),无需包裹 `
      `;
    • 更灵活的 v-model:Vue 2.x 中 v-model 本质是 value + input 事件,3.0 支持自定义绑定属性和事件,且支持多个 v-model:

      <!-- Vue 2.x:仅能绑定一个 v-model,且固定 value/input -->
      <Child v-model="name" />
      
      <!-- Vue 3.0:支持多个 v-model,自定义属性和事件 -->
      <Child v-model:name="name" v-model:age="age" />
    • 移除 filter:Vue 3.0 废弃了 2.x 的 filter 过滤器,推荐用计算属性或方法替代(减少 API 复杂度)。

    3. 全局 API 调整

    Vue 2.x 的全局 API(如 Vue.componentVue.directive)会污染全局环境,3.0 改为「实例化 API」:

    // Vue 2.x
    import Vue from 'vue'
    Vue.component('MyComponent', { /* ... */ })
    Vue.directive('my-dir', { /* ... */ })
    new Vue({ el: '#app' })
    
    // Vue 3.0
    import { createApp } from 'vue'
    const app = createApp({ /* 根组件 */ })
    app.component('MyComponent', { /* ... */ })
    app.directive('my-dir', { /* ... */ })
    app.mount('#app')

    优势:多个 Vue 实例可独立配置,避免全局配置冲突(如多应用嵌入场景)。

    4. 废弃/不推荐的特性

    • 废弃 Vue.prototype:改用 app.config.globalProperties 挂载全局属性;
    • 废弃 $on/$off/$once:移除实例事件总线,推荐用第三方库(如 mitt)替代;
    • 不推荐 filters:用计算属性/方法替代;
    • 不推荐 mixins:用 Composition API 的组合函数替代。

    五、迁移建议(从 Vue 2.x 到 3.0)

    1. 渐进式迁移:先保留 Options API,逐步在新功能中使用 Composition API,降低迁移成本;
    2. 工具适配:升级脚手架到 Vite(Vue 3.0 推荐)或 Vue CLI 5+,安装 @vue/compat 兼容包,可临时兼容 2.x 代码;
    3. 重点检查
      • 替换 Vue.set/$setVue.delete/$delete(3.0 无需);
      • 调整生命周期钩子(如 beforeDestroyonBeforeUnmount);
      • 替换 filter 为计算属性/方法;
    4. TypeScript 接入:新组件建议用 <script setup lang="ts">,充分利用 3.0 的 TS 优势。

    总结

    1. 核心架构:Vue 3.0 的 Composition API 解决了 2.x Options API 逻辑碎片化、复用难、TS 支持差的问题,同时兼容 2.x 写法,支持渐进式升级;
    2. 响应式系统:基于 Proxy 重构,彻底解决 2.x Object.defineProperty 无法监听数组/对象新增属性的痛点,性能和易用性大幅提升;
    3. 性能与体验:体积更小、渲染更快,模板语法更灵活(多根节点、多 v-model),全局 API 更合理,是对 Vue 2.x 核心痛点的全面优化,同时保留了 Vue 一贯的「易用、渐进式」设计理念。
  • 2025佛山50公里徒步(禅城线)

    今年是我来佛山定居的第十年,所以去参加了50km徒步纪念一下。

    6:30 AM 起床

    一大早就起床,吃完早餐就骑电驴向集结地文华公园出发。

    8:24 AM 起点·文华公园,人山人海

    到现场直接吓懵 —— 人也太多了吧!电视塔下面全是人。之前我还想走50公里不是普通的运动量,参加的人应该不多,结果发现满头白发的老人、三岁小孩都来参加,看来我低估了大家对这个活动的积极性。好,从此刻正式开始!

    09:53 AM 第一签:升平里(5KM)

    开局像过年游花市,大家都是一个挨一个,沿途的路都围蔽了。就这样慢吞吞地走了1.5小时。心想,这样走岂不是晚上十点都走不完?!来到升平里人更多了(是我见过这个地方人最多的一次),赶紧找签到点盖章离开。

    11:48 AM 第二签:欧洲工业园C区(12.8KM)

    这段感觉人少了不少,原因其实很多人只是为了打个卡发发朋友圈,(毕竟 5km 对现在的人还是极限)。太阳也出来营业了,还好沿途绿道大树也比较多。这一段是考验耐心的,因为要经过一片老旧的工业区,所以风景一般,路边的大货车经过会扬起一片灰尘……

    13:24 AM 第三签:城发悦城峯境(17.8KM)

    沿汾江河边都是新打造的绿道,风景很不错。沿途都是卖小吃、饮料的小贩。也经过了我一直想去的王借岗公园,又帮我在百度地图点亮了一个片区。

    14:29 PM 第四签:绿岛湖国际中心(20.5KM)

    距离不远,但人走了一个上午已经有点累,所以走到绿岛湖就在湖边休整了20分钟。在这里汇合身穿浅蓝色衣服的"三水线"驴友,这些都是大神人,因为他们的线路比我们多出14km,也就是说别人一大早7点就已经出发,并且走得很快。其实禅城线算是“作弊”线路,因为它全场是36km,是全部线路中最短的。

    15:25 PM 第五签:智慧公园(24.5KM)

    这段就开始拼耐力了,阳光猛烈,大树比较少,脚步也渐渐慢下来。不过令我意外的是智慧公园原来很漂亮,又找多了一个休闲的地方。

    16:46 PM 第六签:青年公园(30.5KM)

    如果认真计算距离和时间,就发现我速度快了很多。不,我没作弊(已经看到很多人骑共享单车跑了),其实原因是身边的队友也受不了骑车去。我只是自己发挥真正的实力 ^_^。PS沿线的公交线路都挤满了人,其实这不是比赛,尽力而为就好。

    18:12 PM 终点·世纪莲体育中心(36KM)

    突然想起十年前那次30公里徒步,走完整个人都废了,所以这次出发前一直怀疑自己到底行不行。现在看来,身体确实比以前能扛了,但更关键的是——只要咬咬牙,好像也没什么走不完的路。

  • 广州11号线开通首日体验

    早上我和家人去了广州动物园,下午回来赶上了11号线的开通首日,顺便体验一下新线路。

    列车进站的时候,大家都掏出手机拍照,我也赶紧拍了几张发朋友圈。11号线的站台和车厢都很大,不愧为A8列车,坐着特别舒服,相比广州其它线路,绝对的王者。而且它是城区环线,所以去哪里换乘都方便。

    到了中医学院站,发现人还挺多的,大家都想第一时间体验新线路,而且这个站也是「大站」,以后可以换乘12号线。站台设计很有特色,融入了很多中医药文化的元素。

  • 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;

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

  • Laravel 学习笔记

    一、前期准备:项目初始化与基础配置

    1.1 项目创建与依赖安装

    Laravel 内置API开发支持,无需额外引入前端脚手架,推荐使用纯净API项目初始化方式:

    # 创建Laravel项目
    composer create-project laravel/laravel laravel-api
    
    # 进入项目目录
    cd laravel-api
    
    # 禁用前端相关依赖(可选,纯API无需前端)
    rm -rf resources/js resources/css package.json vite.config.js

    1.2 环境配置与数据库连接

    修改根目录 .env 文件,配置核心环境变量,确保数据库、时区、接口响应格式适配后端API:

    # 应用基础配置
    APP_NAME=LaravelAPI
    APP_ENV=local
    APP_KEY=生成的密钥
    APP_DEBUG=true
    APP_URL=http://localhost:8000
    APP_TIMEZONE=Asia/Shanghai
    APP_LOCALE=zh-CN
    
    # 数据库配置(MySQL为例)
    DB_CONNECTION=mysql
    DB_HOST=127.0.0.1
    DB_PORT=3306
    DB_DATABASE=laravel_api
    DB_USERNAME=root
    DB_PASSWORD=
    
    # 接口跨域配置(后续详解)
    ALLOWED_ORIGINS=*

    生成应用密钥(必填,保障加密安全):

    php artisan key:generate

    二、路由:API路由定义与规范

    2.1 API专属路由文件

    Laravel 提供 routes/api.php 路由文件,专门用于定义后端接口,默认前缀 /api,无需手动添加,且不包含Session中间件,适配无状态API。

    2.2 基础路由定义

    use Illuminate\Support\Facades\Route;
    use App\Http\Controllers\Api\UserController;
    use App\Http\Controllers\Api\PostController;
    
    // 单接口路由
    Route::get('/test', function () {
        return response()->json([
            'code' => 200,
            'msg' => 'API接口正常',
            'data' => []
        ]);
    });
    
    // 控制器路由(推荐)
    Route::get('/users', [UserController::class, 'index']); // 用户列表
    Route::get('/users/{id}', [UserController::class, 'show']); // 单个用户
    Route::post('/users', [UserController::class, 'store']); // 创建用户
    Route::put('/users/{id}', [UserController::class, 'update']); // 更新用户
    Route::delete('/users/{id}', [UserController::class, 'destroy']); // 删除用户
    

    2.3 资源路由(RESTful规范)

    针对标准CRUD接口,使用资源路由简化定义,自动映射RESTful风格接口:

    // 完整资源路由
    Route::apiResource('posts', PostController::class);
    
    // 仅指定部分接口
    Route::apiResource('posts', PostController::class)->only(['index', 'show']);
    
    // 排除部分接口
    Route::apiResource('posts', PostController::class)->except(['destroy']);

    2.4 路由分组与中间件

    对接口进行分组管理,统一添加前缀、中间件(如鉴权、跨域):

    Route::prefix('v1')->group(function () {
        // 公开接口
        Route::post('/login', [AuthController::class, 'login']);
        Route::post('/register', [AuthController::class, 'register']);
    
        // 需鉴权接口
        Route::middleware('auth:sanctum')->group(function () {
            Route::get('/user/info', [AuthController::class, 'userInfo']);
            Route::apiResource('posts', PostController::class);
        });
    });

    三、控制器:API业务逻辑处理

    3.1 创建API专用控制器

    推荐在 app/Http/Controllers/Api 目录下创建控制器,区分前后端,命令行创建:

    # 创建基础控制器
    php artisan make:controller Api/UserController
    
    # 创建资源控制器(自带CRUD方法)
    php artisan make:controller Api/PostController --api

    3.2 控制器标准写法(API响应规范)

    后端API统一返回JSON格式数据,遵循 code+msg+data 响应规范,避免直接返回原生数据:

    <?php
    
    namespace App\Http\Controllers\Api;
    
    use App\Http\Controllers\Controller;
    use App\Models\User;
    use Illuminate\Http\Request;
    use Illuminate\Support\Facades\Hash;
    
    class UserController extends Controller
    {
        /**
         * 用户列表
         */
        public function index(Request $request)
        {
            // 分页查询
            $users = User::query()->paginate(10);
            return $this->success('用户列表获取成功', $users);
        }
    
        /**
         * 单个用户详情
         */
        public function show($id)
        {
            $user = User::find($id);
            if (!$user) {
                return $this->fail('用户不存在', 404);
            }
            return $this->success('用户详情获取成功', $user);
        }
    
        /**
         * 统一成功响应
         */
        protected function success($msg = '操作成功', $data = [], $code = 200)
        {
            return response()->json([
                'code' => $code,
                'msg' => $msg,
                'data' => $data
            ]);
        }
    
        /**
         * 统一失败响应
         */
        protected function fail($msg = '操作失败', $code = 400)
        {
            return response()->json([
                'code' => $code,
                'msg' => $msg,
                'data' => []
            ]);
        }
    }
    

    四、请求验证:参数校验与异常处理

    4.1 表单请求验证(推荐)

    创建独立验证类,分离校验逻辑与业务逻辑,命令行创建:

    php artisan make:request Api/UserStoreRequest

    编写验证规则(app/Http/Requests/Api/UserStoreRequest.php):

    <?php
    
    namespace App\Http\Requests\Api;
    
    use Illuminate\Foundation\Http\FormRequest;
    
    class UserStoreRequest extends FormRequest
    {
        /**
         * 权限判断
         */
        public function authorize()
        {
            return true; // 开放接口直接返回true
        }
    
        /**
         * 验证规则
         */
        public function rules()
        {
            return [
                'username' => 'required|string|unique:users|max:20',
                'email' => 'required|email|unique:users',
                'password' => 'required|string|min:6|confirmed',
            ];
        }
    
        /**
         * 自定义错误提示
         */
        public function messages()
        {
            return [
                'username.required' => '用户名不能为空',
                'email.unique' => '邮箱已被注册',
                'password.confirmed' => '两次密码不一致',
            ];
        }
    }
    

    4.2 控制器调用验证

    public function store(UserStoreRequest $request)
    {
        // 验证通过后获取安全参数
        $data = $request->validated();
        $data['password'] = Hash::make($data['password']);
        $user = User::create($data);
        return $this->success('用户创建成功', $user);
    }

    4.3 全局异常处理(API适配)

    修改 app/Exceptions/Handler.php,将异常转为JSON响应,避免页面报错:

    use Illuminate\Validation\ValidationException;
    
    public function register()
    {
        $this->renderable(function (ValidationException $e, $request) {
            if ($request->is('api/*')) {
                return response()->json([
                    'code' => 422,
                    'msg' => '参数校验失败',
                    'data' => $e->errors()
                ], 422);
            }
        });
    }

    五、数据库:迁移、模型与数据操作

    5.1 数据迁移(表结构管理)

    通过迁移文件管理数据库表,无需手动建表:

    # 创建迁移文件
    php artisan make:migration create_posts_table
    
    # 执行迁移(建表)
    php artisan migrate
    
    # 回滚迁移
    php artisan migrate:rollback

    迁移文件示例(创建文章表):

    public function up()
    {
        Schema::create('posts', function (Blueprint $table) {
            $table->id();
            $table->string('title');
            $table->text('content');
            $table->integer('user_id');
            $table->timestamps();
        });
    }

    5.2 模型定义与关联

    # 创建模型
    php artisan make:model Post

    模型配置(app/Models/Post.php):

    <?php
    
    namespace App\Models;
    
    use Illuminate\Database\Eloquent\Factories\HasFactory;
    use Illuminate\Database\Eloquent\Model;
    
    class Post extends Model
    {
        use HasFactory;
    
        // 允许批量赋值字段
        protected $fillable = ['title', 'content', 'user_id'];
    
        /**
         * 关联用户(一对多)
         */
        public function user()
        {
            return $this->belongsTo(User::class);
        }
    }

    5.3 API常用数据操作

    • 分页查询Post::paginate(10),自动返回分页元数据

    • 条件筛选Post::where('user_id', $userId)->get()

    • 关联查询Post::with('user')->paginate(10)(预加载避免N+1问题)

    • 新增数据Post::create($validatedData)

    • 更新数据Post::findOrFail($id)->update($validatedData)

    • 删除数据Post::findOrFail($id)->delete()

    六、接口鉴权:Sanctum无状态认证

    6.1 Sanctum安装与配置

    Laravel官方推荐的轻量级API鉴权方案,无状态、适配纯后端接口:

    # 安装Sanctum
    composer require laravel/sanctum
    
    # 发布配置文件
    php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider"
    
    # 执行迁移(生成令牌表)
    php artisan migrate

    app/Models/User.php 引入令牌 trait:

    use Laravel\Sanctum\HasApiTokens;
    
    class User extends Authenticatable
    {
        use HasApiTokens, HasFactory, Notifiable;
    }

    6.2 登录签发令牌

    public function login(Request $request)
    {
        $credentials = $request->validate([
            'email' => 'required|email',
            'password' => 'required|string',
        ]);
    
        if (!Auth::attempt($credentials)) {
            return $this->fail('账号或密码错误', 401);
        }
    
        $user = Auth::user();
        // 签发令牌
        $token = $user->createToken('api_token')->plainTextToken;
    
        return $this->success('登录成功', [
            'token' => $token,
            'user' => $user
        ]);
    }

    6.3 鉴权中间件使用

    在路由中添加 auth:sanctum 中间件,保护接口:

    Route::middleware('auth:sanctum')->group(function () {
        Route::get('/user/profile', [AuthController::class, 'profile']);
        Route::apiResource('posts', PostController::class);
    });

    前端请求时,在请求头携带令牌:Authorization: Bearer 令牌值

    6.4 退出登录(销毁令牌)

    public function logout(Request $request)
    {
        $request->user()->currentAccessToken()->delete();
        return $this->success('退出登录成功');
    }

    七、跨域处理:API接口跨域解决方案

    纯后端API需解决跨域问题,Laravel 自带跨域中间件,修改配置文件 config/cors.php

    return [
        'paths' => ['api/*'],
        'allowed_methods' => ['*'],
        'allowed_origins' => ['*'], // 生产环境替换为具体域名
        'allowed_origins_patterns' => [],
        'allowed_headers' => ['*'],
        'exposed_headers' => [],
        'max_age' => 0,
        'supports_credentials' => false,
    ];

    修改后重启服务,跨域中间件自动生效。

    八、API优化与进阶技巧

    8.1 资源响应(格式化输出)

    使用API资源类统一数据返回格式,避免字段冗余:

    # 创建资源类
    php artisan make:resource PostResource

    资源类编写(app/Http/Resources/PostResource.php):

    public function toArray($request)
    {
        return [
            'id' => $this->id,
            'title' => $this->title,
            'content' => $this->content,
            'author' => $this->user->username ?? '',
            'create_time' => $this->created_at->toDateTimeString(),
        ];
    }

    控制器调用:return $this->success('获取成功', PostResource::collection($posts));

    8.2 接口限流(防恶意请求)

    使用Laravel内置限流中间件,限制接口请求频率:

    // 限制1分钟内最多60次请求
    Route::middleware('throttle:60,1')->group(function () {
        Route::post('/login', [AuthController::class, 'login']);
    });

    8.3 软删除与数据恢复

    迁移文件添加软删除字段,模型引入软删除trait,避免物理删除数据:

    // 迁移文件
    $table->softDeletes();
    
    // 模型
    use Illuminate\Database\Eloquent\SoftDeletes;
    class Post extends Model
    {
        use SoftDeletes;
    }

    九、接口调试与启动

    • 启动服务php artisan serve,默认地址 http://127.0.0.1:8000

    • 路由查看php artisan route:list,查看所有API路由详情

    • 调试工具:使用Postman、ApiFox等接口工具调试,无需前端页面

    • 日志查看:日志文件位于 storage/logs/laravel.log,排查后端异常

    需要我帮你导出纯文本Markdown文件,或者精简成一页速查版方便查阅吗?

  • 【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…
    }> ); } “`