# webpack-demo **Repository Path**: gitzt/webpack-demo ## Basic Information - **Project Name**: webpack-demo - **Description**: No description available - **Primary Language**: JavaScript - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2017-11-06 - **Last Updated**: 2020-12-19 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README ### 热更新是什么? 简单来说,热更新一般是指手机里的app有小规模更新,以直接打补丁的形式更新。 相对应的,另一种更新方式就是下载新的安装包,重新安装。 所以**热更新**在手游里头是比较常见的,毕竟游戏应用个个都几百兆起步。 ### 那Web前端有热更新? 按上面那个说法,Web应该是不存在热更新的。 因为网页的架构是B/S,即 浏览器+服务器 , 它不像手机app一样是 C/S 客户端+服务器 所以在网页这一块是无法推送补丁让浏览器去更新的。 ### 那为什么我搜前端热更新有好多文章在讲? 网上大多数前端热更新讲的都是**热加载 hot-loader 或者是模块热更替 HMR** ### 热加载是什么? - **问题背景** 前端页面是由 HTML+CSS+JavaScript 组成的,我们前端开发页面调试的时候, 一般是这样:编辑器修改保存 --> 切换浏览器刷新 ps:所以前端汪F5键是用得最多的。 - **怎么解决** 能不能我这边修改完,浏览器就自动重新加载那些修改过的文件? 想要解决这个问题,那么我们就要去检测代码文件是否修改了,然后通知浏览器去重新加载。 那我们就需要一个浏览器与服务器之间的通信机制。 在早期,浏览器与服务器间的通信机制就只有http协议,可http协议是无状态协议,这就很尴尬了,而且服务器无法主动给浏览器发消息,那就能浏览器不断跟服务器请求。 - 还好在HTML5里头添加了**websocket** **websocket** 是一种允许浏览器与服务器间建立tcp长链接的通信机制 tcp协议:双向通信,有状态 这样的话,我们就可以通过服务器检测文件修改,有修改了,我们就通知浏览器,没有我们就不通知。 ![盗图](http://upload-images.jianshu.io/upload_images/2954145-76f6c69beaadcdcd.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 所以大概流程就如下: - 服务器检测代码是否修改了 - 修改了通知浏览器 - 浏览器根据修改的文件情况选择局部刷新或全局刷新 ### 具体实例 - webpack-dev-server 根据配置可以开启HMR模块 官方文档:[https://doc.webpack-china.org/guides/hot-module-replacement](https://doc.webpack-china.org/guides/hot-module-replacement) - [vue-loader](https://github.com/vuejs/vue-loader) - 小demo: [https://gitee.com/gitzt/webpack-demo](https://gitee.com/gitzt/webpack-demo) ### 试一下 - package.json 配置文件 ``` "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "start": "webpack", "server": "webpack-dev-server --open", "server-hot": "webpack-dev-server --hot" }, ``` - webpack.config.js 配置文件 ``` // 本地开发服务器 devServer: { // 本地服务器加载资源所在路径 contentBase: "./public", // true 表示所有跳转都是index.html historyApiFallback: true, // 当源文件修改时,自动刷新页面 inline: true, // 端口号,若不设置,默认为8080 port: 3000, }, ``` 所以我们在`webpack-demo`目录下,运行`npm run server`就等于运行了`webpack-dev-server --open` 这样我们就启用了`webpack-dev-server`但是我们配置项没有配置启用**HMR** 所以当你修改了文件时,它只是重新打包了,并通知浏览器重载一遍页面。 ![运行webpack-dev-server](http://upload-images.jianshu.io/upload_images/4697737-a0bb5437df085248.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) `$ npm run server`是我执行的命令,根据package.json 配置文件这个命令等同于`webpack-dev-server --open` 下面那些输出语句,一些是`webpack-dev-server`启用状态,还有`webpack`的打包文件的打包过程 ![浏览器的页面效果是这样的](http://upload-images.jianshu.io/upload_images/4697737-7ef6793d9dbda3d6.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) ### 为什么输出的是这个? - webpack.config.js 配置文件 ``` module.exports = { // 入口文件 entry: __dirname + "/app/main.js", // 输入文件 output: { // 输出路径 path: __dirname + "/public", // 输出文件名 filename: "bundle.js" }, ``` - `/app/main.js`文件 ``` // ES6导入模块的语法,所以入口文件跟下面两个文件有关联 import hello from "./hello"; import "./main.css"; // 简单的DOM操作 document.querySelector("#root").appendChild(hello()); ``` - `/app/hello.js`文件 ``` import style from "./style.css"; // 下面这个是CommonJS的文件导入,因为node.js本身是支持CommonJS的 let test = require("./test.json"); // 这个是CommonJS的模块导出 module.exports = function () { let hello = document.createElement('div'); hello.textContent = test.hhh; // 这里的hello这个元素的class的取值是来自上面导入"./style.css"文件,这涉及到CSS模块 hello.className = style.hello; return hello; }; ``` - `/app/main.css`文件 ``` body{ color: blue; font-size: 64px; } ``` - `/app/style.css`文件 ``` .hello{ color: red; } ``` - `/app/test.json`文件 ``` { "hhh":"this a message from .json" } ``` - 哦,还有`index.html`文件 ``` webpack demo
``` ![实际页面index.html代码](http://upload-images.jianshu.io/upload_images/4697737-ca9cd324359a2072.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 这样应该很清晰了吧 首先入口文件`main.js`引入了`hello.js`和`main.css` 然后`hello.js`又引入了`style.css`和`test.json` 所以上面这些与入口文件有直接联系或间接联系的文件都会通过webpack去打包,输出为一个`bundle.js` 而`index.html`是引用了`bundle.js`文件的,所以就出现了这个效果。 ### 你好像没讲到webpack-dev-server的作用哦 - 我们上面运行的是没有启用**HMR**,所以这时候我们修改`main.css`文件 ``` body{ color: blue; /* font-size: 64px; */ } 你会看到终端webpack在重新打包,打包完后浏览器自动刷新了页面,然后字体变成默认大小 ``` - 如果启用了**HMR**呢? 这时候我们要用到的命令是`$ npm run server-hot` 同样修改`main.css`文件,我们会发现字体大小变了,可是浏览器并没有刷新,而且发现改一次就多一个js文件,页面只是局部刷新 ![多了的js文件其实就是一些样式的更替](http://upload-images.jianshu.io/upload_images/4697737-f4d199a74ff5dfd2.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) - 那是不是用了**HMR**就可以实现任何修改都是局部刷新呢? 不是的。 如果只是修改了CSS样式,那可以通过JS以打补丁的形式去进行替换样式。而且也不是说改样式就都能实现局部刷新,如果你改的是`style.css`这个文件,它的结果是会重新加载页面,为什么呢?因为`style.css`这里头用到了CSS模块的东西,所以没办法说直接打个补丁就能搞定。 ### 结论 热加载这个东西,首要就是靠服务器与浏览器之间的通信,有了通信才能通知浏览器什么时候去刷新,而刷新又分全局和局部,这个要看服务端改了哪些代码文件,而这些文件如果可以局部刷新就局部刷新,不行的话就只能重新加载页面了。 ### 参考资料 [前端开发热更新原理解读](http://www.jianshu.com/p/f4c7254f4e24)