文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

有点东西,Template可以直接使用Setup语法糖中的变量原来是因为这个

2024-11-29 22:00

关注

看个demo

看个简单的demo,代码如下:



在上面的demo中定义了四个顶层绑定:Child子组件、从util.js文件中导入的format方法、使用ref定义的msg只读常量、使用let定义的title变量。并且在template中直接使用了这四个顶层绑定。

由于innerContent是在if语句里面的变量,不是

将断点走到第二部分,代码如下:

for (const node of scriptSetupAst.body) {
  if (node.type === "ImportDeclaration") {
    // ...省略
  }
}

for (const node of scriptSetupAst.body) {
  if (
    (node.type === "VariableDeclaration" ||
      node.type === "FunctionDeclaration" ||
      node.type === "ClassDeclaration" ||
      node.type === "TSEnumDeclaration") &&
    !node.declare
  ) {
    // 顶层声明的变量、函数、类、枚举声明组成的setupBindings对象
    // 给setupBindings对象赋值,{msg: 'setup-ref'}
    // 顶层声明的变量组成的setupBindings对象
    walkDeclaration(
      "scriptSetup",
      node,
      setupBindings,
      vueImportAliases,
      hoistStatic
    );
  }
}

在这一部分的代码中使用for循环遍历了两次scriptSetupAst.body,scriptSetupAst.body为script中的代码对应的AST抽象语法树中body的内容,如下图:

图片

从上图中可以看到scriptSetupAst.body数组有6项,分别对应的是script模块中的6块代码。

第一个for循环中使用if判断node.type === "ImportDeclaration",也就是判断是不是import语句。如果是import语句,那么import的内容肯定是顶层绑定,需要将import导入的内容存储到ctx.userImports对象中。注:后面会专门写一篇文章来讲如何收集所有的import导入。

通过这个for循环已经将所有的import导入收集到了ctx.userImports对象中了,在debug终端看看此时的ctx.userImports,如下图:

图片

从上图中可以看到在ctx.userImports中收集了三个import导入,分别是Child组件、format函数、ref函数。

在里面有几个字段需要注意,isUsedInTemplate表示当前import导入的东西是不是在template中使用,如果为true那么就需要将这个import导入塞到return对象中。

isType表示当前import导入的是不是type类型,因为在ts中是可以使用import导入type类型,很明显type类型也不需要塞到return对象中。

我们再来看第二个for循环,同样也是遍历scriptSetupAst.body。如果当前是变量定义、函数定义、类定义、ts枚举定义,这四种类型都属于顶层绑定(除了import导入以外就只有这四种顶层绑定了)。需要调用walkDeclaration函数将这四种顶层绑定收集到setupBindings对象中。

从前面的scriptSetupAst.body图中可以看到if模块的type为IfStatement,明显不属于上面的这四种类型,所以不会执行walkDeclaration函数将里面的innerContent变量收集起来后面再塞到return对象中。这也就解释了为什么非顶层绑定不能在template中直接使用。

我们在debug终端来看看执行完第二个for循环后setupBindings对象是什么样的,如下图:

从上图中可以看到在setupBindings对象中收集msg和title这两个顶层变量。其中的setup-ref表示当前变量是一个ref定义的变量,setup-let表示当前变量是一个let定义的变量。

移除template模块和style模块

接着将断点走到第三部分,代码如下:

ctx.s.remove(0, startOffset);
ctx.s.remove(endOffset, source.length);

这块代码很简单,startOffset为