搜索

首页  >  问答  >  正文

javascript - 为何webpack2的按需加载不能生效?

刚刚开始接触前端的模块化开发,写了个demo来学习webpack,配置文件及运行流程基本了解了,但是按需加载总是不能成功,还请各位大神帮忙看看:

入口文件中,分别采用了3种方法来加载:

import test from './index/test.js';  
            
// const test=(resolve) => require(['./index/test.js'], resolve)
        
// const test=resolve => { require.ensure(['./index/test.js'], () => { resolve(require('./index/test.js')) }) }
    
test.exe('显示测试文字');//执行

test.js的内容很简单,只是打印到console:

const test={
    exe:function (res) {
        console.log('test方法的输出:'+res);
    }
};
export default test

3种方法都测试了,只有第一种直接导入的方式运行正常,另外两种按需加载的方式都会报错,提示找不到方法。
如果把test.exe('显示测试文字'); 注释掉,只加载不执行,那么就都不会报错。
我的理解是,加载代码没有错,但是需要它们加载的时候,却并没有加载成功,这是为什么呢?是不是我哪里写的不对?还是需要对webpack.config.jx进行额外的配置?

世界只因有你世界只因有你2749 天前759

全部回复(4)我来回复

  • 淡淡烟草味

    淡淡烟草味2017-05-19 10:28:20

    给你个实例参考下

    html

    <input class="btn" type="button" name="" value="load">

    需要异步加载的js文件 plugin.js

    export default function Mod(name) {
      this.name = name;
    }
    Mod.prototype.hi = function() {
      console.log('Hi~ ' + this.name);
    };

    webpack的入口编译文件 entry.js

    let demo = false;
    
    let btn = document.querySelector('.btn');
    btn.addEventListener('click', function() {//1、点击按钮btn时
       require.ensure([], function(require) {
          let mod = require('./bundles/plugin.js').default;//2、异步栽入plugin.js
          if (!demo) {
             demo = new mod('jackson'); //3、创建新实例并禁止多次重复创建
          }
          demo.hi();//4、触发该实例的方法
       }, 'mod1');//5、生成一个名字为mod1.js的异步chunk模块
    });

    效果就是点击时,mod1.js才加载插入head,一开始是不加载的

    最后关于webpack.config.js的配置。

       output: {
          path: path.resolve(__dirname, './dist'),
          filename: 'js/[name].js',
          publicPath: './dist/', 
          chunkFilename: 'js/async/[name].js'
          }

    path + chunkFilename是require.ensurei异步模块生成的路径,但这不是html文件引用它的路径

    真正的引用路径是publicPath + chunkFilename,也就是说如果html在项目根目录,那么html引用这个异步js模块的路径就是:./dist/js/async/[name].js,但是如果你的html在一个文件夹里,比如是index/index.html,要么上面的路径是不可能引用到的,需要改publickPath为:'../dist/'到index文件夹外面去找这个异步模块

    回复
    0
  • 迷茫

    迷茫2017-05-19 10:28:20

    最近也碰到了类似的问题,大致说一下。
    webpack升级到2的时候对你第二种和第三种的信用方式,都没有直接打包到main.js里面。
    也就是说对于首屏加载就需要的模块,不能再使用异步加载的模式,按需加载的可以。
    你可以去打包出来的文件里面看一下,你的除了第一种方式,test方法都没有被打包到你的js里面。

    回复
    0
  • 天蓬老师

    天蓬老师2017-05-19 10:28:20

    第二个和第三种写法想做什么?是想模拟 AMD 或者 CMD 规范的写法么?

    最常见 module 规范 ES6 module 和 node.js 的 commonJS 规范,因为在具体加载细节上有出入,比如加载时间和对文件的引用方式的不同。但是使用webpack的目的就是将不同规范统一化,webpack 会提前将所有 module
    打包在一起,分别给一个 id ,通过 id 进行引用,使得 ES6 module 和 CommonJS 规范在 webpack 编译后没有任何区别,同样对于 AMD 和 CMD 规范。

    如果楼主想用 webpack 实现 CMD 的延迟加载这个思路就是错的,因为无论哪种加载方式,webpack 所做的,就是将你的依赖(或者说即将依赖)的所有模块打包进一个文件,以至于在运行时都能通过 id 找到对应的包,弱化规范间的差异

    回复
    0
  • 为情所困

    为情所困2017-05-19 10:28:20

    不知道你的具体环境, 我自己的环境现状已经升级到Webpack2 + React Router v4. 可以参考文档: https://reacttraining.cn/web/...

    首先需要编码, 创建一个 Bundle 组件, 用于按需加载需要的模块, 组件文件.

    import React, { Component } from 'react'
    
    class Bundle extends Component {
      constructor(props){
        super(props)
        this.state = {
          mod: null
        }
      }
      componentWillMount() {
        this.load(this.props)
      }
      componentWillReceiveProps(nextProps) {
        if (nextProps.load !== this.props.load) {
          this.load(nextProps)
        }
      }
      load(props) {
        this.setState({
          mod: null
        })
        props.load((mod) => {
          this.setState({
            // handle both es imports and cjs
            mod: mod.default ? mod.default : mod
          })
        })
      }
      render() {
        return this.props.children(this.state.mod)
      }
    }
    
    export default Bundle

    上述代码是从文档里面抄的, 修改了状态的初始化方式, 如果不修改状态的初始化方式, 就需要用到babel-plugin-transform-class-properties.

    使用的时候包含三个个步骤

    • 导入Bundle模块

    import Bundle from './bundle.js';
    
    • 异步加载

    import loadHome  from 'bundle-loader?lazy!./components/Home';
    • 初始化

    const Home = ({...props}) => (
      <Bundle load={loadHome}>
        {(Component) => Component? <Component {...props}/>: <p>Loading...</p>}
      </Bundle>
    )

    当然, 你还需要配置你的 .babelrcwebpack.config.js, 下面我给我我自己的, 你可以研究一下.

    webpack.config.js

      module: {
        rules: [
          // Javascript模块加载器
          {
            test: /\.js|jsx$/,
            exclude: /(node_modules|bower_components)/,
            use: {
              loader: 'babel-loader',
              options: {
                cacheDirectory : true,
                presets: [
                  ['es2015', {modules: false}]
                ],
                plugins: [
                  'syntax-dynamic-import',
                  'transform-async-to-generator',
                  'transform-regenerator',
                  'transform-runtime'
                ]
              }
            }
          },
          ...
        ]

    .babelrc

    {
      "presets": [
        "es2017",
        [
          "latest",
          {"es2015":{"modules": false}}
        ],
        "stage-0",
        "react"
      ],
      "plugins": [
        ["import",{"libraryName": "antd","style": true }],
        "react-hot-loader/babel"
      ]
    }
    
    

    还有公共块输出插件的配置

      plugins: [
        ...
        new webpack.optimize.CommonsChunkPlugin({
          name: ["vendor", "manifest"],
          filename: '[name].[hash].js',
          minChunks: 2
        }),
        ...
      ]

    通过上述N个步骤后, 组件Home就可以使用了.

    回复
    0
  • 取消回复