likes
comments
collection
share

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

作者站长头像
站长
· 阅读数 76

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

  • 编译器:用来将模板字符串编译成为 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

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

  • 将刚才的 template 代码放到编译方法里执行, 结果如下

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

  • 可以看到 template 被编译成 JavaScript,_c,_v,_s,是vue实例vm上一些方法的简称

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

  • 编译结果可以翻译成
<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” 关键词

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

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

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

再来找 parse 方法

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

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

compiler.ts compiler.ts

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

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

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

@vue/compiler-sfc

用一段nodejs代码进行验证

// 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>
// 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

[
  'styles',
	'template',
  'script',
  // ...
]

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

  • 继续修改 build.js, 根据文档加上 compile 的部分
// 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)
  • 运行结果如下
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(...) 方法中进行验证

<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 文件编译成浏览器可执行文件的过程

转载自:https://juejin.cn/post/7273384313958187020
评论
请登录