goja 支持es6的一種方法


goja 對於es6 的module 模式是不支持的,但是我們可以通過擴展模式支持

基本原理

k6 是利用了goja 的js 能力,但是為了支持es6,使用了babel (standalone),同時為了方便擴展ls 的能力,使用了core.js
同時利用了js可以直接轉換為golang 方法的模式,直接使用了golang 方法進行es6 編譯處理

  • 核心代碼
    使用babel 的transform 方法,轉換es6 為es6,同時暴露為一個golang 方法
 
func newBabel() (*babel, error) {
    var err error
    once.Do(func() {
        conf := rice.Config{
            LocateOrder: []rice.LocateMethod{rice.LocateEmbedded},
        }
        babelSrc := conf.MustFindBox("lib").MustString("babel.min.js")
        vm := goja.New()
        if _, err = vm.RunString(babelSrc); err != nil {
            return
        }
        this := vm.Get("Babel")
        bObj := this.ToObject(vm)
        globalBabel = &babel{vm: vm, this: this}
        if err = vm.ExportTo(bObj.Get("transform"), &globalBabel.transform); err != nil {
            return
        }
    })
    return globalBabel, err
}

globalBabel.transform 的定義
使用了goja 的Callable

 
transform goja.Callable
  • 進行es6代碼編譯處理
func (b *babel) Transform(logger logrus.FieldLogger, src, filename string) (string, *SourceMap, error) {
    b.mutex.Lock()
    defer b.mutex.Unlock()
    opts := make(map[string]interface{})
    for k, v := range DefaultOpts {
        opts[k] = v
    }
    opts["filename"] = filename
    startTime := time.Now()
    v, err := b.transform(b.this, b.vm.ToValue(src), b.vm.ToValue(opts))
    if err != nil {
        return "", nil, err
    }
    logger.WithField("t", time.Since(startTime)).Debug("Babel: Transformed")
    vO := v.ToObject(b.vm)
    var code string
    if err = b.vm.ExportTo(vO.Get("code"), &code); err != nil {
        return code, nil, err
    }
    var rawMap map[string]interface{}
    if err = b.vm.ExportTo(vO.Get("map"), &rawMap); err != nil {
        return code, nil, err
    }
    var srcMap SourceMap
    if err = mapstructure.Decode(rawMap, &srcMap); err != nil {
        return code, &srcMap, err
    }
    return code, &srcMap, err
}

一個參考集成babel 的demo

  • 參考代碼
package main
import (
    "demoapp-goja/pkg"
    "log"
    "github.com/dop251/goja"
)
const (
    bablename  = "node_modules/babel.min.js"
    corejsname = "node_modules/index.js"
    demomodule = "node_modules/app.js"
)
var convert func(string) string
var jsCompilerVM *goja.Runtime = goja.New()
func main() {
    jsCompilerVM = goja.New()
    // 使用go-bindata
    bableContent, _ := pkg.Asset(bablename)
    corejsContent, _ := pkg.Asset(corejsname)
    mydemomodule, _ := pkg.Asset(demomodule)
    corejs, _ := goja.Compile(corejsname, string(corejsContent), false)
    bable, _ := goja.Compile(bablename, string(bableContent), false)
     // 預加載babel 以及core-js
    jsCompilerVM.RunProgram(bable)
    jsCompilerVM.RunProgram(corejs)
   // es6 編譯es5 轉換為golang 代碼
    jsCompilerVM.RunString(`var convert = function(es6code) {
        return Babel.transform(es6code, { presets: ['env'] }).code;
       }
   `)
    err := jsCompilerVM.ExportTo(jsCompilerVM.Get("convert"), &convert)
    if err != nil {
        panic(err)
    }
    // 調用轉換
    log.Println(convert(string(mydemomodule)))
}
 

node_modules/app.js 內容

import rong from "./rong.js"
var demo = new rong("dalong",222)
console.log(demo.printInfo())

效果

 

 

說明

實踐方法參考自k6一個很不錯的基於golang 開發的壓力測試工具,k6集成goja js 引擎還是比較巧妙的,后邊說明下方法

參考資料

https://github.com/loadimpact/k6/blob/master/js/lib/lib.go
https://github.com/loadimpact/k6/blob/master/js/compiler/compiler.go#L85
https://github.com/dop251/goja
https://github.com/dop251/goja/blob/master/runtime.go#L2017
https://github.com/loadimpact/k6/blob/master/js/compiler/compiler.go#L116
https://k6.io/docs/using-k6/modules


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM