vue框架总结

75 阅读13分钟
  • .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
1234
<!-- 方法处理器 -->
<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)、routeroute、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 一个对象,持有注册过 ref attribute 的所有 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-ifv-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.jsimport 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如何优化首屏加载速度?

  1. 使用CDN资源,减小服务器带宽压力

  2. 路由懒加载

  3. 将一些静态js css放到其他地方(如OSS),减小服务器压力

  4. 按需加载三方资源,如iview,建议按需引入iview中的组件

  5. 使用nginx开启gzip减小网络传输的流量大小

  6. 若首屏为登录页,可以做成多入口,登录页单独分离为一个入口

  7. 使用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实例,作为中间者