美文网首页
2019-03-18

2019-03-18

作者: 伽蓝star | 来源:发表于2019-03-18 14:00 被阅读0次
    1. 盒模型
      页面渲染时,dom 元素所采用的 布局模型。可通过box-sizing进行设置。根据计算宽高的区域可分为: border-box
    2. BFC
      块级格式化上下文,是一个独立的渲染区域,让处于 BFC 内部的元素与外部的元素相互隔离,使内外元素的定位不会相互影响。
    3. 居中布局

    水平居中
    行内元素: text-align: center
    块级元素: margin: 0 auto
    absolute + transform
    flex + justify-content: center
    垂直居中
    line-height: height
    absolute + transform
    flex + align-items: center
    table
    水平垂直居中
    absolute + transform
    flex + justify-content + align-items

    1. 选择器优先级

    !important > 行内样式 > #id > .class > tag > * > 继承 > 默认
    选择器 从右往左 解析

    1. 闭包

    闭包属于一种特殊的作用域,称为 静态作用域。它的定义可以理解为: 父函数被销毁 的情况下,返回出的子函数的[[scope]]中仍然保留着父级的单变量对象和作用域链,因此可以继续访问到父级的变量对象,这样的函数称为闭包。

    1. 对象的拷贝

    浅拷贝: 以赋值的形式拷贝引用对象,仍指向同一个地址,修改时原对象也会受到影响
    Object.assign
    展开运算符(...)
    深拷贝: 完全拷贝一个新对象,修改时原对象不再受到任何影响
    JSON.parse(JSON.stringify(obj)): 性能最快
    具有循环引用的对象时,报错
    当值为函数、undefined、或symbol时,无法拷贝
    递归进行逐一赋值

    1. new运算符的执行过程

    新生成一个对象
    链接到原型: obj.proto = Con.prototype
    绑定this: apply
    返回新对象(如果构造函数有自己 retrun 时,则返回该值)

    1. instanceof原理

    能在实例的 原型对象链 中找到该构造函数的prototype属性所指向的 原型对象,就返回true。即:

    1. 继承

    在 JS 中,继承通常指的便是 原型链继承,也就是通过指定原型,并可以通过原型链继承原型上的属性或者方法。

    1. 模块化

    模块化开发在现代开发中已是必不可少的一部分,它大大提高了项目的可维护、可拓展和可协作性。通常,我们 在浏览器中使用 ES6 的模块化支持,在 Node 中使用 commonjs 的模块化支持。
    分类:
    es6: import / export
    commonjs: require / module.exports / exports
    amd: require / defined
    require与import的区别
    require支持 动态导入,import不支持,正在提案 (babel 下可支持)
    require是 同步 导入,import属于 异步 导入
    require是 值拷贝,导出值变化不会影响导入值;import指向 内存地址,导入值会随导出值而变化

    1. 防抖与节流

    防抖与节流函数是一种最常用的 高频触发优化方式,能对性能有较大的帮助。

    防抖 (debounce): 将多次高频操作优化为只在最后一次执行,通常使用的场景是:用户输入,只需再输入完成后做一次输入校验即可。

    节流(throttle): 每隔一段时间后执行一次,也就是降低频率,将高频操作优化成低频操作,通常使用场景: 滚动条事件 或者 resize 事件,通常每隔 100~500 ms执行一次即可。

    1. ES6/ES7

    由于 Babel 的强大和普及,现在 ES6/ES7 基本上已经是现代化开发的必备了。通过新的语法糖,能让代码整体更为简洁和易读。

    声明
    let / const: 块级作用域、不存在变量提升、暂时性死区、不允许重复声明
    const: 声明常量,无法修改
    解构赋值
    class / extend: 类声明与继承
    Set / Map: 新的数据结构
    异步解决方案:
    Promise的使用与实现
    generator:
    yield: 暂停代码
    next(): 继续执行代码

    1. babel编译原理

    babylon 将 ES6/ES7 代码解析成 AST
    babel-traverse 对 AST 进行遍历转译,得到新的 AST
    新 AST 通过 babel-generator 转换成 ES5

    1. 数组(array)

    map: 遍历数组,返回回调返回值组成的新数组
    forEach: 无法break,可以用try/catch中throw new Error来停止
    filter: 过滤
    some: 有一项返回true,则整体为true
    every: 有一项返回false,则整体为false
    join: 通过指定连接符生成字符串
    push / pop: 末尾推入和弹出,改变原数组, 返回推入/弹出项
    unshift / shift: 头部推入和弹出,改变原数组,返回操作项
    sort(fn) / reverse: 排序与反转,改变原数组
    concat: 连接数组,不影响原数组, 浅拷贝
    slice(start, end): 返回截断后的新数组,不改变原数组
    splice(start, number, value...): 返回删除元素组成的数组,value 为插入项,改变原数组
    indexOf / lastIndexOf(value, fromIndex): 查找数组项,返回对应的下标
    reduce / reduceRight(fn(prev, cur), defaultPrev): 两两执行,prev 为上次化简函数的return值,cur 为当前值(从第二项开始)
    数组乱序:

    1. nextTick

    在下次dom更新循环结束之后执行延迟回调,可用于获取更新后的dom状态

    新版本中默认是mincrotasks, v-on中会使用macrotasks
    macrotasks任务的实现:
    setImmediate / MessageChannel / setTimeout

    1. 生命周期

    init
    initLifecycle/Event,往vm上挂载各种属性
    callHook: beforeCreated: 实例刚创建
    initInjection/initState: 初始化注入和 data 响应性
    created: 创建完成,属性已经绑定, 但还未生成真实dom
    进行元素的挂载: el / vm.mount()
    是否有template: 解析成render function*.vue文件: vue-loader会将<template>编译成render function
    beforeMount: 模板编译/挂载之前
    执行render function,生成真实的dom,并替换到dom tree中
    mounted: 组件已挂载
    update:
    执行diff算法,比对改变是否需要触发UI更新
    flushScheduleQueuewatcher.before: 触发beforeUpdate钩子 - watcher.run(): 执行watcher中的 notify,通知所有依赖项更新UI
    触发updated钩子: 组件已更新
    actived / deactivated(keep-alive): 不销毁,缓存,组件激活与失活
    destroy:
    beforeDestroy: 销毁开始
    销毁自身且递归销毁子组件以及事件监听
    remove(): 删除节点
    watcher.teardown(): 清空依赖
    vm.$off(): 解绑监听
    destroyed: 完成后触发钩子

    
    new Vue({})
    // 初始化Vue实例
    function _init() {
         // 挂载属性
     initLifeCycle(vm) 
     // 初始化事件系统,钩子函数等
     initEvent(vm) 
     // 编译slot、vnode
     initRender(vm) 
     // 触发钩子
     callHook(vm, 'beforeCreate')
     // 添加inject功能
     initInjection(vm)
     // 完成数据响应性 props/data/watch/computed/methods
     initState(vm)
     // 添加 provide 功能
     initProvide(vm)
     // 触发钩子
     callHook(vm, 'created')
            
         // 挂载节点
     if (vm.$options.el) {
     vm.$mount(vm.$options.el)
     }
    }
    // 挂载节点实现
    function mountComponent(vm) {
         // 获取 render function
     if (!this.options.render) {
     // template to render
     // Vue.compile = compileToFunctions
     let { render } = compileToFunctions() 
     this.options.render = render
     }
     // 触发钩子
     callHook('beforeMounte')
     // 初始化观察者
     // render 渲染 vdom, 
     vdom = vm.render()
     // update: 根据 diff 出的 patchs 挂载成真实的 dom 
     vm._update(vdom)
     // 触发钩子 
     callHook(vm, 'mounted')
    }
    // 更新节点实现
    funtion queueWatcher(watcher) {
        nextTick(flushScheduleQueue)
    }
    // 清空队列
    function flushScheduleQueue() {
         // 遍历队列中所有修改
     for(){
         // beforeUpdate
     watcher.before()
     
     // 依赖局部更新节点
     watcher.update() 
     callHook('updated')
     }
    }
    // 销毁实例实现
    Vue.prototype.$destory = function() {
         // 触发钩子
     callHook(vm, 'beforeDestory')
     // 自身及子节点
     remove() 
     // 删除依赖
     watcher.teardown() 
     // 删除监听
     vm.$off() 
     // 触发钩子
     callHook(vm, 'destoryed')
    }
    
    1. 数据响应(数据劫持)

    看完生命周期后,里面的watcher等内容其实是数据响应中的一部分。数据响应的实现由两部分构成: 观察者( watcher ) 和 依赖收集器( Dep ),其核心是 defineProperty这个方法,它可以 重写属性的 get 与 set 方法,从而完成监听数据的改变。

    Observe (观察者)观察 props 与 state
    遍历 props 与 state,对每个属性创建独立的监听器( watcher )
    使用 defineProperty 重写每个属性的 get/set(defineReactive)
    get: 收集依赖
    Dep.depend()watcher.addDep()
    set: 派发更新
    Dep.notify()
    watcher.update()
    queenWatcher()
    nextTick
    flushScheduleQueue
    watcher.run()
    updateComponent()

    大家可以先看下面的数据相应的代码实现后,理解后就比较容易看懂上面的简单脉络了。
    
    let data = {a: 1}
    // 数据响应性
    observe(data)
    // 初始化观察者
    new Watcher(data, 'name', updateComponent)
    data.a = 2
    // 简单表示用于数据更新后的操作
    function updateComponent() {
     vm._update() // patchs
    }
    // 监视对象
    function observe(obj) {
         // 遍历对象,使用 get/set 重新定义对象的每个属性值
     Object.keys(obj).map(key => {
     defineReactive(obj, key, obj[key])
     })
    }
    function defineReactive(obj, k, v) {
     // 递归子属性
     if (type(v) == 'object') observe(v)
     
     // 新建依赖收集器
     let dep = new Dep()
     // 定义get/set
     Object.defineProperty(obj, k, {
     enumerable: true,
     configurable: true,
     get: function reactiveGetter() {
         // 当有获取该属性时,证明依赖于该对象,因此被添加进收集器中
     if (Dep.target) {
     dep.addSub(Dep.target)
     }
     return v
     },
     // 重新设置值时,触发收集器的通知机制
     set: function reactiveSetter(nV) {
     v = nV
     dep.nofify()
     },
     })
    }
    // 依赖收集器
    class Dep {
     constructor() {
     this.subs = []
     }
     addSub(sub) {
     this.subs.push(sub)
     }
     notify() {
     this.subs.map(sub => {
     sub.update()
     })
     }
    }
    Dep.target = null
    // 观察者
    class Watcher {
     constructor(obj, key, cb) {
     Dep.target = this
     this.cb = cb
     this.obj = obj
     this.key = key
     this.value = obj[key]
     Dep.target = null
     }
     addDep(Dep) {
     Dep.addSub(this)
     }
     update() {
     this.value = this.obj[this.key]
     this.cb(this.value)
     }
     before() {
     callHook('beforeUpdate')
     }
    }
    
    
    1. virtual dom 原理实现

    创建 dom 树
    树的diff,同层对比,输出patchs(listDiff/diffChildren/diffProps)
    没有新的节点,返回
    新的节点tagName与key不变, 对比props,继续递归遍历子树
    对比属性(对比新旧属性列表):
    旧属性是否存在与新属性列表中
    都存在的是否有变化
    是否出现旧列表中没有的新属性
    tagName和key值变化了,则直接替换成新节点
    渲染差异
    遍历patchs, 把需要更改的节点取出来
    局部更新dom

    // diff算法的实现梳理
    function diff(oldTree, newTree) {
         // 差异收集
     let pathchs = {}
     dfs(oldTree, newTree, 0, pathchs)
     return pathchs
    }
    function dfs(oldNode, newNode, index, pathchs) {
     let curPathchs = []
     if (newNode) {
     // 当新旧节点的 tagName 和 key 值完全一致时
     if (oldNode.tagName === newNode.tagName && oldNode.key === newNode.key) {
         // 继续比对属性差异
     let props = diffProps(oldNode.props, newNode.props)
     curPathchs.push({ type: 'changeProps', props })
     // 递归进入下一层级的比较
     diffChildrens(oldNode.children, newNode.children, index, pathchs)
     } else {
         // 当 tagName/key 修改了后,表示已经是全新节点,则没必要继续比了
     curPathchs.push({ type: 'replaceNode', node: newNode })
     }
     }
         // 构建出整颗差异树
     if (curPathchs.length) {
            if(pathchs[index]){
                pathchs[index] = pathchs[index].concat(curPathchs)
            } else {
                pathchs[index] = curPathchs
            }
     }
    }
    // 属性对比实现
    function diffProps(oldProps, newProps) {
     let propsPathchs = []
     // 遍历新旧属性列表
     // 查找删除项、修改项、查找新增项
     forin(olaProps, (k, v) => {
     if (!newProps.hasOwnProperty(k)) {
     propsPathchs.push({ type: 'remove', prop: k })
     } else {
     if (v !== newProps[k]) {
     propsPathchs.push({ type: 'change', prop: k , value: newProps[k] })
     }
     }
     })
     forin(newProps, (k, v) => {
     if (!oldProps.hasOwnProperty(k)) {
     propsPathchs.push({ type: 'add', prop: k, value: v })
     }
     })
     return propsPathchs
    }
    // 对比子级差异
    function diffChildrens(oldChild, newChild, index, pathchs) {
            // 标记子级的删除/新增/移动
     let { change, list } = diffList(oldChild, newChild, index, pathchs)
     if (change.length) {
     if (pathchs[index]) {
     pathchs[index] = pathchs[index].concat(change)
     } else {
     pathchs[index] = change
     }
     }
         // 根据 key 获取原本匹配的节点,然后递归从头开始对比
     oldChild.map((item, i) => {
     let keyIndex = list.indexOf(item.key)
     if (keyIndex) {
     let node = newChild[keyIndex]
     // 进一步递归对比
     dfs(item, node, index, pathchs)
     }
     })
    }
    // 1、列表对比,根据 key查找对应的匹配项
    // 2、对比出新旧列表的新增/删除/移动
    function diffList(oldList, newList, index, pathchs) {
     let change = []
     let list = []
     const newKeys = getKey(newList)
     oldList.map(v => {
     if (newKeys.indexOf(v.key) > -1) {
     list.push(v.key)
     } else {
     list.push(null)
     }
     })
     // 很直白的标记删除
     for (let i = list.length - 1; i>= 0; i--) {
     if (!list[i]) {
     list.splice(i, 1)
     change.push({ type: 'remove', index: i })
     }
     }
     // 标记新增以及移动
     newList.map((item, i) => {
     const key = item.key
     const index = list.indexOf(key)
     if (index === -1 || key == null) {
     // 新增
     change.push({ type: 'add', node: item, index: i })
     list.splice(i, 0, key)
     } else {
     // 移动
     if (index !== i) {
     change.push({
     type: 'move',
     form: index,
     to: i,
     })
     move(list, index, i)
     }
     }
     })
     return { change, list }
    }
    
    1. Proxy 相比于 defineProperty 的优势

    数组变化也能监听到
    不需要深度遍历监听
    let data = { a: 1 }
    let reactiveData = new Proxy(data, {
    get: function(target, name){
    // ...
    },
    // ...
    })

    1. vue-router

    modehash
    history
    跳转
    this.$router.push()
    <router-link to=""></router-link>
    占位
    <router-view></router-view>

    1. vuex

    state: 状态中心
    mutations: 更改状态
    actions: 异步更改状态
    getters: 获取状态
    modules: 将state分成多个modules,便于管理

    相关文章

      网友评论

          本文标题:2019-03-18

          本文链接:https://www.haomeiwen.com/subject/pjvvmqtx.html