Redian新闻
>
无缝切换?从Vue到React

无缝切换?从Vue到React

科技


阿里妹导读


本文主要针对Vue开发者或新手快速上手React。

本文主要分析Vue和React在开发上的区别,帮助Vue开发者快速上手React,同时也适用于前端新手入门React。

单文件组件 VS class组件 VS 函数组件

Vue: 单文件组件

<template>  <div>{{ greeting }} world</div></template>
<script> export default { data() { return { greeting: 'hello' } } }</script><style></style>


React: Class组件

class Comp extends Component {  constructor(props) {    super(props);    this.state = {greeting: 'hello'};  }    render() {    return (      <div>        <div>{ greeting } world</div>      </div>    );  }}

官方文档[1]


React: 函数组件(推荐)

在Vue单文件组件和React的Class组件中,我们的元素、数据变量等必须放到固定的位置,以一种固定的格式书写,而在函数组件中书写方式变得更简单,我们可以像写函数一样写组件。更重要的是,这样就不用关心那些难理解的this了。

const Comp = () => {  const [greeting, setGreeting] = useState('hello');    return (    <div>      <div>{ greeting } world</div>    </div>  )}

官方文档[2]


双向绑定 VS 单向数据流


在Vue中我们使用v-bind、v-modal对数据进行绑定,无论是来自用户操作导致的变更,还是在某个方法里赋值都能够直接更新数据,不需要手动进行update操作。
this.data.greeting = "Hello"

而在React里需要调用set方法更新,当React感应到set触发时会再次调用render对dom进行刷新,虽然有些麻烦但这种方式可以让数据变化更加清晰易追寻。

this.state.greeting = "Hello" // 错误写法
this.setState({greeting: 'Hello'}); // 正确写法✅setGreeting('Hello'); // 来自hooks的set写法 后面会介绍


React的大buff:JSX


初次接触JSX的开发者可能会感觉JSX结构混乱,因为你可以在dom元素内部直接写js逻辑,也可以在js逻辑里写dom,这就像把html和js混在一起:
import getUserType from './getUserType'
const Comp = () => { const [greeting, setGreeting] = useState('hello'); const Button = () => { const userType = getUserType() if(userType === 0) { return <button>去购买</button> } if(userType === 1) { return <button>去充值</button> } return null } return ( <div> <div>{ greeting } world</div> {Button()} </div> )}

虽然元素和逻辑的边界模糊了,但我们的组件会变得更加灵活,这样能够将一个组件分成不同的模块,当需要修改是时我们只需关注对应的函数,不用担心影响到其他部分,这对复杂的页面结构非常有用。

Hooks

是什么

上面我们在讲数据流的时候有提到,处理数据的两种方式
// 方式1this.state = {greeting: 'Hello'}this.setState({greeting: 'Hello'}); 
// 方式2const [greeting, setGreeting] = useState('hello');setGreeting('Hello');

其中第二种方式的useState就是Hooks中的一种,是比较常用的Hook,除此之外还有useEffect,useRef等,每个都有着不同的功能。


为什么用

逻辑独立
以数据更新为例,简单来讲,如果不用Hooks,每次更新数据都用setSate,我们的代码里就会出现很多setState调用,setState根据入参可以一次修改一个字段,也可以一次修改多个字段,想要知道某个数据在哪里被做了怎样的修改就会变的很麻烦,甚至可能不小心多写一个字段修改了不该修改的数据。而用Hooks的useState的话,因为它在定义时会对字段创建其专用的修改函数,所以只要有这个函数的调用,就代表这个字段被做了修改。

怎么用

常用Hooks(Hooks只能在的函数组件内使用):
1、useState: 用于定义组件的 State,相当于this.state=xxx或者Vue里的data(){return xxx}
const [greeting, setGreeting] = useState('hello'); // greeting 默认 hello
// 点击greeting变为Hello1<div onClick={setGreeting('Hello1')}>{greeting}</div>

2、useEffect:通过依赖变更触发的钩子函数 ,类似Vue的watcher

// 当userId变化时调用refreshuseEffect(() => {  refresh();}, [userId]);
// 进入页面时会执行init, 退出时会执行destroyuseEffect(() => { init(); return () => { destroy() }}, []);

3、useRef: 返回ref对象,.current可以获取到其原生的元素本身

const el = useRef(null);
<div ref={el}></div>
// console.log(el.current.offsetHeight) 返回div的offsetHeight

状态管理

是什么?为什么用?

官方定义:“集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化”。
举个例子,页面里两个组件需要展示/更新userName,如果不使用状态管理,我们可以用父子组件交互的方式把userName字段和setUserName函数作为组件参数传入两个组件中,调用setUserName即触发page更新userName:

但当业务变得越来越复杂,就会陷入透传地狱!


