webpack學習筆記丁點積累

世有因果知因求果發表於2017-01-04

webpack是什麼?

https://webpack.js.org/concepts/

https://code.tutsplus.com/tutorials/introduction-to-webpack-part-1--cms-25791

webpack是一個為現代javascript application而生的module bundler:模組打包器。

某種意義上說,webpack也是可以代替gulp,grunt等傳統意義上的task runner,而webpack中的loader就來實現gulp,grunt工具鏈build時的不同功能,比如concat,uglify等。

它支援非常多的配置,擁有強大的功能。但也正是因為這一點可能導致我們很多人一團霧水。在學習之前,先搞明白幾個概念:

https://webpack.js.org/glossary/

Module, Chunk, Bundle

{
  entry: {
    foo: ["webpack/hot/only-dev-server.js","./src/foo.js"],
    bar: ["./src/bar.js"]
  },
  output: {
    path: "./dist",
    filename: "[name].js"
  }
}
  • Modules: "webpack/hot/only-dev-server.js", "./src/foo.js", "./src/bar.js" ( + any other modules that are dependencies of these entry points!)
  • Chunks: foo, bar
  • Bundles: foo, bar

上面的樣例中chunks和bundles是1:1 關係, 但是這並不是總對的. 比如,如果你增加了sourcemaps,你將會獲得1:2 的關係 chunk : bundle.

Entry

webpack經過編譯後將會為你的application建立一張依賴圖:dependency graph. 而這張依賴圖的起點就被稱之為: entry point. 這個entry point告訴webpakc從哪裡開始根據掃描require(cmd), import(es6)等呼叫來不斷描繪這張依賴圖從而知道要打包哪些元素到最終的bundle中。

你可以將application的entry point看作 contextual root或者the first file to kick off your app

在webpack configuration object中,我們就使用entry這個屬性來配置,最簡單的例子:

module.exports = {
  entry: './path/to/my/entry/file.js'
};

entry point的設定有多種形式:

單個字串:

const config = {
  entry: './path/to/my/entry/file.js'
};

module.exports = config;

陣列字串:

const config = {
  entry: ['./path/to/lib/library.js','./path/to/my/entry/main.js']
};

module.exports = config;

object syntax:

const config = {
  entry: {
    main: './path/to/my/entry/file.js'
  }
};
const config = {
  entry: {
    app: './src/app.js',
    vendors: './src/vendors.js'
  }
};
const config = {
  entry: {
    pageOne: './src/pageOne/index.js',
    pageTwo: './src/pageTwo/index.js',
    pageThree: './src/pageThree/index.js'
  }
};

具體參考: https://webpack.js.org/concepts/entry-points/

Output

一旦你已經將你的assets全部打包在一起了,我們仍然需要告訴webpack應該將我們的application bundle放到哪裡.

這是通過output 屬性來定義:(output也會有很多其他的屬性,比如filename,path...)

var path = require('path');

module.exports = {
  entry: './path/to/my/entry/file.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'my-first-webpack.bundle.js'
  }
};

Loaders

Loader是webpack中非常重要的概念。webpack的目標是將專案中的所有assets都可以由webpack來打包,而不是直接放到瀏覽器那裡(通過script tag, link tag css)去非同步載入。(注意這也並不意味著這些assets都必須打包在一起,可以有些折中). webpack將任何一個檔案(.css,.html,.less,.scss,.jpg...)都視作一個javascript module.然而,webpack僅能夠理解javascript!

而Loaders就能夠將這些webpack本身並不能夠理解和認識的檔案轉化為一個module從而加入到bundle的依賴樹中去。

loader相關的配置從高層上講有兩個目的:

1. 指定什麼檔案型別應該由一個Loader來transform (通過test 屬性來完成)

2.  指定應該由哪個loader來transform作為bundle的輸入

var path = require('path');

const config = {
  entry: './path/to/my/entry/file.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'my-first-webpack.bundle.js'
  },
  module: {
    rules: [
      {test: /\.(js|jsx)$/, use: 'babel-loader'}
    ]
  }
};

module.exports = config;

上面的配置就像是我們要告訴webpack的編譯器:

"

Hey webpack compiler,when you come across a path that resolves to a '.js' or '.jsx' file inside of a require/import statement, use the babel-loader to transform it before you add it to the bundle

"

建立一個自己的loader

大部分時間,你所需要的webpack功能,都能通過已經存在的loader來幫你完成,但是事情總歸沒有那麼簡單,有時候你需要特殊的檔案處理流程,比如,如果我們希望在require一個json檔案之前我們先把這個json檔案中的comments全部清除,這時,我們已知有一個node module名叫:strip-json-comments,他可以實現這個功能,我們當然可以直接使用這個module來作為loader,配置json檔案被require之前來使用,但是我們又希望在此基礎上列印出strip comments之前和之後的內容,這時,我們就可以以此為基礎來創作一個自己的loader~!

// 在strip.js檔案中定義loader的邏輯
var stripComments = require('strip-json-comments')
module.exports = function (source) {
    this.cacheable()
    console.log('source:',source)
    console.log('sourceStripped:',stripComments(source))
    return stripComments(source)
}
// 在webpack.config.js loaders section定義如下:
                {
                    test: /\.json$/,
                    exclude: /node_modules/,
                    use: ["json-loader",path.resolve('src/customloaders/strip')]
}
// 在app.js中
var config = require('../appconfig/jsonconfig.json') // 這時就將呼叫到我們的strip loader列印出strip之前和之後的結果,最終呼叫strip-loader來返回結果
console.log(config);

Plugin

Loader本身只能對一個個檔案來做transform,而plugin則通常用於對bundled module的chunks或者compilations執行一些特定的action並且定製部分功能。要使用一個plugin,我們需要require(),並且放在plugins屬性中。webpack本身已經提供了很多plugin,我們也可以自己建立適合我們特定需求的plugin. plugin更像grunt或者gulp的tasks.

plugin本身將webpack engine的所有能力暴露給第三方開發者。通過使用階段性的構建callback,開發者可以向webpack的build process中注入自己的期望行為。

const HtmlWebpackPlugin = require('html-webpack-plugin'); //installed via npm
const webpack = require('webpack'); //to access built-in plugins

const config = {
  entry: './path/to/my/entry/file.js',
  output: {
    filename: 'my-first-webpack.bundle.js',
    path: './dist'
  },
  module: {
    rules: [
      {test: /\.(js|jsx)$/, use: 'babel-loader'}
    ]
  },
  plugins: [
    new webpack.optimize.UglifyJsPlugin(),
    new HtmlWebpackPlugin({template: './src/index.html'})
  ]
};

module.exports = config;

建立一個自己的webpack plugin

https://webpack.js.org/contribute/writing-a-plugin/

一個webpack的plugin由以下幾個步驟:

1. 建立一個命名js函式

2.在該命名函式中定一個apply方法

3.指定將該函式繫結到webpack內部的event hook

4.操作webpack internal instance的特定資料

5.在函式完成時呼叫webpack提供的callback函式繼續webpack構建流程

// A named JavaScript function.
function MyExampleWebpackPlugin() {

};

// Defines `apply` method in it's prototype.
MyExampleWebpackPlugin.prototype.apply = function(compiler) {
  // Specifies webpack's event hook to attach itself.
  compiler.plugin('webpacksEventHook', function(compilation /* Manipulates webpack internal instance specific data. */, callback) {
    console.log("This is an example plugin!!!");
    // some other code logic to custom webpack build

    // Invokes webpack provided callback after functionality is complete.
    callback();
  });
};

compiler和compilation物件

在plugin開發時最重要的就是要使用好compiler和compilation兩個objects.下面我們來仔細梳理一下他們在擴充套件webpack engine開發時所擔當的角色.

compiler object代表了配置好的完整webpack environment.這個物件一旦啟動了webpack就被建立,並且該物件會被包括options,loaders,plugins等配置項所配置。當應用一個plugin到webpack環境時,plugin將會接收一個對這個compiler物件的引用。我們使用compiler物件來訪問main webpack environment.

compilation物件則代表了一個versioned assets的一個single build.當執行webpack development middleware時,一旦監測到檔案變更,那麼就會建立一個新的compilation,這樣就新生成了一套編譯後的assets.每一個compilation物件使得module resources,compiled assets,changed files以及watched dependencies有一個參照水平面.該物件也提供了很多個callback points供plugin選擇使用以便定製行為。

這兩個元件是任何plugin必將關心的部分(特別是一個compilation物件),因此建議好好閱讀以下原始碼。

plugin的基礎架構:

plugins本質上是一些在其原型上定義了apply方法的例項化objects.這個apply方法由webpack compiler在安裝對應plugin時來呼叫一次。apply方法將被傳入對webpack compiler的引用,而通過這個compiler引用就授權了對compiler callbacks的訪問。

定義和使用hello plugin:

function HelloWorldPlugin(options) {
  // Setup the plugin instance with options...
}

HelloWorldPlugin.prototype.apply = function(compiler) {
  compiler.plugin('done', function() {
    console.log('Hello World!');
  });
};

module.exports = HelloWorldPlugin;
// 使用時:
var HelloWorldPlugin = require('hello-world');

var webpackConfig = {
  // ... config settings here ...
  plugins: [
    new HelloWorldPlugin({options: true})
  ]
};
Accessing th

訪問compilation物件:

使用compiler object,你可以繫結到callbacks,並且能夠訪問到每個new compilation.這些compilations又提供了能夠hooking到build process不同步驟的callbacks.

比如:

function HelloCompilationPlugin(options) {}

HelloCompilationPlugin.prototype.apply = function(compiler) {

  // Setup callback for accessing a compilation:
  compiler.plugin("compilation", function(compilation) {

    // Now setup callbacks for accessing compilation steps:
    compilation.plugin("optimize", function() {
      console.log("Assets are being optimized.");
    });
  });
};

module.exports = HelloCompilationPlugin;

async compilation plugins:

一些compilation plugin steps是非同步的,將傳入一個callback function進來,當你的plugin完成工作後必須呼叫這個callback以便build系統正常完成.

function HelloAsyncPlugin(options) {}

HelloAsyncPlugin.prototype.apply = function(compiler) {
  compiler.plugin("emit", function(compilation, callback) {

    // Do something async...
    setTimeout(function() {
      console.log("Done with async work...");
      callback();
    }, 1000);

  });
};

module.exports = HelloAsyncPlugin;

一個完整的複雜plugin例子:

該plugin實現build完成時,羅列出所有assets檔案集合,生成一個新的asset檔案

定義:

function FileListPlugin(options) {}

FileListPlugin.prototype.apply = function(compiler) {
  compiler.plugin('emit', function(compilation, callback) {
    // Create a header string for the generated file:
    var filelist = 'In this build:\n\n';

    // Loop through all compiled assets,
    // adding a new line item for each filename.
    for (var filename in compilation.assets) {
      filelist += ('- '+ filename +'\n');
    }

    // Insert this list into the webpack build as a new file asset:
    compilation.assets['filelist.md'] = {
      source: function() {
        return filelist;
      },
      size: function() {
        return filelist.length;
      }
    };

    callback();
  });
};

module.exports = FileListPlugin;

使用:

var FileListPlugin = require('file-list');

var webpackConfig = {
  // ... config settings here ...
  plugins: [
    new FileListPlugin()
  ]
};

webpack compiler event hook and types

