【问题标题】:NPM + Zurb Foundation + WebPack: Cannot resolve module 'foundation'NPM + Zurb Foundation + WebPack:无法解析模块“基础”
【发布时间】:2019-05-05 03:13:42
【问题描述】:

我正在使用带有 WebPack 和 NPM 的 Zurb Foundation,没有 Bower。

我遇到的问题和下面的一样:

https://github.com/zurb/foundation-sites/issues/7386

本质上,当通过 NPM 安装基础站点时,会引用未找到的模块“基础”。错误:

Module not found: Error: Cannot resolve module 'foundation' in c:\Users\Matt\Documents\Projects\test\node_modules\foundation-sites\dist
 @ ./~/foundation-sites/dist/foundation.js 

这是 package.json:

{
  "name": "test",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "dev": "webpack-dev-server"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "foundation-sites": "6.0.5",
    "webpack": "~1.12.6",
    "webpack-dev-server": "~1.2",
    "jquery": "2.1.1"
  }
}

这里是 webpack.config.js:

var path = require("path");
var webpack = require("webpack");
module.exports = {
    entry: {
      main: "./app/js/main.js"
    },
    output: {
        path: __dirname,
        filename: "bundle.js"
    },
    module: {
        loaders: [
            { test: /\.css$/, loader: "style!css" },
            {
                test: /\.scss$/,
                loaders: ["style", "css", "sass"]
              },
              { test: /\.vue$/, loader: 'vue' }
        ],
        resolve: {
            modulesDirectories: ['node_modules']
        }
    },
     sassLoader: {
            includePaths: [path.resolve(__dirname, "./node_modules/foundation-sites/scss/")]
          },
    devServer: {
        proxy: {
            '/api/*': {
                target: 'http://localhost:4567',
                secure: false
            }
        }
    }
};

我可以通过 bower 包含基础来解决此问题,但我想消除 bower 并仅使用 NPM。

【问题讨论】:

标签: npm zurb-foundation webpack


【解决方案1】:

我有同样的问题,但我不想要两个 .js 文件(供应商和应用程序)!

对我来说,一切都需要在一个文件中,所以,我做了这个:

webpack.conf.js中,使用externals(或许不用externals还有其他方式,但对我来说,这就足够了):

externals: {
    jQuery: 'jQuery',
    foundation: 'Foundation'
},

在您的源文件夹中创建一个文件(任何名称,例如 /libs/foundation.js):

// jQuery
var $ = require('jquery');
global.jQuery = $;

// if you want all features of foundation
require('./node_modules_folder/foundation-sites/dist/foundation.js');

// if you want only some features
// require('./node_modules/what-input/what-input');
// require('./node_modules/foundation-sites/js/foundation.core');
// require('./node_modules/foundation-sites/js/....');

export default Foundation;

现在,您可以使用以下语法在任何 js 中使用 Foundation:

import Foundation from './libs/foundation';

【讨论】:

  • 是的,确实如此。我试图将它与 react-boilerplate 集成,这似乎已经做到了。现在到网站上:)
【解决方案2】:

我将根据 Mason Houtz 和 pharmakon 的出色答案发布我的完整解决方法,以防它对某人有所帮助,因为我在此过程中学习 Webpack 时遇到了一些困难。

在我的例子中,我有一个额外的复杂性,因为其他 jQuery 插件不知何故只能在它们自己的模块内工作,而在它们的属性之外是 undefined。显然他们使用的是本地的、重复的 jQuery 对象。

无论如何,这是你需要做的:

  1. 安装脚本加载器:npm install --save-dev script-loader

  2. 在 Webpack 的配置中:

    • 添加新条目,我们称之为vendor。这将在 Webpack 运行时编译一个新的vendor.js

      entry: {
          ...,
          "vendor": [
              "!!script!jquery/dist/jquery.min.js",
              "!!script!foundation-sites/dist/foundation.min.js"
          ]
      },
      
    • jquery 添加到外部。这可以确保您的主 JS 中对 jquery 的任何引用都将替换为对全局 jQuery 变量的引用,该变量由上面的 vendor.js 提供。

      entry : {
          // ...
      },
      externals: {
          jquery: "jQuery"
      }
      
  3. 确保每个使用 jQuery 的模块都导入它:

    var $ = require('jquery');
    

