前端页面一般会通过webpack打包压缩后发布,一般来说,想人肉读懂压缩后的代码是比较困难的。nuxt.js是基于Vue的ssr框架,Vue项目具有一定特征,因此编译后的代码还是有比较高的可读性。逆向nuxt.js项目主要包括:view复原,组件和样式抽取,依赖分析、去polyfill等。
编译过后的View可读性不如直接在template里写的html,因此需要将其复原。复原View主要难点是图片资源处理、组件事件复原、slot恢复、复杂dom恢复等。一般情况下src是本地图片的img,编译后的src会独立成一个module。组件事件编译后会比较啰嗦,因此需要分辨出必要的内容,例如@click.prevent.stop="onClick",会被编译成形如on{click:function(e){e.preventDefault(),e.stopPropagation(),t.onClick()}}的样子。常见的事件修饰符还有trim等,详情可以参看https://www.cnblogs.com/Eden-cola/p/vue-v-model-with-input.html 。slot一般会被编译成t._t("slot-name"),注意前面的一个t其实是this,后面一个_t才是slot构造方法,参数是slot-name,那么复原组件的时候应该是<slot name="slot-name"></slot>。对于复杂的dom,由于嵌套层次过深,编译后的代码肉眼已经比较难以复原了。
样式抽取的时候,要注意区分样式是否是scoped,是否需要深度指示符。scoped的样式会带有f_ft[data-v-1e057d9e]之类的属性。
去polyfill主要是针对babel。一般涉及到Object.assign,Object.keys,解构,Promise,async/await等,其中async/await恢复比较麻烦点,一般会被babel处理成generator。
逆向最大的难点还是依赖分析。
拿到一个nuxt.js的项目后,首先应该分析页面和路由,确定项目规模。这些信息可以从manifest文件中获取。一般会把所有页面的链接放到head里去。在manifest的最前面部分代码里就能找到所有的页面和hash值,因此根据这些信息就能获取所有页面的源码。需要注意的是layout也会被视为页面,出现在这里。manifest的价值仅限于此。
接下去分析app.js。app.js也可能是项目名称,取决于项目配置。这个文件是比较大的,不过一般还是比vendor.js的小很多。vendor里全是用到的三方,也就是node_modules里被用到三方代码。我们一般不会去详细了解它,除了分析依赖和处理polyfill的时候需要知道用到了哪个三方。言归正传,app.js里包括了所有的middleware,plugins,store代码。其中middleware和store会专门有地方告诉你,它们包括了哪些文件,但是plugins是没有这样的地方的。不过也很好理解,因为store最终需要组合成一棵状态树,它需要知道有哪些store。middleware需要提前加载,它也要维护一个中间件列表。plugin就不一样了,到处引用,到处使用。app.js也会包含一些nuxt生成的代码,例如error页面等内置的组件、路由、全局head、scrollRestoration等。
对于css,根据build里的设置不同,会有不同的形态。
extractCSS: { allChunks: true }//会提取所有页面的css并生成一个style.css
extractCSS: true,//仅提取公共的css,包括配置项css里的css、node_modules里使用到的三方,例如bootstrap-vue的css,以及.nuxt中内置页面的样式,例如progress,error等。
谈谈模块化
每个module都被包裹成形如yTq1: function (t, e, n) {"use strict";xxxx}的代码。其中函数原型为
(function (module, __webpack_exports__, __webpack_require__)
参看https://yq.aliyun.com/articles/610004 https://github.com/muwoo/blogs/issues/28 https://blog.csdn.net/HaoDaWang/article/details/77126686?locationNum=9&fps=1 https://www.cnblogs.com/sunshq/p/7922182.html
每个vue的template、style、script代码都会被包裹成module,最终会有一个大的module将这些module导入进去。其中style会被包裹一层。
vue编译对照表
方法:
target._o = markOnce;
target._n = toNumber;
target._s = toString;
target._l = renderList;
target._t = renderSlot;
target._q = looseEqual;
target._i = looseIndexOf;
target._m = renderStatic;
target._f = resolveFilter;
target._k = checkKeyCodes;
target._b = bindObjectProps;
target._v = createTextVNode;
target._e = createEmptyVNode;
target._u = resolveScopedSlots;
target._g = bindObjectListeners;
_v {{}}
_f {{filter | params}}
_t <slot name="name"></slot>
_u <div slot="price" slot-scope="t">{{t.price}}</div>
_e none
_n v-model.number
_l <li v-for="(e,i) in list" :key="i"></li>
sync指令:
attrs:{
sms: e.verify.sms,
}
on: {
"update:sms": function (t) {
e.$set(e.verify, "sms", t)
}
}
等价于
:sms="verify.sms"
@update:sms='$set(verify, "sms", $event)'
----------------------------------------
:sms="verify.sms"
@update:sms='verify.sms = $event'
-------------------------------------
@update:sms.sync='verify.sms'
事件修饰符
.stop
.prevent
.capture
.self
.once
.passive
按键修饰符
.enter
.tab
.delete
(捕获“删除”和“退格”键).esc
.space
.up
.down
.left
.right