在应用程序休息之前加载app.js

时间:2017-07-13 22:31:34

标签: javascript extjs extjs6 sencha-cmd

我试图找出如何在允许用户获取实际应用程序之前加载app.js。我试图做的是在我的所有类Ext.defines触发之前加载用户的配置文件...我想这样做的原因是因为Ext.defines实际上依赖于用户配置中的值。例如,在Ext.define中,我可以将title属性设置为从此全局用户配置var中提取。不,我不想通过并改变所有这些属性来使用initComponent ......这可能需要相当长的时间。

相反,我想要做的是加载配置,然后让Ext.defines运行,但我需要在其他类之前加载Ext JS和我定义的一个类。这可能吗?我一直在研究Sencha Cmd的设置,但是我一直都没有成功实现这个功能。我正在玩bootstrap.manifest.exclude: "loadOrder"属性,它加载了classic.json,并没有定义我的类,但不幸的是,这也没有完全加载Ext JS,因此无法使用Ext.onReady。我也不能用我的模型来加载配置。

我在下面有一个非常高级别的示例(这里是Fiddle)。

Ext.define('MyConfigurationModel', {
    extend: 'Ext.data.Model',
    singleton: true,

    fields: [{
        name: 'testValue',
        type: 'string'
    }],

    proxy: {
        type: 'ajax',
        url: '/configuration',
        reader: {
            type: 'json'
        }
    }
});
// Pretend this would be the class we're requiring in our Main file
Ext.define('MyApp.view.child.ClassThatUsesConfiguration', {
    extend: 'Ext.panel.Panel',
    alias: 'widget.classThatUsesConfiguration',
    /* We get an undefined value here because MyConfigurationModel hasn't
     * actually loaded yet, so what I need is to wait until MyConfigurationModel
     * has loaded, and then I can include this class, so the define runs and
     * adds this to the prototype... and no, I don't want to put this in
     * initComponent, as that would mean I would have to update a ton of classes
     * just to accomplish this */
    title: MyConfigurationModel.get('testValue')
});
Ext.define('MyApp.view.main.MainView', {
    extend: 'Ext.Viewport',
    alias: 'widget.appMain',
    requires: [
        'MyApp.view.child.ClassThatUsesConfiguration'
    ],
    items: [{
        xtype: 'classThatUsesConfiguration'
    }]
});
Ext.define('MyApp.Application', {
    extend: 'Ext.app.Application',
    mainView: 'MyApp.view.main.MainView',
    launch: function() {
        console.log('launched');
    }
});

/* In app.js... right now, this gets called after classic.json is downloaded and
 * after our Ext.defines set up, but I basically want this to run first before
 * all of my classes run their Ext.define */
Ext.onReady(function() {
    MyConfigurationModel.load({
        callback: onLoadConfigurationModel
    })
});
function onLoadConfigurationModel(record, operation, successful) {
    if (successful) {
        Ext.application({
            name: 'MyApp',
            extend: 'MyApp.Application'
        });
    }
    else {
        // redirect to login page
    }
}

3 个答案:

答案 0 :(得分:3)

我称之为"拆分构建",因为它从Ext.app.Application类中删除了Ext.container.Viewport类的依赖关系树。所有Ext JS应用程序都有一个设置为主视图的视口。通过将所有需要的应用程序核心声明移动到viewport类,应用程序可以从应用程序类显式加载视口,并且可以将生成构建配置为输出两个单独的文件app.js和viewport.js。然后,在加载应用程序的核心之前,可以进行任意数量的操作。

// The app.js file defines the application class and loads the viewport
// file.
Ext.define('MyApp.Application', {
   extend: 'Ext.app.Application',
   requires: [
      // Ext JS
      'Ext.Loader'
   ],
   appProperty: 'application',
   name: 'MyApp',

   launch: function() {
      // Perform additional operations before loading the viewport
      // and its dependencies.
      Ext.Ajax.request({
         url: 'myapp/config',
         method: 'GET',
         success: this.myAppRequestSuccessCallback
      });
   },

   myAppRequestSuccessCallback: function(options, success, response) {
      // Save response of the request and load the viewport without
      // declaring a dependency on it.
      Ext.Loader.loadScript('classic/viewport.js');
   }
});

-

// The clasic/viewport.js file requires the viewport class which in turn
// requires the rest of the application.    
Ext.require('MyApp.container.Viewport', function() {
   // The viewport requires all additional classes of the application.
   MyApp.application.setMainView('MyApp.container.Viewport');
});

在生产环境中构建时,视口及其依赖项将不会包含在app.js中,因为它未在requires语句中声明。将以下内容添加到应用程序的build.xml文件中,以将视口及其所有依赖项编译为viewport.js。方便的是,开发和生产文件结构保持不变。

<target name="-after-js">
   <!-- The following is derived from the compile-js target in
        .sencha/app/js-impl.xml. Compile the viewport and all of its
        dependencies into viewport.js. Include in the framework
        dependencies in the framework file. -->
    <x-compile refid="${compiler.ref.id}">
        <![CDATA[
            union
              -r
              -class=${app.name}.container.Viewport
            and
            save
              viewport
            and
            intersect
              -set=viewport,allframework
            and
            include
              -set=frameworkdeps
            and
            save
              frameworkdeps
            and
            include
              -tag=Ext.cmd.derive
            and
            concat
              -remove-text-references=${build.remove.references}
              -optimize-string-references=${build.optimize.string.references}
              -remove-requirement-nodes=${build.remove.requirement.nodes}
              ${build.compression}
              -out=${build.framework.file}
              ${build.concat.options}
            and
            restore
              viewport
            and
            exclude
              -set=frameworkdeps
            and
            exclude
              -set=page
            and
            exclude
              -tag=Ext.cmd.derive,derive
            and
            concat
              -remove-text-references=${build.remove.references}
              -optimize-string-references=${build.optimize.string.references}
              -remove-requirement-nodes=${build.remove.requirement.nodes}
              ${build.compression}
              -out=${build.out.base.path}/${build.id}/viewport.js
              ${build.concat.options}
            ]]>
    </x-compile>

    <!-- Concatenate the file that sets the main view. -->
    <concat destfile="${build.out.base.path}/${build.id}/viewport.js" append="true">
       <fileset file="classic/viewport.js" />
    </concat>
