Mac
发表于|更新于|操作系统
|总字数:987|阅读时长:3分钟|浏览量:
快捷键
| 描述 | 命令 |
|---|---|
| 一个应用多个窗口 切换 | command+` |
| 录屏幕 | shift+command+5 |
| 截图 | shift+command+4 |
| 重装系统 | 先一直按住不要松手:Common+R, 再按开机键;非苹果键盘,按住微软键+R |
| 上一级目录 | command+↑ |
网站链接
iphone强制重启

浏览器返回手势

放歌有噪音
通过【活动监视器】程序终止【coreaudiod】进程:
- 聚焦搜索】中输入【activity monitor】
- 在窗口右上角的搜索栏里输入【coreaudiod】
- 选择【coreaudiod】进程后,点击窗口上方工具栏中的【停止按钮】(按钮图标为X)
只有mac不能访问github
打开
https://github.com.ipaddress.com/打开
https://fastly.net.ipaddress.com/github.global.ssl.fastly.net#ipinfosudo vim /etc/hosts
1
2
3
4# 这个ip从第一步获取到的
140.82.112.4 github.com
# 这个ip从第二步获取到的
199.232.69.194 github.global.ssl.fastly.net刷新:
sudo killall -HUP mDNSResponder;say DNS cache has been flushed
启动台有问号的图标,点击没反应
1 | # 执行了之后,自己新建的文件夹会重置 |
Mac OS X 访问 Windows共享文件夹

在Mac中,点击 Finder菜单的“前往” > “前往服务器”。在弹出的连接服务器对话框中输入「smb://Windows主机的IP地址」,点击“连接”。

一般需要输入Windows对应用户的名称和密码,然后弹出如下窗口,选择对应的文件夹,点击“好”即可进行远程访问

只使用搜狗输入法
系统偏好设置 >> 键盘 >> 快捷键 >> 反选切换输入法的快捷键

网络踪迹
1 | traceroute ap-southeast-1.signin.aws.amazon.com |
安装mysql客户端
1 | brew install mysql-client |
Iterm2
Iterm2多窗口同时输入命令
只需要输入快捷键 ⌘(command) + ⇧(shift) + i
调出复制过的文本历史
快捷键:“shift+cmd+h”
按键回放
回放一段时间内的你敲过的所有字符。
快捷键:“cmd+alt+b”,如图会弹出一个进度条,按左右键就可以实现按键回放了。
CheatSheet
1 | brew install cheatsheet |
Navicat破解
mac安装 protobuf
1 | brew install protobuf |
视频格式转换
1 | brew install ffmpeg |
邮件提示禁止直接载入内容
Mac自带邮件App里,有时候邮件图片不会直接显示,会提示类似:
1 | 你的网络设置导致禁止以私密方式载入内容 |
这个一般和邮件隐私保护或者阻止所有远程内容有关.
邮件里的图片很多不是附件,而是远程图片.打开邮件时加载这些远程内容,发件人可能知道邮件是否被打开,打开时间,IP地址等信息.所以Apple默认会用隐私保护机制处理远程内容.
设置位置
打开Mac自带邮件App:
1 | 邮件 -> 设置 -> 隐私 |
可以检查这几个选项:
1 | 保护邮件活动 |
如果想让图片直接显示,可以尝试:
- 取消勾选
阻止所有远程内容 - 如果仍然不能正常显示,再检查
保护邮件活动
注意
不建议无脑关闭所有隐私保护.
如果只是偶尔一封邮件图片没显示,可以在邮件顶部点击手动载入远程内容.如果经常需要看邮件图片,再考虑调整隐私设置.
参考:
相关推荐

2022-11-19
ElasticSearch
手动安装7.x手动安装单机ElasticSearch7.x123456789101112131415161718192021222324252627282930# 下载tar包,(6.6.2)wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-6.6.2.tar.gz# 解压缩之后修改配置文件vi /elasticsearch-7.10.1/config/elasticsearch.ymlnetwork.host: 0.0.0.0 #设置成外网也可以访问# 追加内容,修改系统环境该变量,修改完需要重启才能生效vi /etc/sysctl.confvm.max_map_count=262144# 加载参数anthony@base:/usr/local/elasticsearch-6.6.2$ sudo sysctl -pvm.max_map_count = 262144# 允许打开的最大文件描述,加入vim /etc/security/limits.conf* soft nofile 65536* hard nofile 65536* soft nproc 4096 或者(65536)* hard nproc 4096 或者(65536)# 修改JVM分配大小 这两个值要一致vi config/jvm.options-Xms4g-Xmx4g# 启动bin/elasticsearchbin/elasticsearch -d #后台启动 安装分词器下载解压缩的哦elasticsearch的plugin目录下IK分词器https://github.com/medcl/elasticsearch-analysis-ik/releases/tag/v6.6.2 1POST /_analyze { "analyzer": "ik_smart", "text": " 程序员 } 拼音分词器https://github.com/medcl/elasticsearch-analysis-pinyin/releases/tag/v6.6.2 12345GET /medcl/_analyze { "text": ["程序员"], "analyzer": "pinyin_analyzer" } 报错原因: 1234567891011# chown -R 用户名:用户名 目录名Exception in thread "main" java.nio.file.AccessDeniedException: /root/home/searchengine/elasticsearch-6.2.4/config/jvm.options at sun.nio.fs.UnixException.translateToIOException(UnixException.java:84) at sun.nio.fs.UnixException.rethrowAsIOException(UnixException.java:102) at sun.nio.fs.UnixException.rethrowAsIOException(UnixException.java:107) at sun.nio.fs.UnixFileSystemProvider.newByteChannel(UnixFileSystemProvider.java:214) at java.nio.file.Files.newByteChannel(Files.java:361) at java.nio.file.Files.newByteChannel(Files.java:407) at java.nio.file.spi.FileSystemProvider.newInputStream(FileSystemProvider.java:384) at java.nio.file.Files.newInputStream(Files.java:152) at org.elasticsearch.tools.launchers.JvmOptionsParser.main(JvmOptionsParser.java:58) 手动安装集群ElasticSearch7.x1.修改一台elasticsearch的elasticsearch.yml 1234567891011cluster.name: kkb-esnode.name: node0node.master: truenetwork.host: 0.0.0.0http.port: 9200transport.tcp.port: 9300discovery.zen.ping.unicast.hosts: ["127.0.0.1:9300","127.0.0.1:9301","127.0.0.1:9302"]discovery.zen.minimum_master_nodes: 2http.cors.enabled: truehttp.cors.allow-origin: "*" 2.拷贝到其他的服务器 修改上面呢的配置文件中的3个配置 123node.name: node0http.port: 9200transport.tcp.port: 9300 3.检查健康状态 1http://localhost:9200/_cat/health?v 手动单机Kibana7.x安装1234567891011# 下载解压https://artifacts.elastic.co/downloads/kibana/kibana-6.6.2-linux-x86_64.tar.gz# 修改配置文件vi kibana-6.4.2-linux-x86_64/config/kibana.ymlserver.port: 5600server.host: "0.0.0.0"elasticsearch.url: "http://localhost:9200"# 启动,后台启动./bin/kibana & 手动单机Logstash7.x安装mysql-connector-java-5.1.48.jar是放在logstash-6.6.2/logstash-core/lib/jars,这样就不需要写相对路径了 12345678910111213141516171819202122input { jdbc { jdbc_driver_library => "mysql-connector-java-5.1.48.jar" jdbc_driver_class => "com.mysql.jdbc.Driver" jdbc_connection_string => "jdbc:mysql://192.168.111.128:3306/ssm" jdbc_user => "root" jdbc_password => "123456" jdbc_paging_enabled => "true" jdbc_page_size => "2" statement => "SELECT id,'name',phone FROM phone where update_data >= :sql_last_value" tracking_column_type => "timestamp" tracking_column => "update_date" schedule => "* * * * *" }}output { elasticsearch { index => "phone" document_id=> "%{id}" hosts => ["http://192.168.111.128:9200"] }} 启动命令 1./logstash -f ../config/logstash-sample.conf 手动安装8.x 📌 8.0 已经自带了jdk 二进制包安装ES安装12345# 下载包并解压wget https://xxxx.tar# 启动./elasticsearch./bin/elasticsearch 运行结果123456789101112131415161718192021✅ Elasticsearch security features have been automatically configured!✅ Authentication is enabled and cluster connections are encrypted.ℹ️ Password for the elastic user (reset with `bin/elasticsearch-reset-password -u elastic`): Shb2+OuCKthxFTxD-*jEℹ️ HTTP CA certificate SHA-256 fingerprint: ceb6add33653a4c8f3493bddde839cdfcf1035a3a50cb01247962495f15ea40fℹ️ Configure Kibana to use this cluster:• Run Kibana and click the configuration link in the terminal when Kibana starts.• Copy the following enrollment token and paste it into Kibana in your browser (valid for the next 30 minutes): eyJ2ZXIiOiI4LjAuMCIsImFkciI6WyIxMC4wLjIuNDo5MjAwIl0sImZnciI6ImNlYjZhZGQzMzY1M2E0YzhmMzQ5M2JkZGRlODM5Y2RmY2YxMDM1YTNhNTBjYjAxMjQ3OTYyNDk1ZjE1ZWE0MGYiLCJrZXkiOiJXaEV4SDM4QlZXV242cU9KejgxNTprUVliUEY0dlJfaU56MnRFaDlRY0hnIn0=ℹ️ Configure other nodes to join this cluster:• On this node: ⁃ Create an enrollment token with `bin/elasticsearch-create-enrollment-token -s node`. ⁃ Uncomment the transport.host setting at the end of config/elasticsearch.yml. ⁃ Restart Elasticsearch.• On other nodes: ⁃ Start Elasticsearch with `bin/elasticsearch --enrollment-token <token>`, using the enrollment token that you generated. 停止服务,修改配置文件然后输入:curl 127.0.0.1:9200,返回是: 12[hy@localhost ~]$ curl 127.0.0.1:9200curl: (52) Empty reply from server 而ES那边也有响应 123# 看来这和以前的7.1.1版本不一样了[2022-02-19T05:02:02,812][WARN ][o.e.x.s.t.n.SecurityNetty4HttpServerTransport] [localhost.localdomain] received plaintext http traffic on an https channel, closing connection Netty4HttpChannel{localAddress=/127.0.0.1:9200, remoteAddress=/127.0.0.1:38518}[2022-02-19T05:02:27,144][WARN ][o.e.x.s.t.n.SecurityNetty4HttpServerTransport] [localhost.localdomain] received plaintext http traffic on an https channel, closing connection Netty4HttpChannel{localAddress=/[0:0:0:0:0:0:0:1]:9200, remoteAddress=/[0:0:0:0:0:0:0:1]:56174} 修改配置文件 123456789# 需要把security features都关闭,否则http不对外服务了# true改成falsexpack.security.http.ssl: enabled: false keystore.path: certs/http.p12# Enable encryption and mutual authentication between cluster nodesxpack.security.transport.ssl: enabled: false 测试访问12http://localhost:9200需要输入账号和密码:elastic / Shb2+OuCKthxFTxD-*jE java访问测试12345<dependency> <groupId>org.elasticsearch.client</groupId> <artifactId>elasticsearch-rest-client</artifactId> <version>8.0.0</version></dependency> 1234567891011121314151617181920212223242526272829@Bean(destroyMethod = "close")public RestClient restClient() throws CertificateException, IOException, KeyStoreException, NoSuchAlgorithmException, KeyManagementException { final CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials("elastic", "VWFjE60dbpKIjbajGJQ4")); Path caCertificatePath = Paths.get("E:\\es\\elasticsearch-8.0.0-windows-x86_64\\elasticsearch-8.0.0\\config\\certs\\http_ca.crt"); CertificateFactory factory = CertificateFactory.getInstance("X.509"); Certificate trustedCa; try (InputStream is = Files.newInputStream(caCertificatePath)) { trustedCa = factory.generateCertificate(is); } KeyStore trustStore = KeyStore.getInstance("pkcs12"); trustStore.load(null, null); trustStore.setCertificateEntry("ca", trustedCa); SSLContextBuilder sslContextBuilder = SSLContexts.custom() .loadTrustMaterial(trustStore, null); final SSLContext sslContext = sslContextBuilder.build(); RestClientBuilder builder = RestClient.builder( new HttpHost("localhost", 9200, "https")) .setHttpClientConfigCallback(httpClientBuilder -> { httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider); return httpClientBuilder.setSSLContext(sslContext); }); return builder.build();} 1234567//注入client@AutowiredRestClient restClient;Request request = new Request("GET","/ess_book/_doc/1");Response response = restClient.performRequest(request);return EntityUtils.toString(response.getEntity()); 集群安装ES全栈开发之ElasticSearch8.0分布式搜索引擎集群及其高可用测试 - 掘金 安装Kibana安装报错max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144] 123456vim /etc/sysctl.confvm.max_map_count=****262144# 使生效sysctl -p**** 安装插件安装HEADHead是elasticsearch的集群管理工具,可以用于数据的浏览和查询 (1)elasticsearch-head是一款开源软件,被托管在github上面,所以如果我们要使用它,必须先安装git,通过git获取elasticsearch-head (2)运行elasticsearch-head会用到grunt,而grunt需要npm包管理器,所以nodejs是必须要安装的 (3)elasticsearch5.0之后,elasticsearch-head不做为插件放在其plugins目录下了。使用git拷贝elasticsearch-head到本地 cd /usr/local/ git clone git://github.com/mobz/elasticsearch-head.git (4)安装elasticsearch-head依赖包 [root@localhost local]# npm install -g grunt-cli [root@localhost _site]# cd /usr/local/elasticsearch-head/ [root@localhost elasticsearch-head]# cnpm install (5)修改Gruntfile.js [root@localhost _site]# cd /usr/local/elasticsearch-head/ [root@localhost elasticsearch-head]# vi Gruntfile.js 在connect–>server–>options下面添加:hostname:’*’,允许所有IP可以访问 (6)修改elasticsearch-head默认连接地址[root@localhost elasticsearch-head]# cd /usr/local/elasticsearch-head/_site/ [root@localhost _site]# vi app.js 将this.base_uri = this.config.base_uri || this.prefs.get(“app-base_uri”) || “

2022-11-19
Go
入门参考:Go英文文档 Hello World123456789// 一定要在main的包下才能运行// 一个main包下,只能有一个main入口package mainimport "fmt"func main() { fmt.Print("hello world")} 123456789# 第一种,编译后运行.exe文件# 编译C:\Users\anthony\go\src\awesomeProject\demo\main>go build demo.go# 运行编译后exe文件C:\Users\anthony\go\src\awesomeProject\demo\main>demo.exehello world# 第二种,直接运行源代码go run *.go 标准输入1234567891011121314151617package mainimport "fmt"func main() { var name string var age int /* 使用"&"获取score变量的内存地址(即取变量内存地址的运算符),通过键盘输入为score变量指向的内存地址赋初值。 fmt.Scan是一个阻塞的函数,如果它获取不到数据就会一直阻塞哟。 fmt.Scan可以接收多个参数,用户输入参数默认使用空格或者回车换行符分割输入设备传入的参数,直到接收所有的参数为止 */ fmt.Scan(&name, &age) fmt.Println(name, age)} 1234567891011121314151617package mainimport "fmt"func main() { var name string var age int /* 和fmt.Scan功能类似,fmt.Scanln也是一个阻塞的函数,如果它获取不到数据就会一直阻塞哟。 fmt.Scanln也可以接收多个参数,用户输入参数默认使用空格分割输入设备传入的参数,遇到回车换行符就结束接收参数 */ fmt.Scanln(&name, &age) fmt.Println(name, age)} 1234567891011121314151617181920212223package mainimport "fmt"func main() { var name string var age int /* 和fmt.Scanln功能类似,fmt.Scanf也是一个阻塞的函数,如果它获取不到数据就会一直阻塞哟。 其实fmt.Scanln和fmt.Scanf可都以接收多个参数,用户输入参数默认使用空格分割输入设备传入的参数,遇到回车换行符就结束接收参数 唯一区别就是可以格式化用户输入的数据类型,如下所示: %s: 表示接收的参数会被转换成一个字符串类型,赋值给变量 %d: 表示接收的参数会被转换成一个整形类型,赋值给变量 生产环境中使用fmt.Scanln和fmt.Scanf的情况相对较少,一般使用fmt.Scan的情况较多~ */ fmt.Scanf("%s%d", &name, &age) fmt.Println(name, age)} 12345func method_if() { reader := bufio.NewReader(os.Stdin) str, _ := reader.ReadString('\n') fmt.Printf("输入的值是:%s\n", str)} 声明变量基本类型,声明变量,不赋值,就有默认值 1234567891011// 基本类型有默认值func main() { var age int = 0 fmt.Println(age) var code int fmt.Println(code) var sex bool fmt.Println(sex)} 一次性声明不同类型的变量 12345678910111213141516171819func main() { var age, name = 10, "anthony" println("可以声明不同类型,只能自动推导,不能定义类型:", age, name) // 报错,多变量赋值值,不能声明类型 // var age2 int,name2 string = 10, "anthony" // println(age2,name2)}func method6() { var ( age int = 23 name string = "anthony" sex bool ) println("使用集合的方式声明变量,可以声明不同类型,并且可以赋值", age, name, sex)} 先声明,后赋值 12345678func main() { // 定义变量 var i int // 给i赋值 i=10 // 使用变量 fmt.Print("i=", i)} 类型推导 1234func method2() { var name = "anthony" print("类型推导:", name)} 常量常量是一个简单值的标识符,在程序运行时,不会被修改的量 出于习惯,常量用大写,不过小写也没有问题 12345678910// 常量的格式定义const identifier [type] = value// 显式类型定义const A string = "abc"// 隐式类型定义const B = "abc"// 多个相同类型的声明可以简写为const C,D = 1,2// 多个不同类型的声明可以简写为const E, F, G = 1, false, "str" 常量用作枚举常量可以作为枚举,常量组 123456789101112131415161718func main() { const ( RED = 1 BLACK = 2 WHITE = 3 ) const ( x int = 16 y s = "abc" z ) fmt.Println(RED, BLACK, WHITE) fmt.Println("常量组中如不指定类型和初始化值,则与上一行非空常量右值相同:", x, y, s, z)} 特殊常量:iotaiota,特殊常量,可以认为是一个可以被编译器修改的常量 如果中断iota自增,则必须显式恢复。且后续自增值按行序递增 自增默认是int类型,可以自行进行显示指定类型 数字常量不会分配存储空间,无须像变量那样通过内存寻址来取值,因此无法获取地址 1234567891011121314151617func demo() { // 报错 //a = iota const ( a = iota //0 b //1 c //2 d = "ha" //独立值,iota += 1 e //"ha" iota += 1 f = 100 //iota +=1 g //100 iota +=1 h = iota //7,恢复计数 i //8 ) fmt.Println(a, b, c, d, e, f, g, h, i)} 字符串12345678func main() { name := "Hello World" for index := range name { fmt.Printf("遍历字符串:%v,%v\n",index,name[index]) // 字符串长度 fmt.Println(len(name[index])) }} 字符串切片12345var response = "var r = [[\"000001\",\"HXCZHH\",\"华夏成长混合\",\"混合型-偏股\",\"HUAXIACHENGZHANGHUNHE\"]];"fmt.Println(response[8:len(response)-1])// 打印[["000001","HXCZHH","华夏成长混合","混合型-偏股","HUAXIACHENGZHANGHUNHE"]] 字符串数组转一维数组1234567891011121314func main() { response := "[1,2,3]" var arr []int if err := json.Unmarshal([]byte(response), &arr); err != nil { fmt.Println("Error:", err) } // 第1行;数据是:1 // 第2行;数据是:2 // 第3行;数据是:3 for i:=0 ;i< len(arr);i++ { fmt.Printf("第%v行;数据是:%v\n",i+1,arr[i]) }} 字符串数组转二维数组12345678910111213func main() { response := "[[1,2,3],[2,3,4]]" var arr [][]int if err := json.Unmarshal([]byte(response), &arr); err != nil { fmt.Println("Error:", err) } // 第1行;数据是:[1 2 3] // 第2行;数据是:[2 3 4] for i:=0 ;i< len(arr);i++ { fmt.Printf("第%v行;数据是:%v\n",i+1,arr[i]) }} 字符串和数字互转1234567string转成int:int, err := strconv.Atoi(string)string转成int64:int64, err := strconv.ParseInt(string, 10, 64)int转成string:string := strconv.Itoa(int)int64转成string:string := strconv.FormatInt(int64,10) type关键字12345678910func main() { // 类型转换 var one int = 17 mean := float32(one) fmt.Println(mean) // 字符串不能强转整型} 流程控制123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120// iffunc method_if() { reader := bufio.NewReader(os.Stdin) str, _ := reader.ReadString('\n') if str == "" { fmt.Println("空的") } fmt.Printf("输入的值是:%s\n", str)}// if-ikfunc main() { var data = make(map[string]int) data["a"] = 0 data["b"] = 1 data["c"] = 2 // 遍历 for k, v := range data { fmt.Println(k, v) } if _, ok := data["a"]; ok { //存在 fmt.Println("存在") }}// if-elsefunc method_if2() { if num := 10; num%2 == 0 { //checks if number is even fmt.Println(num, "is even") } else { fmt.Println(num, "is odd") }}// switchfunc method_switch() { num := 1 result := -1 switch num { case 1: result = 1 case 2, 3, 4: result = 2 default: result = -2 } fmt.Println(result)}// for 开始012345678910结束func demo() { fmt.Print("开始") for i := 0; i <= 10; i++ { fmt.Print(i) } fmt.Print("结束")}// break 开始01234结束func demo_break() { fmt.Print("开始") for i := 0; i <= 10; i++ { if i == 5 { break } fmt.Print(i) } fmt.Print("结束")}// coutinue 1 3 5 7 9func demo_continue() { for i := 1; i <= 10; i++ { if i%2 == 0 { continue } fmt.Printf("%d ", i) }}// go-to// go// 会打印两次 ===func demo_goto() { /* 定义局部变量 */ var a = 0 /* 循环 */LOOP: fmt.Println("===") for a < 10 { if a == 5 { /* 跳过迭代 */ a = a + 1 goto LOOP } fmt.Printf("a的值为 : %d\n", a) a++ }} 1 值传递和引用传递程序中使用的是值传递, 所以两个值并没有实现交互 123456789101112131415161718192021222324252627282930func main() { /* 定义局部变量 */ var a int = 100 var b int = 200 fmt.Printf("交换前 a 的值为 : %d\n", a ) fmt.Printf("交换前 b 的值为 : %d\n", b ) /* 通过调用函数来交换值 */ swap(a, b) fmt.Printf("交换后 a 的值 : %d\n", a ) fmt.Printf("交换后 b 的值 : %d\n", b )}/* 定义相互交换值的函数 */func swap(x, y int) int { var temp int temp = x /* 保存 x 的值 */ x = y /* 将 y 值赋给 x */ y = temp /* 将 temp 值赋给 y*/ return temp;}//交换前 a 的值为 : 100//交换前 b 的值为 : 200//交换后 a 的值 : 100//交换后 b 的值 : 200 引用传递 1234567891011121314151617181920212223242526272829func main() { /* 定义局部变量 */ var a int = 100 var b int= 200 fmt.Printf("交换前,a 的值 : %d\n", a ) fmt.Printf("交换前,b 的值 : %d\n", b ) /* 调用 swap() 函数 * &a 指向 a 指针,a 变量的地址 * &b 指向 b 指针,b 变量的地址 */ swap(&a, &b) fmt.Printf("交换后,a 的值 : %d\n", a ) fmt.Printf("交换后,b 的值 : %d\n", b )}func swap(x *int, y *int) { var temp int temp = *x /* 保存 x 地址上的值 */ *x = *y /* 将 y 值赋给 x */ *y = temp /* 将 temp 值赋给 y */}//交换前,a 的值 : 100//交换前,b 的值 : 200//交换后,a 的值 : 200//交换后,b 的值 : 100 变量作用域12345678910111213141516// 全局变量var global int = 32func main() { // 局部变量 var a,b int =1, 2 fmt.Printf("打印全局变变量:%v\n", global) global = a+ b fmt.Printf("打印全局变变量:%v\n", global) var global int = 3 fmt.Printf("打印全局变变量:%v\n", global)} 匿名代码块 匿名代码块被用来定义一个局部变量 message,该变量仅在代码块内部可见。一旦离开代码块,变量 message 就不再可见,任何尝试在代码块外部访问它都会导致编译错误。 1234567891011121314151617181920212223package mainimport ( "fmt")func main() { fmt.Println("在main函数外部") // 进入匿名代码块 { fmt.Println("在匿名代码块内部") // 定义一个局部变量,仅在代码块内可见 message := "这是一个局部变量" fmt.Println(message) } // message 变量在代码块外不可见 // fmt.Println(message) // 这会导致编译错误 fmt.Println("在main函数外部")} 数组和切片数组 12345678910111213141516171819202122232425262728293031323334353637func get() { list := [5]int{1,2,3,4,5} for i:=0;i< len(list);i++ { fmt.Println(i, "==", list[i]) } for k, v := range my { fmt.Println(k, v) }}func create(){ var numbers []int fmt.Println("新建个空数组:",numbers) var defaultcount [4]int fmt.Println("新建个指定长度的数组:",defaultcount) var balance = []int{1,2,3,4,5} fmt.Println("新建个不指定长度的数组:",balance) // 根据元素的个数,设置数组的大小 d := [...] int{1,2,3,4,5} fmt.Println("新建个指定位置的数组:",d) // 指定位置 e := [5] int{4: 100} // [0 0 0 0 100] fmt.Println("新建个指定位置的数组:",e) // 指定位置 f := [...] int{0: 1, 4: 1, 9: 1} // [1 0 0 0 1 0 0 0 0 1] fmt.Println("新建个指定位置的数组:",f)} 切片 12345678910111213141516171819202122232425262728293031323334func define() { var identifier []int fmt.Println("空数组", identifier) var slice1 []int = make([]int, 10) fmt.Println("切片", slice1) i0 := slice1[0] i1 := slice1[1] i2 := slice1[2] i3 := slice1[3] fmt.Println("通过索引,获取值", i0, i1, i2, i3) // 修改切片 for i := 0; i < len(slice1); i++ { slice1[i] = i } fmt.Println("修改完切片::", slice1) fmt.Println("获取切片区间1:", slice1[0:2]) fmt.Println("获取切片区间2:", slice1[5:]) fmt.Println("获取切片区间3:", slice1[:5]) slice1 = append(slice1, 10, 11, 12) fmt.Println("追加完切片::", slice1) slice2 := make([]int, len(slice1), cap(slice1)*2) fmt.Println("创建个容量是原来容量两位的数组:", slice2) number := copy(slice2, slice1) fmt.Printf("slice:%v,slice2:%v,number:%v:", slice1, slice2, number)} Map12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849func main() { keyvale := make(map[string]string) keyvale["k1"] = "v1" keyvale["k2"] = "v2" keyvale["k3"] = "v3" keyvale["k4"] = "v4" // 循环遍历 for key := range keyvale { fmt.Println("循环遍历:", key, keyvale[key]) } // 删除元素 delete(keyvale, "k1") for key := range keyvale { fmt.Println("删除值之后,循环遍历:", key, keyvale[key]) } // 查看元素是否存在 _, ok := keyvale["United States"] if ok { fmt.Printf("存在\n") } else { fmt.Printf("不存在\n") } // 但是当key如果不存在的时候,我们会得到该value值类型的默认值,比如string类型得到空字符串,int类型得到0。但是程序不会报错 m := make(map[string]int) m["a"] = 1 x, ok := m["b"] fmt.Println("key不存在时,会有默认值", x, ok) // map长度 fmt.Println("map长度", len(m)) // map是引用类型 mymap := map[string]int{ "steven": 12000, "anthony": 15000, } mymap["mike"] = 9000 fmt.Printf("原来的数据:%v\n", mymap) newmymap := mymap newmymap["anthony"] = 50000 fmt.Printf("改变后,原来的数据:%v\n", mymap) fmt.Printf("改变后,新的的数据:%v\n", newmymap)} 函数12345678910111213141516171819202122232425// 函数返回两个数的最大值func max(num1, num2 int) int { /* 声明局部变量 */ var result int if (num1 > num2) { result = num1 } else { result = num2 } return result}// 函数调用func main() { /* 定义局部变量 */ var a int = 100 var b int = 200 var ret int /* 调用函数并返回最大值 */ ret = max(a, b) fmt.Printf( "最大值是 : %d\n", ret )} 函数返回多个值12345678910// 入参多个值,x和y 都是字符串// 返回值是多个,都是字符串func swap(x, y string) (string, string) { return y, x}func main() { a, b := swap("Google", "Runoob") fmt.Println(a, b)} 函数返回值的命名123456789101112131415161718type studen struct { name string age int}func test4() { stu := test4_return() fmt.Println(stu.name)}func test4_return() (stu *studen) { stu = &studen{ "anthony", 13, } return} 函数作为实参12345678910111213141516171819type fb func(x int) intfunc main() { myfunc := func(x int) int{ return x } // 知识点1 fmt.Println(myfunc(3)) // 知识点2 demo(2, myfunc)}// 函数作为参数传递,实现回调func demo(x int,myfb fb) { myfb(x)} 函数作为实参-回调1234567891011121314151617181920func main() { test2()}func test2() { group("/system1", myfunc) group("/system2", func(path2 string) { fmt.Printf("回调Path:%v \n", path2) })}func group(path string, group func(path2 string)) { fmt.Printf("手动实现 \n" + path) group(path)}func myfunc(path2 string) { fmt.Printf("回调Path:%v \n", path2)} 函数作为参数-interface{}12345678func test3() { test3_object(myfunc)}func test3_object(object interface{}) { a := object fmt.Printf("什么鬼:%v", a)} 指针 12345678910111213141516171819202122232425262728293031func main() { var a int= 20 /* 声明实际变量 */ var ip *int /* 声明指针变量 */ ip = &a /* 指针变量的存储地址 */ fmt.Printf("a 变量的地址是: %x\n", &a ) /* 指针变量的存储地址 */ fmt.Printf("ip 变量储存的指针地址: %x\n", ip ) /* 使用指针访问值 */ fmt.Printf("*ip 变量的值: %d\n", *ip )}// 函数使用指针func main() { /* 定义局部变量 */ var a int = 100 var b int= 200 swap(&a, &b); fmt.Printf("交换后 a 的值 : %d\n", a ) fmt.Printf("交换后 b 的值 : %d\n", b )}/* 交换函数这样写更加简洁,也是 go 语言的特性,可以用下,c++ 和 c# 是不能这么干的 */func swap(x *int, y *int){ *x, *y = *y, *x} 结构体123456789101112131415func main() { // 返回的是该实例的结构类型 var s people s.age = 1 fmt.Println(s) // 第二第三种,返回的是一个指向这个结构类型的一个指针,是地址 ming := new(people) ming.name = "xiao ming" ming.age = 18 fmt.Println(ming) ming2 := &people{"xiao ming", 18} fmt.Println(ming2)} 第二第三种返回指针的声明形式,在我们需要修改他的值的时候,其实应该使用的方式是: 1(*ming).name = "xiao wang" 也就是说,对于指针类型的数值,应该要先用*取值,然后再修改。 但是,在Golang中,可以省略这一步骤,直接使用ming.name = "xiao wang"。 方法1234567891011121314151617181920212223242526type Vertex struct { X, Y float64}func (v Vertex) test1() { v.X++ v.Y++}func (v *Vertex) test2() { v.X++ v.Y++}func main() { v1 := Vertex{1, 1} v2 := &Vertex{1, 1} v1.test1() v2.test2() // {1 1} fmt.Println(v1) // &{2 2} fmt.Println(v2)} test1和test2,他们唯一的区别就是方法名前面的接收者不同,一个是指针类型的,一个是值类型的。 测试1234567891011121314151617type Vertex struct { X, Y float64}func (v Vertex) test1(){ // 自己定义的v1内存地址为:0xc00000a0e0 fmt.Printf("在方法中的v的地址为:%p\n", &v) v.X++; v.Y++;}func main() { v1 := Vertex{1, 1} // 在方法中的v的地址为:0xc00000a100 fmt.Printf("自己定义的v1内存地址为:%p\n", &v1) v1.test1()} 1234567891011121314151617type Vertex struct { X, Y float64}func (v *Vertex) test2(){ // 自己定义的v1内存地址为:0xc00000a0e0 fmt.Printf("在方法中的v的地址为:%p\n", v) v.X++; v.Y++;}func main() { v1 := &Vertex{1, 1} // fmt.Printf("自己定义的v1内存地址为:%p\n", v1) v1.test2()} 123456789101112131415161718192021222324252627282930313233type Books struct { title string author string subject string book_id int}type Library struct { // 匿名字段,那么默认Student就包含了Human的所有字段 Books address string}func main() { var book Books book.title = "Go 语言" book.author = "www.runoob.com" book.subject = "Go 语言教程" book.book_id = 6495407 // 初始化一个图书馆 mark := Library{Books{"Go 语言","www.runoob.com","Go 语言教程",6495407},"广东"} // 我们访问相应的字段 fmt.Println("His name is ", mark.title) fmt.Println("His age is ", mark.author) fmt.Println("His weight is ", mark.subject) fmt.Println("His speciality is ", mark.address) // 修改对应的备注信息 mark.title = "AI" fmt.Println("Mark changed his speciality") fmt.Println("His speciality is ", mark.title)} 面向对象123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778// 定义接口type PersonMethod interface { show()}// 定义结构体type Person struct { name string age int}// 对象成员方法func (person *Person) setAge(age int) { person.age = age}// 对象成员方法实现接口func (person Person) show() { fmt.Printf("Person类打印SHOW:%v\n", person)}// 多态func show2(person PersonMethod) { person.show()}// 学生类,继承Person类type Student struct { // 匿名,相当于继承 Person level string}func (student *Student) show() { fmt.Printf("Student类打印SHOW:%v\n", student)}// 老师类,继承Person类type Teacher struct { Person price int}func (teacher Teacher) show() { fmt.Printf("Teacher类打印SHOW:%v\n", teacher)}func main() { anthony := Person{"anthony", 25} fmt.Printf("anthony信息:%v\n", anthony) // 调用成员方法 anthony.setAge(12) fmt.Printf("anthony信息:%v\n", anthony) anthony2 := Person{} fmt.Printf("anthony2信息:%v\n", anthony2) anthony2.age = 26 anthony2.name = "anthony2" fmt.Printf("anthony2信息:%v\n", anthony2) // 学生,继承 student := Student{} student.level = "小学生" student.name = "anthony" student.age = 23 fmt.Printf("学生继承类:%v\n", student) // 老师,继承 teacher := Teacher{price: 12, Person: Person{name: "li teacher", age: 56}} fmt.Printf("老师继承类:%v\n", teacher) show2(&student) show2(teacher) show2(anthony)} 创建对象的方式 12345678910111213141516171819// 方式一:使用T{…}方式,结果为值类型c := Car{}// 方式二:使用new的方式,结果为指针类型c1 := new(Car)// 方式三:使用&方式,结果为指针类型c2 := &Car{}// 以下为创建并初始化c3 := &Car{"红色", "1.2L"}c4 := &Car{color: "红色"}c5 := Car{color: "红色"}// 没有构造函数的概念,对象的创建通常交由一个全局的创建函数来完成,以NewXXX 来命名,表示“构造函数” :func NewCar(color,size string)*Car { return &Car{color,size}} 接口12345678910111213141516171819202122232425262728type Phone interface { call()}type NokiaPhone struct {}func (nokiaPhone NokiaPhone) call() { fmt.Println("I am Nokia, I can call you!")}type IPhone struct {}func (iPhone IPhone) call() { fmt.Println("I am iPhone, I can call you!")}func main() { var phone Phone phone = new(NokiaPhone) phone.call() phone = new(IPhone) phone.call()} 错误处理123456789101112131415161718192021222324252627282930313233343536373839404142434445// 自定义错误信息结构type DivErr struct { etype int // 错误类型 v1 int // 记录下出错时的除数、被除数 v2 int}// 实现接口方法 error.Error()func (divErr DivErr) Error() string { if 0== divErr.etype { return "除零错误" }else{ return "其他未知错误" }}// 除法func div(a int, b int) (int,*DivErr) { if b == 0 { // 返回错误信息 return 0,&DivErr{0,a,b} } else { // 返回正确的商 return a / b, nil }}func main() { // 正确调用 v,r :=div(100,2) if nil!=r{ fmt.Println("(1)fail:",r) }else{ fmt.Println("(1)succeed:",v) } // 错误调用 v,r =div(100,0) if nil!=r{ fmt.Println("(2)fail:",r) }else{ fmt.Println("(2)succeed:",v) }} GoRoutine1234567891011func main() { go say("world") say("hello")}func say(s string) { for i := 0; i < 5; i++ { time.Sleep(100 * time.Millisecond) fmt.Println(s) }} Channel12345678910111213package mainimport "fmt"func main() { messages := make(chan string) go func() { messages <- "ping" }() msg := <-messages fmt.Println(msg)} 包管理器modulesgo mod常用命令 命令 说明 download download modules to local cache(下载依赖包) edit edit go.mod from tools or scripts(编辑go.mod graph print module requirement graph (打印模块依赖图) init initialize new module in current directory(在当前目录初始化mod) tidy add missing and remove unused modules(拉取缺少的模块,移除不用的模块) vendor make vendored copy of dependencies(将依赖复制到vendor下) verify verify dependencies have expected content (验证依赖是否正确) why explain why packages or modules are needed(解释为什么需要依赖) 示例一:创建一个新项目 在GOPATH 目录之外新建一个目录,并使用go mod init 初始化生成go.mod 文件 go.mod文件一旦创建后,它的内容将会被go toolchain全面掌控。go toolchain会在各类命令执行时,比如go get、go build、go mod等修改和维护go.mod文件。 go.mod 提供了module, require、replace和exclude 四个命令 module 语句指定包的名字(路径) require 语句指定的依赖项模块 replace 语句可以替换依赖项模块 exclude 语句可以忽略依赖项模块 添加依赖 新建一个 server.go 文件,写入以下代码: 123456789101112131415package mainimport ( "net/http" "github.com/labstack/echo")func main() { e := echo.New() e.GET("/", func(c echo.Context) error { return c.String(http.StatusOK, "Hello, World!") }) e.Logger.Fatal(e.Start(":1323"))} 执行 go run server.go 运行代码会发现 go mod 会自动查找依赖自动下载: 1234567891011121314151617181920$ go run server.gogo: finding github.com/labstack/echo v3.3.10+incompatiblego: downloading github.com/labstack/echo v3.3.10+incompatiblego: extracting github.com/labstack/echo v3.3.10+incompatiblego: finding github.com/labstack/gommon/color latestgo: finding github.com/labstack/gommon/log latestgo: finding github.com/labstack/gommon v0.2.8# 此处省略很多行... ____ __ / __/___/ / ___ / _// __/ _ \/ _ \/___/\__/_//_/\___/ v3.3.10-devHigh performance, minimalist Go web frameworkhttps://echo.labstack.com____________________________________O/_______ O\⇨ http server started on [::]:1323复制代码 现在查看go.mod 内容: 123456789101112131415$ cat go.modmodule hellogo 1.12require ( github.com/labstack/echo v3.3.10+incompatible // indirect github.com/labstack/gommon v0.2.8 // indirect github.com/mattn/go-colorable v0.1.1 // indirect github.com/mattn/go-isatty v0.0.7 // indirect github.com/valyala/fasttemplate v1.0.0 // indirect golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a // indirect)复制代码 go module 安装 package 的原則是先拉最新的 release tag,若无tag则拉最新的commit,详见 Modules官方介绍。 go 会自动生成一个 go.sum 文件来记录 dependency tree: 123456789$ cat go.sumgithub.com/labstack/echo v3.3.10+incompatible h1:pGRcYk231ExFAyoAjAfD85kQzRJCRI8bbnE7CX5OEgg=github.com/labstack/echo v3.3.10+incompatible/go.mod h1:0INS7j/VjnFxD4E2wkz67b8cVwCLbBmJyDaka6Cmk1s=github.com/labstack/gommon v0.2.8 h1:JvRqmeZcfrHC5u6uVleB4NxxNbzx6gpbJiQknDbKQu0=github.com/labstack/gommon v0.2.8/go.mod h1:/tj9csK2iPSBvn+3NLM9e52usepMtrd5ilFYA+wQNJ4=github.com/mattn/go-colorable v0.1.1 h1:G1f5SKeVxmagw/IyvzvtZE4Gybcc4Tr1tf7I8z0XgOg=github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=... 省略很多行复制代码 再次执行脚本 go run server.go 发现跳过了检查并安装依赖的步骤。 可以使用命令 go list -m -u all 来检查可以升级的package,使用go get -u need-upgrade-package 升级后会将新的依赖版本更新到go.mod * 也可以使用 go get -u 升级所有依赖 go get 升级 运行 go get -u 将会升级到最新的次要版本或者修订版本(x.y.z, z是修订版本号, y是次要版本号) 运行 go get -u=patch 将会升级到最新的修订版本 运行 go get package@version 将会升级到指定的版本号version 运行go get如果有版本的更改,那么go.mod文件也会更改 示例二:改造现有项目(helloword)项目目录为: 1234567$ tree.├── api│ └── apis.go└── server.go1 directory, 2 files server.go 源码为: 12345678910111213package mainimport ( api "./api" // 这里使用的是相对路径 "github.com/labstack/echo")func main() { e := echo.New() e.GET("/", api.HelloWorld) e.Logger.Fatal(e.Start(":1323"))}复制代码 api/apis.go 源码为: 123456789101112package apiimport ( "net/http" "github.com/labstack/echo")func HelloWorld(c echo.Context) error { return c.JSON(http.StatusOK, "hello world")}复制代码 使用 go mod init *** 初始化go.mod 123$ go mod init helloworldgo: creating new go.mod: module helloworld复制代码 运行 go run server.go 123456go: finding github.com/labstack/gommon/color latestgo: finding github.com/labstack/gommon/log latestgo: finding golang.org/x/crypto/acme/autocert latestgo: finding golang.org/x/crypto/acme latestgo: finding golang.org/x/crypto latestbuild command-line-arguments: cannot find module for path _/home/gs/helloworld/api 首先还是会查找并下载安装依赖,然后运行脚本 server.go,这里会抛出一个错误: 1build command-line-arguments: cannot find module for path _/home/gs/helloworld/api 但是go.mod 已经更新: 12345678910111213$ cat go.modmodule helloworldgo 1.12require ( github.com/labstack/echo v3.3.10+incompatible // indirect github.com/labstack/gommon v0.2.8 // indirect github.com/mattn/go-colorable v0.1.1 // indirect github.com/mattn/go-isatty v0.0.7 // indirect github.com/valyala/fasttemplate v1.0.0 // indirect golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a // indirect) 那为什么会抛出这个错误呢?这是因为 server.go 中使用 internal package 的方法跟以前已经不同了,由于 go.mod会扫描同工作目录下所有 package 并且变更引入方法,必须将 helloworld当成路径的前缀,也就是需要写成 import helloworld/api,以往 GOPATH/dep 模式允许的 import ./api 已经失效,详情可以查看这个 issue。 更新旧的package import 方式 所以server.go 需要改写成: 12345678910111213package mainimport ( api "helloworld/api" // 这是更新后的引入方法 "github.com/labstack/echo")func main() { e := echo.New() e.GET("/", api.HelloWorld) e.Logger.Fatal(e.Start(":1323"))}复制代码 一个小坑:开始在golang1.11 下使用go mod 遇到过 go build github.com/valyala/fasttemplate: module requires go 1.12 这种错误,遇到类似这种需要升级到1.12 的问题,直接升级golang1.12 就好了。幸亏是在1.12 发布后才尝试的go mod 🤷♂️ 网络编程最简单的网络编程123456789101112131415161718package mainimport ( "io" "log" "net/http")func HelloServer(w http.ResponseWriter, req *http.Request) { io.WriteString(w, "hello, world!\n")}func main() { http.HandleFunc("/hello", HelloServer) err := http.ListenAndServeTLS(":8080", "cert.pem", "key.pem", nil) if err != nil { log.Fatal("ListenAndServe: ", err) }} TLS服务端和客户端serve.go 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748package mainimport ( "bufio" "crypto/tls" "log" "net")func main() { log.SetFlags(log.Lshortfile) key, err := tls.LoadX509KeyPair("cert.pem", "key.pem") if err != nil { log.Println(err) return } config := &tls.Config{Certificates: []tls.Certificate{key}} ln, err := tls.Listen("tcp", ":8000", config) if err != nil { log.Println(err) return } defer ln.Close() for { conn, err := ln.Accept() if err != nil { log.Println(err) continue } go handleConnection(conn) }}func handleConnection(conn net.Conn) { defer conn.Close() r := bufio.NewReader(conn) for { msg, err := r.ReadString('\n') if err != nil { log.Println(err) return } println(msg) n, err := conn.Write([]byte("world\n")) if err != nil { log.Println(n, err) return } }} clinet.go 123456789101112131415161718192021222324252627282930package mainimport ( "crypto/tls" "log")func main() { log.SetFlags(log.Lshortfile) conf := &tls.Config{ InsecureSkipVerify: true, } conn, err := tls.Dial("tcp", "127.0.0.1:8000", conf) if err != nil { log.Println(err) return } defer conn.Close() n, err := conn.Write([]byte("hello\n")) if err != nil { log.Println(n, err) return } buf := make([]byte, 100) n, err = conn.Read(buf) if err != nil { log.Println(n, err) return } println(string(buf[:n]))} 标准库flag1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768import ( "errors" "flag" "fmt" "strings" "time")/**// 编译go build demo.go// 帮助信息demo.exe -h// 正常使用demo.exe -name yang*/func main() { //demo1() demo2()}// 第一种用法func demo1() { var nFlag = flag.String("name", "anthony", "help message for flag n") flag.Parse() fmt.Println(*nFlag)}// 第二种用法func demo2() { var name string flag.StringVar(&name, "name", "anthonyyang", "help message for flag n") flag.Parse() fmt.Println(name)}// 第三种用法func demo3() { var intervalFlag mystring flag.Var(&intervalFlag, "anthonyyang", "name") fmt.Println(intervalFlag)}type mystring []time.Durationfunc (i *mystring) String() string { return fmt.Sprint()}func (i *mystring) Set(value string) error { if len(*i) > 0 { return errors.New("interval flag already set") } for _, dt := range strings.Split(value, ",") { duration, err := time.ParseDuration(dt) if err != nil { return err } *i = append(*i, duration) } return nil} 参考 logioio.Reader对于要用作读取器的类型,它必须实现 io.Reader 接口的唯一一个方法 Read(p []byte)。换句话说,只要实现了 Read(p []byte) ,那它就是一个读取器。 123type Reader interface { Read(p []byte) (n int, err error)} Read() 方法有两个返回值,一个是读取到的字节数,一个是发生错误时的错误。同时,如果资源内容已全部读取完毕,应该返回 io.EOF 错误。 io.Copysyncsync.WaitGroup1234567891011121314import ( "fmt")// 执行代码很可能看不到输出,因为有可能这两个协程还没得到执行主协程已经结束了,而主协程结束时会结束所有其他协程func main() { go func() { fmt.Println("Goroutine 1") }() go func() { fmt.Println("Goroutine 2") }()} 解决办法: 1234567891011121314151617import ( "fmt" "time")// 在main函数结尾加上等待func main() { go func() { fmt.Println("Goroutine 1") }() go func() { fmt.Println("Goroutine 2") }() time.Sleep(time.Second * 1) // 睡眠1秒,等待上面两个协程结束} 问题来了,不确定协程到底要用运行多长时间,比如协程需要用到2s,这里main等待1s,就不够用了 解决办法:用管道 12345678910111213141516171819202122232425262728import ( "fmt")func main() { ch := make(chan struct{}) count := 2 // count 表示活动的协程个数 go func() { fmt.Println("Goroutine 1") ch <- struct{}{} // 协程结束,发出信号 }() go func() { fmt.Println("Goroutine 2") ch <- struct{}{} // 协程结束,发出信号 }() for range ch { // 每次从ch中接收数据,表明一个活动的协程结束 count-- // 当所有活动的协程都结束时,关闭管道 if count == 0 { close(ch) } }} 虽然比较完美的方案,但是Go提供了更简单的方法——使用sync.WaitGroup WaitGroup顾名思义,就是用来等待一组操作完成的。 WaitGroup内部实现了一个计数器,用来记录未完成的操作个数,它提供了三个方法,Add()用来添加计数。 Done()用来在操作结束时调用,使计数减一。Wait()用来等待所有的操作结束,即计数变为0,该函数会在计数不为0时等待,在计数为0时立即返回 12345678910111213141516171819202122import ( "fmt" "sync")func main() { var wg sync.WaitGroup wg.Add(2) // 因为有两个动作,所以增加2个计数 go func() { fmt.Println("Goroutine 1") wg.Done() // 操作完成,减少一个计数 }() go func() { fmt.Println("Goroutine 2") wg.Done() // 操作完成,减少一个计数 }() wg.Wait() // 等待,直到计数为0} nethttpGet不带参数 123456789101112131415161718func main() { resp, err := http.Get("http://fund.eastmoney.com/js/fundcode_search.js") if err != nil { fmt.Println(err) return } defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) // 打印body fmt.Println(string(body)) // 打印状态码 fmt.Println(resp.StatusCode) if resp.StatusCode == 200 { fmt.Println("ok") parse(string(body)) }} net.ListenListen通知本地网络地址。 网络必须是“tcp”,“tcp4”,“tcp6”,“unix”或“unixpacket”。 对于TCP网络,如果地址参数中的主机为空或文字未指定的IP地址,则Listen会监听本地系统的所有可用单播和任播IP地址。 要仅使用IPv4,请使用网络“tcp4”。 该地址可以使用主机名称,但不建议这样做,因为它将为主机的至多一个IP地址创建一个监听器。 如果地址参数中的端口为空或“0”,如“127.0.0.1:”或“:: 1:0”中所示,则会自动选择一个端口号。Listener的Addr方法可用于发现所选端口。 12345678910for retry := 0; retry < 16; retry++ { l, err := net.Listen("tcp", host+":0") if err != nil { continue } defer l.Close() _, port, err := net.SplitHostPort(l.Addr().String()) p, err := strconv.ParseInt(port, 10, 32) return int(p)} net.Conn.SetReadDeadline设置超时时间 net.Conn为Deadline提供了多个方法Set[Read|Write]Deadline(time.Time)。 Deadline是一个绝对时间值,当到达这个时间的时候,所有的 I/O 操作都会失败,返回超时(timeout)错误。 OSStat12345678910111213141516171819202122232425262728package mainimport ( "fmt" "os")func main() { fileinfo, err := os.Stat(`C:\Users\Administrator\Desktop\UninstallTool.zip`) if err != nil { panic(err) } fmt.Println(fileinfo.Name()) //获取文件名 fmt.Println(fileinfo.IsDir()) //判断是否是目录,返回bool类型 fmt.Println(fileinfo.ModTime()) //获取文件修改时间 fmt.Println(fileinfo.Mode()) fmt.Println(fileinfo.Size()) //获取文件大小 fmt.Println(fileinfo.Sys())}type FileInfo interface { Name() string // 文件的名字(不含扩展名) Size() int64 // 普通文件返回值表示其大小;其他文件的返回值含义各系统不同 Mode() FileMode // 文件的模式位 ModTime() time.Time // 文件的修改时间 IsDir() bool // 等价于Mode().IsDir() Sys() interface{} // 底层数据来源(可以返回nil)} IsExist返回一个布尔值说明该错误是否表示一个文件或目录已经存在。ErrExist和一些系统调用错误会使它返回真。 12345678910func check() bool { stat, err := os.Stat("/Users/anthony/Desktop/go-testt/go.mod") if err != nil { if os.IsExist(err) { return true } return false } return true} Text/templatehtml模板 https://pkg.go.dev/text/template GinDemo1234567891011package mainimport "github.com/gin-gonic/gin"func main() { r := gin.Default() r.GET("/", func(c *gin.Context) { c.String(200, "Hello, Geektutu") }) r.Run() // listen and serve on 0.0.0.0:8080} 路由基本使用 1234567891011121314151617181920// 设置路由router := gin.Default()// 第一个参数是:路径; 第二个参数是:具体操作 func(c *gin.Context)router.GET("/Get", getting)router.POST("/Post", posting)router.PUT("/Put", putting)router.DELETE("/Delete", deleting)// 默认启动的是 8080端口router.Run()// 第一种方法:r.GET("/", func(c *gin.Context) { c.String(http.StatusOK, "Who are you?")})// 第二种方法r.POST("createApi", CreateApi)func CreateApi(c *gin.Context) { // 代码} 路由分组 12345678910111213// 两个路由组,都可以访问,大括号是为了保证规范v1 := r.Group("/v1"){ // 通过 localhost:8080/v1/hello访问,以此类推 v1.GET("/hello", sayHello) v1.GET("/world", sayWorld)}v2 := r.Group("/v2"){ v2.GET("/hello", sayHello) v2.GET("/world", sayWorld)}r.Run(":8080") 大量路由实现 建立routers包,将不同模块拆分到多个go文件 每个文件提供一个方法,该方法注册实现所有的路由 之后main方法在调用文件的方法实现注册 第一种实现方式 1234567891011121314151617// 这里是routers包下某一个router对外开放的方法func LoadRouter(e *gin.Engine) { e.Group("v1") { v1.GET("/post", postHandler) v1.GET("/get", getHandler) } ...}func main() { r := gin.Default() // 调用该方法实现注册 routers.LoadRouter(r) routers.LoadRouterXXX(r) // 代表还有多个 r.Run()} 第二种方式 1像vue一样注册路由,没看懂项目 为啥要注册很多次 获取参数获取body的数据,并绑定实体类 12345678910func (b *BaseApi) Login(c *gin.Context) { // 声明实体类 var l systemReq.Login // 赋值 err := c.ShouldBindJSON(&l) // 获取ip key := c.ClientIP()} 响应处理1234567891011121314151617181920212223// 1.JSONr.GET("/someJSON", func(c *gin.Context) { c.JSON(200, gin.H{ "message": "Json", "status": 200, })})// 2.XMLr.GET("/someXML", func(c *gin.Context) { c.XML(200, gin.H{"message": "abc"})})// 3.YAMLr.GET("/someYAML", func(c *gin.Context) { c.YAML(200, gin.H{"name": "zhangsan"})})// 4.protobufr.GET("/someProtoBuf", func(c *gin.Context) { reps := []int64{1, 2} data := &protoexample.Test{ Reps: reps, } c.ProtoBuf(200, data)}) 中间件中间件分为:全局中间件 和 路由中间件,区别在于前者会作用于所有路由。 1234567891011121314// 默认的中间件有func BasicAuth(accounts Accounts) HandlerFunc // 身份认证func BasicAuthForRealm(accounts Accounts, realm string) HandlerFuncfunc Bind(val interface{}) HandlerFunc //拦截请求参数并进行绑定func ErrorLogger() HandlerFunc //错误日志处理func ErrorLoggerT(typ ErrorType) HandlerFunc //自定义类型的错误日志处理func Logger() HandlerFunc //日志记录func LoggerWithConfig(conf LoggerConfig) HandlerFuncfunc LoggerWithFormatter(f LogFormatter) HandlerFuncfunc LoggerWithWriter(out io.Writer, notlogged ...string) HandlerFuncfunc Recovery() HandlerFuncfunc RecoveryWithWriter(out io.Writer) HandlerFuncfunc WrapF(f http.HandlerFunc) HandlerFunc //将http.HandlerFunc包装成中间件func WrapH(h http.Handler) HandlerFunc //将http.Handler包装成中间件 作用范围 1234567891011121314// 作用于全局r.Use(gin.Logger())r.Use(gin.Recovery())// 作用于单个路由r.GET("/benchmark", MyBenchLogger(), benchEndpoint)// 作用于某个组authorized := r.Group("/")authorized.Use(AuthRequired()){ authorized.POST("/login", loginEndpoint) authorized.POST("/submit", submitEndpoint)} 自定义中间件 123456789101112func Logger() gin.HandlerFunc { return func(c *gin.Context) { t := time.Now() // 给Context实例设置一个值 c.Set("geektutu", "1111") // 请求前 c.Next() // 请求后 latency := time.Since(t) log.Print(latency) }} 控制中间件 中间件控制的方法gin提供了两个函数Abort()和Next(),二者区别在于: next()函数会跳过当前中间件中next()后的逻辑,当下一个中间件执行完成后再执行剩余的逻辑 abort()函数执行终止当前中间件以后的中间件执行,但是会执行当前中间件的后续逻辑 举例子更好理解: 我们注册中间件顺序为m1、m2、m3,如果采用next(): 执行顺序就是 m1的next()前面、m2的next()前面、m3的next()前面、 业务逻辑 m3的next()后续、m2的next()后续、m1的next()后续。 那如果m2中间调用了Abort(),则m3和业务逻辑不会执行,只会执行m2的next()后续、m1的next()后续。 GormCRUD 接口创建创建记录12345678// 没有数据的字段,就是基本数据user := User{Name: "Jinzhu", Age: 18, Birthday: time.Now()}result := db.Create(&user) // 通过数据的指针来创建user.ID // 返回插入数据的主键result.Error // 返回 errorresult.RowsAffected // 返回插入记录的条数 用指定的字段创建记录创建记录并更新给出的字段。 123// 没有数据的字段,插入的是nulldb.Select("Name", "Age", "CreatedAt").Create(&user)// INSERT INTO `users` (`name`,`age`,`created_at`) VALUES ("jinzhu", 18, "2020-07-04 11:05:21.775") 创建一个记录且一同忽略传递给略去的字段值 12db.Omit("Name", "Age", "CreatedAt").Create(&user)// INSERT INTO `users` (`birthday`,`updated_at`) VALUES ("2020-01-01 00:00:00.000", "2020-07-04 11:05:21.775") ❓ 用指定的字段创建记录 不返回ID 批量插入1234567891011var users = []User{{Name: "jinzhu1"}, {Name: "jinzhu2"}, {Name: "jinzhu3"}}db.Create(&users)for _, user := range users { user.ID // 1,2,3}// 使用 CreateInBatches 分批创建时,你可以指定每批的数量var users = []User{{name: "jinzhu_1"}, ...., {Name: "jinzhu_10000"}}// 数量为 100db.CreateInBatches(users, 100) 创建钩子123456789101112131415161718192021222324252627282930313233343536373839404142434445464748type SysMenu struct { gorm.Model // 菜单名称 Title string // 直属上级菜单标识 ParentId int // 菜单URL Path string // 0:Flase,1:True AlwaysShow int // 组件 Component string // 0:Flase,1:True Hidden int // 图标 Icon string // 0:Flase,1:True NoCache string // 前端内部名字 Name string // 前端内部路由 Redirect string // 0:否,1:是 IsButton int // 请求后端接口 Ajax string}// 钩子func (s *SysMenu) BeforeSave(tx *gorm.DB) (err error) { fmt.Println("BeforeSave") return}func (s *SysMenu) BeforeCreate(tx *gorm.DB) (err error) { fmt.Println("BeforeCreate") return}func (s *SysMenu) AfterCreate(tx *gorm.DB) (err error) { fmt.Println("AfterCreate") return}func (s *SysMenu) AfterSave(tx *gorm.DB) (err error) { fmt.Println("AfterSave") return} 根据 Map 创建123456789db.Model(&User{}).Create(map[string]interface{}{ "Name": "jinzhu", "Age": 18,})// batch insert from `[]map[string]interface{}{}`db.Model(&User{}).Create([]map[string]interface{}{ {"Name": "jinzhu_1", "Age": 18}, {"Name": "jinzhu_2", "Age": 20},}) 默认值12345type User struct { ID int64 Name string `gorm:"default:galeone"` Age int64 `gorm:"default:18"`} Cobra简介cobra是一个命令行程序库,可以用来编写命令行程序。 快速入门安装包 1$ go get github.com/spf13/cobra/cobra 文件目录 12345- my-go-test - cmd - version - root main.go main.go 12345import "go-test/cmd"func main() { cmd.Execute()} version.go 1234567891011121314151617181920package cmdimport ( "fmt" "github.com/spf13/cobra")var versionCmd = &cobra.Command{ Use: "version", Short: "显示计算器版本", Long: "显示计算器版本", Run: func(cmd *cobra.Command, args []string) { fmt.Println("anthony's jsq version 0.0.1") },}func init() { rootCmd.AddCommand(versionCmd)} root.go 1234567891011121314151617import ( "fmt" "github.com/spf13/cobra")var rootCmd = &cobra.Command{ Use: "jsq", Short: "计算器", Long: `anthony写的计算器`, Run: func(cmd *cobra.Command, args []string) { fmt.Println("anthony写的计算器,请使用 ./jsq -h") },}func Execute() { rootCmd.Execute()} 打包运行123456789101112131415161718192021222324252627282930313233# 打包➜ go-testt go build -o jsq# 运行不带参数的命令➜ go-testt ./jsq anthony写的计算器,请使用 ./jsq -h# 运行主命令的help➜ go-testt ./jsq -helpError: unknown shorthand flag: 'e' in -elpUsage: jsq [flags] jsq [command]Available Commands: completion Generate the autocompletion script for the specified shell help Help about any command version 显示计算器版本Flags: -h, --help help for jsqUse "jsq [command] --help" for more information about a command.# 运行子命令➜ go-testt ./jsq version -h显示计算器版本Usage: jsq version [flags]Flags: -h, --help help for version 包成window包 1go build -o jsq.exe 库Yaml解析库1go get gopkg.in/yaml.v3 测试文件yaml 1234567mysql: url: 127.0.0.1 port: 3306redis: host: 127.0.0.1 port: 6379 将 yaml 文件的数据转成自定义的结构体或 Map 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647import ( "fmt" "gopkg.in/yaml.v3" "os")type Config struct { Mysql Mysql `json:"mysql"` Redis Redis `json:"redis"`}type Mysql struct { Url string Port int}type Redis struct { Host string Port int}func main() { dataBytes, err := os.ReadFile("test.yaml") if err != nil { fmt.Println("读取文件失败:", err) return } fmt.Println("yaml 文件的内容: \n", string(dataBytes)) config := Config{} err = yaml.Unmarshal(dataBytes, &config) if err != nil { fmt.Println("解析 yaml 文件失败:", err) return } fmt.Printf("config → %+v\n", config) // config → {Mysql:{Url:127.0.0.1 Port:3306} Redis:{Host:127.0.0.1 Port:6379}} mp := make(map[string]any, 2) err = yaml.Unmarshal(dataBytes, mp) if err != nil { fmt.Println("解析 yaml 文件失败:", err) return } fmt.Printf("map → %+v", config) // config → {Mysql:{Url:127.0.0.1 Port:3306} Redis:{Host:127.0.0.1 Port:6379}}} yaml解析库1go get github.com/spf13/viper 测试 123456789101112131415161718192021import ( "fmt" "github.com/spf13/viper")func main() { // 设置配置文件的名字 viper.SetConfigName("test") // 设置配置文件的类型 viper.SetConfigType("yaml") // 添加配置文件的路径,指定 config 目录下寻找 viper.AddConfigPath("./config") // 寻找配置文件并读取 err := viper.ReadInConfig() if err != nil { panic(fmt.Errorf("fatal error config file: %w", err)) } fmt.Println(viper.Get("mysql")) // map[port:3306 url:127.0.0.1] fmt.Println(viper.Get("mysql.url")) // 127.0.0.1} 笔记字符串互转byte1234// string to []bytes1 := "hello" b := []byte(s1)// []byte to strings2 := string(b) 创建对象的方式 12345678910111213141516171819202122232425type Car struct { color string size string}// 方式一:使用T{…}方式,结果为值类型c := Car{}// 方式二:使用new的方式,结果为指针类型c1 := new(Car)// 方式三:使用&方式,结果为指针类型c2 := &Car{}// 以下为创建并初始化c3 := &Car{"红色", "1.2L"}c4 := &Car{color: "红色"}c5 := Car{color: "红色"}// 构造函数:// 在Go语言中没有构造函数的概念,对象的创建通常交由一个全局的创建函数来完成,以// NewXXX 来命名,表示“构造函数” :func NewCar(color,size string)*Car { return &Car{color,size}} 报错1.debug call has arguments but no formatting directives 打印/日志 格式解析错误,错误的原因就是没加%v 2.取消代理 123456go envGOPROXY='https://goproxy.cn,direct' # 这个是国内的代理,在国外不容易下载依赖➜ server git:(main) ✗ go env -u GOPROXY # 取消代理,使用默认的配置➜ server git:(main) ✗ go envGOPROXY='https://proxy.golang.org,direct'

2023-07-08
Telegram Bot
机器人设置消息分类消息分两种: 普通文本消息(比如test) 命令消息(以'/'开头的文本,比如 /test ) 默认在群里只能收到命令消息,和机器人私聊可以接收到全部的消息,如果想让机器人在群里也能接收到全部的消息,操作如下访问BotFather,输入/setprivacy,选择自己的机器人,选择DISABLED,就可以了
2026-05-26
macOS IDEA部署Docker到本机Docker Desktop
说明在macOS上使用IDEA开发项目时,可以直接通过IDEA的Run配置,把项目打包成Docker镜像,然后部署到本机的Docker Desktop里运行. 这个方式适合本地调试Docker环境,不用每次手动执行docker build和docker run. 前提条件 macOS已经安装并启动Docker Desktop IDEA已经安装Docker插件 项目里有可用的Dockerfile Java项目已经可以正常打包,比如Maven或Gradle打包成功 整体流程IDEA部署服务到本机Docker Desktop,本质上就是把手动命令配置到IDEA的Run里. 手动执行时一般是这样: 123mvn clean package -DskipTestsdocker build -t my-app:latest .docker run --name my-app -p 8080:8080 my-app:latest 配置到IDEA之后,点击Run就可以自动完成打包,构建镜像,启动容器. 配置Docker连接打开IDEA设置: Settings | Build, Execution, Deployment | Docker 点击+,选择Docker for Mac. 然后点击Test Connection,看到连接成功就说明IDEA已经能连接到本机Docker Desktop. 准备DockerfileSpring Boot项目可以在项目根目录准备一个简单的Dockerfile. 123456789FROM eclipse-temurin:17-jreWORKDIR /appCOPY target/*.jar app.jarEXPOSE 8080ENTRYPOINT ["java", "-jar", "app.jar"] 如果项目端口不是8080,这里的EXPOSE和后面的端口映射都要换成实际端口. 配置Run打开: Run | Edit Configurations 点击+,这里常用有两个选择: Docker | Dockerfile适合项目里有Dockerfile,需要IDEA先构建镜像,再运行容器的场景. 它会执行类似下面的流程: 12docker build -t my-app:latest .docker run --name my-app -p 8080:8080 my-app:latest 也就是说,代码改了以后,可以让IDEA重新build镜像,再启动容器. Spring Boot项目,前端项目,或者自己写了Dockerfile的项目,一般选这个. Docker | Docker Image适合镜像已经存在,只需要运行这个镜像的场景. 它会执行类似下面的流程: 1docker run --name mysql-test -p 3306:3306 mysql:8 比如运行mysql,redis,nginx,nacos这类现成镜像,或者已经手动build好了镜像,只想让IDEA帮忙启动容器,就选这个. 简单记: 12有Dockerfile,需要构建镜像 -> Docker | Dockerfile已有镜像,只想运行容器 -> Docker | Docker Image Dockerfile配置项如果选择Docker | Dockerfile,关键配置如下: 配置项 说明 Server 选择前面配置好的Docker for Mac Dockerfile 选择项目里的Dockerfile Context folder 一般选择项目根目录 Image tag 镜像名称,比如my-app:latest Container name 容器名称,比如my-app Run built image 勾选,表示构建完成后直接运行镜像 Bind ports 端口映射,比如本机8080映射容器8080 Environment variables 按需配置环境变量 常用配置可以这样填: 123456Server: Docker for MacDockerfile: ./DockerfileContext folder: 项目根目录Image tag: my-app:latestContainer name: my-appBind ports: 8080:8080 Before launch如果是Spring Boot项目,建议在Before launch里加打包任务. Maven项目可以配置: 1clean package -DskipTests Gradle项目可以配置: 1bootJar 这样点击Run时,IDEA会先打包项目,再根据Dockerfile构建镜像并启动容器. 启动后验证启动成功后,可以在Docker Desktop里看到容器. 也可以在终端检查: 1docker ps 如果服务端口是8080,可以访问: 1http://localhost:8080 如果需要看日志,可以在IDEA的Run窗口查看,也可以执行: 1docker logs -f my-app 常见问题端口被占用修改Bind ports里的本机端口. 比如容器内部还是8080,本机可以改成18080. 118080 -> 8080 容器需要访问宿主机服务macOS的Docker Desktop里,容器访问宿主机可以使用: 1host.docker.internal 比如容器里的程序要连接本机MySQL,可以把数据库地址配置成: 1host.docker.internal:3306 挂载本机目录失败检查Docker Desktop是否允许访问这个目录. 可以在Docker Desktop里查看: Settings | Resources | File Sharing 总结本地项目通过IDEA部署到macOS的Docker Desktop,核心就是两步: 在IDEA里配置Docker for Mac 在Run配置里选择Docker | Dockerfile 如果只是运行已有镜像,才选择Docker | Docker Image.

2022-11-19
Vue
按照官方文档的教程步骤来,大部分的代码Demo,都是通过脚手架创建的项目的基础上创建的,用的版本的Vue3,Typescript,组合式的写法 基础创建一个应用多个应用实例 没什么用,现在项目都是从头开始都是vue项目,不会用来一步一步替换,没有想到应用场景 1234567891011121314151617181920212223242526272829303132333435363738394041424344<!DOCTYPE html><html><head> <title>Vue Multiple Instances Example</title> <script src="https://cdn.jsdelivr.net/npm/vue@2"></script></head><body> <div id="app1"> <p>{{ message }}</p> <button @click="updateMessage">更新消息</button> </div> <div id="app2"> <p>{{ message }}</p> <button @click="updateMessage">更新消息</button> </div> <script> var app1 = new Vue({ el: '#app1', data: { message: '这是第一个 Vue 实例的消息' }, methods: { updateMessage() { this.message = '第一个实例的消息已更新'; } } }); var app2 = new Vue({ el: '#app2', data: { message: '这是第二个 Vue 实例的消息' }, methods: { updateMessage() { this.message = '第二个实例的消息已更新'; } } }); </script></body></html> 模板语法文本插值 双大括号标签会被替换为msg 属性的值。同时每次 msg 属性更改时它也会同步更新。 12345678<script setup lang="ts">import {ref} from "vue";const text = ref<string>("文本插值语法");</script><template> {{text}}</template> 原始 HTML 有的时候想想渲染源码,有的时候不想渲染源码 v-html就是指令语法 123456789<script setup lang="ts">import {ref} from "vue";const rawHtml = ref<string>("<span style='color: red'>This should be red.</span>");</script><template> <p>Using text interpolation: {{ rawHtml }}</p> <p>Using v-html directive: <span v-html="rawHtml"></span></p></template> Attribute 绑定 如果想给html的属性绑定数据要怎么操作呢,下面以给div的id和class属性绑定做演示 123456789101112131415161718192021222324252627282930313233343536373839404142<script setup lang="ts">import {ref} from "vue";const myId = ref<string>("myId");const showButton = ref<boolean>(false); type objectOfAttrs={ id: string, class:string} const objectOfAttrsObj=ref<objectOfAttrs>({ id: "myId4", class:"demo"}); </script><template> <!-- ❎错误做法 --> <div id={{myId}}></div> <!-- ✅正确做法,如果绑定的值是 null 或者 undefined,那么该 attribute 将会从渲染的元素上移除。 --> <div v-bind:id="myId"></div> <!-- ✅正确做法,简写写法 --> <div :id="myId"></div> <!-- ✅正确做法,同名简写写法,就是属性(Id),和等号对应的值是一样的话 --> <div :id></div> <!-- ✅正确做法,同名简写写法,第二种写法 --> <div v-bind:id></div> <!-- -------------------------------------------------------- --> <!-- 绑定布尔类型 --> <button :disabled="showButton">按钮</button> <!-- 动态绑定多个值 --> <div v-bind="objectOfAttrsObj">动态绑定多个值,只是需要写v-bind</div></template> 响应式基础这个有点底层,不去了解了,大概意思是简单的数据类型就用ref,对象或者复杂嵌套的结构就用reactive 计算属性基础示例 Vue 的计算属性会自动追踪响应式依赖。它会检测到 authorComputed 依赖于 author.books,所以当 author.books 改变时,任何依赖于 authorComputed 的绑定都会同时更新。 计算属性会被缓存,并且只调用一次,方法是调用一次执行一次,在例子中,触发了一次页面渲染(页面的数据有变动),方法就会被执行一次 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061<script setup lang="ts">import {computed, reactive, ref} from "vue";type authorType={ name:string, books:string[]}const author = reactive<authorType>({ name: 'John Doe', books: [ 'Vue 2 - Advanced Guide', 'Vue 3 - Basic Guide', 'Vue 4 - The Mystery' ]})// 计算属性,使用泛型,返回值必须是字符串const authorComputed = computed<string>(()=>{ return author.books.length >0?author.books.length.toString():"没有书"})function add(){ author.books.push("Vue 5 - Advanced Guide")}// 计算属性,使用泛型,返回值必须是字符串const myDateTimeComputed = computed<number>(() => { return getDateTime();});function getDateTime(){ console.log("方法被调用") return Date.now();}function myDateTimeMethod(){ return getDateTime();}// 定义一个用于触发重新渲染的状态const renderCounter = ref(0);function forceRerender() { console.log("12321") renderCounter.value++;}</script><template> <!-- 计算属性 --> <span>通过计算属性计算出来的结果:{{ authorComputed }}</span> <br> <button @click="add()">添加数据</button> <br> <!-- 计算属性和方法的区别 --> <button @click="forceRerender">触发重新渲染</button> 计算属性和方法的区别-计算属性:{{ myDateTimeComputed }} 计算属性和方法的区别-方法:{{renderCounter}}-{{ myDateTimeMethod() }}</template> 可写计算属性 计算属性默认是只读的。当你尝试修改一个计算属性时,你会收到一个运行时警告。只在某些特殊场景中你可能才需要用到“可写”的属性,你可以通过同时提供 getter 和 setter 来创建 1234567891011121314151617181920212223242526272829303132333435363738394041<script setup lang="ts">import {computed, reactive, ref} from "vue";const firstName = ref('Anthony')/** * 错误的写法 */// const fullName = computed(()=>{// return firstName.value + ' ' + lastName.value// })/** * 正确的写法 */const fullName = computed({ // getter get() { return firstName.value }, // setter set(newValue) { // 注意:我们这里使用的是解构赋值语法 firstName.value = newValue }})/** * 这样是不对的 */function showWarn(){ console.log("修改计算属性") fullName.value="DiDiDi"}</script><template> 演示修改计算属性,控制台报错:{{fullName}} <button @click="showWarn">尝试修改计算属性</button></template> Class 与 Style 绑定太麻烦了,不看了 条件渲染v-if 可以单独使用 v-else-if 要跟v-if 一起使用 v-else 要跟v-if 或者 v-else-if 一起使用 12345678910111213<script setup lang="ts">import {computed, reactive, ref} from "vue";const season = ref<number>(6);</script><template> <h1 v-if="season >=1 && season <=3">春季</h1> <h1 v-else-if="season >=4 && season <=6">不是春季</h1> <h1 v-else>数据错误</h1></template> 列表渲染123456789101112131415161718192021222324<script setup lang="ts">import {computed, reactive, ref} from "vue";const items = ref([{ message: 'Foo' }, { message: 'Bar' }])</script><template> for循环 <li v-for="(item,index) in items"> {{index}}-{{ item.message }} </li> 解构函数 <li v-for="{ message } in items"> {{ message }} </li> 解构函数,有索引的时候 <li v-for="({message},index) in items"> {{index}}-{{ message }} </li></template> 事件处理我们可以使用 v-on 指令 (简写为 @) 来监听 DOM 事件,并在事件触发时执行对应的 JavaScript。用法:v-on:click="handler" 或 @click="handler"。 内联事件处理器 12345678910<script setup lang="ts">import {computed, reactive, ref} from "vue";const count = ref(0)</script><template> <button @click="count++">Add 1</button> <p>Count is: {{ count }}</p></template> 方法事件处理器 1234567891011<script setup lang="ts">function greet() { console.log("方法事件处理器")}</script><template> <!-- `greet` 是上面定义过的方法名 --> <button @click="greet">Greet</button></template> 表单输入绑定主要是看框架了,先不学了,太多细节了 生命周期钩子这个API好多个,以后慢慢看 侦听器每次修改被监听的对象,就会打印旧值和新值 基本示例 12345678910111213141516<script setup lang="ts">import {ref, watch} from 'vue'/*被监听的对象*/const question = ref<number>(1);// 可以直接侦听一个 refwatch(question, async (newQuestion, oldQuestion) => { console.log("老值:", oldQuestion) console.log("新值:", newQuestion)})</script><template> <input v-model="question"/></template> 侦听不同的数据源类型 watch 的第一个参数可以是不同形式的“数据源”:它可以是一个 ref (包括计算属性)、一个响应式对象、一个 getter 函数、或多个数据源组成的数组: 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455<script setup lang="ts">import {reactive, ref, watch} from 'vue'const x = ref<number>(0)const y = ref<number>(0)type authorType={ name: string,}const z = reactive<authorType>({ name:"anthony"})// refwatch(x, (newX,oldValue) => { console.log(`监听单个ref,x的旧值是:${oldValue},新值是:${newX}`)})// getter 函数watch( () => x.value + y.value, (newSum, oldSum) => { console.log(`监听getter函数, 原来的和值是:${oldSum},新值是: ${newSum}`) })// 多个来源组成的数组watch([x, () => y.value], ([newX, newY],[oldX, oldY]) => { console.log(`监听多个数据源:X的旧值是:${oldX},X的新值是:${newX},Y的旧值是:${oldY},Y的新值是:${newY}`)})// 监听reactive// 错误,因为 watch() 得到的参数是一个 number// 错误的写法,会提示报错,// MyClick.vue:35 [Vue warn]: Invalid watch source: anthony A watch source can only be a getter/effect function, a ref, a reactive object, or an array of these types.// at <MyClick>// at <App>// watch(z.name, (count) => {// console.log(`name is: ${count}`)// })// 正确的写法watch( () => z.name, (nameNew,oldName) => { console.log(`name is: ${nameNew},${oldName}`) })</script><template> <input v-model="x" type="number"/> <input v-model="y" type="number"/> <input v-model="z.name" /></template> 模板引用进入到页面会自动聚焦到输入框中 123456789101112131415<script setup>import { ref, onMounted } from 'vue'// 声明一个 ref 来存放该元素的引用// 必须和模板里的 ref 同名const input = ref(null)onMounted(() => { input.value.focus()})</script><template> <input ref="input" /></template> 组件基础定义组件 123456789<script setup>import { ref } from 'vue'const count = ref(0)</script><template> <button @click="count++">You clicked me {{ count }} times.</button></template> 使用组件 在父组件中引入 1234567<script setup lang="ts">import MyClick from "@/components/MyClick.vue";</script><template> <MyClick/></template> 传递 props(父传子) 1234567891011121314151617181920212223// 子组件<script setup lang="ts">import {ref} from 'vue'const count = ref<number>(0)defineProps(['title'])</script><template> <button @click="count++">{{title}},You clicked me {{ count }} times.</button></template>// 父组件<script setup lang="ts">import MyClick from "@/components/MyClick.vue";</script><template> <MyClick title="计算器1"/> <MyClick title="计算器2"/> <MyClick title="计算器3"/> <MyClick title="计算器4"/></template> emit(子传父) 父组件 123456789101112131415161718192021222324<script setup lang="ts">import { ref } from 'vue'import MyEmit from "@/components/MyEmit.vue";type Person = { name: string, age: number}const myName = ref<string>('')const myAge = ref<number>('')const getSubmit = (data: Person) => { myName.value = data.name myAge.value = data.age}</script><template> <MyEmit @myEmit="getSubmit" /> 从子组件接收的数据:{{ myName }} {{ myAge }}</template> 子组件 123456789101112131415<script setup lang="ts">type Person = { name: string, age: number}const me = { name: 'anthony', age: 23}const emit = defineEmits<{ (e: 'myEmit', payload: Person): void }>();emit('myEmit', me)</script> $bus 消息总线main.js 123456789101112131415//引入Vueimport Vue from 'vue'//引入Appimport App from './App.vue'//关闭Vue的生产提示Vue.config.productionTip = false//创建vmnew Vue({ el:'#app', render: h => h(App), beforeCreate() { Vue.prototype.$bus = this //安装全局事件总线 },}) App.vue 1234567891011121314151617181920212223242526272829<template> <div class="app"> <h1>{{msg}}</h1> <School/> <Student/> </div></template><script> import Student from './components/Student' import School from './components/School' export default { name:'App', components:{School,Student}, data() { return { msg:'你好啊!', } } }</script><style scoped> .app{ background-color: gray; padding: 5px; }</style> Student.vue 12345678910111213141516171819202122232425262728293031323334<template> <div class="school"> <h2>学校名称:{{name}}</h2> <h2>学校地址:{{address}}</h2> </div></template><script> export default { name:'School', data() { return { name:'尚硅谷', address:'北京', } }, mounted() { // console.log('School',this) this.$bus.$on('hello',(data)=>{ console.log('我是School组件,收到了数据',data) }) }, beforeDestroy() { this.$bus.$off('hello') }, }</script><style scoped> .school{ background-color: skyblue; padding: 5px; }</style> School.vue 1234567891011121314151617181920212223242526272829303132<template> <div class="student"> <h2>学生姓名:{{name}}</h2> <h2>学生性别:{{sex}}</h2> <button @click="sendStudentName">把学生名给School组件</button> </div></template><script> export default { name:'Student', data() { return { name:'张三', sex:'男', } }, methods: { sendStudentName(){ this.$bus.$emit('hello',this.name) } }, }</script><style lang="less" scoped> .student{ background-color: pink; padding: 5px; margin-top: 30px; }</style> $set$nextTick深入组件插槽插槽内容与出口 主文件 1234567891011121314151617181920212223242526272829<script setup lang="ts">import { ref, provide, reactive } from 'vue'import First from "@/components/First.vue";import MySlotWithName from "@/components/MySlotWithName.vue";import MySlotWithOutName from "@/components/MySlotWithOutName.vue";</script><template> <h1>插槽演示</h1> <!-- 匿名插槽 --> <MySlotWithOutName> <a href="https://baidu.com">跳转到baidu</a> </MySlotWithOutName> <br> <MySlotWithName> <!-- 具名插槽 --> <!-- <template v-slot:url> --> <!-- 简写的方式 --> <template #url> <a href="https://google.com">跳转到谷歌</a> </template> </MySlotWithName></template> MySlotWithOutName.vue 1234<template> 匿名插槽 <slot /></template> 1234<template> 具名插槽 <slot name="url" /></template> 作用域插槽 12345678910111213<script setup lang="ts">import MySlotWithName from "@/components/MySlotWithName.vue";</script><template> <MySlotWithName> <template #url="data"> {{ data.title }}- {{ data.age }} <a href="https://google.com">跳转到谷歌</a> </template> </MySlotWithName></template> 1234<template> 具名插槽 <slot name="url" title="anthony" age="12" /></template> 依赖注入给所有的下级组件,包括直接子组件传递数据和方法 这个例子的最上层组件修改数据之后,可以修改所有组件显示的数据 这个例子的下层组件修改数据之后,也是可以修改所有组件显示的数据 最上层组件 12345678910111213141516171819202122232425262728293031323334353637<script setup lang="ts">import { ref, provide, reactive } from 'vue'import First from "@/components/First.vue";type Person = { name: string, age: number}const me: Person = reactive({ name: 'anthony', age: 23})provide("provideDemo", me)const update = () => { me.age = me.age + 1 console.log(me);}const updateTwo = () => { me.age = me.age + 2 console.log(me);}provide("provideUpdateTwo", updateTwo)</script><template> 最顶层组件,接收到的数据:{{ me.name }},{{ me.age }}<button @click="update">修改所有层数据+1</button> <br> <First /></template> 第一层组件 1234567891011121314151617<script setup lang="ts">import { inject } from 'vue'import Second from "./Second.vue";type Person = { name: string, age: number}const me = inject<Person>("provideDemo")</script><template> 这是第一层,接收到的数据:{{ me.name }},{{ me.age }} <br> <Second /></template> 第二层组件 12345678910111213141516<script setup lang="ts">import { inject } from 'vue'import Third from "./Third.vue";type Person = { name: string, age: number}const me = inject<Person>("provideDemo")</script><template> 这是第二层,接收到的数据:{{ me.name }},{{ me.age }} <br> <Third /></template> 第三层组件 123456789101112131415<script setup lang="ts">import { inject } from 'vue'type Person = { name: string, age: number}const me = inject<Person>("provideDemo")const provideUpdateTwo = inject("provideUpdateTwo")</script><template> 这是第三层,接收到的数据:{{ me.name }},{{ me.age }}<button @click="provideUpdateTwo">修改最顶层数据+2</button></template> yarnyarn的安装 1npm install -g yarn yarn常用命令 12345678910111213141516// 初始化yarn init // 添加包yarn add [package]yarn add [package]@[version]yarn add [package]@[tag]// 添加到不同依赖项yarn add [package] --devyarn add [package] --peeryarn add [package] --optional// 升级包yarn upgrade [package]// 移除依赖包yarn remove [package]// 安装所有依赖yarn 或 yarn install 报错1.node新版本引起的报错 1234this[kHandle] = new _Hash(algorithm, xofLen);^Error: error:0308010C:digital envelope routines::unsupported 解决方法1: 123456推荐:修改package.json,在相关构建命令之前加入SET NODE_OPTIONS=--openssl-legacy-provider"scripts": { "serve": "SET NODE_OPTIONS=--openssl-legacy-provider && vue-cli-service serve", "build": "SET NODE_OPTIONS=--openssl-legacy-provider && vue-cli-service build"}, 解决方法2: 降低node版本 前端Vue父组件点击按钮打开子组件弹窗案例父组件 12345678910111213141516171819202122232425<template> <div> # 需要定义ref=child <indexChild ref="child"></indexChild> <el-button @click="open">打开弹窗</el-button> </div></template><script>import indexChild from "../../components/indexChild.vue";export default { components: { indexChild }, data () { return { }; }, methods: { open () { this.$refs.child.open(); // 这样可以直接访问子组件方法,用ref拿子组件方法 } }}</script> 子组件 123456789101112131415161718192021222324252627<template> <div> <el-dialog title="收货地址" :visible.sync="dialogFormVisible"> <span>这是一段信息</span> <div slot="footer" class="dialog-footer"> <el-button @click="dialogFormVisible = false">取 消</el-button> <el-button type="primary" @click="dialogFormVisible = false">确 定</el-button> </div> </el-dialog> </div></template><script>export default { data () { return { dialogFormVisible: false, }; }, methods: { open () { // 在父组件调用打开 this.dialogFormVisible = true } }};</script> 参考:前端Vue父组件点击按钮打开子组件弹窗案例

2025-05-29
Dapp
HelloWorld合约Remix编辑器 12345678// SPDX-License-Identifier: MITpragma solidity ^0.8.13;contract helloworld{ function helloWorld() public pure returns (string memory) { return "Hello, World!"; }} 发行代币在闲鱼,淘宝上买些测试币,也可以通过水龙头获取(只是有点要求和门槛,不如直接买),这里我买了两笔 新建合约文件 1234567891011// SPDX-License-Identifier: MITpragma solidity ^0.8.13;import "@openzeppelin/contracts/token/ERC20/ERC20.sol";contract MyError20Token is ERC20{ constructor(string memory _name,string memory _symbol) ERC20(_name,_symbol){ _mint(msg.sender,10000 * 10 ** 18); }}
评论
目录