这些年,Webpack 基本成了前端项目打包构建的标配。关于它的原理和用法的文章在网上汗牛充栋,大家或多或少都看过一些。我也一样,大概了解过它的构建过程以及常用 loader 和 plugin 的配置、性能优化方法等等,仅限于“面试够用”的程度。在实际工作中,往往是配置好后就放一边了,没有遇到问题是不会再碰它的。

我一直有个习惯(或者叫毛病),就是不太愿意花时间去研究暂时用不上的技术。我称其为“屠龙之技”:学会了屠龙的技术,可是找不到龙啊。这样的技术没有实际应用来强化,过不了多久就会荒废的。也因为这个,之前面试吃过很多亏,毕竟由于平台所限,工作中根本接触不到某些方面的技术。不过话又说回来,为了面试也要去学,硬着头皮的那种。

扯远了,说回正题。前不久,网上有个哥们通过我的一篇博客找到我,让我帮他解决一个问题。这篇博客是关于如何在现有 Vue.js 项目里快速实现多语言切换的。他的项目也遇到同样的问题,但是他不懂代码,想付费求助。

按照我的方法,应该能很快完成需求。我大概估算了下工作量,报了个价。但是后面了解到的情况让我大跌眼镜:他的项目是打包好的,没有源码!说原来的开发不在了,都联系不上,找不到源码。要在没有源码的已有项目上加功能,写代码这么多年,还是第一次碰到。

我那篇文章的方案,是重写 Vue.prototype.__patch__ 方法,拦截 DOM 渲染过程,将翻译后的文本替换上去。面对一坨可读性极差的压缩代码,还怎么写下去?当时他还没付款,我本打算放弃了。直到晚上睡觉前,这个问题一直盘旋在脑海里,挥之不去。难道我的方案有这么大的局限性?很不服气啊!

没想到第二天,突然开窍了。这个问题的核心,不就是从压缩代码里找到 Vue 的引用吗?剩下的逻辑,都可以通过注入自己的 JS 代码来完成。

明确了这个思路,就开始了压缩代码挖掘之旅。我们都知道,Vue 项目在打包构建后,会在 HTML 文件里注入几个 JS 文件,大概像这样:

其中的 vendor.xxx.js 就包含了 Vue.js 框架代码。但我们知道,这样构建出来的代码肯定是用了闭包,各个模块都被作用域屏蔽了,window下是访问不到这些模块的。可以试试在控制台输入 Vue ,会提示 Uncaught ReferenceError: Vue is not defined