加入状态管理后,不在涉及组件之间的参数传递,数据管理全部放在Store中管理,组件直接从Store中读取数据,修改时调用Store的修改函数修改对应数据:

怎么用

Vue:Vuex
在Vue中,官方脚手架自带Vuex为我们注入好了Store,常用的state负责定义数据,mutations负责修改数据,actions负责利用mutations做一些复杂异步操作(如接口请求)
// store.jsimport { createStore } from 'vuex'const store = createStore({  state: {    count: 0  },  mutations: {    setCount (state, value) {      state.count = value    }  },  actions: {    addon ({ commit, state }) {      const count = state.count      commit('set', count+1)    }  }})
// index.jsimport App from './vue'import { createApp } from 'vue'
const app = createApp(App).mount('#app');
// 将 store 实例作为插件安装app.use(store)
// index.vue<template> <div>{{ this.$store.state.count }} world</div></template>
<script> export default { methods: { increment() { this.$store.commit('setCount', 10) this.$store.dispatch('setCount') console.log(this.$store.state.count) } } }</script><style></style>
React:不止是Redux
React本身并不带状态管理,状态管理对于React更像是一种普通的第三方工具,工作中不同项目可能用了Redux、mobx、rematch等不同的状态管理工具,不同工具写法会有所区别,使用者要自己区分学习,除此之外一些脚手架会自带状态管理,写法会简单些,比如Rax,为方便理解接下来以Rax的写法进行说明。

与上面所说的Vuex的state、mutations、actions对应,React里叫做state、reducers、effectsstate负责定义数据,reducers负责修改数据,effects负责利用reducers做一些复杂异步操作,下面示例解释的更清楚:
// src/pages/Dashboard/models/counter.tsconst delay = (time) => new Promise((resolve) => setTimeout(() => resolve(), time));
export default { // 定义 model 的初始 state state: { count: 0 }, // 定义改变该模型状态的纯函数 reducers: { increment(prevState) { return { count: prevState.count + 1 }; }, }, effects: (dispatch) => ({ async incrementAsync() { await delay(10); dispatch.counter.increment(); }, }),}
// src/pages/Dashboard/store.tsimport { createStore } from 'rax-app';import counter from './models/counter';
const store = createStore({ counter });
export default function Dashboard() { // 使用 counter 模型 const [counterState, counterDispatchers] = store.useModel('counter');
return ( <> <span>{counterState.count}</span> <button onClick={counterDispatchers.increment}>+</button> <button onClick={counterDispatchers.incrementAsync}>+</button> </> );}

React代码实战:开发一个TodoList

// index.jsximport $i18n from '@alife/panda-i18n';import React, { useCallback } from 'react';import { connect } from 'react-redux';import { Link } from '@ice/router';import PropTypes from 'prop-types';import { Form, Input } from 'cn-next';import styles from './index.module.scss';
const FormItem = Form.Item;
const AddTodo = (props) => { const { onAdd } = props; const onSubmit = useCallback( (values, errors) => { if (!errors) { onAdd(values.text); } }, [onAdd], );
return ( <div x-class={[styles.add]}> <Form className={styles.addForm} inline onSubmit={onSubmit}> <FormItem className={styles.addItem} required requiredMessage={$i18n.get({ id: 'EnterAToDoList.other', dm: '请输入待办事项', })} > <Input name='text' placeholder={$i18n.get({ id: 'EnterAToDoList.other', dm: '请输入待办事项', })} /> </FormItem> <Form.Submit className={styles.addSubmit} onClick={onSubmit} validate> {$i18n.get({ id: 'Add.other', dm: '添加' })} </Form.Submit> </Form> </div> );};
AddTodo.propTypes = { onAdd: PropTypes.func,};
AddTodo.defaultProps = { onAdd: () => {},};
const Todos = (props) => { const { list, createAsync } = props;
// 添加 const onAddTodo = useCallback( async (text) => { await createAsync(text); }, [createAsync], );
return ( <div className={styles.todos}> <AddTodo onAdd={onAddTodo} /> <div className='mb-30'> {list.map((item) => { return ( <div key={item.text} className={styles.todo}> <span>{item.text}</span> </div> ); })} </div> <div> <Link to='/'> {$i18n.get({ id: 'ReturnToHomePage.other', dm: '返回首页' })} </Link> </div> </div> );};
Todos.propTypes = { list: PropTypes.arrayOf(PropTypes.shape({})).isRequired, createAsync: PropTypes.func.isRequired,};
const mapState = (state) => ({ list: state.todos.list,});
const mapDispatch = (dispatch) => ({ createAsync: dispatch.todos.createAsync, doneAsync: dispatch.todos.doneAsync, undoneAsync: dispatch.todos.undoneAsync,});
export default connect(mapState, mapDispatch)(Todos);
// todo.jsexport const todos = {  state: {    list: [      {        text: 'Learn typescript',        done: true,      },      {        text: 'Try immer',        done: false,      },    ],  },  reducers: {    create(state, text) {      state.list.push({ text, done: false });      return state;    },    done(state, idx) {      if (state.list[idx]) {        state.list[idx].done = true;      }      return state;    },    undone(state, idx) {      if (state.list[idx]) {        state.list[idx].done = false;      }      return state;    },  },  effects: (dispatch) => ({    async createAsync(payload) {      // 模拟异步操作      await new Promise((resolve) => setTimeout(resolve, 250));      dispatch.todos.create(payload);    },    async doneAsync(payload) {      // 模拟异步操作      await new Promise((resolve) => setTimeout(resolve, 250));      dispatch.todos.done(payload);    },    async undoneAsync(payload) {      // 模拟异步操作      await new Promise((resolve) => setTimeout(resolve, 250));      dispatch.todos.undone(payload);    },  }),};


参考链接:

[1]https://zh-hans.reactjs.org/docs/components-and-props.html#function-and-class-components

[2]https://zh-hans.reactjs.org/docs/components-and-props.html#function-and-class-components



寻找百位乘风者伯乐!邀好友入驻可享多重好礼


即日起邀请身边的朋友、同事一起加入乘风者计划并成为阿里云开发者社区的新入驻博主,邀请人数前100位即可获得乘风者伯乐奖,您将有机会获得Redmi Note 11 5G手机、保温杯、鼠标等奖品。同时您所邀约的博主将有机会获得乘风者权益。


点击阅读原文查看详情。

微信扫码关注该文公众号作者

戳这里提交新闻线索和高质量文章给我们。
相关阅读
Nike搬离西雅图,即将在Bellevue Square 开设东区第一家店!就在Microsoft Store原址今天 Bellevue Square 庆祝农历新年,赏舞龙舞狮、武术歌舞乐器表演,看工艺品还有新年红包Bellevue轻轨出没请小心!底座缺陷、先开路线之争,一波三折的东线终于恢复测试Gucci罕见全品牌9折!VUE电影票£3.8起!React官方网站更新,并启用新域名:react.dev老了也要玩得大今年这个雨水充沛的春节, 我只想冲到Redmond领这100碗的免费拉面!日本啊,日本(十三)利休之死GluteFree到底是什么?洛杉矶8家必去无麸质餐厅。少吃真的可以减重外加睡得好吗?诈骗警报!仅今年一月Bellevue就报告五起案件,被骗金额超过460万美金工作、出游、开黑间的无缝切换,这是“网速自由”的终极意义生源不足 Bellevue提议将合并部分小学AIGC基础:从VAE到DDPM原理、代码详解1亿美金ARR之后,从协同Office到业务三件套,飞书要如何“黏住”To B客户?微软裁员5%,3月20日Puget Sound地区裁878人;不续租Bellevue市中心大楼,专卖店改NikeRedmond,Bellevue,Sammamish,Issaquah数十起入室洗劫,华人受害者亲述经历,警方提醒!从v8到v9,Arm服务器发展之路“二十八画生”与“二十一回猛士”为什么选这里?Bellevue将建无家可归者安全停车试点,预计第三季度启动,年投45万从ClickHouse到StarRocks,易点天下数仓平台建设微软宣布5.26再裁员, 影响Redmond、Bellevue、Issaquah 多少员工?Bellevue 124th Avenue NE施工警报,禁止双向停车,施工影响和绕行路线都在这备受 Vue、Angular 和 React 青睐的 Signals 演进史"𝙇𝙚𝙖𝙙 𝙏𝙝𝙚 𝘾𝙝𝙖𝙧𝙜𝙚"广告#创译挑战尤雨溪Vue 2022回顾及2023展望0基础留学生转码必备:LeetCode到底该怎么速通?Bellevue学区计划合并3所小学!7场公开听证会本周举行题型再变!大摩、美银Hirevue逼疯留学生曝光!五大行潜规则:那些拿到Return Offer的实习生,Excel要达到什么水平?8 个很棒的 Vue 开发技巧香港内地无缝切换!丝滑过关还得靠这个!名企面试进度曝光!春招来了,Hirevue、OA全都回来了!我们家的炒股历程中国标准证明美国新冠只死了6万董车日报 | 兰博基尼推出新一代大牛 Revuelto / 保时捷卡宴新内饰公布
logo
联系我们隐私协议©2024 redian.news
Redian新闻
Redian.news刊载任何文章,不代表同意其说法或描述,仅为提供更多信息,也不构成任何建议。文章信息的合法性及真实性由其作者负责,与Redian.news及其运营公司无关。欢迎投稿,如发现稿件侵权,或作者不愿在本网发表文章,请版权拥有者通知本网处理。