cocosjs_web_打包成多個文件

author:mucjgm

轉載請註明出處。


版本:cocos2dx 3.10


需求:由於默認的打包方式打出來的game.min.js文件比較大。服務器方面要求我分成幾個文件打包。


研究:

1.首先根據打包命令cocos compile -p web -m release找到打包入口。位於引擎目錄下

cocos2d-x-3.10/tools/cocos2d-console/plugins/plugin_compile/project_compile.py

2.在文件中找到web的打包函數。build_web。

3.發現他是用谷歌的closure-compiler打包的。於是去github查看源碼。https://github.com/google/closure-compiler

4.wiki顯示只支持單文件打包。於是想分成多次compiler。然後在index.html裏面多次加載。

5.找到web的關鍵函數gen_buildxml。位於

cocos2d-x-3.10/tools/cocos2d-console/plugins/plugin_compile/build_web/__init__.py

6.開始修改。我的修改方式是根據根據裏面現成的module生成多個js文件。修改後的gen_buildxml函數代碼如下。

def gen_buildxml(project_dir, project_json, output_dir, build_opts):
    # get engine dir (not real)
    engineDir = project_json["engineDir"]
    # get real engine dir
    engine_dir = os.path.normpath(os.path.join(project_dir, engineDir))
    # get real publish dir
    publish_dir = output_dir
    # get tools dir
    if getattr(sys, 'frozen', None):
        tools_dir = os.path.realpath(os.path.dirname(sys.executable))
    else:
        tools_dir = os.path.realpath(os.path.dirname(__file__))

    # download the binary files
    compiler_1_6 = os.path.join(tools_dir, "bin", "compiler-1.6.jar")
    compiler_1_7 = os.path.join(tools_dir, "bin", "compiler-1.7.jar")
    if not os.path.exists(compiler_1_6) or not os.path.exists(compiler_1_7):
        download_cmd_path = os.path.join(tools_dir, os.pardir, os.pardir, os.pardir)
        subprocess.call("python %s -f" % (os.path.join(download_cmd_path, "download-bin.py")), shell=True, cwd=download_cmd_path)

    try:
        f = open(os.path.join(engine_dir, "moduleConfig.json"))
        print os.path.join(engine_dir, "moduleConfig.json")
        module_cfg = json.load(f)
    finally:
        f.close()

    ccModuleMap = module_cfg["module"]
    modules = project_json.get("modules", ["core"])
    renderMode = project_json.get("renderMode", 0)
    mainJs = project_json.get("main", "main.js")
    ccJsList = [module_cfg["bootFile"]]
    userJsList = project_json.get("jsList", [])

    if renderMode != 1 and "base4webgl" not in modules:
        modules[0:0] = ["base4webgl"]

    userJsList.append(mainJs)

    def inGen(loop, ifUser = False):
        xmlname = "build.xml"
        if ifUser:
            xmlname = "buildUser.xml"
        buildXmlTempFile = open(os.path.join(tools_dir, "template", xmlname))
        try:
            buildContent = buildXmlTempFile.read()
        finally:
            buildXmlTempFile.close()

        jdk_version = check_jdk_version()
        sourceMapOpened =  build_opts.get("sourceMapOpened")
        if jdk_version == JDK_1_6:
            sourceMapOpened = False
        sourceMapContent = 'sourceMapOutputFile="' + os.path.join(publish_dir, "sourcemap") + '" sourceMapFormat="V3"' if sourceMapOpened else ""

        buildContent = buildContent.replace("%projectDir%", project_dir)
        buildContent = buildContent.replace("%engineDir%", engine_dir)
        buildContent = buildContent.replace("%publishDir%", publish_dir)

        buildContent = buildContent.replace("%outputFileName%", build_opts["outputFileName"] % loop)

        buildContent = buildContent.replace("%toolsDir%", tools_dir)
        buildContent = buildContent.replace("%compiler%", "compiler-%s.jar" % jdk_version)
        buildContent = buildContent.replace("%compilationLevel%", build_opts["compilationLevel"])
        buildContent = buildContent.replace("%sourceMapCfg%",  sourceMapContent)

        if ifUser:
            buildContent = buildContent.replace("%userJsList%", _getFileArrStr(userJsList))
        else:
            buildContent = buildContent.replace("%ccJsList%", _getFileArrStr(ccJsList))
        buildContent = buildContent.replace("%debug%", build_opts["debug"])

        buildXmlOutputFile = open(os.path.join(publish_dir, "build" + str(loop) + ".xml"), "w")
        buildXmlOutputFile.write(buildContent)
        buildXmlOutputFile.close()

    inloop = 1
    for item in modules:
        print item
        arr = _getJsListOfModule(ccModuleMap, item)
        if arr != None:
            if inloop == 1:
                ccJsList += arr
            else:
                ccJsList = arr
            inGen(inloop)
            inloop = inloop + 1
    inGen(inloop, True)
    return inloop

最後返回文件的數量。提供給前面去操作。

7.同時要添加一個新的文件builduser.xml。和修改之前template裏面的build.xml

這是修改後的build.xml

<?xml version="1.0"?>
<project name="Javascript compress project" basedir="%projectDir%" default="compile">

    <taskdef name="jscomp" classname="com.google.javascript.jscomp.ant.CompileTask"
             classpath="%toolsDir%/bin/%compiler%"/>

    <target name="compile">
        <jscomp compilationLevel="%compilationLevel%" 
                warning="quiet"
                debug="%debug%" 
                output="%publishDir%/%outputFileName%"
                languagein="ECMASCRIPT5"
                %sourceMapCfg%
                >
            <sources dir="%engineDir%">
%ccJsList%
            </sources>
        </jscomp>
    </target>
</project>

這是後添加的builduser.xml
<?xml version="1.0"?>
<project name="Javascript compress project" basedir="%projectDir%" default="compile">

    <taskdef name="jscomp" classname="com.google.javascript.jscomp.ant.CompileTask"
             classpath="%toolsDir%/bin/%compiler%"/>

    <target name="compile">
        <jscomp compilationLevel="%compilationLevel%" 
                warning="quiet"
                debug="%debug%" 
                output="%publishDir%/%outputFileName%"
                languagein="ECMASCRIPT5"
                %sourceMapCfg%
                >
            <sources dir="${basedir}">
%userJsList%
            </sources>
        </jscomp>
    </target>
</project>
文件目錄都位於

cocos2d-x-3.10/tools/cocos2d-console/plugins/plugin_compile/build_web/template/

8.修改project_compile.py裏面的函數。

修改後的build_web函數如下。

