.vue 文件是如何编译成浏览器可执行的文件

vue2官网 其中"安装"介绍了关于Vue的各种版本。

  • 编译器:用来将模板字符串编译成为 JavaScript 渲染函数的代码。
javascript 复制代码
<div id="app">
    <div>{{ a }}</div> 
</div> 
<script src="https://cdn.jsdelivr.net/npm/vue@2.7.14/dist/vue.js"></script> <script> 
const vm = new Vue({ el: '#app', data: { a: 'hello world', }, }) 
</script>

以上是一段简单的Vue2代码, 打开浏览器可以正确显示出 "hello world",- 可以发现 <div>{{ a }}</div> 不是浏览器支持的语法. 所以实际上能显示 hello world, 并不是由以上模板代码直接在浏览器执行的结果, 而是通过"编译器"编译, 再由浏览器执行

  • 接下来用代码为大家演示Vue2是如何将上面代码编译的
  • 首先必须引入"完整版",
  • 打开页面, 在控制台可以发现方法: Vue.compile
  • 将刚才的 template 代码放到编译方法里执行, 结果如下
  • 可以看到 template 被编译成 JavaScript,_c,_v,_s,是vue实例vm上一些方法的简称
  • 编译结果可以翻译成
javascript 复制代码
<script>
    const vm = new Vue({
      el: '#app',
      name: 'App',
      data: () => ({
        a: 'hello world!!!',
      }),
      render() {
        const {_c, _v, _s, a} = this
        //return createElement('div', [createTextNode(toString(a))])
        return  _c('div',[_v(_s(a))])
      }
  })
  </script>

这样 模板template就被编译为了 render函数

  • 以上便是在 .html 文件中使用template 开发, 由 Vue2完整版 进行编译的过程

.vue文件是如何进行编译的?

通常项目中是使用Webpack打包, 再由浏览器引入这些js, css等文件, 那么Webpack是如何对.vue文件进行编译的?

1. ,vue文件

.vue文件.vue 文件是一个自定义的文件类型,用类 HTML 语法描述一个 Vue 组件。每个 .vue 文件包含三种类型的顶级语言块 <template><script><style>,还允许添加可选的自定义块.

2. vue-loader

vue-loader 会解析文件,提取每个语言块,如有必要会通过其它 loader 处理,最后将他们组装成一个 ES Module,它的默认导出是一个 Vue.js 组件选项的对象。

接下来通过vue-loader的源码来了解是如何实现编译的

vue-loader/src/index.ts

vue文件分为 template, script, style 三个部分, 而实际必须通过编译才能在浏览器执行的部分只有template, 所以我们在看 vue-loader 源码时可以重点关注 "template" 关键词

descriptor.template 如上图所示, 在源码搜索 template 关键字可以找到这段处理 script, template, style 的代码, 仔细观察发现这三段都在用 descriptor 对象, 接下来搜索 descriptor, 找到这个变量在哪里生成的 descriptor

再来找 parse 方法

compiler.ts compiler.ts

parse 方法从 vue/compiler-sfc 中引用

至此, 了解到 vue-loader 是通过 @vue/compiler-sfc 的 parse 方法解析 .vue 文件, 从中解析出script, template, style

@vue/compiler-sfc

用一段nodejs代码进行验证

javascript 复制代码
// App.vue
<template>
  <div id="app">
    <div>{{ a }}</div>
    <input type="text" v-model="a">
  </div>
</template>

<script>

export default {
  name: 'App',
  data: () => ({
    a: 'hello world'
  }),
}
</script>
javascript 复制代码
// build.js
const { parse } = require('@vue/compiler-sfc')
const fs = require('fs')

const data = fs.readFileSync('./App.vue')

const { descriptor } = parse(data.toString())

// 结果包含很多项, 本文档中只展示重点字段
console.log(Object.keys(descriptor))

运行 build.js

javascript 复制代码
[
  'styles',
	'template',
  'script',
  // ...
]

通过以上代码可以解析出 .vue 文件的三部分, 接下来开始解析 template vue-template-compiler vue-template-compiler 可以将 template 编译成 Javascript

  • 继续修改 build.js, 根据文档加上 compile 的部分
javascript 复制代码
// build.js
const { parse } = require('@vue/compiler-sfc')
const fs = require('fs')
const compiler = require('vue-template-compiler')

const data = fs.readFileSync('./App.vue')

const { descriptor } = parse(data.toString())
// console.log(Object.keys(descriptor))

console.log(compiler.compile(descriptor.template.content).render)
  • 运行结果如下
javascript 复制代码
with(this){
  return _c(
    'div',
    {attrs:{"id":"app"}},
    [
      _c('div',[_v(_s(a))]),
      _v(" "),
      _c(
        'input',
        {
          directives:[{name:"model",rawName:"v-model",value:(a),expression:"a"}],
          attrs:{"type":"text"},
          domProps:{"value":(a)},
          on:{
            "input": function($event){
              if($event.target.composing) return;
              a=$event.target.value
            }
          }
        }
      )
    ]
  )
}

再将以上代码放到 .html 的 new Vue(...) 方法中进行验证

javascript 复制代码
<div id="app"/>

<script src="https://cdn.jsdelivr.net/npm/vue@2.7.14/dist/vue.js"></script>
<script>
  const vm = new Vue({
    name: 'App',
    data: () => ({
      a: 'hello world!!!',
    }),
    render() {
      const {_c, _v, _s, a} = this

      return _c(
        'div',
        {attrs: {'id': 'app'}},
        [
          _c('div', [_v(_s(a))]),
          _v(' '),
          _c(
            'input',
            {
              directives: [{name: 'model', rawName: 'v-model', value: (a), expression: 'a'}],
              attrs: {'type': 'text'},
              domProps: {'value': (a)},
              on: {
								// 注意这里的 this, 需要改成箭头函数
                'input': ($event) => {
                  if ($event.target.composing) return
                  this.a = $event.target.value
                },
              },
            },
          ),
        ],
      )
    },
  }).$mount('#app')
</script>

以上便是 Webpack 如何将 .vue 文件编译成浏览器可执行文件的过程

相关推荐
AlienZHOU2 小时前
AI Coding 时代下,我的技术面试实践分享
前端·后端·面试
Captaincc5 小时前
AI用量v0.1.11更新发布 新增 jusage doctor 诊断指令 托盘展示token 和余额 新增 AutoClaw 支持
前端·后端·vibecoding
计算机魔术师6 小时前
德国Wiki被黑后两周,OpenAI终于把模型失控的账本摊开了
前端
kyriewen7 小时前
我让 AI 当面试官面了我一轮:第 3 个追问我就卡住了(附 10 道追问清单)
前端·面试·ai编程
IT_陈寒7 小时前
Python的GIL把我坑惨了,多线程跑得比单线程还慢
前端·人工智能·后端
前端snow7 小时前
ai agent --- 多agent框架之图编排引擎-langgraph
前端
竹林8187 小时前
OmniPic Studio v3.2.1 核心技术架构与全平台发版解析文档
前端·浏览器
JamesZhang800787 小时前
页面内存只涨不跌? 一次泄漏排查, 牵出 WeakMap 的诞生
前端
Z小明7 小时前
第 6 章 组件进阶
前端·vue.js
江华森7 小时前
HTTP请求的完整过程详解:从DNS解析到TCP挥手的微秒级实战分析
前端