当前位置: 首页> 汽车> 车展 > 手机软件app制作工具_六安房产网_创建网站的流程_简述网络营销的主要方法

手机软件app制作工具_六安房产网_创建网站的流程_简述网络营销的主要方法

时间:2025/7/9 11:16:52来源:https://blog.csdn.net/qq_44983621/article/details/142592794 浏览次数: 0次
手机软件app制作工具_六安房产网_创建网站的流程_简述网络营销的主要方法

文章目录

    • 一、背景
    • 二、结论
    • 三、@vitejs/plugin-vue 插件
      • 调试前物料准备
      • vuePlugin 入口
      • buildStart 方法
      • transform 方法
    • 四、@vue/compiler-sfc 核心包
      • parse 方法
      • compileScript、rewriteDefault 方法
      • compileTemplate 方法
    • 五、整体架构
    • 六、总结
    • 参考资料

一、背景

最近正在研究 react jsx 转化为 js 的过程,在学习完成之后,突然想到既然 react 是用过 babel 完成 jsx 的转化,那 vue 是不是也是使用 babel 的呢,带着这些疑问我开始探索 vue 的编译过程,经过一些资料的查询发现好像和 react 不太一样,但是网上却很少有深入的文章,因此我开始对源码的探索。

二、结论

vue 3 组件在编译过程中使用了 babel/parse 处理 script 中 js 代码处理为普通 js 代码,其余解析均未使用;下面我们将从源码层面解读。

三、@vitejs/plugin-vue 插件

调试前物料准备

  • vue 3 + vite
  • vscode

添加入口断点:

在这里插入图片描述

  • 找到入口文件 vite.config.js
  • 找到对应解析插件 vitejs/plugin-vue 插件 并在对应使用处打上断点
  • 在控制台打开 js 调试终端 (调试方法和浏览器类似)
  • 在对应终端启动我们的项目

vuePlugin 入口

进入 vue() 方法调用中,会出现 vuePlugin 方法这个方法暴露了很多配置属性,因为我们目的关注编译打包过程,所以我们这次只需关系核心的 buildStart 和 transform 即可,因此我们在这两个方法中打上断点

// src/index.ts
function vuePlugin(rawOptions = {}) {// xxxx 省略return {name: "vite:vue",handleHotUpdate(ctx) {},config(config) {},configResolved(config) {},configureServer(server) {},// 开始打打包时处理的内容buildStart() {options.compiler = options.compiler || resolveCompiler(options.root);},async resolveId(id) {},load(id, opt) {},// 转化代码时执行的内容transform(code, id, opt) {// xxxx 省略}};
}

buildStart 方法

buildStart 方法主要是在服务启动前拿到编译的配置

在这里插入图片描述

接下来我们在 build 处打上断点看之后的流程,在控制台输入 options 查看其中内容:

在这里插入图片描述

此图可以看出最初 compiler 为空,我们的代码执行了 resolveCompiler 方法,并把结果存入了 options.compiler 中,为了清楚该方法做了什么,我们 step into 看一下这个方法的实现。

在这里插入图片描述

这个代码可以看出其实就是加载了并且返回了 vue/compiler-sfc 这个核心包,那加载这个包是做什么呢,我们可以接着往下看,继续走下一个断点,然后会发现我们项目启动了,并没有走到下一个断点。

在这里插入图片描述

transform 方法

那么是什么时候怎么才能走到我们的 transform 中呢?,(vite 的特点:先启动再根据加载页面按需加载所需要编译的代码),明白这一点之后我们只需要在浏览器访问这个地址:我这里就是 http://localhost:3000/ 访问之后发现我们的断点进入到了 transform 中:

在这里插入图片描述

从这边也可以看出 vite 打包为什么比 webpack 快

继续看 transform 里面的代码,可以看到 return transformMain() 这个就是我们的核心转化方法,我们继续 step into 该方法:

在这里插入图片描述

接着我们看一下这个方法的入参:

  • code: 就是我们的源代码
  • filename:这个文件的路径
  • options:配置项
  • pluginContext:插件的上下文(this)
  • ssr:暂不考虑
  • asCustomElement:暂不考虑

然后我们其实核心要看的就是这个 code -> 原生 js 的转化,因此往下看这个 code 都在哪里使用,首先我们就可以看到 createDescriptor 这个方法,这个时候我们打印一下descriptor 得到如下图:
在这里插入图片描述

发现我们 code 被解析如下三个部分 templatestylesscript。接着看这里的代码发现如下图 3 行代码:

// 解析 descriptor 中的 script 部分
const { code: scriptCode, map } = await genScriptCode(descriptor, options, pluginContext, ssr);// 解析 descriptor 中的 style 部分
const stylesCode = await genStyleCode(descriptor, pluginContext, asCustomElement, attachedProps);// 解析 descriptor 中的 template 部分
const ({ code: templateCode, map: templateMap } = await genTemplateCode(descriptor, options, pluginContext, ssr));

然后在这里其实就比较清晰了,createDescriptor 先对源代码进行了初步解析,然后返回了 descriptor,然后我们进入 genScriptCodegenStyleCodegenTemplateCode 这三个方法进行具体代码转化。