Event name
Reason
Params
Type
Event name
entry-option
Reason
-
Params
-
Type
bailResult
Event name
after-plugins
Reason
After setting up initial set of plugins
Params
compiler
Type
sync
Event name
after-resolvers
Reason
After setting up the resolvers
Params
compiler
Type
sync
Event name
environment
Reason
-
Params
-
Type
sync
Event name
after-environment
Reason
Environment setup complete
Params
-
Type
sync
Event name
before-run
Reason
compiler.run() starts
Params
compiler
Type
async
Event name
run
Reason
Before reading records
Params
compiler
Type
async
Event name
watch-run
Reason
Before starting compilation after watch
Params
compiler
Type
async
Event name
normal-module-factory
Reason
After creating a NormalModuleFactory
Params
normalModuleFactory
Type
sync
Event name
context-module-factory
Reason
After creating a ContextModuleFactory
Params
contextModuleFactory
Type
sync
Event name
before-compile
Reason
Compilation parameters created
Params
compilationParams
Type
async
Event name
compile
Reason
Before creating new compilation
Params
compilationParams
Type
sync
Event name
this-compilation
Reason
Before emitting compilation event
Params
compilation
Type
sync
Event name
compilation
Reason
Compilation creation completed
Params
compilation
Type
sync
Event name
make
Reason
-
Params
compilation
Type
parallel
Event name
after-compile
Reason
-
Params
compilation
Type
async
Event name
should-emit
Reason
Can return true/false at this point
Params
compilation
Type
bailResult
Event name
need-additional-pass
Reason
-
Params
-
Type
bailResult
Event name
emit
Reason
Before emitting assets to output dir
Params
compilation
Type
async
Event name
after-emit
Reason
After emitting assets to output dir
Params
compilation
Type
async
Event name
done
Reason
Completion of compile
Params
stats
Type
sync
Event name
failed
Reason
Failure of compile
Params
error
Type
sync
Event name
invalid
Reason
After invalidating a watch compile
Params
fileName, changeTime
Type
sync
Event name
watch-close
Reason
After stopping a watch compile
Params
-
Type
sync

 https://webpack.js.org/contribute/writing-a-plugin/#different-plugin-shapes

 Different Plugin Shapes:

根據plugin註冊到的事件型別我們可以把外掛劃分為不同的型別。每個事件鉤子來定義它是如何在其註冊中來應用這些外掛的。

synchrouous:

applyPlugins/applyPluginsBailResult

waterfull:

applyPluginsWaterfall

asynchronous :

applyPluginsAsync

async waterfall:

applyPluginsAsyncWaterfall

async series:

applyPluginsAsyncSeries

parallel:

applyPluginsParallel,applyPluginsParallelBailResult

https://webpack.js.org/contribute/writing-a-plugin/#different-plugin-shapes

 

webpack的依賴分析

方法: profile: true配置,使用一個plugin來生成stats.json檔案https://github.com/unindented/stats-webpack-plugin,隨後在http://webpack.github.io/analyse頁面選擇這個stats.json檔案就能分析出來下面的圖形來,好強大,好夢幻!

如何使用webpack-stats-graph來清晰分析bundle構成並做優化?

choco install graphviz  // 注意choco是windows下的package manager方便安裝一些package
npm install -g webpack-stats-graph
webpack-stats-graph # by default looks for stats.json in current directory
webpack-stats-graph --help

https://github.com/g0t4/webpack-stats-graph

webpack-dev-server

webpack-dev-server是一個專門用於webpack開發的web服務,一旦啟動該服務,它也會watch檔案的變化,當檔案變化時,自動編譯,但是注意:它並不會將編譯的輸出寫到檔案系統中,而是直接serve,這個webpack --watch是有區別的,因為這種模式下是會直接寫檔案到磁碟上的。

下面一張圖解釋webpack-dev-server的各個引數及其作用.其中最重要的是publicPath和contentBase兩個引數。publicPath指定dev server編譯後的webpack bundle訪問的url地址,耳contentBase則指向非webpack bundle的靜態內容所在的起始目錄(比如index.html)。很多時候,如果你是在整合環境中開發,比如使用laravel作為後端,那麼這時所謂index.html實際上應該是由laravel blade編譯後提供的,這時這個contentBase引數就無所謂了,我們就只需要將bunlder放到blade模板中做好正確的引用即可。

如何除錯webpack-dev-server的某些問題?

