Alan

此刻想举重若轻,之前必要负重前行

webpack的devServer热更新以及HMR局部热更新

DevServer

DevServer可以起一个本地服务并且实现代码的热更新。可以省去我们每次更新代码后重启服务额操作。

npm i webpack-dev-server -D

配置文件

package.json

"scripts": {
    "build": "webpack",
    "start": "webpack-dev-server"
},

webpack.config.js

devServer: {
    contentBase: './dist',
    open: true, // 自动打开浏览器
    port: 3001, // 服务器端口号
},

我们之后只需要使用npm run start就可以把服务跑起来了,之后只要改动代码就会自动更新了,开发效率提高了很多有没有😝

devServer更多配置内容

HMR实现局部热更新

HMR(Hot Module Replacement)

当我们更改了部分文件后,我们发现webpack-dev-server帮我们重新渲染所有内容,假如我只改动了一小部分,只想更新这一部分内容就可以用了使用HMR来实现了。

说再多不如看一个例子

index.js

import './index.css';

var root = document.getElementById('root');

root.innerHTML = '<button id="btn">add new block</button>';

document.getElementById('btn').onclick = function() {
  var newBlock = document.createElement('p');
  newBlock.innerHTML = 'new Block';
  root.append(newBlock);
}

index.css

p {
  width: 100px;
}
p:nth-child(6) {
  background: red;
}

这里例子就是点击按钮添加一个p元素,第6个p元素显示为红色背景

这个时候我觉得红色不好看,想换成黄色,修改,保存一气呵成。

image-20200518122346853

结果webpack-dev-server给我全部重新渲染了,我还要再点6下才能看到效果,这里如果是1000(虽然不太可能)呢,那我岂不是要点1000下😱。

这个时候配置HRM就可以轻松解决这个问题了。

只需要再webpack.config.js中配置

devServer: {
    contentBase: './dist', // Tell the server where to serve content from
    open: true, // 自动打开浏览器
    port: 3001, // 服务器端口号
    hot: true, // 开启HRM
},

搞定😎,不过这里由于有css-loader帮我们做了一些更新的任务,所以我们并没有写过多的代码。那如果没有css-loader处理那怎么办?下面看一下具体配置。

import Counter from './counter'
import Number from './number'

Counter();
Number();
function Counter() {
  var root = document.getElementById('root');
  var counter = document.createElement('div');
  counter.innerHTML = 0;
  counter.onclick = function () {
    counter.innerHTML = parseInt(counter.innerHTML, 10) + 1;
  }
  root.append(counter);

}

export default Counter;
function Number() {
  var root = document.getElementById('root');
  var data = document.createElement('div');
  data.setAttribute('id', 'number');
  data.innerHTML = '2000';
  root.append(data);
}

export default Number;

可以看到当我一改变number,counter中的状态又重新渲染变成0了😩。

这里我们就要通过一部分代码来处理一下了(css-loader就是帮我们完成了这部分工作)

import Counter from './counter'
import Number from './number'

Counter();
Number();

if (module.hot) { // 如果开启HMR
  module.hot.accept('./number.js', () => {
    var root = document.getElementById('root');
    root.removeChild(document.getElementById('number'));
    console.log('number is updated');
    Number();
    // 一旦number.js文件改变,进行一系列处理
  })
}

处理ES6语法

我们先用ES6写一些代码

const name = 'Alan';
const list = [1, 8, 4, 6];

const resultList = list.filter(item => item > 5);

new Promise(() => {
  console.log('666');
})

console.log(resultList);

npm run build打包看一下chrome效果

image-20200520142019038

奈何这个世界上还有IE这种东西😣,IE上看一下效果

image-20200520142207197

可以看一下报错的地方:

eval("const name = 'Alan';\r\nconst list = [1, 8, 4, 6];\r\n\r\nconst resultList = list.filter(item => item > 5);\r\n\r\nnew Promise(() => {\r\n  console.log('666');\r\n})\r\n\r\nconsole.log(resultList);\r\n\n\n//# sourceURL=webpack:///./src/index.js?");

看来IE是识别不了ES6语法的,那我们这里就要借助babel处理了。

npm install --save-dev babel-loader @babel/core @babel/preset-env

@babel/preset-env把ES6转化为ES5

@babel/corebabel核心内容

配置webpack

{
    test: /\.js$/,
        exclude: /node_modules/, // 不对node_modules下的js文件处理
        loader: 'babel-loader',
        options: {
            presets: ['@babel/preset-env']
        }
},

再打包看一看

image-20200520143428061

打包后的index.js文件

var name = 'Alan';
var list = [1, 8, 4, 6];
var resultList = list.filter(function (item) {
  return item > 5;
});
new Promise(function () {
  console.log('666');
});
console.log(resultList);

虽然处理了一些ES6语法(箭头函数,const),但是像filter和Promise还是没有处理的。

这里就要使用@babel-polyfill,它会模拟一个ES5环境

npm install --save @babel/polyfill

安装后在文件开头引入就可以了

import '@babel/polyfill';

const name = 'Alan';
const list = [1, 8, 4, 6];

const resultList = list.filter(item => item > 5);

new Promise(() => {
  console.log('666');
})

console.log(resultList);

再次打包运行

image-20200520144125415

虽然效果完成了,但是对比一下没使用@babel/polyfill和使用了@babel/polyfill打包后的大小😮

image-20200520144511050

就引入了一个@babel-polyfill,体积就增加了这么多?

其实是因为@babel-polyfill模拟了所有的ES5环境,而我们这里只使用了Promise和filter,所以我们可以通过配置useBuiltIns让它只模拟我们使用到的。

配置了useBuiltIns后,它是会自动帮我们引入@babel/polyfill所以这里我们无需再引入。

{
    test: /\.js$/,
        exclude: /node_modules/, // 不对node_modules下的js文件处理
         loader: 'babel-loader',
          options: {
              presets: [['@babel/preset-env', {
                  useBuiltIns: 'usage'
              }]]
          }
},
main.js   68.6 KiB    main  [emitted]  main

再看一下打包后的文件小了很多有没有?

babel的配置是可以单独放在.babelrc文件中的,直接将options中的内容放到.babelrc目录下即可

{
  "presets": [["@babel/preset-env", {
    "useBuiltIns": "usage"
  }]]
}

使用@babel/preset-react打包React文件

npm install --save-dev @babel/preset-react
{
  "presets": [["@babel/preset-env", {
    "useBuiltIns": "usage"
  }],
  "@babel/preset-react"
  ]
}
import React, { Component } from 'react';
import ReactDom from 'react-dom';

class App extends Component {
  render() { 
    return (
      <div>Hello React</div>
    );
  }
}

ReactDom.render(<App />, document.getElementById('root'));

这里有一点需要注意一下,presets顺序是从后往前的,和css-loader一样,也就是js文件是先被@babel/preset-react处理的再被@babel/preset-env处理的。

总结使用的babel

@babel/preset-react处理react的jsx语法

@babel-polyfill处理低版本浏览器无法处理的语法,类似Promise、Array.from、Object.assign

@babel/preset-env把ES6转化为ES5

@babel/corebabel核心内容

useBuiltIns:usage按需引入

评论