(一) Vue
概述
- 我使用的vue版本是
v2.5.17
1. 介绍——Vue:渐进式JavaScript框架
-
声明式渲染→组件系统→客户端路由→集中式状态管理→项目构建
-
易用:熟悉HTML、CSS、JavaScript知识后,可快速上手Vue
灵活:在一个库和一套完整框架之间自如伸缩
高效:20kB运行大小,超快虚拟 DOM
-
框架:vue就是一个框架,提供了很多基础性服务,比如虚拟DOM技术,双向绑定技术
- 框架则是为解决一个(一类)问题而开发的产品 (是一个解决方案)
- 使用框架就不一样,我们好像被它控制着,要使用它,就得听它的,按照它的规则来,即使它某些地方我们用不到,或者不喜欢,也不能说什么。
-
库:jquery就是一个库,提供了很多api(工具:函数,属性)
- 库是将代码集合成的一个产品,供程序员调用
- 使用库的时候我们比较随意,想用哪取哪,哪块好用哪,而且把它拿过来可以按照我们的编写意愿来用
-
其实,也可以将库和框架都理解成工具,程序员使用这些工具,可以更快捷开始
-
渐进式:
- Vue提供了一系列的技术,我们可以选择一个或多个,甚至使用全部,这就是渐进式。
- 就是说,可以渐渐的,一点一点的去使用这个框架
- 渐进式,是从vue的使用方式去理解的:
- 你可以在原有大系统的上面,把一两个组件改用它实现,当jQuery用
- 也可以整个用它全家桶开发,当Angular用
- 还可以用它的视图,搭配你自己设计的整个下层用
- 你可以在底层数据逻辑的地方用OO和设计模式的那套理念
- 也可以函数式,都可以
- Vue提供了一系列的技术,我们可以选择一个或多个,甚至使用全部,这就是渐进式。
(二) Vue
基本使用
1. 传统开发模式对比
原生JS
<div id="msg"></div>
<script type="text/javascript">
var msg = 'Hello World';
var div = document.getElementById('msg');
div.innerHTML = msg;
</script>
jQuery
<div id="msg"></div>
<script type="text/javascript" src="js/jquery.js"></script>
<script type="text/javascript">
var msg = 'Hello World';
$('#msg').html(msg);
</script>
- 原生
js
:代码复杂,使用起来非常繁琐 Jquery
:代码简单,使用起来已经非常轻松- 但是虽然Jquery已经非常简洁,但是Vue可以更加简洁
2. Vue.js之HelloWorld基本步骤
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<div>{
{msg}}</div>
</div>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
new Vue({
el: '#app',
data: {
msg: 'HelloWorld'
}
})
</script>
</body>
</html>
3. Vue.js
之HelloWord
细节分析
示例代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<!-- {
{}} 插值表达式 -->
<!-- 3.把数据渲染到页面中需要使用差值语法 -->
<div>{
{msg}}</div>
<div>{
{1 + 2}}</div>
<div>{
{msg + '------' + 123}}</div>
</div>
<!-- 1. 导入 Vue 全局对象 Vue -->
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
// vue 的基本使用步骤
// 1.需要提供标签用于填充数据
// 2.引入 vue.js 库文件 使用开发版
// 3.可以使用vue 的语法做功能了
// 4.把vue提供的数据填充到标签里面
// 需求 把 Hello Vue 渲染到页面
// 2.创建 vue 的实例
var vm = new Vue({
// el 挂载 DOM :可以是id,也可以是类名
el: '#app',
// 数据定义在data里面
data: {
// 键的话随便写 值就是要渲染的内容
msg: 'Hello Vue'
}
});
</script>
</body>
</html>
-
实例参数分析
el
: 元素的挂载位置(值可以是CSS选择器或者DOM元素)-
el
:element
缩写,指定一个vue
关联的元素 -
el
的值:一般用选择器,多数使用ID选择器
data
:模型数据(值是一个对象) -
-
插值表达式用法
将数据填充到HTML标签中
插值表达式支持基本的计算操作
-
Vue代码运行原理分析
概述编译过程的概念(Vue语法→原生语法)
vue代码是经过vue框架编译(翻译)为原生js代码
-
挂载,理解为关联。我们需要将数据与某个元素进行关联,只有关联了,元素内才能使用这个数据
-
插值表达式:插入数值,用于显示/计算data中的数据
-
插值表达式中可以计算:
(三) Vue
模板语法
1. 渲染
1.1 如何理解前端渲染?
渲染:把数据填充到HTML标签中
Vue的模板就是为了处理前端渲染
1.2 前端渲染方式
-
原生
js
拼接字符串 -
使用前端模板引擎
-
使用
vue
特有的模板语法
1.3 原生js
拼接字符串
基本上就是将数据以字符串的方式拼接到HTML标 签中,前端代码风格大体上如右图所示。
缺点:不同开发人员的代码风格差别很大,随着业务的复杂,后期的维护变得逐渐困难起来。
1.4 使用前端模板引擎
右侧代码是基于模板引擎art-template
的一段代 码,与拼接字符串相比,代码明显规范了很多, 它拥有自己的一套模板语法规则。
优点:大家都遵循同样的规则写代码,代码可读性 明显提高了,方便后期的维护。
缺点:没有专门提供事件机制。
art
模板需要通过js
获取dom
,然后通过js
绑定事件
1.5 Vue
模板语法概览
- 插值表达式
- 指令
- 事件绑定
- 属性绑定
- 样式绑定
- 分支循环结构
2. 指令
2.1 什么是指令
- 什么是自定义属性 :自己给标签添加的属性,一般以
data-
开头 - 指令的本质就是自定义属性
- 指令的格式:以
v-
开始(比如:v-cloak
)- v:vue的首字母
- 所以看到
v-xxx
属性,都是vue中的指令
2.2 v-cloak
指令用法
插值表达式存在的问题:“闪动”
如何解决该问题:使用v-cloak指令
解决该问题的原理:先隐藏,替换好值之后再显示最终的值
-
闪动:如果电脑运行比较慢的话,使用插值表达式的地方,会先将插值表达式原样显示,然后再显示实际内容。
-
闪动出现的原因:因为vue渲染的过程就是,先显示,然后vue在替换插值表达式
-
v-cloak
的原理,就是先让元素隐藏,替换好内容之后,再显示。(所以叫遮盖) -
v-cloak
指令的使用步骤-
提供样式
cloak:覆盖,遮盖 [v-cloak]{ display: none; }
-
在插值表达式所在的标签中添加
v-cloak
指令<div v-cloak>{ {msg}}</div>
-
示例代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style type="text/css">
[v-cloak] {
display: none;
}
</style>
</head>
<body>
<div id="app">
<div v-cloak>{
{msg}}</div>
</div>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
// v-cloak 指令的用法
// 1.提供样式
// [v-cloak] {
// display: none,
// }
// 2.在插值表达式所在的标签中添加v-cloak指令
// 背后的原理:先通过样式隐藏内容,然后在内存中进行值的替换,替换好之后显示最终的结果
var vm = new Vue({
el: '#app',
data: {
msg: 'Hello Vue'
}
});
</script>
</body>
</html>
2.3 数据填充指令
2.3.1 v-text
填充纯文本
- 相比插值表达式更加简洁
v-text
:没有闪动问题
2.3.1 v-html
填充HTML
片段
- ① 存在安全问题
- ② 本网站内部数据可以使用,来自第三方的数据不可以用
v-html
:如果是通过ajax
访问自己网站的数据,可以通过v-html
来显示。其他网站的数据不行v-html
的安全问题:xss:跨站脚本攻击
2.3.3v-pre
填充原始信息
显示原始信息,跳过编译过程(分析编译过程)
2.3.4示例代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<div>{
{msg}}</div>
<div v-text='msg'></div>
<div v-html='msg1'></div>
<div v-pre>{
{msg}}</div>
</div>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
// 1、v-text指令用于将数据填充到标签中,作用于插值表达式类似,但是没有闪动问题
// 2、v-html指令用于将HTML片段填充到标签中,但是可能有安全问题
// 3、v-pre用于显示原始信息
var vm = new Vue({
el: '#app',
data: {
msg: 'Hello Vue',
msg1: '<h1>HTML</h1>'
}
});
</script>
</body>
</html>
-
总结
1、
v-text
指令用于将数据填充到标签中,作用于插值表达式类似,但是没有闪动问题(类似innerText
)
2、v-html
指令用于将HTML
片段填充到标签中,但是可能有安全问题(类似innerHTML
)
3、v-pre
用于显示原始信息
2.4 数据响应式
2.4.1 如何理解响应式
html5
中的响应式(屏幕尺寸的变化导致样式的变化)- 数据的响应式(数据的变化导致页面内容的变化)
- 响应式:你变化,我跟着变化(数据变化,界面跟着变化)
2.4.2 数据绑定
- 数据绑定:将数据填充到标签中
- 数据绑定默认就是响应式的
- 插值表达式,数据填充指令,都是数据绑定
2.4.3 v-once
只编译一次
-
显示内容之后不再具有响应式功能
-
v-once
:内容显示一次之后,不在响应式(once:一次) -
v-once
:不是响应式的所以性能高(响应式的需要检测此变量值是否变化,是否重新渲染数据,所以耗性能)<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <div id="app"> <div>{ {msg}}</div> <div v-once>{ {info}}</div> </div> <script type="text/javascript" src="js/vue.js"></script> <script type="text/javascript"> // v-once 的应用场景:如果显示的信息后续不需要再修改,可以使用v-once,这样可以提高性能 var vm = new Vue({ el: '#app', data: { msg: 'Hello Vue', info: 'nihao' } }); </script> </body> </html>
3. 双向数据绑定指令
3.1 双向数据绑定
- 首先存在双方:一方是界面中显示的数据,另一方是vue中的数据
- vue中的数据变化,显示的数据跟着变化
- 显示的数据变化,vue中的数据跟着变化
3.2 双向数据绑定v-model
双向数据绑定,一般用于有与用户交互的控件,比如表单的控件:文本框
v-model
:通过v-model
可以实现双向绑定
语法:msg
是vue
的data
中的数据
<input type="text" v-model='msg'>
- 这样就实现了,
text
这个文本框输入框与msg
双向绑定 - 相当于文本框的
value
属性与数据属性msg
进行了双向绑定value
改变会影响msg
msg
改变会影响value
示例代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<div>{
{msg}}</div>
<div>
<input type="text" v-model='msg'>
</div>
</div>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
// 双向数据绑定
// 1、从页面到数据
// 2、从数据到页面
// mvvm : 设计思想
// m model 数据 ====> data
// v view 试图 ====> html
// vm vue 的 实例
var vm = new Vue({
el: '#app',
data: {
msg: 'Hello Vue'
}
})
</script>
</body>
</html>
效果:
3.3 MVVM
设计思想
-
双向绑定基于
MVVM
设计思想。 -
① M(model) ② V(view) ③ VM(View-Model)
-
图示:
-
视图到模型:采用监听的方式,视图监听数据的变化
-
模型到视图:采用绑定的方式,模型数据渲染到视图中
- 数据绑定:插值表达式,数据填充指令,都是数据绑定
4. 事件绑定
4.1 Vue
如何处理事件
v-on
指令用法
<button v-on:click="doThis"></button>
v-on
缩写:@
<button @click="doThis"></button>
click
:是事件名" "
引号内:可以写表达式,也可以写函数调用
4.2 事件函数的调用方式
直接绑定函数名称
<button v-on:click="doThat"></button>
调用函数
<button v-on:click="doThat()"></button>
- 加不加括号的区别,后边看传参
示例代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<div>{
{num}}</div>
<div>
<button v-on:click='num++'>点击</button>
<button @click='num++'>点击1</button>
<button @click='handle'>点击2</button>
<button @click='handle()'>点击3</button>
</div>
</div>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
// 事件绑定
var vm = new Vue({
el: '#app',
data: {
num: 0
},// 注意点: 这里不要忘记加逗号
// methods 中 主要是定义一些函数
methods: {
handle: function () {
// 这里的this是Vue的实例对象
console.log(this === vm);
// 在函数中 想要使用data里面的数据 一定要加this
this.num++;
}
}
});
</script>
</body>
</html>
4.3 事件函数传参
普通参数和事件对象
<button v-on:click="doThat('hello', $event)"></button>
示例代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<div>{
{num}}</div>
<div>
<!-- 1.如果事件直接绑定函数名称,那么默认会传递事件对象作为事件函数的第一个参数 -->
<button v-on:click='handle1'>点击1</button>
<!-- 2、如果事件绑定函数调用,那么事件对象必须作为最后一个参数显示传递,
并且事件对象的名称必须是$event
-->
<button v-on:click='handle2(123 ,456 ,$event)'>点击2</button>
</div>
</div>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
// 事件绑定-参数传递
// 1.如果事件直接绑定函数名称,那么默认会传递事件对象作为事件函数的第一个参数
// 2.如果事件绑定函数调用,那么事件对象必须作为最后一个参数进行传递(显示传递),并且事件对象的名称必须是$event
var vm = new Vue({
el: '#app',
data: {
num: 0
},
methods: {
handle1: function (event) {
console.log(event.target.innerHTML);
},
handle2: function (p, p1, event) {
console.log(p);
console.log(p1);
console.log(event.target.tagName);
console.log(event.target.innerHTML); //事件源的内部html
this.num++;
},
}
})
</script>
</body>
</html>
注意:
- 只要是事件发生,就会有事件对象
event/e
event.target
:获取到当前的事件源对象,发生事件的元素对象
总结:
- 不加小括号:如果事件直接绑定函数名称,那么默认会传递事件对象作为事件函数的第一个参数
- 加小括号:如果事件绑定函数调用,那么事件对象必须作为最后一个参数显示传递,并且事件对象的名称必须是
$event
4.4 事件修饰符
事件修饰符:用来处理事件的特定行为:阻止事件冒泡,阻止默认行为
语法:在事件名称之后添加 .修饰符
.stop
阻止冒泡
<button v-on:click.stop="doThis"></button>
<button @click.stop="doThis"></button>
.prevent
阻止默认行为
<button v-on:click.prevent="doThis"></button>
<button @click.prevent="doThis"></button>
示例代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<div>{
{num}}</div>
<div v-on:click='handle0'>
<button v-on:click.stop='handle1'>点击1</button>
</div>
<div>
<!-- 超链接有默认行为,点击的时候不仅会触发点击事件,还会默认跳转到href (默认行为) -->
<a href="http://www.baidu.com" v-on:click.prevent='handle2'>百度</a>
<!-- 表单的submit按钮,点击的时候不仅会触发点击事件 ,还会默认提交表单 (默认行为) -->
</div>
</div>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
// 事件绑定-事件修饰符
var vm = new Vue({
el: '#app',
data: {
num: 0
},
methods: {
handle0: function () {
this.num++
},
handle1: function (event) {
// 阻止冒泡
// event.stopPropagation();
},
handle2: function (event) {
// 阻止默认行为
// event.preventDefault();
}
}
})
</script>
</body>
</html>
4.4.1 补充-官网介绍v-on
4.4.2 官网介绍——事件修饰符
地址:https://cn.vuejs.org/v2/guide/events.html#%E4%BA%8B%E4%BB%B6%E4%BF%AE%E9%A5%B0%E7%AC%A6
.stop
.prevent
.capture
.self
.once
.passive
<!-- 阻止单击事件继续传播 -->
<a v-on:click.stop="doThis"></a>
<!-- 提交事件不再重载页面 -->
<form v-on:submit.prevent="onSubmit"></form>
<!-- 修饰符可以串联 --> <!--既阻止冒泡又阻止行为 -->
<a v-on:click.stop.prevent="doThat"></a>
<!-- 只有修饰符 -->
<form v-on:submit.prevent></form>
4.4.3 修饰符连写顺序
<!-- 添加事件监听器时使用事件捕获模式 -->
<!-- 即内部元素触发的事件先在此处理,然后才交由内部元素进行处理 -->
<div v-on:click.capture="doThis">...</div>
<!-- 只当在 event.target 是当前元素自身时触发处理函数 -->
<!-- 即事件不是从内部元素触发的 -->
<div v-on:click.self="doThat">...</div>
使用修饰符时,顺序很重要;相应的代码会以同样的顺序产生。因此,
用
v-on:click.prevent.self
会阻止所有的点击,而
v-on:click.self.prevent
只会阻止对元素自身的点击。
-
添加了
self
修饰符,只有通过绑定事件的元素进行触发事件,才会执行。 -
比如:只有点击了app这个div,才会执行xxx,而点击子div不会触发xxx
<div id="app" @click.prevent.self='xxx'> <div>{ {msg}}</div> </div>
4.4.4 新增修饰符
<!-- 点击事件将只会触发一次 -->
<a v-on:click.once="doThis"></a>
Vue 还对应 addEventListener
中的 passive
选项提供了 .passive
修饰符
<!-- 滚动事件的默认行为 (即滚动行为) 将会立即触发 -->
<!-- 而不会等待 `onScroll` 完成 -->
<!-- 这其中包含 `event.preventDefault()` 的情况 -->
<div v-on:scroll.passive="onScroll">...</div>
这个 .passive
修饰符尤其能够提升移动端的性能。
不要把
.passive
和.prevent
一起使用,因为.prevent
将会被忽略,同时浏览器可能会向你展示一个警告。请记住,.passive
会告诉浏览器你不想阻止事件的默认行为。
4.5 按键修饰符
按键修饰符一般是对键盘事件的修饰。写法:键盘事件.修饰符
修饰符的语法都是:.修饰符
.enter
回车键
<input v-on:keyup.enter="onEnter">
<input @keyup.enter="onEnter">
.delete
删除键
<input v-on:keyup.delete="onDelete">
<input @keyup.delete="onDelete">
示例代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<form action="">
<div>
用户名:
<!--用户名中敲delete,就可以删除所有内容-->
<input type="text" v-on:keyup.delete='clearContent' v-model='uname'>
</div>
<div>
密码:
<!--密码输完之后,敲回车可以做提交操作-->
<input type="text" v-on:keyup.enter='handleSubmit' v-model='pwd'>
</div>
<div>
<input type="button" v-on:click='handleSubmit' value="提交">
</div>
</form>
</div>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
// 事件绑定-按键修饰符
var vm = new Vue({
el: '#app',
data: {
uname: '',
pwd: ''
},
methods: {
clearContent: function () {
// 按delete 键的时候, 清空用户名
this.uname = '';
},
handleSubmit: function () {
//密码输完之后,敲回车可以做提交操作
console.log(this.uname, this.pwd);
}
}
});
</script>
</body>
</html>
补充:所有的按键修饰符如下:
.enter
.tab
.delete
(捕获“删除”和“退格”键).esc
.space
.up
.down
.left
.right
可以通过全局 config.keyCodes
对象自定义按键修饰符别名:
// 可以使用 `v-on:keyup.f1`
Vue.config.keyCodes.f1 = 112
4.6 自定义按键修饰符
全局config.keyCodes
对象
语法:
Vue.config.keyCodes.f1 = 112
f1
是:自定义的修饰符名字112
是f1
的keycode
- 以上是自定义一个f1按键的自定义修饰符
规则:自定义按键修饰符名字是自定义的,但是对应的值必须是按键对应event.keyCode
值
所以要自定义f1按键的自定义修饰符,就必须知道f1的keyCode
(打印一下即可知道)
示例代码:自定义a按键修饰符aaa
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<input type="text" v-on:keyup.aaa='handle' v-model='info'>
</div>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
// 事件绑定-自定义按键修饰符
// 规则:自定义按键修饰符名字是自定义的,对应的值必须是按键对应的event.keyCode值
Vue.config.keyCodes.aaa = 65
var vm = new Vue({
el: '#app',
data: {
info: ''
},
methods: {
handle: function (event) {
console.log(event.keyCode);
}
}
})
</script>
</body>
</html>
4.7. 案例——简单计算器
-
需求:
-
分析:
-
问题:这里的俩文本框,为啥需要双向绑定?
- 数据绑定有单向的:数据填充指令
- 也有双向的:
v-model
- 因为用户输入内容之后,我们的数据也需要跟着变化,这样才能知道用户输入的数据从而计算
- 而数据填充指令,只是数据影响文本框而已
示例代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<h1>简单计算器</h1>
<div>
<span>数值A:</span>
<span>
<input type="text" v-model='a'>
<!-- 文本表单,需要用双向绑定 -->
</span>
</div>
<div>
<span>数值B:</span>
<span>
<input type="text" v-model='b'>
<!-- 文本表单,需要用双向绑定 -->
</span>
</div>
<div>
<button v-on:click='handle'>计算</button>
</div>
<div>
<span>计算结果:</span>
<span v-text='result'></span>
</div>
</div>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
/*
简单计算器案例
*/
var vm = new Vue({
el: '#app',
data: {
a: