鸿蒙5.0开发进阶:@ohos.bundle.overlay (overlay模块)

往期鸿蒙全套实战文章必看:(文中附带全栈鸿蒙学习资料)


@ohos.bundle.overlay (overlay模块)

本模块提供overlay特征应用的安装,overlay特征应用的OverlayModuleInfo信息的查询以及overlay特征应用的禁用使能的能力。

说明

本模块首批接口从API version 10开始支持。后续版本的新增接口,采用上角标单独标记接口的起始版本。

导入模块

import { overlay } from '@kit.AbilityKit';

overlay.setOverlayEnabled

setOverlayEnabled(moduleName:string, isEnabled: boolean): Promise<void>

以异步方法设置当前应用中overlay特征module的禁用使能状态。使用Promise异步回调。成功返回null,失败返回对应错误信息。

系统能力: SystemCapability.BundleManager.BundleFramework.Overlay

参数:

参数名类型必填说明
moduleNamestringoverlay特征module的名称。
isEnabledboolean值为true表示使能,值为false表示禁用。

返回值:

类型说明
Promise<void>Promise对象。无返回结果的Promise对象。

错误码:

以下错误码的详细介绍。

错误码ID错误信息
401Parameter error. Possible causes: 1. Mandatory parameters are left unspecified; 2. Incorrect parameter types.
17700002The specified module name is not found.
17700033The specified module is not an overlay module.

示例:

import { overlay } from '@kit.AbilityKit';
import { BusinessError } from '@kit.BasicServicesKit';

let moduleName = "feature";
let isEnabled = false;

try {
  overlay.setOverlayEnabled(moduleName, isEnabled)
    .then(() => {
      console.info('setOverlayEnabled success');
    }).catch((err: BusinessError) => {
      console.info('setOverlayEnabled failed due to err code: ' + err.code + ' ' + 'message:' + err.message);
    });
} catch (err) {
  let code = (err as BusinessError).code;
  let message = (err as BusinessError).message;
  console.info('setOverlayEnabled failed due to err code: ' + code + ' ' + 'message:' + message);
}

overlay.setOverlayEnabled

setOverlayEnabled(moduleName:string, isEnabled: boolean, callback: AsyncCallback<void>): void

以异步方法设置当前应用中overlay module的禁用使能状态。使用callback异步回调。成功返回null,失败返回对应错误信息。

系统能力: SystemCapability.BundleManager.BundleFramework.Overlay

参数:

参数名类型必填说明
moduleNamestringoverlay特征module的名称。
isEnabledboolean值为true表示使能,值为false表示禁用。
callbackAsyncCallback<void>回调函数,当设置指定module的overlay禁用使能状态成功时,err为null,否则为错误对象。

错误码:

以下错误码的详细介绍。

错误码ID错误信息
401Parameter error. Possible causes: 1. Mandatory parameters are left unspecified; 2. Incorrect parameter types.
17700002The specified module name is not found.
17700033The specified module is not an overlay module.

示例:

import { overlay } from '@kit.AbilityKit';
import { BusinessError } from '@kit.BasicServicesKit';

let moduleName = "feature";
let isEnabled = false;

try {
  overlay.setOverlayEnabled(moduleName, isEnabled, (err, data) => {
    if (err) {
      console.info('setOverlayEnabled failed due to err code: ' + err.code + ' ' + 'message:' + err.message);
      return;
    }
    console.info('setOverlayEnabled success');
  });
} catch (err) {
  let code = (err as BusinessError).code;
  let message = (err as BusinessError).message;
  console.info('setOverlayEnabled failed due to err code: ' + code + ' ' + 'message:' + message);
}

overlay.getOverlayModuleInfo

getOverlayModuleInfo(moduleName: string): Promise<OverlayModuleInfo>

以异步方法获取当前应用中指定的module的overlayModuleInfo信息。使用promise异步回调,成功返回null,失败返回对应错误信息。

系统能力: SystemCapability.BundleManager.BundleFramework.Overlay

