• 【webpack整理】一、安装、配置、按需加载


    如果你:

    • 是前端热爱者 :)
    • 有JavaScript/nodejs基础
    • 会使用一些常用命令行,mkdir,cd,etc.
    • 会使用npm
    • 想对webpack有更深的认识,或许此时你恰好遇到关于webpack的问题

    那么,请继续阅读:

    什么是webpack,为什么使用webpack

    webpack官方是这样定义她的:

    webpack 是一个用来构建我们应用程序中的 JavaScript 模块的工具。

    简单来说就是一个打包器。(打包器: 它做的事情是,分析你的项目结构,找到JavaScript模块以及其它的一些浏览器不能直接运行的拓展语言(Scss,TypeScript等),并将其打包为合适的格式以供浏览器使用。)

    打包器茫茫多,那么为什么选择她呢?因为她具备以下特性

    • 附加模块按需加载
    • AMD define
    • AMD require / 按需加载
    • CommonJS exports
    • CommonJS require
    • CommonJS require.resolve
    • require 中拼接 require("./fi" + "le")
    • 调试支持 SourceUrl, SourceMaps
    • ES2015 import/export
    • require (guided) 中的表达式 require("./templates/" + template)
    • 生成单独包
    • 间接的 require: var r = require; r("./file")
    • 压缩 uglify
    • 可配置用 common bundle 构建多页
    • 可多个 bundle
    • Node.js 内置 libs require("path")
    • Node.js 的一些变量可用:process, __dir/filename, global
    • 丰富的插件
    • 预处理: loaders, transforms
    • 浏览器替换 web_modules, .web.js, package.json field, alias config option
    • 拥有文件系统: require 文件
    • 执行时(runtime)开销低:243B + 20B 每个模块 + 4B 每个依赖
    • 开发文件监听(watch)模式
    • etc.

    用过的都说好。

    如果你曾经挣扎于下面这些情况中的其中之一:

    • 不小心将一些不需要的样式表或者 JS 库引入生产环境,导致项目体积变大
    • 遇到作用域问题 - 不管是来自 CSS 还是 JavaScript
    • 不停寻找一个好的系统好让你可以在 JavaScript 代码里使用 Node 或 Bower 的模块,或者依赖一系列疯狂的后端配置来正确地使用那些模块
    • 需要优化资源分发机制却又担心会破坏掉某些东西

    那么你就可以受益于 Webpack 了。它通过让 JavaScript 取代开发者的大脑来关心依赖和加载顺序,轻松地解决了上面这些问题。最好的部分是什么?Webpack 甚至可以在服务端无缝运行,这意味着你仍然可以使用 Webpack 来构建渐进式增强的网站。

    安装

    预准备

    在开始前,先要确认你已经安装 Node.js 的最新版本。使用 Node.js 最新的 LTS 版本,是理想的起步。使用旧版本,你可能遇到各种问题,因为它们可能缺少 webpack 功能或缺少相关 package 包。

    本地安装

    使用npm:

    npm install --save-dev webpack
    npm install --save-dev webpack@<version>
    
    • 当你在本地安装 webpack 后,你能够从node_modules/.bin/webpack 访问它的 bin 版本。

    全局安装

    npm install --global webpack
    

    但不推荐这样做,原因是:会锁定 webpack 到指定版本,并且在使用不同的 webpack 版本的项目中可能会导致构建失败。

    配置

    很多情况,你在使用webpack中遇到的问题都是配置问题,好好阅读下下面的配置,90%的问题应该都可以解决了。

    在根目录新建一个 webpack.config.js 文件用来声明 Webpack 的配置,你也可以指定特定的config文件:webpack --config mywebpack.config.js

    const path = require('path');
    
    module.exports = {
      entry: "./app/entry", // string | object | array  entry: ["./app/entry1", "./app/entry2"],
      entry: {
        a: "./app/entry-a",
        b: ["./app/entry-b1", "./app/entry-b2"]
      },
      // 这里应用程序开始执行
      // webpack 开始打包
    
      output: {
        // webpack 如何输出结果的相关选项
    
        path: path.resolve(__dirname, "dist"), // string
        // 所有输出文件的目标路径
        // 必须是绝对路径(使用 Node.js 的 path 模块)
        // PS:__dirname指的是根目录
    
        filename: "bundle.js", // string    filename: "[name].js", // 用于多个入口点(entry point)(出口点?)
        filename: "[chunkhash].js", // 用于长效缓存
        // 「入口分块(entry chunk)」的文件名模板(出口分块?)
    
        publicPath: "/assets/", // string    publicPath: "",
        publicPath: "https://cdn.example.com/",
        // 输出解析文件的目录,url 相对于 HTML 页面
    
        library: "MyLibrary", // string,
        // 导出库(exported library)的名称
    
        libraryTarget: "umd", // 通用模块定义        libraryTarget: "umd2", // 通用模块定义
            libraryTarget: "commonjs2", // exported with module.exports
            libraryTarget: "commonjs-module", // 使用 module.exports 导出
            libraryTarget: "commonjs", // 作为 exports 的属性导出
            libraryTarget: "amd", // 使用 AMD 定义方法来定义
            libraryTarget: "this", // 在 this 上设置属性
            libraryTarget: "var", // 变量定义于根作用域下
            libraryTarget: "assign", // 盲分配(blind assignment)
            libraryTarget: "window", // 在 window 对象上设置属性
            libraryTarget: "global", // property set to global object
            libraryTarget: "jsonp", // jsonp wrapper
        // 导出库(exported library)的类型
    
        /* 高级输出配置 */
        pathinfo: true, // boolean
        // 在生成代码时,引入相关的模块、导出、请求等有帮助的路径信息。
    
        chunkFilename: "[id].js",
        chunkFilename: "[chunkhash].js", // 长效缓存(/guides/caching)
        // 「附加分块(additional chunk)」的文件名模板
    
        jsonpFunction: "myWebpackJsonp", // string
        // 用于加载分块的 JSONP 函数名
    
        sourceMapFilename: "[file].map", // string
        sourceMapFilename: "sourcemaps/[file].map", // string
        // 「source map 位置」的文件名模板
    
        devtoolModuleFilenameTemplate: "webpack:///[resource-path]", // string
        // 「devtool 中模块」的文件名模板
    
        devtoolFallbackModuleFilenameTemplate: "webpack:///[resource-path]?[hash]", // string
        // 「devtool 中模块」的文件名模板(用于冲突)
    
        umdNamedDefine: true, // boolean
        // 在 UMD 库中使用命名的 AMD 模块
    
        crossOriginLoading: "use-credentials", // 枚举
        crossOriginLoading: "anonymous",
        crossOriginLoading: false,
        // 指定运行时如何发出跨域请求问题
    
        /* 专家级输出配置(自行承担风险) */
        devtoolLineToLine: {
          test: /.jsx$/
        },
        // 为这些模块使用 1:1 映射 SourceMaps(快速)
    
        hotUpdateMainFilename: "[hash].hot-update.json", // string
        // 「HMR 清单」的文件名模板
    
        hotUpdateChunkFilename: "[id].[hash].hot-update.js", // string
        // 「HMR 分块」的文件名模板
    
        sourcePrefix: "	", // string
        // 包内前置式模块资源具有更好可读性
      },
    
      module: {
        // 关于模块配置
    
        rules: [
          // 模块规则(配置 loader、解析器等选项)
    
          {
            test: /.jsx?$/,
            include: [
              path.resolve(__dirname, "app")
            ],
            exclude: [
              path.resolve(__dirname, "app/demo-files")
            ],
            // 这里是匹配条件,每个选项都接收一个正则表达式或字符串
            // test 和 include 具有相同的作用,都是必须匹配选项
            // exclude 是必不匹配选项(优先于 test 和 include)
            // 最佳实践:
            // - 只在 test 和 文件名匹配 中使用正则表达式
            // - 在 include 和 exclude 中使用绝对路径数组
            // - 尽量避免 exclude,更倾向于使用 include
    
            issuer: { test, include, exclude },
            // issuer 条件(导入源)
    
            enforce: "pre",
            enforce: "post",
            // 标识应用这些规则,即使规则覆盖(高级选项)
    
            loader: "babel-loader",
            // 应该应用的 loader,它相对上下文解析
            // 为了更清晰,`-loader` 后缀在 webpack 2 中不再是可选的
            // 查看 webpack 1 升级指南。
    
            options: {
              presets: ["es2015"]
            },
            // loader 的可选项
          },
    
          {
            test: ".html$"
    
            use: [
              // 应用多个 loader 和选项
              "htmllint-loader",
              {
                loader: "html-loader",
                options: {
                  /* ... */
                }
              }
            ]
          },
    
          { oneOf: [ /* rules */ ] },
          // 只使用这些嵌套规则之一
    
          { rules: [ /* rules */ ] },
          // 使用所有这些嵌套规则(合并可用条件)
    
          { resource: { and: [ /* 条件 */ ] } },
          // 仅当所有条件都匹配时才匹配
    
          { resource: { or: [ /* 条件 */ ] } },
          { resource: [ /* 条件 */ ] },
          // 任意条件匹配时匹配(默认为数组)
    
          { resource: { not: /* 条件 */ } }
          // 条件不匹配时匹配
        ],
    
        /* 高级模块配置 */
        noParse: [
          /special-library.js$/
        ],
        // 不解析这里的模块
    
        unknownContextRequest: ".",
        unknownContextRecursive: true,
        unknownContextRegExp: /^./.*$/,
        unknownContextCritical: true,
        exprContextRequest: ".",
        exprContextRegExp: /^./.*$/,
        exprContextRecursive: true,
        exprContextCritical: true,
        wrappedContextRegExp: /.*/,
        wrappedContextRecursive: true,
        wrappedContextCritical: false,
        // specifies default behavior for dynamic requests
      },
    
      resolve: {
        // 解析模块请求的选项
        // (不适用于对 loader 解析)
    
        modules: [
          "node_modules",
          path.resolve(__dirname, "app")
        ],
        // 用于查找模块的目录
    
        extensions: [".js", ".json", ".jsx", ".css"],
        // 使用的扩展名
    
        alias: {
          // 模块别名列表
    
          "module": "new-module",
          // 起别名:"module" -> "new-module" 和 "module/path/file" -> "new-module/path/file"
    
          "only-module$": "new-module",
          // 起别名 "only-module" -> "new-module",但不匹配 "module/path/file" -> "new-module/path/file"
    
          "module": path.resolve(__dirname, "app/third/module.js"),
          // 起别名 "module" -> "./app/third/module.js" 和 "module/file" 会导致错误
          // 模块别名相对于当前上下文导入
        },
        /* 可供选择的别名语法 */    alias: [
          {
            name: "module",
            // 旧的请求
    
            alias: "new-module",
            // 新的请求
    
            onlyModule: true
            // 如果为 true,只有 "module" 是别名
            // 如果为 false,"module/inner/path" 也是别名
          }
        ],
    
        /* 高级解析选项 */
        symlinks: true,
        // 遵循符号链接(symlinks)到新位置
    
        descriptionFiles: ["package.json"],
        // 从 package 描述中读取的文件
    
        mainFields: ["main"],
        // 从描述文件中读取的属性
        // 当请求文件夹时
    
        aliasFields: ["browser"],
        // 从描述文件中读取的属性
        // 以对此 package 的请求起别名
    
        enforceExtension: false,
        // 如果为 true,请求必不包括扩展名
        // 如果为 false,请求可以包括扩展名
    
        moduleExtensions: ["-module"],
        enforceModuleExtension: false,
        // 类似 extensions/enforceExtension,但是用模块名替换文件
    
        unsafeCache: true,
        unsafeCache: {},
        // 为解析的请求启用缓存
        // 这是不安全,因为文件夹结构可能会改动
        // 但是性能改善是很大的
    
        cachePredicate: (path, request) => true,
        // predicate function which selects requests for caching
    
        plugins: [
          // ...
        ]
        // 应用于解析器的附加插件
      },
    
      performance: {
        hints: "warning", // 枚举    hints: "error", // 性能提示中抛出错误
        hints: false, // 关闭性能提示
        maxAssetSize: 200000, // 整数类型(以字节为单位)
        maxEntrypointSize: 400000, // 整数类型(以字节为单位)
        assetFilter: function(assetFilename) {
          // 提供资源文件名的断言函数
          return assetFilename.endsWith('.css') || assetFilename.endsWith('.js');
        }
      },
    
      devtool: "source-map", // enum  devtool: "inline-source-map", // 嵌入到源文件中
      devtool: "eval-source-map", // 将 SourceMap 嵌入到每个模块中
      devtool: "hidden-source-map", // SourceMap 不在源文件中引用
      devtool: "cheap-source-map", // 没有模块映射(module mappings)的 SourceMap 低级变体(cheap-variant)
      devtool: "cheap-module-source-map", // 有模块映射(module mappings)的 SourceMap 低级变体
      devtool: "eval", // 没有模块映射,而是命名模块。以牺牲细节达到最快。
      // 通过在浏览器调试工具(browser devtools)中添加元信息(meta info)增强调试
      // 牺牲了构建速度的 `source-map' 是最详细的。
    
      context: __dirname, // string(绝对路径!)
      // webpack 的主目录
      // entry 和 module.rules.loader 选项
      // 相对于此目录解析
    
      target: "web", // 枚举  target: "webworker", // WebWorker
      target: "node", // node.js 通过 require
      target: "async-node", // Node.js 通过 fs and vm
      target: "node-webkit", // nw.js
      target: "electron-main", // electron,主进程(main process)
      target: "electron-renderer", // electron,渲染进程(renderer process)
      target: (compiler) => { /* ... */ }, // 自定义
      // 包(bundle)应该运行的环境
      // 更改 块加载行为(chunk loading behavior) 和 可用模块(available module)
    
      externals: ["react", /^@angular//],  externals: "react", // string(精确匹配)
      externals: /^[a-z-]+($|/)/, // 正则
      externals: { // 对象
        angular: "this angular", // this["angular"]
        react: { // UMD
          commonjs: "react",
          commonjs2: "react",
          amd: "react",
          root: "React"
        }
      },
      externals: (request) => { /* ... */ return "commonjs " + request }
      // 不要遵循/打包这些模块,而是在运行时从环境中请求他们
    
      stats: "errors-only",  stats: { //object
        assets: true,
        colors: true,
        errors: true,
        errorDetails: true,
        hash: true,
        // ...
      },
      // 精确控制要显示的 bundle 信息
    
      devServer: {
        proxy: { // proxy URLs to backend development server
          '/api': 'http://localhost:3000'
        },
        contentBase: path.join(__dirname, 'public'), // boolean | string | array, static file location
        compress: true, // enable gzip compression
        historyApiFallback: true, // true for index.html upon 404, object for multiple paths
        hot: true, // hot module replacement. Depends on HotModuleReplacementPlugin
        https: false, // true for self-signed, object for cert authority
        noInfo: true, // only errors & warns on hot reload
        // ...
      },
    
      plugins: [
        // ...
      ],
      // 附加插件列表
    
      /* 高级配置 */
      resolveLoader: { /* 等同于 resolve */ }
      // 独立解析选项的 loader
    
      profile: true, // boolean
      // 捕获时机信息
    
      bail: true, //boolean
      // 在第一个错误出错时抛出,而不是无视错误。
    
      cache: false, // boolean
      // 禁用/启用缓存
    
      watch: true, // boolean
      // 启用观察
    
      watchOptions: {
        aggregateTimeout: 1000, // in ms
        // 将多个更改聚合到单个重构建(rebuild)
    
        poll: true,
        poll: 500, // 间隔单位 ms
        // 启用轮询观察模式
        // 必须用在不通知更改的文件系统中
        // 即 nfs shares(译者注:Network FileSystem,最大的功能就是可以透過網路,讓不同的機器、不同的作業系統、可以彼此分享個別的檔案 ( share file ))
      },
    
      node: {
        /* TODO */
      },
    
      recordsPath: path.resolve(__dirname, "build/records.json"),
      recordsInputPath: path.resolve(__dirname, "build/records.json"),
      recordsOutputPath: path.resolve(__dirname, "build/records.json"),// TODO}
    

    分割代码(代码分离)

    代码分离是 webpack 中最引人注目的特性之一。你可以把你的代码分离到不同的 bundle 中,然后你就可以去按需加载这些文件

    • 例如,当用户导航到匹配的路由,或用户触发了事件时,加载对应文件。如果使用了正确的使用方式,这可以使我们有更小的 bundle,同时可以控制资源加载优先级,从而对你的应用程序加载时间产生重要影响。

    总的来说,使用 webpack 可以完成两类代码分离工作:

    按资源进行分离

    一、分离第三方库

    现代流行开发网站,多多少少都用到了不少三方库,而这些三方库一般不会频繁的去修改,将这些代码和我们的业务逻辑一同打包,这无疑是低效的。如果我们将这些库(library)中的代码,保留在与应用程序代码相独立的 bundle 中,我们就可以利用浏览器缓存机制,把这些文件长时间地缓存在用户机器上,增加了访问速度。

    为了完成这个目标,不管应用程序代码如何变化,vendor 文件名中的 hash 部分必须保持不变。学习如何使用 CommonsChunkPlugin 分离 vendor/library 代码。

    你可能会想到搞多个入口啊,类似这样:

    var path = require('path');
    
    module.exports = function(env) {
        return {
            entry: {
                main: './index.js',
                vendor: 'moment'
            },
            output: {
                filename: '[name].[chunkhash].js',
                path: path.resolve(__dirname, 'dist')
            }
        }
    }
    

    再次运行 webpack,可以发现生成了两个 bundle。然而如果查看他们的代码,会发现 moment 的代码在两个文件中都出现了!其原因是 moment 是主应用程序(例如 index.js)的依赖模块,每个入口起点都会打包自己的依赖模块。

    为此我们需要使用插件CommonsChunkPlugin,这是一个非常复杂的插件。它从根本上允许我们从不同的 bundle 中提取所有的公共模块,并且将他们加入公共 bundle 中。如果公共 bundle 不存在,那么它将会创建一个出来。

    var webpack = require('webpack');
    var path = require('path');
    
    module.exports = function(env) {
        return {
            entry: {
                main: './index.js',
                vendor: 'moment'
            },
            output: {
                filename: '[name].[chunkhash].js',
                path: path.resolve(__dirname, 'dist')
            },
            plugins: [
                new webpack.optimize.CommonsChunkPlugin({
                    name: 'vendor' // 指定公共 bundle 的名字。
                })
            ]
        }
    }
    

    但是这样vendor的文件的hashcode还是会每个编译都不一样,为此,可以使用manifest,如下:

    var webpack = require('webpack');
    var path = require('path');
    
    module.exports = function(env) {
        return {
            entry: {
                main: './index.js',
                vendor: 'moment'
            },
            output: {
                filename: '[name].[chunkhash].js',
                path: path.resolve(__dirname, 'dist')
            },
            plugins: [
                new webpack.optimize.CommonsChunkPlugin({
                    names: ['vendor', 'manifest'] // 指定公共 bundle 的名字。
                })
            ]
        }
    };
    

    总结使用CommonsChunkPlugin:

    var webpack = require('webpack');
    var path = require('path');
    
    module.exports = function() {
        return {
            entry: {
                main: './index.js' //Notice that we do not have an explicit vendor entry here
            },
            output: {
                filename: '[name].[chunkhash].js',
                path: path.resolve(__dirname, 'dist')
            },
            plugins: [
                new webpack.optimize.CommonsChunkPlugin({
                    name: 'vendor',
                    minChunks: function (module) {
                       // this assumes your vendor imports exist in the node_modules directory
                       return module.context && module.context.indexOf('node_modules') !== -1;
                    }
                }),
                //CommonChunksPlugin will now extract all the common modules from vendor and main bundles
                new webpack.optimize.CommonsChunkPlugin({
                    name: 'manifest' //But since there are no more common modules between them we end up with just the runtime code included in the manifest file
                })
            ]
        };
    }
    

    你还可以使用DllPlugin(提供分离打包的方式,可以极大提高构建时间性能)

    new webpack.DllPlugin({
            path: `${__dirname}/manifest.json`,
            name: '[name]',
            context: __dirname,
          })
    

    二、分离CSS

    为了用 webpack 对 CSS 文件进行打包,你可以像其它模块一样将 CSS 引入到你的 JavaScript 代码中,同时用 css-loader (像 JS 模块一样输出 CSS),也可以选择使用 ExtractTextWebpackPlugin (将打好包的 CSS 提出出来并输出成 CSS 文件)。

    为什么会使用这个插件,假如不使用,那么你可能如下:

    安装CSS加载器:

    npm install --save-dev css-loader style-loader
    

    webpack配置:

    module.exports = {
        module: {
            rules: [{
                test: /.css$/,
                use: [ 'style-loader', 'css-loader' ]
            }]
        }
    }
    

    但是这样,CSS 会跟你的 JavaScript 打包在一起,并且在初始加载后,通过一个 <style> 标签注入样式,然后作用于页面。

    这里有一个缺点就是,你无法使用浏览器的能力,去异步且并行去加载 CSS。取而代之的是,你的页面需要等待整个 JavaScript 文件加载完,才能进行样式渲染。

    webpack 能够用 ExtractTextWebpackPlugin 帮助你将 CSS 单独打包,以解决以上问题。

    安装:

    npm install --save-dev extract-text-webpack-plugin
    

    添加到配置中使用她:

    +var ExtractTextPlugin = require('extract-text-webpack-plugin');
    module.exports = {
        module: {
             rules: [{
                 test: /.css$/,
    -            use: [ 'style-loader', 'css-loader' ]
    +            use: ExtractTextPlugin.extract({
    +                use: 'css-loader'
    +            })
             }]
         },
    +    plugins: [
    +        new ExtractTextPlugin('styles.css'),
    +    ]
    }
    

    OK,CSS分离完成。

    按工程需求进行分离

    工程一旦达到一定的程度,就会有按需加载的需求。接下来是说如何将您的 bundle 拆分成可以在之后异步下载的 chunk。例如,这允许首先提供最低限度的引导 bundle,并在稍后再异步地加载其他功能。如果你们不需要按需加载,那么可以跳过。

    webpack 支持两种相似的技术实现此目的:使用 import() (推荐,ECMAScript 提案) 和 require.ensure() (遗留,webpack 特定,这里不讨论这个,舍弃)。

    import()

    ES2015 loader 规范定义了 import() 作为一种在运行时(runtime)动态载入 ES2015 模块的方法。

    webpack 把 import() 作为一个分离点(split-point),并把引入的模块作为一个单独的 chunk。 import() 将模块名字作为参数并返回一个 Promoise 对象,即 import(name) -> Promise.

    如:

    function determineDate() {
      import('moment').then(function(moment) {
        console.log(moment().format());
      }).catch(function(err) {
        console.log('Failed to load moment', err);
      });
    }
    
    determineDate();
    
    • 注意,由于 webpack 至少需要感知到文件的位置信息,因此类似 import(foo)完全动态语句会导致失败。这是因为 foo 可以是系统或项目中的任意路径下任意文件。import() 至少应感知的信息是模块所处的位置,所以打包将限制在特定目录或一组文件中。

    例如,import(``./locale/${language}.json``) 将会使 ./locale 目录下的每个 .json 都打包到分离点(split-point)中。在运行时(runtime),当计算出变量 language 时,任何像 english.jsongerman.json 这样的文件都可以供使用。所以请牢记,在使用 import() 时,该路径必须包含路径信息或完整的静态路径(就像上面例子中的 'moment' 一样)。

    使用Promise polyfill

    使用它是因为:import() 在内部依赖于 Promise。 如果你想在老版本浏览器使用 import(),请记得使用 polyfill(例如 es6-promise 或 promise-polyfill)来 shim Promise。

    入口处配置:

    import Es6Promise from 'es6-promise';
    Es6Promise.polyfill();
    // 或
    import 'es6-promise/auto';
    // 或
    import Promise from 'promise-polyfill';
    if (!window.Promise) {
      window.Promise = Promise;
    }
    

    自定义Chunk 名称

    这个是webpack 2.4.0新加的"魔力注释"

    import(/* webpackChunkName: "my-chunk-name" */ 'module');
    

    配合 Babel 使用

    import() 是属于 Stage 3 的特性,需要安装/添加 syntax-dynamic-import 插件来避免 parser 报错。

    npm install --save-dev babel-core babel-loader babel-plugin-syntax-dynamic-import babel-preset-es2015
    

    然后你就可以使用了:

    function determineDate() {
      import('moment')
        .then(moment => moment().format('LLLL'))
        .then(str => console.log(str))
        .catch(err => console.log('Failed to load moment', err));
    }
    
    determineDate();
    

    但是你的webpack的配置文件得像下面这样去修改它:

    module.exports = {
      entry: './index-es2015.js',
      output: {
        filename: 'dist.js',
      },
      module: {
        rules: [{
          test: /.js$/,
          exclude: /(node_modules)/,
          use: [{
            loader: 'babel-loader',
            options: {
              presets: [['es2015', {modules: false}]],
              plugins: ['syntax-dynamic-import']
            }
          }]
        }]
      }
    };
    

    没有使用 syntax-dynamic-import 插件会导致构建失败,并提示:

    • Module build failed(模块构建失败): SyntaxError: 'import' and 'export' may only appear at the top level('import' 和 'export' 只能出现在顶层),或提示
    • Module build failed(模块构建失败): SyntaxError: Unexpected token, expected {

    使用async/await

    说了,import(name) -> Promise返回的是promise,so,用async/await大法好;那么如何使用呢:

    安装插件:

    npm install --save-dev babel-plugin-transform-async-to-generator babel-plugin-transform-regenerator babel-plugin-transform-runtime
    

    然后你就可以这样用了:

    async function determineDate() {
      const moment = await import('moment');
      return moment().format('LLLL');
    }
    
    determineDate().then(str => console.log(str));
    

    当然在插件是这样使用的:

    module.exports = {
      entry: './index-es2017.js',
      output: {
        filename: 'dist.js',
      },
      module: {
        rules: [{
          test: /.js$/,
          exclude: /(node_modules)/,
          use: [{
            loader: 'babel-loader',
            options: {
              presets: [['es2015', {modules: false}]],
              plugins: [
                'syntax-dynamic-import',
                'transform-async-to-generator',
                'transform-regenerator',
                'transform-runtime'
              ]
            }
          }]
        }]
      }
    };
    

    import() 导入整个模块命名空间

    需要注意的是import() 导入整个模块命名空间。举个例子:

    // 示例 1: 最顶层 import
    import * as Component from './component';
    // 示例 2: 使用 import() 进行代码分离
    import('./component').then(Component => /* ... */);
    

    但是,在使用带有 ES2015 模块的 import() 时,您必须显式地访问默认导出和命名导出:

    async function main() {
      // 解构赋值用法示例
      const { default: Component } = await import('./component');
      // 行内用法示例
      render((await import('./component')).default);
    }

    可去我博客主页查看更多详情:http://www.fangyongle.com/xiang-xi-webpack-2jiao-cheng/
  • 相关阅读:
    EasyUI Combobox组合框(模糊搜索、默认值绑定)
    Asp.Net下载文件时中途失败问题
    VS使用小技巧之——设置调试时启动项目和启动页
    VS使用小技巧之——给代码添加region
    VS使用小技巧之——引入整个文件夹
    VS使用小技巧之——任务列表
    FineUI给表格行内链接设置弹出窗体
    cnpm私服搭建和发布包
    阿里云产品术语和docker
    angularjs1.x的一些知识点整理
  • 原文地址:https://www.cnblogs.com/skylor/p/7008756.html
Copyright © 2020-2023  润新知