2019-03-18

  1. 盒模型
    頁面渲染時(shí),dom 元素所采用的 布局模型??赏ㄟ^box-sizing進(jìn)行設(shè)置。根據(jù)計(jì)算寬高的區(qū)域可分為: border-box
  2. BFC
    塊級(jí)格式化上下文,是一個(gè)獨(dú)立的渲染區(qū)域,讓處于 BFC 內(nèi)部的元素與外部的元素相互隔離,使內(nèi)外元素的定位不會(huì)相互影響。
  3. 居中布局

水平居中
行內(nèi)元素: text-align: center
塊級(jí)元素: 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. 選擇器優(yōu)先級(jí)

!important > 行內(nèi)樣式 > #id > .class > tag > * > 繼承 > 默認(rèn)
選擇器 從右往左 解析

  1. 閉包

閉包屬于一種特殊的作用域,稱為 靜態(tài)作用域。它的定義可以理解為: 父函數(shù)被銷毀 的情況下,返回出的子函數(shù)的[[scope]]中仍然保留著父級(jí)的單變量對(duì)象和作用域鏈,因此可以繼續(xù)訪問到父級(jí)的變量對(duì)象,這樣的函數(shù)稱為閉包。

  1. 對(duì)象的拷貝

淺拷貝: 以賦值的形式拷貝引用對(duì)象,仍指向同一個(gè)地址,修改時(shí)原對(duì)象也會(huì)受到影響
Object.assign
展開運(yùn)算符(...)
深拷貝: 完全拷貝一個(gè)新對(duì)象,修改時(shí)原對(duì)象不再受到任何影響
JSON.parse(JSON.stringify(obj)): 性能最快
具有循環(huán)引用的對(duì)象時(shí),報(bào)錯(cuò)
當(dāng)值為函數(shù)、undefined、或symbol時(shí),無法拷貝
遞歸進(jìn)行逐一賦值

  1. new運(yùn)算符的執(zhí)行過程

新生成一個(gè)對(duì)象
鏈接到原型: obj.proto = Con.prototype
綁定this: apply
返回新對(duì)象(如果構(gòu)造函數(shù)有自己 retrun 時(shí),則返回該值)

  1. instanceof原理

能在實(shí)例的 原型對(duì)象鏈 中找到該構(gòu)造函數(shù)的prototype屬性所指向的 原型對(duì)象,就返回true。即:

  1. 繼承

在 JS 中,繼承通常指的便是 原型鏈繼承,也就是通過指定原型,并可以通過原型鏈繼承原型上的屬性或者方法。

  1. 模塊化

模塊化開發(fā)在現(xiàn)代開發(fā)中已是必不可少的一部分,它大大提高了項(xiàng)目的可維護(hù)、可拓展和可協(xié)作性。通常,我們 在瀏覽器中使用 ES6 的模塊化支持,在 Node 中使用 commonjs 的模塊化支持。
分類:
es6: import / export
commonjs: require / module.exports / exports
amd: require / defined
require與import的區(qū)別
require支持 動(dòng)態(tài)導(dǎo)入,import不支持,正在提案 (babel 下可支持)
require是 同步 導(dǎo)入,import屬于 異步 導(dǎo)入
require是 值拷貝,導(dǎo)出值變化不會(huì)影響導(dǎo)入值;import指向 內(nèi)存地址,導(dǎo)入值會(huì)隨導(dǎo)出值而變化

  1. 防抖與節(jié)流

防抖與節(jié)流函數(shù)是一種最常用的 高頻觸發(fā)優(yōu)化方式,能對(duì)性能有較大的幫助。

防抖 (debounce): 將多次高頻操作優(yōu)化為只在最后一次執(zhí)行,通常使用的場(chǎng)景是:用戶輸入,只需再輸入完成后做一次輸入校驗(yàn)即可。

節(jié)流(throttle): 每隔一段時(shí)間后執(zhí)行一次,也就是降低頻率,將高頻操作優(yōu)化成低頻操作,通常使用場(chǎng)景: 滾動(dòng)條事件 或者 resize 事件,通常每隔 100~500 ms執(zhí)行一次即可。

  1. ES6/ES7

由于 Babel 的強(qiáng)大和普及,現(xiàn)在 ES6/ES7 基本上已經(jīng)是現(xiàn)代化開發(fā)的必備了。通過新的語法糖,能讓代碼整體更為簡潔和易讀。

聲明
let / const: 塊級(jí)作用域、不存在變量提升、暫時(shí)性死區(qū)、不允許重復(fù)聲明
const: 聲明常量,無法修改
解構(gòu)賦值
class / extend: 類聲明與繼承
Set / Map: 新的數(shù)據(jù)結(jié)構(gòu)
異步解決方案:
Promise的使用與實(shí)現(xiàn)
generator:
yield: 暫停代碼
next(): 繼續(xù)執(zhí)行代碼

  1. babel編譯原理

babylon 將 ES6/ES7 代碼解析成 AST
babel-traverse 對(duì) AST 進(jìn)行遍歷轉(zhuǎn)譯,得到新的 AST
新 AST 通過 babel-generator 轉(zhuǎn)換成 ES5

  1. 數(shù)組(array)

map: 遍歷數(shù)組,返回回調(diào)返回值組成的新數(shù)組
forEach: 無法break,可以用try/catch中throw new Error來停止
filter: 過濾
some: 有一項(xiàng)返回true,則整體為true
every: 有一項(xiàng)返回false,則整體為false
join: 通過指定連接符生成字符串
push / pop: 末尾推入和彈出,改變?cè)瓟?shù)組, 返回推入/彈出項(xiàng)
unshift / shift: 頭部推入和彈出,改變?cè)瓟?shù)組,返回操作項(xiàng)
sort(fn) / reverse: 排序與反轉(zhuǎn),改變?cè)瓟?shù)組
concat: 連接數(shù)組,不影響原數(shù)組, 淺拷貝
slice(start, end): 返回截?cái)嗪蟮男聰?shù)組,不改變?cè)瓟?shù)組
splice(start, number, value...): 返回刪除元素組成的數(shù)組,value 為插入項(xiàng),改變?cè)瓟?shù)組
indexOf / lastIndexOf(value, fromIndex): 查找數(shù)組項(xiàng),返回對(duì)應(yīng)的下標(biāo)
reduce / reduceRight(fn(prev, cur), defaultPrev): 兩兩執(zhí)行,prev 為上次化簡函數(shù)的return值,cur 為當(dāng)前值(從第二項(xiàng)開始)
數(shù)組亂序:

  1. nextTick

在下次dom更新循環(huán)結(jié)束之后執(zhí)行延遲回調(diào),可用于獲取更新后的dom狀態(tài)

新版本中默認(rèn)是mincrotasks, v-on中會(huì)使用macrotasks
macrotasks任務(wù)的實(shí)現(xiàn):
setImmediate / MessageChannel / setTimeout

  1. 生命周期

init
initLifecycle/Event,往vm上掛載各種屬性
callHook: beforeCreated: 實(shí)例剛創(chuàng)建
initInjection/initState: 初始化注入和 data 響應(yīng)性
created: 創(chuàng)建完成,屬性已經(jīng)綁定, 但還未生成真實(shí)dom
進(jìn)行元素的掛載: el / vm.mount()
是否有template: 解析成render function*.vue文件: vue-loader會(huì)將<template>編譯成render function
beforeMount: 模板編譯/掛載之前
執(zhí)行render function,生成真實(shí)的dom,并替換到dom tree中
mounted: 組件已掛載
update:
執(zhí)行diff算法,比對(duì)改變是否需要觸發(fā)UI更新
flushScheduleQueuewatcher.before: 觸發(fā)beforeUpdate鉤子 - watcher.run(): 執(zhí)行watcher中的 notify,通知所有依賴項(xiàng)更新UI
觸發(fā)updated鉤子: 組件已更新
actived / deactivated(keep-alive): 不銷毀,緩存,組件激活與失活
destroy:
beforeDestroy: 銷毀開始
銷毀自身且遞歸銷毀子組件以及事件監(jiān)聽
remove(): 刪除節(jié)點(diǎn)
watcher.teardown(): 清空依賴
vm.$off(): 解綁監(jiān)聽
destroyed: 完成后觸發(fā)鉤子


