研究了一下 Webpack 打包原理,顺手挣了个 AirPods Pro
这些年,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的更多相关文章
- webpack打包原理
什么是 webpack ? 本质上,webpack 是一个现代 JavaScript 应用程序的静态模块打包器(module bundler).当 webpack 处理应用程序时,它会递归地构建一个依 ...
- webpack构建原理和实现简单webpack
webpack打包原理分析 基础配置,webpack会读取配置 (找到入口模块) 如:读取webpack.config.js配置文件: const path = require("path& ...
- Webpack 打包优化之速度篇
在前文 Webpack 打包优化之体积篇中,对如何减小 Webpack 打包体积,做了些探讨:当然,那些法子对于打包速度的提升,也是大有裨益.然而,打包速度之于开发体验和及时构建,相当重要:所以有必要 ...
- Android 多渠道打包原理和使用
每次中午吃饭总会和技术同学聊天.当做 iOS 开发的做安卓开发的人员在一起的时候,他们中间又多了一个话题:iOS 开发难还是安卓开发难. 这个时候做安卓开发的同学最激动说安卓开发要自己画界面.机型复杂 ...
- Webpack 打包之体积优化
谈及如今欣欣向荣的前端圈,不仅有各类框架百花齐放,如Vue, React, Angular等等,就打包工具而言,发展也是如火如荼,百家争鸣:从早期的王者Browserify, Grunt,到后来赢得宝 ...
- 零基础学习webpack打包管理
这些天在项目之余的时间学习了webpack打包项目的东西,非常荣幸的找到一些大神的文章来学习,死劲嚼了几天,终于略知一二.在以后的工作上还需继续学习,下面我将分享我这几天学到的一点东西,希望能让我一个 ...
- 使用webpack打包vue工程
记得去年十月份的时候,自己在研究webpack,当时只是知道大致的用法,写了一个简单的demo,现在,经过了7个月对公司产品架构的使用,以及对vue-cli的使用,在了解了实际应用中各种需求之后,我自 ...
- webpack打包理解
webpack打包理解(将所有依赖文件打包到一个文件中) 由于前端代码变得越来越多,越来越复杂, 纯粹脚本化的代码书写方式已经不能满足工程化得需求. 前端模块被抽象出来, 不仅仅包括js模块, 其它如 ...
- 提升webpack打包速度
webpack打包文件体积过大,怎么提升速度? 借助webpack visualizer可视化插件,来看构建的情况.这个问题要具体情况具体分析,看看打包文件有哪些块头比较大,哪些不常改变,最好列一个l ...
随机推荐
- Failed to initialize policy for cpu: 0
今天在使用vmware安装ubuntu16.04的时候出现下列错误: Failed to initialize policy for cpu: 0 (-19),刚开始还以为是镜像文件出现了问题,结果发 ...
- 使用 requests.post 方法抓取有道翻译结果
import requests as rq import json def get_translate(word=None): url = 'http://fanyi.youdao.com/trans ...
- 2019 Multi-University Training Contest 7 Kejin Player(期望)
题意:给定在当前等级升级所需要的花费 每次升级可能会失败并且掉级 然后q次询问从l到r级花费的期望 思路:对于单次升级的期望 我们可以列出方程: 所以我们可以统计一下前缀和 每次询问O1回答 #inc ...
- HHKB Programming Contest 2020【ABCE】
比赛链接:https://atcoder.jp/contests/hhkb2020/tasks A - Keyboard 代码 #include <bits/stdc++.h> using ...
- POJ 2594 Treasure Exploration 最小可相交路径覆盖
最小路径覆盖 DAG的最小可相交路径覆盖: 算法:先用floyd求出原图的传递闭包,即如果a到b有路径,那么就加边a->b.然后就转化成了最小不相交路径覆盖问题. 这里解释一下floyd的作用如 ...
- python中is,== 和 in 的区别
python对象的三个基本要素:id(身份标识),type(数据类型)和value(值). is 运算符:判断的是对象间的唯一身份标识(id). == 运算符:判断的是对象间的value(值)是否相同 ...
- java调用http接口的几种方式总结
本文参考: https://blog.csdn.net/fightingXia/article/details/71775516 https://www.cnblogs.com/jeffen/p/69 ...
- OpenStack Train版-14.安装块存储服务cinder(存储节点)
安装cindoer块存储服务节点(存储节点192.168.0.40)使用默认的LVM卷方法,之后改为ceph存储 安装LVM软件包 [root@cinder01 ~]# yum install lvm ...
- leetcode15 三数之和 双指针
注意题目没要求数字只能用一次 a + b + c = 0 即为 -b=a+c,同时要求数字不全为正(然后发现a+b+c就行...不过多想想没坏处嘛) 先处理特殊情况,然后 先排序 注意不重复,只需要有 ...
- Leetcode(29)-两数相除
给定两个整数,被除数 dividend 和除数 divisor.将两数相除,要求不使用乘法.除法和 mod 运算符. 返回被除数 dividend 除以除数 divisor 得到的商. 示例 1: 输 ...