【webpack整理】1、安裝、配置、按需加載

若是你:css

  • 是前端熱愛者 :)
  • 有JavaScript/nodejs基礎
  • 會使用一些經常使用命令行,mkdir,cd,etc.
  • 會使用npm
  • 想對webpack有更深的認識,或許此時你剛好遇到關於webpack的問題

那麼,請繼續閱讀:html

什麼是webpack,爲何使用webpack

webpack官方是這樣定義她的:前端

webpack 是一個用來構建咱們應用程序中的 JavaScript 模塊的工具。node

簡單來講就是一個打包器。(打包器: 它作的事情是,分析你的項目結構,找到JavaScript模塊以及其它的一些瀏覽器不能直接運行的拓展語言(Scss,TypeScript等),並將其打包爲合適的格式以供瀏覽器使用。)react

打包器茫茫多,那麼爲何選擇她呢?由於她具有如下特性webpack

  • 附加模塊按需加載
  • 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.

用過的都說好。es6

若是你曾經掙扎於下面這些狀況中的其中之一:web

  • 不當心將一些不須要的樣式表或者 JS 庫引入生產環境,致使項目體積變大
  • 遇到做用域問題 - 不論是來自 CSS 仍是 JavaScript
  • 不停尋找一個好的系統好讓你能夠在 JavaScript 代碼裏使用 Node 或 Bower 的模塊,或者依賴一系列瘋狂的後端配置來正確地使用那些模塊
  • 須要優化資源分發機制卻又擔憂會破壞掉某些東西

那麼你就能夠受益於 Webpack 了。它經過讓 JavaScript 取代開發者的大腦來關心依賴和加載順序,輕鬆地解決了上面這些問題。最好的部分是什麼?Webpack 甚至能夠在服務端無縫運行,這意味着你仍然可使用 Webpack 來構建漸進式加強的網站。正則表達式

安裝

預準備

在開始前,先要確認你已經安裝 Node.js 的最新版本。使用 Node.js 最新的 LTS 版本,是理想的起步。使用舊版本,你可能遇到各類問題,由於它們可能缺乏 webpack 功能或缺乏相關 package 包。npm

本地安裝

使用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 能夠完成兩類代碼分離工做:

按資源進行分離

1、分離第三方庫

現代流行開發網站,多多少少都用到了很多三方庫,而這些三方庫通常不會頻繁的去修改,將這些代碼和咱們的業務邏輯一同打包,這無疑是低效的。若是咱們將這些庫(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,
      })

2、分離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/
相關文章
相關標籤/搜索