问题:有时候要定义一个变量,这个变量是动态生成的,例如,根据url,UA,http response header上的某些参数生成,这个变量会跨模块的多次使用,有人想直接定义在浏览器的window全局对象上,直接window.xxx用。
这种做法很危险,而且需要手动维护模块的依赖关系。下面用一个典型的例子说明,需求是有一个isWap变量,表示当前是什么平台,在各个模块都可能会使用。
- 在
window对象上定义变量:
入口文件,index.js
import Home from "./containers/Home";
const coin = () => Math.random() < 0.5;
window.isWap = coin();
new Home().render();
index.js做的事情是:引入Home视图类,在window对象上定义isWap变量并赋值,然后实例化Home视图类,调用视图类实例的render方法。
Home视图类,containers/Home/index.js:
import * as actionCreators from "./actions";
console.log("Home component isWap: ", window.isWap);
class Home {
render() {
console.log("Home component render isWap: ", window.isWap);
actionCreators.toggle();
return "home";
}
}
export default Home;
这是Home视图类,重点注意两个console.log的位置,打印window.isWap变量。
Home视图类依赖的actions,containers/Home/actions.js
console.log("actions outside isWap: ", window.isWap);
function toggle() {
console.log("actions inside isWap: ", window.isWap);
return {
type: "TOGGLE",
payload: {}
};
}
export { toggle };
这是Home视图类依赖的action模块,重点注意两个console.log的位置,第一个在模块作用域,第二个在toggle函数中,两个console.log的执行时机是不一样的,第一个会在模块被加载(import, require)时就执行,第二个只有在toggle函数调用时才会执行。
运行结果:
actions outside isWap: undefined
Home component isWap: undefined
Home component render isWap: false
actions inside isWap: false
在actions.js和Home.js的模块作用域中的打印的window.isWap是undefined,这是不符合期望的。原因很简单,import Home视图类模块时,还没有执行到给window对象上定义isWap变量的语句,Home视图类模块作用域中的console.log会先执行。
这就出现模块(代码)依赖顺序的问题了,其他模块要使用window.isWap,必须要保证在其他模块被导入之前,window.isWap已经存在并有值。当模块越来越多,依赖关系越来越复杂,手动维护是不现实的。
注意: 本例中,就算把入口文件改写成如下:
const coin = () => Math.random() < 0.5;
window.isWap = coin();
import Home from "./containers/Home";
new Home().render();
结果是一样的,因为import有作用域提升。
包括Webpack和require.js等模块定义,构建打包工具,作用之一,就是用来处理模块依赖顺序(依赖关系),不用再手动的维护传统的script标签的顺序。
- 定义在模块中,通过
export导出使用
index.js
import Home from "./containers/Home";
new Home().render();
containers/Home/index.js
import * as actionCreators from "./actions";
import { isWap } from "../../platform.service";
console.log("Home Component outside isWap: ", isWap);
class Home {
render() {
console.log("Home component render isWap: ", isWap);
actionCreators.toggle();
return "home";
}
}
export default Home;
containers/Home/actions.js
import { isWap } from "../../platform.service";
console.log("Home action outside isWap: ", isWap);
function toggle() {
console.log("Home action inside isWap: ", isWap);
return {
type: "TOGGLE",
payload: {}
};
}
export { toggle };
在platform.service.js模块中定义isWap,并导出
const coin = () => Math.random() < 0.5;
const isWap = coin();
export { isWap };
运行结果:
Home action outside isWap: false
Home Component outside isWap: false
Home component render isWap: false
Home action inside isWap: false
运行结果是符合期望的,需要isWap变量的地方都显示的导入了platform.service.js模块。Webpack会自动处理模块的依赖顺序(依赖关系),保证isWap在被其他模块使用之前,有值。
这种做法很清晰,可读性,可维护性,可扩展性都很好,符合模块化高内聚,低耦合的特点。