首页
归档
友情链接
关于
Search
1
在wsl2中安装archlinux
301 阅读
2
从零开始配置 vim(15)——状态栏配置
176 阅读
3
nvim番外之将配置的插件管理器更新为lazy
165 阅读
4
2018总结与2019规划
154 阅读
5
PDF标准详解(五)——图形状态
131 阅读
软件与环境配置
读书笔记
编程
Thinking
FIRE
菜谱
翻译
登录
Search
标签搜索
c++
c
emacs
elisp
学习笔记
windows
文本操作术
编辑器
NeoVim
Vim
win32
读书笔记
VimScript
linux
文本编辑器
投资理财
Java
反汇编
OLEDB
数据库编程
Masimaro
累计撰写
469
篇文章
累计收到
33
条评论
首页
栏目
软件与环境配置
读书笔记
编程
Thinking
FIRE
菜谱
翻译
页面
归档
友情链接
关于
搜索到
1
篇与
的结果
2026-11-03
《elisp 每日一练》——Emacs的启动加速
在前面的一系列博文中,我们接触到了两种能加速Emacs启动的方案:字节编译和 autoload 机制。今天我们从Emacs启动的角度理解它们是如何做到加速的。方便读者日后能更加灵活的使用这套机制。背景知识下面我们列举一些 Emacs 启动时的关键步骤,顺序大概是这么个顺序,但是某些我们并不关系的步骤没有列举在里面首先当然是我们点击 Emacs程序或者在终端输入 emacs 命令时,正式启动Emacs。early-init.el从 Emacs 27 之后引入了 early-init.el 。它早于传统的 init.el。它初始化的时机及早,它会在 Emacs 初始化包管理系统(package system)和图形界面(GUI)之前被加载。所以我们可以在它里面对GUI显示(如关闭滚动条、调整 Frame 尺寸)进行配置、优化垃圾回收阈值、接管包管理。早期配置入口来自 init.el。但是它加载的时机比较晚,通常在常规初始化完成之后。对于GUI界面来说,我们如果想要禁用滚动条,如果将代码放在 init.el中,那么它会加载GUI,此时GUI界面上有滚动条,随后又加载 init.el 的代码,发现我们要禁止加载滚动条,所以它又会取消滚动条显示,然后刷新界面。当然这是一个极快的过程,因此有时候我们可能会看到界面有一些闪烁。为了解决这些问题,所以提供了一种更早介入的手段。既然 early-init.el 初始化的时机较早,有些资源还没准备好,因此使用时不能无脑的将所有 init.el 中的内容都放到 early-init.el 中。例如,此时 GUI 尚未初始化,任何直接依赖图形界面特性的代码(如设置字体、创建复杂的 UI 组件)都无法可靠运行。包管理接着Emacs 会激活包管理系统。当然如果你想接管这个过程,可以在 early-init.el 中设置 package-enable-at-startup 为 nil Emacs会跳过包管理这步Emacs默认的包管理系统会调用 package-activate-all 激活第三方包。它会通过 package-user-dir 中记录的第三方包的目录进行扫描,并激活里面的每一个包。 我们可以通过 package-load-list 设置加载哪些包 对于每一个包,激活过程主要做两件事:更新 load-path,把该包的目录添加到 Emacs 的 Lisp 搜索路径中, 这样我们后续通过 load 或者 require 时才能找到具体的文件加载 autoload 文件,读取并执行该包目录下的 *-autoloads.el 文件。这个文件里存储了大量“函数占位符”,告诉 Emacs 某个函数将来需要时该去哪个文件加载。如果不存在 *-autoloads.el 文件就加载包的主代码文件。根据主代码文件中的 require 或者 load 加载里面的子代码文件如果执行主代码文件,就会经历执行s表达式,展开宏, 为里面的变量分配内存等操作,但是某些情况下有些函数用户可能并不使用,在启动时一股脑的执行会拖慢启动速度。因此一般都会采用 autoload 机制,以便启动时跳过,未来按需加载。init.el接着会进入 init.el 文件的加载。对于 init.el 中的内容,它会依次执行里面的 S-表达式。遇到 load 或者 require 根据 load-path 中记录的目录去寻找对应的文件,并执行。默认情况下,这部分即使我们在某个配置文件中写下了 autoload 标志,也无法做到启动加速。这是因为emacs 通过执行init.el 或者通过 init.el 中的 require load 加载其他配置时,是在执行 s表达式, 此时的 ;;;###autoload 对lisp计时器来说只是一个注释。真正想做到启动加载还是需要自己生成 autoloads.el 文件。自己生成的 *-autoloads.el 文件有两种方式:因为 *-autoloads.el 本质上还是一个elisp文件,所以我们可以自己手写。利用Emacs 自动扫描为我们生成一个本地的 loaddefs.el 文件我们可以下面的代码来扫描并加载对应的 loaddefs.el 文件;; 1. 指定你的本地插件根目录 (add-to-list 'load-path (expand-file-name "~/.emacs.d/lisp")) ;; 2. 定义自动生成 autoload 索引文件的路径 (setq custom-autoload-file (expand-file-name "~/.emacs.d/lisp/my-loaddefs.el")) ;; 3. 如果文件不存在则自动生成 (unless (file-exists-p custom-autoload-file) (make-directory (file-name-directory custom-autoload-file) t) (loaddefs-generate (expand-file-name "~/.emacs.d/lisp") custom-autoload-file)) ;; 4. 在启动时只加载这个轻量级的存根索引文件 (load custom-autoload-file t)上面代码我们本质上是通过 loaddefs-generate 为目录下 el 文件生成 loaddefs.el 文件。然后利用 load 加载对应的 loaddefs.el 文件。它与前面的 autoloads.el 文件一样,只是记录需要演示加载的函数信息。后面我们将原来 load el 文件改成了 load loaddefs.el 文件。从这里我们应该能深入的理解 autoload 机制,它并不是什么黑魔法,Emacs也无法做到在 require 或者 load 的时候识别 ;;;###autoload 注释,跳过这类函数的执行。它能做的仅仅是在扫描时识别注释并提取函数信息记录到另一个el文件。后面我们也是通过加载这个新生成的 el 文件达到加速的目的。我们无法通过加载原来的 el 文件加速。这就导致了一个问题,如果某个启动时必须执行的函数,与标记了 ;;;###autoload 的函数在同一文件下,而我们又恰好对这个文件生成了对应的 loaddefs.el 文件并加载了这个新生成的文件,这会导致这个必须执行的函数无法执行。所以在使用 autoload 机制时需要注意这一点,我们对于这两种函数应该分开放置Emacs 的新机制在早期的Emacs,对于我们自己的配置代码,需要手动的生成对应的文件。而在最新的 31 版本出现了 User Lisp Directory 机制。简单来说,它将我们放入 ~/.emacs.d/user-lisp/ 中的个人配置代码实现了自动生成对应的 autoload 配置文件。这样就不用我们操心它的生成问题,我们只需要在注释中加上 ;;;###autoload 标志。甚至我们都不需要显示的使用 load 去加载生成的文件,对于 autoload 机制的函数,Emacs会自动帮我们完成自动按需加载。但是对于那些必须启动就加载的代码,仍然需要使用 require 加载对应文件总结本篇我们通过Emacs的启动流程又进一步讨论了启动加速的一些方案:通过 early-init.el 进行优化配置通过 early-init.el 接管包管理系统,按真实流程优化对于第三方package,直接加载autoload 文件,这一步默认由包管理处理,不需要我们操心对于自己的代码,可以通过 loaddefs-generate 生成对应的文件,通过 load 加载对应的 autoload 文件而非原始的el文件如果你是Emacs 31 的用户,可以考虑将代码放入到 ~/.emacs.d/user-lisp/ 让Emacs帮我们自动完成延迟加载机制
2026年11月03日
0 阅读
0 评论
0 点赞