ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Slim 模板引擎 Logic Less 模式完全指南:Mustache 式字典渲染、配置与源码实现

Slim 模板引擎 Logic Less 模式完全指南:Mustache 式字典渲染、配置与源码实现 后端【免费下载链接】slimSlim is a template language whose goal is to reduce the syntax to the essential parts without becoming cryptic.项目地址https://gitcode.com/gh_mirrors/sli/slim点击查看免费下载本文以 Slim 官方文档 doc/logic_less.md及日语版 doc/jp/logic_less.md为核心骨架系统讲解 Slim 的 Logic Less逻辑无逻辑模式如何用纯字典对象驱动模板渲染、条件/反转条件/迭代/拉姆达四种区块的写法、字典访问顺序与self关键字语义并逐一给出 Rails、Sinatra 两种框架下的启用方式与全部可配置选项。文中结合仓库源码与测试用例深入剖析其底层实现读者读完即可在项目中将模板逻辑彻底收敛到数据侧写出完全不带视图代码的 Mustache 风格模板。什么是 Logic Less 模式Slim 默认模板允许在模板内直接编写 Ruby 代码如- if、 helper_method而 Logic Less 模式则走另一条路模板中不出现任何逻辑代码只出现变量名引用渲染结果完全由一份字典对象dictionary驱动。该模式灵感来源于 Mustache 模板思想。字典对象通常是一棵递归的哈希树recursive hash tree其中既包含标量值也包含嵌套的哈希、数组乃至可调用对象动态内容全部由这棵树提供。模板的任务只剩下指出在树的哪个位置、以何种方式展示数据控制流是否显示、是否循环由模板的区块语法隐式表达而不是由 Ruby 代码显式表达。从实现上看Logic Less 模式是一个独立的 Slim 过滤器插件入口文件为 lib/slim/logic_less.rb其核心只有一行注册语句Slim::Engine.after Slim::Interpolation, Slim::LogicLess它把Slim::LogicLess过滤器插入到 Slim 引擎编译管线的Slim::Interpolation之后完整管线见 lib/slim/engine.rb。因此只有当slim/logic_less被 require 时该过滤器才会注册进引擎Logic Less 模式才可用——这也是下文中所有集成步骤的第一步。条件与反转条件Logic Less 的区块section语法是-前缀的控制行。最基本的用法是条件Conditional当区块所指对象非 false 且非空empty?时区块内容才会渲染。- article h1 title只要article在字典中取到的值既不是false也不是空集合h1 title就会被渲染。若article不存在、为false、为nil或为空数组/空字符串/空哈希即满足empty?整块内容被跳过。反转条件Inverted Conditional语法上多一个!语义完全相反对象为false或为空empty?时渲染区块内容通常用于无数据时的兜底提示-! article p Sorry, article not found这两个语义在源码中有精确对应。过滤器 lib/slim/logic_less/filter.rb 的on_slim_control负责把控制行编译为section或inverted_section方法调用def on_slim_control(name, content) method if name ~ /\A!\s*(.*)/ name $1 inverted_section else section end [:block, #{context}.#{method}(#{name.to_sym.inspect}) do, compile(content)] end而运行时上下文 lib/slim/logic_less/context.rb 中的实现则严格对应非 false 且非 empty?的判定规则def inverted_section(name) value scope[name] yield if !value || (value.respond_to?(:empty?) value.empty?) end可以看到反转条件判定的是值为假或对象响应empty?且为空section则反向走有值且非空分支并且对数组对象自动进入迭代逻辑见下节。迭代Iteration当区块引用的对象是数组时区块内容会对数组的每个元素重复渲染一次- articles tr: td title上面的模板会对articles数组中的每一项各渲染一行trtd.../td/tr每次迭代时字典上下文自动切换到当前元素因此title是在当前元素上解析的。这正是递归哈希树思想的体现数组里的每个元素通常又是一个哈希子层字段名继续沿用字典访问规则解析。源码中section的完整逻辑展现了条件与迭代的统一只要值非假若它不是哈希式对象不响应has_key?但响应each就按数组逐元素迭代否则把它当作单一对象推入新作用域执行一次def section(name) if dict scope[name] if !dict.respond_to?(:has_key?) dict.respond_to?(:each) new_scope do dict.each do |d| scope.dict d yield end end else new_scope(dict) { yield } end end end这里的判断也解释了为什么空数组会走反转条件分支空数组是数组但empty?为真外层判断不成立区块被跳过。测试用例 test/logic_less/test_logic_less.rb 中test_symbol_hash验证了数组迭代、test_flag_section验证了布尔开关show_person: true与迭代区块的嵌套组合。拉姆达表达式Lambda与 Mustache 一致Slim 的 Logic Less 模式支持拉姆达Lambda当模板输出一个可调用对象时可以把它当作延迟执行的区块包裹器。语法上带子内容的输出行 person会把 name作为区块体传给该对象 person name对应的拉姆达可以这样定义文档原例def lambda_method div classperson#{yield(name: Andrew)}/div endyield可以接收一个或多个哈希。传入多个哈希时区块体会像数组迭代那样被重复渲染多次每次渲染时字典切换到对应的哈希。这一行为在上下文源码的lambda方法中有完整体现def lambda(name) scope.lambda(name) do |*dict| if dict.empty? yield else new_scope do dict.inject(.dup) do |result, d| scope.dict d result yield end end end end enddict.inject将多次yield的结果按顺序拼接返回拼接后的整段 HTML。测试 test_logic_less.rb 的test_lambda演示了三种典型形态传入单个哈希并包裹输出simple: lambda { |block| div class\simple\#{block.call}/div }对多个名字逐个包裹person拉姆达内部map并拼接一次性传入多个哈希让区块迭代list: lambda { |block| ul#{block.call(*list)}/ul }。此外还有父级作用域上的拉姆达test_render_parent_lambda展示了列表迭代与父字典拉姆达fn的组合使用说明拉姆达解析同样遵循下文的作用域回溯规则。字典访问顺序Dictionary AccessLogic Less 的核心机制是字典访问Dictionary Access模板中的每个裸标识符如title如何从字典对象中取值。Slim 按:dictionary_access选项给定的顺序依次尝试默认顺序为[:symbol, :string, :method, :instance_variable]以示例模板为例- article h1 title对article内部解析title时Slim 依次尝试以下四种方式:symbol符号键若article.respond_to?(:has_key?)且article.has_key?(:title)执行article[:title]:string字符串键若article.respond_to?(:has_key?)且article.has_key?(title)执行article[title]:method方法若article.respond_to?(:title)执行article.send(:title):instance_variable实例变量若article.instance_variable_defined?(title)执行article.instance_variable_get title。如果四种方式全部失败Slim 不会报错而是把title的解析沿父作用域链向上回溯——即对当前对象的父对象按同样的顺序再试一遍。本例中父对象就是渲染模板时所针对的根字典对象。这套逻辑来自Scope类的链式结构每个Scope持有parent自身查找失败时调用parent[name]见 lib/slim/logic_less/context.rb 的Scope#[]与Scope#lambda。同理区块名article本身也是从根字典对象按相同步骤解析的。作用域链通过Context#new_scope维护进入区块或拉姆达迭代时压入新Scope退出时弹出从而保证嵌套区块的上下文隔离与回溯顺序正确def new_scope(dict nil) scope Scope.new(dict, scope.lookup, scope) yield ensure scope.pop end文档还强调了两条使用约束视图代码中不允许声明实例变量但 Slim 可以找到并使用它们——模板中只需去掉前缀书写。这意味着直接给字典对象设置title ...模板里写 title即可取到。不允许参数化方法调用。四种访问方式里没有带参数调用的位置模板中的裸标识符永远被编译为无参取值。从过滤器源码可以印证access(name)对普通标识符生成context[name.to_sym]形式不存在传参路径。值得注意的一个细节是:method分支使用respond_to?(name, false)见Scope#[]与Scope#lambda源码即不包含私有方法测试test_method_access_without_private专门验证了这一点——被设为私有的age方法不会被调用对应输出为空。test_dictionary_option还展示了一个实用变体dictionary: hash可以直接把字典指向渲染作用域对象的某个实例变量配合Scope.new内部以hash存储数据即可让模板读取对象内部数据。字符串与 self 关键字self是 Logic Less 模式中内置的关键字返回当前考察元素即当前作用域字典对象的.to_s值。它最典型的用途是渲染纯字符串数组的元素。给定字典{ article: [ Article 1, Article 2 ] }以及模板- article tr: td self渲染结果为tr tdArticle 1/td /tr tr tdArticle 2/td /tr迭代时当前元素切换为Article 1、Article 2self的to_s依次返回这两个字符串。测试用例test_to_s_access验证的正是该行为people: [Joe, Jack]配合.name self。实现上过滤器把self编译为context.to_s见access方法的when self分支上下文最终委托给当前Scope#to_s即dict.to_s。属性、splat 与插值扩展文档未单独展开、但源码与测试可确认的扩展能力包括属性值引用字典on_slim_attrvalue会把属性值也走字典访问因此b namename Person、a idname age这类写法中name、age都会按字典规则解析测试test_ruby_attributes。布尔属性当属性值为真/假时按 HTML 布尔属性规则输出。测试test_boolean_attributes中input checkedselected对selected: true的元素输出checked对无selected键的元素不输出该属性。splat 展开on_slim_splat支持*attrs式属性展开同样走字典访问。yield保留access方法对yield原样放行使模板可透传外部块内容——测试test_render_with_yield中 yield直接输出了 render 时传入的块结果This is the menu。插值转义test_escaped_interpolation确认模板中\#{...}会被转义为字面文本#{...}不会被当作 Ruby 插值执行。HTML 转义普通输出默认转义输出不转义测试test_lambda中拉姆达返回的 HTML 用 person原样输出。嵌入式代码限制Logic Less 模式的定位是模板零逻辑因此过滤器对嵌入式 Ruby 代码采取硬性禁止策略。在 lib/slim/logic_less/filter.rb 中def on_dynamic(code) raise Temple::FilterError, Embedded code is forbidden in logic less mode end def on_code(code) raise Temple::FilterError, Embedded code is forbidden in logic less mode end任何在模板中直接书写 Ruby 代码的行如- code或动态插值#{...}都会在编译期抛出Temple::FilterError提示信息为 Embedded code is forbidden in logic less mode。这是该模式与传统 Slim 写法最大的差异点模板内唯一的逻辑载体就是字典中的拉姆达与区块结构。在 Rails 中使用 Logic Less 模式安装$ gem install slim在 Gemfile 中声明并要求 logic_less 插件gem slim, require: slim/logic_lessrequire: slim/logic_less是 Rails 场景下启用的关键它加载 lib/slim/logic_less.rb从而把Slim::LogicLess过滤器注册进引擎管线。若只想对少数 action 启用 Logic Less应先全局关闭Slim::Engine.set_options logic_less: false然后在具体 action 中按渲染调用临时开启class Controller def action Slim::Engine.with_options(logic_less: true) do render end end endset_options/with_options是引擎的全局/局部选项切换机制前者持久地修改默认配置后者在块内临时覆盖、块结束后恢复该机制同样被 lib/slim/command.rb 等模块用于切换pretty等选项。需要提醒的是with_options修改的是引擎级配置实际渲染选项最终经由Slim::Template与引擎编译参数生效参见测试辅助 test/core/helper.rb 中Slim::Template.new(options[:file], options)的传参方式。在 Sinatra 中使用 Logic Less 模式Sinatra 本身内置 Slim 支持启用 Logic Less 只需要在config.ru中 require 插件require slim/logic_lessrequire 之后即可直接使用。若只对个别路由启用同样先全局关闭Slim::Engine.set_options logic_less: false再在路由的渲染调用中开启get /page slim :page, logic_less: true endlogic_less: true作为渲染选项传入slim :page实现按路由粒度的开关控制而无需改动全局配置。选项一览Logic Less 模式全部可配置选项定义于过滤器 lib/slim/logic_less/filter.rb 的define_options汇总如下类型名称默认值用途布尔值:logic_lesstrue启用 Logic Less 模式要求已 requireslim/logic_less字符串:dictionaryself变量查找所针对的字典引用默认即当前渲染作用域符号 / 符号数组:dictionary_access[:symbol, :string, :method, :instance_variable]字典访问顺序取值仅限这四种方式关于选项的几点源码级细节:dictionary默认值为self表示从当前渲染作用域对象上解析字典。可改为其它表达式字符串如测试中的hash、ViewEnv.new过滤器会将其原样嵌入生成的上下文构造代码::Slim::LogicLess::Context.new(#{options[:dictionary]}, #{access})见call方法。:dictionary_access在过滤器初始化时会做合法性校验传入值被flatten.compact展平任何不在DEFAULT_ACCESS_ORDER内的取值都会抛出ArgumentError, Invalid dictionary access ...展平后为空则抛出ArgumentError, Option dictionary access is missing。因此你可以把顺序调整为[:method, :symbol, :string]等任意排列或只保留部分方式测试test_string_access即用dictionary_access: :string只启用字符串键访问模板仍能正确渲染。校验逻辑中的flatten支持传入[:symbol, :string]这种数组形式与选项表格中的Symbol/ArraySymbol类型声明吻合。与测试用例交叉验证仓库中的 test/logic_less/test_logic_less.rb 是本文所有行为描述的一手验证来源覆盖矩阵包括test_symbol_hash/test_string_hash/test_symbol_access/test_string_access哈希键为符号或字符串时按对应访问方式取值test_method_access/test_method_access_without_private方法访问及其对私有方法的排除test_instance_variable_access实例变量访问person、name经去前缀后在模板中引用test_to_s_accessself关键字对字符串元素的输出test_dictionary_optiondictionary选项指向实例变量test_flag_section/test_inverted_section/test_conditional_parent条件、反转条件、父级作用域回溯test_lambda/test_render_parent_lambda/test_render_with_yield拉姆达的三种形态、父级拉姆达与yield透传test_ruby_attributes/test_boolean_attributes属性值引用字典与布尔属性test_escaped_interpolation\#{}的转义输出。小结Logic Less 模式把 Slim 从可编程模板变成纯声明式模板所有变量经:dictionary_access的四种顺序在字典树及父作用域链上解析控制流收敛为-/-!区块与数组迭代动态内容由拉姆达在字典侧提供模板内任何 Ruby 代码都会在编译期被拒绝。启用路径只有一条——requireslim/logic_less——之后即可在 Rails 中通过 Gemfile require 或Slim::Engine.set_options/with_options精细控制在 Sinatra 中通过config.rurequire 与渲染选项按路由切换。对于追求模板与逻辑彻底分离的团队而言这是一个现成的、有完整测试背书test/logic_less/test_logic_less.rb的 Mustache 风格替代方案。赞分享后端【免费下载链接】slimSlim is a template language whose goal is to reduce the syntax to the essential parts without becoming cryptic.项目地址https://gitcode.com/gh_mirrors/sli/slim点击查看免费下载相关推荐Slim模板引擎中的Logic Less模式详解Slim模板引擎中的Logic Less模式详解 痛点视图层逻辑复杂度的困境 在日常Web开发中你是否经常遇到这样的困境视图模板中Ruby代码与HTML标后端Yeti Affix 组件指南用纯 CSS 打造共享边框与等高的一体化控件组Yeti Affix 组件指南用纯 CSS 打造共享边框与等高的一体化控件组 导读 Affix 是 Yeti一个 CSS first、零构建的原生布局与样式后端amis 模板渲染完全指南模板字符串、表达式与 JavaScript 模板引擎实战amis 模板渲染完全指南模板字符串、表达式与 JavaScript 模板引擎实战 amis 是前端低代码框架通过 JSON 配置即可生成各类页面。为了让文前端低代码UI组件上一篇拖不动的窗口怎么改用 WindowResizer 三步强制调整窗口大小下一篇BilibiliCacheVideoMerge三步把B站缓存碎片变成MP4支持安卓5.0~13创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表