Flutter_web加Java Spring 整站前后台开发经验梳理
这段时间开发了一个公司内部使用的网站,本着提前探索熟悉(踩坑)未来的全栈UI框架-Flutter的愿望,使用了Flutter for Web作为前端框架,后台部分则循规蹈矩的用了java Spring。目前已经开发完成,在此简要记录一下开发过程中积累的一些经验。
突然发现,我现在不仅会android,又会java后端,又能用flutter写web+android+ios代码,一下子变成了全栈工程师喽!
command1 | command2
用竖线分割两个命令,把第一个命令的输出,作为第二个命令的输入。 此时需要第二个命令支持这种从管道获取输入的功能,例如cat ls就支持这种功能。
command1 | xargs command2
xargs和管道类似,都是把第一个命令的输出传递到第二个命令。
与管道不同的是,管道给第二个命令传递是直接传到命令输入上,这种方式要求命令本身的支持,支持的命令较少。
而xargs,则是把第一个命令的输出作为参数传递到第二个命令上,这种方式只要第二个命令可以接受参数即可,支持的命令较多。
一个例子:
➜ ~ echo test.txt | cat
test.txt
➜ ~ echo test.txt | xargs cat
content of test.txt
echo test.txt | cat 是把 "test.txt" 这个字符串直接让cat输出
echo test.txt | xargs cat 是把 "test.txt" 作为参数传递给cat,表示把 test.txt 这个文件的内容输出
1. -d 选项
默认情况下xargs将其标准输入中的内容以空白(包括空格、Tab、回车换行等)分割成多个之后当作命令行参数传递给其后面的命令,并运行之,我们可以使用 -d 命令指定分隔符,例如:
echo '11@22@33' | xargs echo
输出:
11@22@33
默认情况下以空白分割,那么11@22@33这个字符串中没有空白,所以实际上等价于 echo 11@22@33 其中字符串 '11@22@33' 被当作echo命令的一个命令行参数
echo '11@22@33' | xargs -d '@' echo
输出:
11 22 33
指定以@符号分割参数,所以等价于 echo 11 22 33 相当于给echo传递了3个参数,分别是11、22、33
2. -p 选项
使用该选项之后xargs并不会马上执行其后面的命令,而是输出即将要执行的完整的命令(包括命令以及传递给命令的命令行参数),询问是否执行,输入 y 才继续执行,否则不执行。这种方式可以清楚的看到执行的命令是什么样子,也就是xargs传递给命令的参数是什么,例如:
echo '11@22@33' | xargs -p -d '@' echo
输出:
echo 11 22 33
?...y ==>这里询问是否执行命令 echo 11 22 33 输入y并回车,则显示执行结果,否则不执行
11 22 33 ==>执行结果
3. -n 选项
该选项表示将xargs生成的命令行参数,每次传递几个参数给其后面的命令执行,例如如果xargs从标准输入中读入内容,然后以分隔符分割之后生成的命令行参数有10个,使用 -n 3 之后表示一次传递给xargs后面的命令是3个参数,因为一共有10个参数,所以要执行4次,才能将参数用完。例如:
echo '11@22@33@44@55@66@77@88@99@00' | xargs -d '@' -n 3 echo
输出结果:
11 22 33
44 55 66
77 88 99
00
等价于:
echo 11 22 33
echo 44 55 66
echo 77 88 99
echo 00
实际上运行了4次,每次传递3个参数,最后还剩一个,就直接传递一个参数。
4. -E 选项,有的系统的xargs版本可能是-e eof-str
该选项指定一个字符串,当xargs解析出多个命令行参数的时候,如果搜索到-e指定的命令行参数,则只会将-e指定的命令行参数之前的参数(不包括-e指定的这个参数)传递给xargs后面的命令
echo '11 22 33' | xargs -E '33' echo
输出:
11 22
可以看到正常情况下有3个命令行参数 11、22、33 由于使用了-E '33' 表示在将命令行参数 33 之前的参数传递给执行的命令,33本身不传递。等价于 echo 11 22 这里-E实际上有搜索的作用,表示只取xargs读到的命令行参数前面的某些部分给命令执行。
注意:-E只有在xargs不指定-d的时候有效,如果指定了-d则不起作用,而不管-d指定的是什么字符,空格也不行。
echo '11 22 33' | xargs -d ' ' -E '33' echo => 输出 11 22 33
echo '11@22@33@44@55@66@77@88@99@00 aa 33 bb' | xargs -E '33' -d '@' -p echo => 输出 11 22 33 44 55 66 77 88 99 00 aa 33 bb
## -0 选项表示以 '\0' 为分隔符,一般与find结合使用
find . -name "*.txt"
输出:
./2.txt
./3.txt
./1.txt => 默认情况下find的输出结果是每条记录后面加上换行,也就是每条记录是一个新行
find . -name "*.txt" -print0
输出:
./2.txt./3.txt./1.txt => 加上 -print0 参数表示find输出的每条结果后面加上 '\0' 而不是换行
find . -name "*.txt" -print0 | xargs -0 echo
输出:
./2.txt ./3.txt ./1.txt
find . -name "*.txt" -print0 | xargs -d '\0' echo
输出:
./2.txt ./3.txt ./1.txt
xargs的 -0 和 -d '\0' 表示其从标准输入中读取的内容使用 '\0' 来分割,由于 find 的结果是使用 '\0' 分隔的,所以xargs使用 '\0' 将 find的结果分隔之后得到3个参数: ./2.txt ./3.txt ./1.txt 注意中间是有空格的。上面的结果就等价于 echo ./2.txt ./3.txt ./1.txt
实际上使用xargs默认的空白分隔符也是可以的 find . -name "*.txt" | xargs echo 因为换行符也是xargs的默认空白符的一种。find命令如果不加-print0其搜索结果的每一条字符串后面实际上是加了换行
command1 -exec command2 {} \;
-exec 和 xargs 的作用相似,都是把前一个命令的输出作为参数传给第二个命令
find . -name "test.txt" -exec cat {} \;
content of test.txt
-exec与xargs不同的是:-exec是将结果逐条传递给后面的命令,后面的命令逐条执行。
xargs是将结果作为一个列表全部传递给后面的命令,后面的命令一次性执行参数串,可以通过xargs -p ls -l来查看即将要执行的完整的命令。
打npm包的步骤
加上–no-minify可以设置不混淆
package 中配置name , version ,main ,files 等
下面是一个例子:
1 | { |
到https://www.npmjs.com/注册 npm账号
本地登录 npm账号 npm login
在仓库目录使用 npm publish 发布包。
本地开发时,可以使用npm link功能来本地依赖库
npm link命令npm link mypackagename 即可另外,使用parcel watch ./index.ts --no-source-maps --target node --bundle-node-modules 可以让库中代码修改后自动编译,方便本地依赖开发。
最近有个这样的需求,要在我们的后台界面上,让平台使用者可以输入一段脚本,然后在nodejs层,读取这个脚本并执行。并且还要支持自定义输入参数的能力。
研究了一下Function的使用,记录在这里。
1 | interface FunctionConstructor { |
使用new Function(functionScript)可以根据一个脚本字符串生成一个动态的函数。
然后执行这个函数即可得到函数的结果。
例如:
1 | let functionScript = "return 1+2" |
result执行结果为3。 非常的简单。
复杂一点的也行,比如:
1 | let functionScript = |
运行结果是2。
上面的都是不带参数的,带参数的也可以,比如下面这个函数:
1 | function findMax(data1, data2) { |
它带有两个参数,data1和data2,这就需要传到Function里面。
1 | let functionScript = |
以上,就完成了脚本动态执行的功能。
另外,扩展运算符( spread )...args 在生成Function中经常使用。
扩展运算符能将一个数组转为用逗号分隔的参数序列。
1 | console.log(...[1, 2, 3]) |
上面这两行代码就是完全等价的。
Function 的构造函数 new(...args: string[]): Function; 的args参数,是需要展开的,不是一个数组。但是实际开发中,参数肯定是要用数组来保存的。这里就需要用扩展运算符来展开。
上面的代码,就可以转行成下面的形式。
1 | let argsKey = ["data1", "data2"]; |
Flutter 中 可以给Container设置decoration来设置背景,边框等等效果,非常方便。
1 | Container( |
但是Flutter自带的decoration比较少,只能支持常规样式,当我们需要设置特殊背景时,就满足不了我们的需要了。
这时候,就有必要自定义一个我们定制化的decoration。
搜了一下flutter sdk代码,发现flutter里有一个定制化的FlutterLogoDecoration类,用这个FlutterLogoDecoration可以生成一个flutter logo样式的decoration。
我们可以参考它的代码来写一个我们自定义的decoration。
经过我的分析,自定义decoration的步骤还是很简单的。
一个简单的代码框架是这样的。
1 |
|
这段代码中,我们主要需要实现的,就是_MyBoxPainter的paint方法。
上面的的代码,是先写了一个MyDecoration类继承Decoration,然后重写了其中的createBoxPainter方法,创建一个_MyBoxPainter类。_MyBoxPainter类是继承BoxPainter的,里面的paint方法需要我们实现,在该方法中具体编写decoration实际绘制的内容。
绘制时,主要用到了canvas.draw...相关的api。
下面是一份带有红色边框,和内部有“测试”字样的decoration的demo代码。
1 |
|

这里设置了painter的颜色是红色,宽度是2,用来绘制边框。paint方法的Offset offset参数表示控件左上角的位置。configuration.size 可以得到控件的宽高。
所以,
1 | Offset leftTop = offset; |
通过上面这四行代码,就可以得到控件四个角的位置。
1 | canvas.drawLine(leftTop, rightTop, painter); |
然后通过drawLine可以绘制四个边框。
这里是演示demo,实际要绘制边框可以使用canvas.drawRect(rect, paint)方法更为方便。
1 | var textPainter = TextPainter( |
这里定义了一个“测试”文字绘制的painter,文字颜色是红色,底色是绿色,字号是12,文字方向是从左到右。
需要注意的是,需要先调用textPainter.layout(); 才能真正绘制,否则会报错。
调用textPainter.paint(canvas, (leftTop + rightBottom) / 2); 即可完成绘制。(leftTop + rightBottom) / 2)表示绘制在中心位置。
总体来说代码很简单,只要按照这个框架实现对应的方法即可。
主要是用了canvas.draw...相关的api 和 TextPainter.paint方法。
最近发现了一个线上问题,用户的信息获取错误。多方调试后发现,我们的userId太长了,超过了js支持的精度范围,发生了精度丢失的问题。 js里面16位就开始丢失精度了,我们的到了17位。
比如一个userId:12345678901234567,在js里,如果使用number类型的话,实际会变成 12345678901234570 。
看到了吗,js自带的坑,而且坑的还不是位数变化,位数没变,但是最后一位四舍五入了!!!
经过各种查资料、咨询、调试、测试……
最后确定,js number 对这个情况无解。
换BigInt long bignumber 之类的,也都没啥乱用。这些类型能解决数学运算的问题,但是我这里并不需要数学运算,只是需要返回给后端,而这些框架对json解析仍然是无解。
ps:我并不是直接用的js,而是用的ts,然而这门“现代化的语言”,仍然没有解决这个js原始的坑。
通过git format-patch 、 git diff 、git apply 三个命令,可以生成patch和打入patch,用于在多个git仓库间传递代码的情况。
比如不想提交代码,但是要把代码传给其他协作者,就很适合用这个方式。
git format-patch
1 | # 把当前没push的提交都打成一个patch |
git diff
1 | # 把git diff 的输出内容写入本地,直接作为一个patch文件 |
git apply
1 | # 打入patch |
下面这是一段我们常写出的代码,注意其中的forEach函数,大家看看它的输出是什么。
1 | void main(List<String> arguments) { |
我们会想当然的认为第一次print是false,第二次print是true。
然而实际上的输出
1 | false |
这里是forEach这个函数有个坑
1 | /** |
这是forEach的实现,非常的简单。注意这里传入的函数参数 f 的返回值是void。
函数的实现里也没有对f的返回值做任何处理,毕竟人家声明的就是void——无返回值。
虽然我们在使用的时候,f的实现里面写了return true|false ,并且编译器并没有报错,但是实际上,这里的return是毫无意义的。
更不可能会把当前函数inList的运行中断掉并返回。
那么知道了这个问题,该怎么解决呢?
可以简单的使用any函数。
1 | /** |
any函数和forEach的使用类似, 但是处理了传入函数test的返回值。
1 | static bool inList(String template) { |
改成这样既可,注意及时是any函数,也是不能让return直接中断inList的执行并返回的,return的值是传递到any函数的返回上,然后return any函数的返回既可。
上述代码略显啰嗦,可以再优化下,用一行代码实现:
1 | static bool inList(String template) => list.any((String item) => template == item); |
再次运行,就输出了期望的结果
1 | false |
我们在开发Flutter工程的时候,
经常需要看日志时,发现没有Logcat视图,只能通过Debug视图中的Console Tab来看log。
这个Console Tab比起Logcat来说,功能上差太多了,各种级别过滤、关键字过滤的功能都没有,截图、录屏这些工具不能用。
想要打开Logcat视图,一般在第一次导入工程的时候,会提示检测到Android Framework,这时候按照提示点击导入,并设置Android Sdk即可。
但是有时候并不是第一次导入的工程,莫名其妙Logcat视图就消失了,并且各个地方都还找不回来。
Project Structure,然后切到Facets标签,在当前工程下添加一个Android架构。然后Logcat视图就出来了。


git 同步超大仓库的时候,会报如下错误
1 | git fetch |
此时使用 git config core.compression -1 对代码进行压缩
或者 git fetch --depth 1 origin remote_branch_name 只同步一个分支,都可以解决该问题。
zsh 在超大工程下可能会性能变差,可以执行 git config --add oh-my-zsh.hide-dirty 1 来优化该问题。