1.HTTP及Template介绍

news2024/11/15 11:52:12

目录

来源

介绍

模板与渲染

Go语言的模板引擎

模板引擎的使用

定义模板文件

解析模板文件

模板渲染

基本示例

模板语法

{{.}}

注释

pipeline

变量

移除空格

条件判断

range

with

预定义函数

比较函数

自定义函数

模板的嵌套template

block

修改默认的标识符

text/template与html/tempalte的区别


来源

https://www.liwenzhou.com/posts/Go/go_template/

介绍

html/template包实现了数据驱动的模板,用于生成可防止代码注入的安全的HTML内容。它提供了和text/template包相同的接口,Go语言中输出HTML的场景都应使用html/template这个包。

模板与渲染

        在一些前后端不分离的Web架构中,我们通常需要在后端将一些数据渲染到HTML文档中,从而实现动态的网页(网页的布局和样式大致一样,但展示的内容并不一样)效果。

        我们这里说的模板可以理解为事先定义好的HTML文档文件,模板渲染的作用机制可以简单理解为文本替换操作–使用相应的数据去替换HTML文档中事先准备好的标记。

        很多编程语言的Web框架中都使用各种模板引擎,比如Python语言中Flask框架中使用的jinja2模板引擎。

Go语言的模板引擎

        Go语言内置了文本模板引擎text/template和用于HTML文档的html/template。它们的作用机制可以简单归纳如下:

  1. 模板文件通常定义为.tmpl.tpl为后缀(也可以使用其他的后缀),必须使用UTF8编码。
  2. 模板文件中使用{{}}包裹和标识需要传入的数据。
  3. 传给模板这样的数据就可以通过点号(.)来访问,如果数据是复杂类型的数据,可以通过{ { .FieldName }}来访问它的字段。
  4. {{}}包裹的内容外,其他内容均不做修改原样输出。

模板引擎的使用

        Go语言模板引擎的使用可以分为三部分:定义模板文件、解析模板文件和模板渲染.

定义模板文件

        其中,定义模板文件时需要我们按照相关语法规则去编写,后文会详细介绍。

解析模板文件

        上面定义好了模板文件之后,可以使用下面的常用方法去解析模板文件,得到模板对象:

func (t *Template) Parse(src string) (*Template, error)
func ParseFiles(filenames ...string) (*Template, error)
func ParseGlob(pattern string) (*Template, error)

        当然,你也可以使用func New(name string) *Template函数创建一个名为name的模板,然后对其调用上面的方法去解析模板字符串或模板文件。

模板渲染

        渲染模板简单来说就是使用数据去填充模板,当然实际上可能会复杂很多。

func (t *Template) Execute(wr io.Writer, data interface{}) error
func (t *Template) ExecuteTemplate(wr io.Writer, name string, data interface{}) error

基本示例

        定义模板文件

        我们按照Go模板语法定义一个hello.tmpl的模板文件,内容如下:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <title>hello</title>
</head>
<body>
    <h1> hello golang</h1>
    <h1>hello {{.}}</h1>
</body>
</html>

解析和渲染模板文件

然后我们创建一个main.go文件,在其中写下HTTP server端代码如下:

package main

import (
	"fmt"
	"html/template"
	"net/http"
	"os"
)

func sayHello(w http.ResponseWriter, r *http.Request) {
	// 获取项目的绝对路径
	wd, err := os.Getwd()
	if err != nil {
		fmt.Printf("get wd failed, err:%v \n", wd)
		return
	}
	fmt.Println("wd:", wd + "\\lesson04\\hello.tmpl")
	// 解析指定文件生成模板对象
	tmpl, err := template.ParseFiles( wd + "\\lesson04\\hello.tmpl")

	if err != nil {
		fmt.Println("create template failed, err:", err)
		return
	}
	// 利用给定数据渲染模板,并将结果写入w
	tmpl.Execute(w, "沙河小王子")
}
func main() {
	http.HandleFunc("/", sayHello)
	err := http.ListenAndServe(":9090", nil)
	if err != nil {
		fmt.Println("HTTP server failed,err:", err)
		return
	}
}

将上面的main.go文件编译执行,然后使用浏览器访问http://127.0.0.1:9090就能看到页面上显示了“Hello 沙河小王子”。 这就是一个最简单的模板渲染的示例,Go语言模板引擎详细用法请往下阅读。+

