首页 > 网站开发 > JavaScript教程 >
-
vue
一、What?
Vue 是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,完全能够为复杂的单页应用提供驱动。
二、Why?(优点)
- 方便阅读的中文文档
- 容易上手 (学习曲线比较缓和)
- 体积小
- 基于组件化的开发方式
- 代码的可读性、可维护性得到了提高
三、vue的基础知识
1. MVVM
MVVM是Model-View-ViewModel的简写。
(1)组成
- m(模型) 是vue实例中的data,自定义的数据或后端返回的数组,不是后端mvc里的model概念不同。
- vm(视图模型) 是vue的实例 m和v之间的调度者 是mvvm的核心思想。在视图和数据绑定器之间进行通信。
- v(视图)是 html 要渲染的。
(2)MVVM模式图
采用双向数据绑定
(3)MVVM优点
-
低耦合。视图(View)可以独立于Model变化和修改,一个ViewModel可以绑定到不同的"View"上,当View变化的时候Model可以不变,当Model变化的时候View也可以不变。
-
可重用性。你可以把一些视图逻辑放在一个ViewModel里面,让很多view重用这段视图逻辑。
-
独立开发。开发人员可以专注于业务逻辑和数据的开发(ViewModel),设计人员可以专注于页面设计,使用Expression Blend可以很容易设计界面并生成xaml代码。
-
可测试。界面素来是比较难于测试的,而现在测试可以针对ViewModel来写。
2. MVC
(1)组成
- Model(模型)表示应用程序核心(如数据库)。是应用程序中用于处理应用程序数据逻辑的部分。通常模型对象负责在数据库中存取数据。
- View(视图)显示效果(HTML页面)。是应用程序中处理数据显示的部分。通常视图是依据模型数据创建的。
- Controller(控制器)处理输入(业务逻辑)。是应用程序中处理用户交互的部分。通常控制器负责从视图读取数据,控制用户输入,并向模型发送数据。
(2)MVC模式图
(3)MVC优点
- 耦合性低
视图层和业务层分离,这样就允许更改视图层代码而不用重新编译模型和控制器代码,同样,一个应用的业务流程或者业务规则的改变只需要改动MVC的模型层即可。因为模型与控制器和视图相分离,所以很容易改变应用程序的数据层和业务规则。
模型是自包含的,并且与控制器和视图相分离,所以很容易改变应用程序的数据层和业务规则。如果把数据库从MySQL移植到Oracle,或者改变基于RDBMS数据源到LDAP,只需改变模型即可。一旦正确的实现了模型,不管数据来自数据库或是LDAP服务器,视图将会正确的显示它们。由于运用MVC的应用程序的三个部件是相互独立,改变其中一个不会影响其它两个,所以依据这种设计思想能构造良好的松耦合
- 重用性高
随着技术的不断进步,需要用越来越多的方式来访问应用程序。MVC模式允许使用各种不同样式的视图来访问同一个服务器端的代码,因为多个视图能共享一个模型,它包括任何WEB(HTTP)浏览器或者无线浏览器(wap),比如,用户可以通过电脑也可通过手机来订购某样产品,虽然订购的方式不一样,但处理订购产品的方式是一样的。由于模型返回的数据没有进行格式化,所以同样的构件能被不同的界面使用。例如,很多数据可能用HTML来表示,但是也有可能用WAP来表示,而这些表示所需要的命令是改变视图层的实现方式,而控制层和模型层无需做任何改变。由于已经将数据和业务规则从表示层分开,所以可以最大化的重用代码了。模型也有状态管理和数据持久性处理的功能,例如,基于会话的购物车和电子商务过程也能被Flash网站或者无线联网的应用程序所重用。 [11]
- 生命周期成本低
MVC使开发和维护用户接口的技术含量降低。
- 部署快
使用MVC模式使开发时间得到相当大的缩减,它使程序员(Java开发人员)集中精力于业务逻辑,界面程序员(HTML和JSP开发人员)集中精力于表现形式上。
- 可维护性高
分离视图层和业务逻辑层也使得WEB应用更易于维护和修改。
- 有利软件工程化管理
由于不同的层各司其职,每一层不同的应用具有某些相同的特征,有利于通过工程化、工具化管理程序代码。控制器也提供了一个好处,就是可以使用控制器来联接不同的模型和视图去完成用户的需求,这样控制器可以为构造应用程序提供强有力的手段。给定一些可重用的模型和视图,控制器可以根据用户的需求选择模型进行处理,然后选择视图将处理结果显示给用户。
(4)MVC缺点
- 没有明确的定义
完全理解MVC并不是很容易。使用MVC需要精心的计划,由于它的内部原理比较复杂,所以需要花费一些时间去思考。同时由于模型和视图要严格的分离,这样也给调试应用程序带来了一定的困难。每个构件在使用之前都需要经过彻底的测试。
- 不适合小型,中等规模的应用程序
花费大量时间将MVC应用到规模并不是很大的应用程序通常会得不偿失。
- 增加系统结构和实现的复杂性
对于简单的界面,严格遵循MVC,使模型、视图与控制器分离,会增加结构的复杂性,并可能产生过多的更新操作,降低运行效率。
- 视图与控制器间的过于紧密的连接
视图与控制器是相互分离,但却是联系紧密的部件,视图没有控制器的存在,其应用是很有限的,反之亦然,这样就妨碍了他们的独立重用。
- 视图对模型数据的低效率访问
依据模型操作接口的不同,视图可能需要多次调用才能获得足够的显示数据。对未变化数据的不必要的频繁访问,也将损害操作性能。
- 一般高级的界面工具或构造器不支持模式
改造这些工具以适应MVC需要和建立分离的部件的代价是很高的,会造成MVC使用的困难。
3. 常用指令
{{ }}:”双大括号里的内容将会被替代为数据对象上面的msg属性的值,如果绑定的这个属性的 值发生改变,那么大括号内的内容会跟着更新。
v-text: 和{{}}一样,会先执行覆盖元素中原本的内容,但是插值表达式只会覆盖自己的占位符,默认不会闪烁。
v-cloak: 解决{{}}插值闪烁问题
<div id="app" v-cloak>
<span>{{ msg }}<span>
</div>
<div id="app">
<span v-text="msg"><span>
</div>
v-once:也能执行一次性地插值,当数据改变时,插值处的内容不会更新。
<div id="app">
<span v-once>这个将不会改变: {{ msg }}</span>
</div>
v-html: 渲染 html标签 覆盖元素中原有元素内容,双大括号会将数据解释为普通文本,而非 HTML 代码。为了输出真正的 HTML,需要使用 v-html
<div id="app">
<p>Using mustaches: {{ message }}</p>
<p>Using v-html: <span v-html="message"></span></p>
</div>
new Vue({
el: '#app',
data: {
message: '<h1 style="color:red">梅须逊雪三分白,雪却输梅一段香</h1>'
}
})
Using {{ }}:
<h3 style="color:red">梅须逊雪三分白,雪却输梅一段香</h3>
Using v-html:
梅须逊雪三分白,雪却输梅一段香
### 【注意】 ###
在站点上动态渲染的任意 HTML 可能会非常危险,因为它很容易导致 XSS 攻击。请只对可信内容使用 HTML 插值,绝不要对用户提供的内容使用插值。
v-bind: 简写为: 用来绑定数据 可以写合法的js表达式
v-model: 双向数据绑定
### css ###
.class1{
background: #444;
color: #eee;
}
### html ###
<div id="app">
<input type="checkbox" v-model="use" id="r1"><label for="r1">修改背景颜色</label>
<h3 v-bind:class="{'class1': use}">
验证v-bind指令以及数据绑定
</h3>
<!-- <h3 :class="{'class1': use}">
验证v-bind指令以及数据绑定
</h3> -->
</div>
### js ###
new Vue({
el: '#app',
data:{
use: false
}
});
代码效果如下:
-
[ ] 修改背景颜色
验证v-bind指令以及数据绑定
勾选之后效果如下:
- [X] 修改背景颜色
验证v-bind指令以及数据绑定
v-on: 简写为 @ 用来点击事件
### html ###
<div id="app">
<p>{{ message }}</p>
<button v-on:click="reverseMessage">反转字符串</button>
<!-- <button @:click="reverseMessage">反转字符串</button> -->
</div>
### js ###
new Vue({
el: '#app',
data: {
message: 'Runoob!'
},
methods: {
reverseMessage: function () {
this.message = this.message.split('').reverse().join('')
}
}
})
v-if: 条件渲染可以根据条件来决定是否渲染dom,有较高的切换性能 , 适合元素可能永远不会被用户看到。
v-else: v-else元素必须跟在v-if 的后面
### html ###
<div id="app">
<p v-if="ok">ok为true展示</p>
<template v-else="ok">
<p>ok为false展示</p>
</template>
</div>
### js ###
new Vue({
el: '#app',
data: {
ok: true
}
})
v-show:这个命令可以根据条件选择是否展示元素,有较高的初始渲染消耗,适合元素频繁切换。
v-for:可以遍历, 普通数组,对象数组,对象,还可以是数字
### html ###
<div id="app">
<div v-for='item in object' :key='item.id'>
{{item.name}}---{{item.id}}
</div>
</div>
### js ###
new Vue({
el: '#app',
data: {
object:[
{ id: 1, name: "aaa" },
{ id: 2, name: "bbb" },
{ id: 3, name: "ccc" },
{ id: 4, name: "ddd" }
]
}
})
表达式
### html ###
<div id="app">
{{5+5}}<br>
{{ ok ? 'YES' : 'NO' }}<br>
{{ message.split('').reverse().join('') }}
<div v-text="'list-' + message"></div>
</div>
### js ###
new Vue({
el: '#app',
data: {
ok: true,
message: 'RUNOOB'
}
})
10
YES
BOONUR
4. 常用事件修饰符
-
.stop 阻止冒泡 :外层和里层都有方法 点击里层会产生冒泡,也会触发外层的事件。
顺序 从里到外产生事件 -
.prevent 阻止浏览器默认行为,
a标签有浏览器默认行为。 - .capture 捕获事件 :点击里层先触发外层再触发里层 顺序从外到里产生事件
- .self 只触发自己本身的事件 不会产生冒泡和捕获事件 类似于阻止冒泡 但只针对自己那一层 最外层还是会被最里层冒泡冒到 stop 是阻止所有层次
- .once 事件只执行一次
- .passive 滚动事件的默认行为将会立即触发
- .left 左键事件
- .right 右键事件
- .middle 中间滚轮事件
- .enter .esc .space ……(按键修饰符)
<!-- 阻止单击事件冒泡 -->
<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>
<!-- 添加事件侦听器时使用事件捕获模式 -->
<div v-on:click.capture="doThis">...</div>
<!-- 只当事件在该元素本身(而不是子元素)触发时触发回调 -->
<div v-on:click.self="doThat">...</div>
<!-- click 事件只能点击一次,2.1.4版本新增 -->
<a v-on:click.once="doThis"></a>
<!-- 只有在 keyCode 是 13 时调用 vm.submit() -->
<input v-on:keyup.13="submit">
四、组件化
组件:组件就是可被反复使用的,带有特定功能的视图
所谓的组件化,就像玩积木一样,把封装的组件进行复用,把积木(组件)拼接在一起,构成一个复杂的页面应用程序。
组件树就是由各个组件构成的一种数据结构,它存在的意义是为了帮梳理应用程序
1. 组件的创建
全局组件:
Vue.component('my-component-name',{
// ... 选项 ...
})
全局注册之后可以用在任何新创建的 Vue 根实例 (new Vue) 的模板中
Vue.component('my-component-a',{// ... 选项 ...})
Vue.component('my-component-b',{// ... 选项 ...})
Vue.component('my-component-c',{// ... 选项 ...})
new vue({
el:'#app'
})
<div id="app">
<my-component-a></my-component-a>
<my-component-b></my-component-b>
<my-component-c></my-component-c>
</div>
局部组件:
new Vue({
components: {
'component-a': {},
'component-b': {}
}
})
2.注意事项
- 组件的id和使用方式 遵循烤串式命名方式:a-b-c
- 如果一个组件 要渲染多个元素,将多个元素放在一个顶层标签中,比如div、form
- 全局组件可以用在id为example的范围内的任何一个组件内部,直接调用可以;但是局部组件只能在父模板中直接调用
五、自定义指令
1. 创建指令
全局指令:
// 注册一个全局自定义指令 `v-focus`
Vue.directive('focus', {
// 当被绑定的元素插入到 DOM 中时……
inserted: function (el) {
// 聚焦元素
el.focus()
}
})
局部指令
directives: {
focus: {
// 指令的定义
inserted: function (el) {
el.focus()
}
}
}
定义完之后可以使用新的 v-focus 属性
<input v-focus>
2. 钩子函数
-
bind:只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。
-
inserted:被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。
-
update:所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。指令的值可能发生了改变,也可能没有。但是可以通过比较更新前后的值来忽略不必要的模板更新。
-
componentUpdated:指令所在组件的 VNode 及其子 VNode 全部更新后调用。
-
unbind:只调用一次,指令与元素解绑时调用。
3. 钩子函数参数
(1)el: 指令所绑定的元素,可以用来直接操作 DOM。
(2)binding: 一个对象,包含以下 property:
- name:指令名,不包括 v- 前缀。
- value:指令的绑定值,例如:v-my-directive="1 + 1" 中,绑定值为 2。
- oldValue:指令绑定的前一个值,仅在 update 和 componentUpdated 钩子中可用。无论值是否改变都可用。
- expression:字符串形式的指令表达式。例如 v-my-directive="1 + 1" 中,表达式为 "1 + 1"。
- arg:传给指令的参数,可选。例如 v-my-directive:foo 中,参数为 "foo"。
- modifiers:一个包含修饰符的对象。例如:v-my-directive.foo.bar 中,修饰符对象为 { foo: true, bar: true }。
(3)vnode: Vue 编译生成的虚拟节点。移步 VNode API 来了解更多详情。
(4)oldVnode: 上一个虚拟节点,仅在 update 和 componentUpdated 钩子中可用。
###【注意】###
除了 el 之外,其它参数都应该是只读的,切勿进行修改。如果需要在钩子之间共享数据,建议通过元素的 dataset 来进行。
六、过滤器
全局指令:
Vue.filter('capitalize', function (value) {
if (!value) return ''
value = value.toString()
return value.charAt(0).toUpperCase() + value.slice(1)
})
new Vue({
// ...
})
局部指令
filters: {
capitalize: function (value) {
if (!value) return ''
value = value.toString()
return value.charAt(0).toUpperCase() + value.slice(1)
}
}
<!-- 在双花括号中 -->
{{ message | capitalize }}
<!-- 在 v-bind 中 -->
<div v-bind:id="rawId | capitalize"></div>
过滤器可以串联:
{{ message | filterA | filterB }}
也可以接收参数:
{{ message | filterA('arg1', arg2) }}
七、生命周期
-
beforeCreate():这是我们遇到的第一个生命周期函数,表示实例完全被创建出来之前,会执行它…
-
created(): 这是遇到的第二个生命周期函数…
-
beforeMount():这是遇到的第3个生命周期函数,表示 模板已经在内存中编辑完成,但是尚未把模板渲染(挂载)到页面中。在 beforeMount 执行的时候,页面中的元素,还没有被真正替换过来,只是之前写的一些模板字符串。就像{{text}}这样
-
mounted():这是遇到的第四个生命周期函数,表示内存中的模板,已经真实的挂载到了页面中,用户已经可以看到渲染好的页面了。只要执行完这个生命周期,就表示整个vue实例已经初始化完毕了,此时,组件已经脱离了创建阶段,进入到了运行阶段。
-
beforeUpdate():这时候表示,我们的界面还没有被更新[但数据已经被更新了页面中显示的数据,还是旧的,此时data数据是最新的。页面尚未和最新的数据保持同步
-
update() : 这一步执行的是 先根据data中最新的数据,在内存中重新渲染出一份最新的内存dom树,当最新的内存dom树被更新后,会把最新的内存DOM树重新渲染到真实的页面中去,这时候,就完成了数据data(model层)->view(视图层)的更新,
页面和data数据已经保持同步了,都是最新的。 -
beforeDestory :当执行 beforeDestory 钩子函数的时候,Vue实例就已经从运行阶段,进入到销毁阶段, 当执行beforeDestroy的时候,实例身上所有的data和所有的methods以及过滤器、指令...都处于可用状态,此时,还没有真正执行销毁的过程。
-
destroyed :当执行这个函数的时候,组件已经被完全销毁了,此时,组件中所有的数据,方法,指令,过滤器...都已经不可用了
八、常用属性
1. computed 计算属性
<div id="app">
<p>Original message: "{{ message }}"</p>
<p>Computed reversed message: "{{ reversedMessage }}"</p>
</div>
var vm = new Vue({
el: '#app',
data: {
message: 'Hello'
},
computed: {
// 计算属性的 getter
reversedMessage: function () {
// 'this' 指向 vm 实例
return this.message.split('').reverse().join('')
}
}
})
结果:
Original message: "Hello"
Computed reversed message: "olleH"
console.log(vm.reversedMessage) // => 'olleH'
vm.message = 'Goodbye'
console.log(vm.reversedMessage) // => 'eybdooG'
vm.reversedMessage 依赖于 vm.message,因此当 vm.message 发生改变时,所有依赖 vm.reversedMessage 的绑定也会更新。
2. computed vs methods (方法)
可以通过在表达式中调用方法来达到同样的效果:
<p>Reversed message: "{{ reversedMessage() }}"</p>
// 在组件中
new Vue({
el: '#app',
data: {
message: 'Hello'
},
methods: {
reversedMessage: function () {
return this.message.split('').reverse().join('')
}
}
})
可以将同一函数定义为一个方法而不是一个计算属性。两种方式的最终结果确实是完全相同的。然而,不同的是计算属性是基于它们的响应式依赖进行缓存的。只在相关响应式依赖发生改变时它们才会重新求值。这就意味着只要 message 还没有发生改变,多次访问 reversedMessage 计算属性会立即返回之前的计算结果,而不必再次执行函数。
假设我们有一个性能开销比较大的计算属性A,它需要遍历一个巨大的数组并做大量的计算。然后我们可能有其他的计算属性依赖于 A。如果没有缓存,我们将不可避免的多次执行 A 的 getter!如果你不希望有缓存,请用方法来替代。
3. computed vs watch (侦听属性)
<div id="demo">{{ fullName }}</div>
var vm = new Vue({
el: '#demo',
data: {
firstName: 'Foo',
lastName: 'Bar',
fullName: 'Foo Bar'
},
watch: {
firstName: function (val) {
this.fullName = val + ' ' + this.lastName
},
lastName: function (val) {
this.fullName = this.firstName + ' ' + val
}
}
})
上面代码是命令式且重复的。将它与计算属性的版本进行比较:
var vm = new Vue({
el: '#demo',
data: {
firstName: 'Foo',
lastName: 'Bar'
},
computed: {
fullName: function () {
return this.firstName + ' ' + this.lastName
}
}
})
4. 计算属性中的setter
计算属性默认只有 getter,不过在需要时你也可以提供一个 setter:
computed: {
fullName: {
// getter
get: function () {
return this.firstName + ' ' + this.lastName
},
// setter
set: function (newValue) {
var names = newValue.split(' ')
this.firstName = names[0]
this.lastName = names[names.length - 1]
}
}
}
现在再运行 vm.fullName = 'John Doe' 时,setter 会被调用,vm.firstName 和 vm.lastName 也会相应地被更新。
九、路由模块
1. SPA的基本概念和工作原理
SPA: single page application 单一页面应用程序,只有一个完整的页面;
它在加载页面时,不会加载整个页面,而是只更新某个指定的容器中内容。
比如Gmail、移动的webApp
工作原理:
(1)解析地址栏:完整的页面地址、路由地址
(2)根据路由地址: 从路由词典中找到真正的要加载的页面
(3)发起ajax请求:请求要加载的页面
(4)像指定的容器中插入加载来的页面
2. 路由配置
new VueRouter({
//路由匹配规则
routes:[
{
path:'/',
redirect:'/login'
},
{
path:'login',
component:login
},
{
path:'/register',
component:register
}
]
})
后续陆续补充知识点……
本文链接:https://www.cnblogs.com/hongplum/p/14569573.html