很多時候如果webpack-dev-server工作不如你的意,那麼你可以通過: http://localhost:8080/webpack-dev-server 來檢視。很多時候都是因為地址不對的緣故.

如何讓webpack-dev-server可以在外網訪問?

很多時候使用linux作為開發和部署伺服器都是比較合適的,但是我們自己家裡又沒有linux環境,往往會使用一個雲主機來做開發用機。這時可能就需要webpack-dev-server能夠被外網訪問了.很簡單加上“webpack-dev-server --hot --host 0.0.0.0 --port 9999

簡單webpack bundle的內容學習

app.js require login.js

/******/ (function(modules) { // webpackBootstrap
/******/     // The module cache
/******/     var installedModules = {};
/******/
/******/     // The require function
/******/     function __webpack_require__(moduleId) {
/******/
/******/         // Check if module is in cache
/******/         if(installedModules[moduleId]) {
/******/             return installedModules[moduleId].exports;
/******/         }
/******/         // Create a new module (and put it into the cache)
/******/         var module = installedModules[moduleId] = {
/******/             i: moduleId,
/******/             l: false,
/******/             exports: {}
/******/         };
/******/
/******/         // Execute the module function
/******/         modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/         // Flag the module as loaded
/******/         module.l = true;
/******/
/******/         // Return the exports of the module
/******/         return module.exports;
/******/     }
/******/
/******/
/******/     // expose the modules object (__webpack_modules__)
/******/     __webpack_require__.m = modules;
/******/
/******/     // expose the module cache
/******/     __webpack_require__.c = installedModules;
/******/
/******/     // define getter function for harmony exports
/******/     __webpack_require__.d = function(exports, name, getter) {
/******/         if(!__webpack_require__.o(exports, name)) {
/******/             Object.defineProperty(exports, name, {
/******/                 configurable: false,
/******/                 enumerable: true,
/******/                 get: getter
/******/             });
/******/         }
/******/     };
/******/
/******/     // getDefaultExport function for compatibility with non-harmony modules
/******/     __webpack_require__.n = function(module) {
/******/         var getter = module && module.__esModule ?
/******/             function getDefault() { return module['default']; } :
/******/             function getModuleExports() { return module; };
/******/         __webpack_require__.d(getter, 'a', getter);
/******/         return getter;
/******/     };
/******/
/******/     // Object.prototype.hasOwnProperty.call
/******/     __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/     // __webpack_public_path__
/******/     __webpack_require__.p = "";
/******/
/******/     // Load entry module and return exports
/******/     return __webpack_require__(__webpack_require__.s = 0);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ (function(module, exports, __webpack_require__) {

__webpack_require__(1) // 唯一一個entry入口: app.js,載入login.js(require)
document.write("app.js loaded and new info displayed")
console.log('app loaded')

/***/ }),
/* 1 */
/***/ (function(module, exports) {

console.log('login loaded')

/***/ })
/******/ ]);

app.js require login.js and secondmodule.js

