返回顶部
首页 > 资讯 > 后端开发 > GO >Golang template 包基本原理分析
  • 514
分享到

Golang template 包基本原理分析

2024-04-02 19:04:59 514人浏览 薄情痞子
摘要

目录template 概述解析流程Parse阶段Execute阶段小结template 概述 最近在做脚手架相关的内容, 研究了一下 Go 的 text/template 包, 接下

template 概述

最近在做脚手架相关的内容, 研究了一下 Gotext/template 包, 接下来跟大家分享下 template 的基本原理.

golang 的标准库中, 有两个和 template 有关的包, 一个是 html/template, 另外一个是 text/template, 这两个包的主要区别是 html 版本加入了很多对 js字符串 和 html标签 的处理, 下面我们主要用 text/template 作为案例说明.

解析流程

首先, 无论是使用 template.Parse() 还是 template.New() 都会创建一个 *Template 对象, 用来管理整个处理流程.

模板处理主要分为两个阶段, 第一个阶段是parse 阶段, 在这个阶段, 会把读入的数据, 不论是从文件读入还是直接从字符串读取的内容, 统一解析成节点树, 第二个阶段是 execute 阶段, 在这个阶段, 会把传进来的变量解析到节点树上, 生成最终的输出流, 然后写入到 io.Writer 中.

下边我们看一段简单的 使用template 的代码

package main
import (
   "os"
   "text/template"
)
var templateStr = `
package main
func main() {
    fmt.Println("Hello, {{.Name}}!")
}
`
func main() {
   // 创建template对象
   t := template.New("demo")
   // 解析模板内容
   parse, err := t.Parse(templateStr)
   if err != nil {
      panic(err)
   }
   // 执行解析
   data := map[string]string{"Name": "World"}
   err = parse.Execute(os.Stdout, data)
   if err != nil {
      panic(err)
   }
}

运行后可以看到输出:

package main
func main() {
    fmt.Println("Hello, World!")
}

接下来我们就一起来分析下执行过程:

template.New 创建了一个template对象

// src/text/template/template.go
type Template struct {
   name string
   *parse.Tree
   *common
   leftDelim  string
   rightDelim string
}

Parse阶段

其中 *parse.Tree 只在 html/template 中使用, 表示解析完成的模板, *common 里包含了解析过程中所有输入的模板, 用一个 map 存储, 之所以使用map, 是因为 template 支持 {{define block}} 这种嵌套模板, 每一个模板快都会被解析成一个 Template对象, 然后放到这个map里, 供模板之间引用. 下面我们重点看下, 每个模板解析完之后生成的 parse.Tree.

上边的代码生成的模板对象 t 包含的map只有一个元素, 元素的 Root属性(即parse之后生成的 parse.Tree), 会有三个节点.

其中 Textnode 直接存储了文本, ActionNode 会解析出 .Name 变量, 将文本字符串解析成为 parse.Tree 对象之后, Parse阶段就执行结束了, 接下来就是 Execute 阶段.

Execute阶段

// src/text/template/exec.go
func (t *Template) Execute(wr io.Writer, data any) error {
   return t.execute(wr, data)
}
func (t *Template) execute(wr io.Writer, data any) (err error) {
   defer errRecover(&err)
   // 使用反射解析传入的参数
   value, ok := data.(reflect.Value)
   ...
   state := &state{
      tmpl: t,
      wr:   wr,
      vars: []variable{{"$", value}},
   }
   ...
   // 遍历节点
   state.walk(value, t.Root)
   return
}

Execute阶段开始的时候, 会先反射传入的 data, 用来解析模板内的变量, 接下来遍历模板的Root 也就是从节点树的根开始遍历处理每个节点, 这里使用了递归的方式.

