简单聊聊前端开发中的热更新原理

clipboard.png

背景

前端项目开发过程当中热更新的机制你们都知道,不知道你在开发的时候是否作了这方面的配置。javascript

相信接触最多的就是 webpack 的热更新,文件保存后页面自动刷新,或者 css 自动更新,页面的样式在不刷新页面的状况下就会更新。css

还有就是模块热替换。html

热更新机制很好玩,能提高很多开发效率,可是只是处于会用的阶段不是咱们的目的,咱们应该适当的深刻学习下,看看他背后的原理,一个是否思考过,一个是否能本身实现。前端

热更新原理

我们这里主要说下怎样本身实现一个热更新,也就是文件更改了会自动刷新页面,能够同步 pc 和 移动端,css 更改了能够不刷新页面就应用最新的 css。java

其实热更新的原理并不复杂,或者说很简单。node

我们一步一步的分析下。webpack

本文不是要告诉你一些 api如何使用,而是利用架构的思惟去分析和解决问题。git

【分析】github

  1. 文件内容变动了,浏览器是怎么知道的呢?
  2. css 文件内容变动了,没有刷新页面 怎么加载最新的内容呢?

只要解决了上面两个问题,咱们就算是完成了。由于剩下得就是编码了,这都好说。web

【结果】

文件变动了,我怎样通知浏览器?

  1. 浏览器和服务器保持着链接。 服务器有什么事儿直接经过当前的连接告诉浏览器就能够了。

链接确定是长链接,否则怎么实时通讯。

保持长链接有哪些方法呢? 轮询?eventSorce? 都不够好。

有么有更好的方案呢?那就是 - websocket

浏览器和服务器先创建好连接,服务器就能够直接通知到客户端了。这个时候不管是 pc 上仍是手机上均可以随时根据须要刷新或者加载资源。

  1. css 更新,css 自己是能够经过 dom 去操做的。浏览器只要知道是 css更新了,直接从新加载当前的 css 文件就能够了。

架构思惟

我们在从新捋捋这个架构。

  1. 服务器和浏览器经过 websocket 创建连接。
  2. 服务器和浏览器规定好消息的规则,是刷新页面仍是更新 css。

基本架构有了,其余的就是编码实现了。

服务端使用 node 建立一个 ws 服务。

浏览器使用 websocket 建立一个连接和服务器进行连接。

双方经过对应的 api 进行数据的操做。

代码实现

本文只是讲解下思路,并无实现文件的监听,文件监听后面会介绍。咱暂时先肯定好两个消息规则:

浏览器收到 命令为:htmlFileChange ,此时浏览器刷新;

浏览器收到命令为:cssFileChange,此时不刷新页面,自动加载 css 文件;

具体代码以下:

服务端:

//web-socket.js 建立 ws 服务
var ws = require("nodejs-websocket");//须要安装这个包

module.exports = function(){
    return function () {
        console.log("重度前端提醒,开始创建链接...")

        var sessions = [];//存放每个连接对象
        var server = ws.createServer(function (conn) {
            sessions.push(conn);//将新的连接对象存放在数组中

            conn.on("text", function (str) {
                console.log("收到的信息为:" + str)
                sessions.forEach(item=>{
                    item.sendText(str) //全部客户端都发送消息
                });

            });
            conn.on("close", function (code, reason) {
                console.log("关闭链接")
            });
            conn.on("error", function (code, reason) {
                console.log("异常关闭")
            });
        }).listen(6152)
        console.log("WebSocket创建完毕")
    }
}

//server.js http 服务代码

let http = require('http');
let fs = require('fs');
let webSocket = require('./node/web-socket');

const BASEROOT = process.cwd();//得到当前的执行路径
//读取 index.html内容
let getPageHtml = function () {
    let data = fs.readFileSync(BASEROOT+'/html/index.html');
   return data.toString();
}
//读取 index.css内容
let getPageCss = function () {
    let data = fs.readFileSync(BASEROOT + '/html/index.css');
    return data.toString();
}

//node 端 开启 ws 服务
webSocket()();

http.createServer(function (req, res) {//建立 http 服务

    let body = '',url = req.url;

    req.on('data', function (chunk) {
        body += chunk;
    });

    req.on('end', function () {
        //路由简单处理 根据不一样路径输出不一样内容给浏览器
        if(url.indexOf('/index.css')>-1){
            res.write(getPageCss());
        }else{
            res.write(getPageHtml());
        }

        res.end();

    });

}).listen(6151);

console.log('重度前端提醒...... server start');

页面截图

clipboard.png

clipboard.png

客户端

//index.html 布局代码省略

 const nick = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'aa', 'cc'];
    let index = 0;
    // Create WebSocket connection.
    const socket = new WebSocket('ws://10.70.69.191:6152');

    // Connection opened
    socket.addEventListener('open', function (event) {
        socket.send(navigator.userAgent);
    });

    // 监听服务器推送的消息
    socket.addEventListener('message', function (event) {
        if (index > nick.length) {
            index = 0;//只是为了每次输出不一样的昵称,没实际意义
        }

        var ele = document.createElement('div');
        ele.innerHTML = nick[index] + ':' + event.data;
        if (event.data === 'htmlFileChange') {
            //html 文件更新了 刷新当前页面
            location.reload();
        }
        if (event.data === 'cssFileChange') {
            //css 文件更新了 刷新当前页面
            reloadCss();
        }
        document.getElementById('content').append(ele);
        index += 1;
    });
    //从新加载 css
    function reloadCss() {
        var cssUrl = [],
            links = document.getElementsByTagName('link'),
            len = links.length;
        for (var i = 0; i < len; i++) {
            var url = links[i].href;
            document.getElementsByTagName('head')[0].appendChild(getLinkNode(url)); //建立新的 css 标签
            document.getElementsByTagName('head')[0].removeChild(links[i]); //移除原有 css

        }
        console.log(document.getElementsByTagName('head')[0])

        function getLinkNode(cssUrl) {
            var node = document.createElement('link');
            node.href = cssUrl;
            node.rel = 'stylesheet';
            return node;
        }
    }

    document.getElementById('btn1').onclick = function () {
        socket.send(document.getElementById('message').value);
        document.getElementById('message').value = '';
    }

index.css 内容

input {
      outline: none;
  }

  #content {
      height: 400px;
      width: 400px;
      border: solid 1px #ccc;
      color: red;
  }

代码却是次要的。解决问题的思路才重要。有了解决问题的架构思惟,代码实现都好说。

写到这里我们还能顺便实现一个群聊。

本质就是服务器和浏览器怎样实时通讯,解决了这个问题,其余的都是小事儿。

这个技术实现仍是比较简单的。

另外对模块热更新和 websocket 原理有兴趣的能够研究下,后面可能也会介绍。

总结

本文主要介绍

简易版热更新的原理;

热更新实现思路和代码实现;

架构思惟:简单的带出架构思惟的做用;

但愿本文对你有用。

原创不易、请多鼓励
自家观点、欢迎打脸

代码示例下载

https://github.com/bigerfe/ho...

做者:微信公众号 - 重度前端 主笔:八门欢迎关注 重度前端-每周5原创全栈干货+每周三深度技术文章

相关文章
相关标签/搜索