View / MVVM 框架
对比 React 、Angular 和 Vue
相同点
- React 和 Vue都提供了 Virtual Dom
- 提供了 响应式(Reactive)和组件化(Composable)的视图组件
- 注意力保持在核心库,而将其他功能如路由和全局状态管理交给相关的库
不同点
渲染优化
React:当组件状态发生变化时,以该组件为根,重新渲染整个组件子树
- shouldComponentUpdate,采用合适方式,如不可变对象,比较 props 和 state,决定是否重新渲染当前组件
- 使用 PureComponent:继承自 Component 类,自动加载 shoudComponentUpdate 函数,自动对
props 和 state 浅比较决定是否触发更新
Vue:自动追踪组件依赖,精确渲染状态改变的组件
HTML 和 CSS
React:支持 JSX
- 在构建视图时,使用完整的 JavaScript 功能
- 开发工具多支持 JSX 语法高亮,类型检查和自动完成
Vue:提供渲染函数,支持 JSX,但默认推荐 Vue 模版
- 与书写 HTML 更一致的开发体验
- 基于 HTML 的模版更容易迁移到 Vue
- 设计师和新人开发者更容易理解和参与
- 支持模板预处理器,如 Pug
CSS 作用域
React:通过 CSS-in-JS 方案,如 styled-components 和 emotion
Vue:
-
支持 CSS-in-JS 方案,如 styled-components-vue 和 vue-emotion
-
单文件组件中 style 标签可选 scoped 属性。支持 Sass \ Less \ Stylus 等 CSS 预处理器,深度集成
CSS Modules
规模
向上扩展:
React:
- 路由库和状态管理库,由社区维护支持,生态松散且繁荣
- 提供脚手架工具,故意作了限制
- 不允许在项目生成时进行配置
- 只提供一个单页面应用模板
- 不支持预设配置
Vue:
- 路由库和状态管理库,由官方维护支持,与核心库同步更新
- 提供 CLI脚手架,可构建项目,快速开发组件的原型
- 允许在项目生成时配置
- 提供了各种用途的模板
- 支持预设配置
向下拓展
React:学习曲线陡峭,需要前置知识:JSX ES2015,需要学习构建系统
Vue:既支持向上拓展与 React 一样,也支持向下拓展与 jQuery 一样,上手快
原生渲染
React Native:使用相同组件模型编写具有本地渲染能力的APP,跨平台开发
Weex:阿里巴巴发起,Apache 基金会孵化,同样支持本地渲染,跨平台开发
NativeScript-Vue,基于 Vue.js 构建原生应用的 NativeScript 插件
MobX
React:流行的状态管理框架
Vue:选择 Vue 比 React + MobX 更合理
Preact 和其它类 React 库
难以保证与 React 库 100% 兼容
Vue 和 Angular 相同点
TypeScript 都支持 TypeScript,支持 类型声明 和 组件装饰器
运行时性能,Angular 和 Vue 都很快
Vue 和 Angular 不同点
- 体积
Angular 用 AOT 和 tree-shaking 缩小体积
Vuex + Vue Router (gzip 后 30kB)比使用优化angular-cli (~65kB)小
- 灵活性
Vue 提供构建工具,不限制组织应用代码的方式
Angular 提供构建工具,有相对严格的代码组织规范
- 学习曲线
Vue 只需 HTML 和 JavaScript 基础
Angular 提供 API 和 概念 更多,设计目标针对 大型复杂应用,对新手有难度
如何实现一个组件,前端组件的设计原则是什么?
- 单一原则:一个组件只做一件事
- 通过脑图、结构图,标识组件的 State Props Methods 生命周期,表示层次和数据流动关系
- State 和 Props
- 扁平化:最多使用一层嵌套,便于对比数据变化,代码更简洁
- 无副作用:State 仅响应事件,不受其他 State 变化影响
- 松耦合
- 组件应该独立运行,不依赖其它模块
- 配置、模拟数据、非技术说明文档、helpers、utils 与 组件代码分离 视图组件只关心视图,数据获取,过滤,事件处理应在外部 JS 或 父组件 中处理
- Kiss原则(Keep it Simple Stupid)
- 不需要 State 时,使用 函数组件
- 不要传递不需要的 Props
- 及时抽取复杂组件为独立组件
- 不要过早优化
- 参考 CSS 的 OOSS 方法论,分离 位置 和 样式,利于实现皮肤
- 考虑 多语言、无障碍 等后期需求
Vue
computed 与 watch 的区别
computed
- 支持数据缓存
- 内部数据改变也会触发
- 不支持异步,异步无效
- 适用于 一个属性由其他属性计算而来,依赖其他属性的场景
watch
- 不支持数据缓存
- 可以设置一个函数,带有两个参数,新旧数据
- 支持异步
- 监听数据必须是 data 中声明过或者父组件传递过来的 props 中数据
- immediate:组件加载立即触发回调函数执行
- deep:深度监听
Vue.nextTick 原理和作用
- Vue 异步执行 DOM 更新,观察数据变化,开启队列缓冲同一事件循环中所有数据改变
- 同一个 watcher 被多次触发,只会被推入到队列中一次,避免不必要的计算和 DOM 操作
- 在下一个事件循环 tick 中,Vue 刷新队列并执行实际(已去重)工作
- 异步队列使用 Promise.then 和 MessageChannel,不支持环境使用 setTimeout(fn, 0)
- 在需要立即更新 DOM 的场景中使用
Vue3.x 的新特性
API 风格
- Vue2.x:Options API
- Vue3.x:Composition API
生命周期
- 组件生命周期
//Vue2.x:
beforeCreate
created
beforeMount
mounted
beforeUpdate
updated
beforeDestroy
destroyed
activated
deactivated
errorCaptured
//Vue3.x:
setup
onBeforeMount
onMounted
onBeforeUpdate
onUpdate
onBeforeUnmout
onUnmounted
onActivated
onDeactivated
onErrorCaptured
onRenderTriggered
onRenderTracked
- 指令生命周期
//Vue2.x:
bind
inserted
update
componentUpdated
unbind
//Vue3.x:
beforeMount
mounted
beforeUpdate
updated
beforeUnmount
unmounted
数据
- Vue2.x:data
- Vue3.x
- ref 基础类型和对象的双向绑定
- reactive 对象的双向绑定 ---- 通过 toRefs 转为 ref
监听
- Vue2.x:watch
- Vue3.x:watchEffect
-
传入回调函数,不需要指定监听的数据源,自动收集响应式数据
-
可以从回调函数中,获取数据的最新值,无法获取原始值
slot
- Vue2.x:通过 slot 使用插槽,通过 slot-scope 给插槽绑定数据
- Vue3.x:v-slot: 插槽名 = 绑定数据名
v-model
- Vue2.x:.async 绑定属性和 update:+ 属性名 事件
- Vue3.x:无需 .async 修饰
新功能
-
Teleport:适合 Model 实现
-
Suspense:
-
一个组件两个 template
-
先渲染 #fallback 内容,满足条件再渲染 #default
-
适合异步渲染显示加载动画,骨架屏等
-
-
defineAsyncComponent:定义异步组件
-
允许多个根节点
性能
- 使用 Proxy 代替 definePoperty 和 数组劫持
- 标记节点类型,diff 时,跳过静态节点
- 支持 ES6 引入方法,按需编译
- 配套全新的 Web 开发构建工具 Vite
React
React 生命周期
React 16.4 + 生命周期
Mounting
constructor
getDerivedStateFromProps
render
React 更新 DOM 和 refs
componentDidMount
Updation
props 变化 → getDerivedStateFromProps
setState() → getDerivedStateFromProps → shouldComponentUpdate
forceUpdate() → getDerivedStateFromProps
render
getSnapshotBeforeUpdate
React 更新 DOM 和 refs
componentDidUpate
Unmouting
componentWillUnmount
React 中使用 useEffect 如何清除副作用?
在 useEffect 中返回一个清除函数,名称随意,可以是匿名函数或箭头函数,在清除函数中添加 处理副作用的逻辑,如移除订阅等
JavaScript
function component(props) {
function handleStatusChange(status) {
console.log(status.isOnine) }
useEffect(() => {
API.subscribe(props.id, handleStatusChange))
}
return function cleanup() {
API.unsubscribe(props.id, handleStatusChange)
}
}
对比 React 和 Vue 的 diff 算法
(1)相同点
- 虚拟 Dom 只同级比较,不跨级比较
- 使用 key 标记和复用节点,不建议使用数组索引 index 作为 key
(2)不同点
-
顺序
Vue:两端到中间 React:从左到右
-
节点元素类型相同,ClassName 不同
Vue:不同类型元素,删除重新创建 React:相同类型元素,修改
-
节点类型
Vue 3.x:VNode 创建时,即确定类型
React 中有哪几种类型的组件,如何选择?
-
无状态组件
更适合函数组件 负责展示 无状态,复用度高
-
有状态组件
函数组件 + hooks 或 类组件 useState 或 声明 state useEffect 或 使用生命周期
-
容器组件
子组件状态提升到此,统一管理 异步操作,如数据请求等 提高子组件的复用度
-
高阶组件
接收组件,返回组件 为原有组件增加新功能和行为 代替 mixins,避免状态污染
-
回调组件
高阶组件的另一种形式 将组件本身,通过 props.children 或 prop 属性 传递给子组件 适合不能确定或不关心传给子组件数据的场景,如路由,加载组件的实现
Css
如何实现单行居中,多行居左?
父级元素:
CSS
text-align: center;
自身元素:
CSS
text-align: left;
display: inline-block;
如何实现 1px 边框?
方法一:border-width
CSS
.border {
border: 1px solid;
}
@media screen and {
min-resolution: 2dppx} {
.border {
border: 0.5px solid;
}
}
@media screen and (min-resolution: 3dppx) {
.border {
border: 0.333333px solid;
}
}
方法二:伪类 + transform
CSS
div::after {
content: '';
display: block;
border-bottom: 1px solid;
}
@media only screen and (min-resolution: 2dppx) {
div::after {
-webkit-transform: scaleY(.5);
transform: scaleY(.5);
}
}
@media only screen and (min-resolution: 3dppx) {
div::after {
-webkit-transform: scaleY(.33);
transform: sacleY(.33);
}
}
多方法实现高度 100% 容器
(1)百分比
<style>
html, body {
height: 100%;
}
div {
height: 100%;
background-color: azure;
}
</style>
<div></div>
(2)相对单位
<style>
div {
height: 100vh;
background-color: azure;
}
</style>
<div></div>
(3)calc
<style>
html, body{
height: 100%;
}
div {
height: calc(100%)
}
</style>
<div></div>
多方法实现圆角边框
背景图片:绘制圆角边框的图片,4个圆角 + 4个边框的小图片,拼成圆角边框
border-radius: 5px
clip-path: inset(0 round 5px)
多方法实现文字描边
text-shadow: 0 0 1px black;
-webkit-text-stroke: 1px black;
position: relative / position: absolute 子绝父相
JavaScript
es3相关题
a = [],a.push(...[1, 2, 3]) ,a = ?
a = [1, 2, 3],考核点如下:
[].push:调用数组 push 方法
apply:
第一参数:指定 push 执行时的 this,即正在调用 push 的对象为数组 a
第二参数:传入数组或类数组对象作为参数数组,展开作为 push 的参数列表
push的语法:支持将一个或多个元素添加到数组末尾
arr.push(element1, ..., elementN)
a = ?, a==1 && a==2 && a==3 成立
== 会触发隐式转换,=== 不会
对象转字符串
先尝试调用对象的 toString()
对象无 toString()或 toString 返回非原始值,调用 valueOf() 方法
将该值转为字符串,并返回字符串结果
否则