得到运行结果

模板语法

{{.}}

模板语法都包含在{{}}中间,其中{{.}}中的点表示当前对象。

当我们传入一个结构体对象时,我们可以根据.来访问结构体的对应字段。例如:

package main

import (
	"fmt"
	"html/template"
	"net/http"
	"os"
)

//定义用户结构体
type User struct {
	Name string
	Gender string
	Age int
}

func sayHello(w http.ResponseWriter, r *http.Request) {
	// 获取项目的绝对路径
	wd, err := os.Getwd()
	if err != nil {
		fmt.Printf("get wd failed, err:%v \n", wd)
		return
	}
	fmt.Println("wd:", wd + "\\lesson05\\hello.tmpl")
	// 解析指定文件生成模板对象
	tmpl, err := template.ParseFiles( wd + "\\lesson05\\hello.tmpl")

	if err != nil {
		fmt.Println("create template failed, err:", err)
		return
	}

	u1 := User{
		Name: "小王子",
		Gender: "男",
		Age: 10,
	}

	// 利用给定数据渲染模板,并将结果写入w
	tmpl.Execute(w, u1)
}
func main() {
	http.HandleFunc("/", sayHello)
	err := http.ListenAndServe(":9090", nil)
	if err != nil {
		fmt.Println("HTTP server failed,err:", err)
		return
	}
}

模板文件hello.tmpl内容如下:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Hello</title>
</head>
<body>
<h1>姓名: {{.Name}}</h1>
<h1>性别: {{.Gender}}</h1>
<h1>年龄: {{.Age}}</h1>
</body>
</html>

在浏览器输入如下网址

http://localhost:9090/sayHello

能够渲染出我们结构体中的值

 

同理,当我们传入的变量是map时,也可以在模板文件中通过.根据key来取值。

// 采用一个map
m1 := map[string]interface{}{
    "Name": "小王子",
    "Age": 18,
    "Gender": "男",
}

// 利用给定数据渲染模板,并将结果写入w
tmpl.Execute(w, m1)

如果我们想把map 和 结构体都传递到前端,那么就需要在定义一个大的map来进行存储

// 采用结构体
	u1 := User{
		Name: "小王子",
		Gender: "男",
		Age: 10,
	}

	// 采用一个map
	m1 := map[string]interface{}{
		"Name": "小王子",
		"Age": 18,
		"Gender": "男",
	}

	m2 := map[string]interface{}{
		"map": m1,
		"user": u1,
	}

注释

{{/* a comment */}}
注释,执行时会忽略。可以多行。注释不能嵌套,并且必须紧贴分界符始止。

pipeline

pipeline是指产生数据的操作。比如{{.}}{{.Name}}等。Go的模板语法中支持使用管道符号|链接多个命令,用法和unix下的管道类似:|前面的命令会将运算结果(或返回值)传递给后一个命令的最后一个位置。

**注意:**并不是只有使用了|才是pipeline。Go的模板语法中,pipeline的概念是传递数据,只要能产生数据的,都是pipeline

变量

我们还可以在模板中声明变量,用来保存传入模板的数据或其他语句生成的结果。具体语法如下:

$obj := {{.}}

其中$obj是变量的名字,在后续的代码中就可以使用该变量了。

移除空格

有时候我们在使用模板语法的时候会不可避免的引入一下空格或者换行符,这样模板最终渲染出来的内容可能就和我们想的不一样,这个时候可以使用{{-语法去除模板内容左侧的所有空白符号, 使用-}}去除模板内容右侧的所有空白符号。

例如:

{{- .Name -}}

注意:-要紧挨{{}},同时与模板值之间需要使用空格分隔。

条件判断

Go模板语法中的条件判断有以下几种:

{{if pipeline}} T1 {{end}}

{{if pipeline}} T1 {{else}} T0 {{end}}

{{if pipeline}} T1 {{else if pipeline}} T0 {{end}}

range

Go的模板语法中使用range关键字进行遍历,有以下两种写法,其中pipeline的值必须是数组、切片、字典或者通道。

{{range pipeline}} T1 {{end}}
如果pipeline的值其长度为0,不会有任何输出

{{range pipeline}} T1 {{else}} T0 {{end}}
如果pipeline的值其长度为0,则会执行T0。

with

{{with pipeline}} T1 {{end}}
如果pipeline为empty不产生输出,否则将dot设为pipeline的值并执行T1。不修改外面的dot。

{{with pipeline}} T1 {{else}} T0 {{end}}
如果pipeline为empty,不改变dot并执行T0,否则dot设为pipeline的值并执行T1。

预定义函数

执行模板时,函数从两个函数字典中查找:首先是模板函数字典,然后是全局函数字典。一般不在模板内定义函数,而是使用Funcs方法添加函数到模板里。

预定义的全局函数如下:

and
    函数返回它的第一个empty参数或者最后一个参数;
    就是说"and x y"等价于"if x then y else x";所有参数都会执行;
or
    返回第一个非empty参数或者最后一个参数;
    亦即"or x y"等价于"if x then x else y";所有参数都会执行;
not
    返回它的单个参数的布尔值的否定
len
    返回它的参数的整数类型长度
index
    执行结果为第一个参数以剩下的参数为索引/键指向的值;
    如"index x 1 2 3"返回x[1][2][3]的值;每个被索引的主体必须是数组、切片或者字典。
print
    即fmt.Sprint
printf
    即fmt.Sprintf
println
    即fmt.Sprintln
html
    返回与其参数的文本表示形式等效的转义HTML。
    这个函数在html/template中不可用。
urlquery
    以适合嵌入到网址查询中的形式返回其参数的文本表示的转义值。
    这个函数在html/template中不可用。
js
    返回与其参数的文本表示形式等效的转义JavaScript。
call
    执行结果是调用第一个参数的返回值,该参数必须是函数类型,其余参数作为调用该函数的参数;
    如"call .X.Y 1 2"等价于go语言里的dot.X.Y(1, 2);
    其中Y是函数类型的字段或者字典的值,或者其他类似情况;
    call的第一个参数的执行结果必须是函数类型的值(和预定义函数如print明显不同);
    该函数类型值必须有1到2个返回值,如果有2个则后一个必须是error接口类型;
    如果有2个返回值的方法返回的error非nil,模板执行会中断并返回给调用模板执行者该错误;

比较函数

布尔函数会将任何类型的零值视为假,其余视为真。

下面是定义为函数的二元比较运算的集合:

eq      如果arg1 == arg2则返回真
ne      如果arg1 != arg2则返回真
lt      如果arg1 < arg2则返回真
le      如果arg1 <= arg2则返回真
gt      如果arg1 > arg2则返回真
ge      如果arg1 >= arg2则返回真

为了简化多参数相等检测,eq(只有eq)可以接受2个或更多个参数,它会将第一个参数和其余参数依次比较,返回下式的结果:

{{eq arg1 arg2 arg3}}

比较函数只适用于基本类型(或重定义的基本类型,如”type Celsius float32”)。但是,整数和浮点数不能互相比较。

自定义函数

Go的模板支持自定义函数。

package main

import (
	"fmt"
	"html/template"
	"net/http"
	"os"
)

/**
 * @Description f1函数
 * @Param 
 * @return
 **/
func f1(w http.ResponseWriter, r *http.Request) {
	// 定义模板
	// 解析模板
	// 获取项目的绝对路径
	wd, err := os.Getwd()
	if err != nil {
		fmt.Printf("get wd failed, err:%v \n", wd)
		return
	}

	// 定义一个自定义函数
	// 要么只有一个返回值,要么有两个返回值,第二个返回值必须是error类型
	kua := func(name string)(string, error) {
		return name + "年轻又帅气!", nil
	}

	// 创建一个名字为f的模板对象。注意,这个名字一定要和模板的名字对应上
	tmpl := template.New("hello.tmpl")

	// 告诉模板引擎,我现在多了一个自定义的函数kua
	tmpl.Funcs(template.FuncMap{
		"kua": kua,
	})

	// 解析模板
	_, err = tmpl.ParseFiles( wd + "\\lesson06\\hello.tmpl")
	if err != nil {
		fmt.Printf("parse template failed, err:%v \n", err)
		return
	}

	// 采用一个map
	m1 := map[string]interface{}{
		"Name": "小王子",
		"Age": 18,
		"Gender": "男",
	}

	// 渲染模板
	tmpl.Execute(w, m1)
}
func main() {
	http.HandleFunc("/hello", f1)
	err := http.ListenAndServe(":9090", nil)
	if err != nil {
		fmt.Println("HTTP server failed,err:", err)
		return
	}
}

我们可以在模板文件hello.tmpl中按照如下方式使用我们自定义的kua函数了。

{{kua .Name}}

最后运行的结果

模板的嵌套template

我们可以在template中嵌套其他的template。这个template可以是单独的文件,也可以是通过define定义的template。

举个例子: t.tmpl文件内容如下:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>tmpl test</title>
</head>
<body>
    
    <h1>测试嵌套template语法</h1>
    <hr>
    {{template "ul.tmpl"}}
    <hr>
    {{template "ol.tmpl"}}
</body>
</html> 

{{ define "ol.tmpl"}}
<ol>
    <li>吃饭</li>
    <li>睡觉</li>
    <li>打豆豆</li>
</ol>
{{end}}

ul.tmpl文件内容如下:

<ul>
    <li>注释</li>
    <li>日志</li>
    <li>测试</li>
</ul>

我们注册一个templDemo路由处理函数.

http.HandleFunc("/tmpl", tmplDemo)

tmplDemo函数的具体内容如下:

func tmplDemo(w http.ResponseWriter, r *http.Request) {
	tmpl, err := template.ParseFiles("./t.tmpl", "./ul.tmpl")
	if err != nil {
		fmt.Println("create template failed, err:", err)
		return
	}
	user := UserInfo{
		Name:   "小王子",
		Gender: "男",
		Age:    18,
	}
	tmpl.Execute(w, user)
}

注意:在解析模板时,被嵌套的模板一定要在后面解析,例如上面的示例中t.tmpl模板中嵌套了ul.tmpl,所以ul.tmpl要在t.tmpl后进行解析。

block

{{block "name" pipeline}} T1 {{end}}

block是定义模板{{define "name"}} T1 {{end}}和执行{{template "name" pipeline}}缩写,典型的用法是定义一组根模板,然后通过在其中重新定义块模板进行自定义。

定义一个根模板templates/base.tmpl,内容如下:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <title>Go Templates</title>
</head>
<body>
<div class="container-fluid">
    {{block "content" . }}{{end}}
</div>
</body>
</html>

然后定义一个templates/index.tmpl,”继承”base.tmpl

{{template "base.tmpl"}}

{{define "content"}}
    <div>Hello world!</div>
{{end}}

然后使用template.ParseGlob按照正则匹配规则解析模板文件,然后通过ExecuteTemplate渲染指定的模板:

func index(w http.ResponseWriter, r *http.Request){
	tmpl, err := template.ParseGlob("templates/*.tmpl")
	if err != nil {
		fmt.Println("create template failed, err:", err)
		return
	}
	err = tmpl.ExecuteTemplate(w, "index.tmpl", nil)
	if err != nil {
		fmt.Println("render template failed, err:", err)
		return
	}
}

如果我们的模板名称冲突了,例如不同业务线下都定义了一个index.tmpl模板,我们可以通过下面两种方法来解决。

  1. 在模板文件开头使用{{define 模板名}}语句显式的为模板命名。
  2. 可以把模板文件存放在templates文件夹下面的不同目录中,然后使用template.ParseGlob("templates/**/*.tmpl")解析模板。

修改默认的标识符

Go标准库的模板引擎使用的花括号{{}}作为标识,而许多前端框架(如Vue和 AngularJS)也使用{{}}作为标识符,所以当我们同时使用Go语言模板引擎和以上前端框架时就会出现冲突,这个时候我们需要修改标识符,修改前端的或者修改Go语言的。这里演示如何修改Go语言模板引擎默认的标识符:

template.New("test").Delims("{[", "]}").ParseFiles("./t.tmpl")

最后我们在渲染的时候

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>自定义模板函数</title>
</head>
<body>
<h1>姓名: {[.Name]}</h1>
<h1>性别: {[.Gender]}</h1>
<h1>年龄: {[.Age]}</h1>
</body>
</html>

最后运行结果,发现也能够正常显示

text/template与html/tempalte的区别

html/template针对的是需要返回HTML内容的场景,在模板渲染过程中会对一些有风险的内容进行转义,以此来防范跨站脚本攻击。

例如,我定义下面的模板文件:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Hello</title>
</head>
<body>
    {{.}}
</body>
</html>