</target>

<target name="-before-sass">
    <!-- The viewport is not explicitly required by the application,
         however, its SCSS dependencies need to be included. Unfortunately,
         the property required to filter the output, sass.name.filter, is
         declared as local and cannot be overridden. Use the development
         configuration instead. -->
    <property name="build.include.all.scss" value="true"/>
</target>

此特定实现将框架依赖项保存在自己的文件framework.js中。这被配置为app.json文件中输出声明的一部分。

"output": {
   ...
   "framework": {
      // Split the framework from the application.
      "enable": true
   }
}

https://docs.sencha.com/extjs/6.2.0/classic/Ext.app.Application.html#cfg-mainView https://docs.sencha.com/extjs/6.2.0/classic/Ext.container.Viewport.html https://docs.sencha.com/cmd/guides/advanced_cmd/cmd_build.html#advanced_cmd-_-cmd_build_-_introduction

答案 1 :(得分:2)

据我所知,这对Sencha Cmd来说是不可能的,因为虽然Sencha Cmd可以load framework and application separately,但是不可能告诉生产微装配器等待第二个文件,直到第一个文件中的代码做了什么(大概是从服务器上装了一些东西?)。

所以唯一的方法是在加载ExtJS之前在ExtJS之外获取选项。

您必须编写自己的javascript,使用裸同步XmlHttpRequest将配置加载到全局变量中,并将其包含在ExtJS脚本之前的index.html中。这样,脚本在ExtJS加载之前执行,并且您在开发,测试和生产构建中具有完全一致的行为,而无需修改在框架升级期间可能被覆盖的任何框架文件。

我想这就是您要搜索的内容。

所以我是怎么做到的:在index.html中,我添加了一个填充一些全局变量的自定义脚本:

<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<script type="text/javascript">
    APIURI = '../api/', // <- also used in ExtJS.
    var xhr = new XMLHttpRequest();
    xhr.open('GET', APIURI+'GetOptions', false);
    xhr.setRequestHeader('Accept','application/json');
    xhr.send(null);
    try {
        var configdata = eval("(" + xhr.responseText + ")");
    } catch(e) {
         // snip: custom code for the cases where responseText was invalid JSON because of a shitty backend
    }
    if(configdata.options!=undefined) Settings = configdata.options;
    else Settings = {};
    if(configdata.translations!=undefined) Translations = configdata.translations;
    else Translations = {};
    Translations.get=function(str) {
        if(typeof Translations[str]=="string") return Translations[str];
        return "Translation string "+str+" missing.";
    };
 </script>
<link rel="icon" type="image/vnd.microsoft.icon" href="../favicon.ico">
<title>Application</title>
<script id="microloader" data-app="1a7a9de2-a3b2-2a57-b5af-df428680b72b" type="text/javascript" src="bootstrap.js"></script>

然后我可以在Ext.define()中使用,例如title: Translations.get('TEST')hidden: Settings.HideSomeButtonurl: APIURI + 'GetUserData'

但是,在继续操作之前,您应该考虑这个缺点。

在很短的一段时间后,出现了新的功能请求,我考虑修复的设置应该在运行时更改,我意识到在设置更改时总是重新加载应用程序并不是良好的用户体验。不久之后,我还发现Chrome已弃用同步XmlHttpRequests,这种方法会延迟应用程序启动时间。

因此,决定从长远来看,唯一合理的方法是能够在运行时对任何配置值的更改做出反应,而无需完全重新加载应用程序。这样,可以在加载应用程序后应用设置,并且可以在继续应用程序之前删除需求以等待设置加载。

为此,我必须完全解决完全本地化支持所需的一切,因此用户可以在不重新加载应用程序的情况下在语言之间切换,并且任何其他设置也可以在运行时更改并自动应用于应用程序。 / p>

短期来说,这是一些相当重要的工作,这对我来说并不重要,因为我计划对整个应用程序布局进行修改,但从长远来看,这将节省相当多的时间和头痛,特别是当有人决定我们应该从服务器开始轮询设置的更改,或者我们应该使用ExtJS表单进行登录,而不是使用旧的基本身份验证(当时已经多次询问过了,但由于说的糟糕,我们无法提供ExtJS应用程序架构)。

答案 2 :(得分:2)

我们确实使用了Sencha CMD方法。如上所述@Alexander,我们还使用全局变量来保持应用程序的配置。这种方法还意味着服务器返回全局配置变量的实际声明。

如果您深入查看app.json文件,并找到js配置密钥,您会在说明中看到它

  

正确执行顺序中所有JavaScript资产的列表。

因此,我们在app.js资产

之前添加配置的端点
"js": [
    {
        "path": "data/config",
        "remote": true
    },
    {
        "path": "${framework.dir}/build/ext-all-debug.js"
    },
    {
        "path": "app.js",
        "bundle": true
    }
]

还指定remote: true

  

//如果此文件是远程文件,则指定为true,不应复制   进入构建文件夹

“data / config”端点返回类似于:

的内容
var CONFIG = {
    user: {
        id: 1,
        name: 'User'
    },
    app: {
        language: 'en'
    }
}

现在我们可以在班级的任何地方引用CONFIG变量。