参数:

参数名类型必填说明
moduleNamestring指定当前应用中的overlay module的名称。

返回值:

类型说明
Promise<OverlayModuleInfo>Promise对象,返回OverlayModuleInfo

错误码:

以下错误码的详细介绍。

错误码ID错误信息
401Parameter error. Possible causes: 1. Mandatory parameters are left unspecified; 2. Incorrect parameter types.
17700002The specified module name is not found.
17700032The specified bundle does not contain any overlay module.
17700033The specified module is not an overlay module.

示例:

import { overlay } from '@kit.AbilityKit';
import { BusinessError } from '@kit.BasicServicesKit';

let moduleName = "feature";

(async () => {
  try {
    let overlayModuleInfo = await overlay.getOverlayModuleInfo(moduleName);
    console.log('overlayModuleInfo is ' + JSON.stringify(overlayModuleInfo));
  } catch (err) {
    let code = (err as BusinessError).code;
    let message = (err as BusinessError).message;
    console.log('getOverlayModuleInfo failed due to err code : ' + code + ' ' + 'message :' + message);
  }
})();

overlay.getOverlayModuleInfo

getOverlayModuleInfo(moduleName: string, callback: AsyncCallback<OverlayModuleInfo>): void

以异步方法获取当前应用中指定的module的overlayModuleInfo信息。使用callback异步回调,成功返回null,失败返回对应错误信息。

系统能力: SystemCapability.BundleManager.BundleFramework.Overlay

参数:

参数名类型必填说明
moduleNamestring指定当前应用中的overlay特征module的名称。
callbackAsyncCallback<OverlayModuleInfo>回调函数,当获取当前应用中指定的module的OverlayModuleInfo信息成功时,err返回null。否则回调函数返回具体错误对象。

错误码:

以下错误码的详细介绍。

错误码ID错误信息
401Parameter error. Possible causes: 1. Mandatory parameters are left unspecified; 2. Incorrect parameter types.
17700002The specified module name is not found.
17700032The specified bundle does not contain any overlay module.
17700033The specified module is not an overlay module.

示例:

import { overlay } from '@kit.AbilityKit';
import { BusinessError } from '@kit.BasicServicesKit';

let moduleName = "feature";

try {
  overlay.getOverlayModuleInfo(moduleName, (err, data) => {
    if (err) {
      console.log('getOverlayModuleInfo failed due to err code : ' + err.code + ' ' + 'message :' + err.message);
      return;
    }
    console.log('overlayModuleInfo is ' + JSON.stringify(data));
  });
} catch (err) {
  let code = (err as BusinessError).code;
  let message = (err as BusinessError).message;
  console.log('getOverlayModuleInfo failed due to err code : ' + code + ' ' + 'message :' + message);
}

overlay.getTargetOverlayModuleInfos

getTargetOverlayModuleInfos(targetModuleName: string): Promise<Array<OverlayModuleInfo>>

以异步方法获取指定的目标module的OverlayModuleInfo。使用promise异步回调,成功返回null,失败返回对应错误信息。

系统能力: SystemCapability.BundleManager.BundleFramework.Overlay

参数:

参数名类型必填说明
targetModuleNamestring指定当前应用中的目标module的名称。

返回值:

类型说明
Promise<Array<OverlayModuleInfo>>Promise对象,返回<Array<OverlayModuleInfo>>。

错误码:

以下错误码的详细介绍。

错误码ID错误信息
401Parameter error. Possible causes: 1. Mandatory parameters are left unspecified; 2. Incorrect parameter types.
17700002The specified module name is not found.
17700034The specified module is an overlay module.

示例:

import { overlay } from '@kit.AbilityKit';
import { BusinessError } from '@kit.BasicServicesKit';

let targetModuleName = "feature";

