关于go:Golang-非主流-打包静态资源方案

3次阅读

共计 4140 个字符,预计需要花费 11 分钟才能阅读完成。

说到往 Go 程序外面打包进其余非 *.go 资源,在 Go1.16 之前有 go-bindata 第三方开源反对。

Go1.16 引入了新个性 embed,不依赖第三方就能嵌入动态资源权限。

然而在我的项目中,以上两种计划我都没用,转而自研了一套计划。

不想听我 bb 心路历程的,间接跳到 开整 环节看最终实现。

背景

在 2021 年初,我在参加的我的项目 go-mod-graph-chart 时,须要把前端资源集成到一个由 Go 语言构建的 命令行 程序中。

都说到这了,就介绍下 go-mod-graph-chart,它是一个 将 go mod graph 命令输入的文本可视化 命令行 工具(相似于 graphviz),在我的项目目录下执行go mod graph 会输入以后我的项目,所依赖的第三方包,以及第三方包又依赖了什么包。但当你理论应用时,你会看到一堆的文本,

命令输入后果如下:

$ go mod graph
go-learn github.com/antlabs/pcurl@v0.0.7
go-learn github.com/bxcodec/faker/v3@v3.6.0
go-learn github.com/go-sql-driver/mysql@v1.5.0
go-learn github.com/jinzhu/copier@v0.3.5
go-learn github.com/pingcap/parser@v0.0.0-20220118030345-6a854bcbd929
go-learn github.com/smartystreets/goconvey@v1.7.2
go-learn golang.org/x/text@v0.3.7
go-learn moul.io/http2curl@v1.0.0
github.com/antlabs/pcurl@v0.0.7 github.com/gin-contrib/gzip@v0.0.1
github.com/antlabs/pcurl@v0.0.7 github.com/gin-gonic/gin@v1.6.3
github.com/antlabs/pcurl@v0.0.7 github.com/guonaihong/clop@v0.0.9
github.com/antlabs/pcurl@v0.0.7 github.com/guonaihong/gout@v0.0.12
github.com/antlabs/pcurl@v0.0.7 github.com/stretchr/testify@v1.6.1
github.com/pingcap/parser@v0.0.0-20220118030345-6a854bcbd929 github.com/cznic/golex@v0.0.0-20181122101858-9c343928389c
github.com/pingcap/parser@v0.0.0-20220118030345-6a854bcbd929 github.com/cznic/mathutil@v0.0.0-20181122101859-297441e03548
...

而应用 gmchart 这个工具,将其可视化为多级树状构造。

如下图:

在散发这个命令行工具,如果还要带着动态资源,或是让用户先下个 graphviz,体验就很不好了。于是我就想有什么方法,将动态资源打包到 *.go 代码里。

很不巧在 2020 年底时,Go1.16 embed 还未推出,但我要解决这个问题。go-bindata 在过后无疑是最受欢迎的计划,但会引入第三方依赖。这个时候,我代码洁癖上来了,之前我用 gin 做了 http 服务,起初发现我的项目只引入了 gin,我把 gin 换成内置的 http 服务 后,就变成无依赖的我的项目了。所以,我想持续放弃 no dependency。我感觉这个性能应该不难,本人也能写啊。

实现思路

前端打包,有一步是把所有的 *.js 文件集成到一个 js 文件。并把最终输入的 js 文件名写到 index.html 文件里作为入口js

Go 动态资源打包,就是把其余类型的文件序列化后,保留到 Go 代码 里的动态变量里。

Golang 程序在对外提供 http 服务时,当收到动态资源申请时,就会去读取对应变量,输入到 http 响应体中,并在 http heder 中设置对应的 Content-Type

那么如果想方法干涉下输入流程,让其写 main.js, index.html 文件,改为将内容写入到 go 代码的两个变量,就能够实现 Go 打包动态资源了。

package gostatic

var IndexHtml = `<!DOCTYPE html>^M
<html lang="en">^M
</html>

var MainJs = `echo "hello";`

var Favicon = `data:image/ico;base64,AAABAAEAmpsAAAEAIAT...`

开整

我的项目前端构建用到了 webpack,那就在这下面动动手脚了。

一个 gopher 想要去动 webpack?有点自不量力

于是关上了 webpack 的官网,转了一圈,发现官网提供了 plugin,通过自定义插件,能够影响其构建流程。

这里在plugin,获取到了构建后果,通过遍历构建后果,获取到了对于字符串,以及文件名,而后咱们又插入了一个新的构建后果 go_static.go,这外面蕴含后面的 main.js, index.html 文件的内容。

pack-all-in-go-plugin.js 文件内容如下:

class PackAllInGoPlugin {apply(compiler) {// emit is asynchronous hook, tapping into it using tapAsync, you can use tapPromise/tap(synchronous) as well
    compiler.hooks.emit.tapAsync('PackAllInGoPlugin', (compilation, callback) => {
      // Create a header string for the generated file:
      var filelist = '';
      var indexHtml, mainJs;
      var goCode = `package godist

func GetFile(file string) string {
  switch {
  case \`index.html\` == file:
    return IndexHtml
  case \`main.js\` == file:
    return MainJs
  case \`favicon.ico\` == file:
    return Favicon
  default:
    return ""
  }
}

var IndexHtml = \`--index.html--\`

var MainJs = \`--main.js--\`

var Favicon = \`favicon.ico\``

      // Loop through all compiled assets,
      // adding a new line item for each filename.
      for (var filename in compilation.assets) {if ("main.js" == filename) {let jsCode = compilation.assets[filename].source()
          let jsCodeString = jsCode.slice();
          jsCodeString = jsCodeString.replace(/\`/g, "\` + \"\`\"+ \`")
          goCode = goCode.replace('--main.js--', jsCodeString)
        } else if ("index.html") {let htmlCode = compilation.assets[filename].source()
          goCode = goCode.replace('--index.html--', htmlCode)
        }
      }

      // 将这个列表作为一个新的文件资源,插入到 webpack 构建中:compilation.assets['../godist/static.go'] = {source: function() {return goCode;},
        size: function() {return goCode.length;}
      };

      callback();});
  }
}

module.exports = PackAllInGoPlugin;

webpack 中引入

/*
 * 引入自定义插件
 */
const PackAllInGoPlugin = require('./plugin/pack-all-in-go-plugin');

...

config = {
    pulbins: [
    ...
        new PackAllInGoPlugin({options: true})
    ],
}

这一通设置后,每次执行 npm run build 就能把最新的动态资源打包进 go_static.go 文件内了。再执行 go build -o main main.go go代码和动态资源就打包到一个可执行文件内了。

对了!这个 webpack plugin 没公布到 npm,你如果要用间接把源码抄过去就行了。两头遇到集成问题,能够看看 https://github.com/PaulXu-cn/… , 这个我的项目理论有在用。

最初

总的来说,这次是从前端构建这边来解决了 go 打包动态资源问题,算是横跨 GoWebPack,这计划算是比拟小众,根本属于:

  1. gopher 不想碰前端
  2. 前端为什么要给 gowebpack plugin

我也预感了,这办法也就多数人用用,想试试间接 copy 代码就行,这个小玩意,就不独自开 ” 坑 ” 了。

好了,我是个爱折腾的 gohper,这次填了一个我本人制作的坑。如果大家也喜爱捣鼓点不一样的货色,欢送一起交换。

参考

  • https://xie.infoq.cn/article/…
  • https://mp.weixin.qq.com/s/eq…
  • https://github.com/go-bindata…
  • https://github.com/PaulXu-cn/…
  • https://graphviz.org/
  • https://webpack.js.org/
正文完
 0