类同
简介
- vue
Vue 是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。另一方面,当与现代化的工具链以及各种支持类库结合使用时,Vue 也完全能够为复杂的单页应用提供驱动。
- react
React 是一个声明式,高效且灵活的用于构建用户界面的 JavaScript 库。使用 React 可以将一些简短、独立的代码片段组合成复杂的 UI 界面,这些代码片段被称作“组件”。
生命周期
- vue
- react
响应式
- 数据 -> 视图
vue直接修改data即可。还可以通过Object.freeze()阻止响应
react需要通过setState来修改。
vue更简洁
原理:
- vue:
初始化的时候通过Object.defineProperty劫持data所有属性的getter和setter,获取数据时收集依赖,修改数据时通知依赖更新。
注意:对象属性的新增和删除、数组的部分修改无法劫持到,需要使用特定的方法。
- react:
调用setState会将更新任务加入到批量队列,判断是否立即执行,否则就异步批量更新。同步场景为:原生事件和定时器内部调用setState。
注意:如上图,引用类型不能直接修改,要换一个引用,这样才会触发更新
- 视图 -> 数据
- vue
提供语法糖v-model
而且提供修饰符lazy、number、trim
- react
需绑定事件处理后通过setState更新相应的state
项目结构
入口js
根组件
首页
路由,vue搭配vue-router,react搭配react-router-dom
全局状态,vue搭配vuex,react搭配redux,通过react-redux连接
vue需要一个特定的vue-loader
组件
调用方(父组件)
vue自定义组件上的v-model相当于:
<my-input :value="theName" @change="changeTheName"></my-input>
methods: {
changeTheName(val) {
this.theName = val
},
}
复制代码
组件通信
props写法
- Vue
传递变量值需使用 v-bind: 或 : ,事件绑定或自定义事件使用 v-on: 或 :
<Child name="jeni" :count="count" @change={handleChange} />
复制代码
支持动态参数
<a v-bind:[attributeName]="url"> ... </a>
复制代码
- React
<Child count={3} onChange={handleChange}/>
复制代码
指令
- 条件
- vue
<div v-if="theName.length === 0">1</div>
<div v-else-if="theName.length <= 5">2</div>
<div v-else>3</div>
复制代码
- react
{theName.length === 0 && <div>1</div>}
{theName.length !== 0 && theName.length <= 5 && <div>2</div>}
{theName.length !== 0 && theName.length > 5 && <div>3</div>}
复制代码
或者
{theName.length === 0 ?
(<div>1</div>)
:
(theName.length <= 5 ? (<div>2</div>) : (<div>3</div>))}
复制代码
- 遍历
- vue
当 Vue 正在更新使用 v-for 渲染的元素列表时,它默认使用“就地更新”的策略。如果数据项的顺序被改变,Vue 将不会移动 DOM 元素来匹配数据项的顺序,而是就地更新每个元素,并且确保它们在每个索引位置正确渲染。
建议尽可能在使用 v-for 时提供 key attribute,因为它是 Vue 识别节点的一个通用机制。
<div v-for="(item, index) in users" :key="item.id" class="list-item">
<div>序号:{{index}}</div>
<div>姓名:{{item.name}}</div>
<div>年龄:{{item.age}}</div>
</div>
<span v-for="n in 10">{{ n }} </span>
复制代码
- react
{users.map((item, index) => (
<div key={item.id} className={styles.listItem}>
<div>序号:{index}</div>
<div>姓名:{item.name}</div>
<div>年龄:{item.age}</div>
</div>
))}
复制代码
注意都要加key,方便做diff运算时快速得知两个节点是否是同一个节点,并保证key的唯一性!
- v-show
- vue
<div v-show="theName.length > 10">theName的值长度超过10才显示</div>
复制代码
- react
<div style={{display: theName.length > 10 ? 'block' : 'none'}}>
theName的值长度超过10才显示
</div>
复制代码
- v-html
- vue
<div v-html="htmlContent"></div>
<div>{{htmlContent}}</div>
htmlContent: '<h1>我是h1</h1>'
复制代码
- react
<div dangerouslySetInnerHTML={{__html: htmlContent}} />
<div>{htmlContent}</div>
const [htmlContent] = useState('<h1>我是h1</h1>')
复制代码
- 其他内置指令
- v-model
- v-text
- v-bind
- v-on
- v-cloak
- v-pre
- v-once
- 自定义指令
- vue
以下代码是局部指令,只可以在当前组件用,还可以定义全局指令
<input value="页面初始化时自动聚焦" v-focus />
directives: {
focus: {
bind: function() {
// 只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。
},
/**
* el: 指令所绑定的元素
* vnode: Vue 编译生成的虚拟节点
*/
inserted: function (el, binding, vnode, oldVnode) {
// 被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。
el.focus();
const {
name, // 指令名,不包括 v- 前缀
value, // 指令的绑定值
oldValue, // 指令绑定的前一个值,仅在 update 和 componentUpdated 钩子中可用
expression, // 字符串形式的指令表达式
arg, // 传给指令的参数
modifiers, // 一个包含修饰符的对象
} = binding;
},
update: function() {
// 所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。
// 指令的值可能发生了改变,也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新。
},
componentUpdated: function() {
// 指令所在组件的 VNode 及其子 VNode 全部更新后调用。
},
unbind: function() {
// 调用一次,指令与元素解绑时调用。
},
}
},
复制代码
- react
模拟实现
<input value="页面初始化时自动聚焦" ref={inputRef} />
const inputRef = useRef(null);
useEffect(() => {
//类似vue directive中的inserted
inputRef.current.focus();
return () => {
// 类似vue directive中的unbind
};
}, []);
useEffect(() => {
// 类似vue directive中的componentUpdated
});
复制代码
事件
- vue
在模板中通过指令 v-on: 或简写 @ 绑定事件
支持修饰符
<form v-on:submit.prevent="onSubmit">...</form>
复制代码
还有stop、capture、self、once、passive(不想阻止事件的默认行为),可以串联。
还有按键修饰符:enter、page-down、tab、delete (捕获“删除”和“退格”键)、esc、space、up、down、left、right
还有系统修饰符:ctrl、alt、shift、meta
其他: .exact 修饰符允许你控制由精确的系统修饰符组合触发的事件。
- react
在JSX中通过 onClick={handleClick} 绑定事件
参数e是合成事件SyntheticEvent,没有修饰符,要阻止默认行为或冒泡等等要在绑定的回调函数中具体处理
计算属性
- Vue
基于响应式依赖进行缓存。只在相关响应式依赖发生改变时它们才会重新求值。
computed: {
theName2() {
return this.theName + '2';
},
},
复制代码
- React
const theName2 = useMemo(() => {
return theName + '2';
}, [theName])
复制代码
侦听属性
- Vue
在数据变化时执行异步或开销较大的操作时
watch: {
theName: function(newValue, oldValue) {
if(newValue - oldValue > 8) {
alert('theName新值比旧值大8');
}
},
},
复制代码
- React
const theNameRef = useRef(theName);
useEffect(() => {
if(theName - theNameRef.current > 8) {
alert('theName新值比旧值大8');
}
theNameRef.current = theName;
}, [theName])
复制代码
class
- vue
<div class="container" :class="{active: isActive}">
复制代码
- react
<div className={styles.container}>
复制代码
style
- vue
<div style="width: 20px;height: 20px;">diff2</div>
<div :style="{color: textColor}">diff3</div>
复制代码
当 v-bind:style 使用需要添加浏览器引擎前缀的 CSS property 时,如 transform,Vue.js 会自动侦测并添加相应的前缀。
- react
<div style={{width: '20px', height: '20px'}}>diff2</div>
<div style={{color: textColor}}>diff3</div>
复制代码
prop验证
vue:不仅为你的组件提供了文档,还会在它们遇到错误的类型时从浏览器的JavaScript 控制台提示用户。
react:还需要单独引入一个包:prop-types
插槽
- vue
// 父组件
<slot-test>
I am slot content
</slot-test>
// 子组件
<template>
<div>
I am SlotTest.vue
<div>
<slot></slot>
</div>
</div>
</template>
复制代码
这是最简单的,还有具名插槽、作用域插槽
- react
// 父组件
<SlotTest>
I am slot content
</SlotTest>
// 子组件
<div>
I am SlotTest.jsx
<div>{props.children}</div>
</div>
复制代码
动态组件
- vue
<component :is="currentComponent"></component>
复制代码
适用场景:
- tab切换
- 有些 HTML 元素,诸如 <ul>、<ol>、<table> 和 <select>,对于哪些元素可以出现在其内部是有严格限制的。而有些元素,诸如 <li>、<tr> 和 <option>,只能出现在其它某些特定的元素内部。
- react
const components = [Parent, SlotTest];
const CurrentComponent = components[0];
<CurrentComponent />
复制代码
异步组件
- vue
路由配置中
{
path: '/list',
component: () => import(/* webpackChunkName: "list" */ '../pages/list'),
},
{
path: '/diff',
component: r => require(['../pages/diff'], r),
},
复制代码
- react
{
path: '/list',
component: lazy(() => import('../pages/list')),
},
// 必须在组件外面包一层 <Suspense fallback="<div>...</div>">
复制代码
组件缓存
- vue
<keep-alive>
组件实例能够被在它们第一次被创建的时候缓存下来
- react
无
混入
mixins
react基本废弃,推荐使用 HOC(高阶组件)或 Render Props
插件
- vue
插件通常用来为 Vue 添加全局功能。插件的功能范围没有严格的限制——一般有下面几种:
- 添加全局方法或者 property。如:vue-custom-element
- 添加全局资源:指令/过滤器/过渡等。如 vue-touch
- 通过全局混入来添加一些组件选项。如 vue-router
- 添加 Vue 实例方法,通过把它们添加到 Vue.prototype 上实现。
- 一个库,提供自己的 API,同时提供上面提到的一个或多个功能。如 vue-router
使用:
Vue.use(插件名,选项)
开发:
MyPlugin.install = function (Vue, options) {...}
复制代码
vue-router和vuex都是插件
- react
无
过滤器
- vue
可被用于一些常见的文本格式化。过滤器可以用在两个地方:双花括号插值和 v-bind 表达式 (后者从 2.1.0+ 开始支持)。过滤器应该被添加在 JavaScript 表达式的尾部,由“管道”符号指示
{{ message | filterA | filterB }}
复制代码
先调用filterA,再将结果传入filterB作为参数,如果filterA传递了参数,那么message将作为第一个参数
- react
方法调用
组件之间相互访问
- vue
- $root
- $parent
- $children
- $refs
- react
forwardRef
单元素/组件的过渡
- vue
<transition name="fade">
<p v-if="show">hello</p>
</transition>
复制代码
- react
无
模板根节点
必要要有根节点
- vue
只能是实际的html节点,3版本有提供虚拟节点
- react
可以是虚拟节点React.Fragment或<></>减少页面上不必要的标签
vue坑
1. 搭配ts使用没有react那么顺畅
2. 很多全局的东西(如组件、指令、Vue.prototype)容易造成混乱,不易维护
vue亮点
1. 提供keep-alive,多组件切换非常实用
2. 内在优化及众多语法糖和API,对开发者更友好
react坑
1. 条件渲染:当条件不是boolean类型且值转为boolean为false时,会显示相应的值
const isShow = 0
{isShow && <div>1234567890</div>}
复制代码
以上会显示 ‘0’
2. 异步请求返回后setState,可能报提示:组件已卸载,不能setState。因为可能其他state变化导致组件重渲染
3. 很多重复无用的diff运算开销,需要手动优化
当某个组件的状态发生变化时,它会以该组件为根,重新渲染整个组件子树。
如要避免不必要的子组件的重渲染,你需要在所有可能的地方使用 PureComponent,或是手动实现 shouldComponentUpdate 方法,函数式组件使用memo包裹。同时你可能会需要使用不可变的数据结构来使得你的组件更容易被优化。
vue组件的依赖是在渲染过程中自动追踪的,所以系统能精确知晓哪个组件确实需要被重渲染。
4. 引用类型只改了值没改引用,不会触发更新
react亮点
1. fiber
Fiber 是 React 16 中新的协调引擎。它的主要目的是使 Virtual DOM 可以进行增量式渲染。
为解决主线程长时间被 JS 运算占用而存在,是将运算切割为多个步骤,分批完成。
旧版 React 通过递归的方式进行渲染,使用的是 JS 引擎自身的函数调用栈,它会一直执行到栈空为止。而Fiber实现了自己的组件调用栈,它以链表的形式遍历组件树,可以灵活的暂停、继续和丢弃执行的任务。实现方式是使用了浏览器的requestIdleCallback这一 API。
window.requestIdleCallback()会在浏览器空闲时期依次调用函数,这就可以让开发者在主事件循环中执行后台或低优先级的任务,而且不会对像动画和用户交互这些延迟触发但关键的事件产生影响。函数一般会按先进先调用的顺序执行,除非函数在浏览器调用它之前就到了它的超时时间。
2. React Native用于开发原生应用技术成熟且应用广泛
3. 复用技术在不断进步,mixins -> HOC -> Render Props -> hooks
参考
lq782655835.github.io/blogs/vue/d…
jishuin.proginn.com/p/763bfbd54…
www.html.cn/qa/react/15…
www.cnblogs.com/xzsj/p/1351…
注:以上代码在项目中的配置都是:webpack5 + babel7