专栏首页Super 前端vue-loader&vue-template-compiler详解

vue-loader&vue-template-compiler详解

在 vue 工程中,安装依赖时,需要 vue 和 vue-template-compiler 版本必须保持一致,否则会报错。

Module build failed: Error: Cannot find module ‘vue-template-compiler’

为什么二者版本必须一致呢?vue-template-compiler 承担哪些作用?其和 vue-loader 又有何关联?

vue-template-compiler

作用: 该模块可用于将 Vue 2.0 模板预编译为渲染函数(template => ast => render),以避免运行时编译开销和 CSP 限制。大都数场景下,与 vue-loader一起使用,只有在编写具有非常特定需求的构建工具时,才需要单独使用它

内容安全策略 (CSP) 是一个附加的安全层,用于帮助检测和缓解某些类型的攻击,包括跨站脚本 (XSS) 和数据注入等攻击。

需要注意的是,This package is auto-generated. For pull requests please see src/platforms/web/entry-compiler.js.这个包是自动生成的,请查看 entry-compiler.js 文件。

其文件路径,vue/src/platforms/web/entry-compiler.js

export { parseComponent } from 'sfc/parser'
export { compile, compileToFunctions } from './compiler/index'
export { ssrCompile, ssrCompileToFunctions } from './server/compiler'
export { generateCodeFrame } from 'compiler/codeframe'

可得知,vue-template-compiler 的代码是从 vue 源码中抽离的!接着,我们对比一下 vue-template-compiler 和 vue 关于编译的 API。发现对于 compile 等函数是一致,只是 vue-template-compiler 开放的参数和方法更多。因此,vuevue-template-compiler 的版本必须一致(同一份源码)!

const compiler = require('vue-template-compiler')

const result = compiler.compile(`
  <div id="test">
    <div>
      <p>This is my vue render test</p>
    </div>
    <p>my name is {{myName}}</p>
  </div>`
)

console.log(result)

compiler.compile(template, [options])

{
  ast: ASTElement, // 解析模板生成的ast
  render: string,	 // 渲染函数
  staticRenderFns: Array<string>, // 静态子树
  errors: Array<string>,
  tips: Array<string>
}
{
  ast: {
    type: 1,
    tag: 'div',
    attrsList: [ [Object] ],
    attrsMap: { id: 'test' },
    rawAttrsMap: {},
    parent: undefined,
    children: [ [Object], [Object], [Object] ],
    plain: false,
    attrs: [ [Object] ],
    static: false,
    staticRoot: false
  },
  render: `with(this){return _c('div',{attrs:{"id":"test"}},[
		_m(0),			// 上述提到的静态子树,索引为0 <div><p>This is my vue render test</p></div>
		_v(" "),		// 空白节点 </div> <p> 之间的换行内容
		_c('p',[_v("my name is "+_s(myName))])	// <p>my name is {{myName}}</p>
	])}`,
  staticRenderFns: [
    `with(this){return _c('div',[_c('p',[_v("This is my vue render test")])])}`
  ],
  errors: [],
  tips: []
}

需要注意的是:children 存在3个节点,其中第二个为空节点 { type: 3, text: ' ', static: true } 即代码中的</div><p> 之间的内容。

compiler.compileToFunctions(template)

简化版的 compiler.compile() ,但只返回

{
  render: Function,
  staticRenderFns: Array<Function>
}

其通过 new Function() 方式

compiler.parseComponent(file, [options])

将 SFC (单文件组件或* .vue文件)解析为描述符「以下述提供SFC为例」

{
  template: {
    type: 'template',
    content: '\n<div class="example">{{ msg }}</div>\n',
    start: 10,
    attrs: {},
    end: 54
  },
  script: {
    type: 'script',
    content: '\n' +
      'export default {\n' +
      '  data () {\n' +
      '    return {\n' +
      "      msg: 'Hello world!'\n" +
      '    }\n' +
      '  }\n' +
      '}\n',
    start: 77,
    attrs: {},
    end: 174
  },
  styles: [
    {
      type: 'style',
      content: '\n.example {\n  color: red;\n}\n',
      start: 194,
      attrs: {},
      end: 236
    }
  ],
  customBlocks: [
    {
      type: 'custom1',
      content: '自定义块',
      start: 257,
      attrs: {},
       end: 261
    }
  ],
  errors: []
}

通常用于 SFC 构建工具,如 vue-loader、vueify等

compiler.generateCodeFrame(template, start, end)

高亮展示 start,end 代码段。

compiler.generateCodeFrame(`上述代码`, 15, 20)
1  |  <template>
2  |      <div class="example">{{ msg }}</div>
   |      ^^^^^
3  |    </template>

简介解析过程

入口文件 src/compiller/index.js

// 生成ast语法树
const ast = parse(template.trim(), options)
// 标记静态内容(以免diff的时候需要重复比较)
optimize(ast, options)
// 生成 render function code
const code = generate(ast, options)

vue-loader

webpack loader for Vue Single-File Components. 用于 Vue 单文件组件的 webpack 加载器。

Vue Single-File Components

*.vue 文件是一种自定义文件格式,使用类似于 HTML 的语法来描述 Vue 组件。每个 *.vue 文件都包含三种类型的顶级语言块:<template><script><style>,以及其他可选的自定义块:

<template>
  <div class="example">{{ msg }}</div>
</template>

<script>
export default {
  data () {
    return {
      msg: 'Hello world!'
    }
  }
}
</script>

<style>
.example {
  color: red;
}
</style>

<custom1>自定义块</custom1>

vue-loader 将解析文件,提取每个语言块,如有必要,将它们通过其他加载器进行管道传输,最后将它们组装回ES 模块,其默认导出为 Vue.js 组件选项对象。

  • Template:每个*.vue. 文件一次最多可以包含一个 <template> 块;内容将被提取并传递给 vue-template-compiler 并预编译为 JavaScript 渲染函数,最后注入<script> 部分的导出组件中
  • Script: 每个 *.vue. 文件一次最多可以包含一个 <script> 块;任何针对 .js 文件的 webpack rules 都将应用于 <script> 块中的内容
  • Style: 默认匹配/\.css$/;可以包含多个 <style> 块;可以包含 Scoped 或者 module 属性;任何针对 .css 文件的 webpack rules 都将应用于 <style> 块中的内容
  • Custom Blocks: 自定义块,以满足任何项目的特定需求

如果您希望将 *.vue. 组件拆分为多个文件,则可以使用 src 属性为语言块导入外部文件:

<template src="./template.html"></template>
<style src="./style.css"></style>
<script src="./script.js"></script>
  • 相对路径必须以 ./ 开头
  • 可以从 npm 依赖项导入资源

如何工作

处理 SFC 中的每个语言块,然后组装成最终模块。

  1. 使用 @vue/component-compiler-utils 解析 SFC 为每个语言块生成一个导入 // import the <template> block import render from 'source.vue?vue&type=template' // import the <script> block import script from 'source.vue?vue&type=script' export * from 'source.vue?vue&type=script' // import <style> blocks import 'source.vue?vue&type=style&index=1' script.render = render export default script
  2. 对相应语言块,增加其他处理规则 import script from 'babel-loader!vue-loader!source.vue?vue&type=script' import 'source.vue?vue&type=style&index=1&scoped&lang=scss'
  3. 处理扩展请求时,再次调用 vue-loader,但这次会明确将其传递给匹配的目标加载器
  4. 对于 scoped 等进行处理(VueLoaderPlugin 会注入一个全局的Pitching Loader(src/pitcher.ts),它会拦截Vue的 <template><style>请求并注入必要的加载器)

参考地址

  • https://astexplorer.net/
  • https://github.com/vuejs/vue/tree/v2.6.10/packages/vue-template-compiler
  • https://developer.mozilla.org/zh-CN/docs/Web/Security/CSP
  • https://github.com/vuejs/vue-loader/blob/master/docs/spec.md
  • https://github.com/vuejs/vue-loader/blob/master/docs/guide/custom-blocks.md

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • JavaScript回调函数

    JavaScript API里这样解释:A callback is a function that is passed as an argument to a...

    奋飛
  • Promise 常用API

    这段代码创建一个promise对象,定义了处理onFulfilled和onRejected的函数(handler),然后返回这个promise对象。这个prom...

    奋飛
  • Promise

    注意:Node.js等规定在JavaScript的回调函数的第一个参数为 Error 对象,这也是它的一个惯例。

    奋飛
  • 超简单入门Vuex小示例

    本文转载自掘金上面的一篇博客,原文地址为: 超简单入门Vuex小示例,项目源代码我已经托管到github上面,源代码地址为:https://github.com...

    ccf19881030
  • 如何在Vue面试环节,证明自己值月薪15K?

    回想一下,自己平时面试的坐姿,你在面试官眼里,大概是什么形象,可能是工程师,也可能是键盘侠,或者找麻烦的。

    闰土大叔
  • 12 手写配置启动一个 vue2 项目

    2019年10月5日,vue 团队发布了 Vue3.0 预览版源码,预计到 2020 年第一季度将发布 3.0 正式版。3.0 包涵了许多激动人心的新特性。

    李艺
  • 81道经典Vue面试题总结(长期更新)

    不仅可以帮你一次性详细阅读所有关于vue的面试题、更可以帮你拓展关于vue开发的视野。

    前端博客 : alili.tech
  • Vue2.0仿今日头条

    之前打算做个东西熟悉vue的使用,由于自己蛮喜欢刷手机看看新闻的,借鉴了其他同学的项目(链接在下面),自己也做了一个。项目中还有许多可以完善的地方,不足之处希望...

    前端博客 : alili.tech
  • 【程序源代码】Vue开源项目库汇总

    最近在学习VUE,感觉确实不错的前端框架。但光学习基本有点太慢,时间太长,主要是为了项目上手使用,所以在网上找了找比较好的VUE框架开发的项目实例。分享给大家。...

    程序源代码
  • Vue系列——vue2-webpack2框架搭建之路(1)

    react、vue、angular代表了3种前端工程化的思想,学习三大框架主要是理解它们的核心概念,比如组件、生命周期、单向数据流、双向绑定等。这些概念在非框架...

    前端博客 : alili.tech

扫码关注云+社区

领取腾讯云代金券