- .camel
(2.1.0+) - 将 kebab-case attribute 名转换为 camelCase。
主要是针对 attribute 的,props 和 .prop 会默认将 kebab-case 转化为 camelCase。
在使用字符串模板或通过 vue-loader/vueify 编译时,无需使用 .camel。
<svg :view-box.camel="viewBox"></svg>
- .sync
(2.3.0+) - 语法糖,会扩展成一个更新父组件绑定值的 v-on 侦听器。
<child :msg.sync="msg"></child>
//会扩展成:
<child :msg="msg" @update:msg="val => msg = val"></child>
class 与 style
支持数组、对象,style建议优先使用驼峰式。
<div class="static" :class="{ active: isActive, 'text-danger': hasError }"></div>
<div :class="['classA', { classB: isB }, isActive ? activeClass : '']">
<div :style="{ fontSize: '14px', 'background-color': bgColor}"></div>
<div :style="[styleObjectA, styleObjectB]"></div>
//style兼容性写法
<div :style="{ display: ['-webkit-box', '-ms-flexbox', 'flex'] }"></div>
v-on
缩(简)写符号“@”,绑定事件监听,表达式可以是一个方法的名字或一个内联语句。
注:用在普通的html元素上时,只能监听 原生 DOM 事件。
用在自定义元素组件上时,也可以监听子组件触发的自定义事件。
| 名称 | 描述 | 汇总 | 1 |
|---|---|---|---|
| 1 | 2 | 3 | 4 |
<!-- 方法处理器 -->
<button v-on:click="doThis"></button>
<!-- 内联语句 -->
<button v-on:click="doThat('hello', $event)"></button>
<!-- 对象语法 (2.4.0+) -->
<button v-on="{ mousedown: doThis, mouseup: doThat }"></button>
<!-- 动态事件缩写 (2.6.0+) -->
<button @[event]="doThis"></button>
<!-- 在子组件上监听自定义事件 -->
<my-component @my-event="handleThis"></my-component>
<!-- 组件中的原生事件 -->
<my-component @click.native="onClick"></my-component>
<!-- 阻止冒泡、默认行为+串联修饰符 -->
<button @click.stop.prevent="doThis"></button>
<!-- 阻止默认行为,没有表达式 -->
<form @submit.prevent></form>
<!-- 键修饰符,键别名 -->
<input @keyup.enter="onEnter”>
修饰符
- .stop
调用 event.stopPropagation() 防止事件冒泡。
- .prevent
调用 event.preventDefault() 防止默认事件。
- .self
只作用本身,忽略了事件冒泡和事件捕获的影响
- .once
只触发一次回调。
- .native
监听组件根元素的原生事件。
- .left - (2.2.0)
只当点击鼠标左键时触发。
- .right - (2.2.0)
只当点击鼠标右键时触发。
- .middle - (2.2.0)
只当点击鼠标中键时触发。
键事件
.{keyCode | keyAlias} - 只当事件是从特定键触发时才触发回调。
可以通过全局 config.keyCodes 对象自定义键值修饰符别名:
camelCase 不可用 ,取而代之的是 kebab-case 且用双引号括起来。
<!-- 给 v-on 自定义键位别名 -->
Vue.config.keyCodes = {
f1: 112,
"media-play-pause": 179,
up: [38, 87]
}
<input type="text" @keyup.f1="method”>
<!-- 组合键事件 -->
<div @keyup.alt.67=”function”></div> //Alt + C
<div @click.ctrl=”function”></div> //Ctrl + Click
- .passive - (2.3.0)- 不拦截默认事件
一般用在滚动监听,如@scoll、@touchmove。
原理:滚动监听过程中,移动每个像素都会产生一次事件,每次都使用内核线程查询preventDefault会使滑动卡顿;通过passive将内核线程查询跳过,可以大大提升滑动的流畅度。
- .capture - 捕获
给元素添加一个监听器,当元素发生冒泡时,先触发带有该修饰符的元素。若有多个该修饰符,则由外而内触发。
div1 > div2.capture > div3 > div4
- 点击div4怎么触发呢? //先触发带有关键字的 div2;然后触发点击的 div4; 最后从里向外执行
顺序就是 div2=》div4=》div3=》div1
- 点击div3怎么触发呢?
顺序就是 div2=》div3=》 div1
- 点击div2怎么触发呢?
顺序就是 div2=》div1
v-model
在表单控件或者组件上创建双向绑定
修饰符
- .lazy
取代 input 监听 change 事件(也就是在失去焦点 或者 按下回车键时才更新数据)
- .number
输入字符串转为有效的数字
- .trim
输入首尾空格过滤
:is
动态组件
其值可以是 已注册组件的名字,或一个组件的选项对象;
也可以用于常规 HTML 元素,但这些元素将被视为组件,这意味着所有的 attribute 会作为 DOM attribute 被绑定。
<!-- 通过is特性,可以动态切换当前组件 -->
<keep-alive>
<component v-bind:is="currentTabComponent"></component>
</keep-alive>
解除 DOM 内模板的限制
有些 HTML 元素,诸如 <ul>、<table> 和 <select>,对于哪些元素可以出现在其内部是有严格限制的;
如果我们从以下来源使用模板的话,这条限制是不存在的:
-
字符串 (例如:
template: '...') -
单文件组件 ()
.vue -
<script type="text/x-template">
<table>
<!-- 自定义组件 <blog-post-row> 会被作为无效的内容提升到外部,并导致最终渲染结果出错。-->
<blog-post-row></blog-post-row>
<tr is="blog-post-row"></tr>
</table>
v-slot
缩(简)写符号“#”,提供具名插槽或需要接收 prop 的插槽。
注意默认插槽的缩写语法不能和具名插槽混用,因为它会导致作用域不明确。
<!-- child.vue -->
<template>
<div class="child">
<slot name="header" :user="{name:111}">
<div>这是默认header</div>
</slot>
<slot name="main" :user="{name:111}" :job="222">
</slot>
<slot></slot>
</div>
</template>
<!-- parent.vue -->
<template>
<div class="info">
<child>
//默认插槽
<template #default>
<p>我是footer</p>
</template>
//具名插槽 + 作用域插槽
<template v-slot:main="slotProps">
<p>{slotProps.user.name}</p>
</template>
//解构插槽Prop
<template #header="{user={name:11}}">
<p>{{name}}</p>
</template>
//动态插槽名
<template v-slot:[dynamicSlotName]>
...
</template>
//独占默认插槽的缩写语法
<current-user v-slot="slotProps">
{{ slotProps.user.firstName }}
</current-user>
</child>
</div>
</template>
:ref
被用来给元素或子组件注册引用信息;引用信息将会注册在父组件的 $refs对象上。
关于 ref 注册时间的重要说明:因为 ref 本身是作为渲染结果被创建的,在初始渲染的时候你不能访问它们 - 它们还不存在!$refs 也不是响应式的,因此你不应该试图用它在模板中做数据绑定。
<template>
<div id="app">
<div ref="testDom">11111</div>
<button @click="getTest">获取test节点</button>
<HelloWorld ref="hello"/>
<button @click="getHello">获取helloworld组件中的值</button>
</div>
</template>
<script>
export default {
methods: {
getTest() {
console.log(this.
$refs.testDom)
},
getHello() {
this.$
refs.hello.open();
}
}
};
</script>
:key
1、主要用在 Vue 的虚拟 DOM 算法,在新旧 nodes 对比时辨识 VNodes。如果不使用 key,Vue 会使用一种最大限度减少动态元素并且尽可能的尝试就地修改/复用相同类型元素的算法。而使用 key 时,它会基于 key 的变化重新排列元素顺序,并且会移除 key 不存在的元素。
2、也可以用于强制替换元素/组件而不是重复使用它。
without keys:

with keys:

<!-- 当 text 发生改变时,<span> 总是会被替换而不是被修改,因此会触发过渡 -->
<transition>
<span :key="text">{{ text }}</span>
</transition>
- v-cloak
隐藏未编译的 Mustache 标签直到实例准备完毕。
[v-cloak] {
display: none;
}
<div v-cloak>{{message}}</div>
- v-pre
跳过这个元素和它的子元素的编译过程。可以用来显示原始 Mustache 标签.跳过大量没有指令的节点会加快编译。
<span v-pre>{{ this will not be compiled }}</span>
- v-once
创建低开销的静态组件,除第一次以外的渲染,元素/组件及其所有的子节点将被视为静态内容并跳过。这可以用于优化更新性能;
- v-text
更新元素的 textContent;若更新部分的 textContent则使用 {{ Mustache }} 插值
- v-html
更新元素的 innerHTML,值作为 Vue 模板进行编译
- v-show
根据表达式的真假条件来切换元素的display属性
<h1 v-show="ok">Hello!</h1>
- v-if
根据表达式的值的真假条件来决定销毁和重建
<div v-if="type === 'A'">A</div>
<div v-else-if="type === 'B'">B</div>
<div v-else>Not A/B</div>
- v-for
<!-- 数组 -->
<li v-for="(item,index) in list">
{{ index }} - {{ item.message }}
</li>
<!-- 对象 -->
<div v-for="(value, key, index) in object">
{{ index }} - {{ key }} : {{ value }}
</div>
scoped 属性
vue-loade对单文件组件 <style> 标签增加了scoped属性的处理。原理就是在html标签上添加data-v-xxxxxxxx属性,然后在css类名后添加属性选择器,即利用css类选择 + 属性选择器实现样式局部化。
注:1、在单文件组件里,scoped 的样式不会应用在 v-html 内部,因为那部分没有被 Vue 的模板编译器处理。
2、使用 >>> 操作符实现渗透;css预处理器无法正确解析 >>> ,需使用别名 /deep/ 代替
<style scoped>
.a >>> .b { }
</style>
//编译结果: .a[data-v-f3f3eg9] .b { }
<style lang="less" scoped>
.a {
/deep/ .b{ }
}
</style>
functional 函数式组件
函数式组件不需要实例化,无状态,没有生命周期,所以渲染性能要好于普通组件。
区别:1、函数式组件不能通过$emit对外暴露事件,调用事件只能通过context.listeners.click的方式调用外部传入的事件。
2、函数式组件的props可以只声明一部分或者全都不声明,所有没有在props里面声明的属性都会被自动隐式解析为prop 。 3、因为函数式组件是没有实例化的,所以在外部通过ref去引用组件时,实际引用的是HTMLElement。
没有this,通过 context (上下文)传递,包含:
-
props:提供所有 prop 的对象 -
children:VNode 子节点的数组 -
slots:一个函数,返回了包含所有插槽的对象 -
scopedSlots:(2.6.0+) 一个暴露传入的作用域插槽的对象。也以函数形式暴露普通插槽。 -
data:传递给组件的整个数据对象,作为createElement的第二个参数传入组件 -
parent:对父组件的引用 -
listeners:(2.3.0+) 一个包含了所有父组件为当前组件注册的事件监听器的对象。这是data.on的一个别名。 -
injections:(2.3.0+) 如果使用了inject选项,则该对象包含了应当被注入的 property
模板语法
<template functional>
<div>
<slot name="header">
<p @click="listeners.ok(item)">
保存
</p>
</slot>
<p v-for="(item,index) in props.items" :key="index" @click="props.itemClick(item)" />
</div>
</template>
//使用
<template>
<div>
<List
:items="['Wonderwoman', 'Ironman']"
:item-click="item => (clicked = item)"
@ok="log"
/>
<p>Clicked hero: {{ clicked }}</p>
</div>
</template>
JSX语法
- <script> export default { functional: true, props: { text: { type: String } }, /** 渲染函数 @param {*} h @param {*} context 函数式组件没有this, props, slots等都在context上面挂着 */ render(h, context) { const { props } = context if (props.text) { return <p>{props.text}</p> } return <p>哈哈嗝</p> } } </script>
图片引用
//使用
<img src="~img/logo.png">
div{
background-image: url("~img/logo.png");
}
//动态路径配置
this.imgUrl = require(
img/${'logo'}.png
);
<img :src="imgUrl ">
<div :style="{backgroundImage: 'url(' + imgUrl + ')'}"></div>
data
组件间数据隔离(防止数据污染),使用方法返回data模型对象。
受现代JavaScript的限制,vue不能检测到对象属性的添加或删除。vue会在初始化实例是对属性执行getter/setter转换过程(使用Object.defineProperty进行数据的劫持)。所以属性必须在data对象上存在才能让vue转换它,才能让它是响应的。
data(){
return {
a:1
}
}
不能检测以下变动的数组、对象
vue重写了7个数组方法:push、pop、unshift、shift、spilce、sort、reverse;
// 错误
this.items[0]="变更";
this.obi.a="变更";
// 正确
this.$set(this.items, 0, newValue)
this.items.splice(this.items, 0, newValue)
this.$set(this.obi,"a",'变更')
为已有对象赋予多个新属性
vm.userProfile = Object.assign({}, vm.userProfile, { age: 27 })
动态添加根级别的响应式属性
this.$set(this.someObject,'b',2)
Vue.set(vm.someArray, 3, newObj)
this.$delete(this.smoeArray, 1)
Vue.delete(vm.someObject, 'a')
props
接收来自父组件的数据
<child :child-msg="msg"></child> //这里必须要用 - 代替驼峰
子组件:
props: {
childMsg: {
type: [String, Number], //可能的类型
default: 0, //默认值
required: true, //必填的数据
validator: function (value) {
return value >= 0
} //自定义验证函数
}
}
props: ['childMsg']
props: { childMsg: Array} //这样可以指定传入的类型,如果类型不对,会警告
computed
计算属性将被混入到 Vue 实例中。所有 getter 和 setter 的 this 上下文自动地绑定为 Vue 实例。
//动态样式
computed: {
toggleClass() {
return [ this.goleft ? "toggle" : "" ];
},
toggleStyle() {
return {fontSize: this.size + 'px'};
}
}
//计算属性默认只有 getter ,不过在需要时你也可以提供一个 setter :
computed: {
fullName: {
get: function () {
return this.firstName + ' ' + this.lastName
},
set: function (newValue) {
var names = newValue.split('')
this.firstName = names[0]
this.lastName = names[names.length - 1]
}
}
}
this.fullName = 'John Doe'
watch
主要用于监听data中未提供change回调的v-model值、props(或inject)、store中的改变
watch:{
//普通的watch监听
lastName:function(val,oldval){
console.log(this.lastName)
}
//深度监听,可监听到对象、数组的变化
childrens:{
handler:function(val,oldval){
console.log(val.name)
},
deep:true
},
//键路径必须加上引号
'childrens.name':function(){
console.log(val+"aaa")
},
//路由监听
$route(to,from){
console.log(to.path);
},
//进入组件会立即执行
a:{
handler:function(val,oldval){
console.log(val.name)
},
immediate: true
},
}
实例方法
实例 property
-
vm.$el 当前组件树的根 Vue 实例
-
vm.$refs 一个对象,持有注册过
refattribute 的所有 DOM 元素和组件实例 -
vm.$root 当前组件树的根 Vue 实例
-
vm.$parent 父实例,如果当前实例有的话。
-
vm.$children 当前实例的直接子组件
-
vm.$slots 用来访问被插槽分发的内容。每个具名插槽 有其相应的属性
-
vm.$scopedSlots 用来访问作用域插槽
-
vm.$data 实例观察的数据对象
-
vm.$props 当前组件接收到的 props 对象
-
vm.$attrs 包含了父作用域中不作为 prop 被识别 (且获取) 的特性绑定
-
vm.$listeners 包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器
-
vm.$options 用于当前 Vue 实例的初始化选项
-
vm.$isServer 当前 Vue 实例是否运行于服务器
$set
增、改state
$delete
删除state
$watch
返回一个取消观察函数,用来停止触发回调
var unwatch = vm.$watch('a', cb, {deep, immediate})
unwatch() //取消监听
$on
监听一个自定义事件;事件可以由vm.$emit触发;回调函数会接收所有传入事件触发函数的额外参数。
this.$on('test', function (msg) {
console.log(msg)
}
$once
只触发一次,在第一次触发之后移除监听器。
this.$once( event, callback )
$off
关闭自定义监听
如果没有提供参数,则移除所有的事件监听器;
如果只提供了事件,则移除该事件所有的监听器;
如果同时提供了事件与回调,则只移除这个回调的监听器;
this.$off( [event, callback] )
$emit
触发自定义监听
this.$emit('test', 'hi’)
$mount 手动挂载实例
如果 Vue 实例在实例化时没有收到 el 选项,则它处于“未挂载”状态,没有关联的 DOM 元素。可以使用 vm.$mount() 手动地挂载一个未挂载的实例。 如果没有提供 elementOrSelector 参数,模板将被渲染为文档之外的的元素,并且你必须使用原生 DOM API 把它插入文档中。
var MyComponent = Vue.extend({
template: '<div>Hello!</div>'
})
// 创建并挂载到 #app (会替换 #app)
new MyComponent().$mount('#app')
// 同上
new MyComponent({ el: '#app' })
// 或者,在文档之外渲染并且随后挂载
var component = new MyComponent().$mount()
document.getElementById('app').appendChild(component.$el)
$forceUpdate 强制刷新
手动强制更新视图(同$set问题场景,一般是依赖了一个未被 Vue 的响应式系统追踪的状态)。
注意它仅仅影响实例本身和插入插槽内容的子组件,而不是所有子组件
$nextTick 回调延迟
将回调延迟到下次 DOM 更新循环之后执行。在修改数据之后立即使用它,获取更新后的 DOM。
this.msg = 'Hello';// DOM 还没有更新
this.$nextTick(function () {
//DOM 更新了
})
// 作为一个 Promise 使用 (2.1.0 起新增,如果没有提供回调且在支持 Promise 的环境中,则返回一个 Promise)
Vue.nextTick().then(function () {
// DOM 更新了
})
$destroy() 销毁实例
完全销毁一个实例。清理它与其它实例的连接,解绑它的全部指令及事件监听器。触发 beforeDestroy 和destroyed 的钩子。
在大多数场景中不应该调用这个方法。最好使用 v-if 和 v-for 指令以数据驱动的方式控制子组件的生命周期。
组件传参
1.父子组件通信
.sync修饰符扩展双向数据绑定
注:会被扩展为一个自动更新父组件属性的 v-on 监听器。
<child @childMsg.sync="msg"></child>
//会扩展成:
<comp :childMsg="msg" @update:childMsg="val => msg=val"></comp>
props:['childMsg'] //接收父组件参数
this.$emit('update:childMsg', newValue) //触发更新事件
2.公共实例传值
定义一个公共实例文件bus.js,作为中间仓库来传值
步骤1:在common.js中创建一个实例
import Vue from 'vue'
export var vm = new Vue();
步骤2:组件1触发
<div @click="eve">触发</div>
import {vm} from "../../assets/common.js"
methods: {
eve: function () {
vm.$emit('get', 4)
}
}
步骤3:组件2接收
import {vm} from "../../assets/common.js”;
created () {
vm.$on('get', (data) => {
console.log(data)
vm.name = data
})
},
beforeDestroy () {
vm.$off('get', this.myhandle) //防止触发多次
}
进阶使用
parent 指定父实例
指定已创建的实例之父实例,在两者之间建立父子关系。子实例可以用 this.$parent 访问父实例,子实例被推入父实例的 $children 数组中。
filters 过滤器
声明一个本地的过滤器:
filters: {
Upper: function (value) {
return value.toUpperCase()
},
}
全局定义过滤器:
第1步:新建filter.js文件
const vfilter = {
addZero : value => {
return value.replace(/(\d{4})(\d{2})(\d{2})/g, '
$1-$
2-$3')
}
}
export default vfilter
第2步:Main.js文件加入
import vfilter from ‘./filter.js’;
Object.keys(vfilter).forEach(key => {
Vue.filter(key, vfilter[key])
})
第3步:使用规则
{{content | addZero}}
{{ message | filterA | filterB }} //filterB执行的是A的结果
<div v-bind:id="rawId | formatId"></div>
{{ message | filterA('arg1', arg2) }} //filterA 为接收三个参数的过滤器函数( message arg1 arg2 )
directives 自定义指令
1、组件中注册自定义指令:加directives这个选项
directives:{
focus:{
inserted(target,binding){
target.focus()
}
}
}
2、全局自定义指令:
第1步:新建directive.js文件
const wdirectives = {
myDirective: {
bind: function () {指令第一次绑定到元素时调用},
inserted: function () {被绑定元素插入父节点时调用},
update: function () {所在组件的 VNode 更新时调用},
componentUpdated: function () {指令所在组件的 VNode 及其子 VNode 全部更新后调用},
unbind: function () {指令与元素解绑时调用}
}
}
export default (Vue)=>{
Object.keys(wdirectives).forEach(key => {
Vue.directive(key, wdirectives[key])
})
}
第2步:Main.js文件加入
import directives from '@/components/directives'
Vue.use(directives);
第3步:组件中使用
<input type="text" v-myDirective />
3、全局标签指令
Vue.elementDirective('bg-color', {
bind( color ) { this.el.style.backgroundColor = color ? color : '#ccc'; }
});
<bg-color>hello Vue!</bg-color>
mixins 混入
一般有两种用途:
1、在你已经写好了构造器后,需要增加方法或者临时的活动时使用的方法,这时用混入会减少源代码的污染。
2、很多地方都会用到的公用方法,用混入的方法可以减少代码量,实现代码重用。
-
方法和参数在各组件中不共享;
-
当组件使用minxins对象时,选项会被合并,键冲突的组件会覆盖minxins对象的;
-
选项为钩子函数(对象)会被合并调用,混合对象里的钩子函数在组件里的钩子函数之前调用;
//申明混合对象
export const mixin = {
data(){
return {
a:1
}
},
created(){
this.foo()
},
methods: {
foo: function () {
console.log('foo')
},
conflicting: function () {
console.log('from mixin')
}
}
}
//组件内使用
import mixin from "./mixin.js";
export default {
mixins: [mixin],
methods: {
bar: function () {
console.log('bar')
},
conflicting: function () {
console.log('from self')
}
}
}
//全局引用
import mixin from './mixin'
Vue.mixin(mixin)
extends 拓展
允许声明扩展另一个组件 (可以是一个简单的选项对象或构造函数),而无需使用 Vue.extend。这主要是为了便于扩展单文件组件。
extends扩展和mixins很像,不同点是extends传入的是对象写法,而mixins是数组写法。
export const extendsTest = {
methods: {}
created: {}
}
//组件中使用
import extendsTest from "./extendsTest.js";
export default {
extends: extendsTest,
methods: {
bar: function () {
console.log('bar')
}
}
}
Vue.extend 扩展
Object | Function 允许声明扩展另一个组件
实例:全局弹框开发
Vue.extend( options ) 使用基础 Vue 构造器,创建一个“子类”。参数是一个包含组件选项的对象
vm.$mount() 手动地挂载一个未挂载的实例
vm.$destroy() 完全销毁一个实例。清理它与其它实例的连接,解绑它的全部指令及事件监听器。触发 beforeDestroy 和 destroyed 的钩子
实现步骤:
1、src/toast/index.vue
<template>
<div class="wrap" v-if="showWrap" :class="showContent ?'fadein':'fadeout'">{{text}}</div>
</template>
<script>
export default {
props:['showWrap','text','showContent'],
}
</script>
2、src/toast/index.js
import vue from 'vue'
import toastComponent from './index.vue'
vue.component('toastComponent', toastComponent)
function showToast(text, duration = 2000,callBack) {
let VueMessage = vue.extend({
render(h) {
let props = {
text,
showWrap: this.showWrap,
showContent:this.showContent
}
return h('toastComponent', {props})
},
data() {
return {
showWrap:true,
showContent:true
}
}
})
let vm = new VueMessage().
$mount()
document.body.appendChild(vm.$
el)
let t1 = setTimeout(() => {
clearTimeout(t1)
vm.showContent = false;
let t2 = setTimeout(() => {
clearTimeout(t2)
document.body.removeChild(el)
vm.$destroy();
vm.showWrap = false;
vm = null // 设置为null,好让js垃圾回收算法回收,释放内存
callBack && (typeof callBack === 'function') && callBack()
}, 300)
}, duration)
}
export default function() {
vue.prototype.$toast = showToast
}
3、src/main.js
import toastRegistry from './toast'
Vue.use(toastRegistry) //等同toastRegistry()
4.组件中使用
this.$toast('提示内容',3000,function(){})
component 自定义组件
组件构建的三种方式
//用Vue.extend()方法,构建一个名字为myCom的组件
var myCom = Vue.extend({
template: '<div>这是我的组件</div>'
})
//template标签构建法
<template id="myCom">
<div>这是template标签构建的组件</div>
</template>
//script标签构建法
<script type="text/x-template" id="myCom1">
<div>这是script标签构建的组件</div>
</script>
//注册组件
Vue.component('my-com',myCom)
<template>
<div>
<myComponent :show.sync='valueChild'/>
</div>
</template>
<script>
import Vue from 'vue'
Vue.component('myComponent', {
template:
<div v-if="show"> <p>默认初始值是{{show}},所以是显示的</p> <button @click.stop="closeDiv">关闭</button> </div>
,
props:['show'],
methods: {
closeDiv() {
this.$emit('update:show', false); //触发 input 事件,并传入新值
}
}
})
export default{
}
</script>
//公共组件开发
第1步:src/components/Table.vue
export default {
props:{
listData:Array
},
methods:{
showDetail(){
this.$emit('showDetail',data)
}
}
}
第2步:src/components/index.js
import Vue from 'vue'
import TableList from './Table'
const components = {
'table-list': TableList
}
Vue.use({
install: Vue => {
Object.keys(components).forEach(key => {
Vue.component(
w-${key}
, components[key])
})
}
})
第3步:src/main.js
import './components’
第4步:组件中使用
<w-table-list :listData = “configList” @showDetail = 'myDetail'></w-table-list>
render 字符串模板的代替方案
有些场景中用 template 实现起来代码冗长繁琐而且有大量重复,这时候就可以用 render 函数
// 根组件
<template>
<div>
<child>
<h1 slot="childheader">childheader</h1>
<template slot="childbody">childbody</template>
</child>
</div>
</template>
<script>
import child from './child.vue'
export default {
components: {
child
}
}
</script>
//子组件child.vue
<script>
export default {
render: function(createElement) {
let childheader = this.
$slots.childheader,
childbody = this.$
slots.childbody;
return createElement(
'div', {
ref: 'myRef',
class: {
foo: true
},
style: {
width: '100%',
height: '50px'
},
attrs: {
id: 'child-h2'
},
domProps: {
//innerHTML: '内容是'
},
// 组件 props传参
props: {
myProp: 'bar'
},
// 事件监听器基于
on
所以不再支持如
v-on:keyup.enter
修饰器,需要手动匹配 keyCode。
on: {
click: this.clickHandler
},
// 仅对于组件,用于监听原生事件,而不是组件内部使用
vm.$emit
触发的事件。
nativeOn: {
click: this.nativeClickHandler
},
directives: [],
}, [
createElement('div', childheader),
childbody,
]
)
}
}
</script>
provide/inject 依赖注入
provide 选项允许我们指定我们想要提供给后代组件的数据/方法;
在任何后代组件里,我们都可以使用 inject 选项来接收指定的我们想要添加在这个实例上的属性
provide 和 inject 绑定并不是可响应的。这是刻意为之的。然而,如果你传入了一个可监听的对象,那么其对象的属性还是可响应的。
实例:根路由注册路由刷新方法
<template>
<router-view v-if="isRouterAlive"/>
</template>
<script>
export default {
name:’app’,
provide(){
return{
reload: this.reload
}
},
data () {
return {
isRouterAlive: true
}
},
methods: {
reload () {
this.isRouterAlive = false;
this.$nextTick(() => (this.isRouterAlive = true))
}
}
}
</script>
//调用
inject:[‘reload’]
this.reload();
方案二:
//空白页src/pages/refresh.vue
<script>
export default {
beforeRouteEnter(to, from, next) {
next(vm => {
vm.$router.replace(from.path)
})
}
}
</script>
//调用
this.$router.replace('/refresh’)
全局API
Vue.compile 模版字符串生成render
将一个模板字符串编译成 render 函数,只在完整版时可用。
var res = Vue.compile('<div><span>{{ msg }}</span></div>')
new Vue({
data: {
msg: 'hello'
},
render: res.render,
staticRenderFns: res.staticRenderFns
})
Vue.observable 让一个对象可响应
返回的对象可以直接用于渲染函数和计算属性内,并且会在发生变更时触发相应的更新。
也可以作为最小化的跨组件状态存储器,用于简单的场景。
const state = Vue.observable({ count: 0 })
const Demo = {
render(h) {
return h('button', {
on: { click: () => { state.count++ }}
},
count is: ${state.count}
)
}
}
Vue.use 注册组件
如果插件是一个对象,必须提供 install 方法;如果插件是一个函数,它会被作为 install 方法。
install 方法调用时,会将 Vue 作为参数传入。
第一步:MyPlugin.js中:
import Vue from 'vue'
let MyPlugin = {};
MyPlugin.install = function (Vue, options) {
//添加实例方法
Vue.prototype.
$myMethod = function (Options) {
},
//自定义指令
Vue.directive(key, directives[key]),
//自定义过滤器
Vue.filter(key, filters[key]),
//自定义组件
Vue.component(`w-$
{key}`, components[key]),
// 2.全局混入
Vue.mixin({
created: function () {
console.log(112) //Vue 实例下的每个组件都会在挂载的时候执行一次这个方法,输出多次112
}
})
}
export default MyPlugin
第二步:main.js中
import MyPlugin from './myPlugin'
Vue.use(MyPlugin)
内置组件
component 元组件
渲染一个“元组件”为动态组件。依 is 的值,来决定哪个组件被渲染。
<component :is="componentId"></component>
<!-- 也能够渲染注册过的组件或 prop 传入的组件 -->
<component :is="$options.components.child"></component>
slot 插槽
<slot> 元素作为组件模板之中的内容分发插槽。<slot> 元素自身将被替换。
<slot></slot>
<slot name='header'></slot>
//使用
<template v-slot:default>
<p>匿名插槽内容</p>
</template>
<template v-slot:header>
<p>header插槽内容</p>
</template>
keep-alive 缓存组件
包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们;
首次进入: beforeRouteEnter => created => ... => activated => ... => deactivated
后续进入: beforeRouteEnter => activated => deactivated,
-
include- 字符串或正则表达式。只有名称匹配的组件会被缓存。 -
exclude- 字符串或正则表达式。任何名称匹配的组件都不会被缓存。 -
max- 数字达到了,在新实例被创建之前,已缓存组件中最久没有被访问的实例会被销毁掉。
<keep-alive include="a,b">
<component :is="view"></component>
</keep-alive>
<keep-alive :include="/a|b/">
<component :is="view"></component>
</keep-alive>
<keep-alive :include="['a','b']">
<component :is="view"></component>
</keep-alive>
结合router,缓存部分页面
<template>
<div id="app">
<router-view v-if="!
$route.meta.keepAlive"></router-view>
<keep-alive>
<router-view v-if="$
route.meta.keepAlive"></router-view>
</keep-alive>
</div>
</template>
export default new Router({
routes: [
{
path: '/',
name: 'Hello',
component: Hello,
meta: {
keepAlive: false // 不需要缓存
}
},
{
path: '/page1',
name: 'Page1',
component: Page1,
meta: {
keepAlive: true // 需要被缓存
}
}
]
})
//在需要缓存的页面加两个钩子beforeRouteEnter,activated
beforeRouteEnter(to, from, next) {
//判断返回需要缓存的路由地址
if(from.name == 'carInfo') {
to.meta.keepAlive = true; //是否返回的判断依据改为true
}
next();
},
//activated会在history模式时正常执行
activated() {
//非从详情页返回时正常加载数据
if (!this.
$route.meta.keepAlive) {
this.getList(1) //执行加载数据的方法
}
//执行完初始化isBack
this.$
route.meta.keepAlive = false;
}
transition 过渡组件
<div id="demo">
<button v-on:click="show = !show">
Toggle
</button>
<transition name="fade">
<p v-if="show">hello</p>
</transition>
</div>
<script>
export default {
data() {
return{
show: true
}
}
}
</script>
<style>
.fade-enter-active, .fade-leave-active {
transition: opacity .5s
}
.fade-enter, .fade-leave-active {
opacity: 0
}
</style>
过渡模式
in-out:首先是新元素转换,然后在完成时,当前元素转换出来。
out-in:当前元素首先转换,然后在完成时,新元素转换
class定义:
.fade-enter{ } 进入过渡的开始状态,元素被插入时生效,只应用一帧后立即删除;(运动的初始状态)
.fade-enter-active{ } 进入过渡的结束状态,元素被插入时就生效,在 transition/animation 完成之后移除。这个类可以被用来定义过渡的过程时间
,延迟和曲线函数。
.fade-leave{ } 离开过渡的开始状态,元素被删除时触发,只应用一帧后立即删除;
.fade-leave-active{ } 离开过渡的结束状态,元素被删除时生效,在 transition/animation 完成之后移除。这个类可以被用来定义过渡的过程时间,
延迟和曲线函数。
自定义类名:
<transition
name="fade"
enter-class="fade-in-enter"
enter-active-class="fade-in-active"
leave-class="fade-out-enter"
leave-active-class="fade-out-active"
>
</transition>
相关函数:
<transition name="fade"
@before-enter="beforeEnter” //动画enter之前
@enter="enter” // 动画enter进入
@after-enter="afterEnter” //动画进入之后
@before-leave="beforeLeave” //动画leave之前
@leave="leave” // 动画leave
@after-leave="afterLeave” //动画leave之后
<p v-show="show"></p>
</transition>
扩展动画animate.css库:
链接:www.cnblogs.com/xiaohuochai…
npm install animate.css --save安装,再引入
import animated from 'animate.css'
Vue.use(animated)
直接使用animated中的动画class名,注意:必须使用animated这个class名,否则动画会无效
<div class="box animated bounceInDown"></div>
使用animate跳转页面的动画效果:
<!-- 方式一: -->
<transition mode="out-in" enter-active-class="bounceInLeft" leave-active-class="bounceOutRight">
<router-view class="animated"></router-view>
</transition>
<!-- 方式二: -->
<transition mode="out-in" enter-active-class="animated fadeInRight" leave-active-class="animated fadeOutLeft">
<router-view></router-view>
</transition>
transition-group 列表过渡
<transition-group name='listcss' tag="ul">
<li v-for='item of items' :key='item.id'>{{item.title}}</li>
</transition-group>
<button @click='add'>css动画</button>
add:function(){
this.items.push({
id:this.count ++,
title:'hello animate!'
})
}
.listcss-enter,.listcss-leave-to{
opacity:0;
}
.listcss-enter-active,.listcss-leave-active{
transition:opacity 3s;
}
vue-router
<router-view> 视图组件
1、通过命名视图(name),来设置不同的路由中用不同的header和main模板
<template>
<router-view name="header"></router-view> //header
<router-view name="main"></router-view> //内容区
</template>
{
path: '/vueRouterPlay1',
name: 'vueRouterPlay1',
components: {
'header': header1,
'main': vueRouterPlay1
}
}
<router-link> 导航链接
- replace
设置 replace 属性的话,当点击时,会调用 router.replace() 而不是 router.push(),于是导航后不会留下 history 记录
- append
设置 append 属性后,则在当前 (相对) 路径前添加基路径。/a导航/b,如果配了,则为 /a/b。没配则为/b。
<router-link to="home">Home</router-link>
<router-link :to="{ name: 'user', params: { userId: 123 }}">User</router-link>
<router-link :to="{ path: 'register', query: { plan: 'private' }}">Register</router-link>
- tag
将<router-link> 渲染成指定标签
- event
默认值: 'click'声明可以用来触发导航的事件。可以是一个字符串或是一个包含字符串的数组
- active-class
设置链接激活时使用的 CSS 类名,默认值: "router-link-active"
默认值可以通过路由的构造选项 linkActiveClass 来全局配置。
- exact
“是否激活”默认类名的依据是包含匹配。想要链接使用“精确匹配模式”,则使用 exact 属性
<!-- 这个链接只会在地址为 / 的时候被激活 -->
<router-link to="/" exact></router-link>
- exact-active-class
默认值: "router-link-exact-active"配置当链接被精确匹配的时候应该激活的 class。
注意默认值也是可以通过路由构造函数选项 linkExactActiveClass 进行全局配置的。
$route 路由信息对象
包括 path,params,hash,query,fullPath,matched,name 等路由信息参数。
{
fullPath: "/",
hash: "",
matched: [{…}, {…}],//路由匹配项参数
meta: {keepAlive: true},
name: "index",
params: {},
path: "/",
query: {},
}
$router “路由实例”对象
即使用 new VueRouter创建的实例,包括了路由的跳转方法,钩子函数等。
router.push(location, onComplete?, onAbort?)
router.push(location).then(onComplete).catch(onAbort)
router.replace(location, onComplete?, onAbort?)
router.replace(location).then(onComplete).catch(onAbort)
router.go(n)
router.back()
router.forward()
//动态添加更多的路由规则
router.addRoutes(routes: Array<RouteConfig>
//注册一个回调,该回调会在路由导航过程中出错时被调用
router.onError(callback)
路由配置
mode history与hash路由模式切换
base 项目基础路径
scrollBehavior 路由滚动行为控制
alias “别名”的功能让你可以自由地将 UI 结构映射到任意的 URL
redirect 重定向
meta 路由元信息
'/user/:id’ 动态路由
beforeEnter 路由独享的导航钩子
const router = new VueRouter({
mode: 'history',
base: '/ibay',
scrollBehavior(to, from, savedPosition) {
if (savedPosition) {
return savedPosition
} else {
if (to.hash) {
return {selector: to.hash}
} else {
return {x:0,y:0}
}
}
},
routes: [
{ path: '/a', component: A, alias: '/b’ },
{ path: '/a', redirect: { name: 'foo' }},
{ path: '/user/:id', component: User },
{
path: '/foo',
component: Foo,
beforeEnter: (to, from, next) => {
next('/');
},
children: [
{
path: 'bar',
component: Bar,
meta: { keepAlive: true }
}
]
}
]
})
导航钩子
- 路由独享守卫
{
path: '/drinks',
name: 'Drinks',
component: Drinks,
beforeEnter:(to, from,next) =>{
alert('drinks')
next()
}
},
- 全局守卫
next(false): 中止当前导航。如果URL已更改(由用户手动或通过后退按钮),则会将其重置为from的路径
next('/'): 重定向到其他位置
注:确保始终调用该next函数,否则永远不会解析挂钩。
router.beforeEach((to, from, next) => {
next()
})
router.afterEach((to, from, next) => {
window.document.title = to.meta.name
next()
})
- 组件内部守卫
beforeRouteEnter 渲染该组件的对应路由被 confirm 前调用
beforeRouteUpdate 当前路由改变(二级路由)
beforeRouteLeave 导航离开该组件的对应路由时被调用
*beforeRouteEnter 是支持给 next 传递回调的唯一守卫
beforeRouteEnter 守卫 不能 访问 this,因为守卫在导航确认前被调用,因此即将登场的新组件还没被创建。
可通过传一个回调给 next来访问组件实例。在导航被确认的时候执行回调,并且把组件实例作为回调方法的参数。
beforeRouteEnter (to, from, next) {
next(vm => {
// 通过
vm
访问组件实例
})
}
vuex的流程?
页面通过mapAction异步提交事件到action。action通过commit把对应参数同步提交到mutation。mutation会修改state中对于的值。最后通过getter把对应值跑出去,在页面的计算属性中,通过mapGetter来动态获取state中的值
vuex的优势?
状态管理工具 核心是响应式的做到数据管理
一个页面发生数据变化。动态的改变对应的页面
vuex工作原理?
相关博客:www.jianshu.com/p/d95a7b8af…
vuex整体思想诞生于flux,可其的实现方式完完全全的使用了vue自身的响应式设计,依赖监听、依赖收集都属于vue对对象Property set get方法的代理劫持。vuex中的store本质就是没有template的隐藏着的vue组件。
vuex有哪几种状态和属性?
有五种,分别是 State、 Getter、Mutation 、Action、 Module (就是mapAction等)
vuex的State特性是?
stae就是存放数据的地方,类似一个仓库
特性就是当mutation修改了state的数据的时候,他会动态的去修改所有的调用这个变量的所有组件里面的值,若是store中的数据发生改变,依赖这个数据的组件也会发生更新。
//只读:
this.$store.state.user.count
vuex的Getter特性是?
从 store 中的 state 中派生出一些状态,mapGetter经常在计算属性中被使用;
getters: {
//可以接受其他 getter 作为第二个参数
doneTodosCount: (state, getters) => {
return getters.doneTodos.length
}
//返回一个函数,来实现给 getter 传参
getTodoById: (state) => (id) => {
return state.todos.find(todo => todo.id === id)
}
}
computed: {
doneTodosCount () {
//不支持直接修改
return this.$store.getters.doneTodosCount
}
}
this.$store.getters.getTodoById(2);
vuex的Mutation特性是?
更改 Vuex 的 store 中的状态的唯一方法是提交 mutation。
混合异步会导致很难追踪状态state的改变;所以官方推荐使用action提交异步
同步的意义在于这样每一个 mutation 执行完成后都可以对应到一个新的状态。
mutations: {
increment (state, payload) {
state.count += payload.amount
}
}
store.commit('increment', {
amount: 10
})
store.commit({
type: 'increment',
amount: 10
})
vuex的Action特性是?
Action 提交的是 mutation,而不是直接变更状态。 只是一个架构性的概念,并不是必须的,说到底只是一个函数,你在里面想干嘛都可以,只要最后触发 mutation 就行。
//接受一个与 store 实例具有相同方法和属性的 context 对象
actions: {
incrementAsync (context, payload) {
const {commit} = context;
setTimeout(() => {
commit('increment')
}, 1000)
}
}
// 以载荷形式分发
store.dispatch('incrementAsync', {
amount: 10
})
// 以对象形式分发
store.dispatch({
type: 'incrementAsync',
amount: 10
})
组合action
actions: {
async actionA ({ commit }) {
commit('gotData', await getData())
},
async actionB ({ dispatch, commit }) {
await dispatch('actionA') // 等待 actionA 完成
commit('gotOtherData', await getOtherData())
}
}
vuex模块化

1.在src下新建store/index.js
import Vue from 'vue'
import vuex from 'vuex'
Vue.use(vuex);
import car from './modules/car.js';
import product from './modules/product.js';
export default new vuex.Store({
modules: {
car,
product
}
})
2.在store下新建store/modules/user.js
const state = {
count:10,
cityStats: {
siteNum: 0,
cartNum: 0
}
};
const user = {
//定义命名空间,防止多个模块同名共享,使用时需要带上命名空间
namespaced: true,
//单一状态树,数据源
state,
//根据存储状态计算派生状态
getters:{
arrList: state => state.arr.map(item => item.score >= 60 ? '及格' : '不及格’ ),
//可以通过让 getter 返回一个函数,来实现给 getter 传参
getTodoById: (state) => (id) => {
return state.todos.find(todo => todo.id === id)
}
},
mutations: {
//批量注册
...Object.keys(state).reduce((obj, key) => {
return {
...obj,
[key]: (state, payload) => state[key] = payload,
}
}, {}),
increment (state) {
state.count++
}
},
actions: {
increment (context) {
context.commit('increment')
},
fetchCityStats({state}, payload) {
if (!payload.cityCode) return
apiMap.citySummary(payload.cityCode).then(res => {
state.cityStats.cartNum = res.cartNum
state.cityStats.siteNum = res.siteNum
})
}
}
}
export default user;
3.main.js
import store from './store'
new Vue({
el: '#app',
router,
store,
template: '<App/>',
components: { App }
})
4.组件中使用
import { mapState, mapGetters, mapMutations, mapActions } from 'vuex’;
computed:{
// 映射带有命名空间的state,第一个参数模块名(可选)
...mapState('user', [
'count'
]),
...mapState({
// 传字符串参数 'username' 等同于
state => state.username
createBy: 'username',
countAlias: state => state.user.count,
// 为了能够使用
this
获取局部状态,必须使用常规函数
countPlusLocalState (state) {
return state.count + this.localCount
}
}),
...mapGetters({
doneCount: 'doneTodosCount'
})
},
methods: {
...mapMutations('user', {
add: 'increment',
}),
...mapActions('user', [
'addCardFun',
]),
}
createNamespacedHelpers 创建基于某个命名空间辅助函数
import { createNamespacedHelpers } from 'vuex'
const { mapState, mapActions } = createNamespacedHelpers('user')//子模块user/mine
export default {
computed: {
// 在
user
中查找
...mapState({
a: state => state.a,
})
},
methods: {
// 在
user
中查找
...mapActions([
'foo',
])
}
}
SPA单页面优缺点?
- 优点
良好的交互体验
前端进行的是局部的渲染,避免了不必要的跳转和重复的渲染。
前后端职责业分离(前端负责view,后端负责model),架构清晰
单页web应用可以和RESTful规约一起使用,通过REST API提供接口数据,并使用Ajax异步获取,这样有助于分离客户端和服务器的工作。
减轻服务器的压力
服务器只需要提供数据,不需要管前端的展示逻辑和页面合成,提高了 性能
SPA应用中服务器可以先将一份包含静态资源(HTML CSS JS等)的静荷数据(payload)发送给客户端,之后客户端只需要获取渲染页面或视图数据即可,
共用一套后端程序代码
不用修改后端程序代码就可以同时用于web界面、手机、平板灯多种客户端
- 缺点
SEO(搜索引擎优化)难度高
由于所有内容都在一个页面中进行动态的替换,也就是利用hash片段实现路由,而利用hash片段不会作为HTTP请求中的一部分发送给服务器,所以在SEO上有着天然的弱势
而SPA使用哈市片段的目的是;片段内容发送变化时,浏览器不会像URL发送变化时那样发送请求,这样就可以只请求页面或渲染所需的数据,而不是每一个页面获取并解析整份文档
首次加载时间过长
为实现单页Web应用功能及显示效果,需要在加载页面使将js、CSS统一加载,部分页面按需加载。
页面复杂都提高,复杂逻辑程度成倍
由于后端只提供数据而不再管前端的展示逻辑和页面合成,所以这些展示逻辑和页面合成都需要在前端进行编写(前进、后退等),所以会大大提高页面的复杂性和逻辑的难度
vue路由跳转与loaction.href的区别?
1、使用location.href='/url'来跳转,简单方便,但是刷新了页面
2、使用history.pushState('/url'),无刷新页面,静态跳转。
3、引进router,然后使用router.push('/url')来跳转,使用了diff算法,实现了按需加载,减少了dom的消耗。
其实使用router跳转和使用history.pushState()没什么差别的,因为vue-router就是用了history.pushState(),尤其是在history模式下。
vue路由懒加载的几种方式?
- vue异步组件
component: resolve => require(['@/components/home'],resolve)
- es提案的import()
component: () => import("@/components/HelloWorld")
- webpack的require,ensure()
component: r => require.ensure([], () => r(require('@/components/home')), 'demo')
vue项目优化?
1.代码包优化,屏蔽sourceMap
2.对路由组件进行懒加载
3.细分vuejs组件,数据变更时,由于组件代码比较庞大,vuejs的数据驱动视图更新比较慢,造成渲染比较慢
4.减少watch的数据,换vuex
事件及时销毁
使用公用的CDN资源,可以起到缓存作用,并减少打包体积
gzip压缩
1、什么是MVVM?
MVVM是一种设计思想。Model 层代表数据模型,也可以在Model中定义数据修改和操作的业务逻辑;View 代表UI 组件,它负责将数据模型转化成UI 展现出来,ViewModel 是一个同步View 和 Model的对象。
2、mvvm和mvc区别?
mvvm主要解决了mvc中大量的DOM 操作使页面渲染性能降低,加载速度变慢,影响用户体验。
3、vue生命周期的作用是什么?
它的生命周期中有多个事件钩子,让我们在控制整个Vue实例的过程时更容易形成好的逻辑。
4、完整的 vue-router 导航解析流程?
1.导航被触发;
2.在失活的组件里调用beforeRouteLeave守卫;
3.调用全局beforeEach守卫;
4.在复用组件里调用beforeRouteUpdate守卫;
5.调用路由配置里的beforeEnter守卫;
6.解析异步路由组件;
7.在被激活的组件里调用beforeRouteEnter守卫;
8.调用全局beforeResolve守卫;
9.导航被确认;
10..调用全局的afterEach钩子;
11.DOM更新;
12.用创建好的实例调用beforeRouteEnter守卫中传给next的回调函数。
7、vue如何优化首屏加载速度?
-
使用CDN资源,减小服务器带宽压力
-
路由懒加载
-
将一些静态js css放到其他地方(如OSS),减小服务器压力
-
按需加载三方资源,如iview,建议按需引入iview中的组件
-
使用nginx开启gzip减小网络传输的流量大小
-
若首屏为登录页,可以做成多入口,登录页单独分离为一个入口
-
使用uglifyjs-webpack-plugin插件代替webpack自带UglifyJsPlugin插件
14、生命周期钩子的一些使用方法?
-
beforecreate : 可以在这加个loading事件,在加载实例时触发
-
created : 初始化完成时的事件写在这里,如在这结束loading事件,异步请求也适宜在这里调用
-
mounted : 挂载元素,获取到DOM节点
-
updated : 如果对数据统一处理,在这里写上相应函数
-
activated: keep-alive组件激活时调用
-
beforeDestroy : 可以做一个确认停止事件的确认框
-
nextTick : 更新数据后立即操作dom
15、生命周期
beforeCreate
--在实例开始初始化时同步调用。此时数据观测、事件和watcher 都尚未初始化。
created
--在实例创建之后同步调用,已完成watch/event事件回调,挂载阶段还没开始,$el 属性目前不可见
beforeMount
--在挂载开始之前被调用:相关的render函数首次被调用
mounted
--在编译结束后调用。此时所有的指令已生效,因而数据的变化将触发DOM更新
beforeUpdate
—数据更新前调用,发生在虚拟DOM打补丁之前。这里适合在更新之前访问现有的DOM,比如手动移除已添加的事件监听器。
update
—数据更新后调用,由于数据更改导致的虚拟DOM重新渲染和打补丁,在这之后会调用该钩子
activated
--keep-alive 组件激活时调用。该钩子在服务器端渲染期间不被调用。。
deactivated
--keep- alive组件停用时调用
beforeDestroy
--实例销毁之前调用。在这一步,实例仍然完全可用。
destroyed
--Vue实例销毁后调用。调用后,Vue实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁
errorCaptured
--当捕获-一个来自子孙组件的错误时被调用。此钩子会收到三个参数:错误对象、发生错误的组件实例以及一一个包含错误来源信息的字符
5、vue中 key 值的作用?
diff算法:如果节点类型不同,直接干掉前面的节点,再创建并插入新的节点,不会再比较这个节点以后的子节点了。
如果节点类型相同,则会重新设置该节点的属性,从而实现节点的更新。
用v-for更新已渲染的元素列表时,默认采用旧地复用策略,会复用之前的元素,不加key的话,更新机制的进行diff的时候是会全部比较的,
如果有key的话,就会根据key值去判断某个是否修改,重新渲染这一项;所以这个key值对数据改变之后的diff更新比较有很大的性能提升
6、Vue 组件中 data 为什么必须是函数?
因为如果默认为data是对象的话,对象为引用类型,这样的话,所有复用的组件都是引用的同一个数据,但是如果是函数的话,每次函数都会先创建一个新的数据,从而使每个组件的数据独立
8、v-if 和 v-show 有什么区别?
v-show 高的初始渲染开销;v-if 有更高的切换开销(惰性的,为真才开始渲染条件块)
9、vue常用的修饰符?
.stop - 调用 event.stopPropagation()。
.prevent - 调用 event.preventDefault()。
.once - 只触发一次回调。
.{keyCode | keyAlias} - 只当事件是从特定键触发时才触发回调。
.native - 监听组件根元素的原生事件。
10、计算属性的缓存和方法调用的区别?
computed只有在相关依赖发生改变时才会重新求值,method每次都会执行;
思考:假设我们有一个性能开销比较大的计算属性 A,它需要遍历一个巨大的数组并做大量的计算。然后我们可能有其他的计算属性依赖于 A 。
如果没有缓存,我们将不可避免的多次执行 A 的 getter!如果你不希望有缓存,请用方法来替代。
思考:当你有一些数据需要随着其它数据变动而变动时,你很容易滥用 watch,更好的做法是使用计算属性而不是命令式的 watch 回调
10.1、computed和watch的区别
1.如果一个数据依赖于其他数据的简易计算处理的,那么使用computed比较合适。
2.如果需要在某个数据变化时做一些事情,使用watch来观察这个数据变化
11、为什么避免 v-if 和 v-for 用在一起?
当 Vue 处理指令时,v-for 比 v-if 具有更高的优先级,通过v-if 移动到容器元素,不会再重复遍历列表中的每个值。取而代之的是,我们只检查它一次,且不会在 v-if 为否的时候运算 v-for。
12、vue中双向数据绑定的原理是什么?
vue双向数据绑定的原理主要通过数据劫持Object.defineProperty和发布订阅模式实现的,通过Object.defineProperty监听数据发生变化然后通知订阅者(watcher),订阅者触发响应的回调
13、vue中的父子组件传值和兄弟组件传值都是如何实现的?
父向子传值,主要通过子组件的props,获取父组件绑定的数据
子向父传值,主要通过子组件利用$emit触发父组件上的事件
兄弟组件传值利用eventbus的方式,主要利用创建一个空的vm实例,作为中间者