new Vue({})
// 初始化Vue實(shí)例
function _init() {
     // 掛載屬性
 initLifeCycle(vm) 
 // 初始化事件系統(tǒng),鉤子函數(shù)等
 initEvent(vm) 
 // 編譯slot、vnode
 initRender(vm) 
 // 觸發(fā)鉤子
 callHook(vm, 'beforeCreate')
 // 添加inject功能
 initInjection(vm)
 // 完成數(shù)據(jù)響應(yīng)性 props/data/watch/computed/methods
 initState(vm)
 // 添加 provide 功能
 initProvide(vm)
 // 觸發(fā)鉤子
 callHook(vm, 'created')
        
     // 掛載節(jié)點(diǎn)
 if (vm.$options.el) {
 vm.$mount(vm.$options.el)
 }
}
// 掛載節(jié)點(diǎn)實(shí)現(xiàn)
function mountComponent(vm) {
     // 獲取 render function
 if (!this.options.render) {
 // template to render
 // Vue.compile = compileToFunctions
 let { render } = compileToFunctions() 
 this.options.render = render
 }
 // 觸發(fā)鉤子
 callHook('beforeMounte')
 // 初始化觀察者
 // render 渲染 vdom, 
 vdom = vm.render()
 // update: 根據(jù) diff 出的 patchs 掛載成真實(shí)的 dom 
 vm._update(vdom)
 // 觸發(fā)鉤子 
 callHook(vm, 'mounted')
}
// 更新節(jié)點(diǎn)實(shí)現(xiàn)
funtion queueWatcher(watcher) {
    nextTick(flushScheduleQueue)
}
// 清空隊(duì)列
function flushScheduleQueue() {
     // 遍歷隊(duì)列中所有修改
 for(){
     // beforeUpdate
 watcher.before()
 
 // 依賴局部更新節(jié)點(diǎn)
 watcher.update() 
 callHook('updated')
 }
}
// 銷毀實(shí)例實(shí)現(xiàn)
Vue.prototype.$destory = function() {
     // 觸發(fā)鉤子
 callHook(vm, 'beforeDestory')
 // 自身及子節(jié)點(diǎn)
 remove() 
 // 刪除依賴
 watcher.teardown() 
 // 刪除監(jiān)聽
 vm.$off() 
 // 觸發(fā)鉤子
 callHook(vm, 'destoryed')
}
  1. 數(shù)據(jù)響應(yīng)(數(shù)據(jù)劫持)

看完生命周期后,里面的watcher等內(nèi)容其實(shí)是數(shù)據(jù)響應(yīng)中的一部分。數(shù)據(jù)響應(yīng)的實(shí)現(xiàn)由兩部分構(gòu)成: 觀察者( watcher ) 和 依賴收集器( Dep ),其核心是 defineProperty這個(gè)方法,它可以 重寫屬性的 get 與 set 方法,從而完成監(jiān)聽數(shù)據(jù)的改變。

Observe (觀察者)觀察 props 與 state
遍歷 props 與 state,對(duì)每個(gè)屬性創(chuàng)建獨(dú)立的監(jiān)聽器( watcher )
使用 defineProperty 重寫每個(gè)屬性的 get/set(defineReactive)
get: 收集依賴
Dep.depend()watcher.addDep()
set: 派發(fā)更新
Dep.notify()
watcher.update()
queenWatcher()
nextTick
flushScheduleQueue
watcher.run()
updateComponent()

大家可以先看下面的數(shù)據(jù)相應(yīng)的代碼實(shí)現(xiàn)后,理解后就比較容易看懂上面的簡單脈絡(luò)了。

let data = {a: 1}
// 數(shù)據(jù)響應(yīng)性
observe(data)
// 初始化觀察者
new Watcher(data, 'name', updateComponent)
data.a = 2
// 簡單表示用于數(shù)據(jù)更新后的操作
function updateComponent() {
 vm._update() // patchs
}
// 監(jiān)視對(duì)象
function observe(obj) {
     // 遍歷對(duì)象,使用 get/set 重新定義對(duì)象的每個(gè)屬性值
 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() {
     // 當(dāng)有獲取該屬性時(shí),證明依賴于該對(duì)象,因此被添加進(jìn)收集器中
 if (Dep.target) {
 dep.addSub(Dep.target)
 }
 return v
 },
 // 重新設(shè)置值時(shí),觸發(fā)收集器的通知機(jī)制
 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 原理實(shí)現(xiàn)

創(chuàng)建 dom 樹
樹的diff,同層對(duì)比,輸出patchs(listDiff/diffChildren/diffProps)
沒有新的節(jié)點(diǎn),返回
新的節(jié)點(diǎn)tagName與key不變, 對(duì)比props,繼續(xù)遞歸遍歷子樹
對(duì)比屬性(對(duì)比新舊屬性列表):
舊屬性是否存在與新屬性列表中
都存在的是否有變化
是否出現(xiàn)舊列表中沒有的新屬性
tagName和key值變化了,則直接替換成新節(jié)點(diǎn)
渲染差異
遍歷patchs, 把需要更改的節(jié)點(diǎn)取出來
局部更新dom

// diff算法的實(shí)現(xiàn)梳理
function diff(oldTree, newTree) {
     // 差異收集
 let pathchs = {}
 dfs(oldTree, newTree, 0, pathchs)
 return pathchs
}
function dfs(oldNode, newNode, index, pathchs) {
 let curPathchs = []
 if (newNode) {
 // 當(dāng)新舊節(jié)點(diǎn)的 tagName 和 key 值完全一致時(shí)
 if (oldNode.tagName === newNode.tagName && oldNode.key === newNode.key) {
     // 繼續(xù)比對(duì)屬性差異
 let props = diffProps(oldNode.props, newNode.props)
 curPathchs.push({ type: 'changeProps', props })
 // 遞歸進(jìn)入下一層級(jí)的比較
 diffChildrens(oldNode.children, newNode.children, index, pathchs)
 } else {
     // 當(dāng) tagName/key 修改了后,表示已經(jīng)是全新節(jié)點(diǎn),則沒必要繼續(xù)比了
 curPathchs.push({ type: 'replaceNode', node: newNode })
 }
 }
     // 構(gòu)建出整顆差異樹
 if (curPathchs.length) {
        if(pathchs[index]){
            pathchs[index] = pathchs[index].concat(curPathchs)
        } else {
            pathchs[index] = curPathchs
        }
 }
}
// 屬性對(duì)比實(shí)現(xiàn)
function diffProps(oldProps, newProps) {
 let propsPathchs = []
 // 遍歷新舊屬性列表
 // 查找刪除項(xiàng)、修改項(xiàng)、查找新增項(xiàng)
 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
}
// 對(duì)比子級(jí)差異
function diffChildrens(oldChild, newChild, index, pathchs) {
        // 標(biāo)記子級(jí)的刪除/新增/移動(dòng)
 let { change, list } = diffList(oldChild, newChild, index, pathchs)
 if (change.length) {
 if (pathchs[index]) {
 pathchs[index] = pathchs[index].concat(change)
 } else {
 pathchs[index] = change
 }
 }
     // 根據(jù) key 獲取原本匹配的節(jié)點(diǎn),然后遞歸從頭開始對(duì)比
 oldChild.map((item, i) => {
 let keyIndex = list.indexOf(item.key)
 if (keyIndex) {
 let node = newChild[keyIndex]
 // 進(jìn)一步遞歸對(duì)比
 dfs(item, node, index, pathchs)
 }
 })
}
// 1、列表對(duì)比,根據(jù) key查找對(duì)應(yīng)的匹配項(xiàng)
// 2、對(duì)比出新舊列表的新增/刪除/移動(dòng)
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)
 }
 })
 // 很直白的標(biāo)記刪除
 for (let i = list.length - 1; i>= 0; i--) {
 if (!list[i]) {
 list.splice(i, 1)
 change.push({ type: 'remove', index: i })
 }
 }
 // 標(biāo)記新增以及移動(dòng)
 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 {
 // 移動(dòng)
 if (index !== i) {
 change.push({
 type: 'move',
 form: index,
 to: i,
 })
 move(list, index, i)
 }
 }
 })
 return { change, list }
}
  1. Proxy 相比于 defineProperty 的優(yōu)勢(shì)

數(shù)組變化也能監(jiān)聽到
不需要深度遍歷監(jiān)聽
let data = { a: 1 }
let reactiveData = new Proxy(data, {
get: function(target, name){
// ...
},
// ...
})

  1. vue-router

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

  1. vuex

state: 狀態(tài)中心
mutations: 更改狀態(tài)
actions: 異步更改狀態(tài)
getters: 獲取狀態(tài)
modules: 將state分成多個(gè)modules,便于管理

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 概要 64學(xué)時(shí) 3.5學(xué)分 章節(jié)安排 電子商務(wù)網(wǎng)站概況 HTML5+CSS3 JavaScript Node 電子...
    阿啊阿吖丁閱讀 9,799評(píng)論 0 3
  • 第3章 基本概念 3.1 語法 3.2 關(guān)鍵字和保留字 3.3 變量 3.4 數(shù)據(jù)類型 5種簡單數(shù)據(jù)類型:Unde...
    RickCole閱讀 5,502評(píng)論 0 21
  • 函數(shù)和對(duì)象 1、函數(shù) 1.1 函數(shù)概述 函數(shù)對(duì)于任何一門語言來說都是核心的概念。通過函數(shù)可以封裝任意多條語句,而且...
    道無虛閱讀 4,943評(píng)論 0 5
  • Swift1> Swift和OC的區(qū)別1.1> Swift沒有地址/指針的概念1.2> 泛型1.3> 類型嚴(yán)謹(jǐn) 對(duì)...
    cosWriter閱讀 11,631評(píng)論 1 32
  • 我叫小麗,現(xiàn)居住上海,我的職業(yè)是一名律師,工作中我雷厲風(fēng)行,說一不二,工作外我孤高冷傲,別人都說我難以接近,包括我...
    云磬閱讀 456評(píng)論 0 0

友情鏈接更多精彩內(nèi)容