// src/text/template/exec.go
func (s *state) walk(dot reflect.Value, node parse.Node) {
   s.at(node)
   switch node := node.(type) {
   case *parse.ActionNode:
      val := s.evalPipeline(dot, node.Pipe)
      if len(node.Pipe.Decl) == 0 {
         s.printValue(node, val)
      }
   ...
   case *parse.ListNode:
       // 循环遍历节点, 递归 
      for _, node := range node.Nodes {
         s.walk(dot, node)
      }
   ...
   case *parse.TextNode:
      if _, err := s.wr.Write(node.Text); err != nil {
         s.writeError(err)
      }
   ...
}

我们看下 ActionNodeTextNode 的处理

  • ActionNode ActionNode 在Parse阶段生成语法树, 在Execute阶段分为两步处理, 第一步, 是解析语法树, 把对应的变量替换成实际的值, 对应方法evalPipeline(), 第二步是把生成的结果输出, 对应了代码printValue()
  • TextNode TextNode 就很简单了, 直接把Node存储的文本原封不动的打印, 直接使用了 Write()方法写入

节点遍历完成之后, 所有的文本已经都输出到 io.Writer 中, 模板执行结束.

至此, 整个流程完成.

小结

text/template 的解析过程主要经历了两个阶段:

  • Parse阶段

在这个阶段, 输入内容经历了从 template.Templateparse.NodeList 再到 parse.Node几个步骤, 将纯文本变成了可以统一处理的节点, 方便后续操作

  • Execute阶段

遍历全部的 parse.Node, 根据不同的规则把每个node的内容处理过之后, 输出到 io.Writer里, 完成执行

template功能用在代码生成上非常简单高效, 诸如脚手架生成基础开发模板, 还有 protobuf 等 IDL 生成代码都很方便,更多的实践应用,请关注编程网其它相关文章!

您可能感兴趣的文档:

--结束END--

本文标题: Golang template 包基本原理分析

本文链接: https://lsjlt.com/news/121157.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • Golang template 包基本原理分析
    目录template 概述解析流程Parse阶段Execute阶段小结template 概述 最近在做脚手架相关的内容, 研究了一下 Go 的 text/template 包, 接下...
    99+
    2024-04-02
  • C++模板template原理解析
    目录前言1. 函数模板1.1函数模板的概念1.2函数模板的格式1.3 函数模板的原理1.4 函数模板的实例化1.4.1 隐式实例化1.4.2 显式实例化1.5 模...
    99+
    2024-04-02
  • 分析和解决golang template失败问题
    Go语言自带的模板引擎template,是一款灵活、高效、易用的模板引擎,它可以快速、简单地生成HTML、XML等格式的文本。但是,有时候我们在使用template过程中,会遇到一些困难和问题,比如template的失败。那么,该如何解决t...
    99+
    2023-05-14
  • 学习golang打包:轻松理解打包的基本原理和操作步骤,从基础开始
    从零开始学习golang打包:轻松掌握打包的基本原理和操作步骤,需要具体代码示例随着Go语言(golang)的快速发展和广泛应用,打包成为了开发者们日常工作的一个重要部分。无论你是初学者还是有一定经验的开发者,掌握打包的基本原理和操作步骤都...
    99+
    2023-12-29
    学习 Golang 打包
  • MySQL分页的基本原理
    这篇文章主要介绍“MySQL分页的基本原理”,在日常操作中,相信很多人在MySQL分页的基本原理问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”MySQL分页的基本原理”的疑惑...
    99+
    2024-04-02
  • gosync.Map基本原理深入解析
    目录引言map 在并发下的问题map 并发读写异常的例子使用 sync.Mutex 保证并发安全使用 sync.RWMutex 保证并发安全有了读写锁为什么还要有 sync.Map?...
    99+
    2023-01-28
    go sync.Map基本原理 go sync.Map
  • Go语言基础闭包的原理分析示例详解
    目录一. 闭包概述二. 代码演示运行结果代码说明一. 闭包概述 闭包就是解决局部变量不能被外部访问的一种解决方案 闭包是把函数当作返回值的一种应用 二. 代码演示...
    99+
    2024-04-02
  • golang匿名函数及闭包原理解析
    是的,go 中的匿名函数可用于快速定义一次性函数或立即执行函数,而闭包则用于将局部变量封锁在匿名函数中,即使后者返回也能访问这些变量。 Go 中的匿名函数和闭包理解 匿名函数是在不定义...
    99+
    2024-05-03
    函数 golang 闭包
  • Docker基本概念和底层原理的示例分析
    这篇文章将为大家详细讲解有关Docker基本概念和底层原理的示例分析,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。Docker架构图:我们依照Docker架构图进行Docker基础概念的说明。1、Dock...
    99+
    2023-06-29
  • webpack打包原理的示例分析
    这篇文章将为大家详细讲解有关webpack打包原理的示例分析,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。webpack打包原理是根据文件间的依赖关系对其进行静态分析,将这些模块按指定规则生成静态资源,当...
    99+
    2023-06-14
  • MySQL分页基本原理有哪些
    这篇文章主要介绍“MySQL分页基本原理有哪些”,在日常操作中,相信很多人在MySQL分页基本原理有哪些问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”MySQL分页基本原理有...
    99+
    2024-04-02
  • flink基本原理
    一、简介 开源流式处理系统在不断地发展,从一开始只关注低延迟指标到现在兼顾延迟、吞吐与结果准确性,在发展过程中解决了很多问题,编程API的易用性也在不断地提高。本文介绍一下 Flink 中的核心概念,这些概念是学习与使用 Flink 十分重...
    99+
    2021-02-28
    flink基本原理
  • 基于JSONP原理的示例分析
    这篇文章主要介绍了基于JSONP原理的示例分析,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。前言我工作以来接触的第一个项目就是前后端分离的,...
    99+
    2024-04-02
  • 深入了解Golang中reflect反射基本原理
    目录反射概述反射基础 - go 的 interface 是怎么存储的反射对象 - reflect.Type 和 reflect.Value反射定律Elem 方法reflect.Val...
    99+
    2023-01-04
    Golang reflect反射原理 Golang reflect反射 Golang reflect
  • Android APK 签名打包原理分析(二)【Android签名原理】
    说到签名,从这个词来理解,正常个人需要签名的时候,一般是用来证明这是某个人的特属认证。 大家是否有印象?还记得我们之前在学习、总结网络相关知识的时候,说到过,客户端和服务端虽然通信数据上,可以采用对称加密和非对称加密组合去进行数据的加密...
    99+
    2023-08-16
    android 签名 打包 消息摘要
  • Android APK 签名打包原理分析(一)【APK结构分析】
    1.引言 最近在看AOSP Apk安装的相关源码时,发现自己对这块知识一直停留到用的层面,并未有深入的了解,例如打包的具体过程、签名的具体过程、渠道打包,最重要的,自己这几年在做系统方面的应用时,也解决过很多apk 安装的问题,修改过部分的...
    99+
    2023-08-19
    android java apk 签名打包
  • Docker基本概念和底层原理解析
    目录1、Docker的底层原理2、Docker中常用的基本概念3、run命令的运行流程4、为什么Docker比VM快Docker架构图: 我们依照Docker架构图进行Docker基...
    99+
    2024-04-02
  • Django中ORM基本应用与原理解析
    目录1.ORM构建数据表2.数据迁移3.Model相关的概念与使用方法Model的组成部分Meta元数据类属性说明Field的通用字段选项基础字段类型关系字段类型多对一一对一多对多关...
    99+
    2024-04-02
  • RxJava 触发流基本原理源码解析
    目录正文触发流小结总结正文 本节,我们从Rxjava使用代码入手,去结合自己已有的知识体系,加查阅部分源码验证的方式,来一起探索一下Rxjava实现的基本原理。 为了本文原理分析环...
    99+
    2022-12-30
    RxJava 触发流原理 RxJava 触发流
  • RxJava构建流基本原理示例解析
    目录正文1.构建流1.1 just的解读1.2 map的解读1.3 subscribeOn、observeOn1.4 小结正文 本节,我们从Rxjava使用代码入手,去结合自己已有...
    99+
    2022-12-30
    RxJava构建流基本原理 RxJava构建流
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作