1. mode
在 webpack.config.js 文件中配置模式
// development--开发环境// production--生产环境mode: 'development'
在项目开发的时候使用开发环境,上线的时候使用生产环境
2. source map
当 webpack 打包源代码时,可能会很难追踪到 error(错误) 和 warning(警告) 在源代码中的原始位置。例如,如果将三个源文件(a.js, b.js 和 c.js)打包到一个 bundle(bundle.js)中,而其中一个源文件包含一个错误,那么堆栈跟踪就会直接指向到 bundle.js。你有时候很难知道错误来自于哪个源文件
为了更容易地追踪 error 和 warning,webpack 内置了 source maps 功能,可以将编译后的代码映射回原始源代码。如果一个错误来自于 b.js,source map 就会明确的告诉你
在webpack.config.js文件中配置 devtool 属性:
// 能够准确的捕获代码方式错误的位置// 开发中推荐使用 'cheap-module-source-map',生产环境一般不开启 sourcemapdevtool: 'cheap-module-source-map'
webpack-devtool 共提供了7种 SourceMap 模式:
项目入口文件 index.js :
class Hello {sayHello() {console.log('Hello Webpack5!!!')}}var a = new Hello()a.sayHello()
① eval:
这是devtool默认值,即使我们不设置devServer,项目也默认执行devtool:'eval'
每个 module 通过eval来执行
复制打包后 index.html 的路径进入浏览器:
发现可以追踪到源码的输出,再观察打包后的 bundle,发现它会返回一个 sourceURL
② source-map
会打包出一个单独的 sourceMap 文件,而且 bundle 最后会追加一个 sourceMappingURL,它的值为 sourceMap 文件名(两者作关联)


③ hidden-source-map
依然会打包出一个 sourceMap 文件,但是 bundle 最后不会有 sourceMappingURL,即生成的 sourceMap 文件不会和 bundle 作关联了。最重要的是,它不能在锁定源代码的行数了


④ inline-source-map
不会生成单独的 sourceMap 文件,bundle 末尾追加一个 sourceMappingURL,它的值是一个 dataURL,而且可以锁定源代码的行数。它和source-map的区别就是,它不会把 sourceMap 给打包出来

⑤ eval-source-map
不会生成单独的 sourceMap 文件,bundle 末尾追加一个 sourceMappingURL,它的值是一个 dataURL,而且可以锁定源代码的行数
每个 module 通过eval来执行,并且会生成 dataURL 形式的 sourceMap


⑥ cheap-source-map
会打包出单独的 sourceMap 文件,bundle 末尾会追加一个 sourceMappingURL,它的值为 sourceMap 文件名(两者作关联)
cheap-source-map文件中只记录源代码的行(减少文件的体积),而且也不包含loader信息
注:它可以锁定源代码的行数;但是它不能锁定通过 babel-loader 解析的代码行数

⑦ cheap-module-source-map(推荐)
会打包出单独的 sourceMap 文件,bundle 末尾会追加一个 sourceMappingURL,它的值为 sourceMap 文件名(两者作关联)
注:它可以锁定源代码的行数,而且它也能锁定通过 babel-loader 解析的代码行数

source-map、cheap-source-map、cheap-module-source-map的区别:
- 相同点:会打包出单独的 sourceMap 文件,bundle 末尾会追加一个 sourceMappingURL,它的值为 sourceMap 文件名(两者作关联)
- 不同点:
source-map文件中会记录源代码代码的行和列(文件体积较大);cheap-source-map文件中只记录源代码的行(减少文件的体积),而且也不包含loader信息;cheap-module-source-map文件中只记录源代码的行(减少文件的体积),而且包含loader信息;
要注意的是,生产环境我们一般不会开启sourcemap功能,主要有两点原因:
1. 通过 bundle 和 sourcemap 文件,可以反编译出源码————也就是说,线上产物有 soucemap 文件的话,就意味着有暴漏源码的风险
2. 我们可以观察到,sourcemap 文件的体积相对比较巨大,这跟我们生产环境的追求不同,生产环境追求更小更轻量的 bundle
3. watch mode
在每次编译代码时,手动运行 npx webpack 会显得很麻烦。
我们可以在 webpack 启动时添加 “watch” 参数。如果其中一个文件被更新,代码将被自动重新编译,不必再去手动运行整个构建
npx webpack —watch
唯一的缺点是,为了看到修改后的实际效果,你需要刷新浏览器。如果浏览器也能够自动刷新就好了,因此我们会通过 webpack-dev-server 实现此功能
4. devServer
开发环境下,我们往往需要启动一个 web 服务,方便我们模拟一个用户从浏览器中访问我们的 web 服务,读取我们的打包产物,以观测我们的代码在客户端的表现。 webpack 内置了这样的功能:webpack-dev-server ,它提供了一个基本的 web 服务,具有实时重新加载功能
安装依赖:
npm i webpack-dev-server
配置 devServer ( webpack.config.js ) ```javascript const path = require(‘path’)
module.exports = { devServer: { static: path.resolve(__dirname, ‘./dist’), compress: true, port: 8080, headers: { ‘x-token’: ‘abc123’ } } }
当`static`配置好后,输入命令 `npx webpack-dev-server --open`,当项目代码改动时,会自动进行打包并且自动进入浏览器进行查看,执行这个命令它会开启服务器,提供网络路径供你访问<br /><br />浏览器也会提示:<br />> 提示:> webpack-dev-server 在编译之后不会写入到任何输出文件。而是将 bundle 文件保留在内存中,然后将它们 serve 到 server 中,就好像它们是挂载在 server 根路径上的真实文件一样。<a name="fhH1O"></a>#### ① static`static: path.resolve(__dirname, './dist')`告知 webpack devServer,将 dist 目录下的文件作为 web 服务的根目录,修改代码后实现浏览器热更新(结合 npx webpack-dev-server 命令开启本地服务器一起使用)<a name="OvXMr"></a>#### ② compress是否开启`gzip`压缩,对应静态资源请求的响应头里的`Content-Encoding: gzip`。保证从服务器传递给浏览器的文件是压缩的,提高传输效率。不开启的话,则从服务器传递给浏览器的文件是不压缩的<br /> <a name="jil2j"></a>#### ③ port配置服务的端口号以上是`DevServer`的基本配置,除此以外,`devServer.proxy`基于强大的中间件`http-proxy-middleware`实现的,因此它支持很多的配置项,我们基于此,可以做应对绝大多数开发场景的定制化配置:<a name="kWhXi"></a>#### ④ headers有些场景需求下,我们需要为所有响应添加 headers(响应头),来对资源的请求和响应打入标志,以便做一些安全防范,或者方便发生异常后做请求的链路追踪<br /> <a name="aSUfQ"></a>#### ⑤ 开启代理 -- proxy我们打包出的`js bundle`里有时会含有一些对特定接口的网络请求 (ajax/fetch) 。要注意,此时客户端地址是在 [http://localhost:3000/](http://localhost:3000/) 下,假设我们的接口来自 [http://localhost:4001/](http://localhost:4001/) ,那么毫无疑问,此时控制台里会报错并提示你跨域。 如何解决这个问题? 在开发环境下,我们可以使用`devServer`自带的`proxy`功能:```javascriptmodule.exports = {//...devServer: {proxy: {'/api': {target: 'http://localhost:4001'}}}}
现在,对 /api/users 的请求会将请求代理到 http://localhost:4001/api/users 。 如果不希望传递 /api,则需要重写路径:
'/api': {target: 'http://localhost:4001',pathRewrite: { '^/api': ' ' },}
默认情况下,将不接受在 HTTPS 上运行且证书无效的后端服务器。 如果需要,可以这样修改配置:
'/api': {target: 'http://localhost:4001',pathRewrite: { '^/api': ' ' },secure: false}
⑥ https
如果想让我们的本地 http 服务变成 https 服务,只需要这样配置:
module.exports = {//...devServer: {proxy: {'/api': {target: 'http://localhost:4001'}},https: true}}
注意:此时我们访问 http://localhost:port 是无法访问我们的服务的,我们需要在地址栏里加前缀:https注意:由于默认配置使用的是自签名证书,所以有得浏览器会告诉你是不安全的,但我们依然可以继续访问它
⑦ http2
如果想要配置 http2( http2 默认自带 https 自签名证书,所以我们仍然可以通过 https 配置项来使用自己的证书),那么直接设置:
module.exports = {//...devServer: {proxy: {},// https: true,http2: true}}
⑧ historyApiFallback
当我们开发一个 SPA (单页面)应用时,如果使用的路由模式为 history,那么当我们刷新页面的时候会报错。例如当路由到 /some 时(可以直接在地址栏里输入),会发现此时刷新页面后,控制台报错。
GET http://localhost:3000/some 404 (Not Found)
此时打开 network,刷新并查看,就会发现问题所在——浏览器把这个路由当作了静态资源地址去请求,然而我们并没有打包出 /some 这样的资源,所以这个访问无疑是404的。 如何解决它? 这种时候,我们可以通过配置来提供页面代替任何404的静态资源响应:
module.exports = {//...devServer: {historyApiFallback: true}}
此时重启服务刷新后发现请求变成了 index.html
⑨ host
开发服务器主机。如果你在开发环境中起了一个 devServe 服务,并期望其他人也能访问到它,你只需要配置:
module.exports = {//...devServer: {host: '0.0.0.0'}}
这时候,如果其他人跟你处在同一局域网下,就可以通过局域网ip来访问你的服务了
⑩ HMR
模块热替换(HMR - hot module replacement) 功能会在应用程序运行过程中,替换、添加或删除模块,而无需重新加载整个页面。换言之,就是实现页面的部分刷新
启用 webpack 的 热模块替换 特性,需要配置devServer.hot参数:
module.exports = {//...devServer: {// 注:webpack 默认 hot: truehot: true}}
- CSS HMR:
HMR 加载 CSS,如果你配置了style-loader,那么现在已经同样支持样式文件的热替换功能了。这是因为style-loader的实现使用了module.hot.accept,在 CSS 依赖模块更新之后,会对 style 标签打补丁,从而实现了这个功能。
module.exports={module: {rules: [{test: /\.css$/,use: ['style-loader', 'css-loader']}]}}
- JS HMR:
除了需要配置devServer.hot参数外,还要使用module.hot.accept。例:
style.css:
.squre {height: 50px;width: 50px;background-color: red;margin-bottom: 10px;}
input.js:
const ipt = document.createElement('input')
ipt.placeholder = '0001'
document.body.appendChild(ipt)
index.js:
import '../css/style.css'
import './input.js'
const outDiv = document.createElement('div')
const btn = document.createElement('button')
btn.innerHTML = '点击添加DIV'
outDiv.appendChild(btn)
btn.addEventListener('click', () => {
const inDiv = document.createElement('div')
inDiv.classList.add('squre')
outDiv.appendChild(inDiv)
})
document.body.appendChild(outDiv)
// js 模块实现热替换
if(module.hot) {
module.hot.accept('./input.js', () => {})
}
⑪ liveReload
热加载:文件更新时,自动刷新我们的服务和页面 。新版的 webpack-dev-server 默认已经开启了热加载的功能。 它对应的参数是devServer.liveReload,默认为 true 。 注意,如果想要关掉它,要将liveReload设置为 false 的同时,也要设置hot: false
module.exports = {
//...
devServer: {
liveReload: false
}
}
5. eslint
eslint 是用来扫描我们所写的代码是否符合规范的工具。 往往我们的项目是多人协作开发的,我们期望统一的代码规范,这时候可以让 eslint 来对我们进行约束。 严格意义上来说,eslint 配置跟 webpack 无关,但在工程化开发环境中,它往往是不可或缺的。
① 安装 eslint
npm i eslint
② 生成 eslint 配置文件
npx eslint —init
它会帮助我们生成了个配置文件.eslintrc.js(我们自己选择生成 JS 文件),这样我们就完成了eslint 的基本规则配置
运行npx eslint './src',就可以检测代码规范了。
eslint配置文件里的配置项含义如下:
- env 指定脚本的运行环境。每种环境都有一组特定的预定义全局变量。此处使用的 browser 预定义了浏览器环境中的全局变量,es6 启用除了 modules 以外的所有 ECMAScript 6 特性(该选项会自动设置 ecmaVersion 解析器选项为 6)。
- globals 脚本在执行期间访问的额外的全局变量。也就是 env 中未预定义,但我们又需要使用的全局变量。
- extends 检测中使用的预定义的规则集合。
- rules 启用的规则及其各自的错误级别,会合并 extends 中的同名规则,定义冲突时优先级更高。
- parserOptions ESLint 允许你指定你想要支持的 JavaScript 语言选项。ecmaFeatures 是个对象,表示你想使用的额外的语言特性,这里 jsx 代表启用 JSX。ecmaVersion 用来指定支持的 ECMAScript 版本 。默认为 5,即仅支持 es5,你可以使用 6、7、8、9 或 10 来指定你想要使用的 ECMAScript 版本。你也可以用使用年份命名的版本号指定为 2015(同 6),2016(同 7),或 2017(同 8)或 2018(同 9)或 2019 (same as 10)。上面的 env 中启用了 es6,自动设置了ecmaVersion 解析器选项为 6。 plugins plugins 是一个 npm 包,通常输出 eslint 内部未定义的规则实现。rules 和 extends 中定义的规则,并不都在 eslint 内部中有实现。比如 extends 中的 plugin:react/recommended,其中定义了规则开关和等级,但是这些规则如何生效的逻辑是在其对应的插件
react中实现的。
