企业网站源码(vue打包后反编译到源代码详细步骤)

vue打包后反编译到源代码详细步骤

若仅持有编译后的Vue前端文件,且原始文件夹丢失,还原项目源代码的步骤如下:

使用反编译库 reverse-sourcemap

借助此库,可从.map文件还原编译前的Vue文件。

安装

执行命令,生成对应源文件至src文件夹

在dist/static/js下,找到大量xxxxx.js.map文件。使用Python脚本统一导出。

执行后,获取源代码文件,位于dist/src/static/js/webpack/src(根据原始编译路径)。

删除/static/js下编译过的js文件,保留正常js文件。

还原的node_modules目录位于dist/src/static/js/webpack。

调整项目目录结构

复制反编译得到的src、node_modules文件夹,替换原代码目录。

替换static文件夹至原代码static目录。

删除编译后的index.html中引入的css、js代码,检查静态js、css文件,保证未误删。

管理依赖包信息

进入备份的反编译node_modules目录。

执行npm shrinkwrap,生成npm-shrinkwrap.json文件。

文件记录项目所用npm包,但不包括版本号和编译库信息。

检查node_modules目录中的库信息,确认重要库如vue、npm的版本号。

启动项目

回到构建项目目录。

撰改原package.json,保留编译所需库,如本地使用webpack。

根据需求调整本地package.json,执行npm run start。

耐心查找依赖,根据报错提示逐个安装。

查看源代码引入的库,推测内容。

生成package.json

项目启动成功后,执行npm shrinkwrap生成新的npm-shrinkwrap.json文件。

对照第3步得到的npm-shrinkwrap.json文件,确认库版本和信息。

额外提示

复制项目,删除node_modules,新建目录,执行npm run install后,重新npm run start。

根据报错提示逐个安装依赖库,直至项目启动。

留意,执行npm run start时需删除npm-shrinkwrap.json文件。

完成上述步骤后,可成功还原Vue项目源代码。祝您反编译成功!

源码分析:Vue编译(compile)流程编译入口解析

Vue编译(Compile)入口

Vue中的数据渲染到页面的整个流程,我们之前介绍过,不清楚可以点击这里,其中的编译流程,是将我们的template模板编译生成render函数。那为何需要编译呢,因为template模板中有很多指令(例如@click,v-for等),原生js是识别不了的。接下来我们来分析一下Vue编译的过程:

在我们之前分析的mount过程中有这样一段逻辑,compileToFunctions就是执行编译的函数,将我们定义的template作为参数传入,并生成render函数,接下来我们来看下compileToFunctions:

if?(template)?{......const?{?render,?staticRenderFns?}?=?compileToFunctions(template,?{shouldDecodeNewlines,shouldDecodeNewlinesForHref,delimiters:?options.delimiters,comments:?options.comments},?this)options.render?=?renderoptions.staticRenderFns?=?staticRenderFns......}compileToFunctions

compileToFunctions定义在src/platform/web/compiler/index.js中:

import?{?baseOptions?}?from?'./options'import?{?createCompiler?}?from?'compiler/index'const?{?compile,?compileToFunctions?}?=?createCompiler(baseOptions)export?{?compile,?compileToFunctions?}

可看到,compileToFunctions是通过调用createCompiler生成的,参数baseOptions是web平台的一些配置项,继续看createCompiler函数,定义在src/compiler/index.js中:

import?{?parse?}?from?'./parser/index'import?{?optimize?}?from?'./optimizer'import?{?generate?}?from?'./codegen/index'import?{?createCompilerCreator?}?from?'./create-compiler'//?`createCompilerCreator`?allows?creating?compilers?that?use?alternative//?parser/optimizer/codegen,?e.g?the?SSR?optimizing?compiler.//?Here?we?just?export?a?default?compiler?using?the?default?parts.export?const?createCompiler?=?createCompilerCreator(function?baseCompile?(template:?string,options:?CompilerOptions):?CompiledResult?{//?解析成ast树const?ast?=?parse(template.trim(),?options)//?优化ast树if?(options.optimize?!==?false)?{optimize(ast,?options)}//?生成代码const?code?=?generate(ast,?options)return?{ast,render:?code.render,staticRenderFns:?code.staticRenderFns}})

可看到此处的createCompiler又是通过createCompilerCreator这一个函数去生成的,createCompilerCreator这一个函数的参数是一个函数baseCompile,这里面的逻辑是编译过程的核心,包含ast树的生成、ast树的优化、代码生成,这三个流程我们之后再去详细的分析。我们继续看createCompilerCreator这一个函数,定义在src/compiler/create-compiler.js:

