我需要自定义Webpack在我的应用程序中处理导入的方式。
我的某些服务具有模拟实现。 在测试模式下,如果服务旁边存在带有“ .mock”后缀的文件,则我想导入模拟文件而不是真实服务,否则导入服务本身。
请注意,我需要不同的输出文件(“ main.js”和“ test.js”)。因此,我需要确保test.js不包括真实的服务实现(防止执行不够,根本不应该导入源代码)。
Services文件夹包含以下文件:
service-1.js
service-1.mock.js
service-2.js
index.js
services / index.js:
import service1 from ‘./service-1’
import service2 from ‘./service-2’
export {service1, service2}
请告知我如何配置我的Webpack。
答案 0 :(得分:3)
根据评论,我可以使用自定义加载程序建议这些解决方法:
沿着主文件为您的每个组件创建一个.mobile
和.desktop
(例如:component.js
,component.mobile.js
,component.desktop.js
)并使用此自定义加载器:
const targets = {
desktop: 'desktop',
mobile: 'mobile'
};
const source = `
import Home from './components/home';
import About from './components/about';
import Header from './shared/Header';
import Footer from './shared/about';
import Categories from './category/categories';
// rest of code
`;
const manipulated = manipulateSource(source, targets.mobile, ['components', 'shared']);
console.log(manipulated);
function manipulateSource(src, target = targets.desktop, pathMatches = []) {
const paths = pathMatches.length ? `(${pathMatches.join('|')})` : '';
const pattern = new RegExp(`(?<left>.*import.*${paths}.*\\\/)(?<name>[\\w\\-_]*)(?<rest>.*\\n)`, 'g');
const manipulated = src.replace(pattern, (...args) => {
const [{
left,
name,
rest
}] = args.slice(-1);
return `${left}${name}.${target}${rest}`;
});
return manipulated;
}
对于这些文件,.mobile
和.desktop
的实现方式不同,请使用相同的名称和有意义的扩展名创建第三个文件(如果要在主文件中放置可共享的代码,则请创建第四个文件)(例如: component.platformAdaptive.js
),可以使用regular expresion
(或其他任何操纵方式)进行处理。在这种方法中,如果您使用strongTypes
(例如:Typescript
),则可能需要将基本实现放在最后一个文件中:
const targets = {
desktop: 'desktop',
mobile: 'mobile'
};
const source = `
import Home from './components/home';
import About from './components/about';
import Header from './shared/Header.platformAdaptive';
import Footer from './shared/about.platformAdaptive';
import Categories from './category/categories.platformAdaptive';
// rest of code
`;
const manipulatedMob = manipulateSource(source, 'platformAdaptive', targets.mobile);
const manipulatedDesk = manipulateSource(source, 'platformAdaptive');
console.log(manipulatedMob);
console.log(manipulatedDesk);
function manipulateSource(src, replace, target = targets.desktop) {
const pattern = new RegExp(`(?<left>.*\\\/)(?<name>[\\w\\-_]*\.)${replace}(?<rest>.*\\n)`, 'g');
const manipulated = src.replace(pattern, (...args) => {
const [{
left,
name,
rest
}] = args.slice(-1);
return `${left}${name}${target}${rest}`;
});
return manipulated;
}
以上两种方法都在导入方面有一些限制,例如您不能使用Barrel files (index.js)
,因为它们假定导入的最后一块是组件文件。
在这种情况下,您可以添加多个带有barrel
的文件夹来处理这些导入。例如,在第二种方法中,您将需要这样的结构:
|-- components.platformAdaptive
|-- index.js
|-- components.mobile
|-- index.js
|-- components.desktop
|-- index.js
或者您可以使用/
代替.
来创建嵌套结构(例如:components/platformAdaptive
):
|-- components
|-- [+] platformAdaptive
|-- [+] mobile
|-- [+] desktop
处理这种情况的另一种方法是使用具有不同名称的不同类。例如,一个List
组件在移动设备和台式机上的实现方式不同,那么将有三个组件,例如ListPlatformAdaptive
,ListMobile
,ListDesktop
-ListPlatformAdaptive可能具有基本的实现-以及导出组件的组件文件夹中的barrel
:
import * as ListPlatformAdaptive from './list.platformAdaptive';
import * as ListMobile from './list.mobile';
import * as ListDesktop from './list.desktop';
export {
ListPlatformAdaptive,
ListMobile,
ListDesktop
}
结构如下:
|-- components
|-- list.platformAdaptive.js
|-- list.mobile.js
|-- list.desktop.js
|-- index.js
然后进行如下操作:
const targets = {
desktop: 'Desktop',
mobile: 'Mobile'
};
const source = `
import Home from './components/home';
import About from './components/about';
import HeaderPlatformAdaptive as Header from './shared/Header';
import FooterPlatformAdaptive as Footer from './shared/about';
import CategoriesPlatformAdaptive as Categories from './category/categories';
// rest of code
`;
const replace = 'PlatformAdaptive';
const manipulatedMob = manipulateSource(source, replace, targets.mobile);
const manipulatedDesk = manipulateSource(source, replace);
console.log(manipulatedMob);
console.log(manipulatedDesk);
function manipulateSource(src, replace, target = targets.desktop) {
const pattern = new RegExp(replace, 'g');
const manipulated = src.replace(pattern, target);
return manipulated;
}
在此方法中,您应该注意要排除barrel
文件,该方法的缺点是所有组件均已导入,因此导入成本不可接受。
我可以想到的另一种方法是添加一些注释作为注释,并再次对该行中的存在进行反应:
const targets = {
desktop: 'Desktop',
mobile: 'Mobile'
};
const source = `
import Home from './components/home';
import About from './components/about';
import Header from './shared/Header'; /* @adaptive */
import Footer from './shared/about'; /* @adaptive: Desktop */
import Categories from './category/categories'; /* @adaptive: Mobile */
// rest of code
`;
const manipulatedMob = manipulateSource(source, targets.mobile);
const manipulatedDesk = manipulateSource(source);
console.log(manipulatedMob);
console.log(manipulatedDesk);
function manipulateSource(src, targetDevice = targets.desktop) {
const pattern = /(?<left>.*import\s+)(?<name>\w+)(?<rest1>.*)\@adaptive(\:\s*(?<target>\w+))?(?<rest2>.*)/g
const manipulated = src.replace(pattern, (matched, ...args) => {
let [{
left,
name,
rest1,
target,
rest2
}] = args.slice(-1);
target = target || targetDevice;
return target == targetDevice ?
`${left}${name}${target}$ as ${name}${rest1}${rest2}` :
matched;
});
return manipulated;
}
在此方法中,如方法2那样,导入的组件名称与原始名称不同,但映射到原始名称根本不好,但是我最喜欢它,因为如果在barrel
文件中使用它,则可能更改导入的文件地址。另一个有趣的部分是传递关于target device
的目标文件地址并进行解析。
您可以看到我所有的答案都是关于句柄源的,而不检查文件是否存在,并假设开发人员对此有把握。顺便说一句,您可以搜索以查看是否存在找到文件绝对路径的信息,然后检查目标替代项的可用性。