这些年,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函数,代码简化后是这样的:

(function(modules) {
window["webpackJsonp"] = function webpackJsonpCallback(chunkIds, moreModules, executeModules) {
var moduleId, result;
for (moduleId in moreModules) {
if (Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {
modules[moduleId] = moreModules[moduleId];
}
}
if (executeModules) {
for (i = 0; i < executeModules.length; i++) {
result = __webpack_require__(executeModules[i]);
}
}
return result;
};
var installedModules = {}; function __webpack_require__(moduleId) {
if (installedModules[moduleId]) {
return installedModules[moduleId].exports;
}
var module = installedModules[moduleId] = {
exports: {}
};
modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
return module.exports;
}
})([]);

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

webpackJsonp([38], {
"+abY": function(t, e, n) {
"use strict";
n("DmDj")("sup", function(t) {
return function() {
return t(this, "sup", "", "")
}
})
},
"+fX/": function(t, e, n) {
var r = n("awYD")
, i = n("JE6n")
, o = n("0U5H")("match");
t.exports = function(t) {
var e;
return r(t) && (void 0 !== (e = t[o]) ? !!e : "RegExp" == i(t))
}
},
"IvJb": function(t, e, n) {
// 这就是 Vue 框架代码
}
)

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

var vue = webpackJsonp([], {}, ['IvJb']);
var __patch__ = vue.default.prototype.__patch__;
vue.default.prototype.__patch__ = function () {
var elm = __patch__.apply(this, arguments);
var lang = getUrlParam('lang')
if (lang) {
//翻译DOM里的文本
translate(elm, lang);
}
return elm;
};

关键问题解决了!通过同样的办法,还可以获取 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. 牛客编程巅峰赛S2第10场 - 钻石&王者 C.牛牛的路径和 (位运算,dfs)

    题意:给你节点数为\(n\)的树,每个节点都有自己的权值,求所有路径的上的点的权值按位与的和. 题解:题目给的数据很大,我们不能直接去找.因此我们可以枚举二进制\([1,20]\)的每一位,然后再枚举 ...

  2. (数据科学学习手札106)Python+Dash快速web应用开发——回调交互篇(下)

    本文示例代码已上传至我的Github仓库https://github.com/CNFeffery/DataScienceStudyNotes 1 简介 这是我的系列教程Python+Dash快速web ...

  3. 【cpp上】课后正误小题

    State whether each of the following is true or false. If false, explain why. Assume the state ment u ...

  4. TextCNN论文解读

    引言 本文是对<Convolutional Neural Networks for Sentence Classification>的原理解读,简称TextCNN. 作者提出了一种基于CN ...

  5. k8s二进制部署 - traefik安装

    配置traefik资源清单rbac.yaml 配置traefik资源清单 rbac.yaml apiVersion: v1 kind: ServiceAccount metadata: name: t ...

  6. 【转】K8S中部署Helm

    K8S中的包管理工具 1. 客户端Helm(即Helm)  通过脚本安装:curl https://raw.githubusercontent.com/helm/helm/master/scripts ...

  7. 实现基于股票收盘价的时间序列的统计(用Python实现)

    时间序列是按时间顺序的一组真实的数字,比如股票的交易数据.通过分析时间序列,能挖掘出这组序列背后包含的规律,从而有效地预测未来的数据.在这部分里,将讲述基于时间序列的常用统计方法. 1 用rollin ...

  8. Leetcode(94)-二叉树的中序遍历

    给定一个二叉树,返回它的中序 遍历. 示例: 输入: [1,null,2,3] 1 \ 2 / 3 输出: [1,3,2] 思路:和上篇的前序遍历一样,同样有递归和非递归的做法 (1)递归 vecto ...

  9. Tomcat连接配置

    DBCP连接池配置: <bean class="org.apache.tomcat.jdbc.pool.PoolProperties"> <property na ...

  10. springboot(六)Email demo

    项目中经常使用邮件发送提醒功能,比如说更新安全机制,发送邮件通知用户等 一.简单邮件发送 导入依赖: <dependency> <groupId>org.springframe ...