Vue 简介
Vue 是一个渐进式 JavaScript 框架。严格来说 Vue 不是一个框架,只是和他旁边的生态环境组成了一个框架。
一个vue渐进式框架一般由下面几部分构成:
- 声明式渲染 vue.js
- 组件系统 element-ui
- 客户端路由 vue-router
- 大型状态管理 vuex
- 构建工具 webpack
Vue 著名的全家桶其实就是,vue-router,vuex,axios,vue-cli。就是一个完整vue项目的核心构成。
同时调试方面,可以选择安装chrome 插件vue Devtools 。
Vue 只兼容IE9 及以上版本。
Vue 知识点
1、过渡效果
Vue 提供一个强大的过渡效果系统,可以在 Vue 插入/更新/移除元素时自动应用过渡效果。
2、插值
-
Mustache 语法(双大括号)提供了完全的JavaScript支持,即可以在双大括号内写单个模板表达式。最终会会将数据解释为普通文本。
-
模板表达式都被放在沙盒中,只能访问全局变量的一个白名单,如
Math
和Date
。你不应该在模板表达式中试图访问用户定义的全局变量。 -
v-html 则可以输出真正的 HTML
你的站点上动态渲染的任意 HTML 可能会非常危险,因为它很容易导致XSS攻击。请只对可信内容使用 HTML 插值,绝不要对用户提供的内容使用插值。
3、指令 v-
- v-html 绑定输出的 HTML
- v-bind 缩写
:
绑定属性,class,id,style,src 等,父子组件绑定props里的数据 - v-once 不会改变
- v-show 与v-if相比,v-show只是简单地基于css 进行切换,display。一开始就渲染,元素始终存在。
- v-if 条件,只有在第一次条件判断为真时,才会渲染
- v-else-if 紧跟 v-if
- v-else 紧跟 v-if 或v-else-if
- v-for 循环
- v-on 缩写
@
事件绑定 - v-model 双向数据绑定 input textarea select
4、组件
<div id="app-7">
<ol>
<todo-item
v-for="item in groceryList"
v-bind:todo="item"
v-bind:key="item.id">
</todo-item>
</ol>
</div>
Vue.component('todo-item', {
props: ['todo'],
template: '<li></li>'
})
var app7 = new Vue({
el: '#app-7',
data: {
groceryList: [
{ id: 0, text: '蔬菜' },
{ id: 1, text: '奶酪' },
{ id: 2, text: '随便其它什么人吃的东西' }
]
}
})
所有的 Vue 组件都是 Vue 实例,并且接受相同的选项对象 (一些根实例特有的选项除外)。
5、Vue 中当数据发生变化时,视图会进行重渲染
唯一的例外是使用Object.freeze()
,这会阻止修改现有的属性,也意味着响应系统无法再追踪变化。
6、Vue 实例的属性与方法
除了数据属性,Vue 实例还暴露了一些有用的实例属性与方法。它们都有前缀 $
,以便与用户定义的属性区分开来。例如:
let vm = new Vue({
el:'#example',
data:{
name:'wang',
message:'do something',
},
})
vm.$el = document.querySelector('#example');//true
### 7、生命周期勾子函数
beforeCrete 实例开始创建,此时,$el 和 data 都为 undefined,没有初始化
created 实例创建完毕,创建后 data 初始化了,而 $el 没有
beforeMount 挂载之前,$el 和 data 都初始化了,但 data 还没挂载到 dom
mounted Vue 实例挂载完毕
beforeUpdate 组件更新之前
updated 组件更新完毕
beforeDestroy 组件销毁前,组件销毁的意思是组件与 dom 解耦
destroyed 组件销毁完成
在对应的阶段希望做什么处理,就可以利用钩子函数,例如:
beforeCreate : 举个栗子:可以在这加个loading事件
created :在这结束loading,还做一些初始化,实现函数自执行
mounted : 在这发起后端请求,拿回数据,配合路由钩子做一些事情
beforeDestory: 你确认删除XX吗?
destoryed :当前组件已被删除,清空相关内容
8、计算属性
computed,函数返回值作为属性值。一般不在 `` 内放入复杂的数据,如果需要,一般使用数据属性处理。
计算属性和方法的区别在于,计算属性是基于它们的依赖进行缓存的。计算属性只有在它的相关依赖改变时才会重新求值。这意味着,如果依赖没变,多次访问数据属性,会立即返回之前的计算结果,不必再次执行函数。
计算属性默认只有 getter 。不过在需要时,也可以两个都设置
//...
computed:{
fullName:{
get:function(){
},
set:function(){
}
},
}
9、侦听器
当数据属性无法满足需求时,可以自定义一个侦听器。watch
//浅度监视,即没办法监视对象属性值的变化
vm.$watch('a', callback)
//深度监视,监视所有变化
vm.$watch('a', callback,{deep:true})
watch:{
a:{
handler(){
window.alert('发生了变化');
},
deep:true
}
}
10、key
用于追踪节点,常与 v-for
结合使用。
11、数组更新检测
变异方法:push()、pop()、shift()、unshift()、splice()、sort()、reverse()
替换数组:filter()、map()、concat()、slice()
注意事项:由于Javascript 的限制,Vue 不能检测以下变动的数组:
-
当你利用索引直接设置一个项时,例如:
vm.items[indexOfItem] = newValue
-
当你修改数组的长度时,例如:
vm.items.length = newLength
这时可以用Vue.set(arr,index,newValue)
12、对象更改检测
还是由于JavaScript的限制,Vue不能检测对象属性的添加或删除:
let vm = new Vue({
data:{
a:1
}
})//Vm.a 是响应式的
vm.b = 2;//vm.b不是响应式的
这时,也可以通过Vue.set(obj,key,value)
方法添加响应式属性。
###13、v-for with v-if
当它们处于同一节点,v-for 的优先级比 v-if 更高,这意味着 v-if 将分别重复运行于每个 v-for 循环中。
### 14、事件修饰符(可串联)
- .stop e.stopPropagation()
- .prevent e.preventDefault()
- .capture 事件捕获模式
- .self 事件是当前元素自身触发,而不是内部元素触发
- .once 事件只会触发一次
- .passive
<!-- 滚动事件的默认行为 (即滚动行为) 将会立即触发 -->
<!-- 而不会等待 `onScroll` 完成 -->
<!-- 这其中包含 `event.preventDefault()` 的情况 -->
<div v-on:scroll.passive="onScroll">...</div>
事件修饰符可串联,顺序很重要。
15、按键修饰符
<!-- 只有在 `keyCode` 是 13 时调用 `vm.submit()` -->
<input v-on:keyup.13="submit">
全部的按键别名:
- .enter
- .tab
- .delete(捕获“删除”和“退格”两个键)
- .esc
- .space
- .up
- .down
- .left
- .right
可以通过全局 config.keyCodes
对象自定义按键修饰符别名:
// 可以使用 `v-on:keyup.f1`
Vue.config.keyCodes.f1 = 112
16、系统修饰键
可以用如下修饰符来实现公在按下相应按键时才触发鼠标或键盘事件的监听器。
- .ctrl
- .alt
- .shift
- .meta
例如:
<!-- Alt + C -->
<input @keyup.alt.67="clear">
<!-- Ctrl + Click -->
<div @click.ctrl="doSomething">Do something</div>
17、修饰符
.exact
修饰符允许你控制由精确的系统修饰符组合触发的事件。
<!-- 即使 Alt 或 Shift 被一同按下时也会触发 -->
<button @click.ctrl="onClick">A</button>
<!-- 有且只有 Ctrl 被按下的时候才触发 -->
<button @click.ctrl.exact="onCtrlClick">A</button>
<!-- 没有任何系统修饰符被按下的时候才触发 -->
<button @click.exact="onClick">A</button>
18、鼠标按钮修饰符
- .left
- .right
- .middle
这些修饰符会限制处理函数公响应特定的鼠标按钮。
### 19、用v-on绑定事件的好处
- 扫一眼 HTML 模板便能轻松定位在 JavaScript 代码里对应的方法。
- 因为你无须在 JavaScript 里手动绑定事件,你的 ViewModel 代码可以是非常纯粹的逻辑,和 DOM 完全解耦,更易于测试。
- 当一个 ViewModel 被销毁时,所有的事件处理器都会自动被删除。你无须担心如何自己清理它们。
20、表单
表单用 v-model
双向绑定数据时,如果是checkbox,radio 需要设置 value 的值。
多选的情况,就绑定到同一个数组,单选的情况就绑定到字符串。
<!-- 当选中时,`picked` 为字符串 "a" -->
<input type="radio" v-model="picked" value="a">
<!-- `toggle` 为 true 或 false -->
<input type="checkbox" v-model="toggle">
<!-- 当选中第一个选项时,`selected` 为字符串 "abc" -->
<select v-model="selected">
<option value="abc">ABC</option>
</select>
修饰符
-
.lazy:默认情况下 v-model 在每次 input 事件触发后就将输入框的值与数据同步,添加.lazy 修饰符后,转为使用 change 事件时进行同步:
-
.number:如果想自动将用户的输入值转为数值类型,可以给 v-model 添加 .number 修饰符:
<input v-model.number="age" type="number">
这通常很有用,因为即使在
type="number"
时,HTML 输入元素的值也总会返回字符串。 -
.trim:自动过滤用户输入的首尾空白字符。
21、组件
构造Vue实例时传入的各种选项大多数都可以在组件里使用。只有一个例外:组件中 data 必须是函数,在函数里返回一个对象。这样子是为了避免多个相同子组件引用了同一个对象。
全局组件
<div id="example">
<my-component></my-component>
</div>
// 注册全局组件
Vue.component('my-component', {
template: '<div>A custom component!</div>'
})
// 创建根实例
new Vue({
el: '#example'
})
渲染为:
<div id="example">
<div>A custom component!</div>
</div>
局部组件
你不必把每个组件都注册到全局。你可以通过某个 Vue 实例/组件的实例选项 components
注册仅在其作用域中可用的组件:
new Vue({
// ...
components: {
// <my-component> 将只在父组件模板中可用
myComponent: {
template: '<div>A custom component!</div>'
}
}
})
组件中的选项:
- template:模板字符串
- props:父组件向子组件传递数据
- data:必须为一个函数,一般是返回一个对象,里面存放这个模板所需要的数据
22、prop
在 Vue 中,父子组件的关系可以总结为 prop 向下传递,事件向上传递。父组件通过 prop 给子组件下发数据,子组件通过事件给父组件发送消息。看看它们是怎么工作的。
组件实例的作用域是孤立的。这意味着不能 (也不应该) 在子组件的模板内直接引用父组件的数据。父组件的数据需要通过 prop 才能下发到子组件中。
一般通过v-bind 进行父子组件的数据绑定。
单向数据流
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() } }
23、prop 验证
我们可以为组件的 prop 指定验证规则。如果传入的数据不符合要求,Vue 会发出警告。这对于开发给他人使用的组件非常有用。
要指定验证规则,需要用对象的形式来定义 prop,而不能用字符串数组:
Vue.component('example', {
props: {
// 基础类型检测 (`null` 指允许任何类型)
propA: Number,
// 可能是多种类型
propB: [String, Number],
// 必传且是字符串
propC: {
type: String,
required: true
},
// 数值且有默认值
propD: {
type: Number,
default: 100
},
// 数组/对象的默认值应当由一个工厂函数返回
propE: {
type: Object,
default: function () {
return { message: 'hello' }
}
},
// 自定义验证函数
propF: {
validator: function (value) {
return value > 10
}
}
}
})
type
可以是下面原生构造器:
- String
- Number
- Boolean
- Function
- Object
- Array
- Symbol
type
也可以是一个自定义构造器函数,使用 instanceof
检测。
当 prop 验证失败,Vue 会抛出警告 (如果使用的是开发版本)。注意 prop 会在组件实例创建之前进行校验,所以在 default
或 validator
函数里,诸如 data
、computed
或 methods
等实例属性还无法使用。
24、非 prop 特性
所谓非 prop 特性,就是指它可以直接传入组件,而不需要定义相应的 prop。
尽管为组件定义明确的 prop 是推荐的传参方式,组件的作者却并不总能预见到组件被使用的场景。所以,组件可以接收任意传入的特性,这些特性都会被添加到组件的根元素上。
25、父子组件解耦
为了更方便维护,父子组件间一般是要解耦的。
父组件通过 prop 传递数据给子组件。
子组件则通过 事件接口 $on
、$emit
来告诉父组件,自已内部发生了什么。父组件再通过 v-on 绑定自定义事件函数,来对子组件所发生的事件作出反应。
26、插槽
当子组件插入到父组件后,还想在子组件标签中插入内容,则需要在子组件中定义至少一个插口<slot></slot>
<div id="parent">
<h1>父组件标题</h1>
<my-component>
<p slot="header">你好啊1</p><!--插入的内容,把插槽header内容替换掉-->
<p slot="footer">你好啊2</p><!--插入的内容,把插槽footer内容替换掉-->
<p>你好啊3</p><!--插入的内容,把未命名插槽内容替换掉,如果没有插槽则不显示-->
</my-component>
</div>
//子组件模板
<div>
<h2>这是子组件标题</h2>
<slot name="header">这是插槽备用内容</slot>
<slot name="footer">这是插槽备用内容</slot>
<slot>没有名字</slot>
</div>
作用域插槽
作用域插槽是一种特殊类型的插槽,用作一个 (能被传递数据的) 可重用模板,来代替已经渲染好的元素。
也就是父组件给子组件方便传递参数。
27、动态组件(重点)
通过使用保留的 <component>
元素,并对其 is
特性进行动态绑定,你可以在同一个挂载点动态切换多个组件:
var vm = new Vue({
el: '#example',
data: {
currentView: 'home'
},
components: {
home: { /* ... */ },
posts: { /* ... */ },
archive: { /* ... */ }
}
})
<component v-bind:is="currentView">
<!-- 组件在 vm.currentview 变化时改变! -->
</component>
keep-alive
如果把切换出去的组件保留在内存中,可以保留它的状态或避免重新渲染。为此可以添加一个 keep-alive
指令参数:
<keep-alive>
<component :is="currentView">
<!-- 非活动组件将被缓存! -->
</component>
</keep-alive>
28、编写可复用组件(重点)
在编写组件时,最好考虑好以后是否要进行复用。一次性组件间有紧密的耦合没关系,但是可复用组件应当定义一个清晰的公开接口,同时也不要对其使用的外层数据作出任何假设。
Vue 组件的 API 来自三部分——prop、事件和插槽:
- Prop 允许外部环境传递数据给组件;
- 事件允许从组件内触发外部环境的副作用;
- 插槽允许外部环境将额外的内容组合在组件中。
使用 v-bind
和 v-on
的简写语法,模板的意图会更清楚且简洁:
<my-component
:foo="baz"
:bar="qux"
@event-a="doThis"
@event-b="doThat"
>
<img slot="icon" src="...">
<p slot="main-text">Hello!</p>
</my-component>
29、子组件引用
尽管有 prop 和事件,但是有时仍然需要在 JavaScript 中直接访问子组件。为此可以使用 ref
为子组件指定一个引用 ID。例如:
<div id="parent">
<user-profile ref="profile"></user-profile>
</div>
var parent = new Vue({ el: '#parent' })
// 访问子组件实例
var child = parent.$refs.profile
30、异步组件(困难)
在大型应用中,我们可能需要将应用拆分为多个小模块,按需从服务器下载。为了进一步简化,Vue.js 允许将组件定义为一个工厂函数,异步地解析组件的定义。Vue.js 只在组件需要渲染时触发工厂函数,并且把结果缓存起来,用于后面的再次渲染。
你可以在工厂函数中返回一个 Promise
,所以当使用 webpack 2 + ES2015 的语法时可以这样:
Vue.component(
'async-webpack-example',
// 该 `import` 函数返回一个 `Promise` 对象。
() => import('./my-async-component')
)
当使用局部注册时,也可以直接提供一个返回 Promise
的函数:
new Vue({
// ...
components: {
'my-component': () => import('./my-async-component')
}
})
31、组件间的循环引用
假设你正在构建一个文件目录树。
你可能会有一个 tree-folder
组件:
<p>
<span></span>
<tree-folder-contents :children="folder.children"/>
</p>
以及一个 tree-folder-contents
组件:
<ul>
<li v-for="child in children">
<tree-folder v-if="child.children" :folder="child"/>
<span v-else></span>
</li>
</ul>
这两个组件同时为对方的父节点和子节点,这是矛盾的。当使用Vue.component 将这两个组件注册为全局组件的时候,框架会自动解决这个矛盾。
32、对低开销的静态组件使用 v-once
尽管在 Vue 中渲染 HTML 很快,不过当组件中包含大量静态内容时,可以考虑使用 v-once
将渲染结果缓存起来,就像这样:
Vue.component('terms-of-service', {
template: '\
<div v-once>\
<h1>Terms of Service</h1>\
...很多静态内容...\
</div>\
'
})
33、进入/离开 & 列表过渡
Vue 提供了 transition
的封装组件,在下列情形中,可以给任何元素和组件添加 entering/leaving 过渡
- 条件渲染 (使用
v-if
) - 条件展示 (使用
v-show
) - 动态组件
- 组件根节点
这里是一个典型的例子:
<div id="demo">
<button v-on:click="show = !show">
Toggle
</button>
<transition name="fade">
<p v-if="show">hello</p>
</transition>
</div>
new Vue({
el: '#demo',
data: {
show: true
}
})
.fade-enter-active, .fade-leave-active {
transition: opacity .5s;
}
.fade-enter, .fade-leave-to /* .fade-leave-active below version 2.1.8 */ {
opacity: 0;
}
可以结合 vue2-animate.css
使用。
还有一些动画的相关事件函数,例如:@before-enter、@enter、@after-enter、@before-leave、@leave、@after-leave、
34、自定义过渡的类名
我们可以通过以下特性来自定义过渡类名:
enter-class
enter-active-class
enter-to-class
(2.1.8+)leave-class
leave-active-class
leave-to-class
(2.1.8+)
他们的优先级高于普通的类名,这对于 Vue 的过渡系统和其他第三方 CSS 动画库,如 Animate.css 结合使用十分有用。
示例:
<link href="https://cdn.jsdelivr.net/npm/animate.css@3.5.1" rel="stylesheet" type="text/css">
<div id="example-3">
<button @click="show = !show">
Toggle render
</button>
<transition
name="custom-classes-transition"
enter-active-class="animated tada"
leave-active-class="animated bounceOutRight"
>
<p v-if="show">hello</p>
</transition>
</div>
new Vue({
el: '#example-3',
data: {
show: true
}
})
35、过渡模式
同时生效的进入和离开的过渡不能满足所有要求,所以 Vue 提供了 过渡模式
in-out
:新元素先进行过渡,完成之后当前元素过渡离开。out-in
:当前元素先进行过渡,完成之后新元素过渡进入。
<transition name="fade" mode="out-in">
<!-- ... the buttons ... -->
</transition>
36、虚拟DOM
虚拟dom只是描述了一种数据结构,他并不代表真实的dom对象,他只是描述了一个dom tree该有的组织方式,编译的过程就是根据模版结构生成dom数据结构的方式,高大上一点叫做虚拟dom。
Sumarry一下:
- Virtual DOM is any kind of representation of a real DOM (虚拟dom只是真实dom的一个代表)
- When we change something in our Virtual DOM Tree, we get a new Virtual Tree. Algorithm compares these two trees (old and new), finds differences and makes only necessary small changes to real DOM so it reflects virtual (如果我们在虚拟dom中对数据做了修改,我们会得到一个新的虚拟dom结构,算法比较这两颗dom树,找到不同之处,只需要在真实dom中做必要的修改来对应虚拟DOM)在编译期间我们要做的就是根据模版DOM结构来构造一个数据结构来对应模版html结构
37、混入
混入(mixins)是一种分发 Vue 组件中可复用功能的非常灵活的方式。混入对象可以包含任意组件选项。当组件使用混入对象时,所有混入对象的选项将被混入该组件本身的选项。当数据发生冲突时,会数据合并,以组件自身的数据优先。
var mixin = {
methods: {
foo: function () {
console.log('foo')
},
conflicting: function () {
console.log('from mixin')
}
}
}
var vm = new Vue({
mixins: [mixin],
methods: {
bar: function () {
console.log('bar')
},
conflicting: function () {
console.log('from self')
}
}
})
vm.foo() // => "foo"
vm.bar() // => "bar"
vm.conflicting() // => "from self"
全局混入(谨慎使用)
也可以全局注册混入对象。注意使用! 一旦使用全局混入对象,将会影响到 所有 之后创建的 Vue 实例。使用恰当时,可以为自定义对象注入处理逻辑。
// 为自定义的选项 'myOption' 注入一个处理器。
Vue.mixin({
created: function () {
var myOption = this.$options.myOption
if (myOption) {
console.log(myOption)
}
}
})
new Vue({
myOption: 'hello!'
})
// => "hello!"
38、自定义指令
当页面加载时,该元素将获得焦点 (注意:autofocus
在移动版 Safari 上不工作)。事实上,只要你在打开这个页面后还没点击过任何内容,这个输入框就应当还是处于聚焦状态。现在让我们用指令来实现这个功能:
// 注册一个全局自定义指令 `v-focus`
Vue.directive('focus', {
// 当被绑定的元素插入到 DOM 中时……
inserted: function (el) {
// 聚焦元素
el.focus()
}
})
如果想注册局部指令,组件中也接受一个 directives
的选项:
directives: {
focus: {
// 指令的定义
inserted: function (el) {
el.focus()
}
}
}
然后你可以在模板中任何元素上使用新的 v-focus
属性。
39、渲染函数 render & JSX
简单来说就是自已定义 v-if
等模板指令的行为。
重新定义 render
函数
JSX
是为了更容易的写 render
函数,使用之前需要装一个 Babel
插件。