421
社区成员




单例模式;打包;公共代码提取;webpack
系统版本:Openharmony-3.1release
sdk版本:API9 - 3.1.5.5Release
问题现象:单例对象在Stage模型中无法使用,FA模型中可以使用
测试步骤:
export default class Single {
private static _routerUtil: Single = new Single();
num: number = 0;
static instance(): Single{
return this._routerUtil
}
constructor() {
console.log("Single constructor ");
}
add() {
this.num ++;
}
}
分别在pageA和pageB页面引入,调用instance方法获取对象。
Stage模型下pageA和pageB通过instance方法获取的对象之间数据num是不互通的,在FA模型下pageA和pageB获取的对象是一个对象,Single的实例化对象的num属性在两个页面之间互通。
两种模式打包时,webpack对两种模式打包进行了区分,Stage模型下没有提取公共部分代码,每次引入公共代码,最终会复制一份到使用的地方。FA模型下进行了公共部分代码提取,多次引用的代码会被单独打包到公共文件中。
// pageA.ets
import router from '@ohos.router';
import Single from './Single';
const single = Single.instance();
@Entry
@Component
struct PageA {
@State message: string = "Hello World"
aboutToAppear() {
console.log("进入 pageA 打印single.num");
console.log("pageA single.num : " + single.num);
}
add() {
console.log("pageA 点击 add 后打印single.num");
single.add();
console.log("pageA single.num : " + single.num);
}
build() {
Row() {
Column({ space: 20 }) {
Button("pageA 点击 add").fontSize(32).onClick(() => {
this.add();
})
Button("跳转到 pageB").fontSize(32).onClick(() => {
console.log("pageB 跳转到 pageB");
router.push({
url: "pages/pageB"
})
})
}.width("100%")
}.height("100%")
}
}
// pageB.ets
import router from '@ohos.router';
import Single from './Single';
const single = Single.instance();
@Entry
@Component
struct PageB {
aboutToAppear() {
console.log("进入 pageB 打印single.num");
console.log("pageB single.num : " + single.num);
}
add() {
console.log("pageB 点击 add 后打印single.num");
single.add();
console.log("pageB single.num : " + single.num);
}
build() {
Row() {
Column({ space: 20 }) {
Button("pageB 点击 add").fontSize(32).onClick(() => {
this.add();
})
Button("跳转到 pageA").fontSize(32).onClick(() => {
console.log("pageB 跳转到 pageA");
router.push({
url: "pages/pageA"
})
})
}.width("100%")
}.height("100%")
}
}
// /entry/build-profile.json5
{
"apiType": 'faMode', // FA模型
"buildOption": {},
"targets": [{
"name": "default",
},
{
"name": "ohosTest",
}
]
}
// 第一次进入pageA页面打印了初始的 single.num 0
[phone][Console DEBUG] 06/17 10:39:49 4008 app Log: Single constructor
[phone][Console DEBUG] 06/17 10:39:49 4008 app Log: 进入 pageA 打印single.num
[phone][Console DEBUG] 06/17 10:39:49 4008 app Log: pageA single.num : 0
// 点击三次 pageA的add按钮,每次打印最新的single.num
[phone][Console DEBUG] 06/17 10:39:58 4008 app Log: pageA 点击 add 后打印single.num
[phone][Console DEBUG] 06/17 10:39:58 4008 app Log: pageA single.num : 1
[phone][Console DEBUG] 06/17 10:39:58 4008 app Log: pageA 点击 add 后打印single.num
[phone][Console DEBUG] 06/17 10:39:58 4008 app Log: pageA single.num : 2
[phone][Console DEBUG] 06/17 10:39:58 4008 app Log: pageA 点击 add 后打印single.num
[phone][Console DEBUG] 06/17 10:39:58 4008 app Log: pageA single.num : 3
// 点击 跳转到pageB 按钮,进入pageB页面后打印了 signle.num 为 3
[phone][Console DEBUG] 06/17 10:40:01 4008 app Log: pageB 跳转到 pageB
[phone][Console DEBUG] 06/17 10:40:01 4008 app Log: 进入 pageB 打印single.num
[phone][Console DEBUG] 06/17 10:40:01 4008 app Log: pageB single.num : 3
// 点击三次 pageB的add按钮,每次打印最新的single.num
[phone][Console DEBUG] 06/17 10:40:09 4008 app Log: pageB 点击 add 后打印single.num
[phone][Console DEBUG] 06/17 10:40:09 4008 app Log: pageB single.num : 4
[phone][Console DEBUG] 06/17 10:40:10 4008 app Log: pageB 点击 add 后打印single.num
[phone][Console DEBUG] 06/17 10:40:10 4008 app Log: pageB single.num : 5
[phone][Console DEBUG] 06/17 10:40:11 4008 app Log: pageB 点击 add 后打印single.num
[phone][Console DEBUG] 06/17 10:40:11 4008 app Log: pageB single.num : 6
// 点击 跳转到pageA 按钮,进入pageA页面后打印了 signle.num 为 6
[phone][Console DEBUG] 06/17 10:40:14 4008 app Log: pageB 跳转到 pageA
[phone][Console DEBUG] 06/17 10:40:14 4008 app Log: 进入 pageA 打印single.num
[phone][Console DEBUG] 06/17 10:40:14 4008 app Log: pageA single.num : 6
经过日志可以看出在两个页面切换时获取到的single.num的数据是互通的,两个页面之间共用了一个single对象。
查看编译后的代码\entry\build\default\intermediates\assets\default\js\MainAbility\commons.js中将Single作为模块导出:
Object.defineProperty(exports, "__esModule", ({ value: true }));
class Single {
constructor() {
this.num = 0;
console.log("Single constructor ");
}
static instance() {
return this._routerUtil;
}
add() {
this.num++;
}
}
exports["default"] = Single;
Single._routerUtil = new Single();
在 \entry\build\default\intermediates\assets\default\js\MainAbility\pages\pageA.js中没有Single代码,Single通过引入的方式使用:
const Single_1 = __importDefault(__webpack_require__( /*! ./Single */ "D:\\codeOpenHarmony\\demo1\\entry\\src\\main\\ets\\MainAbility\\pages\\Single.ts"));
const single = Single_1.default.instance();
pageB.js与pageA.js一样是通过引入的方式使用。由此看出在FA模型下,公共使用的代码会打包到commons.js文件中。
// /entry/build-profile.json5
{
"apiType": 'stageMode', // Stage模型
"buildOption": {},
"targets": [{
"name": "default",
},
{
"name": "ohosTest",
}
]
}
// 第一次进入pageA页面打印了初始的 single.num 0
[phone][Console DEBUG] 06/17 11:07:14 12072 app Log: Single constructor
[phone][Console DEBUG] 06/17 11:07:14 12072 app Log: 进入 pageA 打印single.num
[phone][Console DEBUG] 06/17 11:07:14 12072 app Log: pageA single.num : 0
// 点击三次 pageA的add按钮,每次打印最新的single.num
[phone][Console DEBUG] 06/17 11:07:24 12072 app Log: pageA 点击 add 后打印single.num
[phone][Console DEBUG] 06/17 11:07:24 12072 app Log: pageA single.num : 1
[phone][Console DEBUG] 06/17 11:07:25 12072 app Log: pageA 点击 add 后打印single.num
[phone][Console DEBUG] 06/17 11:07:25 12072 app Log: pageA single.num : 2
[phone][Console DEBUG] 06/17 11:07:26 12072 app Log: pageA 点击 add 后打印single.num
[phone][Console DEBUG] 06/17 11:07:26 12072 app Log: pageA single.num : 3
// 点击 跳转到pageB 按钮,进入pageB页面后打印了 signle.num 为 0
[phone][Console DEBUG] 06/17 11:07:28 12072 app Log: pageB 跳转到 pageB
[phone][Console DEBUG] 06/17 11:07:28 12072 app Log: Single constructor
[phone][Console DEBUG] 06/17 11:07:28 12072 app Log: 进入 pageB 打印single.num
[phone][Console DEBUG] 06/17 11:07:28 12072 app Log: pageB single.num : 0
// 点击三次 pageB的add按钮,每次打印最新的single.num
[phone][Console DEBUG] 06/17 11:07:31 12072 app Log: pageB 点击 add 后打印single.num
[phone][Console DEBUG] 06/17 11:07:31 12072 app Log: pageB single.num : 1
[phone][Console DEBUG] 06/17 11:07:32 12072 app Log: pageB 点击 add 后打印single.num
[phone][Console DEBUG] 06/17 11:07:32 12072 app Log: pageB single.num : 2
[phone][Console DEBUG] 06/17 11:07:32 12072 app Log: pageB 点击 add 后打印single.num
[phone][Console DEBUG] 06/17 11:07:32 12072 app Log: pageB single.num : 3
// 点击 跳转到pageA 按钮,进入pageA页面后打印了 signle.num 为 0
[phone][Console DEBUG] 06/17 11:07:34 12072 app Log: pageB 跳转到 pageA
[phone][Console DEBUG] 06/17 11:07:34 12072 app Log: Single constructor
[phone][Console DEBUG] 06/17 11:07:34 12072 app Log: 进入 pageA 打印single.num
[phone][Console DEBUG] 06/17 11:07:34 12072 app Log: pageA single.num : 0
由打印日志可以看出,每次进入页面signle.num 都为 0并且还打印了Single constructor ,由此判断出single在页面间并不互通。
查看编译后的文件,在\entry\build\default\intermediates\assets\default\ets\目录下没有发现common相关文件。
打开\entry\build\default\intermediates\assets\default\ets\pages\目录下的pageA.js和pageB.js,发现在两个文件中均存在以下代码:
Object.defineProperty(exports, "__esModule", ({ value: true }));
class Single {
constructor() {
this.num = 0;
console.log("Single constructor ");
}
static instance() {
return this._routerUtil;
}
add() {
this.num++;
}
}
exports["default"] = Single;
Single._routerUtil = new Single();
pageA.js和pageB.js中使用的Single均为自己创建的Single。由此判断出在Stage模式下没有提取公共使用的代码。
function setOptimizationConfig(config) {
// 根据模型判断是否需要添加公共代码提取,moduleJson为Stage模型下的配置文件名称
if (process.env.compileMode !== 'moduleJson') {
config.optimization = {
splitChunks: {
// 屏蔽workers和TestAbility
chunks(chunk) {
return !/^\.\/workers\//.test(chunk.name) && !/^\.\/TestAbility/.test(chunk.name);
},
// 生成 chunk 的最小体积
minSize: 0,
cacheGroups: {
// 提取node_modules中使用的库打包到vendors.js中
vendors: {
// 匹配的文件夹
test: /[\\/]node_modules[\\/]/,
// 优先级
priority: -10,
// 文件名称
name: "vendors",
},
commons: {
name: 'commons',
priority: -20,
// 拆分前必须共享模块的最小 chunks 数。
minChunks: 2,
// 如果当前 chunk 包含已从主 bundle 中拆分出的模块,则它将被重用,而不是生成新的模块。
reuseExistingChunk: true
}
}
}
}
}
}
由代码可判断出,在FA模型中webpack配置了splitChunks,Stage模型中屏蔽了此部分配置。因此FA模型在打包时会提取公共代码,而Stage模型下没有提取公共部分代码。
OpenHarmony应用开发在打包的时候是使用webpack进行代码的解析编译,目前webpack在公共代码提取的部分对FA和Stage两种模型做了一定的区分。