网站首页 > 技术文章 正文
作者:杜尼卜
转发链接:https://segmentfault.com/a/1190000022531144
前言
React.js和Vue.js都是很好的框架。而且Next.js和Nuxt.js甚至将它们带入了一个新的高度,这有助于我们以更少的配置和更好的可维护性来创建应用程序。但是,如果你必须经常在框架之间切换,在深入探讨另一个框架之后,你可能会轻易忘记另一个框架中的语法。在本文中,我总结了这些框架的基本语法和方案,然后并排列出。我希望这可以帮助我们尽快掌握语法,不过限于篇幅,这篇文章只比较React.js和Vue.js,下一篇再谈Next.js个Nuxt.js。
Github:https://github.com/oahehc/react-vue-comparison
Render
React.js
ReactDOM.render(<App />, document.getElementById("root"));Vue.js
new Vue({
  render: (h) => h(App),
}).$mount("#root");基本组件
React.js
Class component
class MyReactComponent extends React.Component {
  render() {
    return <h1>Hello world</h1>;
  }
}Function component
function MyReactComponent() {
  return <h1>Hello world</h1>;
}Vue.js
<template>
  <h1>Hello World</h1>
</template>
<script>
  export default {
    name: "MyVueComponent",
  };
</script>Prop
React.js
function MyReactComponent(props) {
  const { name, mark } = props;
  return <h1>Hello {name}{mark}</h1>;
}
MyReactComponent.propTypes = {
  name: PropTypes.string.isRequired,
  mark: PropTypes.string,
}
MyReactComponent.defaultProps = {
  mark: '!',
}
...
<MyReactComponent name="world">Vue.js
<template>
  <h1>Hello {{ name }}</h1>
</template>
<script>
  export default {
    name: "MyVueComponent",
    props: {
      name: {
        type: String,
        required: true,
      },
      mark: {
        type: String,
        default: "!",
      },
    },
  };
</script>
...
<MyVueComponent name="World" />事件绑定
React.js
Class component
class MyReactComponent extends React.Component {
  save = () => {
    console.log("save");
  };
  render() {
    return <button onClick={this.save}>Save</button>;
  }
}Function component
function MyReactComponent() {
  const save = () => {
    console.log("save");
  };
  return <button onClick={save}>Save</button>;
}Vue.js
<template>
  <button @click="save()">Save</button>
</template>
<script>
  export default {
    methods: {
      save() {
        console.log("save");
      },
    },
  };
</script>自定义事件
React.js
function MyItem({ item, handleDelete }) {
  return <button onClick={() => handleDelete(item)}>{item.name}</button>;
  /*
   * 应用useCallback钩子来防止在每次渲染时生成新的函数。
   *
   * const handleClick = useCallback(() => handleDelete(item), [item, handleDelete]);
   *
   * return <button onClick={handleClick}>{item.name}</button>;
  */
}
...
function App() {
  const handleDelete = () => { ... }
  return <MyItem item={...} handleDelete={handleDelete} />
}Vue.js
<template>
  <button @click="deleteItem()">{{item.name}}</button>
</template>
<script>
  export default {
    name: "my-item",
    props: {
      item: Object,
    },
    methods: {
      deleteItem() {
        this.$emit("delete", this.item);
      },
    },
  };
</script>
...
<template>
  <MyItem :item="item" @delete="handleDelete" />
</template>
<script>
  export default {
    components: {
      MyItem,
    },
    methods: {
      handleDelete(item) { ... }
    },
  };
</script>State
React.js
Class component
class MyReactComponent extends React.Component {
  state = {
    name: 'world,
  }
  render() {
    return <h1>Hello { this.state.name }</h1>;
  }
}Function component
function MyReactComponent() {
  const [name, setName] = useState("world");
  return <h1>Hello {name}</h1>;
}Vue.js
<template>
  <h1>Hello {{ name }}</h1>
  <!-- 使用组件状态作为prop -->
  <my-vue-component :name="name">
</template>
<script>
  export default {
    data() {
      return { name: "world" };
    },
  };
</script>Change-State
React.js
Class component
class MyReactComponent extends React.Component {
  state = {
    count: 0,
  };
  increaseCount = () => {
    this.setState({ count: this.state.count + 1 });
    // 在更新之前获取当前状态,以确保我们没有使用陈旧的值
    // this.setState(currentState => ({ count: currentState.count + 1 }));
  };
  render() {
    return (
      <div>
        <span>{this.state.count}</span>
        <button onClick={this.increaseCount}>Add</button>
      </div>
    );
  }
}Function component
function MyReactComponent() {
  const [count, setCount] = useState(0);
  const increaseCount = () => {
    setCount(count + 1);
    // setCount(currentCount => currentCount + 1);
  };
  return (
    <div>
      <span>{count}</span>
      <button onClick={increaseCount}>Add</button>
    </div>
  );
}Vue.js
<template>
  <div>
    <span>{{count}}</span>
    <button @click="increaseCount()">Add</button>
  </div>
</template>
<script>
  export default {
    data() {
      return { count: 0 };
    },
    methods: {
      increaseCount() {
        this.count = this.count + 1;
      },
    },
  };
</script>双向绑定 (仅Vue.js)
React.js
React没有双向绑定,因此我们需要自己处理数据流
function MyReactComponent() {
  const [content, setContent] = useState("");
  return (
    <input
      type="text"
      value={content}
      onChange={(e) => setContent(e.target.value)}
    />
  );
}Vue.js
<template>
  <input type="text" v-model="content" />
</template>
<script>
  export default {
    data() {
      return { content: "" };
    },
  };
</script>计算属性
React.js
React.js没有计算属性,但我们可以通过react hook轻松实现
function DisplayName({ firstName, lastName }) {
  const displayName = useMemo(() => {
    return `${firstName} ${lastName}`;
  }, [firstName, lastName]);
  return <div>{displayName}</div>;
}
...
<DisplayName firstName="Hello" lastName="World" />Vue.js
<template>
  <div>{{displayName}}</div>
</template>
<script>
  export default {
    name: "display-name",
    props: {
      firstName: String,
      lastName: String,
    },
    computed: {
      displayName: function () {
        return `${this.firstName} ${this.lastName}`;
      },
    },
  };
</script>
...
<DisplayName firstName="Hello" lastName="World" />Watch
React.js
React.js没有 watch 属性,但是我们可以通过react hook轻松实现
function MyReactComponent() {
  const [count, setCount] = useState(0);
  const increaseCount = () => {
    setCount((currentCount) => currentCount + 1);
  };
  useEffect(() => {
    localStorage.setItem("my_count", newCount);
  }, [count]);
  return (
    <div>
      <span>{count}</span>
      <button onClick={increaseCount}>Add</button>
    </div>
  );
}Vue.js
<template>
  <div>
    <span>{{count}}</span>
    <button @click="increaseCount()">Add</button>
  </div>
</template>
<script>
  export default {
    data() {
      return { count: 0 };
    },
    methods: {
      increaseCount() {
        this.count = this.count + 1;
      },
    },
    watch: {
      count: function (newCount, oldCount) {
        localStorage.setItem("my_count", newCount);
      },
    },
  };
</script>Children-and-Slot
React.js
function MyReactComponent({ children }) {
  return <div>{children}</div>;
}
...
<MyReactComponent>Hello World</MyReactComponent>Vue.js
<template>
  <div>
    <slot />
  </div>
</template>
<script>
  export default {
    name: "my-vue-component",
  };
</script>
...
<MyVueComponent>Hello World</MyVueComponent>渲染HTML
React.js
function MyReactComponent() {
  return <div dangerouslySetInnerHTML={{ __html: "<pre>...</pre>" }} />;
}Vue.js
<template>
  <div v-html="html"></div>
</template>
<script>
  export default {
    data() {
      return {
        html: "<pre>...</pre>",
      };
    },
  };
</script>条件渲染
React.js
function MyReactComponent() {
  const [isLoading, setLoading] = useState(true);
  return (
    <div>
      {isLoading && <span>Loading...</span>}
      {isLoading ? <div>is loading</div> : <div>is loaded</div>}
    </div>
  );
}Vue.js
<template>
  <div>
    <!--v-show: 总是渲染,但根据条件更改CSS-->
    <span v-show="loading">Loading...</span>
    <div>
      <div v-if="loading">is loading</div>
      <div v-else>is loaded</div>
    </div>
  </div>
</template>
<script>
  export default {
    data() {
      return { loading: true };
    },
  };
</script>列表渲染
React.js
function MyReactComponent({ items }) {
  return (
    <ul>
      {items.map((item) => (
        <li key={item.id}>
          {item.name}: {item.desc}
        </li>
      ))}
    </ul>
  );
}Vue.js
<template>
  <ul>
    <li v-for="item in items" :key="item.id">
      {{item.name}}: {{item.desc}}
    </li>
  </ul>
</template>
<script>
  export default {
    props: {
      items: Array,
    },
  };
</script>Render-Props
React.js
function Modal({children, isOpen}) {
  const [isModalOpen, toggleModalOpen] = useState(isOpen);
  return (
    <div className={isModalOpen ? 'open' : 'close'}>
      {type children === 'function' ? children(toggleModalOpen) : children}
    </div>)
  ;
}
Modal.propTypes = {
  isOpen: PropTypes.bool,
  children: PropTypes.oneOfType([PropTypes.string, PropTypes.element]).isRequired,
}
Modal.defaultProps = {
  isOpen: false,
}
...
<Modal isOpen>
  {(toggleModalOpen) => {
    <div>
      <div>...</div>
      <button onClick={() => toggleModalOpen(false)}>Cancel</button>
    </div>
  }}
</Modal>Vue.js(slot)
<template>
  <div v-show="isModalOpen">
    <slot v-bind:toggleModal="toggleModalOpen" />
  </div>
</template>
<script>
  export default {
    name: "modal",
    props: {
      isOpen: {
        type: Boolean,
        default: false,
      },
    },
    data() {
      return {
        isModalOpen: this.isOpen,
      };
    },
    methods: {
      toggleModalOpen(state) {
        this.isModalOpen = state;
      },
    },
  };
</script>
...
<Modal isOpen>
  <template v-slot="slotProps">
    <div>...</div>
    <button @click="slotProps.toggleModal(false)">Close</button>
  </template>
</Modal>生命周期
React.js
Class component
class MyReactComponent extends React.Component {
  static getDerivedStateFromProps(props, state) {}
  componentDidMount() {}
  shouldComponentUpdate(nextProps, nextState) {}
  getSnapshotBeforeUpdate(prevProps, prevState) {}
  componentDidUpdate(prevProps, prevState) {}
  componentWillUnmount() {}
  render() {
    return <div>Hello World</div>;
  }
}Function component
function MyReactComponent() {
  // componentDidMount
  useEffect(() => {}, []);
  // componentDidUpdate + componentDidMount
  useEffect(() => {});
  // componentWillUnmount
  useEffect(() => {
    return () => {...}
  }, []);
  // 在渲染之后但在屏幕更新之前同步运行
  useLayoutEffect(() => {}, []);
  return <div>Hello World</div>;
}Vue.js
<template>
  <div>Hello World</div>
</template>
<script>
  export default {
    beforeCreate() {},
    created() {},
    beforeMount() {},
    mounted() {},
    beforeUpdate() {},
    updated() {},
    beforeDestroy() {},
    destroyed() {},
  };