def build_web(self):
        if not self._platforms.is_web_active():
            return

        project_dir = self._platforms.project_path()

        # store env for run
        cfg_obj = self._platforms.get_current_config()
        if cfg_obj.run_root_dir is not None:
            self.run_root = cfg_obj.run_root_dir
        else:
            self.run_root = project_dir

        if cfg_obj.sub_url is not None:
            self.sub_url = cfg_obj.sub_url
        else:
            self.sub_url = '/'

        output_dir = CCPluginCompile.OUTPUT_DIR_SCRIPT_RELEASE
        if self._is_debug_mode():
            output_dir = CCPluginCompile.OUTPUT_DIR_SCRIPT_DEBUG
            if not self._web_advanced:
                return

        self.sub_url = '%s%s/%s/' % (self.sub_url, output_dir, CCPluginCompile.WEB_PLATFORM_FOLDER_NAME)

        f = open(os.path.join(project_dir, "project.json"))
        project_json = json.load(f)
        f.close()
        engine_dir = os.path.join(project_json["engineDir"])
        realEngineDir = os.path.normpath(os.path.join(project_dir, engine_dir))
        publish_dir = os.path.normpath(os.path.join(project_dir, output_dir, CCPluginCompile.WEB_PLATFORM_FOLDER_NAME))

        # need to config in options of command
        buildOpt = {
                "outputFileName" : "game%d.js",
                "debug": "true" if self._is_debug_mode() else "false",
                "compilationLevel" : "advanced" if self._web_advanced else "simple",
                "sourceMapOpened" : True if self._has_sourcemap else False
                }

        if os.path.exists(publish_dir):
            shutil.rmtree(publish_dir)
        os.makedirs(publish_dir)

        # generate build.xml
        counts = build_web.gen_buildxml(project_dir, project_json, publish_dir, buildOpt)
        for x in xrange(1,counts + 1):
            outputJsPath = os.path.join(publish_dir, buildOpt["outputFileName"] % x)
            if os.path.exists(outputJsPath) == True:
                os.remove(outputJsPath)


        # call closure compiler
        ant_root = cocos.check_environment_variable('ANT_ROOT')
        ant_path = os.path.join(ant_root, 'ant')
        for x in xrange(1,counts + 1):
            self._run_cmd("%s -f %s" % (ant_path, os.path.join(publish_dir, "build%d.xml" % x)))

        # handle sourceMap
        sourceMapPath = os.path.join(publish_dir, "sourcemap")
        if os.path.exists(sourceMapPath):
            smFile = open(sourceMapPath)
            try:
                smContent = smFile.read()
            finally:
                smFile.close()

            dir_to_replace = project_dir
            if cocos.os_is_win32():
                dir_to_replace = project_dir.replace('\\', '\\\\')
            smContent = smContent.replace(dir_to_replace, os.path.relpath(project_dir, publish_dir))
            smContent = smContent.replace(realEngineDir, os.path.relpath(realEngineDir, publish_dir))
            smContent = smContent.replace('\\\\', '/')
            smContent = smContent.replace('\\', '/')
            smFile = open(sourceMapPath, "w")
            smFile.write(smContent)
            smFile.close()

        # handle project.json
        del project_json["engineDir"]
        del project_json["modules"]
        del project_json["jsList"]
        project_json_output_file = open(os.path.join(publish_dir, "project.json"), "w")
        project_json_output_file.write(json.dumps(project_json))
        project_json_output_file.close()

        # handle index.html
        indexHtmlFile = open(os.path.join(project_dir, "index.html"))
        try:
            #indexContent = indexHtmlFile.read()
            linesContent = indexHtmlFile.readlines()
        finally:
            indexHtmlFile.close()
        #reg1 = re.compile(r'<script\s+src\s*=\s*("|\')[^"\']*CCBoot\.js("|\')\s*><\/script>')
        #indexContent = reg1.sub("", indexContent)
        indexHtmlOutputFile = open(os.path.join(publish_dir, "index.html"), "w")
        mainJs = project_json.get("main") or "main.js"
        for line in linesContent:
            if re.search(r"CCBoot.js", line):
                pass
            elif re.search(mainJs, line):
                for x in range(1, counts + 1):
                    inline = line.replace(mainJs, buildOpt["outputFileName"] % x)
                    indexHtmlOutputFile.write(inline)
            else:
                indexHtmlOutputFile.write(line)
        #indexContent = indexContent.replace(mainJs, buildOpt["outputFileName"])
        indexHtmlOutputFile.close()
        
        # copy res dir
        if cfg_obj.copy_res is None:
            dst_dir = os.path.join(publish_dir, 'res')
            src_dir = os.path.join(project_dir, 'res')
            if os.path.exists(dst_dir):
                shutil.rmtree(dst_dir)
            shutil.copytree(src_dir, dst_dir)
        else:
            for cfg in cfg_obj.copy_res:
                cocos.copy_files_with_config(cfg, project_dir, publish_dir)

        # copy to the output directory if necessary
        pub_dir = os.path.normcase(publish_dir)
        out_dir = os.path.normcase(os.path.normpath(self._output_dir))
        if pub_dir != out_dir:
            cpy_cfg = {
                "from" : pub_dir,
                "to" : out_dir
            }
            cocos.copy_files_with_config(cpy_cfg, pub_dir, out_dir)

9.修改完畢。編譯。結果顯示正常。這是publish裏面生成的文件。index.html對應加載。

我對html不太瞭解。不知道這樣加載有沒有問題。不過我run了之後是暫時沒發現問題的。


end.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章