Vue.js
一、Vue.js概述
1. Vue.js介绍
-
Vue.js和Vue相同(读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式JavaScript框架
渐进式框架要实现的目标就是方便项目增量开发(即插即用)
2. Vue.js库的引入方法
-
在HTML页面使用script标签引入Vue.js的库即可使用
远程CDN <script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script> 本地 <script src="vue.min.js"></script>
3. Vue.js的基本组成
-
Vue.js的基本组成
-
标签内容中:插值表达式
{{}}
作用:通常用来获取Vue实例中定义的各个数据(data)
使用场景:可以作为div标签的内容
标签属性中不能使用插值表达式
-
Vue实例中:挂载点(element)
el:'xxx'
作用:定义Vue实例挂载的元素节点,表示Vue接管该区域,Vue会管理el选项命中的元素及其内部元素
使用场景:一般写在Vue实例中,xxx为选择器
el选择挂载点时,建议使用id选择器
-
Vue实例中:数据对象
data:{xxx}
作用: Vue中用到的数据定义在data中
使用场景:一般写在Vue实例中,xxx可以是定义的不同类型数据
data中的类型
- 普通
key:value
,value可以是数字、字符串、布尔值 - 对象类型数据(value是
{}
,在{}
中可以包含多组普通key:value
) - 数组类型(value是
[]
,在[]
中包含多个value)
- 普通
-
Vue实例中:方法
methods:{xxx}
作用:在
{}
中可以包含多组方法定义方法名:function(){}
使用场景:一般写在Vue实例中,其中在一个方法的定义中可以使用this获取当前实例数据对象data中的某字段的value,获取后的字段可以看作是一个全局变量,可以在多个方法的定义中对其value进行改变
-
Vue实例中的其他属性
- computed
- filters
- watch
- components
-
示例Code
<body> <div id="app"> <!-- {{}} 双括号是 VUE 中的差值表达式,将表达式的值输出到 HTML 页面 --> {{name}} <br> {{student.name}} {{student.age}} <ul> <li>{{names[0]}}</li> <li>{{names[1]}}</li> <li>{{names[2]}}</li> </ul> </div> </body> <script> var VM = new Vue({ // 定义 Vue 实例挂载的元素节点,表示 vue 接管该 div el:"#app", // 定义 model 模型数据对象 data:{ name:"hello, how are you doing", // 对象类型 student: { name: "Jeff", age: "18" }, // 数组类型 names: ["Kim", "Chris", "Jack"] } }) </script>
-
4. Vue.js的特点
-
Vue.js声明式渲染的好处
声明数据,Vue帮我们将数据渲染到HTML,将数据和DOM分离
使用jQuery将数据渲染到HTML有时需要将数据和标签进行拼接,这种方式将会影响程序执行效率
在Vue中不需要考虑如何更改DOM元素, 重点放在更改数据, 数据更新之后, 使用数据的那个元素会同步更新
二、Vue.js常用指令
1. 指令概述
- 指令是带有
v-
前缀的特殊属性,通过指令来操作DOM元素(HTML标签)
2. v-text (获取data中的数据)
-
作用: 获取data中的数据,设置标签的内容(类似用插值表达式)
v-text与插值表达式的区别:
若v-text所在的标签中有内容,v-text 获取data数据,设置标签内容,会覆盖当前标签的内容
插值表达式会将标签的内容拼接到v-text获取的内容之后
-
v-text的value中可以使用运算符
+
拼接额外内容(拼接内容支持数字或字符串) -
示例Code
<body> <div id="app"> <h1>{{message}}</h1> <!-- 使用插值表达式,不会覆盖 --> <h1>{{message}}golang</h1> <!-- v-text 获取data数据,设置标签内容,会覆盖之前的内容体--> <!-- 拼接字符串 --> <h2 v-text="message+1">golang</h2> <h2 v-text="message+'abc'"></h2> </div> </body> <script> var VM = new Vue({ el: "#app", data: { message: "Java Developer" } }) </script>
2. v-html (设置标签的 innerHTML)
-
作用: 设置元素的 innerHTML,可以向当前的标签中写入新的标签
与v-text和插值表达式一样可以获取data中的数据,前两种仅仅是获取数据,v-html可以获取整个标签
-
使用场景:
当data数据中某个key对应的value是带链接的a标签时,可以将属性v-html指定属性值为这个数据的key
-
示例Code
<body> <div id="app"> <!-- 获取普通文本 --> {{name}} <h1 v-text="name"></h1> <h1 v-html="name"></h1> <!-- 设置元素的innerHTML --> <h1 v-html="url"></h1> </div> </body> <script> var VM = new Vue({ el: "#app", data: { name: "JavaScript Developer", url: "<a href='http://www.baidu.com'>baidu</a>" } }) </script>
3. v-on (为标签绑定事件)
-
作用:为元素绑定事件, 比如:
v-on:click="方法名"
,可以简写为@click="方法名"
绑定的方法定义在 Vue实例的, methods属性中定义
-
事件绑定方法,可以传入自定义参数(定义方法时,需要定义形参,来接收实际的参数)
-
事件的后面跟上
.修饰符
可以对事件进行限制,如.enter
可以限制触发的按键为回车 -
示例Code
Vue实例methods定义的方法中,使用this代表当前实例的data数据对象来调用当前实例data中的字段对应的value
<body> <div id="app"> <!-- 单击事件 --> <input type="button" value="f1 click here" v-on:click="f1"> <!-- 单击事件省略写法 --> <input type="button" value="f2 click here" @click="f2"> <!-- 双击事件 --> <input type="button" value="f1 double click" @dblclick="f1"> <!-- 双击事件调用方法并传参 --> <input type="button" value="hello double click" @dblclick="hello(1, 'a')"> <!-- 输入后按下回车调用方法hi --> <input type="text" @keyup.enter="hi"> <h2 @click="f1">{{name}}</h2> </div> </body> <script> var VM = new Vue({ el: "#app", data: { name: "Jeff", hobby: "hiking" }, methods: { f1: function() { alert("123!") }, f2: function() { alert(this.name) console.log(this.hobby) }, hi: function() { alert("hi") }, hello: function(p1, p2) { alert(p1+p2) } }, }) </script>
4. v-show (切换内容显示状态)
-
作用:根据布尔值,切换元素内容(可以是h1、img标签等)的显示状态(值为true显示, 为false则隐藏),数据改变之后,显示的状态会同步更新
-
原理:是修改元素的display属性,实现显示或者隐藏
-
使用方法:v-show的属性值设置为Vue实例数据对象data中的值为布尔的字段名
-
示例Code
<body> <div id="app"> <h1 v-show="isShow">Hi, How are you doing?</h1> <button v-on:click="f1">click here</button> </div> </body> <script> var VM = new Vue({ el:"#app", data:{ isShow: true }, methods: { f1: function(){ this.isShow = !this.isShow; } } }) </script>
5. v-if (切换标签的显示状态)
-
作用:作用同v-show,根据布尔值切换元素的显示状态
-
原理:通过操作DOM元素(隐藏整个当前标签),来切换显示状态,效率较低
-
使用场景:频繁切换使用v-show,反之使用v-if
-
示例Code
<body> <div id="app"> <h1 v-if="isShow">Have a good day</h1> <button v-on:click="f1">click here</button> </div> </body> <script> var VM = new Vue({ el:"#app", data:{ isShow: true }, methods: { f1: function(){ this.isShow = !this.isShow; } } }) </script>
6. v-bind (为标签绑定属性)
-
作用:标签属性中不能使用插值表达式,需要使用v-bind为标签绑定属性
-
使用方法:完整写法
v-bind:属性名
,可以简写为:属性名
-
示例Code:
<body> <div id="app"> <!-- 使用v-bind设置src属性值 --> <img v-bind:src="imgSrc" > <!-- v-bind简写 设置src属性值和设置title属性值 --> <img :src="imgSrc" :title="imgTitle" > <!-- 设置class --> <div v-bind:style="{fontSize: size + 'px'}">v-bind</div> <div :style="{fontSize: size + 'px'}">v-bind</div> </div> </body> <script> var VM = new Vue({ el:"#app", data:{ imgSrc:"./img/icon01.jpg", imgTitle:"image01", size:100 } }) </script>
7. v-for (遍历数组生成列表项)
-
作用:根据数据生成列表结构
-
数组经常和 v-for结合使用,数组有两个常用方法:
push()
向数组末尾添加一个或多个元素shift()
把数组中的第一个元素删除 -
数组的长度可以进行动态的变化,会同步更新到页面上,是响应式的
-
示例Code
<body> <div id="app"> <input type="button" value="add" v-on:click="f1"> <input type="button" value="remove" v-on:click="f2"> <!-- 对普通数组进行遍历 --> <ul> <li v-for="(item, index) in arr"> {{index}} city: {{item}} </li> </ul> <!-- 对对象数组进行遍历 --> <ul> <li v-for="p in persons"> {{p.name}} </li> </ul> </div> </body> <script> var VM = new Vue({ el:"#app", data:{ // 普通数组 arr:["Detroit", "Chicago", "San Francisco"], // 对象数组 persons:[ {name: "Jeff"}, {name: "Lucy"}, {name: "Helen"} ] }, methods: { f1: function(){ this.persons.push({name: "Chris"}); }, f2: function(){ this.persons.shift(); } }, }) </script>
三、MVVM模式
1. MVVM概述
-
MVVM模式将页面,分层了 M 、V、和VM ,解释为:
Model: 负责数据存储
View: 负责页面展示
View Model: 负责业务逻辑处理(比如Ajax请求等),对数据进行加工后交给视图展示
-
示例Code
<body> <div id="app"> <!-- View 视图部分 --> <h2>{{name}}</h2> </div> </body> <script> //创建的vue实例,就是 VM ViewModel var VM = new Vue({ el: "#app", //data就是MVVM模式中的 model data: { name: "hello", }, }); </script>
2. 数据双向绑定
-
MVVM提供了数据的双向绑定
-
单向绑定: 就是把Model绑定到View,当我们用JavaScript代码更新Model时,View就会自动更新
<body> <div id="app"> <input type="text" v-bind:value="message"> <input type="button" value= "next one" v-on:click="f1"> </div> </body> <script> var VM = new Vue({ el:"#app", data:{ message:"how are you doing" }, methods:{ f1:function(){ this.message="I am good" } } }) </script>
-
双向绑定: 用户更新了View,Model的数据也自动被更新了,这种情况就是双向绑定
更新View的情况:
填写表单就是一个最直接的例子。当用户填写表单时,View的状态就被更新了,如果此时
MVVM框架可以自动更新Model的状态,那就相当于我们把Model和View做了双向绑定
-
-
v-mode指令实现数据双向绑定
v-model 指令的作用:
- 便捷的设置和获取表单元素的值
- 绑定的数据会和表单元素值相关联
- 双向数据绑定
<body> <div id="app"> <!-- 通过在view输入内容来修改model --> <input type="text" v-model="message"><br/> <input type="text" v-model="password"><br/> <!-- 按钮通过调用方法修改model来改变view上的输出结果 --> <input type="button" value="update" @click="f1"><br/> {{message}}<br/> {{password}}<br/> </div> </body> <script> var VM = new Vue({ el:"#app", data:{ message:"how are you doing", password:"123" }, methods: { f1:function(){ this.message="this item has been changed"; this.password="this item has been changed"; } }, }) </script>
四、axios库
1. axios概述
-
axios:目前十分流行网络请求库, 专门用来发送请求, 其内部还是Ajax, 进行封装之后使用更加方便
Vue2.0之后建议用axios替换jQuery Ajax
-
作用: 在浏览器中可以帮助我们完成Ajax异步请求的发送
Ajax 是一种在无需重新加载整个网页的情况下,能够更新部分网页的技术, 维护用户体验性, 进行网页的局部刷新
-
axios库的引入方法
<!-- 官网提供的 axios 在线地址 --> <script src="https://unpkg.com/axios/dist/axios.min.js"></script> <!-- 本地引入axios --> <script src="./js/axios.min.js"></script>
2. axios发送请求
-
GET
-
一般格式
axios.get(URL?key=value&key2=value2).then(function(response){},function(error) {});
要访问接口的URL和key主要由接口文档提供
then方法中的传参是回调函数:
function(response){}
表示请求后有响应就会触发function(error) {}
表示请求后失败就会触发通过回调函数的形参可以获取响应的内容或者错误信息
-
示例Code
接口文档
请求地址:https://autumnfish.cn/api/joke/list 请求方法:get 请求参数:num(笑话条数,数字) 响应内容:随机笑话
从URL中获取2条笑话,打印在console上
<body> <input type="button" value="get请求" id="get"/> </body> <script> document.getElementById("get").onclick = function() { axios.get("https://autumnfish.cn/api/joke/list?num=2").then( //请求成功,调用 function(resp) { console.log(resp); }, //请求失败,调用 function(err) { console.log(err); } ); }; </script>
-
-
POST
-
一般格式
axios.post(URL,{key:value,key2:value2}).then(function(response) {},function(error){})
要访问接口的URL和key主要由接口文档提供
then方法中的传参是回调函数:
function(response){}
表示请求后有响应就会触发function(error) {}
表示请求后失败就会触发通过回调函数的形参可以获取响应的内容或者错误信息
-
示例Code
接口文档
请求地址:https://autumnfish.cn/api/user/reg 请求方法:post 请求参数:username(用户名,字符串) 响应内容:注册成功或失败
通过username进行注册,将注册结果打印在控制台上
<body> <input type="button" value="post请求" id="post"/> </body> <script> document.getElementById("post").onclick = function() { axios.post("https://autumnfish.cn/api/user/reg", {username:"123456"}).then( //请求成功,调用 function(resp) { console.log(resp); }, //请求失败,调用 function(err) { console.log(err); } ); }; </script>
-
3. axios案例
-
通过Vue+axios 完成一个获取笑话的案例
接口文档:
请求地址:https://autumnfish.cn/api/joke 请求方法:get 请求参数:无 响应内容:随机笑话
示例Code
axios回调函数中this指向已经改变, 无法访问data中的数据
解决方案:
- 将this进行保存, 回调函数中直接使用保存的this即可
- 或者使用ES6的箭头函数,就可以使用this
<body> <div id="app"> <input type="button" value="get请求" id="get" v-on:click="getJoke()"/> {{message}} </div> </body> <script> var VM = new Vue({ el:"#app", data:{ message:"default content" }, methods: { getJoke:function() { // 回调函数中无法直接调用this,需要进行保存 var that = this; // 异步访问 axios.get("https://autumnfish.cn/api/joke").then( function(resp) { console.log(resp); console.log(resp.data); console.log(that.message); that.message = resp.data; }, function(err) { console.log(err); } ); } }, }) </script>
-
解决插值表达式闪烁的问题
-
问题:当网络较慢,网页还在加载 Vue.js ,而导致 Vue 来不及渲染,这时页面就会显示出 Vue 源代码
-
解决方法:可以使用 v-cloak 指令来解决这一问题,可以使页面加载完成后再进行渲染
-
添加样式
<style> /* 通过属性选择器,设置 添加了v-cloak */ [v-cloak] { display: none; } </style>
-
在id为app的div中添加v-cloak
<div class="wrap" id="app" v-cloak>
-
-
五、computed
1. 计算属性概述
-
作用: 减少运算次数,缓存运算结果,运用于重复相同的计算
-
定义函数也可以实现与 计算属性相同的效果,都可以简化运算
不同的是计算属性是基于它们的响应式依赖进行缓存的,只在相关响应式依赖发生改变时它们才会重新求值
2. 计算属性案例
-
示例Code
<body> <div id="app"> <!-- 重复调用插值表达式 --> <h1>{{a*b}}</h1> <h1>{{a*b}}</h1> <!-- 调用方法res1 --> <h1>{{res1()}}</h1> <h1>{{res1()}}</h1> <!-- 计算属性直接使用属性名 --> <h1>{{res2}}</h1> <h1>{{res2}}</h1> </div> </body> <script> var VM = new Vue({ el:"#app", data:{ a:10, b:20 }, methods:{ res1:function(){ console.log("res1 is running..."); return this.a*this.b; } }, computed:{ res2:function(){ console.log("res2 is running..."); return this.a*this.b; } } }) </script>
六、filter和watch
1. 过滤器概述
- 过滤器常被用来处理文本格式化的操作
- 过滤器使用的两个位置:插值表达式
{{}}
、v-bind表达式中 - 过滤器通过管道(
|
)传输数据
2. 局部过滤器
-
示例Code
需求: 通过过滤器给电脑价格前面 添加一个符号¥
<body> <div id="app"> <p>price of computer: {{price | addIcon}}</p> </div> </body> <script> var VM = new Vue({ el:"#app", data:{ price:200 }, methods: { }, computed: { }, //局部过滤器 filters: { //处理函数,value = price ,是固定参数,表示"|"前的部分 addIcon(value) { return "¥" + value; } } }) </script>
3. 全局过滤器
-
示例Code
需求: 将用户名开头字母大写
<body> <div id="app"> <p>{{user.name | changeName}}</p> </div> </body> <script> //在创建Vue实例之前 创建全局过滤器 Vue.filter("changeName", function(value){ //将姓名开头字母大写,然后再重新拼接 return value.charAt(0).toUpperCase()+value.slice(1); }); var VM = new Vue({ el:"#app", data:{ user:{ name:"tom", } }, }) </script>
4. 侦听器
-
watch:用于观察Vue实例上的数据变动,观察变量的变化,进行相应的处理
-
作用:当有一些数据需要随其他数据变动而变动时,可以使用侦听属性
-
示例Code
<body> <div id="app"> <h2>counter:{{count}}</h2> <input type="button" @click="count++" value="click here"> </div> </body> <script> var VM = new Vue({ el:"#app", data: { count:1 }, watch: { // 监听器中的方法名必须是data中的指定数据名 count:function(nval, oval) { alert("counter has been changed from : " + oval + " to " + nval); } } }) </script>
5. 侦听器案例
-
需求: 监听输入的firstName和lastName的变化,实时显示fullName
-
示例Code
<body> <div id="app"> <div>firstName: <input type="text" v-model="userFirstName"></div> <div>lastName: <input type="text" v-model="userLastName"></div> {{userFullName}} </div> </body> <script> var VM = new Vue({ el:"#app", data: { userFirstName:"default_firstName", userLastName:"default_lastName", userFullName:"default_fullName" }, watch: { //监听,程序在运行的时候,实时监听事件 userFirstName:function(nval, oval){ this.userFullName = nval + " " + this.userLastName; }, userLastName:function(nval, oval){ this.userFullName = this.userFirstName + " " + nval; } } }) </script>
七、component
1. 组件概述
- 将相同的功能进行抽取,封装为组件;在组件化开发时,只需要书写一次代码,随处引入即可使用
- 调用全局组件或是局部组件只能是在Vue接管的区域中,以标签
<组件名></组件名>
方式调用
2. 全局组件
-
基本格式
Vue.component("组件名", { template:"<xxx> </xxx>", data(){ return { xxx:"xxx" } }, methods:{ 方法名(){ } } })
-
template模板表示组件的内容,在template模板中, 只能有一个根元素
-
组件中的data 必须是一个函数, 注意与Vue实例中的data区分
在data函数中的return中定义数据的格式与Vue示例中的data相同
-
组件名以小写开头,采用短横线分割命名: 例如 hello-Word
-
示例Code
<body> <div id="app"> <!-- 可以多次使用组件 --> <cony-header></cony-header> <cony-header></cony-header> <cony-header></cony-header> </div> </body> <script> //全局组件位置在Vue实例之外 Vue.component("cony-header", { //组件的命名一般使用短横线方式, 组件中的模板只能有一个根元素 template: "<div>header component code<h1 @click='hello'>{{msg}}</h1></div>", //组件中的data是一个函数 data() { return { msg: "I am good" } }, methods: { hello(){ alert("hi, how are you doing?"); } }, }); var VM = new Vue({ el:"#app", data:{ }, methods: { }, }); </script>
3. 局部组件
-
基本格式
var VM = new Vue({ el:"app", components: { 组件名: { template: "", data() { return { xxx:"xxx" } } } } })
-
局部组件components指的是写在Vue实例中的组件,与el、methods、data同级
-
在components中可以定义多个组件
-
示例Code
<body> <div id="app"> <web-msg></web-msg> </div> </body> <script> var VM = new Vue({ el:"#app", data:{ }, methods: { }, components: { "web-msg": { template:"<div><h1>{{msg1}}</h1><h2>{{msg2}}</h2></div>", data() { return{ msg1:"developing...", msg2:"complete!" } } } } }) </script>
4. template和组件分离
-
由于把HTML写在组件里面不方便也不好看,所以将它们分开写
-
分离后,浏览器会把 HTML 里的 template 标签过滤掉,所以 template 标签的内容是不会在页面中展示的。直到它被 JS 中的 Vue 调用
-
在 html 中,template 标签一定要有一个 id,因为通过 id 是最直接被选中的。 data 和 methods等参数,全部都要放到 Vue 实例里面写
-
示例Code
<body> <div id="app"> <web-msg></web-msg> </div> <!-- 将模板写在HTML中, 给模板指定一个ID --> <template id="temp1"> <div> <button @click="show">{{msg}}</button> </div> </template> </body> <script> var VM = new Vue({ el:"#app", data:{ }, methods: { }, components: { "web-msg": { // 模板内容与使用id选择器类似 template:"#temp1", data() { return{ msg: "click here" } }, methods: { show() { alert("wait a moment...") } }, } } }) </script>
八、Vue.js的生命周期
1. 生命周期概述
- 每个Vue实例在被创建之前都要经过一系列的初始化过程,这个过程就是Vue.js的生命周期
2. 常用的钩子函数
-
beforeCreate
在Vue对象实例化之前执行 -
created
执行时,组件的实例化已经完成,但是DOM还未生成使用场景:定义created函数在DOM生成之前进行查询操作,created执行完成后显示DOM
-
beforeMount
执行时,模板已经在内存中编辑完成了,但还没有渲染到页面中beforeMount执行时,模板的插值表达式原样显示,不显示挂载组件中的内容
-
mounted
执行时,模板已经被渲染到页面,执行完就会显示页面的内容 -
beforeUpdate
执行时,内存中的数据已经更新了,但是还没有渲染页面 -
updated
执行时,内存中的数据已经更新了,此方法执行完显示页面
九、router库
1. 路由与SPA概述
- 路由:实现了根据指定的URL分配到对应的处理程序
- SPA(single page web application):只有一张Web页面的应用,加载单个HTML页面并在用户与应用程序交互时动态局部更新该页面Web应用程序
3. Vue.js中的路由
-
router:是 Vue.js 官方的路由管理器
-
route:route相当于一条路由,一个路由就对应了一条访问路径,route也代表一个路由对象,一个路由对象中包含了path和component两部分
-
routes:routes代表一组路由,相当于是route数组
-
router-link组件:用于设置一个导航链接,切换不同 HTML内容,to 属性为目标地址, 即要显示的内容
router-link 是对a标签的封装,通过to属性指定连接
-
router-view组件:路由导航到指定组件后,进行渲染显示页面
2. 路由的使用
-
使用步骤:
-
导入Vue.js的router库
-
定义路由所需的组件(作为下一步中的临时变量)
-
定义路由(数组),每个路由有两个部分(path和component)
-
创建路由管理实例VueRouter并初始化实例中的routes属性
-
创建Vue实例,将router注入到vue实例中,让整个应用都有路由的功能,使用
$mount()
指定挂载点Vue 的$mount()为手动挂载,在项目中可用于延时挂载(例如在挂载之前要进行一些其他操作、判断等), 之后要手动挂载上。new Vue时,el和$mount并没有本质上的不同。
-
添加超链接:使用router-link组件来进行导航,to属性指定链接(route中定义的path),指定的链接会跳转到对应的component下
-
添加路由匹配到组件之后的出口:使用router-view组件
-
-
示例Code
测试效果:
- 打开页面后
- 单击go to homePage后
- 单击go to newsPage后
<body> <div id="app"> <h1>test router</h1> <p> <!-- 使用 router-link 组件来导航,to属性指定链接 --> <router-link to="/home">go to homePage</router-link> <router-link to="/news">go to newsPage</router-link> </p> <!-- 路由的出口, 路由匹配到的组件(页面)将渲染在这里 --> <router-view></router-view> </div> </body> <script> //1.定义路由所需的组件(作为定义路由中的中间变量) const home = {template: "<div>首页</div>"}; const news = {template: "<div>新闻</div>"}; //2.定义路由 每个路由都有两部分 path和component const routes = [ {path: "/home", component: home}, {path: "/news", component: news}, ]; //3.创建router路由器实例,对路由对象routes进行管理 const router = new VueRouter({ routes: routes, }); //4.创建Vue实例, 调用挂载mount函数,让整个应用都有路由功能 const VM = new Vue({ router }).$mount("#app");//$mount是手动挂载代替el </script>
本文摘自 :https://www.cnblogs.com/