在编写代码时经常会发现热更新失效,那是因为相应的 loader 没有去实现热更新,我们看看如何简单实现一个热更新。
import moduleA from "./moduleA"; if (module.hot) { module.hot.accept('./moduleA.js', function() { console.log("moduleA 支持热更新拉"); console.log(moduleA()); }) }代码解释:
我们引人自己编写的一个普通 ES6 语法模块,假如我们想要实现热更新就必须手动监听相关文件,然后当接收到更新回调时,主动调用。
还记得上面讲 webpack 打包后的源码分析吗,webpack 给模块都建立了一个 module 对象,当你开启模块热更新时,在初始化 module 对象时增加了(源码经过删减):
function hotCreateModule(moduleId) { var hot = { active: true, accept: function(dep, callback){ if (dep === undefined) hot._selfAccepted = true; else if (typeof dep === "function") hot._selfAccepted = dep; else if (typeof dep === "object") for (var i = 0; i < dep.length; i++) hot._acceptedDependencies[dep[i]] = callback || function() {}; else hot._acceptedDependencies[dep] = callback || function() {}; } } }module 对象中保存了监听文件路径和回调函数的依赖表,当监听的模块发生变更后,会去主动调用相关的回调函数,实现手动热更新。
[注意] 所有编写的业务模块,最终都会被 webpack 转换成 module 对象进行管理,如果开启热更新,那么 module 就会去增加 hot 相关属性。这些属性构成了 webpack 编译运行时对象。
编译 ES6显然大家都知道必须要使用 babel 来支持了,我们具体看看如何配置
配置1、安装相关包
npm install babel-loader @babel/core @babel/preset-env @babel/polyfill -D2、修改配置 webpack.config.json
还记得文章上面说过,碰到不认识的文件类型的编译问题要求助 loader
module:{ rules:[ { test: /\.js$/, // 正则匹配js文件 exclude: /node_modules/, // 排除 node_modules 文件夹 loader: "babel-loader", // 使用 babel-loader options:{ presets:[ [ "@babel/preset-env", // {1} { useBuiltIns: "usage" } // {2} ] ] } } ] }babel 配置解析:
{1} babel presets 是一组插件的集合,它的作用是转换 ES6+ 的新语法,但是一些新 API 它不会处理的
Promise Generator 是新语法
Array.prototype.map 方法是新 API ,babel 是不会转换这个语法的,因此需要借助 polyfill 处理
{2} useBuiltIns 的配置是处理 @babel/polyfill 如何加载的,它有3个值 false entry usage
false: 不对 polyfills做任何操作;
entry: 根据 target中浏览器版本的支持,将polyfills拆分引入,仅引入有浏览器不支持的 polyfill
usage:检测代码中ES6/7/8等的使用情况,仅仅加载代码中用到的polyfills
演示新建文件 src/moduleES6.js
const arr = [ new Promise(()=>{}), new Promise(()=>{}) ]; function handleArr(){ arr.map((item)=>{ console.log(item); }); } export default handleArr;修改文件 src/index.js
import moduleES6 from "./moduleES6"; moduleES6();执行打包后的源文件(简化后):
"./node_modules/core-js/modules/es6.array.map.js": (function(module, exports, __webpack_require__) { "use strict"; var $export = __webpack_require__("./node_modules/core-js/modules/_export.js"); var $map = __webpack_require__("./node_modules/core-js/modules/_array-methods.js")(1); $export($export.P + $export.F * !__webpack_require__(/*! ./_strict-method */ "./node_modules/core-js/modules/_strict-method.js")([].map, true), 'Array', { map: function map(callbackfn) { return $map(this, callbackfn, arguments[1]); } });看代码就应该能明白了 polyfill 相当于是使用 ES5 的语法重新实现了 map 方法来兼容低版本浏览器。
而 polyfill 实现了 ES6+ 所有的语法,十分庞大,我们不可能全部引入,因此才会有这个配置 useBuiltIns: "usage" 只加载使用的语法。
编译 React 文件 配置安装相关依赖包
npm install @babel/preset-react -D npm install react react-domwebpack.config.js
module:{ rules:[ { test: /\.js$/, // 正则匹配js文件 exclude: /node_modules/, // 排除 node_modules 文件夹 loader: "babel-loader", // 使用 babel-loader options:{ presets:[ [ "@babel/preset-env", { useBuiltIns: "usage" } ], ["@babel/preset-react"] ] } } ] }直接在 presets 配置中增加一个 ["@babel/preset-react"] 配置即可, 那么这个 preset 就会帮助我们把 React 中 JSX 语法转换成 React.createElement 这样的语法。
演示修改文件:src/index.js
import React,{Component} from 'react'; import ReactDom from 'react-dom'; class App extends Component{ render(){ const arr = [1,2,3,4]; return ( arr.map((item)=><p>num: {item}</p>) ) } } ReactDom.render(<App />, document.getElementById('root'));执行打包命令 yarn build 可以正确打包并且显示正常界面。