若是你: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/