Vue是一个前端js框架,由尤雨溪开发,是我的项目javascript
Vue近几年来特别的受关注,三年前的时候angularJS霸占前端JS框架市场很长时间,接着react框架横空出世,由于它有一个特性是虚拟DOM,从性能上碾轧angularJS,这个时候,vue1.0悄悄的问世了,它的优雅,轻便也吸引了一部分用户,开始收到关注,16年中旬,VUE2.0问世,这个时候vue无论从性能上,仍是从成本上都隐隐超过了react,火的一塌糊涂,这个时候,angular开发团队也开发了angular2.0版本,而且改名为angular,吸取了react、vue的优势,加上angular自己的特色,也吸引到不少用户,目前已经迭代到5.0了。css
学习vue是如今前端开发者必须的一个技能。html
js框架帮助开发者写js逻辑代码,在开发应用的时候js的功能划分为以下几点:前端
渲染数据vue
操做dom(写一些效果)java
操做cookie等存储机制apinode
在前端开发中,如何高效的操做dom、渲染数据是一个前端工程师须要考虑的问题,并且当数据量大,流向较乱的时候,如何正确使用数据,操做数据也是一个问题react
而js框架对上述的几个问题都有本身趋于完美的解决方案,开发成本下降。高性能高效率。惟一的缺点就是须要使用必定的成原本学习。webpack
vue是渐进式JavaScript框架ios
“渐进式框架”和“自底向上增量开发的设计”是Vue开发的两个概念
Vue能够在任意其余类型的项目中使用,使用成本较低,更灵活,主张较弱,在Vue的项目中也能够轻松融汇其余的技术来开发,而且由于Vue的生态系统特别庞大,能够找到基本全部类型的工具在vue项目中使用
特色:易用(使用成本低),灵活(生态系统完善,适用于任何规模的项目),高效(体积小,优化好,性能好)
Vue是一个MVVM的js框架,可是,Vue 的核心库只关注视图层,开发者关注的只是m-v的映射关系
Vue的不少api、特性都与angularJS类似,实际上是由于Vue在开发的时候借鉴了不少AngularJS中的特色,而AngularJS中固有的缺点,在Vue中已经解决,也就是青出于蓝而胜于蓝,Vue的学习成本比AngularJS低不少,由于复杂性就低
AngularJS是强主张的,而Vue更灵活
Vue的数据流是单向的,数据流行更清晰
Angular里指令能够是操做dom的,也能够封装一段结构逻辑代码,例如:广告展现模块
Vue中的指令只是操做dom的,用组件来分离结构逻辑
AngularJS的性能比不上Vue
Vue不支持IE8,由于使用了ES5的不少特性
能够直接经过script标签来引入vue.js,有开发版本和生产版本,开发版本通常咱们在开发项目的时候引入,当最后开发完成上线的时候引入生产版本,开发版本没有压缩的,而且有不少提示,而生产版本所有删掉了
在Vue中提供了一个脚手架(命令行工具)能够帮咱们快速的搭建基于webpack的开发环境...
每个应用都有一个根实例,在根实例里咱们经过组件嵌套来实现大型的应用
也就是说组件不必定是必须的,可是实例是必需要有的
在实例化实例的时候咱们能够传入一个;配置项,在配置项中设置不少属性方法能够实现复杂的功能
在配置中能够设置el的属性,el属性表明的是此实例的做用范围
在配置中同过设置data属性来为实例绑定数据
mvc 分为三层,其实M层是数据模型层,它是真正的后端数据在前端js中的一个映射模型,他们的关系是:数据模型层和视图层有映射关系,model改变,view展现也会更改,当view产生用户操做或会反馈给controller,controller更改model,这个时候view又会进行新的数据渲染
这是纯纯的MVC的模式,可是不少框架都会有一些更改
前端mvc框架,如angularjs,backbone:
会发现,用户能够直接操做controller(例如用户更改hash值,conrtoller直接监听hash值变化后执行逻辑代码,而后通知model更改)
控制器能够直接操做view,若是,让某一个标签得到进入页面得到焦点,不须要model来控制,因此通常会直接操做(angularJS,指令)
view能够直接操做model (数据双向绑定)
MVP:
view和model不能直接通讯,全部的交互都由presenter来作,其余部分的通讯都是双向的
view较薄 ,presenter较为厚重
MVVM:
MVVM和MVP及其类似,只是view和viewmodel的通讯是双向绑定,view的操做会自动的像viewmodel经过
这是一个指令,只要有v-的就是指令(directive 操做dom )
在vue中能够经过v-for来循环数据的通知循环dom,语法是item in/of items,接收第二个参数是索引 (item,index) of items,还能够循环键值对,第一个参数是value,第二个是key,第三个依然是索引
在vue中还有v-on来为dom绑定事件,在v-on:后面加上要绑定的事件类型,值里能够执行一些简单javascript表达式:++ -- = ...
能够将一些方法设置在methods里,这样就能够在v-on:click的值里直接写方法名字能够,默认会在方法中传入事件对象,当写方法的时候加了()就能够传参,这个时候若是须要事件对象,那就主动传入$event
v-on绑定的事件能够是任意事件,v-on:能够缩写为@
为何在 HTML 中监听事件?
你可能注意到这种事件监听的方式违背了关注点分离 (separation of concern) 这个长期以来的优良传统。但没必要担忧,由于全部的 Vue.js 事件处理方法和表达式都严格绑定在当前视图的 ViewModel 上,它不会致使任何维护上的困难。实际上,使用 v-on 有几个好处:
在vue中,咱们使用mustache插值({{}})来将数据渲染在模板中
使用v-once指令能够控制只能插入一次值,当数据变化的时候,模板对应的视图不更新
使用v-html指令能够解析html格式的数据
在html标签属性里不能使用mustache插值,这个时候给元素添加动态属性的时候使用v-bind来绑定属性,能够缩写成:
在使用v-bind绑定class和内联样式的时候,vue作了一些优化,可使用对象语法和数组的语法来控制
防止表达式闪烁:
v-cloak
给模板内的元素添加v-cloak属性后,元素在vue没有加载完的时候就有这个属性,当vue加载完成后这个属性就消失了,因此咱们能够给这个属性设置css样式为隐藏
<style>
[v-cloak]{
visibility: hidden;
}
</style>
复制代码
v-text/v-html
v-text会指定将模板内元素的textContent属性替换为指令值所表明的数据,也能够用于防止闪烁 v-html能够解析标签,更改元素的innerHTML,性能比v-text较差
v-pre
跳过元素和其子元素的编译过程,能够用来显示mustache
这是一款vue的插件,能够用来进行数据交互,支持的请求方式:GET/POST/JSONP/OPTIONS...
这个插件官方宣布不在更新维护,也就是说尽可能不要使用
有的时候咱们须要在模板中使用数据a,这个时候就须要用到表达式,可是有的地方咱们须要对a数据进行一些简单的处理后才能使用,那么咱们就会在表达式中写一些js逻辑运算
```
<div id="example">
{{ message.split('').reverse().join('') }}
</div>
```
这样咱们的维护就会很是困难,也不便于阅读
那め咱们就能够在methods里设置一个方法,在模板的表达式中使用这个方法
```
<p>Reversed message: "{{ reversedMessage() }}"</p>
// 在组件中
methods: {
reversedMessage: function () {
return this.message.split('').reverse().join('')
}
}
```
可是这个时候,只要vm中有数据变化,这个变化的数据可能和咱们关注的数据无关,可是vm都会从新渲染模板,这个时候表达式中的方法就会从新执行,大大的影响性能
这个时候其实咱们可使用监听器里完成:
在vm实例中设置watch属性,在里面经过键值对来设置一些监听,键名为数据名,值能够是一个函数,这个函数在数据改变以后才会执行,两个参数分别是性格前的值和更改后的值
复制代码
a: function (val, oldVal) {
console.log('new: %s, old: %s', val, oldVal)
}
复制代码
值还能够是一个方法名字,当数据改变的时候这个方法会执行
当数据为object的时候,object的键值对改变不会被监听到(数组的push等方法能够),这个时候须要设置深度监听:
复制代码
c: {
deep:true,
handler:function (val, oldVal) {
console.log('new: %s, old: %s', val, oldVal)
}
},
复制代码
监听的handler函数前面的这几种写法都是在数据变化的时候才会执行,初始化的时候不会执行,可是若是设置immediate为true就能够了
复制代码
num:{
immediate:true,
handler:function(val){
this.nums = val*2
}
}
复制代码
咱们在回到上面的问题,用监听器加上immediate属性就能够作到该效果,可是你们能够看到的是逻辑稍稍有点复杂
watch还能够经过实例对象直接使用:vm.$watch,返回一个取消监听的函数,这个函数执行以后会取消监听
咱们通常都会用到一个叫计算属性的东西来解决:
计算属性就是在实例配置项中经过computed来为vm设置一个新的数据,而这个新数据会拥有一个依赖(一条已经存在的数据),当依赖发送变化的时候,新数据也会发送变化
与方法的方式相比,它性能更高,计算属性是基于它们的依赖进行缓存的。计算属性只有在它的相关依赖发生改变时才会从新求值。相比之下,每当触发从新渲染时,调用方法将总会再次执行函数。
与watch相比,写起来简单,逻辑性更清晰,watch通常多用于,根据数据的变化而执行某些动做,而至于这些动做是在干什么其实无所谓,而计算属性更有针对性,根据数据变化而更改另外一个数据
计算属性也拥有getter和setter,默认写的是getter,设置setter执行能够当此计算属性数据更改的时候去作其余的一些事情,至关于watch这个计算属性
复制代码
xm:{
get:function(){//getter 当依赖改变后设置值的时候
return this.xing+'丶'+this.ming
},
set:function(val){//setter 当自身改变后执行
this.xing = val.split('丶')[0]
this.ming = val.split('丶')[1]
}
}
复制代码
vue中能够设置filter(过滤器)来实现数据格式化,双花括号插值和 v-bind 表达式中使用
vue1.0的有默认的过滤器,可是在2.0的时候所有给去掉了
因此在vue中若是想要使用过滤器就须要自定义
自定义的方法有两种:全局定义和局部定义,全局定义的过滤器在任意的实例、组件中均可以使用,局部定义就是在实例、组件中定义,只能在这个实例或组件中使用
全局定义
Vue.filter(name,handler)
name是过滤器的名字,handler是数据格式化处理函数,接收的第一个参数就是要处理的数据,返回什么数据,格式化的结果就是什么
在模板中经过 | (管道符) 来使用,在过滤器名字后面加()来传参,参数会在handler函数中第二个及后面的形参来接收
<p>{{msg | firstUpper(3,2)}}</p>
Vue.filter('firstUpper',function (value,num=1,num2) {
console.log(num2)
return value.substr(0,num).toUpperCase()+value.substr(num).toLowerCase()
})
复制代码
局部定义
在实例、组件的配置项中设置 filters,键名为过滤器名,值为handler
filters:{
firstUpper:function (value,num=1,num2) {
console.log(num2)
return value.substr(0,num).toUpperCase()+value.substr(num).toLowerCase()
}
}
复制代码
注意:
过滤器只能在mustache插值、v-bind里使用,其余的指令等地方都不能用
复制代码
做业:自定义相似于angularJS中的currency、order、filter过滤器
在Vue中可使用v-if来控制模板里元素的显示和隐藏,值为true就显示,为false就隐藏
v-if控制的是是否渲染这个节点
当咱们须要控制一组元素显示隐藏的时候,能够用template标签将其包裹,将指令设置在template上,等等vm渲染这一组元素的时候,不会渲染template
当有else分支逻辑的时候,能够给该元素加上v-else指令来控制,v-else会根据上面的那个v-if来控制,效果与v-if相反,注意,必定要紧挨着
还有v-else-if指令能够实现多分支逻辑
<input type="text" v-model="mode">
<template v-if="mode=='A'">
<h1>1.title</h1>
<p>个人第一个P标签</p>
</template>
<template v-else-if="mode=='B'">
<h1>2.title</h1>
<p>个人第二个P标签</p>
</template>
<template v-else-if="mode=='C'">
<h1>3.title</h1>
<p>个人第三个P标签</p>
</template>
<template v-else>
<p>很差意思,输入有误</p>
</template>
复制代码
须要注意的另外一个地方是:Vue 会尽量高效地渲染元素,一般会复用已有元素而不是从头开始渲染。这样确实能使Vue变得更快,性能更高,可是有的时候咱们须要让实例去更新dom而不是复用,就须要给dom加上不一样的key属性,由于vue在判断到底渲染什么的时候,包括哪些dom能够复用,都会参考key值,若是dom表现基本一致,符合复用的条件,可是key值不一样,依然不会复用
Vue还提供了v-show指令,用法和v-if基本同样,控制的是元素的css中display属性,从而控制元素的显示和隐藏 , 不能和v-else配合使用,且不能使用在template标签上,由于template不会渲染,再更改它的css属性也不会渲染,不会生效
v-if 是“真正”的条件渲染,由于它会确保在切换过程当中条件块内的事件监听器和子组件适当地被销毁和重建。 v-if 也是惰性的:若是在初始渲染时条件为假,则什么也不作——直到条件第一次变为真时,才会开始渲染条件块。 相比之下,v-show 就简单得多——无论初始条件是什么,元素老是会被渲染,而且只是简单地基于 CSS 进行切换。 通常来讲,v-if 有更高的切换开销,而 v-show 有更高的初始渲染开销。所以,若是须要很是频繁地切换,则使用 v-show 较好;若是在运行时条件不多改变,则使用 v-if 较好。
在Vue中,咱们能够经过定义多个mixin来实现代码抽离复用,便于维护,提高页面的逻辑性
要注意的是:data属性不要使用mixin,由于从逻辑上来讲,每个实例、组件的数据都应该是独立的
一个mixin其实就是一个纯粹的对象,上面挂载着抽离出来的配置,在某一个实例中,经过mixins选项(数组)导入后,此实例就拥有导入的mixin的配置
且导入的配置不会覆盖原有的,而是合并到一块儿
频繁且复杂的dom操做一般是前端性能瓶颈的产生点,Vue提供了虚拟dom的解决办法
虚拟的DOM的核心思想是:对复杂的文档DOM结构,提供一种方便的工具,进行最小化地DOM操做。这句话,也许过于抽象,却基本概况了虚拟DOM的设计思想
(1) 提供一种方便的工具,使得开发效率获得保证 (2) 保证最小化的DOM操做,使得执行效率获得保证
也就是说,虚拟dom的框架/工具都是这么作的:
这样的话,就能大量减小真实dom的操做,提升性能
模块化就是将系统功能分离成独立的功能部分的方法,通常指的是单个的某一种东西,例如js、css
而组件化针对的是页面中的整个完整的功能模块划分,组件是一个html、css、js、image等外链资源,这些部分组成的一个聚合体
优势:代码复用,便于维护
划分组件的原则:复用率高的,独立性强的
组件应该拥有的特性:可组合,可重用,可测试,可维护
在vue中,咱们经过Vue.extend来建立Vue的子类,这个东西其实就是组件
也就是说Vue实例和组件的实例有差异可是差异不带,由于毕竟一个是父类一个是子类
通常的应用,会拥有一个根实例,在根实例里面都是一个一个的组件
由于组件是要嵌入到实例或者父组件里的,也就是说,组件能够互相嵌套,并且,全部的组件最外层必须有一个根实例,因此组件分为:全局组件和局部组件
全局组件在任意的实例、父级组件中都能使用,局部组件只能在建立本身的父级组件或者实例中使用
组件经过不一样的注册方法成为全局、局部组件
建立组件:
Vue.extend(options)
复制代码
全局注册:
var App = Vue.extend({
template:"<h1>hello world</h1>"
})
Vue.component('my-app',App)
复制代码
简便写法:
// 建立组件构造器和注册组件合并一块儿
Vue.component('hello',{//Vue会自动的将此对象给Vue.extend
template:"<h1>hello</h1>"
})
复制代码
组件经过template来肯定本身的模板,template里的模板必须有根节点,标签必须闭合
组件的属性挂载经过:data方法来返回一个对象做为组件的属性,这样作的目的是为了每个组件实例都拥有独立的data属性
局部注册:
new Vue({
el:"#app",
components:{
'my-app':App
}
})
复制代码
简便写法:
data:{},
components:{
'hello':{
template:"<h1>asdasdasdasdasdas</h1>"
}
}
复制代码
在实例或者组件中注册另外一个组件,这个时候,被注册的组件只能在注册它的实例或组件的模板中使用,一个组件能够被多个组件或实例注册
由于vue在解析模板的时候会根据某些html的规则,例如,在table里只能放tr,td,th..,若是放入组件不会解析 这个时候咱们能够放入tr使用is方式来标识这个tr实际上是组件
<table id="app">
<tr is="hello"></tr>
</table>
复制代码
咱们能够在html的某个地方经过template标签来定义组件的模板,在组件的template属性中经过选择器指定对应的template标签内容就能够了,注意,须要给template标签加id来指定
<template id="my-hello">
<div>
<h1>hello world</h1>
<p>hahahah</p>
</div>
</template>
//组件中
template:"#my-hello"
复制代码
在实例、组件的模板中的某一个标签上,能够经过is属性来指定为另外一个目标的组件,这个时候咱们通常会使用component标签来占位、设置is属性来指定目标组件
<component :is="type"></component>
//组件中
data:{
type:'aaa'
},
components:{
'aaa':{template:"<h1>AAAAAAAAAAAAA</h1>"},
'bbb':{template:"<h1>BBBBBBBBBBBBB</h1>"}
}
复制代码
应用中划分的组件可能会不少,为了更好的实现代码复用,因此必然会存在组件的嵌套关系
组件设计初衷就是要配合使用的,最多见的就是造成父子组件的关系:组件 A 在它的模板中使用了组件 B。
组件实例的做用域是孤立的,父组件不能直接使用子组件的数据,子组件也不能直接使用父组件的数据
父组件在模板中使用子组件的时候能够给子组件传递数据
<bbb money="2"></bbb>
复制代码
子组件须要经过props属性来接收后才能使用
'bbb':{
props:['money']
复制代码
若是父组件传递属性给子组件的时候键名有'-',子组件接收的时候写成小驼峰的模式
<bbb clothes-logo='amani' clothes-price="16.58"></bbb>
////
props:['clothesLogo','clothesPrice']
复制代码
咱们能够用 v-bind 来动态地将 prop 绑定到父组件的数据。每当父组件的数据变化时,该变化也会传导给子组件
Prop 是单向绑定的:当父组件的属性变化时,将传导给子组件,可是反过来不会。这是为了防止子组件无心间修改了父组件的状态,来避免应用的数据流变得难以理解。
另外,每次父组件更新时,子组件的全部 prop 都会更新为最新值。这意味着你不该该在子组件内部改变 prop。若是你这么作了,Vue 会在控制台给出警告。
在两种状况下,咱们很容易忍不住想去修改 prop 中数据:
Prop 做为初始值传入后,子组件想把它看成局部数据来用;
Prop 做为原始数据传入,由子组件处理成其它数据输出。 对这两种状况,正确的应对方式是:
定义一个局部变量,并用 prop 的值初始化它:
props: ['initialCounter'],
data: function () {
return { counter: this.initialCounter }
}
//定义一个计算属性,处理 prop 的值并返回:
props: ['size'],
computed: {
normalizedSize: function () {
return this.size.trim().toLowerCase()
}
}
复制代码
注意在 JavaScript 中对象和数组是引用类型,指向同一个内存空间,若是 prop 是一个对象或数组,在子组件内部改变它会影响父组件的状态。
咱们能够为组件的 prop 指定验证规则。若是传入的数据不符合要求,Vue 会发出警告。这对于开发给他人使用的组件很是有用
验证主要分为:类型验证、必传验证、默认值设置、自定义验证
props:{
//类型验证:
str:String,
strs:[String,Number],
//必传验证
num:{
type:Number,
required:true
},
//默认数据
bool:{
type:Boolean,
// default:true,
default:function(){
return true
}
},
//自定义验证函数
nums:{
type:Number,
validator: function (value) {
return value %2 == 0
}
}
}
复制代码
当父组件传递数据给子组件的时候,子组件不接收,这个数据就会挂载在子组件的模板的根节点上
vue里提供了一种将父组件的内容和子组件的模板整合的方法:内容分发,经过slot插槽来实现
<aaa>abc</aaa>
template:"<h1><slot></slot></h1>"
复制代码
在父组件中使用子组件的时候,在子组件标签内部写的内容,在子组件的模板中能够经过来使用
父组件在子组件标签内写的多个内容咱们能够给其设置slot属性来命名,在子组件的模板经过经过使用带有name属性的slot标签来放置对应的slot,当slot不存在的时候,slot标签内写的内容就出现
<my-button>提交</my-button>
<my-button>重置</my-button>
<my-button></my-button>
template:"<button><slot>按钮</slot></button>"
复制代码
Vue提供了transition组件来帮助咱们实现过渡效果,依据就是在控制元素显示隐藏的时候为dom在指定的时刻添加上对应的类名
而咱们只要在这些类名里写上对应的css样式
在进入/离开的过渡中,会有 6 个 class 切换(v表明的是transition的name属性的值)。
v-enter:定义进入过渡的开始状态。在元素被插入时生效,在下一个帧移除。
v-enter-active:定义过渡的状态。在元素整个过渡过程当中做用,在元素被插入时生效,在 transition/animation 完成以后移除。这个类能够被用来定义过渡的过程时间,延迟和曲线函数。
v-enter-to: 2.1.8版及以上 定义进入过渡的结束状态。在元素被插入一帧后生效 (于此同时 v-enter 被删除),在 transition/animation 完成以后移除。
v-leave: 定义离开过渡的开始状态。在离开过渡被触发时生效,在下一个帧移除。
v-leave-active:定义过渡的状态。在元素整个过渡过程当中做用,在离开过渡被触发后当即生效,在 transition/animation 完成以后移除。这个类能够被用来定义过渡的过程时间,延迟和曲线函数。
v-leave-to: 2.1.8版及以上 定义离开过渡的结束状态。在离开过渡被触发一帧后生效 (于此同时 v-leave 被删除),在 transition/animation 完成以后移除。
若是有多个元素须要用transition-group包裹,而且须要有key值作标记
animate.css:
引入animate.css以后,按照下面的写法:
<transition
leave-active-class="animated fadeOut"
enter-active-class="animated slideInLeft">
<p v-if="isShow" class="box"></p>
</transition>
复制代码
在vue中咱们能够不用template来指定组件的模板,而是用render函数来建立虚拟dom结构,用这种方法优势就是性能高,缺点就是使用成本高,代码可读性较低,可使用jsx来在render函数中建立,这样既提升了性能,又减小了成本
可是,咱们在使用了vue-cli脚手架以后,由于脚手架中有对template标签转换虚拟dom的处理,因此,不须要使用jsx,咱们也能高效的转换为createElement形式
通讯:传参、控制(A操控B作一个事件)、数据共享
模式:父子组件间、非父子组件
父组件能够将一条数据传递给子组件,这条数据能够是动态的,父组件的数据更改的时候,子组件接收的也会变化
子组件被动的接收父组件的数据,子组件不要再更改这条数据了
父组件若是将一个引用类型的动态数据传递给子组价的时候,数据会变成双向控制的,子组件改数据的时候父组件也能接收到数据变化,由于子组件改的时候不是在改数据,而是在改数据里的内容,也就是说引用类型数据的地址始终没有变化,不算改父组件数据
父子间数据共享(双向控制)
父组件能够将一个方法传递给子组件,子组件调用这个方法的时候,就能够给父组件传递数据
父组件被动的接收子组件的数据
父组件能够将一个事件绑定在子组件的身上,这个事件的处理程序是父组件某一个方法,当子组件触发本身的这个被绑定的事件的时候,至关于触发了父组件的方法
父组件被动的接收子组件的数据
在组件间能够用过ref造成ref链,组件还拥有一个关系链(children,$root),经过这两种链;理论来讲,任意的两个组件均可以互相访问,互相进行通讯
任意组件通讯,用的少...
event bus 事件总线 小天使 专一于非父子组件的通讯,其实父子组件也可使用,只是没有必要
在B组件的某个钩子函数为event_bus绑定一个事件,事件的处理程序是B想作的事情
在A组件的某一个操做里,触发event_bus绑定的事件
大量组件间数据共享的时候 vuex
每个组件或者实例都会经历一个完整的生命周期,总共分为三个阶段:初始化、运行中、销毁
实例、组件经过new Vue() 建立出来以后会初始化事件和生命周期,而后就会执行beforeCreate钩子函数,这个时候,数据尚未挂载ね,只是一个空壳,没法访问到数据和真实的dom,通常不作操做
挂载数据,绑定事件等等,而后执行created函数,这个时候已经可使用到数据,也能够更改数据,在这里更改数据不会触发updated函数,在这里能够在渲染前倒数第二次更改数据的机会,不会触发其余的钩子函数,通常能够在这里作初始数据的获取
接下来开始找实例或者组件对应的模板,编译模板为虚拟dom放入到render函数中准备渲染,而后执行beforeMount钩子函数,在这个函数中虚拟dom已经建立完成,立刻就要渲染,在这里也能够更改数据,不会触发updated,在这里能够在渲染前最后一次更改数据的机会,不会触发其余的钩子函数,通常能够在这里作初始数据的获取
接下来开始render,渲染出真实dom,而后执行mounted钩子函数,此时,组件已经出如今页面中,数据、真实dom都已经处理好了,事件都已经挂载好了,能够在这里操做真实dom等事情...
当组件或实例的数据更改以后,会当即执行beforeUpdate,而后vue的虚拟dom机制会从新构建虚拟dom与上一次的虚拟dom树利用diff算法进行对比以后从新渲染,通常不作什么事儿
当更新完成后,执行updated,数据已经更改完成,dom也从新render完成,能够操做更新后的虚拟dom
当通过某种途径调用$destroy方法后,当即执行beforeDestroy,通常在这里作一些善后工做,例如清除计时器、清除非指令绑定的事件等等
组件的数据绑定、监听...去掉后只剩下dom空壳,这个时候,执行destroyed,在这里作善后工做也能够
如今使用前端工程化开发项目是主流的趋势,也就是说,咱们须要使用一些工具来搭建vue的开发环境,通常状况下咱们使用webpack来搭建,在这里咱们直接使用vue官方提供的,基于webpack的脚手架工具:vue-cli
安装方法:
# 全局安装 vue-cli
npm install --global vue-cli
# 建立一个基于 webpack 模板的新项目
vue init webpack my-project
//init以后能够定义模板的类型
# 安装依赖,走你
cd my-project
npm install
npm run dev
复制代码
模板类型:
simple 对应的是一个超级简单的html文件
webpack 在配置的时候能够选择是否须要vue-router
注意的是,模板建立的时候会询问使用须要使用ESLINT来标准化咱们的代码
在脚手架中,开发目录是src文件夹,build负责打包的,config是负责配置(内置服务器的端口、proxy代理),static是静态目录,test是测试
src中main.js是入口文件,在里面建立了一个根实例,根实例的模板就是根组件App的模板,其余的组件都在根组件里面进行嵌套实现。
每个组件都是一个单文件组件,这种文件会被webpack利用vue-loader的工具进行编译
template部分负责写组件的模板内容,script中建立组件。style里写组件的样式
assets目录也是静态目录,在这个目标中的文件咱们使用相对路径引入,而static目录中的文件使用绝对地址来引入
在style上添加scoped能使这个style里的样式只做用于当前的组件,不加scoped就是全局样式
习惯于在App.vue根组件的style里写全局样式,而每一个组件的style最好都是局部的
配置sass编译环境
vue-cli没有内置sass编译,咱们须要本身修改配置
下载对应工具:node-sass(4.0.0) sass-loader
在build目录下的webpack.base.conf.js中的module.rule里添加以下配置
{
test: /\.scss$/,
loader:'style-loader!css-loader!sass-loader'
}
复制代码
如今的应用都流行SPA应用(single page application)
传统的项目大多使用多页面结构,须要切换内容的时候咱们每每会进行单个html文件的跳转,这个时候受网络、性能影响,浏览器会出现不定时间的空白界面,用户体验很差
单页面应用就是用户经过某些操做更改地址栏url以后,动态的进行不一样模板内容的无刷新切换,用户体验好。
Vue中会使用官方提供的vue-router插件来使用单页面,原理就是经过检测地址栏变化后将对应的路由组件进行切换(卸载和安装)
import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)
复制代码
new Router(options)
复制代码
var routes = [
{path,component}//path为路径,component为路径对应的路由组件
]
new Router({
routes
})
复制代码
new Vue({
el: '#app',
router,
template: '<App/>',
components: { App }
})
复制代码
利用router-view来指定路由切换的位置
使用router-link来建立切换的工具,会渲染成a标签,添加to属性来设置要更改的path信息,且会根据当前路由的变化为a标签添加对应的router-link-active/router-link-exact-active(彻底匹配成功)类名
<router-link to="main">main</router-link>
<router-link to="news">news</router-link>
.router-link-active{
color:red;
}
复制代码
在建立路由表的时候,能够为每个路由对象建立children属性,值为数组,在这个里面又能够配置一些路由对象来使用多级路由,注意:一级路由path前加'/'
const routes = [
{path:'/main',component:AppMain},
{path:'/news',component:AppNews,children:[
{path:'inside',component:AppNewsInside},
{path:'outside',component:AppNewsOutside}
]},
]
复制代码
二级路由组件的切换位置依然由router-view来指定(指定在父级路由组件的模板中)
<router-link to='inside'>inside</router-link>
<router-link to='outside'>outside</router-link>
<router-view></router-view>
复制代码
当咱们进入应用,默认像显示某一个路由组件,或者当咱们进入某一级路由组件的时候想默认显示其某一个子路由组件,咱们能够配置默认路由:
{path:'',component:Main}
复制代码
当咱们须要进入以后进行重定向到其余路由的时候,或者当url与路由表不匹配的时候:
{path:'',redirect:'/main'}
///...放在最下面
{path:'**',redirect:'/main'},
复制代码
咱们能够给路由对象配置name属性,这样的话,咱们在跳转的时候直接写name:main就会快速的找到此name属性对应的路由,不须要写大量的urlpath路径了
有的时候咱们须要在路由跳转的时候跟上参数,路由传参的参数主要有两种:路径参数、queryString参数
路由参数须要在路由表里设置
{path:'/user/:id',component:User}
复制代码
上面的代码就是给User路由配置接收id的参数,多个参数继续在后面设置
在组件中能够经过this.$route.params来使用
queryString参数不须要在路由表设置接收,直接设置?后面的内容,在路由组件中经过this.$route.query接收
组件支持用户在具备路由功能的应用中(点击)导航。 经过 to 属性指定目标地址,默认渲染成带有正确连接的 标签,能够经过配置 tag 属性生成别的标签.。另外,当目标路由成功激活时,连接元素自动设置一个表示激活的 CSS 类名。
router-link的to属性,默认写的是path(路由的路径),能够经过设置一个对象,来匹配更多
:to='{name:"detail",params:{id:_new.id},query:{content:_new.content}}'
复制代码
name是要跳转的路由的名字,也能够写path来指定路径,可是用path的时候就不能使用params传参,params是传路径参数,query传queryString参数
replace属性能够控制router-link的跳转不被记录\
active-class属性能够控制路径切换的时候对应的router-link渲染的dom添加的类名
有的时候须要在跳转前进行一些动做,router-link直接跳转,须要在方法里使用$router的方法
router.push = router-link:to router.replace = router-link:to.replace router.go() = window.history.go
路由有两种模式:hash、history,默认会使用hash模式,可是若是url里不想出现丑陋hash值,在new VueRouter的时候配置mode值为history来改变路由模式,本质使用H5的histroy.pushState方法来更改url,不会引发刷新,可是须要后端进行路由的配置
在某些状况下,当路由跳转前或跳转后、进入、离开某一个路由前、后,须要作某些操做,就可使用路由钩子来监听路由的变化
全局路由钩子:
router.beforeEach((to, from, next) => {
//会在任意路由跳转前执行,next必定要记着执行,否则路由不能跳转了
console.log('beforeEach')
console.log(to,from)
//
next()
})
//
router.afterEach((to, from) => {
//会在任意路由跳转后执行
console.log('afterEach')
})
复制代码
单个路由钩子: 只有beforeEnter,在进入前执行,to参数就是当前路由
routes: [
{
path: '/foo',
component: Foo,
beforeEnter: (to, from, next) => {
// ...
}
}
]
复制代码
路由组件钩子:
beforeRouteEnter (to, from, next) {
// 在渲染该组件的对应路由被 confirm 前调用
// 不!能!获取组件实例 `this`
// 由于当守卫执行前,组件实例还没被建立
},
beforeRouteUpdate (to, from, next) {
// 在当前路由改变,可是该组件被复用时调用
// 举例来讲,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
// 因为会渲染一样的 Foo 组件,所以组件实例会被复用。而这个钩子就会在这个状况下被调用。
// 能够访问组件实例 `this`
},
beforeRouteLeave (to, from, next) {
// 导航离开该组件的对应路由时调用
// 能够访问组件实例 `this`
}
复制代码
有时候想同时(同级)展现多个视图,而不是嵌套展现,例如建立一个布局,有 sidebar(侧导航) 和 main(主内容) 两个视图,这个时候命名视图就派上用场了。你能够在界面中拥有多个单独命名的视图,而不是只有一个单独的出口。若是 router-view 没有设置名字,那么默认为 default。
<router-view class="view one"></router-view>
<router-view class="view two" name="a"></router-view>
<router-view class="view three" name="b"></router-view>
复制代码
一个视图使用一个组件渲染,所以对于同个路由,多个视图就须要多个组件。确保正确使用 components 配置(带上 s):
const router = new VueRouter({
routes: [
{
path: '/',
components: {
default: Foo,//默认的,没有name的router-view
a: Bar,
b: Baz
}
}
]
})
复制代码
在组件中接收路由参数须要this.$route.params.id,代码冗余,如今能够在路由表里配置props:true
{path:'detail/:id',component:AppNewsDetail,name:'detail',props:true}
复制代码
在路由本身中能够经过props接收id参数去使用了
props:['id']
vue官方宣布在2.0版本中再也不对Vue-resource进行维护了,推荐使用axios工具
注意,axios默认配置不会设置session-cookie,须要进行配置
axios.defaults.withCredentials = true
由于vue是mvvm的框架,因此当数据变化的时候,视图会当即更新,视图层产生操做后会自动通知vm来更改model,因此咱们能够实现双向数据绑定,而其中的原理就是实例会将设置的data逐个遍历利用Object.defineProperty给数据生成getter和setter,当数据变化地方时候setter会监听到而且通知对应的watcher工具进行逻辑运算会更新视图
vuex借鉴了flux和redux的思想,可是flux和redux是独立且完整的架构,vuex是耦合与vue框架的,因此使用成本要比flux、redux低
在vue中,咱们能够先在vue实例中声明数据,而后经过{{}}等方式渲染在dom中
Vuex是vue官方的一款状态管理工具,什么是状态呢?咱们在前端开发中有一个概念:数据驱动,页面中任意的显示不一样,都应该有一条数据来控制,而这条数据又叫作state,状态。
在vue中。组件间进行数据传递、通讯很频繁,而父子组件和非父子组件的通讯功能也比较完善,可是,惟一困难的就是多组件间的数据共享,这个问题由vuex来处理
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
//能够设置store管理的state/getter,mutations,actions
const store = new Vuex.Store({
})
复制代码
state就是一个纯对象,上面有一些状态挂载,并且一个应用应该只有一个数据源:单一状态树、惟一数据源
import state from './modules/state'
//能够设置store管理的state/getter,mutations,actions
const store = new Vuex.Store({
state
})
复制代码
这样,咱们就能够在任意的组件中经过this.$store来使用关于store的api
import store from './store'
new Vue({
el: '#app',
router,
store,
template: '<App/>',
components: { App }
})
复制代码
由于在组件中能够经过this.store.state来使用state中管理的数据
data(){
return {
num:this.$store.state.num
}
},
复制代码
可是咱们发现,这样使用的话,当state的数据更改的时候,vue组件并不会从新渲染,不会触发组件的相关生命周期函数
也就是说,若是想要在组件中响应式的使用的时候,咱们须要经过计算属性(computed)来使用
computed:{
num(){
return this.$store.state.num
}
}
复制代码
这样的写法很无趣,并且若是使用的状态较多会产生冗余的感受,因此vuex提供了mapState辅助函数,帮助咱们在组件中获取并使用vuex的store中保存的状态
因此咱们能够这样写:
computed:mapState(['num']),
复制代码
可是若是组件中已经有了num这个数据了,而state中的数据名字也叫num就会照成冲突,这个时候咱们能够在组件使用state的时候,给状态起个别名:
computed:mapState({
// _num:'num',//键名为别名,值字符串表明的是真正的状态
_num(state){//方法名为别名,函数体里还能够对真正的状态作出一些处理
return state.num
}
}),
复制代码
可是,有的时候咱们在组件中还有本身的业务逻辑须要用到计算属性:
computed:{
a(){
return num+1
},
...mapState({
// _num:'num',//键名为别名,值字符串表明的是真正的状态
_num(state){//方法名为别名,函数体里还能够对真正的状态作出一些处理
return state.num
}
}),
},
复制代码
有的时候,咱们须要根据state中的某一个状态派生出一个新的状态,例如,咱们state中有一个num,在某些组件中须要用到是num的二倍的一个状态,咱们就能够经过getters来建立
const getters = {
doublenum(state){
return state.num*2
}
}
复制代码
建立了以后,在组件中经过this.$store.getters来获取里面的数据
固然vuex也提供了mapGetters辅助函数来帮助咱们在组件中使用getters里的状态,且,使用的方法和mapState如出一辙
咱们不能直接在组件中更改state:this.$store.state.num=2,而是须要使用mutations来更改,mutations也是一个纯对象,里面包含不少更改state 的方法,这些方法的形参接收到state,在函数体里更改,这时,组件用到的数据也会更改,实现响应式。
可是咱们也不能直接调用mutations 的方法,须要使用this.$store.commit来调用,第一个参数为调用的方法名,第二げ参数为传递参数
const mutations = {
increment(state){
state.num++
}
}
复制代码
vuex提供了mapMutations方法来帮助咱们在组件中调用mutations 的方法,使用方法和mapState、mapGetters同样
Action 相似于 mutation,不一样在于:
Action 提交的是 mutation,而不是直接变动状态。 Action 能够包含任意异步操做。
也就是说,若是有这样的需求:在一个异步处理以后,更改状态,咱们在组件中应该先调用actions,来进行异步动做,而后由actions调用mutation来更改数据
const actions = {
[CHANGE_NUM]({commit}){
alert(1)
setTimeout(() => {
let num = Math.floor(Math.random()*10)
//调用mitations的方法
commit(CHANGE_NUM,num)
}, 1000);
}
}
``
如上,actions的方法中能够进行异步的动做,且形参会接收store,从中取出commit方法用以调用mutations的方法
在组件中经过this.$store.dispatch方法调用actions的方法
固然也可使用mapMutations来辅助使用
组件使用数据且经过异步动做更改数据的一系列事情:
1.生成store,设置state
2.在根实例中注入store
3.组件经过计算属性或者mapState来使用状态
4.用户产生操做,调用actions的方法,而后进行异步动做
5.异步动做以后,经过commit调用mutations的方法
6.mutations方法被调用后,更改state
7.state中的数据更新以后,计算属性从新执行来更改在页面中使用的状态
8.组件状态被更改...建立新的虚拟dom......
9.组件的模板更新以后从新渲染在dom中
vuex的使用:
目前市场上有两种使用vuex的状况,
第一种:将须要共享、须要管理的状态放入vuex中管理,也就是说在必要时使用
第二种:将全部的数据都交由vuex管理,由vuex来承担更多的责任,组件变得更轻量级,视图层更轻
---
##### 自定义指令
在实现回到顶部功能的时候,咱们写了一个backTop组件,接下来须要经过监听window.scroll事件来控制这个组件显示隐藏
由于可能会有其余的组件会用到这样的逻辑,因此将此功能作成一个自定义指令:
根据滚动的距离控制一个数据为true仍是为false(v-scroll-show)
问题:
惟一须要注意的是,在指令的钩子函数中咱们能够访问到el,也就是使用指令的标签,可是咱们不能直接更改value(指令的值所表明的数据)
因此咱们使用引用类型来进行地址的传递来解决这个问题
接下来有写了一个v-back-top指令,就是将回到顶部功能作成一个指令,哪一个组件或者dom须要使用到回到顶部,就加上这个指令就能够,设置不一样的参数来控制在不一样的状况下触发
##### Vue的组件库
组件库就是通用组件的集合
pc:element-ui iview
mobile: mint-ui
##### nextTick
当咱们在使用一些插件的时候,常常须要在dom更新完成后进行必要操做,可是在vue中提供的api只有updated钩子函数,而在这个函数里,任意数据的变化致使的dom更新完成都会触发,因此极可能会形成无关数据的影响,而使用监听的话只能监听到数据的变化,此时dom尚未更新,咱们只能强行使用setTimeout来处理
这里推荐你们使用nextTick全局方法:
在下次 DOM 更新循环结束以后执行延迟回调。在修改数据以后当即使用这个方法,获取更新后的 DOM。
eq:
getBillBoards(){
axios.get(this.$root.config.host+'mz/v4/api/billboard/home',{
params:{__t:Date.now()}
}).then(res => {
console.log(res.data.data.billboards)
this.billboards = res.data.data.billboards
//当数据更新,dom循环完成后,执行回调
Vue.nextTick(function () {
new Swiper('.app-home-banner',{
loop:true
})
})
})
}
##### keep-alive
在component组件、router-view外面包裹上keep-alive的话,就会对组件进行缓存,当切换回来的时候,组件会当即渲染,理论来讲,切换组件的时候其实会把上一个组件销毁,使用了keep-alive则不会
设置include、exclude属性控制有选择的缓存
include匹配到的组件会被缓存,exclude匹配到的不会被缓存
值能够为逗号隔开的字符串include = 'a,b';正则:include = '/a|b/';数组:include=['a','b']
使用keep-alive缓存的组件连带它的子组件们都会拥有activated、deactivated钩子函数,会在切换回来和要切换出去的时候触发
好比,main作了缓存,可是main的banner咱们但愿每次都去从新获取数据,因此就在banner的activated里获取数据
### Vue试题分析
1. v-for能够实现数据遍历显示,不只能够遍历数组,也能够遍历对象,还能够从数值中取值:
v-for='n in 10' n会打印1-10
2. vue的生命周期钩子:
通用:beforeCreate/created/beforeMount/mounted/beforeUpdate/updated/beforeDestroy/destroyed
路由守卫:beforeRouteEnter/beforeRouteUpdate (2.2 新增)/beforeRouteLeave
keep-alive:activated/deactivated
3. v-if v-show
v-if是真正的条件渲染,会确保在切换中条件块内的事件监听、子组件都会适当的被销毁和重建
v-show老是将节点渲染在dom中,只是基于css:display来控制节点的显示和隐藏
v-if有更高的切换开始,v-show有更高的初始渲染开销
v-if是惰性的,初始条件为假,就不会渲染
4. axios相关
axios请求的时候不会带上cookie,不会影响带宽,能够经过withCredentials:true来设置
对axios 的请求头进行设置:
axios.defaults.headers = {'Content-Type':'...'}
vue2.0不在更新维护vue-resource,官方推荐使用axios
axios拦截器能够拦截请求和响应,在then、catch以前拦截
6. 组件实例的做用域是孤立的,意味着不能(不该该)在子组件模板里直接引用父组件的数据,要让子组件使用父组件数据的话,须要经过props来将父组件的数据传递给子组件,子组件不能也不该该修改父组件传入的数据,可是能够经过传入引用类型的数据来实现数据共享
7.为了让组件能够组合,咱们须要一种方式来混合父组件的内容与子组件本身的模板。这个过程被称为内容分发 (即 Angular 用户熟知的“transclusion”)。Vue.js 实现了一个内容分发 API,参照了当前 Web Components 规范草案,使用特殊的 <slot> 元素做为原始内容的插槽。
a-template:
<p>hello world</p>
<b>
<h1>hello world</h1>
</b>
b-template:
<slot></slot>
....
8. 若是把切换出去的组件保存在内存中,保留状态避免从新渲染,可使用keep-alive
include exclude
9. 注册方式:
全局:Vue.component(name,Vue.extend({}))
局部:{ components:{name:Vue.extend({})} }
10. 事件总线实现非父子组件通讯
//建立bus
let bus = new Vue()
//a
new Vue({
template:'...',
mounted(){
bus.$on('emit-a',function(){
alert(1)
})
}
})
//b
new Vue({
template:'...',
methods:{
emitA(){
bus.$emit('emit-a')
}
}
})
//当b组件的emitA方法被调用的时候,A组件就会执行alert(1)
11. methods和计算属性的区别
假设咱们有一个数据为num,还但愿拥有一个数据为doublenum,并且但愿doublenum的值永远都是num的二倍
方法:
* 由于是直接显示在模板中,也就是说,咱们能够来一个doublenum的方法,这个方法返回num的二倍,将这个方法放到模板中的某个地方执行 {{doublenum()}}
可是,当无关的例如一个str的数据更改的时候,组件会从新建立虚拟dom树,与上一次的虚拟dom树对比以后从新渲染,这个时候在从新渲染模板的时候doublenum函数会被再次的调用,形成没必要要的性能浪费
* 建立一个doublenum数据,使其初始值为num的二倍,而后利用watch来监听这两个数据,在改变的时候更改对应的数据,可是须要初始的为doublenum赋值为num的二倍,若是num是动态获取到的,doublenun赋值会更繁琐
* computed计算数据,咱们能够利用computed来建立一条新的doublenum数据。而且设置它的getter和setter,并与num创建关系,且computed会缓存,在从新渲染的时候,不会从新执行getter和setter
computed:{
doublenum:{
get(){
return this.num*2
},
set(val){
this.num = val/2
}
}
}
12. 绑定class的对象语法和数组语法
<a :class="{a:true,b:false,c:1}"> => </a> => <a class='a c'></a>
data(){
return {
c:'c'
}
}
<a :class = '["a","b",c]'></a> => </a> => <a class='a b c'></a>
13.
new Vue({
el:"#example-3",
methods:{
say(str){
alert(str)
}
}
})
14. 单向数据流
prop是单向绑定的,父组件属性变化,传递给子组件,可是,子组件数据变化,不能直接传递给父组件,也就是数据的流行是从父组件流向子组件的,为了防止子组件修改父组件的数据(会让应用的数据流变的更难开发、更新、维护)
使用了vuex工具的时候,store中数据在组件中使用的过程也是单向数据流,state->vue component->actions->mutations->state->vue component
15. this.$router.push/replace({name:'user',params:{userId:1})
this.$router.push/replace({path:'/register',query:{plan:private})
##### key相关
当数据改变以后,vue会建立新的虚拟dom来和原来的虚拟dom作对比,在建立新的虚拟的dom的时候,会根据key来查找在原来的虚拟dom中有没有某个部分,若是原来的有,此次的也须要,就会实现复用,并且在作diff对比的时候,若是有key会加快对比的查找速度,提升性能
尽可能循环的时候不要将key设置为数组的索引,由于当删除某一个元素的时候,就会致使删除位置下面的全部元素的key值都与上一次虚拟dom的key值不一样,致使复用失败,这个时候咱们最好使用关键的惟一的,例如id这样的数据做为key
若是数据变化只是值的变化而不是条数和位置的变化,可使用索引做为key
##### Vue.use()
Vue.use会查找插件对象里的install方法去执行,而且给install方法里传入Vue对象
var a = {
install(Vue){
Vue.component("my-a",{...})
}
}
Vue.use(a)
##### 进入域后根据不一样的状况显示不一样的页面(PC/MOBILE)
不少状况下,一个应用会有PC和移动端两个版本,而这两个版本由于差异大,内容多,因此不能用响应式开发可是单独开发,而域名只有一个,用户进入域后直接返回对应设备的应用,作法主要有两种:
1. 前端判断并跳转
进入一个应用或者一个空白页面后,经过navigator.userAgent来判断用户访问的设备类型,进行跳转
2. 后端判断并响应对应的应用
用户地址栏进入域的时候,服务器能接收到请求头上包含的userAgent信息,判断以后返回对应应用
---
function foo(){// 第16行
getName = function(){console.log(1)}
return this
}
foo.getName = function(){console.log(2)}
foo.prototype.getName = function(){console.log(3)}
var getName = function(){console.log(4)}
function getName(){console.log(5)}
foo.getName()//2
//foo是一个函数,也能够说是一个对象,因此它也能够挂载一些属性和方法,18行在其上挂载了一个getName方法
//执行的结果是
getName()//4
//21行有一个全局函数,全局函数声明提早后被20行的getName覆盖,因此输出4
foo().getName()//1
//foo()执行完成后,将全局的getName也就是window.getName给更改后返回this,而在这里this执行的就是window,因此最后执行的就是window.getName,因此输出1
getName()//1
//在上面已经更改全局的getName,因此依然是1
new foo.getName()//2
//new 操做符在实例化构造器的时候,会执行构造器函数,也就是说,foo.getName会执行,输出2
new foo().getName()//3
//new操做符的优先级较高,因此会先new foo()获得一个实例,而后再执行实例的getName方法,这个时候,实例的构造器里没有getName方法,就会执行构造器原型上的getName方法
new new foo().getName()//3
//先执行new foo()获得一个实例,而后在new 这个实例的getName方法,这个时候会执行这个方法,因此输出3
//除了本地对象的方法,其余的函数都能new
---
复制代码