webpack通俗易懂(四)
- 1,source-map
- 2,devServer
- 3, https
- 4,
- 5,
辛苦编写良久,还望点赞鼓励呦~
1,提高开发效率,完善团队开发规范
1.1,source-map
之前我们通过webpack, 将我们的源码打包成了 bundle.js, 实际上客户端(浏览器)读取的是打包后的 bundle.js, 当浏览器执行代码报错的时候,报错的信息自然也是 bundle 的内容。我们如何将报错信息(bundle错误的语句及其所在行列)映射到源码上,这时候我们就需要用 source-map
了,webpack已经内置了sourcemap功能,我们只需要通过简单的配置,就可以开启它
module.exports = {
// 开启 source map
// 开发中推荐使用 'source-map' // 生产环境一般不开启 sourcemap
devtool: 'source-map'
}
当我们执行打包命令后,我们发现bundle的最后一行总是会多出一个注释,指向打包出的bundle.map.js(sourcemap文件)。 sourcemap文件用来描述源码文件和 bundle 文件的代码位置映射关系。基于它,我们将bundle文件的错误信息映射到源码文件上。
除开’source-map’外,还可以基于我们的需求设置其他值,webpack——devtool一共提供了7种SourceMap模式:
- eval:每个module会封装到 eval 里包裹起来执行,并且会在末尾追加注释 //@ sourceURL.
- source-map:生成一个SourceMap文件
- hidden-source-map:和 source-map一样,但不会在 bundle 末尾追加注释
- inline-source-map:生成一个 DataUrl 形式的 SourceMap 文件.
- eval-source-map:每个module会通过eval()来执行,并且生成一个DataUrl形式的 SourceMap
- cheap-source-map:生成一个没有列信息(column-mappings)的SourceMaps文 件,不包含loader的 sourcemap(譬如 babel 的 sourcemap)
- cheap-module-source-map:生成一个没有列信息(column-mappings)的SourceMaps文件,同时 loader 的 sourcemap 也被简化为只包含对应行的。
要注意的是,生产环境我们一般不会开启sourcemap功能,主要有两点原因:
- 通过bundle和sourcemap文件,可以反编译出源码————也就是说,线上产物有soucemap文件的话,就意味着有暴漏源码的风险。
- 我们可以观察到,sourcemap文件的体积相对比较巨大,这跟我们生产环境的追求不同(生产环境追求更小更轻量的bundle)。
有时候我们期望能第一时间通过线上的错误信息,来追踪到源码位置,从而快速解决掉bug以减轻损失。但又不希望sourcemap文件报漏在生产环境,有什么比较好的方案呢?
1.2,devServer
开发环境下,我们往往需要启动一个web服务,方便我们模拟一个用户从浏览器中访问我们的web服务,读取我们的打包产物,以观测我们的代码在客户端的表现。webpack内置了这样的功能,我们只需要简单的配置就可以开启它。
安装 devServer
yarn add -D webpack-dev-server
devServer.proxy基于强大的中间件 http-proxy-middleware 实现的,因此它支持很多的配置项,我们基于此,可以做应对绝大多数开发场景的定制化配置。
基础使用:
const path = require('path')
devServer: {
static: {
directory: path.join(__dirname, 'dist')
}, // 默认是把dist目录作为web服务的根目录
compress: true, // 可选择开启gzips压缩功能,对应静态资源请求的响应头里的Content-Encoding: gzip
port: 3000, // 端口号
},
为了方便,我们配置一下工程的脚本命令,在package.json的scripts里:
{
"scripts": {
"dev": "webpack serve --mode development"
}
}
- 1.2.1, 添加响应头
有些场景需求下,我们需要为所有响应添加headers, 来对资源的请求和响应打入标志,以便做一些安全防范,或者方便发生异常后做请求的链路追踪。比如:
module.exports = {
devServer: {
headers: {
'X-Token': 'ZlcjLCe+sAW1S4QC8Z'
}
}
}
- 1.2.2, 开启代理
我们打包出的js bundle里有时会含有一些对特定接口的网络请求(ajax/fetch). 要注意,此时客户端地址是在 http://localhost:3000/ 下,假设我们的接口来自 http://localhost:4001/,那制台就会报错跨域,在开发环境下,我们可以使用devServer自带的proxy功能来解决这个问题。
我们新搭建一个服务,在当前项目下新建 server.js:
const http = require('http');
const app = http.createServer((req, res) => {
if (req.url === '/api/user') {
res.end('hello node')
}
})
app.listen(4001, 'localhost', () => {
console.log('localhost listening on 4001')
})
再次打开一个终端执行node server.js,启动服务
浏览器输入:
下面我们开始请求,请求我们可以使用浏览器自带的方法fetch,这个方法返回的是一个promise
fetch('/api/user')
.then(val => val.text()) // res.text()可以把返回的结果变成文本)
.then(res => {
console.log(res)
})
如何解决上面跨域的问题呢:
module.exports = {
//...
devServer: {
proxy: {
'/api': 'http://localhost:4001'
}
}
}
// 如果用户在地址栏一旦请求了一个资源叫 /api 的话,我们就给他指向到 http://localhost:4001 服务器上去
现在对 /api/user 的请求会将请求代理到 http://localhost:4001/api/user。如果不希望传递 /api, 则可以重写路径:
proxy: {
'/api': {
target: 'http://localhost:4001',
pathRewrite: {
'^/api': '/' // 这里可以是'/'也可以是''
}
}
}
- 1.2.3,https
默认情况下,将不接受在 HTTPS 上运行且证书无效的后端服务器。如果想让我们的本地http服务改为https服务,可以这样配置:
devServer: {
https: true
}
重新启动服务:npx webpack, 我们发现访问http://localhost:port是无法访问我们的服务的,我们需要在地址栏里加前缀: https,注意: 由于默认配置使用的是自签名证书,所以有的浏览器会告诉你是不安全的,但我们依然可以继续访问它。当然我们也可以提供自己的证书:
module.exports = {
devServer: {
https: {
cacert: './server.pem',
pfx: './server.pfx',
key: './server.key',
cert: './server.crt',
passphrase: 'webpack-dev-server',
requestCert: true,
}
}
}
- 1.2.4, http2
我们也可以不使用https,可以使用http2
如果想要配置http2,那么直接设置:
devServer: {
http2: true
}
http2默认自带https自签名证书,当然我们仍然可以通过https配置项来使用自己的证书
- 1.2.5, historyApiFallback
如果我们的应用是个SPA(单页面应用),当路由到/some 时(可以直接在地址栏里输入),会发现此时刷新页面后,控制台会报错:
GET http://localhost:3000/some 404 (Not Found)
此时打开network,刷新并查看,就会发现问题所在———浏览器把这个路由当作了静态资源的地址去请求,然而我们并没有打包出/some这样的资源,所以这个访问无疑是404的。如何解决它?我们可以通过配置来提供页面代替任何404的静态资源响应:
module.exports = {
//...
devServer: {
historyApiFallback: true
}
}
此时重启服务刷新后发现请求变成了index.html, 当然, 在多数业务场景下,我们需要根据不同的访问路径定制替代的页面,这种情况下,我们可以使用rewrites这个配置项。 类似这样:
module.exports = {
//...
devServer: {
historyApiFallback: {
rewrites: [
{ from: /^\/$/, to: '/views/landing.html' },
{ from: /^\/subpage/, to: '/views/subpage.html' },
{ from: /./, to: '/views/404.html' },
]
}
}
}
- 1.2.6, 开发服务器主机
如果我们在开发环境中起了一个devserve服务,并希望在同一局域网下的同事也能访问它,只需要配置:
devServer: {
host: '0.0.0.0'
}
这时候,如果我们的同事跟我们处在同一局域网下,就可以通过局域网ip来访问我们的服务 啦
1.3, 模块热替换与热加载
- 模块热替换
模块热替换(HMR - hot module replacement)功能会在应用程序运行过程中,
替换、添加或删除 模块,而无需重新加载整个页面
module.exports = {
//...
devServer: {
hot: true,
},
}
HMR 加载样式,如果我们配置了style-loader,那么现在已经同样支持样式文件的
热替换功能了
module.exports = {
module: {
rules: [
{
test: /\.css$/,
use: ['style-loader', 'css-loader'],
},
],
},
}
这是因为style-loader的实现使用了module.hot.accept,在CSS依赖模块更新之后,
会对 style 标签打补丁。从而实现了这个功能。
热加载(文件更新时,自动刷新我们的服务和页面) 新版的webpack-dev-server
默认已经开启了热加载的功能。 它对应的参数是devServer.liveReload,默认为
true。 注意,如果想要关掉它,要将liveReload设置为false的同时,也要关掉
hot
module.exports = {
//...
devServer: {
liveReload: false, //默认为true,即开启热更新功能。
},
};
1.4,eslint
eslint是用来扫描我们所写的代码是否符合规范的工具。往往我们的项目是多人协作开发的,我们期望统一的代码规范,这时候可以让eslint来对我们进行约束。严格意义上来说,eslint配置跟webpack无关,但在工程化开发环境中,它往往是不可或缺的
yarn add eslint -D
配置eslint,只需要在根目录下添加一个 .eslintrc
文件(或者.eslintrc.json, .js等)。 当然,我们可以使用eslint工具来自动生成它:
npx eslint --init
我们可以看到控制台里的展示:
并生成了一个配置文件(.eslintrc.json),这样我们就完成了eslint的基本规则配置。 eslint配置文件里的配置项含义如下:
-
- env 指定脚本的运行环境。每种环境都有一组特定的预定义全局变量。此处使用的 browser 预定义了浏览器环境中的全局变量,es6 启用除了 modules 以外的所有 ECMAScript 6 特性(该选项会自动设置 ecmaVersion 解析器选项为 6)。
-
- globals 脚本在执行期间访问的额外的全局变量。也就是 env 中未预定义,但我
们又需要使用的全局变量。
- globals 脚本在执行期间访问的额外的全局变量。也就是 env 中未预定义,但我
-
- extends 检测中使用的预定义的规则集合。
-
- rules 启用的规则及其各自的错误级别,会合并 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’ 中实现的
- parserOptions ESLint 允许你指定你想要支持的 JavaScript 语言选项。
修改配置文件:
{
"rules": {
"no-console": "warn" // 我们在rules里自定义我们的约束规范
}
}
我们通过命令来让elisnt检测代码——在我们的package.scripts里添加一个脚本命令: