Ruby 中 load、require 和 require_relative 的详解

4 阅读2分钟

Ruby 中 load、require 和 require_relative 的详解

本文档整理了 load、require 和 require_relative 的作用、区别、应用场景、代码举例,以及 load 的参数。基于 Ruby 3.x 版本的标准行为,内容简洁实用。

1. 作用概述

  • load: 加载并执行指定的 Ruby 文件。每次调用都会重新加载文件,即使文件已经被加载过。这意味着文件中的代码会多次执行。
  • require: 加载并执行指定的 Ruby 文件,但只加载一次。如果文件已经被加载,后续调用会直接返回 true,而不重新执行。require 会搜索 Ruby 的加载路径($LOAD_PATH 或 $:)。
  • require_relative: 类似于 require,但路径是相对于当前文件的目录计算的。只加载一次,不搜索 $LOAD_PATH。

这些方法的主要目的是模块化代码:将代码拆分成多个文件,便于维护和复用。它们返回 true(如果加载成功)或 false(如果 require/require_relative 已加载过),load 总是返回 true。

2. 区别总结

用表格比较区别:

方面loadrequirerequire_relative
加载次数每次调用都重新加载和执行只加载一次(已加载则跳过)只加载一次(已加载则跳过)
路径计算绝对路径或相对当前工作目录搜索 $LOAD_PATH(加载路径)相对当前文件的位置
文件后缀可以省略 .rb,但不推荐可以省略 .rb可以省略 .rb
异常处理文件不存在抛 LoadError文件不存在抛 LoadError文件不存在抛 LoadError
性能影响可能多次执行,适合动态变化高效,一次加载高效,一次加载,路径更可靠
常见用途开发调试、配置文件加载 gem 或标准库项目内部文件依赖
安全性易导致重复加载问题内置重复加载检查内置重复加载检查,路径更明确
  • 关键不同点:load 不检查是否已加载,适合需要重新评估的文件;require 和 require_relative 有内置的 $LOADED_FEATURES 检查,避免重复;require_relative 解决了 require 的路径依赖问题。

3. 应用场景

  • load:

    • 场景:开发环境中,需要反复修改并重新加载文件;或加载可能动态变化的配置文件。
    • 为什么:总是重新执行,能捕捉变化。但生产环境中慎用,避免性能开销或副作用。
    • 示例:IRB 或 Rails console 中测试代码;加载插件脚本。
  • require:

    • 场景:加载第三方 gem、标准库或项目中的共享模块。一旦加载,全局可用。
    • 为什么:标准实践,高效且安全。Ruby 的 gem 系统依赖它。
    • 示例:require 'json';require 'active_record' 在 Rails 中。
  • require_relative:

    • 场景:项目内部文件依赖,尤其是文件结构复杂或在不同目录运行时。
    • 为什么:路径相对当前文件,避免硬编码绝对路径或修改 $LOAD_PATH。
    • 示例:app/models/user.rb 中加载 app/helpers/user_helper.rb。

注意:在脚本顶部通常用 require/require_relative;在测试或动态环境中用 load。优先 require_relative > require > load。

4. 代码举例

假设项目结构:

my_project/
├── main.rb
├── lib/
│   └── utils.rb
└── config/
    └── settings.rb
  • utils.rb

    module Utils
      def greet
        puts "Hello from Utils!"
      end
    end
    puts "Utils loaded at #{Time.now}"
    
  • settings.rb

    SETTINGS = { env: 'development' }
    puts "Settings loaded at #{Time.now}"
    

示例1: load(重新加载)

load 'config/settings.rb'  # 第一次
load 'config/settings.rb'  # 第二次,重新执行
puts SETTINGS[:env]

输出:显示两次加载时间和 development。

示例2: require(只加载一次)

$LOAD_PATH << 'lib'
require 'utils'  # 第一次
require 'utils'  # 第二次,不执行
include Utils
greet

输出:只打印一次加载时间和 "Hello from Utils!"。

示例3: require_relative(相对路径)

require_relative 'lib/utils'  # 第一次
require_relative 'lib/utils'  # 第二次,不执行
include Utils
greet

输出:类似 require。

注意:文件不存在抛 LoadError;在模块化项目中注意副作用。

5. load 的参数

load 的签名:load(filename, wrap = false) → true

  • filename (String, 必选):文件路径(绝对/相对)。不存在抛 LoadError。
  • wrap (Boolean, 可选,默认 false):true 时,在匿名模块中执行,隔离命名空间;false 时,直接在当前作用域执行。

作用:filename 指定源;wrap 用于隔离(Ruby 1.9+)。load 总是重新加载,不搜索 $LOAD_PATH。

示例1: 默认 (wrap=false)

load 'plugin.rb'  # 内容全局可见

示例2: wrap=true

load 'plugin.rb', true  # 内容隔离,不可直接访问

注意:慎用重复加载;避免从用户输入加载文件。