(async () => {
  try {
    let overlayModuleInfos = await overlay.getTargetOverlayModuleInfos(targetModuleName);
    console.log('overlayModuleInfos are ' + JSON.stringify(overlayModuleInfos));
  } catch (err) {
    let code = (err as BusinessError).code;
    let message = (err as BusinessError).message;
    console.log('getTargetOverlayModuleInfos failed due to err code : ' + code + ' ' + 'message :' + message);
  }
})();

overlay.getTargetOverlayModuleInfos

getTargetOverlayModuleInfos(targetModuleName: string, callback: AsyncCallback<Array<OverlayModuleInfo>>): void

以异步方法获取指定的目标module的OverlayModuleInfo。使用callback异步回调,成功返回null,失败返回对应错误信息。

系统能力: SystemCapability.BundleManager.BundleFramework.Overlay

参数:

参数名类型必填说明
targetModuleNamestring指定当前应用中的目标module的名称。
callbackAsyncCallback<Array<OverlayModuleInfo>>回调函数,当获取指定的目标module的OverlayModuleInfo成功时,err返回null。否则回调函数返回具体错误对象。

错误码:

以下错误码的详细介绍。

错误码ID错误信息
401Parameter error. Possible causes: 1. Mandatory parameters are left unspecified; 2. Incorrect parameter types.
17700002The specified module name is not found.
17700034The specified module is an overlay module.

示例:

import { overlay } from '@kit.AbilityKit';
import { BusinessError } from '@kit.BasicServicesKit';

let targetModuleName = "feature";

try {
  overlay.getTargetOverlayModuleInfos(targetModuleName, (err, data) => {
    if (err) {
      console.log('getTargetOverlayModuleInfos failed due to err code : ' + err.code + ' ' + 'message :' + err.message);
      return;
    }
    console.log('overlayModuleInfo is ' + JSON.stringify(data));
  });
} catch (err) {
  let code = (err as BusinessError).code;
  let message = (err as BusinessError).message;
  console.log('getTargetOverlayModuleInfos failed due to err code : ' + code + ' ' + 'message :' + message);
}

