【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: "\t", // 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/
posted @ 2017-06-14 14:39  Skylor.min  阅读(18407)  评论(1编辑  收藏  举报