export?function?createCompilerCreator?(baseCompile:?Function):?Function?{return?function?createCompiler?(baseOptions:?CompilerOptions)?{//?这一个函数的作用是将一些配置项合并一下,再执行传入的核心编译函数baseCompilefunction?compile?(template:?string,options?:?CompilerOptions):?CompiledResult?{//?web平台的基础配置,利用原型继承const?finalOptions?=?Object.create(baseOptions)const?errors?=?[]const?tips?=?[]finalOptions.warn?=?(msg,?tip)?=>?{(tip?tips?:?errors).push(msg)}//?配置合并if?(options)?{//?merge?custom?modulesif?(options.modules)?{finalOptions.modules?=(baseOptions.modules?||?[]).concat(options.modules)}//?merge?custom?directivesif?(options.directives)?{finalOptions.directives?=?extend(Object.create(baseOptions.directives?||?null),options.directives)}//?copy?other?optionsfor?(const?key?in?options)?{if?(key?!==?'modules'?&&?key?!==?'directives')?{finalOptions[key]?=?options[key]}}}//?核心编译函数const?compiled?=?baseCompile(template,?finalOptions)if?(process.env.NODE_ENV?!==?'production')?{errors.push.apply(errors,?detectErrors(compiled.ast))}compiled.errors?=?errorscompiled.tips?=?tipsreturn?compiled}return?{compile,compileToFunctions:?createCompileToFunctionFn(compile)}}}

在这里,我们看到,compileToFunctions最终是调用了createCompileToFunctionFn这一个函数,我们继续看createCompileToFunctionFn,定义在src/compiler/to-function.js:

export?function?createCompileToFunctionFn?(compile:?Function):?Function?{//?用来缓存const?cache?=?Object.create(null)return?function?compileToFunctions?(template:?string,options?:?CompilerOptions,vm?:?Component):?CompiledFunctionResult?{//?复制配置项options?=?extend({},?options)const?warn?=?options.warn?||?baseWarndelete?options.warn/*?istanbul?ignore?if?*///?判断是不是支持new?Function语法if?(process.env.NODE_ENV?!==?'production')?{//?detect?possible?CSP?restrictiontry?{new?Function('return?1')}?catch?(e)?{if?(e.toString().match(/unsafe-eval|CSP/))?{warn('It?seems?you?are?using?the?standalone?build?of?Vue.js?in?an?'?+'environment?with?Content?Security?Policy?that?prohibits?unsafe-eval.?'?+'The?template?compiler?cannot?work?in?this?environment.?Consider?'?+'relaxing?the?policy?to?allow?unsafe-eval?or?pre-compiling?your?'?+'templates?into?render?functions.')}}}//?做缓存,编译是很耗时的工作,做缓存有助于提高性能,用空间换时间//?check?cacheconst?key?=?options.delimitersString(options.delimiters)?+?template:?templateif?(cache[key])?{return?cache[key]}//?compile?这儿执行传入的compile函数const?compiled?=?compile(template,?options)//?check?compilation?errors/tips//?检查编译的错误等if?(process.env.NODE_ENV?!==?'production')?{if?(compiled.errors?&&?compiled.errors.length)?{warn(`Error?compiling?template:\n\n${template}\n\n`?+compiled.errors.map(e?=>?`-?${e}`).join('\n')?+?'\n',vm)}if?(compiled.tips?&&?compiled.tips.length)?{compiled.tips.forEach(msg?=>?tip(msg,?vm))}}//?turn?code?into?functions//?这儿将编译生成的字符串代码转换成render函数,通过createFunction函数const?res?=?{}const?fnGenErrors?=?[]res.render?=?createFunction(compiled.render,?fnGenErrors)res.staticRenderFns?=?compiled.staticRenderFns.map(code?=>?{return?createFunction(code,?fnGenErrors)})//?check?function?generation?errors.//?this?should?only?happen?if?there?is?a?bug?in?the?compiler?itself.//?mostly?for?codegen?development?use/*?istanbul?ignore?if?*/if?(process.env.NODE_ENV?!==?'production')?{if?((!compiled.errors?||?!compiled.errors.length)?&&?fnGenErrors.length)?{warn(`Failed?to?generate?render?function:\n\n`?+fnGenErrors.map(({?err,?code?})?=>?`${err.toString()}?in\n\n${code}\n`).join('\n'),vm)}}return?(cache[key]?=?res)}}function?createFunction?(code,?errors)?{try?{return?new?Function(code)}?catch?(err)?{errors.push({?err,?code?})return?noop}}

可看到我们绕了很多圈,最终找到了compileToFunctions这一个函数,现在我们来捋一下这一个执行流程。

1.进入compileToFunctions函数:执行compileToFunctions这一个函数,这一个函数首先会验证当前环境是不是支持newFunction语法,再利用闭包做了一个编译器缓存,然后执行传入的compile函数。

2.进入compile函数compile函数将平台的配置拿到做了一个赋值,再结合当前传入的options参数,做了一些合并配置的操作,然后会执行传入的baseCompile函数,

3.进入baseCompile函数baseCompile函数是编译的核心函数,这一个地方会根据传入的template生成ast树、优化ast树、生成字符串代码,完成编译后,

4.完成baseCompile函数的执行,进入compile函数对编译返回结果compiled对象挂载了errors、tips两个属性,

5.完成compile函数,进入compileToFunctions函数进入compileToFunctions这一个函数,检查编译过程有没有错误,再将生成的字符串代码利用newFunction语法,转换成匿名函数。

最终的这一个匿名函数就是最终生成的render函数。关于模板编译的结果可参考这一个网址。

到此为止,我们清楚了编译的入口了,下一节我们继续去分析编译的核心流程!

点击去往下一节

原文:

以上所转载内容均来自于网络,不为其真实性负责,只为传播网络信息为目的,非商业用途,如有异议请及时联系btr2020@163.com,本人将予以删除。
THE END
分享
二维码
< <上一篇
下一篇>>