参考地址-官网javascript
启用此功能须要更新 webpack-dev-server 的配置,和使用 webpack 内置的 HMR 插件。css
webpack.config.jshtml
module.exports = {
// ...
devServer: {
// ...
hot: true
}
};
复制代码
worker.js前端
if (module.hot) {
module.hot.accept('./print.js', function() {
console.log('Accepting the updated printMe module!');
printMe();
})
}
复制代码
注意点vue
CSS 的模块热更新,借助于 style-loader
。java
false
,全部文件代码都没有反作用「反作用」的定义是,在导入时会执行特殊行为的代码,而不是仅仅暴露一个 export 或多个 export。举例说明,例如 polyfill,它影响全局做用域,而且一般不提供 export。有反作用的不能tree sharking!webpack
注意,任何导入的文件都会受到 tree shaking 的影响。这意味着,若是在项目中使用相似 css-loader
并导入 CSS 文件,则须要将其添加到 side effect 列表中,以避免在生产模式中无心中将它删除。git
{
"name": "your-project",
"sideEffects": [
"./src/some-side-effectful-file.js",
"*.css"
]
}
复制代码
还能够在
module.rules
配置选项 中设置"sideEffects"
es6
从 webpack 4 开始,也能够经过 "mode"
配置选项轻松切换到压缩输出,只需设置为 "production"
。
webpack.config.js
module.exports = {
// ...
mode: "production"
};
复制代码
import
和 export
)package.json
文件中,添加一个 sideEffects
入口UglifyJSPlugin
, webpack4 开始能够设置 mode
: "production"
来代替)生产环境,在使用
uglifyjs-webpack-plugin
时,你必须提供sourceMap:true
选项来启用 source map 支持。鼓励你在生产环境中启用 source map,由于它们对调试源码(debug)和运行基准测试(benchmark tests)颇有帮助。
eval
- 映射到转换后的代码eval-source-map
- 行数可以正确映射,会映射到原始代码中。cheap-eval-source-map
- 相似 eval-source-map
,每一个模块使用 eval()
执行。这是 "cheap(低开销)" 的 source map,由于它没有生成列映射(column mapping),只是**映射行数。它会忽略源自 loader 的 source map,**而且仅显示转译后的代码,就像 eval
devtool。cheap-module-eval-source-map
- 相似 cheap-eval-source-map
,而且,在这种状况下,源自 loader 的 source map 会获得更好的处理结果。然而,loader source map 会被简化为每行一个映射(mapping)。这些选项一般用于生产环境中:
(none)
(省略 devtool
选项) - 不生成 source map。这是一个不错的选择。
source-map
- 整个 source map 做为一个单独的文件生成。它为 bundle 添加了一个引用注释,以便开发工具知道在哪里能够找到它。
你应该将你的服务器配置为,不容许普通用户访问 source map 文件!
hidden-source-map
- 与 source-map
相同,但不会为 bundle 添加引用注释。若是你只想 source map 映射那些源自错误报告的错误堆栈跟踪信息,但不想为浏览器开发工具暴露你的 source map,这个选项会颇有用。
你不该将 source map 文件部署到 web 服务器。而是只将其用于错误报告工具。
nosources-source-map
- 建立的 source map 不包含 sourcesContent(源代码内容)
。它能够用来映射客户端上的堆栈跟踪,而无须暴露全部的源代码。你能够将 source map 文件部署到 web 服务器。
这仍然会暴露反编译后的文件名和结构,但它不会暴露原始代码。
如下选项对于开发环境和生产环境并不理想。他们是一些特定场景下须要的,例如,针对一些第三方工具。
inline-source-map
- source map 转换为 DataUrl 后添加到 bundle 中。cheap-source-map
- 没有列映射(column mapping)的 source map,忽略 loader source map。inline-cheap-source-map
- 相似 cheap-source-map
,可是 source map 转换为 DataUrl 后添加到 bundle 中。cheap-module-source-map
- 没有列映射(column mapping)的 source map,将 loader source map 简化为每行一个映射(mapping)。inline-cheap-module-source-map
- 相似 cheap-module-source-map
,可是 source mapp 转换为 DataUrl 添加到 bundle 中。许多 library 将经过与 process.env.NODE_ENV
环境变量关联,以决定 library 中应该引用哪些内容。例如,当不处于生产环境中时,某些 library 为了使调试变得容易,可能会添加额外的日志记录(log)和测试(test)。其实,**当使用 process.env.NODE_ENV === 'production'
时,一些 library 可能针对具体用户的环境进行代码优化,从而删除或添加一些重要代码。**咱们可使用 webpack 内置的 DefinePlugin
为全部的依赖定义这个变量:
NODE_ENV 属性:
- 这个变量并非 pocess.env 直接就有的,而是经过设置获得的。
- 能够经过判断这个变量区分开发环境或生产环境。
webpack.prod.js
const webpack = require('webpack');
const merge = require('webpack-merge');
const common = require('./webpack.common.js');
module.exports = merge(common, {
plugins: [
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify('production')
})
]
});
复制代码
技术上讲,NODE_ENV
是一个由 Node.js 暴露给执行脚本的系统环境变量。一般用于决定在开发环境与生产环境(dev-vs-prod)下,服务器工具、构建脚本和客户端 library 的行为。然而,与预期不一样的是,没法在构建脚本 webpack.config.js
中,将 process.env.NODE_ENV
设置为 "production"
,请查看 #2537。
例如 process.env.NODE_ENV === 'production' ? '[name].[hash].bundle.js' : '[name].bundle.js'
这样的条件语句,在 webpack 配置文件中,没法按照预期运行。
任何位于
/src
的本地代码均可以关联到 process.env.NODE_ENV 环境变量,因此如下检查也是有效的:
src/worker.js
import { cube } from './math.js';
if (process.env.NODE_ENV !== 'production') {
console.log('Looks like we are in development mode!');
}
复制代码
为何 webpack官网 里面说:
process.env.NODE_ENV === 'production' ? '[name].[hash].bundle.js' : '[name].bundle.js'`这样的条件语句,在 webpack 配置文件中,没法按照预期运行。
可是 MiniCssExtractPlugin 官网 里面能够以下使用:
const devMode = process.env.NODE_ENV !== 'production'
复制代码
process.env.NODE_ENV
值都是 undefined
。// 方式一
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify('production')
})
// 方式二
{
mode: 'production'
}
复制代码
只设置
NODE_ENV
,则会自动设置mode
。
webpack
配置文件里获取NODE_ENV
的值呢,这样就能够根据不一样的值定义相关的参数了,如上所述,答案是:corss-env
,在package.json里增长以下配置:"scripts": {
"build-cross-env":"cross-env NODE_ENV=production webpack"
}
复制代码
经过
cross-env NODE_ENV=production
,信息传递给了webpack的配置文件, src文件下面不能访问。
把代码分离到不一样的 bundle 中,而后能够按需加载或并行加载这些文件。代码分离能够用于获取更小的 bundle,以及控制资源加载优先级,若是使用合理,会极大影响加载时间。
有三种经常使用的代码分离方法:
entry
配置手动地分离代码。CommonsChunkPlugin
去重和分离 chunk。当涉及到动态代码拆分时,webpack 提供了两个相似的技术。对于动态导入,第一种,也是优先选择的方式是,使用符合 ECMAScript 提案 的 import()
语法。第二种,则是使用 webpack 特定的 require.ensure
。让咱们先尝试使用第一种……
import()
调用会在内部用到 promises。若是在旧有版本浏览器中使用import()
,记得使用 一个 polyfill 库(例如 es6-promise 或 promise-polyfill),来 shimPromise
。
worker.js
// 方式一
const _ = await import(/* webpackChunkName: "lodash" */ 'lodash');
// 方式二
import(/* webpackChunkName: "print" */ './print').then(module => {
var print = module.default;
print();
});
复制代码
注意当调用 ES6 模块的
import()
方法(引入模块)时,必须指向模块的.default
值,由于**它才是 promise 被处理后返回的实际的module
对象*。*
webpack.config.js
output: {
filename: '[name].bundle.js',
chunkFilename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist')
}
复制代码
跟整个webpack构建项目相关的,每次项目构建hash对应的值都是不一样的,即便项目文件没有作**“任何修改”**。
实际上是有修改的,由于每次webpack打包编译都会注入webpack的运行时代码,致使整个项目有变化,因此每次hash值都会变化的。
**根据不一样的入口文件(Entry)进行依赖文件解析、构建对应的chunk,生成对应的hash值。**在生产环境里把一些公共库和程序入口文件区分开,单独打包构建,采用chunkhash的方式生成hash值,那么只要不改动公共库的代码,就能够保证其hash值不会受影响。
对css使用了chunkhash以后,它与依赖它的chunk共用chunkhash,测试后会发现,css与js文件名的chunkhash值是同样的,JS
和 CSS
相互影响。
css文件最好使用contenthash。
contenthash表示由文件内容产生的hash值,内容不一样产生的contenthash值也不同。
对于图片、字体等静态资源,生成对应的文件hash值是由对应的file-loader
来计算的。
那么这些静态文件的hash值使用的是什么hash值呢?
其实就是hash
属性值。此hash非webpack每次项目构建的hash,它是***由file-loader根据文件内容计算出来的,不是webpack构建的hash***。
module.exports = function(env, argv) {
return {
mode: env.production ? 'production' : 'development',
devtool: env.production ? 'source-maps' : 'eval',
plugins: [
new webpack.optimize.UglifyJsPlugin({
compress: argv['optimize-minimize'] // 只有传入 -p 或 --optimize-minimize
})
]
};
};
复制代码
当 webpack 配置对象导出为一个函数时,能够向起传入一个"环境对象(environment)"。也能够经过 [指定环境变量](#4. 指定环境变量) 中的 cross-env NODE_ENV=production webpack"
配置
webpack --env.NODE_ENV=local --env.production --progress
复制代码
若是设置
env
变量,却没有赋值,--env.production
默认将--env.production
设置为true
。还有其余可使用的语法。有关详细信息,请查看 webpack CLI 文档。
webpack 将运行由配置文件导出的函数,而且等待 Promise 返回。便于须要异步地加载所需的配置变量。
module.exports = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve({
entry: './app.js',
/* ... */
})
}, 5000)
})
}
复制代码
iframe
iframe
,将咱们本身的应用注入到这个iframe
当中去。live.bundle.js
文件,其不但建立了iframe
标签,同时包含socket.io
的client
代码,以和webpack-dev-server
进行websocket
通信,从而完成自动编译打包、页面自动刷新的功能。inline
socket.io
的client
代码被打包进了你的包(bundle
)中,以此来与webpack-dev-server
进行websocket
通信,从而完成自动编译打包、页面自动刷新的功能。bundle
文件很臃肿。总结
Iframe mode
和Inline mode
最后达到的效果都是同样的,都是监听文件的变化,而后再将编译后的文件推送到前端,完成页面的reload
的。devServer.inline
切换两种模式,默认为inline
模式。inline mode
。记住,只设置
NODE_ENV
,则不会自动设置mode
。
只在配置中提供 mode
选项:
module.exports = {
mode: 'production'
};
复制代码
或者从 CLI 参数中传递:
webpack --mode=production
复制代码
会将
process.env.NODE_ENV
的值设为production
。启用FlagDependencyUsagePlugin
,FlagIncludedChunksPlugin
,ModuleConcatenationPlugin
,NoEmitOnErrorsPlugin
,OccurrenceOrderPlugin
,SideEffectsFlagPlugin
和UglifyJsPlugin
.
mode: production
// webpack.production.config.js
module.exports = {
+ mode: 'production',
- plugins: [
- new UglifyJsPlugin(/* ... */),
- new webpack.DefinePlugin({ "process.env.NODE_ENV": JSON.stringify("production") }),
- new webpack.optimize.ModuleConcatenationPlugin(),
- new webpack.NoEmitOnErrorsPlugin()
- ]
}
复制代码
会将
process.env.NODE_ENV
的值设为development
。启用NamedChunksPlugin
和NamedModulesPlugin
。
mode: development
// webpack.development.config.js
module.exports = {
+ mode: 'development'
- plugins: [
- new webpack.NamedModulesPlugin(),
- new webpack.DefinePlugin({ "process.env.NODE_ENV": JSON.stringify("development") }),
- ]
}
复制代码
对于用途普遍的 library,咱们但愿它可以兼容不一样的环境,例如 CommonJS,AMD,Node.js 或者做为一个全局变量。为了让你的 library 可以在各类用户环境(consumption)中可用,须要在 output
中添加 library
属性。
为了让 library 和其余环境兼容,还须要在配置文件中添加 libraryTarget
属性。这是能够控制 library 如何以不一样方式暴露的选项,output.libraryTarget
的默认选项是 var
。
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'webpack-numbers.js',
library: 'webpackNumbers'
library: 'webpackNumbers',
libraryTarget: 'umd'
},
复制代码
output.publicPath 和 devServer.publicPath 的区别
devServer.publicPath 的意义就是决定外部能以怎样的路径经过 devServer来访问构建在内存中的文件,这个字段未显式设定时,则会去沿用 output.publicPath
字段的显式值(若是output.publicPath有值的话,不然就用本身的 default
值)。output.publicPath 的意义是用来为构建的文件生成知足特定需求的前缀,并将这个前缀提供给须要的 resolver、plugin 或者其余的配置字段。
HtmlWebpackPlugin 中的filename 也会依赖于public.publicPath
参考博客-你必定能看懂的关于 devServer.publicPath、output.publicPath 和 output.path 的意义的阐述
这个字段只在 production
配置下有效,若是你去试一下的话,你会发如今 development
配置下,不管你给把它配置成什么值(前提必须是一个合规的绝对地址),它都不会对你访问开发时构建在内存中的文件产生影响,因此 development
配置下,直接忽略就行了。另外,它的 default
值是 `path.resolve(__dirname, './dist')
过去 webpack 打包时的一个取舍是将 bundle 中各个模块单独***打包成闭包***。这些打包函数使你的 JavaScript 在浏览器中***处理的更慢***。相比之下,一些工具像 Closure Compiler 和 RollupJS 能够提高(hoist)或者***预编译全部模块到一个闭包中***,提高你的代码在浏览器中的执行速度。这个插件会在 webpack 中实现以上的预编译功能。
devServer
构建的文件是在内存里的,而非你电脑的磁盘上,可是若是内存中找不到想要的文件时,devServer
会根据文件的路径尝试去电脑的磁盘上找,若是这样还找不到才会404
。开发时在内存和
contentBase
下真实的磁盘路径中存在着一样文件名的文件,那么devServer
返回的是内存的那个
historyApiFallback: {
rewrites: [
{ from: /.*/, to: path.posix.join(devConfig.assetsPublicPath, 'index.html') },
],
},
复制代码
当使用 HTML5 History API 时,任意的 404
响应均可能须要被替代为 index.html
。经过传入如下启用:
historyApiFallback: true
复制代码
经过传入一个对象,好比使用 rewrites
这个选项,此行为可进一步地控制:
historyApiFallback: {
rewrites: [
{ from: /^\/$/, to: '/views/landing.html' },
{ from: /^\/subpage/, to: '/views/subpage.html' },
{ from: /./, to: '/views/404.html' }
]
}
复制代码
决定外部可以以什么样的路径访问到构建的文件。
与 output.publicPath 的关系 [查看](#10.2 output.publicPath 和 devServer.publicPath)
这个文件放在根目录下面设置转码规则的。例如要想在代码中使用es6,就要在这个文件中配置"presets": ["es2015"]
。在项目代码中要使用jsx语法除了安装babel-plugin-transform-vue-jsx插件以外,还要配置"plugins": ["transform-runtime", "transform-vue-jsx"]
。
这个文件配置编辑器的编码风格
配置 ESLint
能够经过如下三种方式配置 ESLint:
.eslintrc 文件示例:
{
"env": {
"browser": true,
},
"globals": {
"angular": true,
},
"rules": {
"camelcase": 2,
"curly": 2,
"brace-style": [2, "1tbs"],
"quotes": [2, "single"],
"semi": [2, "always"],
"space-in-brackets": [2, "never"],
"space-infix-ops": 2,
}
}
复制代码
放在项目根目录,则会应用到整个项目;若是子目录中也包含 .eslintrc 文件,则子目录会忽略根目录的配置文件,应用该目录中的配置文件。这样能够方便地对不一样环境的代码应用不一样的规则。
想要引入三方js库,可是这些库不符合eslint规范,能够在这个文件里忽略掉,例如:
build/*.js
config/*.js
static
复制代码
这个文件用于配置不须要加入版本管理的文件,例如:
.DS_Store
node_modules/
npm-debug.log
test/unit/coverage
test/e2e/reports
selenium-debug.log
.idea
/clear
/src/modules/cache.js
复制代码
在不一样前端工具之间共享目标浏览器和Node.js版本的配置。
使用方法
(1) package.json (推荐)
{
"browserslist": [
"last 1 version",
"> 1%",
"maintained node versions",
"not dead"
]
}
复制代码
(2) .browserslistrc
# Browsers that we support
last 1 version
> 1%
maintained node versions
not dead
复制代码
Browserslist 的数据都是来自Can I Use的。若是你想知道配置语句的查询结果可使用online demo
只是多种配置方式的一种
module.exports = {
"plugins": {
"postcss-import": {},
"postcss-url": {},
// to edit target browsers: use "browserslist" field in package.json
"autoprefixer": {}
}
}
复制代码
webpack-dev-server 和 webpack-dev-middleware 里 Watch 模式默认开启。
默认配置便是咱们推荐的web最佳实践,可是你项目的最佳策略根据项目类型可能会有所不一样
默认将全部来源于node_modules
的模块分配到叫作venders
的缓存组,全部引用超过两次的模块分配到default
缓存组.