浅谈 vue-loader---合格前端

什么是 vue-loader?

vue-loader 是一个 webpack 的 loader,它允许你以一种名为单文件组件的格式撰写 Vue 组件。

如何使用?

1. 安装

npm install vue-loader vue-template-compiler --save-dev

2. 配置 webapck

// webpack.config.jsconst VueLoaderPlugin = require(‘vue-loader/lib/plugin‘)

module.exports = {  mode: ‘development‘,  module: {    rules: [      {        test: /\.vue$/,        loader: ‘vue-loader‘      },      // 它会应用到普通的 `.js` 文件      // 以及 `.vue` 文件中的 `<script>` 块      {        test: /\.js$/,        loader: ‘babel-loader‘      },      // 它会应用到普通的 `.css` 文件      // 以及 `.vue` 文件中的 `<style>` 块      {        test: /\.css$/,        use: [          ‘vue-style-loader‘,          ‘css-loader‘        ]      }    ]  },  plugins: [    // 请确保引入这个插件来施展魔法    new VueLoaderPlugin()  ]}

3. 创建一个 .vue 组件

一个标准的 .vue 组件可以分为三部分:

  • template: 模板
  • script: 脚本
  • stype: 样式
<template>  <div id="app">    <div class="title">{{msg}}</div>  </div></template>

<script>export default {  name: ‘app‘,  data() {    return {      msg: ‘Hello world‘,    };  },}</script>

<style lang="scss">#app {  text-align: center;  color: #2c3e50;  margin-top: 60px;}.title {  color: red;}</style>

4. 见证奇迹的时刻

打包完之后,这个 Vue 组件就会被解析到页面上:

<head>  <style type="text/css">    #app {      text-align: center;      color: #2c3e50;      margin-top: 60px;    }    .title {      color: red;    }  </style></head><body>  <div id="app">    <div class="title">Hello world</div>  </div>  <script type="text/javascript" src="/app.js"></script></body>

上面 Vue 组件里的 <template> 部分解析到 <body> 下,css 部分解析成<style> 标签,<script> 部分则解析到 js 文件里。

简单来说 vue-loader 的工作就是处理 Vue 组件,正确地解析各个部分。

vue-loader 的源码较长,我们分几个部分来解析。

源码解析之主要流程

我们先从入口看起,从上往下看:

module.exports = function (source) {}

vue-loader 接收一个 source 字符串,值是 vue 文件的内容。

const stringifyRequest = r => loaderUtils.stringifyRequest(loaderContext, r)

loaderUtils.stringifyRequest 作用是将绝对路径转换成相对路径。

接下来有一大串的声明语句,我们暂且先不看,我们先看最简单的情况。

const { parse } = require(‘@vue/component-compiler-utils‘)

const descriptor = parse({  source,  compiler: options.compiler || loadTemplateCompiler(loaderContext),  filename,  sourceRoot,  needMap: sourceMap})

parse 方法是来自于 component-compiler-utils,代码简略一下是这样:

// component-compiler-utils parsefunction parse(options) {  const { source, filename = ‘‘, compiler, compilerParseOptions = { pad: ‘line‘ }, sourceRoot = ‘‘, needMap = true } = options;  // ...  output = compiler.parseComponent(source, compilerParseOptions);  // ...  return output;}

可以看到,这里还不是真正 parse 的地方,实际上是调用了compiler.parseComponent 方法,默认情况下 compiler 指的是 vue-template-compiler

// vue-template-compiler parseComponentfunction parseComponent (  content,  options) {  var sfc = {    template: null,    script: null,    styles: [],    customBlocks: [],    errors: []  };  // ...  function start() {}  function end() {}  parseHTML(content, {    warn: warn,    start: start,    end: end,    outputSourceRange: options.outputSourceRange  });  return sfc;}

这里可以看到,parseComponent 应该是调用了 parseHTML 方法,并且传入了两个方法:start 和 end,最终返回 sfc

这一块的源码我们不多说,我们可以猜测 start 和 end 这两个方法应该是会根据不同的规则去修改 sfc,我们看一下 sfc 即 vue-loader 中 descriptor 是怎么样的:

// vue-loader descriptor{  customBlocks: [],  errors: [],  template: {    attrs: {},    content: "\n<div id="app">\n  <div class="title">{{msg}}</div>\n</div>\n",    type: "template"  },  script: {    attrs: {},    content: "... export default {} ...",    type: "script"  },  style: [{    attrs: {      lang: "scss"    },    content: "... #app {} ...",    type: "style",    lang: "scss"  }],}

vue 文件里的内容已经分别解析到对应的 type 去了,接下来是不是只要分别处理各个部分即可。

parseHTML 这个命名是不是有点问题。。。

vue-loader 如何处理不同 type

你们可以先思考五分钟,这里的分别处理是如何处理的?比如,样式内容需要通过style-loader 才能将其放到 DOM 里。

好了,就当作聪明的你已经有思路了。我们继续往下看。

// templatelet templateImport = `var render, staticRenderFns`let templateRequestif (descriptor.template) {  const src = descriptor.template.src || resourcePath  const idQuery = `&id=${id}`  const scopedQuery = hasScoped ? `&scoped=true` : ``  const attrsQuery = attrsToQuery(descriptor.template.attrs)  const query = `?vue&type=template${idQuery}${scopedQuery}${attrsQuery}${inheritQuery}`  const request = templateRequest = stringifyRequest(src + query)  templateImport = `import { render, staticRenderFns } from ${request}`}

// scriptlet scriptImport = `var script = {}`if (descriptor.script) {  const src = descriptor.script.src || resourcePath  const attrsQuery = attrsToQuery(descriptor.script.attrs, ‘js‘)  const query = `?vue&type=script${attrsQuery}${inheritQuery}`  const request = stringifyRequest(src + query)  scriptImport = (    `import script from ${request}\n` +    `export * from ${request}` // support named exports  )}

// styleslet stylesCode = ``if (descriptor.styles.length) {  stylesCode = genStylesCode(    loaderContext,    descriptor.styles,    id,    resourcePath,    stringifyRequest,    needsHotReload,    isServer || isShadow // needs explicit injection?  )}

这三段代码的结构很像,最终作用是针对不同的 type 分别构造一个 import 字符串:

templateImport = "import { render, staticRenderFns } from ‘./App.vue?vue&type=template&id=7ba5bd90&‘";

scriptImport = "import script from ‘./App.vue?vue&type=script&lang=js&‘ \n export * from ‘./App.vue?vue&type=script&lang=js&‘";

stylesCode = "import style0 from ‘./App.vue?vue&type=style&index=0&lang=scss&‘";

这三个 import 语句有啥子用呢, vue-loader 是这样做的:

let code = `${templateImport}${scriptImport}${stylesCode}`.trim() + `\n`code += `\nexport default component.exports`return code

此时, code 是这样的:

code = "import { render, staticRenderFns } from ‘./App.vue?vue&type=template&id=7ba5bd90&‘import script from ‘./App.vue?vue&type=script&lang=js&‘export * from ‘./App.vue?vue&type=script&lang=js&‘import style0 from ‘./App.vue?vue&type=style&index=0&lang=scss&‘

// 省略 ...export default component.exports"

我们知道 loader 会导出一个可执行的 node 模块,也就是说上面提到的 code 是会被 webpack 识别到然后执行的。

我们看到 code 里有三次的 importimport 的文件都是 App.vue,相当于又加载了一次触发这次 vue-loader 的那个 vue 文件。不同的是,这次加载是带参的,分别对应着 template / script / style 三种 type 的处理。

你们可以先思考五分钟,这里的分别处理是如何处理的?

这个问题的答案就是,webpack 在加载 vue 文件时,会调用 vue-loader 来处理vue 文件,之后 return 一段可执行的 js 代码,其中会根据不同 type 分别import 一次当前 vue 文件,并且将参数传递进去,这里的多次 import 也会被vue-loader 拦截,然后在 vue-loader 内部根据不同参数进行处理(比如调用style-loader)。

原文地址:https://www.cnblogs.com/pwindy/p/12290134.html

时间: 2024-08-30 14:36:45

浅谈 vue-loader---合格前端的相关文章

【Vue】浅谈Vue不同场景下组件间的数据交流

浅谈Vue不同场景下组件间的数据“交流” Vue的官方文档可以说是很详细了.在我看来,它和react等其他框架文档一样,讲述的方式的更多的是“方法论”,而不是“场景论”,这也就导致了:我们在阅读完文档许多遍后,写起代码还是不免感到有许多困惑,因为我们不知道其中一些知识点的运用场景.这就是我写这篇文章的目的,探讨不同场景下组件间的数据“交流”的Vue实现 父子组件间的数据交流 父子组件间的数据交流可分为两种: 1.父组件传递数据给子组件 2.子组件传递数据给父组件 父组件传递数据给子组件——pro

浅谈Vue响应式(数组变异方法)

很多初使用Vue的同学会发现,在改变数组的值的时候,值确实是改变了,但是视图却无动于衷,果然是因为数组太高冷了吗? 查看官方文档才发现,不是女神太高冷,而是你没用对方法. 看来想让女神自己动,关键得用对方法.虽然在官方文档中已经给出了方法,但是在下实在好奇的紧,想要解锁更多姿势的话,那就必须先要深入女神的心,于是乎才有了去探索Vue响应式原理的想法.(如果你愿意一层一层地剥开我的心.你会发现,你会讶异-- 沉迷于鬼哭狼嚎 无法自拔QAQ). 前排提示,Vue的响应式原理主要是使用了ES5的Obj

浅谈WEB安全性(前端向)

相信进来的时候你已经看到alert弹窗,显示的是你cookie信息(为配合博客园要求已删除).单纯地在你的客户端弹出信息只是类似于迫使你在自己的房间脱衣服——没人看得到,自然也不算啥恶意行为.那么如果我把你的信息通过脚本发送到我的服务器保存起来呢?先放心,我不打算这么做,也没那笔闲钱去购置一个服务器来做羞羞的事情,也不希望博客园把我这地盘给封掉了. 如同标题所写的,今天要聊的是WEB安全机制,但这“前端”二字倒是说的狭义了些,安全的问题大部分还是更依赖于后端的过滤和拦截措施,后端的朋友如果感兴趣

浅谈Vue项目优化

前几天看到大家说 vue 项目越大越难优化,带来很多痛苦,这是避免不了的,问题终究要解决,框架的性能是没有问题的,各大测试网站都有相关数据.下面进入正题 转自https://blog.csdn.net/qq_33834489/article/details/79144762 基础优化 所谓的基础优化是任何 web 项目都要做的,并且是问题的根源.HTML,CSS,JS 是第一步要优化的点 分别对应到 .vue 文件内的,<template>,<style>,<script&g

浅谈Vue的生命周期模型

Vue实例从创建到销毁的过程,就是生命周期.Vue的生命周期包括:开始创建.初始化数据.编译模板.挂载Dom.渲染→更新→渲染.卸载等一系列过程. 在Vue的整个生命周期中,提供了一系列的事件,可以注册JavaScript方法,达到控制整个过程的目的,在这些javascript方法中的this直接指向的是vue的实例. 在Vue的整个生命周期中,实例可以调用一些生命周期钩子,这提供了执行自定义逻辑的机会. Vue提供的生命周期钩子如下:① beforeCreate在实例初始化之后,数据观测(da

浅谈vue.js

在使用vue.js前,首先得安装vue.js. 对于vue.js的安装,有以下几种方法: 1.通过官网下载: Vue.js 官网下载地址:http://vuejs.org/guide/installation.html 2.使用CDN方法: 以下推荐国外比较稳定的两个 CDN,个人建议下载到本地. BootCDN(国内) : https://cdn.bootcss.com/vue/2.2.2/vue.min.js unpkg:https://unpkg.com/vue/dist/vue.js,

[原]浅谈vue过渡动画,简单易懂

在vue中什么是动画 开始先啰嗦一下,动画的解释(自我理解??) 在一个标签里面的类容,我们视觉看到它,这时候,这个标签以什么形式出现,中间变化了什么,并且以什么形式消失,是有一个过渡的存在的方式,我叫做动画 (不是那种干出,干消失哈??,大神原谅我粗糙的说辞\(^o^)/~) 闲言碎语不多讲,上干货了 在vue中,提供给我们一个很好写过渡动画的内置组件transition 基本用法就是给我们需要动画的标签外面嵌套transition标签,并且给上属性,起码name不要忘了 <transitio

浅谈Vue模板的那些事儿

接触过vue的童鞋都知道,组件的模板一般都是在template选项内定义的,如 1 Vue.component('child-component', { 2 3 template: '<h3>我是闰土大叔</h3>' 4 5 }) 这个用法都是老生常谈了,今天来聊聊Vue的内联模板.看过vue文档的同学都知道,Vue提供了一个内联模板的功能,在使用组件时,给组件标签使用inline-template特性,组件就会把它的内容当做模板,而不是把它当成内容分发,这样做的好处是,让模板更灵

浅谈Vue.use

我们先来看一个简单的事例首先我使用官方脚手架新建一个项目vue init webpack vue-demo然后我创建两个文件index.js plugins.js.我将这两个文件放置在src/classes/vue-use目录下 接下来对这两个文件进行编写 // 文件: src/classes/vue-use/plugins.js const Plugin1 = { install(a, b, c) { console.log('Plugin1 第一个参数:', a); console.log(

数据可视化:浅谈热力图如何在前端实现

作者 个推开发工程师甄鑫 当我们需要用更直观有效的形式来展现各类大数据信息时,热力图无疑是一种很好的方式.作为一种密度图,热力图一般使用具备显著颜色差异的方式来呈现数据效果,热力图中亮色一般代表事件发生频率较高或事物分布密度较大,暗色则反之.值得一提的是,热力图最终效果常常优于离散点的直接显示,可以在二维平面或者地图上直观地展现空间数据的疏密程度或频率高低.那么制作一张完整的热力图,需要前端做哪些工作呢?接下来,我将基于自己在工作过程中的实践,为大家详细解析热力图在前端的实现过程.首先给大家看一