点击 webpack resolve/loader 添加回退

Wha*_*ber 1 javascript reactjs webpack

我有一个反应应用程序(学生门户)。

我的文件夹结构如下:

\- build
  - webpack.config.js
\- src
  - App.js
  - components\
    - Login\
      - Login.scss
      - Login.js
    - Dashboard\
    - Register\
  - redux\
  - helpers\ 
Run Code Online (Sandbox Code Playgroud)

我的 webpack 非常标准。

 var path = require('path');
 var webpack = require('webpack');
 module.exports = {
     entry: './src/app.js',
     output: {
         path: path.resolve(__dirname, 'build'),
         filename: 'app.bundle.js'
     },
     module: {
         loaders: [
             {
                 test: /\.js$/,
                 loader: 'babel-loader',
                 query: {
                     presets: ['es2015']
                 }
             }
         ]
     },
     stats: {
         colors: true
     },
     devtool: 'source-map'
 };
Run Code Online (Sandbox Code Playgroud)

我想建立在这个应用程序之上。所以我将此应用发布到 npm(本地)。我的文件夹结构现在是:

\- node_modules
  - student-portal\
\ - src\
\ - build\
Run Code Online (Sandbox Code Playgroud)

我想要的是替换/扩展现有组件的能力,而不必复制现有文件。

所以,如果我创建一个新文件,我希望 webpack 使用它。如果此文件不存在,则应查看node_modules/student-portal\src.

所以 webpack 应该先查看src文件,然后查看node_modules\student-portal\src.

我尝试创建一个解析器函数:

var MyResolver = {
  apply: function(resolver) {
    resolver.plugin('module', function(request, callback) {
      console.log(request.request); // this only consoles node_modules not .js files
    });
  }
};


Run Code Online (Sandbox Code Playgroud)

但这不会控制 .js 文件。我还尝试了许多其他插件,如 NormalModuleReplacementPlugin、ResolverPlugin 等,但都没有成功。

如果文件不存在,任何关于我如何利用 webpack 解析和替换路径的指针将不胜感激。理想情况下,我想扩展现有组件并让 webpack 将旧组件的路径替换为新组件的路径。

Der*_*yen 8

我认为您可以使用这些resolve.modules选项,因为它符合您的描述。但是,由于问题是关于解析插件的,我也会尝试编写一个。

解析模块

这是一个lodash作为目标模块的示例。我们可以建立这样的结构:

node_modules
  `--lodash
src
  |--lodash
  |    `-- zip.js
  `-index.js
Run Code Online (Sandbox Code Playgroud)

zip.js 可以是这样的 export default () => 'local zip'

在我们的webpack.config.js,做

module.exports = {
  ...
  resolve: {
    modules: [path.resolve(__dirname, 'src'), 'node_modules'],
  },
  ...
}
Run Code Online (Sandbox Code Playgroud)

在我们的 index.js 中,让我们导入zipisObject

// src/index.js
import zip from 'lodash/zip';
import isObject from 'lodash/isObject';

console.log(zip()); // 'local zip'
console.log(isObject({ a: 100 }));  // 'true'
Run Code Online (Sandbox Code Playgroud)

这基本上是您想要的,但不是编写自定义组件的相对路径,而是编写模块路径。

解决插件

但既然问题问的是插件,让我们试一试吧!我之前评论过你的 q,但后来发现插件系统在 webpack 4 中发生了变化。我在 node v10 上,所以一些语法在旧版本中可能不起作用。

目录结构:

node_modules
  `--lodash
src
  |--components
  |    `-- zip.js
  `-index.js
Run Code Online (Sandbox Code Playgroud)

首先,快速浏览一个解析插件。Webpack 允许我们利用解析管道中的多个钩子(您可以在此处查看完整列表)。我们对resolve,parsedResolve和尤其感兴趣module。我们的计划是:

1. Tap into the `resolve` hook  
2. Is the resolve request points to our 'components' folder?  
   - If not, go to **step 3**.  
   - If yes, is there something there it can use?  
       - If not, point it to `lodash` module instead.  
       - If yes, go to **step 3**.  
3. Continue to the next hook in the pipeline (`parsedResolve`).
Run Code Online (Sandbox Code Playgroud)

当我们点击一​​个钩子时,我们会得到一个非常有用的request对象和这些道具:

  • context: 包含发行人(到 的绝对路径index.js
  • path:发行人的目录(到 的绝对路径src
  • request: 请求字符串 ('./components/zip')

有了这个,我们可以编写我们的插件:

const path = require('path');
const fs = require('fs');

class CustomResolverPlugin {
  constructor ({ dir, moduleName }) {
    this.dir = dir;  // absolute path to our 'components' folder
    this.moduleName = moduleName; // name of the module, 'lodash' in this case
  }
  apply(resolver) {
    resolver.getHook('resolve').tapAsync('CustomResolverPlugin', (request, resolveContext, callback) => {

      // 1. check if the request is point to our component folder
      // resolver.join is same as path.join, but memoized
      const { dir } = path.parse(resolver.join(request.path, request.request));
      const match = dir === this.dir;

      if (match) {

        // 2. get the name of the file being requested & check if it exists.
        // in import zip from `./src/components/zip`, 'zip' is the name.
        const { name } = path.parse(request.request);
        const pathExist = fs.existsSync(path.join(this.dir, `${name}`));
        if (!pathExist) {

          // create a new request object.
          // we'll swap the request to something like 'lodash/zip'
          const _request = {
            ...request,
            request: `${this.moduleName}/${name}`
          }
          // swap the target hook to 'module' to resolve it as a module.
          const _target = resolver.ensureHook('module');
          return resolver.doResolve(_target, _request, null, resolveContext, callback);
        }
      }

      // 3. otherwise continue to the next hook
      const target = resolver.ensureHook('parsedResolve');
      return resolver.doResolve(target, request, null, resolveContext, callback);
    });
  }
}
Run Code Online (Sandbox Code Playgroud)

用途 webpack.config.js

module.exports = {
  ...
  resolve: {
    plugins: [
      new CustomResolverPlugin({
        dir: path.resolve(__dirname, './src/components'),
        moduleName: 'lodash',
      }),
    ],
  },
  ...
}
Run Code Online (Sandbox Code Playgroud)

在你的 index.js 中:

import zip from './components/zip';
import isObject from './components/isObject';

console.log(zip(['a', 'b'], [1, 2])); // 'local zip'
console.log(isObject({ a: 100 }));    // true
Run Code Online (Sandbox Code Playgroud)

希望能帮助到你!