浅谈 vue-loader---合格前端
什么是 vue-loader?
vue-loader是一个webpack的loader,它允许你以一种名为单文件组件的格式撰写Vue组件。
如何使用?
1. 安装
npm install vue-loader vue-template-compiler --save-dev
2. 配置 webapck
// webpack.config.js
const 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 parse
function 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 parseComponent
function 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 里。
好了,就当作聪明的你已经有思路了。我们继续往下看。
// template
let templateImport = `var render, staticRenderFns`
let templateRequest
if (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}`
}
// script
let 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
)
}
// styles
let 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 里有三次的 import,import 的文件都是 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)。
浅谈 vue-loader---合格前端的更多相关文章
- 浅谈Vue.js
作为一名Vue.js的忠实用户,我想有必要写点文章来歌颂这一门美好的语言了,我给它的总体评价是“简单却不失优雅,小巧而不乏大匠”,下面将围绕这句话给大家介绍Vue.js,希望能够激发你对Vue.js的 ...
- 浅谈Vue不同场景下组件间的数据交流
浅谈Vue不同场景下组件间的数据“交流” Vue的官方文档可以说是很详细了.在我看来,它和react等其他框架文档一样,讲述的方式的更多的是“方法论”,而不是“场景论”,这也就导致了:我们在阅读完 ...
- 【Vue】浅谈Vue不同场景下组件间的数据交流
浅谈Vue不同场景下组件间的数据“交流” Vue的官方文档可以说是很详细了.在我看来,它和react等其他框架文档一样,讲述的方式的更多的是“方法论”,而不是“场景论”,这也就导致了:我们在阅读完 ...
- 浅谈Vue下的components模板
浅谈Vue下的components模板在我们越来越深入Vue时,我们会发现我们对HTML代码的工程量会越来越少,今天我们来谈谈Vue下的 components模板的 初步使用方法与 应用 我们先来简单 ...
- 浅谈Vue响应式(数组变异方法)
很多初使用Vue的同学会发现,在改变数组的值的时候,值确实是改变了,但是视图却无动于衷,果然是因为数组太高冷了吗? 查看官方文档才发现,不是女神太高冷,而是你没用对方法. 看来想让女神自己动,关键得用 ...
- 浅谈Vue中计算属性(computed)和方法(methods)的差别
浅谈Vue中计算属性(computed)和方法(methods)的差别 源码地址 methods方法和computed计算属性,两种方式的最终结果确实是完全相同 计算属性是基于它们的响应式依赖进行缓存 ...
- 【Vue】浅谈Vue(一):从模板语法数据绑定、指令到计算属性
写在前面 今年前端届比较有意思,从大漠穷秋发表文章比较angular和vue,继而致歉vue作者.社区,从谷歌辞去Angular Developer PM in China一职并且呼吁大家停止各种无谓 ...
- 浅谈Vue.js2.0核心思想
Vue.js是一个提供MVVM数据双向绑定的库,专注于UI层面,核心思想是:数据驱动.组件系统. 数据驱动: Vue.js数据观测原理在技术实现上,利用的是ES5Object.defineProper ...
- 浅谈WEB安全性(前端向)
相信进来的时候你已经看到alert弹窗,显示的是你cookie信息(为配合博客园要求已删除).单纯地在你的客户端弹出信息只是类似于迫使你在自己的房间脱衣服——没人看得到,自然也不算啥恶意行为.那么如果 ...
- 浅谈vue性能优化
基础优化 所谓的基础优化是任何 web 项目都要做的,并且是问题的根源.HTML,CSS,JS 是第一步要优化的点 分别对应到 .vue 文件内的,<template>,<style ...
随机推荐
- NetBeans8.2 修改代码补全延迟时间
NetBeans早期版本提供修改提示速度的选项,大概是6.5版本之后就没这个修改功能了,但是仍可以配置: Windows配置如下: 1.C:\Users\${用户名}\AppData\Roaming\ ...
- Static、Final、static final
Static.Final.static final final可以修饰:属性,方法,类,局部变量(方法中的变量) 用final关键字修饰的变量,只能进行一次赋值操作,并且在生存期内不可以改变它的值. ...
- ios--->self.view.window在逻辑判断中的作用
- (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that ...
- CTF--HTTP服务--SQL注入-X-Forwarded-For报文头
开门见山 1. 扫描靶场ip,发现PCS 192.168.31.196 2. 扫描靶场开放服务信息 3. 扫描靶场全部信息 4. 探测敏感信息 5. 查看靶场80端口的主界面 6. 使用AVWS工具进 ...
- 什么是 Trait
Trait 是从 PHP 5.4 加入的一种细粒度代码复用的语法.以下是官方手册对 Trait 的描述: Trait 是为类似 PHP 的单继承语言而准备的一种代码复用机制.Trait 为了减少单继承 ...
- 解决AS加载gradle时出现的Could not find com.android.tools.build:gradle:3.5.0.的错误
时间:2019/12/7 最近在做安卓大作业时总是遇到从GitHub上下载下来的demo不能在本地Android studio中运行的问题,感觉真的被安卓中的各种版本给恶心到了,下面记录其中比较典型的 ...
- HTTPS详解二:SSL / TLS 工作原理和详细握手过程
HTTPS 详解一:附带最精美详尽的 HTTPS 原理图 HTTPS详解二:SSL / TLS 工作原理和详细握手过程 在上篇文章HTTPS详解一中,我已经为大家介绍了 HTTPS 的详细原理和通信流 ...
- 曹工说Spring Boot源码(14)-- AspectJ的Load-Time-Weaving的两种实现方式细细讲解,以及怎么和Spring Instrumentation集成
写在前面的话 相关背景及资源: 曹工说Spring Boot源码(1)-- Bean Definition到底是什么,附spring思维导图分享 曹工说Spring Boot源码(2)-- Bean ...
- yum管理及源码安装
一.配置YUM库及更新操作 yum概述 基于RPM包构建的软件更新机制,自动解决软件依赖关系 YUM仓库格式 本地:file:// 网络:ftp://或http:// yum源里面包含的内容 .rpm ...
- Linux磁盘管理及挂载
1.在vm workstations添加磁盘 添加完之后重启一下虚拟机 然后fdisk -l 可以看到虚拟机已经识别到了新的磁盘 2.添加磁盘分区 输入命令 fdisk /dev/sdb 命令(输入 ...