若是你:css
mkdir
,cd
,etc.npm
那麼,請繼續閱讀:html
webpack官方是這樣定義她的:前端
webpack 是一個用來構建咱們應用程序中的 JavaScript 模塊的工具。node
簡單來講就是一個打包器。(打包器: 它作的事情是,分析你的項目結構,找到JavaScript模塊以及其它的一些瀏覽器不能直接運行的拓展語言(Scss,TypeScript等),並將其打包爲合適的格式以供瀏覽器使用。)react
打包器茫茫多,那麼爲何選擇她呢?由於她具有如下特性webpack
define
require
/ 按需加載exports
require
require.resolve
require("./fi" + "le")
SourceUrl
, SourceMaps
import
/export
require (guided)
中的表達式 require("./templates/" + template)
require
: var r = require; r("./file")
uglify
common
bundle
構建多頁bundle
require("path")
process
, __dir/filename
, global
loaders
, transforms
web_modules
, .web.js
, package.json
field, alias
config optionrequire
文件watch
)模式用過的都說好。es6
若是你曾經掙扎於下面這些狀況中的其中之一:web
那麼你就能夠受益於 Webpack 了。它經過讓 JavaScript 取代開發者的大腦來關心依賴和加載順序,輕鬆地解決了上面這些問題。最好的部分是什麼?Webpack 甚至能夠在服務端無縫運行,這意味着你仍然可使用 Webpack 來構建漸進式加強的網站。正則表達式
在開始前,先要確認你已經安裝 Node.js 的最新版本。使用 Node.js 最新的 LTS 版本,是理想的起步。使用舊版本,你可能遇到各類問題,由於它們可能缺乏 webpack 功能或缺乏相關 package 包。npm
使用npm
:
npm install --save-dev webpack npm install --save-dev webpack@<version>
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 中,而後你就能夠去按需加載這些文件
總的來講,使用 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, })
爲了用 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 特定,這裏不討論這個,捨棄)。
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();
import(foo)
的徹底動態語句會致使失敗。這是由於 foo 能夠是系統或項目中的任意路徑下任意文件。import()
至少應感知的信息是模塊所處的位置,因此打包將限制在特定目錄或一組文件中。例如,import(``./locale/${language}.json``)
將會使 ./locale
目錄下的每一個 .json
都打包到分離點(split-point)中。在運行時(runtime),當計算出變量 language
時,任何像 english.json
或 german.json
這樣的文件均可以供使用。因此請牢記,在使用 import()
時,該路徑必須包含路徑信息或完整的靜態路徑(就像上面例子中的 'moment'
同樣)。
使用它是由於: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; }
這個是webpack 2.4.0新加的"魔力註釋"
import(/* webpackChunkName: "my-chunk-name" */ 'module');
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
插件會致使構建失敗,並提示:
SyntaxError: 'import' and 'export' may only appear at the top level
('import' 和 'export' 只能出如今頂層),或提示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() 導入整個模塊命名空間。舉個例子:
// 示例 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/