这个时候传入一段JS代码并使用html/template去渲染该文件,会在页面上显示出转义后的JS内容。 alert('嘿嘿嘿') 这就是html/template为我们做的事。

但是在某些场景下,我们如果相信用户输入的内容,不想转义的话,可以自行编写一个safe函数,手动返回一个template.HTML类型的内容。示例如下:

func xss(w http.ResponseWriter, r *http.Request){
	tmpl,err := template.New("xss.tmpl").Funcs(template.FuncMap{
		"safe": func(s string)template.HTML {
			return template.HTML(s)
		},
	}).ParseFiles("./xss.tmpl")
	if err != nil {
		fmt.Println("create template failed, err:", err)
		return
	}
	jsStr := `<script>alert('嘿嘿嘿')</script>`
	err = tmpl.Execute(w, jsStr)
	if err != nil {
		fmt.Println(err)
	}
}

这样我们只需要在模板文件不需要转义的内容后面使用我们定义好的safe函数就可以了。

{{ . | safe }}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/356514.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

加油站会员管理小程序实战开发教程13

我们上一篇讲解了会员注册的功能,本篇我们介绍一下会员开卡的功能。 会员注册之后,可以进行开卡的动作。一个会员可以有多张会员卡,在微搭中用来描述这种一对多的关系的,我们用关联关系来表达。 登录微搭的控制台,点击数据模型,点击新建数据模型 输入数据源的名称会员卡…

基于 Debain11 构建 asp.net core 6.x 的基础运行时镜像

基于 Debain11 构建 asp.net core 6.x 的基础运行时镜像Linux 环境说明Debian 简介Debian 发行版本关于 Debian 11Linux 常用基础工具Dockerfile 中 RUN 指令RUN 语法格式RUN 语义说明编写 Dockerfile 构建 Runtime 基础镜像ASP.NET Core Runtime 基础镜像Dockerfile 编写Windo…

sklearn主成分分析PCA

文章目录基本原理PCA类图像降维与恢复基本原理 PCA&#xff0c;即主成分分析(Principal components analysis)&#xff0c;顾名思义就是把矩阵分解成简单的组分进行研究&#xff0c;而拆解矩阵的主要工具是线性变换&#xff0c;具体形式则是奇异值分解。 设有mmm个nnn维样本X…

【面试题】数组reduce的用法

1. 对数组求和 传统的数组求和方式需要使用forEach循环遍历数组中的每一个元素&#xff0c;然后累加。然而这种方式需要新增一个用于存储累加结果的变量。 function sum(arr) {let res 0arr.forEach(element > res res element);return res }还可以通过数组的reduce方法…

算法笔记(九)—— 暴力递归

暴力递归&#xff08;尝试&#xff09; 1. 将问题转化为规模缩小了的同类问题子问题 2. 有明确的不需要的继续递归的条件 3. 有当得到子问题结果之后的决策过程 4. 不记录每一个子问题的解 Question&#xff1a;经典汉诺塔问题 1. 理解清楚&#xff0c;基础三个圆盘的移动…

2.5|iot|嵌入式Linux系统开发与应用|第4章:Linux外壳shell脚本程序编程

1.shell基础 Shell是Linux操作系统内核的外壳&#xff0c;它为用户提供使用操作系统的命令接口。 用户在提示符下输入的每个命令都由shell先解释然后发给Linux内核&#xff0c;所以Linux中的命令通称为shell命令。 通常我们使用shell来使用Linux操作系统。Linux系统的shell是…

高斯课堂 计算机网络(上)

第一章、概述 0、第一章导图 1、计算机网络概述 &#xff08;1&#xff09;计算机网络发展历史1&#xff1a;ARPANET->互联网 小写internet是一个宽泛的概念&#xff0c;指的就是互联网 大写Internet是一个专有名词&#xff0c;指的就是我们现在所使用的这样一个全球最大的…

Python(1)变量的命名规则

目录 1.变量的命名原则 3.内置函数尽量不要做变量 4.删除变量和垃圾回收机制 5.结语 参考资料 1.变量的命名原则 ①由英文字母、_(下划线)、或中文开头 ②变量名称只能由英文字母、数字、下画线或中文字所组成。 ③英文字母大小写不相同 实例&#xff1a; 爱_aiA1 print(…

2023VNCTF的两道(暂时)

from http://v2ish1yan.top/2023/02/19/%E6%AF%94%E8%B5%9Bwp/2023vnctf/ 比赛的时候在回学校的路上&#xff0c;所以没有打&#xff0c;听说质量挺高&#xff0c;赛后做一下 象棋王子 一个普通的js游戏&#xff0c;玩过关了就给flag&#xff0c;所以flag肯定在前端源码里 这…

设计模式之观察者模式与访问者模式详解和应用

目录1.访问者模式详解1.1 访问者模式的定义1.1.1 访问者模式在生活中的体现1.1.2 访问者模式的适用场景1.2 访问者模式的通用实现1.3 访问者模式的使用案例之KPI考核1.3.1 类图设计1.3.2 代码实现1.4 访问者模式扩展---分派1.4.1 java中静态分派示例代码1.4.2 java中动态分派1.…

C语言 基于Ncurse库的贪吃蛇游戏项目

为了敲键盘及时响应&#xff0c;需要用到ncurse 测试代码&#xff1a; ncurse1.c /* ncurse1.c */ #include <curses.h> //ncurse的头文件。int main() {char c;int i 0;//ncurse界面的初始化函数。initscr(); for(i0;i<2;i){c getch();printw("\n");//…

一起学 pixijs(2):修改图形属性

大家好&#xff0c;我是前端西瓜哥。 我们做动画、游戏、编辑器&#xff0c;需要根据用户的交互等操作&#xff0c;去实时地改变图形的属性&#xff0c;比如位置&#xff0c;颜色等信息。今天西瓜哥带大家来看看在 pixijs 怎么修改图形的属性。 因为 pixijs 的底层维护了图形…

2023年美赛C题Wordle预测问题三、四建模及Python代码详细讲解

更新时间:2023-2-19 16:30 相关链接 &#xff08;1&#xff09;2023年美赛C题Wordle预测问题一建模及Python代码详细讲解 &#xff08;2&#xff09;2023年美赛C题Wordle预测问题二建模及Python代码详细讲解 &#xff08;3&#xff09;2023年美赛C题Wordle预测问题三、四建模…

Android 基础知识4-2.8 TableLayout(表格布局)详解

一、TableLayout的概述 表格布局是以行数和列数来确定位置进行排列。就像一间教室&#xff0c;确定好行数与列数就能让同学有序入座。 注意&#xff1a;我们需要先添加<TableRow容器&#xff0c;每添加一个就会多一行&#xff0c;然后再往<TableRow容器中添加其它组件。…

研报精选230219

目录 【行业230219山西证券】煤炭行业周报&#xff1a;复工改善&#xff0c;港口价格企稳反弹【行业230219中航证券】农林牧渔行业周观点&#xff1a;一号文件落地&#xff0c;生物育种超势不改【行业230219华西证券】汽车行业周报&#xff1a;新车密集上市 自主转型提速【个股…

[vue3] pinia的基本使用

使用Pinia npm install piniastore文件里index.js import { createPinia } from piniaconst pinia createPinia()export default piniamain.js导入并引用 import { createApp } from vue import App from ./App.vue import pinia from ./storescreateApp(App).use(pinia).m…

「技术选型」深度学习软件如何选择?

深度学习(DL, Deep Learning)是机器学习(ML, Machine Learning)领域中一个新的研究方向&#xff0c;它被引入机器学习使其更接近于最初的目标——人工智能(AI, Artificial Intelligence)。 深度学习是学习样本数据的内在规律和表示层次&#xff0c;这些学习过程中获得的信息对…

【Flutter入门到进阶】Dart进阶篇---DartVM单线程设计原理

1 虚拟机的指令执行设计 1.1 虚拟机的分类 基于栈的虚拟机&#xff0c;比如JVM虚拟机 基于寄存器的虚拟机&#xff0c;比如Dalvik虚拟机 1.2 虚拟机的概念 首先问一个基本的问题&#xff0c;作为一个虚拟机&#xff0c;它最基本的要实现哪些功能&#xff1f; 他应该能够模拟…

使用uni-app框架中uni.chooseAddress()接口,获取不到用户收货地址

错误描述 在我们使用uni-app框架或微信原生开发微信小程序时&#xff0c;使用到uni.chooseAddress(OBJECT)接口获取用户收货地址时&#xff0c;无法跳转到收货地址页面获取。 打印接口返回信息&#xff0c;显示 "chooseAddress:fail the api need to be declared in the …