注意:本课程是基于最新API12版本,HarmonyOS5.x为学习开发环境的!!!!课程笔记附件在课程第一个视频上!鸿蒙应用开发系列课程重点一览!课程亮点- 全面性:覆盖从基础语法到高级架构设计的全方位知识。- 实践性:通过实际编码练习,加深对知识点的理解和应用。- 深入性:深入探讨类型系统,强化代码质量和开发效率。- 创新性:专注于ArkTS特性,引领鸿蒙开发新趋势。- 实用性:教授实用编程技巧,应对真实世界开发挑战。 学习阶段介绍【001-002】 开发学习准备 & 构建第一个ArkTS工程,了解项目结构,为后续开发奠定坚实基础。【003-005】引导你从设计并实现应用的第一张界面开始,逐步深入到多页面应用的构建。【006-013】 了解应用的打包发布流程,包括不同类型的包(如HAR)的创建、配置、编译与发布。【014-023】 通过实践,掌握如何在应用中高效整合和利用HAR、HSP包中的资源。【024-033】 深入解析应用配置文件(如app.json5、module.json5)的结构与配置项。【044-054】 掌握ArkUI的核心概念与开发范式,包括声明式编程、组件化思想。【069-089】 学习如何使用状态管理装饰器(如@State、@Prop)来控制组件状态。【222-238】 Canvas的基本用法至组件动画,探索动画的魅力,从基础属性动画到高级的粒子动画。【239-255】 事件分发机制至多层级手势事件,通过实战演练,提升应用的交互体验和用户满意度。【其他章节】覆盖资源匹配与overlay新特性、环境感知(如深浅色适配)、主题设置等。 
/* eslint-disable no-console */ /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ const fs = require('fs'); const path = require('path'); const webpack = require('webpack'); const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer'); const CopyPlugin = require('copy-webpack-plugin'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); const CssMinimizerPlugin = require('css-minimizer-webpack-plugin'); const SpeedMeasurePlugin = require('speed-measure-webpack-plugin'); const { WebpackManifestPlugin, getCompilerHooks, } = require('webpack-manifest-plugin'); const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin'); const parsedArgs = require('yargs').argv; const Visualizer = require('webpack-visualizer-plugin2'); const getProxyConfig = require('./webpack.proxy-config'); const packageConfig = require('./package'); // input dir const APP_DIR = path.resolve(__dirname, './'); // output dir const BUILD_DIR = path.resolve(__dirname, '../superset/static/assets'); const ROOT_DIR = path.resolve(__dirname, '..'); const { mode = 'development', devserverPort = 9000, measure = false, nameChunks = false, } = parsedArgs; const isDevMode = mode !== 'production'; const isDevServer = process.argv[1].includes('webpack-dev-server'); const ASSET_BASE_URL = process.env.ASSET_BASE_URL || ''; const output = { path: BUILD_DIR, publicPath: `${ASSET_BASE_URL}/static/assets/`, }; if (isDevMode) { output.filename = '[name].[contenthash:8].entry.js'; output.chunkFilename = '[name].[contenthash:8].chunk.js'; } else if (nameChunks) { output.filename = '[name].[chunkhash].entry.js'; output.chunkFilename = '[name].[chunkhash].chunk.js'; } else { output.filename = '[name].[chunkhash].entry.js'; output.chunkFilename = '[chunkhash].chunk.js'; } if (!isDevMode) { output.clean = true; } const plugins = [ new webpack.ProvidePlugin({ process: 'process/browser.js', ...(isDevMode ? { Buffer: ['buffer', 'Buffer'] } : {}), // Fix legacy-plugin-chart-paired-t-test broken Story }), // creates a manifest.json mapping of name to hashed output used in template files new WebpackManifestPlugin({ publicPath: output.publicPath, seed: { app: 'superset' }, // This enables us to include all relevant files for an entry generate: (seed, files, entrypoints) => { // Each entrypoint's chunk files in the format of // { // entry: { // css: [], // js: [] // } // } const entryFiles = {}; Object.entries(entrypoints).forEach(([entry, chunks]) => { entryFiles[entry] = { css: chunks .filter(x => x.endsWith('.css')) .map(x => `${output.publicPath}${x}`), js: chunks .filter(x => x.endsWith('.js') && x.match(/(?<!hot-update).js$/)) .map(x => `${output.publicPath}${x}`), }; }); return { ...seed, entrypoints: entryFiles, }; }, // Also write manifest.json to disk when running `npm run dev`. // This is required for Flask to work. writeToFileEmit: isDevMode && !isDevServer, }), // expose mode variable to other modules new webpack.DefinePlugin({ 'process.env.WEBPACK_MODE': JSON.stringify(mode), 'process.env.REDUX_DEFAULT_MIDDLEWARE': process.env.REDUX_DEFAULT_MIDDLEWARE, 'process.env.SCARF_ANALYTICS': JSON.stringify(process.env.SCARF_ANALYTICS), }), new CopyPlugin({ patterns: [ 'package.json', { from: 'src/assets/images', to: 'images' }, { from: 'src/assets/stylesheets', to: 'stylesheets' }, ], }), // static pages new HtmlWebpackPlugin({ template: './src/assets/staticPages/404.html', inject: true, chunks: [], filename: '404.html', }), new HtmlWebpackPlugin({ template: './src/assets/staticPages/500.html', inject: true, chunks: [], filename: '500.html', }), ]; if (!process.env.CI) { plugins.push(new webpack.ProgressPlugin()); } if (!isDevMode) { // text loading (webpack 4+) plugins.push( new MiniCssExtractPlugin({ filename: '[name].[chunkhash].entry.css', chunkFilename: '[name].[chunkhash].chunk.css', }), ); // Runs type checking on a separate process to speed up the build plugins.push(new ForkTsCheckerWebpackPlugin()); } const PREAMBLE = [path.join(APP_DIR, '/src/preamble.ts')]; if (isDevMode) { // A Superset webpage normally includes two JS bundles in dev, `theme.ts` and // the main entrypoint. Only the main entry should have the dev server client, // otherwise the websocket client will initialize twice, creating two sockets. // Ref: https://github.com/gaearon/react-hot-loader/issues/141 PREAMBLE.unshift( `webpack-dev-server/client?http://localhost:${devserverPort}`, ); } function addPreamble(entry) { return PREAMBLE.concat([path.join(APP_DIR, entry)]); } const babelLoader = { loader: 'babel-loader', options: { cacheDirectory: true, // disable gzip compression for cache files // faster when there are millions of small files cacheCompression: false, presets: [ [ '@babel/preset-react', { runtime: 'automatic', importSource: '@emotion/react', }, ], ], plugins: [ [ '@emotion/babel-plugin', { autoLabel: 'dev-only', labelFormat: '[local]', }, ], ], }, }; const config = { entry: { preamble: PREAMBLE, theme: path.join(APP_DIR, '/src/theme.ts'), menu: addPreamble('src/views/menu.tsx'), spa: addPreamble('/src/views/index.tsx'), embedded: addPreamble('/src/embedded/index.tsx'), }, cache: { type: 'filesystem', // Enable filesystem caching cacheDirectory: path.resolve(__dirname, '.temp_cache'), buildDependencies: { config: [__filename], }, }, output, stats: 'minimal', /* Silence warning for missing export in @data-ui's internal structure. This issue arises from an internal implementation detail of @data-ui. As it's non-critical, we suppress it to prevent unnecessary clutter in the build output. For more context, refer to: https://github.com/williaster/data-ui/issues/208#issuecomment-946966712 */ ignoreWarnings: [ { message: /export 'withTooltipPropTypes' \(imported as 'vxTooltipPropTypes'\) was not found/, }, { message: /Can't resolve.*superset_text/, }, ], performance: { assetFilter(assetFilename) { // don't throw size limit warning on geojson and font files return !/\.(map|geojson|woff2)$/.test(assetFilename); }, }, optimization: { sideEffects: true, splitChunks: { chunks: 'all', // increase minSize for devMode to 1000kb because of sourcemap minSize: isDevMode ? 1000000 : 20000, name: nameChunks, automaticNameDelimiter: '-', minChunks: 2, cacheGroups: { automaticNamePrefix: 'chunk', // basic stable dependencies vendors: { priority: 50, name: 'vendors', test: new RegExp( `/node_modules/(${[ 'abortcontroller-polyfill', 'react', 'react-dom', 'prop-types', 'react-prop-types', 'prop-types-extra', 'redux', 'react-redux', 'react-hot-loader', 'react-sortable-hoc', 'react-table', 'react-ace', '@hot-loader.*', 'webpack.*', '@?babel.*', 'lodash.*', 'antd', '@ant-design.*', '.*bootstrap', 'moment', 'jquery', 'core-js.*', '@emotion.*', 'd3', 'd3-(array|color|scale|interpolate|format|selection|collection|time|time-format)', ].join('|')})/`, ), }, // viz thumbnails are used in `addSlice` and `explore` page thumbnail: { name: 'thumbnail', test: /thumbnail(Large)?\.(png|jpg)/i, priority: 20, enforce: true, }, }, }, usedExports: 'global', minimizer: [new CssMinimizerPlugin(), '...'], }, resolve: { // resolve modules from `/superset_frontend/node_modules` and `/superset_frontend` modules: ['node_modules', APP_DIR], alias: { // TODO: remove aliases once React has been upgraded to v17 and // AntD version conflict has been resolved antd: path.resolve(path.join(APP_DIR, './node_modules/antd')), react: path.resolve(path.join(APP_DIR, './node_modules/react')), // TODO: remove Handlebars alias once Handlebars NPM package has been updated to // correctly support webpack import (https://github.com/handlebars-lang/handlebars.js/issues/953) handlebars: 'handlebars/dist/handlebars.js', /* Temporary workaround to prevent Webpack from resolving moment locale files, which are unnecessary for this project and causing build warnings. This prevents "Module not found" errors for moment locale files. */ 'moment/min/moment-with-locales': false, // Temporary workaround to allow Storybook 8 to work with existing React v16-compatible stories. // Remove below alias once React has been upgreade to v18. '@storybook/react-dom-shim': path.resolve( path.join( APP_DIR, './node_modules/@storybook/react-dom-shim/dist/react-16', ), ), }, extensions: ['.ts', '.tsx', '.js', '.jsx', '.yml'], fallback: { fs: false, vm: require.resolve('vm-browserify'), path: false, ...(isDevMode ? { buffer: require.resolve('buffer/') } : {}), // Fix legacy-plugin-chart-paired-t-test broken Story }, }, context: APP_DIR, // to automatically find tsconfig.json module: { rules: [ { test: /datatables\.net.*/, loader: 'imports-loader', options: { additionalCode: 'var define = false;', }, }, { test: /\.tsx?$/, exclude: [/\.test.tsx?$/], use: [ 'thread-loader', babelLoader, { loader: 'ts-loader', options: { // transpile only in happyPack mode // type checking is done via fork-ts-checker-webpack-plugin happyPackMode: true, transpileOnly: true, // must override compiler options here, even though we have set // the same options in `tsconfig.json`, because they may still // be overridden by `tsconfig.json` in node_modules subdirectories. compilerOptions: { esModuleInterop: false, importHelpers: false, module: 'esnext', target: 'esnext', }, }, }, ], }, { test: /\.jsx?$/, // include source code for plugins, but exclude node_modules and test files within them exclude: [/superset-ui.*\/node_modules\//, /\.test.jsx?$/], include: [ new RegExp(`${APP_DIR}/(src|.storybook|plugins|packages)`), ...['./src', './.storybook', './plugins', './packages'].map(p => path.resolve(__dirname, p), ), // redundant but required for windows /@encodable/, ], use: [babelLoader], }, { test: /ace-builds.*\/worker-.*$/, type: 'asset/resource', }, // react-hot-loader use "ProxyFacade", which is a wrapper for react Component // see https://github.com/gaearon/react-hot-loader/issues/1311 // TODO: refactor recurseReactClone { test: /\.js$/, include: /node_modules\/react-dom/, use: ['react-hot-loader/webpack'], }, { test: /\.css$/, include: [APP_DIR, /superset-ui.+\/src/], use: [ isDevMode ? 'style-loader' : MiniCssExtractPlugin.loader, { loader: 'css-loader', options: { sourceMap: true, }, }, ], }, { test: /\.less$/, include: APP_DIR, use: [ isDevMode ? 'style-loader' : MiniCssExtractPlugin.loader, { loader: 'css-loader', options: { sourceMap: true, }, }, { loader: 'less-loader', options: { sourceMap: true, lessOptions: { javascriptEnabled: true, modifyVars: { 'root-entry-name': 'default', }, }, }, }, ], }, /* for css linking images (and viz plugin thumbnails) */ { test: /\.png$/, issuer: { not: [/\/src\/assets\/staticPages\//], }, type: 'asset', generator: { filename: '[name].[contenthash:8][ext]', }, }, { test: /\.png$/, issuer: /\/src\/assets\/staticPages\//, type: 'asset', }, { test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, issuer: /\.([jt])sx?$/, use: [ { loader: '@svgr/webpack', options: { titleProp: true, ref: true, // this is the default value for the icon. Using other values // here will replace width and height in svg with 1em icon: false, }, }, ], }, { test: /\.(jpg|gif)$/, type: 'asset/resource', generator: { filename: '[name].[contenthash:8][ext]', }, }, /* for font-awesome */ { test: /\.(woff|woff2|eot|ttf|otf)$/i, type: 'asset/resource', }, { test: /\.ya?ml$/, include: ROOT_DIR, loader: 'js-yaml-loader', }, { test: /\.geojson$/, type: 'asset/resource', }, // { // test: /\.mdx?$/, // use: [ // { // loader: require.resolve('@storybook/mdx2-csf/loader'), // options: { // skipCsf: false, // mdxCompileOptions: { // remarkPlugins: [remarkGfm], // }, // }, // }, // ], // }, ], }, externals: { cheerio: 'window', 'react/lib/ExecutionEnvironment': true, 'react/lib/ReactContext': true, }, plugins, devtool: isDevMode ? 'eval-cheap-module-source-map' : false, }; // find all the symlinked plugins and use their source code for imports Object.entries(packageConfig.dependencies).forEach(([pkg, relativeDir]) => { const srcPath = path.join(APP_DIR, `./node_modules/${pkg}/src`); const dir = relativeDir.replace('file:', ''); if (/^@superset-ui/.test(pkg) && fs.existsSync(srcPath)) { console.log(`[Superset Plugin] Use symlink source for ${pkg} @ ${dir}`); config.resolve.alias[pkg] = path.resolve(APP_DIR, `${dir}/src`); } }); console.log(''); // pure cosmetic new line if (isDevMode) { let proxyConfig = getProxyConfig(); // Set up a plugin to handle manifest updates config.plugins = config.plugins || []; config.plugins.push({ apply: compiler => { const { afterEmit } = getCompilerHooks(compiler); afterEmit.tap('ManifestPlugin', manifest => { proxyConfig = getProxyConfig(manifest); }); }, }); config.devServer = { historyApiFallback: true, hot: true, port: devserverPort, proxy: [() => proxyConfig], client: { overlay: { errors: true, warnings: false, runtimeErrors: error => !/ResizeObserver/.test(error.message), }, logging: 'error', }, static: { directory: path.join(process.cwd(), '../static/assets'), }, }; } // To // e.g. npm run package-stats if (process.env.BUNDLE_ANALYZER) { config.plugins.push(new BundleAnalyzerPlugin({ analyzerMode: 'static' })); config.plugins.push( // this creates an HTML page with a sunburst diagram of dependencies. // you'll find it at superset/static/stats/statistics.html // note that the file is >100MB so it's in .gitignore new Visualizer({ filename: path.join('..', 'stats', 'statistics.html'), throwOnError: true, }), ); } // Speed measurement is disabled by default // Pass flag --measure=true to enable // e.g. npm run build -- --measure=true const smp = new SpeedMeasurePlugin({ disable: !measure, }); module.exports = smp.wrap(config); 分析webpack.config.js,确认一下路径应该如何修改
最新发布
10-24
你提供的 `webpack.config.js` 是 **Apache Superset 5.0** 的核心前端构建配置文件。我们来深入分析它,重点是: --- ## ✅ 核心目标:确认构建输出路径,并指导如何修改 --- ### 🔍 1. 构建输出目录(Build Output Path) 在你的 `webpack.config.js` 中,有如下代码: ```js // output dir const BUILD_DIR = path.resolve(__dirname, '../superset/static/assets'); ``` 然后这个变量被用于 Webpack 的 `output.path`: ```js const output = { path: BUILD_DIR, publicPath: `${ASSET_BASE_URL}/static/assets/`, }; ``` 👉 所以最终的 **构建产物输出绝对路径是**: ``` /superset-frontend/../superset/static/assets → 即:/your-project-root/superset/static/assets ``` > 📌 这意味着:**前端资源不会输出到 `./build` 或 `./dist`,而是直接输出到后端 Python Flask 应用的静态资源目录中!** --- ### 🧩 路径结构示意图 假设你的项目根目录结构如下: ``` superset-5.0.0/ ├── superset/ │ └── static/ │ └── assets/ ← 构建产物将放在这里 ├── superset-frontend/ │ ├── webpack.config.js │ ├── package.json │ └── src/ ``` ✅ 所以当你运行 `npm run build`,Webpack 会把所有 JS/CSS/图片等文件打包进: ``` superset/static/assets/ ``` 而不是 `superset-frontend/build/` 或 `superset-frontend/dist/` --- ## ✅ 回答你的问题:路径应该如何修改? ### ❓你想改什么?以下是几种常见需求场景: --- ### ✅ 场景一:你想让构建产物输出到 `superset-frontend/build`(便于 Docker 打包) #### ✔️ 修改方式: 修改 `BUILD_DIR` 变量: ```js // 原始: // const BUILD_DIR = path.resolve(__dirname, '../superset/static/assets'); // 改为: const BUILD_DIR = path.resolve(__dirname, 'build'); ``` 同时建议更新 `publicPath`(避免引用错): ```js const output = { path: BUILD_DIR, publicPath: isDevMode ? '/' : '/static/assets/', // 开发时相对路径,生产仍指向 Flask 静态服务路径 }; ``` > ⚠️ 注意:如果你只是临时构建用于复制到镜像,可以先输出到 `build/`,再通过 Dockerfile 复制过去。 --- ### ✅ 场景二:你想保留原逻辑(输出到 `../superset/static/assets`),但在 Docker 中找不到文件 #### ❗️原因: 你在 Docker 构建时只 `COPY . /app`,但没有确保 `superset/static/assets` 目录存在或可写! #### ✅ 解决方案: 在 Dockerfile 中提前创建目录并赋权: ```dockerfile RUN mkdir -p /app/superset/static/assets && \ chown -R node:node /app/superset/static ``` 并在构建前切换用户(如果用了非 root 用户): ```dockerfile USER node ``` 否则可能因权限问题导致构建成功但无输出! --- ### ✅ 场景三:你想输出到 `dist/` 并保留一份副本用于 CDN 分发 #### ✔️ 修改: ```js const BUILD_DIR = path.resolve(__dirname, 'dist'); // 新增 dist 输出 ``` 然后你可以用 `CopyPlugin` 同时复制到多个位置,或者在构建完成后手动拷贝: ```bash npm run build && cp -r ../superset/static/assets/* ./dist/ ``` --- ## ✅ 如何验证构建是否真的生成了文件? ### 方法一:查看关键输出文件是否存在 ```bash ls -la superset/static/assets/ # 应该看到: # index.html # static/js/*.js # static/css/*.css # manifest.json ``` ### 方法二:搜索入口 chunk ```bash find superset/static/assets -name "*.entry.js" -o -name "*.chunk.js" ``` 你应该能看到类似: ``` superset/static/assets/spa.abc123.entry.js superset/static/assets/vendors.def456.entry.js ``` --- ## ✅ Dockerfile 中应如何处理? ### 推荐做法:构建完后从 `superset/static/assets` 复制资源 ```dockerfile # 假设 WORKDIR 是 /app/superset-frontend RUN npm run build # 确保产物已生成 RUN ls -la ../superset/static/assets || echo "❌ 构建失败:无输出文件" # 后续步骤复制这些静态资源到 Nginx 或 Flask 容器 COPY ../superset/static/assets /app/superset/static/assets ``` --- ## ✅ 总结:你应该怎么做? | 目标 | 操作 | |------|------| | 让构建输出到 `build/` 目录(方便调试) | 修改 `BUILD_DIR = path.resolve(__dirname, 'build')` | | 保持默认行为(输出到 Flask 静态目录) | 不改,但确保 `superset/static/assets` 存在且可写 | | 在 Docker 中正确获取构建产物 | 使用 `../superset/static/assets` 路径,不要找 `build/` 或 `dist/` | | 验证构建是否成功 | 查看 `superset/static/assets` 是否有 `.js`, `.css`, `index.html` | --- ## 💡 补充建议:添加构建完成提示 你可以在 `package.json` 的 `"postbuild"` 脚本中加个提示: ```json "scripts": { "build": "webpack --mode production", "postbuild": "echo '🎉 构建完成!产物位于 ../superset/static/assets/'" } ``` 这样每次构建完都能看到提示。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值