引入话题

我在使用Vue2开发一个前端项目,由于在代码中经常使用console.log()打印输出某些重要的参数,不过有一个地方忘记注释掉console.log(),此时浏览器正处于开发者模式,打印输出以下数据,这些数据是前端请求后端,我进行了console.log()打印。上线环境中这种console.log()打印肯定是要注释掉的,因为浏览器打印出来的同时后面会标明

运行方式

image-20230810113806485

console.log()打印输出

image-20230810113413477

如果将相关index.vue:159 处的代码在VSCode中注释掉就会出现VM1634 94736:177,这是因为 index.vue:159 处的代码已经注释掉了,浏览器中的此处源代码也会注释,我还以为会剩下编译打包后的静态代码

image-20230810114559619

为了验证VM1634 94736:177是否为编译打包后的静态代码,我重新打包为H5的方式,使用Python自带的HTTP服务器重新部署了一个服务

image-20230810143813342

再次运行到之前的console.log()

image-20230810144345542

点进pages-admin-set_conf-index.720cbcbe.js,此文件其实是由源码src/pages/admin/set_conf/index.vue编译而来,但是有两个结果,虽然代码基本一样的,但如下可做一个对比,静态编译后的代码和"serve": "npm run dev:h5"方式中VM1634 94736:177代码逻辑基本一致,但有一点VM1634 94736:177的代码文件VM1650 94736是不能在浏览器的缓存文件中被找到的,但是

"serve": "npm run dev:h5" 方式

编译后的静态代码文件可以右击文件导航栏点击Reveal in sidebar找到

image-20230810150616857

由此可以猜测可能是Chrome浏览器开发工程师防止你在编写Vue代码的时候突然在源码中注释掉console.log()随后保存代码,浏览器调试工具的命令行还是存在的,之前打印结果对应的源码已经不存在了,只是临时保存一份静态代码而已,说白了还是对应静态文件代码文件的某段相关代码。

浏览器调试

经过以上的过程,还发现了"serve": "npm run dev:h5"方式在浏览器进入开发者模式可以在调试的过程中通过浏览器的命令行导航到源代码,对,没错就是源代码,由此浏览器调试就是一个相比console.log()方式更为便捷高效率的调试方式。

但是有一点,我记得在Vue项目的配置文件中需要开开启相应的功能才可以在浏览器端看到原原本本的代码,如果不开启,就只能看到编译后的文件,编译后的文件相对源代码还是很难看懂的,虽然也可以打断点进行调试。

浏览器源码结构

如果你的项目配置开启了源码调试功能,就可以看到以下结果,上方的目录结构是网页静态文件,下方是开启了源码调试功能才会有的,且可使用console.log()打印出指示源码的位置:

image-20230810153015671

指示位置index.vue:159,鼠标点击可找到文件

image-20230810154002373

文件在浏览器缓存中路径

image-20230810153715685

断点

点击源码中的某一行的数字,就会打上一个断点,当你再次刷新同一页并保证可以执行到console.log(data)处时,你就会发现代码会停止运行到此处,并且携带了好多可见到的参数,这个和后端调试几乎差不多的方便,让我感到惊喜:

image-20230810155830113

同时网页中会出现如下

image-20230810155910558

这个时候你还可以直接再点击一个后面一定会执行的代码行数,打一个新的断点,并点击继续运行的图标就会发现再次停住,并且你可以完全的看到代码执行后数据处理的展现,非常方便调试

image-20230810160708102

编译打包

简述一下我自己对这个过程的理解,编译过程其实就是将Vue代码编译成了浏览器直接可以执行的代码,虽然编译后逻辑依旧还是JavaScript,但听说Vue是基于虚拟DOM的方式,我看了编译后的代码文件结构,找到一处HTML页面代码,其余只剩下css、js,这个结果可能就是实现虚拟DOM的方式了,虚拟DOM是通过编译后的js代码逻辑实现的,同时编译后js代码会加载css文件中虚拟DOM所需渲染样式,于是只使用一个HTML文件来引导加载js和css文件,此HTML文件相当于一个 BIOS (Basic Input/Output System)是一种计算机固件,它在计算机加电时首先运行,然后初始化硬件组件,如内存、硬盘、显示适配器等,最后加载操作系统。

