Webpack
ESLint
CI(Continuous integration)
浏览器原理
- async 和 defer
- 一张图:
- 一张图:
Web Component
- ( 待补:juejin.cn/post/684490… )
React
生命周期
- 一张图:
- setState 为什么是异步的?
- 批量更新事务:
- batchingStrategy.isBatchingUpdates 和 batchingStrategy.batchedUpdates()
- react是通过合成事件实现了对于事件的绑定,当进入事件处理流程后,该事务的isBatchingUpdates为true,如果在事件中调用setState方法,也会进入dirtyComponent流程。
- 如果我们在浏览器的 click 句柄(handler)中,Child 和 Parent 都调用了 setState,则我们不希望重新渲染 Child 两次,而是倾向于将它们标记为脏,然后在浏览器事件结束的时候重新渲染它们。
- 保证内部一致性:
- 使并发更新变得可行:React 可以根据 setState() 来自的不同的位置:事件句柄,网络响应,动画等,来为它们分配不同的优先级。
- 批量更新事务:
- 钩子函数(函数组件不能使用这些方法,因为这需要继承react Component这个类)
- constructor(props, context)
- getDefaultProps()
- 对于每个组件实例来讲,这个方法只会调用一次,该组件类的所有后续应用,getDefaultPops 将不会再被调用,其返回的对象可以用于设置默认的 props值。
- getInitialState()
- 对于组件的每个实例来说,这个方法的调用有且只有一次,用来初始化每个实例的 state,在这个方法里,可以访问组件的 props。
- getInitialState 和 getDefaultPops 的调用是有区别的,getDefaultPops 是对于组件类来说只调用一次,后续该类的使用都不会被调用,相当于 Object.prototype.XXX。而 getInitialState 是对于每个组件实例来讲都会调用,并且只调一次,相当于 this.XXX。
- componentWillMount()
- 在组件挂载到DOM前调用,且只会被调用一次,在这边调用this.setState不会引起组件重新渲染,也可以把写在这边的内容提前到constructor()中,所以项目中很少用。
- render()
- 根据组件的props和state(无两者的重传递和重赋值,论值是否有变化,都可以引起组件重新render) ,return 一个React元素(描述组件,即UI),不负责组件实际渲染工作,之后由React自身根据此元素去渲染出页面DOM。render是纯函数(Pure function:函数的返回结果只依赖于它的参数;函数执行过程里面没有副作用),不能在里面执行this.setState,会有改变组件状态的副作用。
- setState引起的state更新 或 父组件重新render引起的props更新,更新后的state和props相对之前无论是否有变化,都将引起子组件的重新render。可通过shouldComponentUpdate方法优化。
- componentDidMount()
- 组件挂载到DOM后调用,且只会被调用一次,这里一般是发起异步请求,因为在componentWillMount里发起AJAX,不管多快得到结果也赶不上首次render。
- 该方法被调用时,已经渲染出真实的 DOM,可以再该方法中通过 this.getDOMNode() 访问到真实的 DOM(推荐使用 ReactDOM.findDOMNode())。
- componentWillReceiveProps(nextProps)
- 在此函数中调用 this.setState() 将不会引起第二次渲染。所以可以把外部 props 转换成自己的state,达到优化渲染的效果。
- shouldComponentUpdate(nextProps, nextState)
- true:重新加载组件
- false:不更新组件
- componentWillUpdate(nextProps, nextState)
- 此方法在调用render方法前执行,在这边可执行一些组件更新发生前的工作,一般较少用。
re- render() 重新调用- componentDidUpdate(prevProps, prevState)
- 此方法在组件更新后被调用,可以操作组件更新的DOM,prevProps和prevState这两个参数指的是组件更新前的props和state
- componentWillUnmount()
- 此方法在组件被卸载前调用,可以在这里执行一些清理工作,比如清楚组件中使用的定时器,清楚componentDidMount中手动创建的DOM元素等,以避免引起内存泄漏。
- 16版本新增生命周期
- static getDerivedStateFromProps(props, state)
- 在组件创建时和更新时的render方法之前调用,它应该返回一个对象来更新状态,或者返回null来不更新任何内容。强制开发者在render之前只做无副作用的操作,而且能做的操作局限在根据props和state决定新的state,而已。
- getSnapshotBeforeUpdate()
- 被调用于render之后,可以读取但无法使用DOM的时候。它使您的组件可以在可能更改之前从DOM捕获一些信息(例如滚动位置)。此生命周期返回的任何值都将作为参数传递给componentDidUpdate(prevProps, prevState, snapshot)
- static getDerivedStateFromProps(props, state)
Context
- 解决嵌套组件自顶向下多层传递 props 和 state 的问题,this.context.XXX 可以跨级传递数据。
- 生产者消费者模式
- 父组件是生产者,父组件必须用静态属性 childContextTypes 声明提供给子组件的 Context 内容,并实现一个实例getChildContext方法,返回一个代表Context的纯对象 (plain object)
- 子组件是消费者,子组件必须用静态属性 contextTypes 申请要消费的 Context 内容,之后才能通过 this.context.XXX 来访问父组件提供的 Context 内容。
- 无状态子组件
- 使用:ChildComponent = (props, context) => {console.log(context.propsA)};
- 声明:ChildComponent.contextProps = { propA: PropTypes.string };
- React.createContext() 会创建一个 Context 对象(例如 TestContext)
<Provider />:<TestContext.Provider value={{propsA: 'xxxxx'}} />的value相当于的 getChildContext()<Consumer />:<TestContext.Consumer>{context => (<h1>{context.propsA}</h1>)}</TestContext.Consumer>的children必须是一个函数,通过函数的参数获取<Provider />提供的Context。
- context 从哪来
- this.context
- constructor(props, context)
- componentWillReceiveProps(nextProps, nextContext)
- shouldComponentUpdate(nextProps, nextState, nextContext)
- componetWillUpdate(nextProps, nextState, nextContext)
- 无状态函数式组件 ChildComponent = (props, context) => {console.log(context.propsA)};
- 为什么要用父声明子申请的设计
- 父子组件是一对多的线性依赖。随意的使用Context其实会破坏这种依赖关系,导致组件之间一些不必要的额外依赖,降低组件的复用性,进而可能会影响到App的可维护性。通过childContextTypes和contextTypes这两个静态属性的约束,可以在一定程度保障,只有组件自身,或者是与组件相关的其他子组件才可以随心所欲的访问Context的属性,无论是数据还是函数。
JSX
React-Router
Redux
数据流
- 一张图:
- Store > subscribe > Component > trigger >Action > dispatch > Reducer > put > Store
Reducer
- 一个纯函数,执行一个action并且返回一个新的状态。
- 入参是state 和 action
- state是当前保存在store中的数据,初始时无值需要入参处指定初始值。
- action相当于一个容器,有两个 key
- type - 一个简单的字符串常量,例如ADD, UPDATE, DELETE等。
- payload - 用于更新state的数据。
- 代码设计上不应该在reducer里用 if-else/switch 来取针对不同的 action.type做业务处理,service code 不应该写在这一层,应该用 myActionsaction.type 解耦。dva就是做了这件事,把 action处理单独抽出来
- 多个 reducer 的场景:
let store = redux.createStore(redux.combineReducers({reducer1, reducer2})); - Action触发流:
- 抽离的 action 方法是接收 payload,组装并返回一个 action 容器,例如
function action1(data1, data2) {return {type: 'ACTION_1', payload: {data1, data2}}}; - 用 store.dispatch()去分发这个 action:如
store.dispatch(action1('data_1', 'data_2')); - reducer接收到相应的 action 开始处理:如
reducer1(state, action){ if(action.type === 'ACTION_1'){return {...state, ...action.payload}};
- 抽离的 action 方法是接收 payload,组装并返回一个 action 容器,例如
- Immutability(不可变性)
- 如果代码只处理原始数据类型(numbers, strings, booleans),那么你不用担心。但是,如果在处理Arrays和Objects时,则需要小心执行可变操作。不可变操作实现有 immutable.js/Object.assign()/{...Obj}
- immutable.js
- React 结合
- react-redux
- 用 Provider 包起来
ReactDOM.render(<Provider store={store}> <h1>xxx</h1></Provider>, document.getElementById('root'));
- 用 Provider 包起来
- react-redux
- 异步数据处理
- Redux本身只能处理同步的Action,但可以通过中间件来拦截处理其它类型的action,比如 Thunk 函数,再用回调触发普通Action,从而实现异步处理,在这点上所有Redux的异步方案都是类似的。
- 概念
- 模板代码
- 乐观更新
- 错误处理
- 竞态
- 模块
- redux-thunk
- redux-promise
- redux-loop
- redux-saga
- redux-observable
- redux-action-tools
Diff
Vue
生命周期
- 一张图:
- 钩子函数(Vue的所有生命周期函数都是自动绑定到this的上下文上,所以箭头函数会报错)
-
beforeCreate
-
created
-
el 和 template 过程参与
- 实例没有指定 el 属性时就不会触发下面的 beforeMount 和 mounted,可以用$mount 方法手动挂载 el,生命周期方法会按序触发。
- render函数 > template属性 > 外部html, vue 的编译过程就是把tempalte编译成render函数的过程。
-
beforeMount
-
mounted
-
beforeUpdate
- 这里的数据只有和模版中的数据绑定了才会发生更新,才会调用 berforeUpdate 和 updated 这两个钩子。
-
updated
-
beforeDestroy
- 在 beforeDestroy 生命钩子调用之前,所有实例都可以用,但是当调用后,Vue 实例的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。
-
destroyed
-
其他钩子
- activated:当组件激活的时候调用
- deactivated:当组件停用的时候调用
- errorCaptured:这个生命钩子可以看官网,2.5.0之后才有的。当捕获一个来自子孙组件的错误时被调用。
-
自带属性方法
- data = $data
- 在定义一个组件时,data 必须声明为一个返回初始数据对象的无副作用的函数,因为可能会使用此函数创建多个实例。如果 data 是一个普通对象,则所有创建出来的实例将共享引用同一个数据对象!
- data: (vm) => ({x: vm.myProp});
指令
- v-text = textContent
- v-html = innerHtml
- v-bind:href = :href
- 作用:当表达式的值改变时,将其产生的连带影响,响应式地作用于 DOM
- 语法:v-bind:href="url"
- 简写::href="url"
- 说明:绑定的数据从 data 或 props中获取
- v-on:click = @click
- 作用:绑定事件
- 语法:v-on:click="say" or v-on:click="say('参数', $event)"
- 简写:@click="say"
- 说明:绑定的事件从methods中获取
- 事件修饰符
*
v-on:click.stop="say"阻止冒泡,调用event.stopPropagation()v-on:click.prevent阻止默认事件,调用event.preventDefault()v-on:click.capture="say"添加事件侦听器时使用事件捕获模式v-on:click.self="say"只当事件在该元素本身(比如不是子元素)触发时触发回调- v-on:click.once="say" 事件只触发一次
- 顺序很重要:
v-on:click.prevent.self会阻止所有的点击,而v-on:click.self.prevent只会阻止对元素自身的点击。
- v-model
- 作用:在表单元素上创建双向数据绑定
- 说明:监听用户的输入事件以更新数据
- demo:
<input v-model="message" placeholder="edit me"><p>Message is: {{ message }}</p>
- v-for
- item 为值,key 为键,index 为索引
- demo:
<p v-for="(item, key, index) in obj" :key="item.key">{{item}} -- {{key}}</p> - 性能:使用 key,VUE会基于 key 的变化重新排列元素顺序,并且会移除 key 不存在的元素。
- 样式处理 v-bind
- :class="[{active: true}, 'warning', errorClass]"
- :style="[{fontSize: fs + 'px'}, baseStyle, overridingStyles]"
- v-if: 根据表达式的值的真假条件,销毁或重建元素
- v-show: 根据表达式之真假值,切换元素的 display CSS 属性
- 性能和体验
- v-cloak: 这个指令保持在元素上直到关联实例结束编译。
- 防止未编译的 Mustache 模板数据出现在页面上。
- CSS 样式
[v-cloak] { display: none }可以在实例 ready 前隐藏未编译的 Mustache 标签。
- v-pre: 跳过这个元素和它的子元素的编译过程。可以用来显示原始 Mustache 标签,跳过大量没有指令的节点会加快编译。
- v-once: 只渲染元素和组件一次。随后的重新渲染,元素/组件及其所有的子节点将被视为静态内容并跳过。这可以用于优化更新性能。
- v-cloak: 这个指令保持在元素上直到关联实例结束编译。
- 自定义指令
- 全局注册
- 注册方法:
Vue.directive( directiveName, [definition] ) - demo:
<div id="app" class="demo"> <!-- 全局注册 --> <input type="text" v-focus> </div> <script> Vue.directive("focus", { inserted: function(el){ el.focus(); } }) new Vue({ el: "#app" }) </script> - 注册方法:
- 局部注册
- 注册方法:在Vue实例中添加 directives 对象
- demo:
<div id="app" class="demo"> <!-- 局部注册 --> <input type="text" v-focus2> </div> <script> new Vue({ el: "#app", directives: { focus2: { inserted: function(el){ el.focus(); } } } }) </script> - 自定义指令的钩子函数
bind(el, binding, vnode):只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。inserted(el, binding, vnode):被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。update(el, binding, vnode, oldVnode):所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。指令的值可能发生了改变,也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新。componentUpdated(el, binding, vnode, oldVnode):指令所在组件的 VNode 及其子 VNode 全部更新后调用。unbind(el, binding, vnode):只调用一次,指令与元素解绑时调用。
- 全局注册
Vue-Router
VueX
Node
EventLoop
- 一张图:
- Timer(setTimeout/setInterval) --tick-- I/O --tick-- Check(setImmediate)
- macrotasks: setTimeout setInterval setImmediate I/O UI渲染
- microtasks: Promise process.nextTick Object.observe MutationObserver
- 一个有趣的例子:process.nextTick是注册在当前的tick阶段进行拼接,继续执行,从而导致了死循环
process.nextTick(function tick () {
process.nextTick(tick)
});
函数式编程
柯里化
function realSum(...args) {
return args.reduce((prev, next) => prev + next, 0);
}
function sum(...args1) {
let value = realSum(...args1);
function fn(...args2) {
value += realSum(...args2)
return fn
};
fn.valueOf = () => value;
return fn;
}
sum(1,2)(3)(4)(2,3).valueOf(); // 15
寻找对称数
function findSymmetricNum(min, max) {
let num = min;
let temp = '';
const result = [];
for(let i=0; i< max-min+1;i++) {
temp = num + '';
// 字符串反转后是否与之前相等
if(temp.split('').reverse().join('') === temp) {
result.push(num);
}
num ++;
}
return result;
};
异步链式调用
Sass
Less
HTTP
原理
HTTP2
长链接复用
数据 chunk 方式通信
- HTTP头:Transfer-Encoding: chunked
- 编码格式:[chunk size][\r\n][chunk data][\r\n][chunk size][\r\n][chunk data][\r\n][chunk size = 0][\r\n][\r\n]
CDN
- 查找链路
- 一张图:
- 客户端 -> ( * CNAME:把自有域名换成 cdn 域名) -> DNS查询 -> 全局负载均衡系统 -> (透传客户端 IP 和目标资源 URL) -> 区域负载均衡系统 ->(根据客户端 IP 和目标资源 URL,获取距离客户端最近且有相应资源的cdn 缓存服务器的地址) -> 全局负载均衡系统 -> (cdn 缓存服务器地址)-> 客户端 -> (请求缓存服务器上的文件)
- 一张图:
- 回源
- 当 cdn 缓存服务器中没有符合客户端要求的资源的时候,缓存服务器会请求上一级缓存服务器,以此类推,直到获取到。最后如果还是没有,就会回到我们自己的服务器去获取资源。没有资源,资源过期,访问的资源是不缓存资源等都会导致回源。
- 更新数据
- 主动(PUSH)
- 被动(PULL)
DNS
-
一张图
-
查找方式
- 递归查询:递归查询是一种DNS 服务器的查询模式,在该模式下DNS 服务器接收到客户机请求,必须使用一个准确的查询结果回复客户机。如果DNS 服务器本地没有存储查询DNS 信息,那么该服务器会询问其他服务器,并将返回的查询结果提交给客户机。
- 迭代查询:DNS 服务器会向客户机提供其他能够解析查询请求的DNS 服务器地址,当客户机发送查询请求时,DNS 服务器并不直接回复查询结果,而是返回客户机另一台DNS 服务器地址,客户机再向这台DNS 服务器提交请求,依次循环直到返回查询的结果为止。
-
多域名查询优化