</script>错误处理
React.js
class ErrorBoundary extends React.Component {
  state = { hasError: false };
  static getDerivedStateFromError(error) {
    // 更新状态,这样下一个渲染将显示回退UI。
    return { hasError: true };
  }
  componentDidCatch(error, errorInfo) {}
  render() {
    if (this.state.hasError) return <h1>Something went wrong.</h1>;
    return this.props.children;
  }
}
...
<ErrorBoundary>
  <App />
</ErrorBoundary>Vue.js
const vm = new Vue({
  data: {
    error: "",
  },
  errorCaptured: function(err, component, details) {
    error = err.toString();
  }
}Ref
React.js
Class component
class AutofocusInput extends React.Component {
  constructor(props) {
    super(props);
    this.ref = React.createRef();
  }
  state = {
    content: "",
  };
  componentDidMount() {
    this.ref.current.focus();
  }
  setContent = (e) => {
    this.setState({ content: e.target.value });
  };
  render() {
    return (
      <input
        ref={this.ref}
        type="text"
        value={this.state.content}
        onChange={this.setContent}
      />
    );
  }
}Function component
function AutofocusInput() {
  const [content, setContent] = useState("");
  const ref = useRef(null);
  useEffect(() => {
    if (ref && ref.current) {
      ref.current.focus();
    }
  }, []);
  return (
    <input
      ref={ref}
      type="text"
      value={content}
      onChange={(e) => setContent(e.target.value)}
    />
  );
}Vue.js
<template>
  <input ref="input" type="text" v-model="content" />
</template>
<script>
  export default {
    name: "autofocus-input",
    data() {
      return { content: "" };
    },
    mounted() {
      this.$refs.input.focus();
    },
  };
</script>性能优化
React.js
PureComponent
class MyReactComponent extends React.PureComponent {
  ...
}shouldComponentUpdate
class MyReactComponent extends React.Component {
  shouldComponentUpdate(nextProps) {...}
  ...
}React.memo
export default React.memo(
  MyReactComponent,
  (prevProps, nextProps) => {
    ...
  }
);useMemo
export default function MyReactComponent() {
  return React.useMemo(() => {
    return <div>...</div>;
  }, []);
}useCallback
function MyItem({ item, handleDelete }) {
  const handleClick = useCallback(() => handleDelete(item), [
    item,
    handleDelete,
  ]);
  return <button onClick={handleClick}>{item.name}</button>;
}Vue.js
v:once
<span v-once>This will never change: {{msg}}</span>函数式组件:我们可以将组件标记为 functional,这意味它无状态 (没有响应式数据),也没有实例 (没有 this 上下文)。
<template functional>
  <h1>Hello {{ name }}</h1>
</template>
<script>
  export default {
    name: "MyVueComponent",
    props: {
      name: String,
    },
  };
</script>keep-alive 组件
<keep-alive>
  <component :is="view"></component>
</keep-alive>推荐React和Vue学习资料文章:
《深入浅出通过vue-cli3构建一个SSR应用程序【实践】》
《聊聊昨晚尤雨溪现场针对Vue3.0 Beta版本新特性知识点汇总》
《【新消息】Vue 3.0 Beta 版本发布,你还学的动么?》
《Vue + Koa从零打造一个H5页面可视化编辑器——Quark-h5》
《深入浅出Vue3 跟着尤雨溪学 TypeScript 之 Ref 【实践】》
《手把手教你深入浅出vue-cli3升级vue-cli4的方法》
《Vue 3.0 Beta 和React 开发者分别杠上了》
《手把手教你用vue drag chart 实现一个可以拖动 / 缩放的图表组件》
《Vue3 尝鲜》
《手把手让你成为更好的Vue.js开发人员的12个技巧和窍门【实践】》
《2020 年,Vue 受欢迎程度是否会超过 React?》
《手把手教你Vue解析pdf(base64)转图片【实践】》
《手把手教你Vue之父子组件间通信实践讲解【props、$ref 、$emit】》
《深入浅出Vue3 的响应式和以前的区别到底在哪里?【实践】》
《干货满满!如何优雅简洁地实现时钟翻牌器(支持JS/Vue/React)》
《基于Vue/VueRouter/Vuex/Axios登录路由和接口级拦截原理与实现》
《手把手教你D3.js 实现数据可视化极速上手到Vue应用》
《吃透 Vue 项目开发实践|16个方面深入前端工程化开发技巧【上】》
《吃透 Vue 项目开发实践|16个方面深入前端工程化开发技巧【中】》
《吃透 Vue 项目开发实践|16个方面深入前端工程化开发技巧【下】》
React
《Vue 3.0 Beta 和React 开发者分别杠上了》
《手把手深入Redux react-redux中间件设计及原理(上)【实践】》
《手把手深入Redux react-redux中间件设计及原理(下)【实践】》
《为了学好 React Hooks, 我解析了 Vue Composition API》
《【React 高级进阶】探索 store 设计、从零实现 react-redux》
《深入浅出掌握React 与 React Native这两个框架》
《你需要的 React + TypeScript 50 条规范和经验》
《手把手教你深入浅出实现Vue3 & React Hooks新UI Modal弹窗》
《全平台(Vue/React/微信小程序)任意角度旋图片裁剪组件》
作者:杜尼卜
转发链接:https://segmentfault.com/a/1190000022531144
猜你喜欢
- 2024-10-25 文档在线预览新版(四)使用js前端组件实现文档在线预览
- 2024-10-25 细聊Vue 3 系列之 JSX 语法(vue中jsx语法)
- 2024-10-25 带你了解 vue-next(Vue 3.0)之 初入茅庐【实践】
- 2024-10-25 10个Vue开发技巧「实践」(vue开发视频教程)
- 2024-10-25 Python开发打印服务(2)(python打印代码)
- 2024-10-25 Vue.js 项目实践——创建记忆卡片游戏
- 2024-10-25 让Jenkins自动部署你的Vue项目「实践」
- 2024-10-25 Java实战系列-前端VUE代码开发及接口流程设计(1)
- 2024-10-25 Vue高性能渲染大数据Tree组件「实践」
- 2024-10-25 轻量级Vue图片上传插件——Vue-core-image-Upload
- 最近发表
- 
- 聊一下 gRPC 的 C++ 异步编程_grpc 异步流模式
- [原创首发]安全日志管理中心实战(3)——开源NIDS之suricata部署
- 超详细手把手搭建在ubuntu系统的FFmpeg环境
- Nginx运维之路(Docker多段构建新版本并增加第三方模
- 92.1K小星星,一款开源免费的远程桌面,让你告别付费远程控制!
- Go 人脸识别教程_piwigo人脸识别
- 安卓手机安装Termux——搭建移动服务器
- ubuntu 安装开发环境(c/c++ 15)_ubuntu安装c++编译器
- Rust开发环境搭建指南:从安装到镜像配置的零坑实践
- Windows系统安装VirtualBox构造本地Linux开发环境
 
- 标签列表
- 
- cmd/c (90)
- c++中::是什么意思 (84)
- 标签用于 (71)
- 主键只能有一个吗 (77)
- c#console.writeline不显示 (95)
- pythoncase语句 (88)
- es6includes (74)
- sqlset (76)
- apt-getinstall-y (100)
- node_modules怎么生成 (87)
- chromepost (71)
- flexdirection (73)
- c++int转char (80)
- mysqlany_value (79)
- static函数和普通函数 (84)
- el-date-picker开始日期早于结束日期 (76)
- js判断是否是json字符串 (75)
- c语言min函数头文件 (77)
- asynccallback (87)
- localstorage.removeitem (77)
- vector线程安全吗 (73)
- java (73)
- js数组插入 (83)
- mac安装java (72)
- 无效的列索引 (74)
 