接下来我们看下这四个方法的具体实现(只保留核心代码):

// 得到原始的 descriptor
function createDescriptor(filename, source, { root, isProduction, sourceMap, compiler }) {const { descriptor, errors } = compiler.parse(source, {filename,sourceMap});return { descriptor, errors };
}// 处理 descriptor 中 script 部分
async function genScriptCode(descriptor, options, pluginContext, ssr) {// 只保留核心代码const script = resolveScript(descriptor, options, ssr);scriptCode = options.compiler.rewriteDefault(script.content, "_sfc_main", xxx);return {code: scriptCode,map};
}
// 处理 css 
async function genStyleCode(descriptor, pluginContext, asCustomElement, attachedProps) {//  没有使用 compiler
}
// 处理 template
async function genTemplateCode(descriptor, options, pluginContext, ssr) {const template = descriptor.template;if (!template.lang && !template.src) {return transformTemplateInMain(template.content, descriptor, options, pluginContext, ssr);}
}function transformTemplateInMain(code, descriptor, options, pluginContext, ssr) {const result = compile(code, descriptor, options, pluginContext, ssr);
}function compile(code, descriptor, options, pluginContext, ssr) {const result = options.compiler.compileTemplate(__spreadProps(__spreadValues({}, resolveTemplateCompilerOptions(descriptor, options, ssr)), {source: code}));return result;
}function resolveScript(descriptor, options, ssr) {resolved = options.compiler.compileScript(descriptor, __spreadProps(__spreadValues({}, options.script), {// xxx}));return resolved;
}

genStyleCode 则处理为 import "/Users/zcy/Desktop/毕设/smart-port/src/App.vue?vue&type=style&index=0&lang.less" 后续文章中会介绍为什么这个东西是怎么解析的,本文不会过多讲解。

经过上面代码分析:可以看出核心处理方法为:

options.compiler.compileTemplate、
options.compiler.compileScript、 
options.compiler.rewriteDefault、
options.compiler.parse

此时再看 options.compiler 这个对象是不是很眼熟呢?这个就是在 buildStart 中 获取到的 vue/compiler-sfc 核心包源码如下:

options.compiler = options.compiler || resolveCompiler(options.root);

因此我们要看懂到底是怎么解析 vue 组件的,就需要深入这个包中。

四、@vue/compiler-sfc 核心包

parse 方法

我们在 options.compiler.parse 方法调用处打上断点,然后我们逐层进入方法:

在这里插入图片描述
在这里插入图片描述

我们进入到了 parse 方法这边可以看到有一个 ast 的转化,如下图调用了 compiler.parse 方法,经过分析发现这个方法源于,@vue/compiler-dom ,从这个包的依赖看出并没有使用 babel,略过这个核心包,因此得出结论 parse 方法中没有使用 babel。

在这里插入图片描述

compileScript、rewriteDefault 方法

继续刚才操作我们看 compileScript 方法:

在这里插入图片描述

顺腾摸瓜找到 parser$2 这个变量的源头

var parser$2 = require('@babel/parser');

在这里插入图片描述

rewriteDefault 也是如此:

在这里插入图片描述

因此得出结论在 script 的解析中会使用 @babel/parse。下面为解析后的源码:

import { ref } from 'vue';const _sfc_main = {setup(__props, { expose }) {expose();const state = ref(1)const __returned__ = { state, ref }
Object.defineProperty(__returned__, '__isScriptSetup', { enumerable: false, value: true })
return __returned__
}
}

compileTemplate 方法

同样使用了 @vue/compiler-dom 进行转化,具体转化细节就不进行详细展开了, 结果会转化为一个 render 函数如下:

import { resolveComponent as _resolveComponent, createVNode as _createVNode, toDisplayString as _toDisplayString, createElementVNode as _createElementVNode, Fragment as _Fragment, openBlock as _openBlock, createElementBlock as _createElementBlock } from "vue"const _hoisted_1 = { id: "nav" }function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {const _component_router_link = _resolveComponent("router-link")const _component_router_view = _resolveComponent("router-view")return (_openBlock(), _createElementBlock(_Fragment, null, [_createElementVNode("div", _hoisted_1, [_createVNode(_component_router_link, { to: "/login" }),_createVNode(_component_router_link, { to: "/" }),_createElementVNode("div", null, _toDisplayString($setup.state), 1 /* TEXT */)]),_createVNode(_component_router_view)], 64 /* STABLE_FRAGMENT */))
}ƒ

在这里插入图片描述

五、整体架构

在这里插入图片描述

六、总结

本文只是出于好奇浅浅研究下了一下 vue3 的编译过程,和其中涉及到 babel 使用的点,对于很多细节没有深入研究,因此只做了一个简单的分析,希望对大家有参考价值。

参考资料

  • 原来 vue3 文件编译是这样工作的!看完后更懂vue3了
关键字:手机软件app制作工具_六安房产网_创建网站的流程_简述网络营销的主要方法

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

责任编辑: