vue面试题(二)

VUE核心概念

vuex其实就是一个重新封装的new Vue 对象,他的动态响应数据就是data属性,而commit这些方法,都只是回调

vuex的构成
在这里插入图片描述

  • state:提供一个响应式数据;
  • Getter:借助Vue的计算属性computed来实现缓存;
  • Mutation:更改state方法;
  • Action:触发mutation 方法;
  • Module:Vue.set 动态添加state 到响应式数据中;

Vuex的映射:
state(数据)、getters(计算属性)需要映射在computed实例中
mutations(同步操作放),actions(异步操作方法)等需要放在methods实例中
vuex运行机制

new vue =>init=>$mount=>render function =>vnode
init 对数据进行响应式化
render function 会被转化成 VNode 节点
    obj: 目标对象
    prop: 需要操作的目标对象的属性名
    descriptor: 描述符
Object.defineProperty(obj,prop,descrptor)

Object.defineProperty 用来把对象变成可观察的

function defineReactive(obj,key,val){
    Object.defineProperty(obj,key,{
        enumerable:true, //属性可枚举
        configurable:true, //属性可被修改或删除
        get:function reactiveGetter(){
            return val;
        },
        set:function reactiveSetter(newVal){
            if(newVal===val) return ;
            cb(newVal)
        }
    })
}

在上面再封装一层 observer 。这个函数传入一个
value(需要「响应式」化的对象),通过遍历所有属性的方式对该对象的每一个属性都通过 defineReactive 处理。 (注:实际上
observer 会进行递归调用,为了便于理解去掉了递归的过程)

function observer(obj){
    if(!obj||(typeof obj!=='object')){
        return
    }
    Object.keys(obj).forEach(key=>{
        defineReactive(obj,key,obj[key])
    })
}
class Vue{
    constructor(options){
        this._data=options.data;
        observer(this._data)
    }
}

订阅者dep (Dependency) 主要作用是存放watcher观察者对象

class Dep{
    constructor(){
        //用来存放watcher对象的数组
        this.subs=[]
    }
    //在subs中添加一个watch对象
    addSub(sub){
        this.subs.push(sub)
    }
    //通知所有watcher对象视图更新
    notify(){
        this.subs.forEach(sub=>{
            sub.update()
        })
    }
}

观察者watcher

class watcher{
    constructor(){
        /*在new一个watch对象时将该对象赋值给Dep.target,在get中用到*/
        Dep.target=this;
    }
    update(){
        console.log('视图更新啦)
    }
}

修改一下 defineReactive 以及 Vue 的构造函数,来完成依赖收集。

function defineReactive(obj,key,val){
    //一个Dep类对象
    const dep=new Dep()

    Object.defineProperty(obj,key,{
        enumerable:true,
        configurable:true,
        get:function reactiveGetter(){
            dep.addSub(Dep.target)
            return val;
        },
        set:function reactiveSetter(newVal){
            if(newVal===val) return;
            dep.notify()
        }
    })
}
class Vue{
    constructor(options){
        this._data=options.data;
        observer(this._data)
        /*新建一个watch观察者对象,这时候Dep.target会指向这个watcher对象*/
        new watcher()
        //模拟render过程,触发test属性的get函数
        console.log('render~',this._data.test)

    }
}

数据状态更新时的差异diff及patch机制

const nodeOps={
    setTextContent(text){
        if(platform==='weex'){
            node.parentNode.setAttr('value',text)
        }else if(platform==='web'){
            node.setTextContent=text
        }
    },
    parentNode(){

    },
    removeChild(){

    },
    nextSibling(){

    },
    insertBefore(){

    }

}

patch的diff算法,是通过同层的树节点进行比较,而非对树进行逐层遍历搜索,时间复杂度O(n)

patch的简单过程代码
function patch(oldVnode,vnode,parentElm){
    if(!oldVnode){
        addVnodes(parentElm,null,vnode,0,vnode.length-1)
    }else if(!vnode){
        removeVnodes(parentElm,oldVnode,0,oldVnode.length-1)
    }else{
        if(sameVnode(oldVnode,vnode)){
            patchVnode(oldVNode,vnode)
        }else{
            removeVnodes(parentElm,oldVNode,0,oldVNode.length-1)
            addVnodes(parentElm,null,vnode,0,vnode.length-1)
        }
    }
}

sameVnode的判断

function sameVnode(){
    return (
        a.key===b.key&&
        a.tag===b.tag&&
        a.isComment===b.isComment&&
        (!!a.data)===(!!b.data) &&
        sameInputType(a,b)
    )
}
function sameInputType(a,b){
    if(a.tag!=='input') return true
    let i
    const typeA=(i=a.data)&&(i=i.attrs)&&i.type
    const typeB=(i=b.data)&&(i=i.attrs)&&i.type
    return typeA===typeB
}
######对比相同的节点有哪些变化
function patchVnode(oldVnode,vnode){
    if(oldVNode===vnode){
        return
    }
    if(vnode.isStatic&&oldVnode.isStatic&&vnode.key===oldnode.key){
        vnode.elm=oldVnode.elm;
        vnode.componentInstance=oldVnode.componentInstance
        return
    }
    const elm=vnode.elm=oldVNode.elm
    const oldCh=oldVnode.children;
    const ch=vnode.children

    if(vnode.text){
        nodeOps.setTextContent(elm,vnode.text)
    }else 
    if(oldCh&&ch&&(oldCh!==ch)){
        updateChildren(elm,oldCh,ch)
    }else if(ch){
        if(oldVnode.text) nodeOps.setTextContent(elm,'')
        addVnodes(elm,null,ch,0,ch.length-1)
    }else if(oldCh){
        removeVnodes(elm,oldCh,0,oldCh.length-1)
    }else if(oldVNode.text){
        nodeOps.setTextContent(elm,'')
    }
}
//updateChildren
function updateChildren(parentElm,oldCh,newCh){
    let oldStartIdx=0;
    let newStartIdx=0;
    let oldEndIdx=oldCh.length-1
    let oldStartVnode=oldCh[0]
    let oldEndVnode=oldCh[oldEndIdx]
    let newEndIdx=newCh.length-1
    let newStartVnode=newCh[0]
    let newEndVnode=newCh[newEndIdx]
    let oldKeyToIdx,idxInOld,elmToMove,refElm;

    while(oldStartIdx<=oldEndIdx&&newStartIdx<=newEndIdx){
        if(!oldStartVnode){
            oldStartVnode=oldCh[++oldStartIdx]
        }else if(!oldEndVnode){
            oldEndVnode=oldCh[--oldEndIdx]
        }else if ( sameVnode(oldStartVnode,newStartVnode) ){
            patchVnode(oldStartVnode,newStartVnode)
            oldStartVnode=oldCh[++oldStartIdx]
            newStartVnode=newCh[++newStartIdx]
        }else if(sameVnode(oldEndVnode,newEndVnode)){
            patchVnode(oldEndVnode,newEndVnode)
            oldEndVnode=oldCh[--oldEndIdx]
            newEndVnode=newCh[--newEndIdx]
        }else if(sameVnode(oldStartVnode,newEndVnode)){
            patchVnode(oldStartVnode,newEndVnode)
            nodeOps.insertBefore(parentElm,oldStartVnode.elm,nodeOps.nextSibling(oldEndVnode.oldStartVnode=oldCh[++oldStartIdx]))
            newEndVnode=newCh[--newEndIdx]
        }else if(sameVnode(oldEndVnode,newStartVnode)){
            patchVnode(oldEndVnode,newStartVnode)
            nodeOps.insertBefore(parentElm,oldEndVnode.elm,oldStartVnode.elm)
            oldEndVnode=oldCh[--oldEndIdx]
            newStartVnode=newCh[++newStartIdx]
        }else{
            let elmToMove=oldCh[idxInOld]
            if(!oldKeyToIdx) oldKeyToIdx=createKeyToOldIdx(oldCh,oldStartIdx,oldEndIdx)
            idxInOld=newStartVnode.key?oldKeyToIdx[newStartVnode.key]:null
            if(!idexInOld){
                createElm(newStartVnode,parentElm);
                newStartVnode=newCh[++newStartIdx]
            }else{
                elmToMove=oldCh[idxInOld]
                if(sameVnode(elmToMove,newStartVnode)){
                    patchVnode(elmToMove,newStartVnode)
                    oldCh[idxInOld]=undefined
                    nodeOps.insertBefore(parentElm,newStartVnode.elm,oldStartVnode.elm)
                    newStartVnode=newCh[++newStartIdx]
                }else{
                    createElm(newStartVnode,parentElm)
                    newStartVnode=newCh[++newStartIdx]
                }
            }
        }
    }
    if(oldStartIdx>oldEndIdx){
        refElm=(newCh[newEndIdx+1])?newCh[newEndIdx+1].elm:null;
        addVnodes(parentElm,refElm,newCh,newStartIdx,newEndIdx)
    }else if(newStartIdx>newEndIdx){
        removeVnodes(parentElm,oldCh,oldStartIdx,oldEndIdx)
    }
}
axios封装

配置axios
首先,创建一个Service.js,这里面存放的时axios的配置以及拦截器等,最后导出一个axios对象。我平常elementUI用的比较多,这里你也可以使用自己的UI库。

import axios from 'axios'
import { Message, Loading } from 'element-ui'
const ConfigBaseURL = 'https://localhost:3000/' //默认路径,这里也可以使用env来判断环境
let loadingInstance = null //这里是loading
//使用create方法创建axios实例
export const Service = axios.create({
  timeout: 7000, // 请求超时时间
  baseURL: ConfigBaseURL,
  method: 'post',
  headers: {
    'Content-Type': 'application/json;charset=UTF-8'
  }
})
// 添加请求拦截器
Service.interceptors.request.use(config => {
  loadingInstance = Loading.service({
    lock: true,
    text: 'loading...'
  })
  return config
})
// 添加响应拦截器
Service.interceptors.response.use(response => {
  loadingInstance.close()
  // console.log(response)
  return response.data
}, error => {
  console.log('TCL: error', error)
  const msg = error.Message !== undefined ? error.Message : ''
  Message({
    message: '网络错误' + msg,
    type: 'error',
    duration: 3 * 1000
  })
  loadingInstance.close()
  return Promise.reject(error)
})

封装请求
这里我再创建一个request.js,这里面放的是具体请求。

export function getConfigsByProductId(productId) {
  return Service({
    url: '/manager/getConfigsByProductId',
    params: { productId: productId }
  })
}
export function getAllAndroidPlugins() {
  return Service({
    url: '/manager/getAndroidPlugin ',
    method: 'get'
  })
}
export function addNewAndroidPlugin(data) {
  return Service({
    url: '/manager/addAndroidPlguin',
    data: JSON.stringify(data)
  })
}

在vue组件中使用

import {getAllAndroidPlugins,getConfigsByProductId,addNewAndroidPlugin} from '@/api/request.js'

getAllAndroidPlugins()
.then(res=>{
})

全局使用 在main.js中

import {Service} from '@/api/Service.js'
Vue.prototype.$axios=Service
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值