以下是HTML引导文件内容,浏览器目录结构

image-20230810164416303

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<!DOCTYPE html>
<html lang="zh-CN">

<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>
页面标题
</title>
<script>
var coverSupport = 'CSS' in window && typeof CSS.supports === 'function' && (CSS.supports('top: env(a)') || CSS.supports('top: constant(a)'))
document.write('<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0' + (coverSupport ? ', viewport-fit=cover' : '') + '" />')
</script>
<link rel="stylesheet" href="/static/index.5841170f.css" />
<script defer src="/static/js/chunk-vendors.js"></script><script defer src="/static/js/index.js"></script></head>

<body>
<noscript>
<strong>Please enable JavaScript to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>

</html>
<script>
(function () {
var userAgent = navigator.userAgent
window.addEventListener('resize', function () {
if (navigator.userAgent !== userAgent) {
/* eslint-disable no-undef */
location.reload()
}
})
})()

</script>

打包

分别对比了 ‘打包成静态网站的代码’ ‘对应依赖中的源码’ 和 ‘"serve": "npm run dev:h5"环境下的静态网页代码’ 得出以下结论

首先明确一点,一个vue页面被编译后打包成一个js文件,比如以下的两个页面对应:

image-20230810174825728

image-20230810174704878

对比三者代码

对应依赖中的源码

image-20230810234745557

"serve": "npm run dev:h5"环境下

image-20230810221524826

打包成静态网站的代码

image-20230810221225368

对比以上可发现node_modules/crypto-js/aes.js文件中三个结果的代码特征

  • 源码打包到单页面js文件,"serve": "npm run dev:h5"方式基本上没太多差别,处理了一部分逻辑,比如运行下来必是true
  • 如果编译为H5静态网页,就会发现好多变量/函数使用几个字母代替,从而减小打包体积,同时以上的的换行/空格也会被删除

crypto-js

是一个集成好多加密算法的依赖,但如果使用以下导入方式,就会将所有加密算法代码打包进单页面js文件

1
2
3
4
// ES6
import CryptoJS from 'crypto-js';
// CommonJS
const CryptoJS = require('crypto-js');

解决方案

  • 使用按需加载(懒加载)的方式来导入 crypto-js 的特定部分,而不是整个库

    1
    import { AES } from 'crypto-js';
  • ProvidePluginexpose-loader 这两个插件都是用来将模块或变量设置为全局变量,从而在你的代码中不需要显式导入模块。但是它们的使用场景有些不同。

    • ProvidePlugin:它用于自动加载模块,并将它们设置为全局变量,以便在代码中使用。你可以在 Webpack 配置中配置该插件,指定模块和对应的全局变量名。比如,你可以配置 ProvidePlugincrypto-js 设置为全局变量:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      javascriptCopy code// webpack.config.js
      const webpack = require('webpack');

      module.exports = {
      // ...
      plugins: [
      new webpack.ProvidePlugin({
      CryptoJS: 'crypto-js'
      })
      ]
      };
    • expose-loader:它可以在需要时显式地将模块暴露到全局变量上。你需要在代码中显式导入模块,然后使用 expose-loader 将模块设置为全局变量。

      1
      2
      javascriptCopy code// 在需要的文件中
      import 'expose-loader?CryptoJS!crypto-js';
  • externals 配置: externals 配置用于告诉 Webpack 你的代码中使用的模块是外部可用的,并且不应该将其打包进最终的输出文件中。它是为了避免重复打包某些库,而期望它们在运行时从全局环境获取。

    1
    2
    3
    4
    5
    6
    7
    javascriptCopy code// webpack.config.js
    module.exports = {
    // ...
    externals: {
    'crypto-js': 'CryptoJS'
    }
    };

浏览器运行

浏览器访问编译后的静态代码网站,浏览器输入地址后面的部分一般就是前端路由了,点击页面自然会路由到那些对应的页面并下载到浏览器缓存,点击过多少页面,就会加载多少页面代码,并不是全部加载到本地,可能可以写一些逻辑实现所有静态代码加载到本地,运行时只调用后端接口,来提升加载速度吧。