Vue 浅谈前端js框架vue
Vue
Vue 近几年来特别的受关注,三年前的时候 angularJS 霸占前端 JS 框架市场很长时间,接着 react 框架横空出世,因为它有一个特性是虚拟 DOM,从性能上碾轧 angularJS,这个时候,vue1.0 悄悄
的问世了,它的优雅,轻便也吸引了一部分用户,开始收到关注,16 年中旬,VUE2.0 问世,这个时候 vue 不管从性能上,还是从成本上都隐隐超过了 react,火的一塌糊涂,这个时候,angular
开发团队也开发了 angular2.0 版本,并且更名为 angular,吸收了 react、vue 的优点,加上 angular 本身的特点,也吸引到很多用户,目前已经迭代到 5.0 了。
学习 vue 是现在前端开发者必须的一个技能。
前端 js 框架到底在干嘛,为什么要用
js 框架帮助开发者写 js 逻辑代码,在开发应用的时候 js 的功能划分为如下几点:
-
渲染数据
-
操作 dom(写一些效果)
-
操作 cookie 等存储机制 api
在前端开发中,如何高效的操作 dom、渲染数据是一个前端工程师需要考虑的问题,而且当数据量大,流向较乱的时候,如何正确使用数据,操作数据也是一个问题
而 js 框架对上述的几个问题都有自己趋于完美的解决方案,开发成本降低。高性能高效率。唯一的缺点就是需要使用一定的成本来学习。
Vue 官网介绍
vue 是渐进式 JavaScript 框架
“渐进式框架”和“自底向上增量开发的设计”是 Vue 开发的两个概念
Vue 可以在任意其他类型的项目中使用,使用成本较低,更灵活,主张较弱,在 Vue 的项目中也可以轻松融汇其他的技术来开发,并且因为 Vue 的生态系统特别庞大,可以找到基本所有类型的工
具在 vue 项目中使用
特点:易用(使用成本低),灵活(生态系统完善,适用于任何规模的项目),高效(体积小,优化好,性能好)
Vue 是一个 MVVM 的 js 框架,但是,Vue 的核心库只关注视图层,开发者关注的只是 m-v 的映射关系
与 AngularJS 的对比
Vue 的很多 api、特性都与 angularJS 相似,其实是因为 Vue 在开发的时候借鉴了很多 AngularJS 中的特点,而 AngularJS 中固有的缺点,在 Vue 中已经解决,也就是青出于蓝而胜于蓝,Vue 的学习
成本比 AngularJS 低很多,因为复杂性就低
AngularJS 是强主张的,而 Vue 更灵活
Vue 的数据流是单向的,数据流行更清晰
Angular 里指令可以是操作 dom 的,也可以封装一段结构逻辑代码,例如:广告展示模块
Vue 中的指令只是操作 dom 的,用组件来分离结构逻辑
AngularJS 的性能比不上 Vue
Vue 的使用
Vue 不支持 IE8,因为使用了 ES5 的很多特性
可以直接通过 script 标签来引入 vue.js,有开发版本和生产版本,开发版本一般我们在开发项目的时候引入,当最后开发完成上线的时候引入生产版本,开发版本没有压缩的,并且有很多提
示,而生产版本全部删掉了
在 Vue 中提供了一个脚手架(命令行工具)可以帮我们快速的搭建基于 webpack 的开发环境...
Vue 的实例
每一个应用都有一个根实例,在根实例里我们通过组件嵌套来实现大型的应用
也就是说组件不一定是必须的,但是实例是必须要有的
在实例化实例的时候我们可以传入一个;配置项,在配置项中设置很多属性方法可以实现复杂的功能
在配置中可以设置 el 的属性,el 属性代表的是此实例的作用范围
在配置中同过设置 data 属性来为实例绑定数据
mvc/mvvm
摘自阮一峰博客
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-for
在 vue 中可以通过 v-for 来循环数据的通知循环 dom,语法是 item in/of items,接收第二个参数是索引 (item,index) of items, 还可以循环键值对,第一个参数是 value,第二个是 key,第三
个依然是索引
v-on
在 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 有几个好处:
- 扫一眼 HTML 模板便能轻松定位在 JavaScript 代码里对应的方法。
- 因为你无须在 JavaScript 里手动绑定事件,你的 ViewModel 代码可以是非常纯粹的逻辑,和 DOM 完全解耦,更易于测试。
- 当一个 ViewModel 被销毁时,所有的事件处理器都会自动被删除。你无须担心如何自己清理它们。
模板语法
在 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-resource
这是一款 vue 的插件,可以用来进行数据交互,支持的请求方式:GET/POST/JSONP/OPTIONS...
这个插件官方宣布不在更新维护,也就是说尽量不要使用
计算属性、监听
有的时候我们需要在模板中使用数据a,这个时候就需要用到表达式,但是有的地方我们需要对a数据进行一些简单的处理后才能使用,那么我们就会在表达式中写一些js逻辑运算
<div <span class="hljs-built_in">id</span>=<span class="hljs-string">"example"</span>> {{ message.split(<span class="hljs-string">''</span>).reverse().<span class="hljs-built_in">join</span>(<span class="hljs-string">''</span>) }} </div>
这样我们的维护就会非常困难,也不便于阅读
那め我们就可以在 methods 里设置一个方法,在模板的表达式中使用这个方法
<p>Reversed message: <span class="hljs-string">"{{ reversedMessage() }}"</span></p> // 在组件中 methods: { reversedMessage: <span class="hljs-function"><span class="hljs-title">function</span></span> () { <span class="hljs-built_in">return</span> this.message.split(<span class="hljs-string">''</span>).reverse().<span class="hljs-built_in">join</span>(<span class="hljs-string">''</span>) } }
但是这个时候,只要 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>
<span class="hljs-title class_">Vue</span>.<span class="hljs-title function_">filter</span>(<span class="hljs-string">'firstUpper'</span>,<span class="hljs-keyword">function</span> (<span class="hljs-params">value,num=<span class="hljs-number">1</span>,num2</span>) {
<span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(num2)
<span class="hljs-keyword">return</span> value.<span class="hljs-title function_">substr</span>(<span class="hljs-number">0</span>,num).<span class="hljs-title function_">toUpperCase</span>()+value.<span class="hljs-title function_">substr</span>(num).<span class="hljs-title function_">toLowerCase</span>()
})
-
局部定义
在实例、组件的配置项中设置 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里使用,其他的指令等地方都不能用
条件渲染
在 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>
<span class="hljs-tag"><<span class="hljs-name">p</span>></span>不好意思,输入有误<span class="hljs-tag"></<span class="hljs-name">p</span>></span>
<span class="hljs-tag"></<span class="hljs-name">template</span>></span>
需要注意的另一个地方是:Vue 会尽可能高效地渲染元素,通常会复用已有元素而不是从头开始渲染。这样确实能使 Vue 变得更快,性能更高,但是有的时候我们需要让实例去更新 dom 而不是
复用,就需要给 dom 加上不同的 key 属性,因为 vue 在判断到底渲染什么的时候,包括哪些 dom 可以复用,都会参考 key 值,如果 dom 表现基本一致,符合复用的条件,但是 key 值不同,依然不会复
用
Vue 还提供了 v-show 指令,用法和 v-if 基本一样,控制的是元素的 css 中 display 属性,从而控制元素的显示和隐藏 , 不能和 v-else 配合使用, 且不能使用在 template 标签上,因为 template 不
会渲染,再更改它的 css 属性也不会渲染,不会生效
v-if vs v-show
v-if 是“真正”的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建。
v-if 也是惰性的:如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块。
相比之下,v-show 就简单得多——不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 进行切换。
一般来说,v-if 有更高的切换开销,而 v-show 有更高的初始渲染开销。因此,如果需要非常频繁地切换,则使用 v-show 较好;如果在运行时条件很少改变,则使用 v-if 较好。
mixin
在 Vue 中,我们可以通过定义多个 mixin 来实现代码抽离复用,便于维护,提升页面的逻辑性
要注意的是:data 属性不要使用 mixin,因为从逻辑上来说,每一个实例、组件的数据都应该是独立的
一个 mixin 其实就是一个纯粹的对象,上面挂载着抽离出来的配置,在某一个实例中,通过 mixins 选项(数组)导入后,此实例就拥有导入的 mixin 的配置
且导入的配置不会覆盖原有的,而是合并到一起
虚拟 dom
频繁且复杂的 dom 操作通常是前端性能瓶颈的产生点,Vue 提供了虚拟 dom 的解决办法
虚拟的 DOM 的核心思想是:对复杂的文档 DOM 结构,提供一种方便的工具,进行最小化地 DOM 操作。这句话,也许过于抽象,却基本概况了虚拟 DOM 的设计思想
(1) 提供一种方便的工具,使得开发效率得到保证
(2) 保证最小化的 DOM 操作,使得执行效率得到保证
也就是说,虚拟 dom 的框架 / 工具都是这么做的:
- 根据现有的真实 dom 来生成一个完整的虚拟 dom 树结构
- 当数据变化,或者说是页面需要重新渲染的时候,会重新生成一个新的完整的虚拟 dom
- 拿新的虚拟 dom 来和旧的虚拟 dom 做对比(使用 diff 算法),。得到需要更新的地方之后,更新内容
这样的话,就能大量减少真实 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)
组件通过 template 来确定自己的模板,template 里的模板必须有根节点,标签必须闭合
组件的属性挂载通过:data 方法来返回一个对象作为组件的属性,这样做的目的是为了每一个组件实例都拥有独立的 data 属性
响应式原理
因为 vue 是 mvvm 的框架,所以当数据变化的时候,视图会立即更新,视图层产生操作后会自动通知 vm 来更改 model,所以我们可以实现双向数据绑定,而其中的原理就是实例会将设置的 data 逐
个遍历利用 Object.defineProperty 给数据生成 getter 和 setter,当数据变化地方时候 setter 会监听到并且通知对应的 watcher 工具进行逻辑运算会更新视图
声明式渲染
在 vue 中,我们可以先在 vue 实例中声明数据,然后通过 {{}} 等方式渲染在 dom 中