这个时候就需要研究 Webpack 是怎么打包的了。这里的关键在 manifest.js 文件,它是 Webpack 的运行时代码,定义了一个webpackJsonp函数,代码简化后是这样的:

  1. (function(modules) {
  2. window["webpackJsonp"] = function webpackJsonpCallback(chunkIds, moreModules, executeModules) {
  3. var moduleId, result;
  4. for (moduleId in moreModules) {
  5. if (Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {
  6. modules[moduleId] = moreModules[moduleId];
  7. }
  8. }
  9. if (executeModules) {
  10. for (i = 0; i < executeModules.length; i++) {
  11. result = __webpack_require__(executeModules[i]);
  12. }
  13. }
  14. return result;
  15. };
  16. var installedModules = {};
  17. function __webpack_require__(moduleId) {
  18. if (installedModules[moduleId]) {
  19. return installedModules[moduleId].exports;
  20. }
  21. var module = installedModules[moduleId] = {
  22. exports: {}
  23. };
  24. modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
  25. return module.exports;
  26. }
  27. })([]);

打包后就是通过这个函数来加载各个模块的。因此,只要找到 Vue 这个模块被打包后的 ID,就能通过它来获取。再看看vendor.xxx.js这个文件内容:

  1. webpackJsonp([38], {
  2. "+abY": function(t, e, n) {
  3. "use strict";
  4. n("DmDj")("sup", function(t) {
  5. return function() {
  6. return t(this, "sup", "", "")
  7. }
  8. })
  9. },
  10. "+fX/": function(t, e, n) {
  11. var r = n("awYD")
  12. , i = n("JE6n")
  13. , o = n("0U5H")("match");
  14. t.exports = function(t) {
  15. var e;
  16. return r(t) && (void 0 !== (e = t[o]) ? !!e : "RegExp" == i(t))
  17. }
  18. },
  19. "IvJb": function(t, e, n) {
  20. // 这就是 Vue 框架代码
  21. }
  22. )

可以看到各个模块就是一个个的function。通过 Vue 框架里的一些关键字搜索,找到了 Vue 打包后的 ID 是IvJb。因此只要调用webpackJsonp函数就能获取 Vue变量:

  1. var vue = webpackJsonp([], {}, ['IvJb']);
  2. var __patch__ = vue.default.prototype.__patch__;
  3. vue.default.prototype.__patch__ = function () {
  4. var elm = __patch__.apply(this, arguments);
  5. var lang = getUrlParam('lang')
  6. if (lang) {
  7. //翻译DOM里的文本
  8. translate(elm, lang);
  9. }
  10. return elm;
  11. };

关键问题解决了!通过同样的办法,还可以获取 axios ,把 axios baseUrl 改成了完整路径方便本地调试。剩下的工作就简单了,一是多语言文件文字翻译,那都是体力活,就交给那哥们自己干了。二是加一个语言切换菜单,这个也不难,原生 DOM 操作而已,再稍微调下样式就搞定了。

前前后后花了不到一天时间,完成了这个看似不可能的任务。由此可见,了解工具和框架的底层原理,对于解决特定问题有着决定性的作用。当然,Webpack 功能非常强大,底层逻辑比这里说的复杂多了,我也没有继续深入研究。或许下次碰到问题时又是一次契机。

关于多语言切换的方案,参考我之前写的博客:现有 Vue.js 项目快速实现多语言切换的一种思路

本文首发于公众号 1024译站

研究了一下 Webpack 打包原理,顺手挣了个 AirPods Pro的更多相关文章

  1. webpack打包原理

    什么是 webpack ? 本质上,webpack 是一个现代 JavaScript 应用程序的静态模块打包器(module bundler).当 webpack 处理应用程序时,它会递归地构建一个依 ...

  2. webpack构建原理和实现简单webpack

    webpack打包原理分析 基础配置,webpack会读取配置 (找到入口模块) 如:读取webpack.config.js配置文件: const path = require("path& ...

  3. Webpack 打包优化之速度篇

    在前文 Webpack 打包优化之体积篇中,对如何减小 Webpack 打包体积,做了些探讨:当然,那些法子对于打包速度的提升,也是大有裨益.然而,打包速度之于开发体验和及时构建,相当重要:所以有必要 ...

  4. Android 多渠道打包原理和使用

    每次中午吃饭总会和技术同学聊天.当做 iOS 开发的做安卓开发的人员在一起的时候,他们中间又多了一个话题:iOS 开发难还是安卓开发难. 这个时候做安卓开发的同学最激动说安卓开发要自己画界面.机型复杂 ...

  5. Webpack 打包之体积优化

    谈及如今欣欣向荣的前端圈,不仅有各类框架百花齐放,如Vue, React, Angular等等,就打包工具而言,发展也是如火如荼,百家争鸣:从早期的王者Browserify, Grunt,到后来赢得宝 ...

  6. 零基础学习webpack打包管理

    这些天在项目之余的时间学习了webpack打包项目的东西,非常荣幸的找到一些大神的文章来学习,死劲嚼了几天,终于略知一二.在以后的工作上还需继续学习,下面我将分享我这几天学到的一点东西,希望能让我一个 ...

  7. 使用webpack打包vue工程

    记得去年十月份的时候,自己在研究webpack,当时只是知道大致的用法,写了一个简单的demo,现在,经过了7个月对公司产品架构的使用,以及对vue-cli的使用,在了解了实际应用中各种需求之后,我自 ...

  8. webpack打包理解

    webpack打包理解(将所有依赖文件打包到一个文件中) 由于前端代码变得越来越多,越来越复杂, 纯粹脚本化的代码书写方式已经不能满足工程化得需求. 前端模块被抽象出来, 不仅仅包括js模块, 其它如 ...

  9. 提升webpack打包速度

    webpack打包文件体积过大,怎么提升速度? 借助webpack visualizer可视化插件,来看构建的情况.这个问题要具体情况具体分析,看看打包文件有哪些块头比较大,哪些不常改变,最好列一个l ...

随机推荐

  1. Failed to initialize policy for cpu: 0

    今天在使用vmware安装ubuntu16.04的时候出现下列错误: Failed to initialize policy for cpu: 0 (-19),刚开始还以为是镜像文件出现了问题,结果发 ...

  2. 使用 requests.post 方法抓取有道翻译结果

    import requests as rq import json def get_translate(word=None): url = 'http://fanyi.youdao.com/trans ...

  3. 2019 Multi-University Training Contest 7 Kejin Player(期望)

    题意:给定在当前等级升级所需要的花费 每次升级可能会失败并且掉级 然后q次询问从l到r级花费的期望 思路:对于单次升级的期望 我们可以列出方程: 所以我们可以统计一下前缀和 每次询问O1回答 #inc ...

  4. HHKB Programming Contest 2020【ABCE】

    比赛链接:https://atcoder.jp/contests/hhkb2020/tasks A - Keyboard 代码 #include <bits/stdc++.h> using ...

  5. POJ 2594 Treasure Exploration 最小可相交路径覆盖

    最小路径覆盖 DAG的最小可相交路径覆盖: 算法:先用floyd求出原图的传递闭包,即如果a到b有路径,那么就加边a->b.然后就转化成了最小不相交路径覆盖问题. 这里解释一下floyd的作用如 ...

  6. python中is,== 和 in 的区别

    python对象的三个基本要素:id(身份标识),type(数据类型)和value(值). is 运算符:判断的是对象间的唯一身份标识(id). == 运算符:判断的是对象间的value(值)是否相同 ...

  7. java调用http接口的几种方式总结

    本文参考: https://blog.csdn.net/fightingXia/article/details/71775516 https://www.cnblogs.com/jeffen/p/69 ...

  8. OpenStack Train版-14.安装块存储服务cinder(存储节点)

    安装cindoer块存储服务节点(存储节点192.168.0.40)使用默认的LVM卷方法,之后改为ceph存储 安装LVM软件包 [root@cinder01 ~]# yum install lvm ...

  9. leetcode15 三数之和 双指针

    注意题目没要求数字只能用一次 a + b + c = 0 即为 -b=a+c,同时要求数字不全为正(然后发现a+b+c就行...不过多想想没坏处嘛) 先处理特殊情况,然后 先排序 注意不重复,只需要有 ...

  10. Leetcode(29)-两数相除

    给定两个整数,被除数 dividend 和除数 divisor.将两数相除,要求不使用乘法.除法和 mod 运算符. 返回被除数 dividend 除以除数 divisor 得到的商. 示例 1: 输 ...