上面的externals 配置将用对全局jQuery 变量的引用替换它,而不是“正确”重新导入重复的jQuery。 或者,您可以使用 ProvidePlugin,它会在模块中遇到 jQuery 时自动执行上述操作,从而为您节省一些击键。如果你想这样,在 Webpack 的配置中添加以下内容:

plugins: [
    // ...,
    new webpack.ProvidePlugin({
      '$': 'jquery', 
      jQuery: 'jquery'
    })
]
  1. 将新的vendor.js 包含到您的页面中,显然在主 JS 之前。

很可能有一种更简单或更优雅的方法来做到这一点,但我只是想要一个快速、有效的解决方案,直到 Foundation 希望尽快解决这个问题。

【讨论】:

    【解决方案3】:

    我能够使用 webpack 做到这一点,基本上是在将它作为一个模块加载的过程中进行最终运行。

    不过,这基本上是一个 hack,Foundation 确实需要更新其 JS 以作为 commonJS 模块加载。

    问题源于 Foundation 的 JS 在源代码的嵌套 IFFE 中以不稳定的方式引用依赖项。有时 jQuery 是本地 jQuery 参数,有时是 $,有时是 window.jQuery。这真的是一个混合包。所有不同机制的组合意味着除了以非模块化方式加载外,没有单一的匀场解决方案。

    老实说,这几乎是业余时间,但截至撰写本文时,他们刚刚发布了这个东西,所以希望它会很快得到修复。

    Anyhoo... 黑客:

    我制作了一个单独的供应商捆绑包并在那里加载所有业余时间的 3rd 方 npm 库,因为我厌倦了与包装交付不佳的开源 npm 包代码所需的所有各种填充机制作斗争。

    我的供应商包是我在 webpack 注册的一个单独入口点,它包含所有不能作为模块运行的库。

    require('!!script!jquery/dist/jquery.min.js');
    
    require('!!script!uglify!foundation-sites/js/foundation.core.js');
    require('!!script!uglify!foundation-sites/js/foundation.accordion.js');
    require('!!script!uglify!foundation-sites/js/foundation.util.keyboard.js');
    require('!!script!uglify!foundation-sites/js/foundation.util.motion.js');
    // etc.
    

    确保您已安装脚本加载器

    npm install script-loader -D
    

    !!意思是“忽略我已经在配置中定义的所有其他规则”。使用 script-loader 告诉 webpack 在窗口范围内加载和执行文件,这与您刚刚在页面上包含脚本标记基本相同。 (但显然不是。)

    您可以变得更有趣并编写自己的解析规则,以便它只检查基础库中的内容,但我没有打扰,因为我希望像 Foundation 这样普及的库在不久的将来能够一起行动,所以我可以删除这个hack。

    另外...在您的主 webpack 配置中,您将希望引用 jQuery 和以这种方式加载的任何其他全局窗口变量作为外部变量。

    var webpackConfig = {
        entry: { // blah },
        output: { // blah },
        loaders: [ // blah ],
        externals: {
            jquery: "jQuery"
        }
    };
    

    【讨论】:

    • 你如何为基础脚本提供 jQuery 来使用这样的 require shimming?
    • 啊,抱歉,我只发布了我的供应商捆绑包的一部分。在我的供应商捆绑包中,我这样做的方式基本相同。将更新答案。
    • 遗憾的是,我在浏览器上收到了“未捕获的 TypeError: $(...).foundation is not a function” :( 。如果是一个完整的例子,那就太好了……
    • 你在哪里放 $(document).foundation(); ?创建单独的供应商条目后,我遇到了与 slothy 相同的错误。
    • @Mihail 我认为对于所有方法,您都需要使用import $ as jquery$ = jquery = require('jquery')
    【解决方案4】:

    只需使用脚本加载器 (npm i script-loader) 并在导入前加上 script!。 然后它将在全局范围内进行评估。

    要从 Foundation 加载所有 js 文件,请使用此

    import 'script!jquery'
    import 'script!what-input'
    import 'script!foundation-sites'
    

    就像我在 entry point 中所做的那样

    您可以查看我的样板项目进行尝试:https://github.com/timaschew/r3-foundation-boilerplate

    【讨论】:

    • 谢谢,必须查看您的模板一段时间才能使样式正常工作
    • 一旦我根据 Roberto Correia 解决方案将 externals: { foundation: 'Foundation' }, 添加到 webpack.conf 中,这适用于粘性导航和 vue.js
    【解决方案5】:

    虽然@roberto 的回答看起来不错,但我想提供一个更简单的解决方案(它不需要任何额外的供应商/基础文件)。

    在你的 webpack 配置中使用这个:

    // this will force the export of the jQuery 'foundation' function, 
    // which we'll use later on
    loaders: [
      {
        test: /(foundation\.core)/,
        loader: 'exports?foundation=jQuery.fn.foundation'
      }
    ],
    
    // this makes sure that every module can resolve define(['foundation']) calls
    resolve: {
      extensions: ['', '.js'],
      alias: {
        foundation: 'foundation-sites/js/foundation.core'
      }
    },
    
    // this makes sure 'jQuery' is available to any jQuery plugin you might want 
    // to load (including Foundation files) regardless of how they are written
    plugins: [
      new webpack.ProvidePlugin({
        $: 'jquery',
        jQuery: 'jquery',
        'window.jQuery': 'jquery'
      })
    ]
    

    在你的 index.js 中:

    // thanks to the ProvidePlugin we don't need to
    // > import $ from 'jquery';
    
    // required core foundation files
    import { foundation } from 'foundation-sites/js/foundation.core';
    import 'foundation-sites/js/foundation.util.mediaQuery';
    
    /* import here any additional module */
    
    // we need to attach the function we force-exported in the config
    // above to the jQuery object in use in this file
    $.fn.foundation = foundation;
    
    // ready to go
    $(document).ready(function() {
      $(document).foundation();
      …
    });
    

    注意 #1(谢谢@mtyson)
    您需要使用导出加载器:$ npm install --save exports-loader$ npm install --save-dev exports-loader

    注意 #2
    由于 jQuery 在单个模块内不是全局的(或者由于某些我无法理解的其他原因),因此 Foundation JS 组件依赖 data- 属性可能会出现问题。如果是这种情况,您始终可以使用 Foundation 文档中记录的纯 javascript 方式。

    【讨论】:

    • 我必须将它包含在我的 package.json 中:“exports-loader”:“^0.6.3”,
    • 另外,为了使样式发挥作用,必须查看基础 - 一切都包括如下所示:stackoverflow.com/a/35373347/467240
    • 当我尝试将它与foundation.dropdown 一起使用时,我收到错误Cannot read property 'register' of undefined。有什么想法吗?
    【解决方案6】:

    这是我如何使用 hack。我将基础和 jquery 放在一个单独的入口点,称为 vendor 并使用脚本加载器加载它们。唯一相关的位在供应商入口点中。

    var path = require('path');
    var webpack = require('webpack');
    var hotMiddlewareScript = 'webpack-hot-middleware/client?path=/__webpack_hmr&timeout=20000&reload=true';
    var autoprefixer = require('autoprefixer');
    
    module.exports = {
      name: 'main',
    
      devtool: 'eval',
    
      entry: {
        client: [
          path.resolve(__dirname, 'client', 'index.js'),
          hotMiddlewareScript
        ],
        vendor: [
          'font-awesome/css/font-awesome.css',
          'foundation-sites/dist/foundation-flex.css',
          '!!script!jquery/dist/jquery.min.js',
          '!!script!foundation-sites/dist/foundation.min.js',
        ]
      },
    
      output: {
        path: path.resolve(__dirname, 'dist'),
        filename: '[name].js',
        publicPath: '/dist/'
      },
    
      resolve: {
        modulesDirectories: ['node_modules', './client'],
        extensions: ['', '.js', '.jsx']
      },
    
      plugins: [
        new webpack.optimize.OccurenceOrderPlugin(),
        new webpack.HotModuleReplacementPlugin(),
        new webpack.NoErrorsPlugin(),
        new webpack.optimize.CommonsChunkPlugin('vendor', 'vendor.bundle.js'),
        new webpack.ProvidePlugin({'$': 'jquery', jQuery: 'jquery'})
      ],
    
      module: {
        loaders: [
          { test: /\.(js|jsx)$/, loaders: ['react-hot', 'babel-loader'], exclude: /node_modules/, include: path.resolve(__dirname, 'client') },
          { test: /\.scss$/, loader: "style!css!autoprefixer-loader?browsers=last 2 versions!sass" },
          { test: /\.css$/, loader: "style!css" },
          // { test: /\.(png|jpg|jpeg|gif)$/, loader: 'file-loader?name=images/[name].[ext]' },
          { test: /\.(webm|mp4|mov|m4v|ogg)$/, loader: 'file-loader?name=videos/[name].[ext]' },
          { test: /\.(eot|svg|ttf|woff|woff2)/, loader: 'file-loader?name=fonts/[name].[ext]' }
        ]
      }
    };
    

    【讨论】:

      【解决方案7】:

      如果你可以告诉它忽略define 测试下面的麻烦代码,它对 webpack 工作正常:

        if (typeof module !== 'undefined' && typeof module.exports !== 'undefined')
           module.exports = Reveal;
        if (typeof define === 'function')
           define(['foundation'], function() {
           return Reveal;
        });
      

      最好的方法是使用imports-loader 并将define 设置为false。

      require('foundation-sites/js/foundation.core.js');
      require('foundation-sites/js/foundation.util.keyboard.js');
      require('foundation-sites/js/foundation.util.box.js');
      require('foundation-sites/js/foundation.util.triggers.js');
      require('foundation-sites/js/foundation.util.mediaQuery.js');
      require('foundation-sites/js/foundation.util.motion.js');
      require('imports?define=>false!foundation-sites/js/foundation.reveal.js');
      

      【讨论】:

        【解决方案8】:

        如果你仔细查看foundation-sites 6.2.0模块,你会发现path的变化如下

        1. foundation-sites/dist/css/foundation.min.css
        2. foundation-sites/dist/js/foundation.min.js

        所以基本上你必须改变 webpack 配置文件条目如下

        module.exports =  {
                 entry: ['script!jquery/dist/jquery.min.js',
                'script!foundation-sites/dist/js/foundation.min.js',
                 './app/app.jsx'
               ]
        }
        

        样式的入口应该是这样的

        require('style!css!foundation-sites/dist/css/foundation.min.css');

        【讨论】:

          【解决方案9】:

          对于我自己,我使用了这个解决方案:

          我使用的是 Laravel 框架,所以首先我将.webpackConfig (...) 方法添加到webpack.mix.js 文件中:

          mix.js('resources/assets/js/app.js', 'public/js')
          .sass('resources/assets/sass/app.scss', 'public/css')
          
          // By default, files from the directory "/node_modules" are not processed by the Babel loader,
          // so in the Webpack configuration,
          // an exception was added for loading from the directory "/node_modules/foundation-sites/js".
          .webpackConfig({
              module: {
                  rules: [{
                      // Section "// Babel Compilation." from "/node_modules/laravel-mix/src/builder/webpack-rules.js"
                      test: /\.jsx?$/,
                      // Thanks for the help with "exclude": http://qaru.site/questions/97960/import-a-module-from-nodemodules-with-babel-but-failed/624982#624982
                      exclude(file) {
                          if (file.startsWith(__dirname + '/node_modules/foundation-sites/js')) {
                              return false;
                          }
                          return file.startsWith(__dirname + '/node_modules');
                      },
                      use: [
                          {
                              loader: 'babel-loader',
                              options: Config.babel()
                          }
                      ]
                  }]
              }
          });
          

          注意: 要安装Foundation,我使用了包https://github.com/laravel-frontend-presets/zurb-foundation。并添加了将Foundation加载到/resources/assets/js/bootstrap.js文件中的代码:

          /**
           * We'll load jQuery and the Foundation jQuery plugin which provides support
           * for JavaScript based Foundation features such as modals and tabs. This
           * code may be modified to fit the specific needs of your application.
           */
          
          try {
              window.$ = window.jQuery = require('jquery');
          
              require('foundation-sites/dist/js/foundation'); // 'foundation.min' can also be used if you like
          
              // The app plugins for the Foundation
              require('./plugins/entries/foundation');
          
          } catch (e) {}
          

          其次,我创建了文件/resources/assets/js/plugins/entries/foundation.js(该文件包含在// The app plugins for the Foundation.上面的代码中)。我在其中包含了我的模块(示例):

          import { CropText } from '../cropText';
          Foundation.plugin(CropText, 'CropText');
          

          第三,我创建了两个文件来包含Foundation插件:

          1) /resources/assets/js/plugins/foundation.plugin.js

          // By default, files from the directory "/node_modules" are not processed by the Babel loader,
          // so in the Webpack configuration,
          // an exception was added for loading from the directory "/node_modules/foundation-sites/js".
          import { Plugin } from 'foundation-sites/js/foundation.plugin';
          
          export {Plugin};
          

          2)/resources/assets/js/plugins/foundation.util.mediaQuery.js

          // By default, files from the directory "/node_modules" are not processed by the Babel loader,
          // so in the Webpack configuration,
          // an exception was added for loading from the directory "/node_modules/foundation-sites/js".
          import { MediaQuery } from 'foundation-sites/js/foundation.util.mediaQuery';
          
          export {MediaQuery};
          

          在第四个中,我使用Foundation插件模板为我的插件创建了一个文件,其中包括上述两个文件:

          /resources/assets/js/plugins/cropText.js

          'use strict';
          
          import $ from 'jquery';
          import { MediaQuery } from './foundation.util.mediaQuery';
          import { Plugin } from './foundation.plugin';
          
          /**
           * CropText plugin.
           * @plugin app.cropText
           */
          class CropText extends Plugin {
              /**
               * Creates a new instance of CropText.
               * @class
               * @name CropText
               * @fires CropText#init
               * @param {Object} element - jQuery object to add the trigger to.
               * @param {Object} options - Overrides to the default plugin settings.
               */
              _setup(element, options = {}) {
                  this.$element = element;
                  this.options  = $.extend(true, {}, CropText.defaults, this.$element.data(), options);
          
                  this.className = 'CropText'; // ie9 back compat
                  this._init();
              }
          
              /**
               * Initializes the CropText plugin.
               * @private
               */
              _init() {
                  MediaQuery._init();
          
                  this.cropText();
              }
          
              /**
               * Crops the text.
               */
              cropText() {
                  var size = +this.options.cropSize;
          
                  $(this.$element).each(function(i, value) {
                      var $valueText = $(value).text(),
                          $valueHtml = $(value).html();
          
                      if($valueText.length > size){
                          $(value).html('<span>' + $valueText.slice(0, size).trim() + '</span>' + '...').wrapInner('<a></a>');
          
                          var revealId = '#' + $(value).attr('data-open');
          
                          if ($(revealId + ' .close-button').attr('data-close') != undefined) {
                              $(revealId + ' .close-button').before($valueHtml);
                          } else {
                              $(revealId).append($valueHtml);
                          }
          
                          new Foundation.Reveal($(revealId), {
                              'data-overlay' : false
                          });
                      } else {
                          $(value).removeAttr('data-open').removeAttr('tabindex');
                      }
                  });
              }
          }
          
          /**
           * Default settings for plugin
           */
          CropText.defaults = {
              /**
               * The size of the cropped text.
               * @option
               * @type {number}
               * @default 255
               */
              cropSize: 255
          };
          
          export {CropText};
          

          就是这样。接下来,我只需要在页面的HTML代码中包含一个标准的JavaScript文件并初始化Foundation(示例):

          /resources/views/layouts/app.blade.php

          <script src=" {{ mix('/js/app.js') }} "></script>
          
          <script>
              $(document).foundation();
          </script>
          

          附:对不起我的英语 ;-),我使用了谷歌翻译。

          【讨论】:

            【解决方案10】:

            这是从网络搜索中获得与问题中提到的技术相关的初学者问题的常见位置。

            这是我的 Zurb Foundation with SCSS and Webpack Example Project

            随意克隆它。它通过 Unlicense 获得许可。

            背景

            尽管模块兼容性问题已在 Zurb Foundation 的最新 (> 6.4) 版本中得到解决,但对于初学者来说,基本设置仍可能看起来像是黑魔法。我认为在某个地方一定还有其他可能更好的例子,但我就是找不到它们。我在这里添加我的学习之旅的成果,希望它能让某人的骑行少一些颠簸。

            【讨论】:

              猜你喜欢
              • 2016-12-18
              • 2021-07-21
              • 2016-03-10
              • 2016-11-04
              • 2021-05-21
              • 2017-09-21
              • 1970-01-01
              • 2017-04-05
              相关资源
              最近更新 更多