前端框架:vue

338 阅读6分钟

1.组件传参

1. props emit
2.$children $parent
3. provide inject  provide: { foo: 'bar'},   inject: ['foo'],
4.ref refs
5.eventBus  const EventBus = new Vue()
6. vuex
7.localStorage /sessionStorage
8.$attrs $listeners  子组件( inheritAttrs: false, this.$attrs)

父子组件通信: props; $parent / $children; provide / inject ; ref ;  $attrs / $listeners
兄弟组件通信: eventBus ; vuex
跨级通信:  eventBus;Vuex;provide / inject 、$attrs / $listeners

2.vue的组件有两种:局部组件和全局组件

其中全局组件的调用方式有三中: 

a. Vue.component(component.name, component);

b.指令的方式,指令的组件中不需要定义props

import messageOriginal from '...'
let ConstructMessage = Vue.extend(messageOriginal)
const Instance= {
    install:(Vue)=>{
        Vue.directive('message',{
            bind:(el,binding)=>{
                let instanceMessage = new ConstructMessage({
                    data:{
                        info:el.getAttribute('message-info'),
                        fullscreen:!!binding.modifiers.fullscreen
                    }
                })
                Vue.nextTick(()=>{
                    el.appendChild(instanceMessage.$el)
                })
            }
        })
    }
}
Vue.use(Instance)

使用:
<div v-message.fullscreen="true" :messageInfo="hello"></div>
fullscreen是组件中定义的参数

c.vue的prototype的继承的方式

import messageOriginal  from '...'
let ConstructorMessage = Vue.extend(messageOriginal)

const Intance = (options)=>{
    let instanceMessage = new ConstrctorMessage({data:{...options}})
    instanceMessage.$mount()
    document.appendChild(instanceMessage.$el)
    return instanceMessage
}

Vue.prototype.$message = Instance 

3. v-model 和 .sync 的区别

 v-model 是一个语法糖 value /@input

<myComponent v-model='value'></myComponent>
// 编译后
<myComponent :value="value" @input="(e)=>value = e.target.value"></myComponent>


  .sync 

<myComponent :value.sync="value"></myComponent>
//编译后
<myComponent :value="value" @update="(val)=>value = val"

本质上都是一样的监听触发事件

区别:

v-model默认对应的是input、textarea。。。组件中不可以有多个

.sync 组件中可以有多个属性使用.sync修饰符

v-model更多的是操作结果,是双向数据绑定的结果

.sync 更多的是一种状态的改变..loading状态,菜单收起还是展开...

v-model 可以在model 属性中进行自定所以prop和event

 model: { prop: 'checked', event: 'change' },

4.使用div实现v-model

<div contenteditable="true" v-text="text" @input="inputHandler"></div>

export defalut{
    props:{
        value:[String,Number]
    },
    data(){
        return {
            text: this.value
        }
    },
    methods:{
        inputHandler(e){
            if(e.target.innerText === this.value) return
            this.text= e.tartet.innerText
            this.$emit('input',e.target.innerText)
        }
    }
}

//使用组件
<myInput v-model="value"></myInput>

5. data数据重新初始化

//初始化 data 中的 footData 数据
Object.assign(this.$data.formData, this.$options.data().formData);
//初始化全部的 data数据
Object.assign(this.$data, this.$options.data());

6.动态组件和异步组件

1.动态组件是使用 is来绑定组件

<component v-bind:is="currentTabComponent"></component>

<!-- 失活的组件将会被缓存!-->
<keep-alive>
  <component v-bind:is="currentTabComponent"></component>
</keep-alive>

可以使用keep-alive 将组件进行缓存保存组件中状态

2. 异步组件

我们全局注册组件时一般的方式:

import ExampleComponent from "..."
Vue.component('example-component', ExampleComponent)

1.通过返回回调函数实现异步组件

import ExampleComponent from "..."
Vue.component('example-component',function(resolve,reject){
    resolve(ExampleComponent)
})

Vue只有在这个组件需要被渲染的时候才会触发该工厂函数,且会把结果缓存起来供未来重渲染   (按需加载组件)

2. 使用webpack的code-spliting功能实现

Vue.component('example-conponent',function(resolve){
    // 这个特殊的 require 语法将会告诉webpack
    // 自动将构建的代码切割成多个包,这些包会通过Ajax请求加载
    require(['./components/exampleComponents.vue'],resolve)
})

3.结合 promise

// 这个动态导入会返回一个promise对象
Vue.component('example-component', ()=>import('./components/exampleComponents.vue'))

局部注册的时候也可以直接返回promise的函数

7.Vue directive

一个指令定义对象可以提供5个钩子函数(均为可选)

  • bind: 只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置
  • inserted:被绑定元素插入父节点时调用(仅保证父节点存在,但不一定已被插入文档中)
  • update: 所在组件的VNode更新时调用,但是可能发生在其子VNode更新之前。指令的值可能发生了改变,也可能没有,但是你可以通过比较更新前后的值来忽略不必要的模板更新
  • componentUpdated:指令所在组件的Vnode及其子VNode全部更新后调用
  • unbind: 只调用一次,指令与元素解绑时调用

钩子函数参数:

  • el:指令所绑定的元素,可以用来直接操作DOM

  • 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}

  • vnode:Vue编译生成的虚拟节点

  • oldVnode:上一个虚拟节点,仅在update和componentUpdated钩子中可用

    Vue.directive('my-directive',{
    bind:function(el,binding,vnode){},
    inserted:function(){},
    update:function(){},
    componentUpdated:function(){},
    unbind:function(){}
    })
    

8.组件中对props中的参数校验规则复杂,vue提供的无法满足请使用validator

props:{
    age:{
        required:true,
        validator:function(value){
            return value >=0 && value<=128
        }
    }
}

9.双向数据绑定

详情

基于数据劫持的双向数据绑定离不开Proxy与Object.defineProperty等方法对对象/对象属性的劫持,实现一个完整的双向数据绑定需要三个要点;

  1. 利用Proxy或Object.defineProperty生成的Observer针对对象/对象的属性进行劫持,在属性发生变化后通知订阅者
  2. 解析器Compile解析模板中Directive(指令),收集指令依赖的方法和数据,等待数据变化然后进行渲染
  3. Watcher属于Observer和Compile桥梁,它将收到的Observer产生的数据变化,并根据Compile提供的指令进行试图渲染,使得数据变化促使视图变化。

10.MVC和MVVM原理

MVVM和MVC是一种软件架构模式

vue是MVVM中一种在前端层面上的实现。

MVC模式原理,MVC即Model-View-Controller,模型,视图和控制器

Model

  • 程序需要操作的数据来源。通常是从数据库、网络请求的数据
  • 负责提供数据

View

  • 程序用来展示内容的界面,通常是UI组件
  • 负责展示数据

Controller

  • 程序用于处理Model数据业务逻辑并将结果输送给View的中间层
  • 负责处理业务逻辑

优点

  • 耦合性低:视图层和业务层分离
  • 重用性高:多个View能共享一个Model
  • 部署快:责任分离,各司其职,职责清晰
  • 可维护性高

缺点:

  • Controller角色比重太大,虽然担任控制器的职责,但在开发中做了很多view的操作,VC耦合性较高

MVVM原理

MVVM模式利用了一个工具数据绑定实现了其中的VM。将数据和View绑定在一起,这样一来,数据发生改变,View会即时更新。

Model

  • 负责提供实体模型,提供数据

View

  • UI组件,这一块只包含UI相关代码,不含有业务逻辑代码

ViewModel

  • 只负责业务逻辑
  • 将Model提供的实体数据和View中真正展示数据的UI组件通过数据绑定在一起
  • VM绑定在一起,Model一改变,View自动更新

优点

  • 双向绑定技术,Model变化,view和ViewModel自动更新
  • 模块之间职责更清晰
  • 控制器功能大都到了view上,大大减轻压力

缺点

  • 大项目中Model会很大,长期占有不能释放内存
  • view的重用性降低,MVVM中一个View绑定一个Model,不同模块的View都不同,重用困难

11.样式穿透

在网页检查页面元素的时候发现自己写的样式不生效,原因是因为中scoped的问题导致,所以我们需要用到样式穿透:

  • 外层类 >>> 想要修改的类名 {}

  • 外层类 /deep/ 想要修改的类名{}

  • ::v-deep 想要修改的类名 {}

12.Object.defineProperty()和Proxy

对于Object.defineProperty的缺陷

  1. vue实例创建后,再向其上添加属性,不能监听

当创建一个vue实例时,将遍历所有DOM对象,并为每个数据属性添加了get和set,get和set允许vue观察数据的更改并触发更新。但是,如果你在vue实例化后添加或删除一个属性,这个属性不会被vue处理,改变set和get

可以使用Vue.set(obj,key,value)的方式创建一个响应式属性

2.数组

vue重构了push,pop,shift,unshift,splice,sort,reverse的方法

Ⅰ. Object.defineProperty只能劫持对象的属性,而Proxy是直接代理对象

由于Object.defineProperty只能对属性进行劫持,需要遍历对象的每个属性,如果属性值也是对象,则需要深度遍历。而Proxy直接代理对象,不需要遍历操作

Ⅱ. Object.defineProperty对新增属性需要手动进行Observe

由于Object.defineProperty劫持的是对象的属性,所以新增属性时,需要重新遍历对象,对其新增属性在使用Object.defineProperty进行劫持。

也正是因为这个原因,使用Vue给Data中的数组或者对象新增属性时,需要使用vm.$set才能保证新增的属性也是响应式的

13. vue.$nextTick执行原理

由于js是单线程,js设计者把任务分为同步任务和异步任务,同步任务都在主线程上排队执行,前面任务没有执行完成,后面的任务会一直等待;异步任务则是挂在一个任务队列里,等待主线程所有任务执行完成后,通知任务队列可以把可执行的任务放到主线程执行。异步任务放到主线程执行完成后,又通知任务队列把下一个异步任务放到主线程中执行。这个过程一直持续,直到异步任务执行完成,这个持续重复的过程叫做event loop。

浏览器: 宏任务执行完成后执行浏览器渲染。微任务执行在宏任务之前。

nextTick使用mutationObserver渲染微任务,

nextTick执行顺序:宏任务->浏览器渲染->微任务(nextTick)->宏任务->浏览器渲染

链接

14. computed和watch的区别

计算属性computed和监听器watch都可以观察属性的变化从而做出响应,不同的是:

计算属性computed:

  1. 支持缓存,只有依赖的数据发生变化,才重新计算
  2. 不支持异步,当computed内有异步操作时无效,无法监听数据的变化
  3. computed属性值会默认走缓存,计算属性是基于它们响应式依赖进行缓存的,也就是基于data中声明过或者父组件传递的props中的数据计算得到的值
  4. 如果computed属性值是一个函数,那么默认走get方法;函数的返回值就是属性的属性值;在computed中,属性都有一个get和一个set方法

监听器watch:

  1. 不支持缓存,数据发生变化,直接会触发相应的操作

  2. watch支持异步

  3. 监听的函数接受两个参数,第一个参数是最新的值,第二个参数是输入之前的值

  4. 监听数据必须是data中声明过或者父组件传递过来的props中的数据,当数据放生变化时,触发其他操作

  5. immediate:组件加载立即触发回调函数执行

  6. deep:深度监听,为了发现对象内部值的变化,复杂类型的数据时使用注意:deep无法监听到数组的变动和新增,只有以响应式的方式触发才会被监听到。

      watch: {
        a: function (val, oldVal) {
          console.log('new: %s, old: %s', val, oldVal)
        },
        // 方法名
        b: 'someMethod',
        // 该回调会在任何被侦听的对象的 property 改变时被调用,不论其被嵌套多深
        c: {
          handler: function (val, oldVal) { /* ... */ },
          deep: true
        },
        // 该回调将会在侦听开始之后被立即调用
        d: {
          handler: 'someMethod',
          immediate: true
        },
        // 你可以传入回调数组,它们会被逐一调用
        e: [
          'handle1',
          function handle2 (val, oldVal) { /* ... */ },
          {
            handler: function handle3 (val, oldVal) { /* ... */ },
            /* ... */
          }
        ],
        // watch vm.e.f's value: {g: 5}
        'e.f': function (val, oldVal) { /* ... */ }
      }