/******/ (function(modules) { // webpackBootstrap
/******/     // The module cache
/******/     var installedModules = {};
/******/
/******/     // The require function
/******/     function __webpack_require__(moduleId) {
/******/
/******/         // Check if module is in cache
/******/         if(installedModules[moduleId]) {
/******/             return installedModules[moduleId].exports;
/******/         }
/******/         // Create a new module (and put it into the cache)
/******/         var module = installedModules[moduleId] = {
/******/             i: moduleId,
/******/             l: false,
/******/             exports: {}
/******/         };
/******/
/******/         // Execute the module function
/******/         modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/         // Flag the module as loaded
/******/         module.l = true;
/******/
/******/         // Return the exports of the module
/******/         return module.exports;
/******/     }
/******/
/******/
/******/     // expose the modules object (__webpack_modules__)
/******/     __webpack_require__.m = modules;
/******/
/******/     // expose the module cache
/******/     __webpack_require__.c = installedModules;
/******/
/******/     // define getter function for harmony exports
/******/     __webpack_require__.d = function(exports, name, getter) {
/******/         if(!__webpack_require__.o(exports, name)) {
/******/             Object.defineProperty(exports, name, {
/******/                 configurable: false,
/******/                 enumerable: true,
/******/                 get: getter
/******/             });
/******/         }
/******/     };
/******/
/******/     // getDefaultExport function for compatibility with non-harmony modules
/******/     __webpack_require__.n = function(module) {
/******/         var getter = module && module.__esModule ?
/******/             function getDefault() { return module['default']; } :
/******/             function getModuleExports() { return module; };
/******/         __webpack_require__.d(getter, 'a', getter);
/******/         return getter;
/******/     };
/******/
/******/     // Object.prototype.hasOwnProperty.call
/******/     __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/     // __webpack_public_path__
/******/     __webpack_require__.p = "";
/******/
/******/     // Load entry module and return exports
/******/     return __webpack_require__(__webpack_require__.s = 0);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ (function(module, exports, __webpack_require__) {

__webpack_require__(1); // 載入第一個entry app.js
module.exports = __webpack_require__(4); // 載入第二個entry utils.js


/***/ }),
/* 1 */
/***/ (function(module, exports, __webpack_require__) {

__webpack_require__(2)
__webpack_require__(3)
document.write("app.js loaded and new info displayed")
console.log('app loaded')

/***/ }),
/* 2 */
/***/ (function(module, exports) {

console.log('login loaded')

/***/ }),
/* 3 */
/***/ (function(module, exports) {

console.log('this is the second module loaded by app')

/***/ }),
/* 4 */
/***/ (function(module, exports) {
// utils.js放在entry陣列中作為multi-entry build
// global lib for example jquery can be put into entry
console.log('utils.js loaded')

/***/ })
/******/ ]);

 如何在js中引入css(注意webpack2.5.1以上的區別)

在webpack.config.js的module section中,增加rules欄位,指定css必須使用style-loader和css-loader,而這一點在webpack2.5.1之前只需要在loaders section增加對應配置!

module:{
        // comment out jshint
        rules: [
            // {
            //     test: /\.js$/,
            //     exclude: /node_modules/,
            //     enforce: "pre",
            //     loader: 'jshint-loader'
            // },
                   { test: /\.css$/, use: [ 'style-loader', 'css-loader' ] }
        ]
...
}

 webpack code splitting and ajax loading bundle

http://jilles.me/webpack-async-bundle-loading/

https://toddmotto.com/lazy-loading-angular-code-splitting-webpack

loader vs plugin

loader完成對幾乎任何檔案格式的的預處理轉換,比如通過以下程式碼就可以呼叫你自己的loader來實現對相關require檔案的轉換,只有轉換之後才能進入bundle.

require("my-loader!./my-awesome-module")

和plugin相比,loader要簡單很多,因為他們僅僅暴露給webpack一個簡單的函式,並且不具有影響到webpack的實際build過程的能力。

而, plugin則不同,plugin可以深度整合到webpack中,因為他們可以通過註冊webpack build system的一些鉤子今兒可以訪問或者更改compiler, compilation,從而更改webpack的構建過程。

loader只為一個單一的檔案在進入bundle之前做變換。plugin則往往處理的物件是bundle或者chunk level,並且往往在bundle genneration的結尾時開始被呼叫工作。

 

使用Nodejs V8 Inspector Manager結合chrome除錯webpack執行過程

如果我們安裝了chrome的v8 inspector manager的話,可以結合chrome dev tool來除錯webpack的執行過程,對學習和開發webpack構建是一個很大的幫助。

https://mattdesl.svbtle.com/debugging-nodejs-in-chrome-devtools

 

node --inspect-brk build/build.js

Debugger listening on ws://127.0.0.1:9229/44fed39e-501a-43d3-b928-8e0a51eebc03
For help see https://nodejs.org/en/docs/inspector

之後使用chrome連線,下面就是使用chrome dev tool開啟的vue-cli建立的webpack專案對應做product build時的配置物件。

node --inspect-brk ./node_modules/jest/bin/jest --runInBand --no-cache --no-watchman

除錯npm run dev的方法

node --inspect-brk ./node_modules/webpack-dev-server/bin/webpack-dev-server.js --inline --progress --config "build/webpack.dev.conf.js"

vue-cli npm run dev時可能報code generator exceeds the max of "500KB"錯誤

 10% building modules 4/4 modules 0 active[BABEL] Note: The code generator has deoptimised the styling of "D:/devenv/Code/newkidsit/resources/assets/js/vueapp/node_modules/lodash/lodash.js" as it exceeds the max of "500KB".
 95% emitting ERROR  Failed to compile with 1 errors11:08:48

解決辦法,bable增加compact: false:

{
        test: /\.js$/,
        loader: 'babel-loader',
        query: {compact: false}
}

如何解決context載入時不必要的載入?以momentjs的locale為例

https://webpack.js.org/plugins/context-replacement-plugin/

new webpack.ContextReplacementPlugin(
  /moment[\/\\]locale$/,
  /zh-cn/
)

https://stackoverflow.com/questions/25384360/how-to-prevent-moment-js-from-loading-locales-with-webpack

https://github.com/moment/moment/issues/2373

resolve.mainFields package.json中的main,module, browser是什麼意思?

https://webpack.js.org/configuration/resolve/#resolve-mainfields

https://github.com/defunctzombie/package-browser-field-spec

當我們import一個npm package時,比如

import * as D3 from "d3"

時,這個resolve.mainFeilds的配置選項將決定package.json中的哪個欄位的值被檢查並使用。預設值將隨著webpack configuration檔案的target選項而不同,而target配置專案具有"node","webworker","web"(預設值),"electron-main"。其中"node" target用於nodejs的服務端環境;"web" target用於一個browser-like的環境,這是預設配置。

如果target欄位設定為“web”,"webworker"或者不做設定的話,mainFields的預設值順序如下:

mainFields: ["browser", "module", "main"]

對於任何其他的非web,webworker值設定,則mainFields的預設值順序如下:

mainFields: ["module", "main"]

比如,D3js的package.json包含以下欄位:

{
  ...
  main: 'build/d3.Node.js',
  browser: 'build/d3.js',
  module: 'index',
  ...
}

這意味著,在web target的情況下,import * as D3 from "d3"將會使用browser欄位中指定的檔案:build/d3.js,因為browser欄位在web target下為第一個出現的值。而如果我們為node target做d3的build則引入的是module欄位的檔案:

如何載入一個模組下面的某個檔案的部分內容?比如main檔案中並未export,而其他的utils.js部分函式希望被引用??

// 注意qiniu-js目錄下的index.js是其“主"檔案,export了一些函式,
// 但是我們希望使用base64目錄下面的urlSafeBase64Encode函式,就可以
// 像下面的語法來挑選我們想用的函式
import { urlSafeBase64Encode } from 'qiniu-js/src/base64' 

tree-shaking and side-effect

很多時候,我們import一個module時,可能僅僅用到其中的一兩個export函式,大部分程式碼為無用的死程式碼,如何能夠找到這些死程式碼,並且在webpack4做bundle時剔除這些無用程式碼呢?

https://webpack.js.org/guides/tree-shaking/

harmony modules = webpack builtin ES2015 module

何時使用module.noParse配置項?

module.noParse配置項用於阻止webpack掃描解析任何匹配了正則規則的檔案。需要注意的是被忽略的那些檔案不允許有任何 import, require, defined或者任何其他的importing機制呼叫。這可以大大提高ewbpack build的效能,特別是大的庫檔案。再比如,你如果只有dist/xxxlib.min.js檔案的時候,就可以派上用場了。

noParse: /jquery|lodash/

// since webpack 3.0.0
noParse: function(content) {
  return /jquery|lodash/.test(content);
}

webpack dev server訪問時出現Invalid Host header

devServer: {

    compress: true,

    disableHostCheck: true,   // That solved it

 }   

 

相關文章