I watched Paul Irish's talk announcing Yeoman (www.yeoman.io), and I'm hooked on the concept of running a continuous build environment. Not content to wait for a Yeoman invite, I tried Grunt and Brunch. Both install easily, and I can get new projects up and running with minimal effort.
我看了Paul Irish的演讲,宣布了Yeoman(www.yeoman.io),我迷上了运行连续构建环境的概念。不满足于等待Yeoman邀请,我尝试了Grunt和Brunch。两者都很容易安装,我可以轻松地启动和运行新项目。
I don't understand how one would migrate an existing project into either platform. My project uses a single namespace and uses two conventions for modules (one for instancing another for utility), each of which are wrapped in self-executing anonymous functions which export to the instance or the namespace.
我不明白如何将现有项目迁移到任一平台。我的项目使用单个命名空间,并对模块使用两个约定(一个用于实例化另一个实例),每个约束都包含在导出实例或命名空间的自执行匿名函数中。
I have at least 200 modules and many more simple, helper function exports to the namespace; so it is not at all efficient to use the console to create these in a grunt/brunch project and then manually import each module individually. Further, I'm using at least 15 different 3rd-party JavaScript tools. It is not clear to me how to bring these in.
我有至少200个模块和更多简单的辅助函数导出到命名空间;因此,使用控制台在grunt / brunch项目中创建这些并且然后单独手动导入每个模块完全没有效率。此外,我使用至少15种不同的第三方JavaScript工具。我不清楚如何把它们带入。
What is the most efficient way to take a large, existing project and migrate it into Grunt/Brunch with the least amount of refactoring and support for arbitrary 3rd party tools?
以最少的重构和对任意第三方工具的支持,采用大型现有项目并将其迁移到Grunt / Brunch的最有效方法是什么?
Update: of the two, I've found Brunch a bit easier to cope with. If you use the stock "skeleton" (that is "template"--from the command line {in the folder you want the change to occur} execute "brunch new [project_name] --skeleton git://github.com/brunch/simple-js-skeleton.git") for pure JS, you get a new folder structure which is actually quite responsive. Anything you drop into to 'app' (your own code) or 'vendor' (3rd-party) folders will get automatically recompiled for you on file edit (when you run "brunch watch").
更新:在这两个中,我发现Brunch更容易应对。如果您使用股票“骨架”(即“模板” - 从命令行{在您希望更改的文件夹中}执行“brunch new [project_name] --skeleton git://github.com/brunch /simple-js-skeleton.git“)对于纯JS,你得到一个新的文件夹结构,实际上是非常敏感的。您在“app”(您自己的代码)或“供应商”(第三方)文件夹中输入的任何内容都将在文件编辑时自动重新编译(当您运行“早午餐手表”时)。
This is great, except. According the documentation, you control the order vendor scripts are compiled and concatenated together from the Brunch config.coffee file (JSON text file). Changes to this file seem to have no effect, thus you end up with 3rd party race conditions from plugins expecting other plugins.
这很棒,除了。根据文档,您可以控制订单供应商脚本从Brunch config.coffee文件(JSON文本文件)编译和连接在一起。对此文件的更改似乎没有任何效果,因此您最终会遇到插件期望其他插件的第三方竞争条件。
Further, when you drop your own code into the auto-created 'app' folder you do get an auto-compiled, real-time, as-you-edit version of your code; but it's not accessible. Brunch obfuscates the window object, so my initial namespace declaration to window.myNameSpace fails and all subsequent library calls to the namespace fail as well. This has something to do with Brunch's module system, for which I can find no documentation.
此外,当您将自己的代码放入自动创建的“app”文件夹中时,您会获得自动编译的,实时的编辑版本的代码;但它无法访问。早午餐会混淆窗口对象,因此我对window.myNameSpace的初始名称空间声明失败,并且对名称空间的所有后续库调用也会失败。这与Brunch的模块系统有关,我找不到任何文档。
I solved this by placing my namespace class in the 'vendor' folder, which ensured that it attached to the window object; however, now there is a race condition: my namespace isn't always available for all of my modules.
我通过将我的命名空间类放在'vendor'文件夹中来解决这个问题,这确保了它附加到window对象;但是,现在存在竞争条件:我的命名空间并不总是可用于我的所有模块。
The problem is now this:
现在的问题是这样的:
Once you have copied all of your internal and external libraries into a Brunch project, how do you configure the app to load them in a sane order?
将所有内部和外部库复制到Brunch项目后,如何配置应用程序以合理的顺序加载它们?
2 个解决方案
#1
8
This is a bit of an opus, but I finally figured it out. When I started with Brunch, it was not obvious how to make the first step: import my directory structure. It took me a few passes over the documentation, before it became obvious:
这是一个作品,但我终于明白了。当我开始使用Brunch时,如何完成第一步并不明显:导入我的目录结构。在它变得明显之前,我花了几遍文档。
- Execute
brunch new MyAppName -s https://github.com/damassi/Javascript-App-Skeleton
, which will generate a skeleton folder structure and config.coffee file - For me, the only relevant folders in this structure were 'app' (the raw src content for CSS, JS and HTML), 'public' (the destination for compiled content and the location servicing the NodeJS server) and 'vendor' (the place for 3rd party files).
- Brunch creates a config.coffee file at the root of the directory structure with this content:
files: javascripts: defaultExtension: 'js' joinTo: 'javascripts/app.js': /^app/ 'javascripts/vendor.js': /^vendor/ order: before: [ 'vendor/scripts/console-helper.js', 'vendor/scripts/jquery-1.7.1.min.js' ]
- The 'joinTo' property of this object confused me, until I realized that 'javascripts' is really just a mask for 'client-side code' and that 'apps.js' is effectively a call to 'get all *.js files in the folder "app", recursively'.
- Once this is clear, all you need to do is drop your content into 'app'. I placed my *.html and images files into the 'assets' subfolder and put all of my JavaScript content into lib.
- At this point, you can run brunch build and brunch watch, and your project is up and running, compiling in real-time as you make changes, live reloading in the browser.
执行早午餐新的MyAppName -s https://github.com/damassi/Javascript-App-Skeleton,它将生成一个骨架文件夹结构和config.coffee文件
对我来说,这个结构中唯一相关的文件夹是'app'(CSS,JS和HTML的原始src内容),'public'(编译内容的目的地和服务NodeJS服务器的位置)和'vendor'(第三方文件的地方)。
Brunch使用以下内容在目录结构的根目录下创建一个config.coffee文件:files:javascripts:defaultExtension:'js'joinTo:'javascripts / app.js':/ ^ app /'javascripts / vendor.js':/ ^ vendor / order:before:['vendor / scripts / console-helper.js','vendor / scripts / jquery-1.7.1.min.js']
这个对象的'joinTo'属性使我感到困惑,直到我意识到'javascripts'实际上只是'客户端代码'的掩码,并且'apps.js'实际上是对'获取所有* .js文件'的调用文件夹“app”,递归'。
一旦清楚,您需要做的就是将内容放入“app”。我将* .html和图像文件放入'assets'子文件夹中,并将我的所有JavaScript内容放入lib中。
此时,您可以运行早午餐构建和早午餐监视,并且您的项目已启动并正在运行,在您进行更改时实时编译,在浏览器中实时重新加载。
While Brunch is better out of the box than Grunt at the ease of using step 6, where it failed for me is the nature of compilation in Brunch. Every JavaScript file gets wrapped in a CommonJS module and the module name is based on the relative path and file name ('lib/core/ajax', etc.). The CommonJS philosophy is not for me, and the work involved in refactoring my library to use CommonJS is huge.
虽然Brunch在使用第6步方面比Grunt更好,但对我来说失败的原因是Brunch的编译性质。每个JavaScript文件都包含在CommonJS模块中,模块名称基于相对路径和文件名('lib / core / ajax'等)。 CommonJS哲学不适合我,并且重构我的库以使用CommonJS所涉及的工作是巨大的。
So, back to Grunt. Once I understood how to import a project into Brunch, importing into Grunt was a snap. I'm on windows, so all grunt calls use grunt.cmd.
所以,回到Grunt。一旦我理解了如何将项目导入Brunch,导入Grunt就很容易了。我在Windows上,所以所有的grunt调用都使用grunt.cmd。
- Call
grunt init:jquery
(this can be anywhere, I moved the created directory structure into my existing project folder) - Like Brunch, you get an auto generated directory structure and config file (grunt.js), but it's much, much thinner. Grunt's config looks like this:
concat: { dist: { src: ['<config:lint.files>'], dest: 'dist/<%= pkg.name %>.js' } }, min: { dist: { src: ['<banner:meta.banner>', '<config:concat.dist.dest>'], dest: 'dist/<%= pkg.name %>.min.js' } }, qunit: { files: ['test/**/*.html'] }, lint: { files: ['grunt.js', 'src/**/*.js', 'test/**/*.js'] }, watch: { files: '<config:lint.files>', tasks: 'lint qunit' }
- This looked a bit alien to my mind at first, but it's actually quite elegant. The 'min' property defines the final, concatenated, linted and minified file that my web app will be serving. Its source value is '', which is Grunt magic to look at the value of concat object's dist dest property value, which is then derived from lint's file's property value. So, you define the resources you want to be linted, concatenated, minified and output to a destination at the lint level.
- Once this piece is in place, you have to do a bit of extra work to get the build, watch and server pieces in place. In grunt, when the server is finished executing, it quits. That means that if you execute the grunt server task, it will start the server and with no other tasks to do, quit.
- My first mistake was to bundle the server task with the watch's task, by setting watch.task = 'server lint qunit'. This works for the first change you make to the source, but the second change will attempt to start a second instance of the server on the same port and fail. Instead, you can register a task
grunt.registerTask('dev', 'server watch qunit');
and call grunt dev to get a server running with real-time, continuous build. - Next, my HTML content depended on server-side includes to assemble the page. I couldn't figure out how to get this working in Node, and client-side includes using
<object/>
don't work, as they insert the content (in my case various<script/>
and<link/>
elements) into an Iframe, which of course breaks my module pattern (My namespace is in a different window object than the window object of the iframes). Fortunately, grunt's concat object is a multitask and it can concatenate anything. So I added my HTML files to concat, and my single-page app was ready to go. - Next, because the Node server is running on a different port than my IIS instance, you have the cross domain ajax issue. This SO article started me on the right path, but I ended up needing the following changes to IIS default content headers:
Access-Control-Allow-Credentials: true Access-Control-Allow-Headers: X-Requested-With, X-Prototype-Version, Content-Type, Origin, Allow Access-Control-Allow-Methods: PUT, GET, POST, DELETE, OPTIONS Access-Control-Allow-Origin: http://localhost:88
- Finally, since I'm using jQuery for my default AJAX handler, I needed to add this to my ajax options:
xhrFields: { withCredentials: true }
- Obviously, there are security implications here; but since this will only affect my dev environment and will not be pushed to Production, I think it's OK.
- Last but not least, I spent an hour trying to debug an error on minification through Uglify, which was conveniently answered by this SO post. Since Visual Studio insists on inserting BOM all over the pace ("UTF-8 With Signature" is the euphemism), but UTF-8 Cast fixes this in quick order.
调用grunt init:jquery(这可以在任何地方,我将创建的目录结构移动到我现有的项目文件夹中)
就像早午餐一样,你会得到一个自动生成的目录结构和配置文件(grunt.js),但它要薄得多。 Grunt的配置如下所示:concat:{dist:{src:['
起初看起来有些陌生,但实际上它非常优雅。 'min'属性定义了我的Web应用程序将服务的最终,连接,linted和minified文件。它的源值是'',这是Grunt魔术来查看concat对象的dist dest属性值的值,然后从lint的文件的属性值派生。因此,您可以定义要在lint级别进行linted,连接,缩小和输出到目标的资源。
一旦这件作品到位,你必须做一些额外的工作来使构建,观察和服务器部件到位。在grunt中,当服务器完成执行时,它退出。这意味着如果您执行grunt服务器任务,它将启动服务器而没有其他任务要做,退出。
我的第一个错误是通过设置watch.task ='server lint qunit'将服务器任务与watch的任务捆绑在一起。这适用于您对源所做的第一次更改,但第二次更改将尝试在同一端口上启动服务器的第二个实例并失败。相反,你可以注册一个任务grunt.registerTask('dev','server watch qunit');并调用grunt dev以使服务器以实时连续构建运行。
接下来,我的HTML内容依赖于服务器端包含来组装页面。我无法弄清楚如何在Node中使用它,并且客户端包括使用不起作用,因为它们插入内容(在我的情况下是各种和 元素)进入一个Iframe,当然这会破坏我的模块模式(我的命名空间与iframe的窗口对象位于不同的窗口对象中)。幸运的是,grunt的concat对象是一个多任务,它可以连接任何东西。所以我将我的HTML文件添加到concat中,我的单页应用程序已准备就绪。
接下来,因为节点服务器在与我的IIS实例不同的端口上运行,所以您具有跨域ajax问题。这篇SO文章让我走上了正确的道路,但我最终需要对IIS默认内容标题进行以下更改:Access-Control-Allow-Credentials:true Access-Control-Allow-Headers:X-Requested-With,X-Prototype -Version,Content-Type,Origin,Allow Access-Control-Allow-Methods:PUT,GET,POST,DELETE,OPTIONS Access-Control-Allow-Origin:http:// localhost:88
最后,由于我使用jQuery作为我的默认AJAX处理程序,我需要将其添加到我的ajax选项中:xhrFields:{withCredentials:true}
显然,这里有安全隐患;但是因为这只会影响我的开发环境并且不会被推送到Production,我认为没关系。
最后但并非最不重要的是,我花了一个小时试图通过Uglify调试缩小的错误,这个SO帖子很方便地回答了。由于Visual Studio坚持在整个节奏中插入BOM(“UTF-8 With Signature”是委婉语),但UTF-8 Cast以快速顺序修复此问题。
Once I figured all of this out, Grunt seems to be working pretty well. I have not yet had a chance to begin testing the actual process of development in this new continuous build environment; but this is what it took to get to being able to start.
一旦我弄明白这一切,Grunt似乎工作得很好。我还没有机会在这个新的持续构建环境中开始测试实际的开发过程;但这是能够开始的。
#2
2
config.coffee
isn't really json rather than real js / coffeescript, but the order editing should work. Can you open an issue in brunch bugtracker with exact config order?
config.coffee不是真正的json而不是真正的js / coffeescript,但订单编辑应该有效。你能用精确的配置顺序在brunch bugtracker中打开一个问题吗?
I don't think there's a fast way of rewriting your app to use modules rather than global window
variables. Globals are considered as a bad taste, by the way. But your solution could work, yep.
我不认为有一种快速的方法来重写您的应用程序以使用模块而不是全局窗口变量。顺便说一句,Globals被认为是一种不好的味道。但你的解决方案可行,是的。
#1
8
This is a bit of an opus, but I finally figured it out. When I started with Brunch, it was not obvious how to make the first step: import my directory structure. It took me a few passes over the documentation, before it became obvious:
这是一个作品,但我终于明白了。当我开始使用Brunch时,如何完成第一步并不明显:导入我的目录结构。在它变得明显之前,我花了几遍文档。
- Execute
brunch new MyAppName -s https://github.com/damassi/Javascript-App-Skeleton
, which will generate a skeleton folder structure and config.coffee file - For me, the only relevant folders in this structure were 'app' (the raw src content for CSS, JS and HTML), 'public' (the destination for compiled content and the location servicing the NodeJS server) and 'vendor' (the place for 3rd party files).
- Brunch creates a config.coffee file at the root of the directory structure with this content:
files: javascripts: defaultExtension: 'js' joinTo: 'javascripts/app.js': /^app/ 'javascripts/vendor.js': /^vendor/ order: before: [ 'vendor/scripts/console-helper.js', 'vendor/scripts/jquery-1.7.1.min.js' ]
- The 'joinTo' property of this object confused me, until I realized that 'javascripts' is really just a mask for 'client-side code' and that 'apps.js' is effectively a call to 'get all *.js files in the folder "app", recursively'.
- Once this is clear, all you need to do is drop your content into 'app'. I placed my *.html and images files into the 'assets' subfolder and put all of my JavaScript content into lib.
- At this point, you can run brunch build and brunch watch, and your project is up and running, compiling in real-time as you make changes, live reloading in the browser.
执行早午餐新的MyAppName -s https://github.com/damassi/Javascript-App-Skeleton,它将生成一个骨架文件夹结构和config.coffee文件
对我来说,这个结构中唯一相关的文件夹是'app'(CSS,JS和HTML的原始src内容),'public'(编译内容的目的地和服务NodeJS服务器的位置)和'vendor'(第三方文件的地方)。
Brunch使用以下内容在目录结构的根目录下创建一个config.coffee文件:files:javascripts:defaultExtension:'js'joinTo:'javascripts / app.js':/ ^ app /'javascripts / vendor.js':/ ^ vendor / order:before:['vendor / scripts / console-helper.js','vendor / scripts / jquery-1.7.1.min.js']
这个对象的'joinTo'属性使我感到困惑,直到我意识到'javascripts'实际上只是'客户端代码'的掩码,并且'apps.js'实际上是对'获取所有* .js文件'的调用文件夹“app”,递归'。
一旦清楚,您需要做的就是将内容放入“app”。我将* .html和图像文件放入'assets'子文件夹中,并将我的所有JavaScript内容放入lib中。
此时,您可以运行早午餐构建和早午餐监视,并且您的项目已启动并正在运行,在您进行更改时实时编译,在浏览器中实时重新加载。
While Brunch is better out of the box than Grunt at the ease of using step 6, where it failed for me is the nature of compilation in Brunch. Every JavaScript file gets wrapped in a CommonJS module and the module name is based on the relative path and file name ('lib/core/ajax', etc.). The CommonJS philosophy is not for me, and the work involved in refactoring my library to use CommonJS is huge.
虽然Brunch在使用第6步方面比Grunt更好,但对我来说失败的原因是Brunch的编译性质。每个JavaScript文件都包含在CommonJS模块中,模块名称基于相对路径和文件名('lib / core / ajax'等)。 CommonJS哲学不适合我,并且重构我的库以使用CommonJS所涉及的工作是巨大的。
So, back to Grunt. Once I understood how to import a project into Brunch, importing into Grunt was a snap. I'm on windows, so all grunt calls use grunt.cmd.
所以,回到Grunt。一旦我理解了如何将项目导入Brunch,导入Grunt就很容易了。我在Windows上,所以所有的grunt调用都使用grunt.cmd。
- Call
grunt init:jquery
(this can be anywhere, I moved the created directory structure into my existing project folder) - Like Brunch, you get an auto generated directory structure and config file (grunt.js), but it's much, much thinner. Grunt's config looks like this:
concat: { dist: { src: ['<config:lint.files>'], dest: 'dist/<%= pkg.name %>.js' } }, min: { dist: { src: ['<banner:meta.banner>', '<config:concat.dist.dest>'], dest: 'dist/<%= pkg.name %>.min.js' } }, qunit: { files: ['test/**/*.html'] }, lint: { files: ['grunt.js', 'src/**/*.js', 'test/**/*.js'] }, watch: { files: '<config:lint.files>', tasks: 'lint qunit' }
- This looked a bit alien to my mind at first, but it's actually quite elegant. The 'min' property defines the final, concatenated, linted and minified file that my web app will be serving. Its source value is '', which is Grunt magic to look at the value of concat object's dist dest property value, which is then derived from lint's file's property value. So, you define the resources you want to be linted, concatenated, minified and output to a destination at the lint level.
- Once this piece is in place, you have to do a bit of extra work to get the build, watch and server pieces in place. In grunt, when the server is finished executing, it quits. That means that if you execute the grunt server task, it will start the server and with no other tasks to do, quit.
- My first mistake was to bundle the server task with the watch's task, by setting watch.task = 'server lint qunit'. This works for the first change you make to the source, but the second change will attempt to start a second instance of the server on the same port and fail. Instead, you can register a task
grunt.registerTask('dev', 'server watch qunit');
and call grunt dev to get a server running with real-time, continuous build. - Next, my HTML content depended on server-side includes to assemble the page. I couldn't figure out how to get this working in Node, and client-side includes using
<object/>
don't work, as they insert the content (in my case various<script/>
and<link/>
elements) into an Iframe, which of course breaks my module pattern (My namespace is in a different window object than the window object of the iframes). Fortunately, grunt's concat object is a multitask and it can concatenate anything. So I added my HTML files to concat, and my single-page app was ready to go. - Next, because the Node server is running on a different port than my IIS instance, you have the cross domain ajax issue. This SO article started me on the right path, but I ended up needing the following changes to IIS default content headers:
Access-Control-Allow-Credentials: true Access-Control-Allow-Headers: X-Requested-With, X-Prototype-Version, Content-Type, Origin, Allow Access-Control-Allow-Methods: PUT, GET, POST, DELETE, OPTIONS Access-Control-Allow-Origin: http://localhost:88
- Finally, since I'm using jQuery for my default AJAX handler, I needed to add this to my ajax options:
xhrFields: { withCredentials: true }
- Obviously, there are security implications here; but since this will only affect my dev environment and will not be pushed to Production, I think it's OK.
- Last but not least, I spent an hour trying to debug an error on minification through Uglify, which was conveniently answered by this SO post. Since Visual Studio insists on inserting BOM all over the pace ("UTF-8 With Signature" is the euphemism), but UTF-8 Cast fixes this in quick order.
调用grunt init:jquery(这可以在任何地方,我将创建的目录结构移动到我现有的项目文件夹中)
就像早午餐一样,你会得到一个自动生成的目录结构和配置文件(grunt.js),但它要薄得多。 Grunt的配置如下所示:concat:{dist:{src:['
起初看起来有些陌生,但实际上它非常优雅。 'min'属性定义了我的Web应用程序将服务的最终,连接,linted和minified文件。它的源值是'',这是Grunt魔术来查看concat对象的dist dest属性值的值,然后从lint的文件的属性值派生。因此,您可以定义要在lint级别进行linted,连接,缩小和输出到目标的资源。
一旦这件作品到位,你必须做一些额外的工作来使构建,观察和服务器部件到位。在grunt中,当服务器完成执行时,它退出。这意味着如果您执行grunt服务器任务,它将启动服务器而没有其他任务要做,退出。
我的第一个错误是通过设置watch.task ='server lint qunit'将服务器任务与watch的任务捆绑在一起。这适用于您对源所做的第一次更改,但第二次更改将尝试在同一端口上启动服务器的第二个实例并失败。相反,你可以注册一个任务grunt.registerTask('dev','server watch qunit');并调用grunt dev以使服务器以实时连续构建运行。
接下来,我的HTML内容依赖于服务器端包含来组装页面。我无法弄清楚如何在Node中使用它,并且客户端包括使用不起作用,因为它们插入内容(在我的情况下是各种和 元素)进入一个Iframe,当然这会破坏我的模块模式(我的命名空间与iframe的窗口对象位于不同的窗口对象中)。幸运的是,grunt的concat对象是一个多任务,它可以连接任何东西。所以我将我的HTML文件添加到concat中,我的单页应用程序已准备就绪。
接下来,因为节点服务器在与我的IIS实例不同的端口上运行,所以您具有跨域ajax问题。这篇SO文章让我走上了正确的道路,但我最终需要对IIS默认内容标题进行以下更改:Access-Control-Allow-Credentials:true Access-Control-Allow-Headers:X-Requested-With,X-Prototype -Version,Content-Type,Origin,Allow Access-Control-Allow-Methods:PUT,GET,POST,DELETE,OPTIONS Access-Control-Allow-Origin:http:// localhost:88
最后,由于我使用jQuery作为我的默认AJAX处理程序,我需要将其添加到我的ajax选项中:xhrFields:{withCredentials:true}
显然,这里有安全隐患;但是因为这只会影响我的开发环境并且不会被推送到Production,我认为没关系。
最后但并非最不重要的是,我花了一个小时试图通过Uglify调试缩小的错误,这个SO帖子很方便地回答了。由于Visual Studio坚持在整个节奏中插入BOM(“UTF-8 With Signature”是委婉语),但UTF-8 Cast以快速顺序修复此问题。
Once I figured all of this out, Grunt seems to be working pretty well. I have not yet had a chance to begin testing the actual process of development in this new continuous build environment; but this is what it took to get to being able to start.
一旦我弄明白这一切,Grunt似乎工作得很好。我还没有机会在这个新的持续构建环境中开始测试实际的开发过程;但这是能够开始的。
#2
2
config.coffee
isn't really json rather than real js / coffeescript, but the order editing should work. Can you open an issue in brunch bugtracker with exact config order?
config.coffee不是真正的json而不是真正的js / coffeescript,但订单编辑应该有效。你能用精确的配置顺序在brunch bugtracker中打开一个问题吗?
I don't think there's a fast way of rewriting your app to use modules rather than global window
variables. Globals are considered as a bad taste, by the way. But your solution could work, yep.
我不认为有一种快速的方法来重写您的应用程序以使用模块而不是全局窗口变量。顺便说一句,Globals被认为是一种不好的味道。但你的解决方案可行,是的。