2.7 使用 WebpackDevServer 提升开发效率
会将打包的东西放到内存中来提升打包速度,因此出口文件夹dist中没有文件
目前为止,我们修改文件的内容后,想要再次打包,智能再次使用 npm run bundle
命令,这样的操作未免有些重复繁琐。因此我们可以将 package.json
文件中的 scripts
修改一下:
"scripts": {
- "bundle": "webpack"
"watch": "webpack --watch"
}
加 --watch
的目的是:监听打包的文件,如果打包的文件发生变化,webpack就会重新打包。
加 --watch
参数仅仅只是能监听文件变化,并不能做到如第一次运行命令后直接打开打包好后的文件,且文件内容发生变化的时候,也不能直接刷新页面,需要手动刷新。这时候我们就可以借助 WebpackDevServer
来帮助我们实现这些功能。
- 安装
npm i webpack-dev-server -D
- 配置
// webpack.config.js
module.exports = {
// 最简单的 webpackDevServer 配置,在 ./dist 文件夹写开启一个服务
devServer: {
contentBase: './dist',
open: true // 自动打开浏览器
}
}
- 写脚本
// package.json
"scripts": {
"start": "webpack-dev-server"
}
做了以上配置,第一次运行命令后,会自动的打开浏览器,且修改文件后,浏览器会自动刷新,这样就大大提高了开发效率。
除此之外 webpackDevServer 还能做跨域的配置:
// webpack.config.js
module.exports = {
// 最简单的 webpackDevServer 配置,在 ./dist 文件夹写开启一个服务
devServer: {
contentBase: './dist',
open: true, // 自动打开浏览器
proxy: {
'/api': 'https://www.baidu.com' // 访问 api 这个接口,就会自动转到 http://www.baidu .com 上
}
}
}
更多的 webpackDevServer 配置请参考 官网
- 拓展:自己实现一个 webpackDevServer (express实现)
在工程文件夹下新建一个 server.js
文件,通过 node server.js
命令打包项目
const express = require('express');
const webpack = require('webpack');
const webpackDevMiddleware = require('webpack-dev-middleware');
const config = require('./webpack.config.js');
const complier = webpack(config);
const app = express();
app.use(webpackDevMiddleware(complier, {
publicPath: config.output.publicPath
}))
app.listen(3000, () => {
console.log('3000 is running!');
})
以上自己实现的 webpackDevServer,是在 node 中使用 webpack,它不能自动打开文件,不能自动刷新,仅供了解 webpackDevServer ,实际项目中多使用 webpackDevServer。
2.8 Hot Module Replacement 热模块替换
有这样的情况:我们修改 css 样式后,不希望浏览器刷新后才看到修改过的样式,修改样式后,直接就可以看到修改样式后的界面。这时候我们就要借助 Hot Module Replacement 来实现这个功能。
- 引入插件
// webpack.config.js
const webpack = require('webpack');
module.exports = {
plugins: [
new webpack.HotModuleReplacementPlugin()
]
}
- 配置
// webpack.config.js
module.exports = {
devServer: {
contentBase: './dist',
open: true,
hot: true, // 开启热模块替换
hotOnly: true // 即使热模块替换不生效,浏览器也不自动刷新
}
}
这样就可以直接更新样式,并不会影响 页面 加载出来的内容。
不同的 JS 模块之间,要实现 HMR,需要写一下代码
// js 模块,引入了 number.js 模块
// 先判断是否开启了 HMR,再判断模块文件是否改变
if(module,hot) { // 支不支持 HMR
module.hot.accrpt('./number', () => {
munber();
})
}
之所以 CSS 不用这么写,是因为 css-loader
在底层写好了这段代码。vue-cli
、react-create-app
也封装了,所以我们不用重新写。但在实际项目中,可能会遇见一些很少见的类型,这时候就需要我们自己收懂写 热模块替换 的代码了。
2.9 借助babel处理ES6语法
在使用 ES6+ 语法写项目时,有些低版本浏览器根本识别不了 ES6 语法导致报错,所以这时候需要借助 babel 将这些语法转换成 ES5 语法。
- 安装babel
## 安装 Babel
npm install --save-dev babel-loader @babel/core
## 安装 babel/preset-env,这里面是一些 ES6 到 ES5 的翻译规则
npm install @babel/preset-env --save-dev
- 配置
// webpack.config.js
module.exports = {
rules: [
{
test: /\.js$/,
exclude: /node_modules/, // 不翻译 node_modules 文件夹下的js文件
loader: "babel-loader",
options: {
"presets": ["@babel/preset-env"]
}
}
]
}
以上配置完成以后,babel已经可以做一部分的语法翻译了,例如箭头函数、const、let…,然而像promise、Array.map之类的ES6语法就不会翻译,所以以上配置只是翻译了一部分,还需做更多配置。
我们可以借助 polyfill 来给低版本浏览器加上一些 ES6 的新的对象或方法
- 安装
npm install --save @babel/polyfill
- 配置
// webpack.config.js
module.exports = {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader',
options: {
presets: [['@babel/preset-env', {
useBuiltins: 'usage' // 按需注入,可有效减小目标文件体积
}]]
}
}
]
}
然后只需在写了 ES6 语法的文件中引入 @babel/polyfill
即可:
import "@babel/polyfill";
const arr = [
new Promise(),
new Promise()
]
arr.map(item => {
console.log(item)
})
使用 babel 后虽然可以 es6 转 es5,可是也使得目标文件变得很大,这时候可以通过设置项目运行浏览器的最低版本来判断需不需要转换:
// webpack.config.js
module.exports = {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader',
options: {
presets: [['@babel/preset-env', {
targets: {
edge: "17",
firefox: "60",
chrome: "67",
safari: "11.1",
},
useBuiltins: 'usage'
}]]
}
}
]
}
如果是开发一个第三方模块的时候,使用 @babel/polyfill
就很可能污染全局环境,因为它是通过全局变量的方式注入代码。
我们可以借助 @babel/plugin-transform-runtime
来解决这个问题,它会通过闭包的形式注入,不会污染全局环境,但必须强调的是如果你只是写简单的业务代码,只需做上面的配置,如果你做的类库、第三方包,不想@babel/polyfill污染全局环境时,才需做下面的配置
- 安装
npm install --save-dev @babel/plugin-transform-runtime
npm install --save @babel/runtime
- 配置
// webpack.config.js
module.exports = {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader',
options: {
plugins: [
[
"@babel/plugin-transform-runtime",
{
"absoluteRuntime": false,
"corejs": false,
"helpers": true,
"regenerator": true,
"useESModules": false
}
]
]
}
}
]
}
用了 ES6 语法的 JS 文件中不用引入 import "@babel/polyfill"