Due to the project in more areas, in order to reuse, the main project using the same, but this will lead to a problem, because the distribution of equipment in different, and the local choice of font is different, resulting in some of the properties of the global can’t reuse, and must be configured two sets, so how to solve this problem?
Solutions
Since there is a very basic variable module in the project, called basic. SCSS, and this file is referenced in many SCSS files, now we need to distinguish the basic configuration of multiple locations, so just copy basic. SCSS. The name is basic-[country]. SCSS. The next step is to find a reference to basic. SCSS and then replace it with basic-[country].
So the problem here is how to find the file, find the reference association, and then replace it, the packaging class problem, of course WebPack is the first choice.
Webpack choice
At the beginning, the idea was to use Webpack to solve this problem, so should we use Loader or Plugin? This is where you need to think about the difference between loader and plugin.
Here’s a quote:
Effect of different
- Loader is a Loader. Webpack treats all files as modules, but Webpack natively only parses JS files, and if you want to package other files, you use loader. So what Loader does is give WebPack the ability to load and parse non-javascript files.
- A Plugin is a Plugin. Plugin can extend the functionality of WebPack to make it more flexible. A number of events are broadcast during the life cycle of a Webpack run, and the Plugin can listen for these events and change the output when appropriate through the API provided by Webpack.
Use different
- Loader is configured in module.rules, that is, it exists as a parsing rule for modules. The type is array, each item is an Object, which describes the type of file (test), what to use (loader), and the parameters (options) to use.
- Plugins are configured separately in plugins. Each item is an instance of plugin, and the parameters are passed in through the constructor.
Webpack can only parse JS files, if you want to package other files, you need to use loader, so here we choose to use loader.
Loader plug-in selection
String-replace-loader: String-replace-loader: String-replace-loader: String-replace-loader: String-replace-loader: String-replace-loader
module.exports = {
// ...
module: {
rules: [{test: /fileInWhichJQueryIsUndefined\.js$/,
loader: 'string-replace-loader'.options: {
search: '$'.replace: 'window.jQuery',}}]}}Copy the code
The usage is also relatively simple, using the re to query the file to be processed, and then using the string-replace-loader to process, the first parameter is search to find the string to be replaced, the second parameter is the string to be replaced.
Thinking of this, then directly modify to the following way can not be?
module.exports = {
// ...
module: {
rules: [{test: /[\s][\S]\.scss$/,
loader: 'string-replace-loader'.options: {
search: 'basic.scss'.replace: 'basic-[country].scss',}}]}}Copy the code
This would be fine if your own WebPack plug-in did, but since we’re using the NextJS framework, WebPack is automatically generated, so we’ll need to see how NextJS works.
Nextjs access
Nextjs officially provides the writing method of loader or plugin. The following is an official example
module.exports = {
webpack: (config, options) = > {
config.module.rules.push({
test: /\.mdx/,
use: [
options.defaultLoaders.babel,
{
loader: '@mdx-js/loader'.options: pluginOptions.options,
},
],
})
return config
},
}
Copy the code
According to the example on the official website, we modify it to the following SCSS replacement method
module.exports = {
webpack: (config, options) = > {
config.module.rules.push({
test: /[\s][\S]\.scss$/,
use: [
{
loader: 'string-replace-loader'.options: {
search: 'basic.scss'.replace: 'basic-[country].scss',}},],})return config
},
}
Copy the code
After the modification is complete, run it, you will find that there will be an error in the parse, and either that means that CSS can not parse, or less can not parse, or js problems and so on.
After looking at the reason, we found that we cannot push it directly, but should add the loader to the existing rules. Then we will solve this problem next.
First we write a method to add the loader rule:
const addUserLoader = function(arr){
if(! (arrinstanceof Array)){arr = [arr]; } arr.push( {loader: 'string-replace-loader'.options: {
search: 'basic.scss'.replace: 'basic-[country].scss',}});return arr;
};
Copy the code
Next, we need to iterate through all of the current rules. Let’s add this to all of the rules to see if there are any problems.
webpackConfig.module.rules.forEach(
rule= > {
if(rule.oneOf instanceof Array) {let cssRules = [];
rule.oneOf.forEach(cssRule= > {
if(cssRule.use instanceof Array){
addUserLoader(cssRule.use);
} else if(cssRule.use){
addUserLoader([cssRule.use])
}
cssRules.push(cssRule);
});
rule.oneOf = cssRules;
}
newRules.push(rule);
});
webpackConfig.module.rules = newRules;
return webpackConfig;
Copy the code
The code logic is relatively simple, traversing rules, rules oneOf non-array will not be processed, array will be traversed to determine whether the use under rule is array, if not array, it means a single loader, so first convert to array, and then add the loader. If it’s an array, you just push it in.
So let’s run it again, and we’re done, ok so can we optimize it further? Of course I can
To optimize the direction
We don’t need to add the loader to every file. We just need to re files that match the SCSS end and.global.scss end. There are rules that end with.scss and.global.scss and rules that don’t include.global but end with.scss, so there are three rules.
/\.(css|less|scss|sass)$/ /(? <! \.global)\.(scss|sass)$/ /\.global\.(scss|sass)$/ [ /\.global\.css$/, /\.global\.less$/, /\.global\.(scss|sass)$/ ]Copy the code
To accommodate these rules, let’s write a method that just makes some judgments, as follows.
const checkNeedReplace = function(ruleTest){
if(! ruleTest){return false;
}
if(! (ruleTestinstanceof Array)){
ruleTest = [ruleTest];
}
return ruleTest.some(rule= > {
if(rule.test('.global.scss') || rule.test('test.scss')) {return true; }}); }Copy the code
Since some rules are arrays and some are not, they are uniformly converted into arrays, and then the array loop is processed to determine whether there is one item that meets the conditions. Then,.global. SCSS and test. SCSS are used to match.
With the above method, all we need to do is add a layer of filtering. The code is as follows.
webpackConfig.module.rules.forEach(
rule= > {
if(rule.oneOf instanceof Array) {let cssRules = [];
rule.oneOf.forEach(cssRule= > {
if(! checkNeedReplace(cssRule.test)){// Filter rules that are not needed
cssRules.push(cssRule);
return;
}
cssRule.use = addUserLoader(cssRule.use);
cssRules.push(cssRule);
});
rule.oneOf = cssRules;
}
newRules.push(rule);
})
Copy the code
That’s it, so finally let’s take a look at the entire code
webpack: (webpackConfig, { dev, isServer, buildId, defaultLoaders }) = > {
let newRules = [];
const addUserLoader = function(arr){
if(! (arrinstanceof Array)){arr = [arr]; } arr.push( {loader: 'string-replace-loader'.options: {
search: 'basic.scss'.replace: 'basic-[country].scss'}});return arr;
};
const checkNeedReplace = function(ruleTest){
if(! ruleTest){return false;
}
if(! (ruleTestinstanceof Array)){
ruleTest = [ruleTest];
}
return ruleTest.some(rule= > {
if(rule.test('.global.scss') || rule.test('test.scss')) {return true; }}); } webpackConfig.module.rules.forEach(rule= > {
if(rule.oneOf instanceof Array) {let cssRules = [];
rule.oneOf.forEach(cssRule= > {
if(! checkNeedReplace(cssRule.test)){// Filter rules that are not needed
cssRules.push(cssRule);
return;
}
cssRule.use = addUserLoader(cssRule.use);
cssRules.push(cssRule);
});
rule.oneOf = cssRules;
}
newRules.push(rule);
});
webpackConfig.module.rules = newRules;
return webpackConfig;
}
Copy the code
If you have any other questions, please join us.
Refer to the article: segmentfault.com/a/119000003…