Vue2和Reatc对比

1 篇文章 0 订阅

1、安装

React

npn i react react-dom

Vue

npm install vue

2、脚手架

React

// 1、安装脚手架
npm install create-react-app
// 2、创建demo项目
npm create-react-app myApp

Vue

// 1、安装脚手架
npn install -g @vue/cli
// 2、创建demo项目
vue create myApp

3、实例

React

import React from "React";
import ReactDom from "React-dom/client"; //将虚拟dom渲染到文档中变成真实dom
import App from "./App.tsx";
const Root = ReactDom.createRoot(
	document.querySelector("#root") as HTMLElement // tsx element 需要写类型
);
Root.render(
	<React.StrictMode>
		{""}
		// 严格模式只在开发环境生效 不会渲染可见UI
		<App />
	</React.StrictMode>
);
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <div id="root"></div>
  </body>
</html>

tips:
StrictMode 目前有助于:

  1. 识别不安全的生命周期
  2. 关于使用过时字符串 ref API 的警告
  3. 关于使用废弃的 findDOMNode 方法的警告
  4. 检测意外的副作用
  5. 检测过时的 context API
  6. 确保可复用的状态

Vue

import Vue from "Vue";
import App from "./App.js"
new Vue({
	el: "#app",
	render: (h) => h(App)
})
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <div id="app"></div>
  </body>
</html>

4、常见的UI库搭档

场景前端库UI库
PCReactAnt-Design
PCVueElement-UI、Iview
MobileReactMaterial-UI、Antd-Mobile
MobileVueVant
MobileVueVant
多端ReactTaro
多端Vueuni-app、mpvue

5、生命周期

React
分为三个阶段:挂载阶段、更新阶段、销毁阶段
常见的生命周期钩子函数:
挂载阶段:
render 适用于 class 组件(检查 props 和 state)
constructor 适用于 class 组件 (组件挂载之前会调用他的构造函数,初始化 state 在这里)
componentDidMount (初始页面依赖的数据 ajax 请求放在这里)
更新阶段:
shouldComponentUpdate
componentDidUpate
卸载阶段:
componentWillUnmount (在这里取消时间监听)

tips:
函数组件中,提供了useEffect副作用钩子,让开发者只用关注状态,React负责同步到DOM。

  1. useEffect 相当于 componentDidMount、componentDidUpdate、componentWillUnMount三个生命周期钩子的合并方法;
  2. 模拟componentDidMountuseEffech(fn,[])的第二个参数传入空数组,相当于componentDidMount
  3. 模拟componentDidUpdateuseEffect(fn,[dep])的第二个参数数组中传入依赖参数,相当于componentDidUpdate
  4. 模拟 componentWillUnMountuseEffect(()=>{return callback},[])的第一个参数函数返回的callback函数会在组件卸载前被调用,相当于componentWillUnMount

Vue
分为四个阶段:创建、挂载、更新、卸载

常见的生命周期钩子函数:
创建阶段:
beforeCreate
created
挂载阶段:
beforeMount
Mounted(初始页面依赖的数据ajax请求放在这里)
更新阶段:
beforeUpdate
updated
销毁阶段:
beforeDestroy(在这里取消时间监听)
destroied

6、模板语法

React

jsx语法

1.文本

const Demo = () => {
	rerurn <div>{message}</div>
}

2.html-innerHTML
使用富文本编辑器生产的数据,回到页面时也要按html来展示,React中没有像Vue一样的v-html指令,但提供了dangerouslySetInnerHTML 属性

function MyComponent(){
	const html = "<div>innerHTML</div>"
	return <div dangerouslySetInnerHTML={{ _html: html }} />
}

注意这里的用法比较特殊
3.style
style 接收一个对象,width 和 height 的单位px可以省略
style 多用于添加动态样式 不推荐直接将 css 属性写在里面 可读性差
style 中的 key 使用小驼峰命名法

const Demo = () => {
	return <div style={{ width: 100, fontSize:"16px" }}></div>
}

4.属性

const Demo = () => {
	return <div loading={loading}></div>
}

5.js 表达式
jsx 中可以书写任意表达式,甚至可以使用map:

const Demo = () => {
	let flag = true;
	return <div>{ flag ? message : "暂无数据" }</div>
}

Vue

基于HTML的模板语法
1.文本
Mustache 双大括号语法
数据绑定放在 双大括号 里面

<template>
	<div>{{ message }}</div>
</template>

2.html

<template>
	<div v-html="html"></div>
</template>
<script>
export default {
	data(){
		return {
			html: `<div>v-html directive</div>`
		}
	}
}	
</script>

3.style

<template>
	<div style="width:100px">{{message}}</div>
	<div :style="{width:100px}"></div>
</template>

4.属性
v-bind 可缩写为冒号“:”

<template>
	<div v-bind:loading="loading"></div>
</template>
  1. js表达式
    Vue的模板语法中,只能包含单个表达式:
<template>
	<div>{{ flag ? message : "暂无数据" }}</div>
</template>
<script>
export default {
	data(){
		return {
			flag: false,
		}
	}
}
</script>

6.动态参数

<template>
	<div v-bind[attributeName]="url">动态参数</div>
</template>

7.修饰符

  • .stop 阻止冒泡 相当于 event.stopPropagation
  • .prevent 阻止默认 相当于event.preventDefault()
  • .lazy 数据绑定放在change时间之后
  • .number 自动装换用户输入为number 类型
  • .trm 自动去除用户输入的头尾空白字符
  • .native 将原生事件绑定到组件
  • .sync 对一个prop进行“双向绑定”

tips:
Vue 也支持jsx语法,这里仅用模板语法做对比
Vue 中 template 标签内只能有一个根节点,每个组件必须只有一个根元素

7、计算属性

计算属性的常见使用场景是:
1.对复杂逻辑的抽象
2.缓存计算结果

React 中使用 useMemo Hook 实现 Vue 中的 computed 实现的功能

import { useMemo, useState } from "react";
import { Input } from "antd";
const demo = () => {
	const [ firstName, setFirstName ] = useState("");
	const [ secondName, setSecodName ] = useState("");
	
	const name = useMemo(() => {
		return firstName + "" + secondName
	}, [ firstName, secondName ])

	const handleFisrtNameChange = ( e: any ) => {
		setFirstName( e.target.value )
	}

	const handleSecondNameChange = ( e: any ) => {
		setSecondName( e.target.value )
	}
	
	return (
		<div>
			<div>hello {name}</div>
			<Input
				placeholder="first name"
				onChange={handleFisrtNameChange}
			></Input>
			<Input
				placeholder="second name"
				onChange={handleSecondNameChange}
			></Input>
		</div>
	)
}

tips:
useMemo 可以缓存计算结果或者缓存组件

Vue
Vue 提供 computed 实现计算属性

<template>
	<div>
		<!-- 模版当中应该是简单的声明式逻辑 复杂逻辑我们使用 computed -->
		<div>hello {{ name }}</div>
	</div>
</template>
<script>
export default{
	data(){
		return {
			firstName: "firstName",
			secondName: "secondName"
		}
	}
	computed:{
		name(){
			return this.fristName + "" + this.secondName
		}
	}
}
</script>

平时开发的时候很多同学的用法是:
1.兼容边界值

return data || [];

2.封装一个长的调用

return res.data.pageData.total;

感觉上面这两种用法都是不准确的,我想 computed 这个方法一方面其实应该是用来补充 Vue 模板语法的缺陷,在Vue的模板语法中只支持单个表达式,computed 封装一个函数正好解决了这个问题(排序或者过滤也是不错的使用场景);另一方面应该是考虑缓存的必要性,当在模板中多个地方使用的时候,应该缓存起来避免重复技算。
你可能会说函数也有抽象复杂逻辑的功能,为什么还要使用 computed,他们的不同点在于缓存结果。
computed 只有在依赖更新时才会重新计算。

8、侦听器

React
React 中只能使用 useEffect 自己封装一个侦听器,useEffect 本身其实就是一个侦听器

function useWatch( target,callback ){
	const oldValue = useRef(); // useRef 用来保存旧的值
	useEffect(() => {
		callback( target, oldValue.current );
		oldValue.current = target;
	},[target])
}

Vue
Vue中监听器一般用作监听 props 的变化、route的变化、state中的数据变化,当监听的数据变化的时候执行相应的依赖逻辑:

<script>
export default{
	props:["flag"]
	data(){
		return {};
	},
	watch:{
		flag(newVal,oldVal){
			// 监听到父组件传来的 flag 改变时 执行 initData 函数
			this.initData();
		}
	},
	methods:{
		initDate(){
			// do something
		}
	}
}
</script>

9、class

界面开发时常需要用到 class 属性来修改样式
React
React 绑定 class 使用 className 属性:

const Demo = () => {
	return (
		<div className="box">
			<i  className={isCollapse ? 'unfold' : 'fold'}></i>
		</div>
	)
}

Vue
Vue使用 :class 属性

<template>
  <div class="box">
    <i :class="[isCollapse ? 'el-icon-s-unfold' : 'el-icon-s-fold']"></i>
  </div>
</template>

10、条件渲染

React
React 中直接书写 js 实现条件渲染:

const Demo = () => {
	if(hasToken){
		return (
			<div>
				<div style={hasToken ? {display: "block"} : {display: "none"} }></div>
				登录成功
			</div>
		)
	}else{
		return <div>未登录</div>
	}
}

Vue
Vue 提供相关指令实现条件渲染

<template>
	<div>
		<div v-if="hasToken">
			<div v-show="hasToken">hello</div>
			登陆成功
		</div>
		<div v-else>未登录</div>
	</div>
</template>

tips:
1.v-if 和 v-show 的区别
v-if直接控制是否渲染;v-show 控制 display 属性,常用于频繁切换展示的情况,不同业务考虑性能以决定使用适合的指令
2.v-for 和 v-if 的优先级
v-for 的优先级高于 v-if,应当使用 v-if 嵌套v-for

11、列表渲染

出于性能考虑,Vue 和 React 在列表渲染时都需要为子组件提供 key
React
React中使用数组的map方法渲染列表:

const Demo = () => {
	const list = [1,2,3].map((item) => {
		return <div key={item.toString()}>{item}</div>
	})
	return <div>{list}</div>
}

tips:
一个元素的key最好是这个元素在列表中拥有的一个独一无二的字符串

Vue
Vue 当中使用 v-for渲染列表

<template>
  <div>
    <div v-for="item in list" :key="item">{{ item }}</div>
  </div>
</template>
<script>
  export default {
      data(){
          reutrn {
              list: [1,2,3]
          }
      }
  }
</script>

12、事件处理

React
React 中使用类似原生的写法,不过是采用驼峰命名:

import { Button } from "antd";
const Demo = () => {
	const handleClick = () => {
		console.log("click")
	};
	return (
		<div>
			<Button onClick={handleClick}></Button>
		</div>
	)
}

tips:
React 中阻止默认只能显示的执行:e.preventDefault

Vue
Vue 中使用v-on指令绑定事件,v-on可以缩写为 “@”

<template>
    <div>
        <el-button v-on:click="handleClick"><el-button>
    </div>
</template>
<script>
export default {
    data() {
        return {

        }
    },
    methods: {
        handleClick(){
            console.log('click')
        }
    }
}
</script>

tips:
Vue 中阻止默认可以在事件中处理也是可以使用修饰符:.prevent(阻止默认)、.stop(阻止冒泡)

13、获取DOM

虽然Vue和React已经封装了虚拟dom,但是在某种场景下,我们还是需要操作DOM元素
React
React 提供了操作DOM的方法:在class组件中使用creatRef,在函数组件使用useRef Hook

1.React.createRef
将引用自动通过组件传递到子组件,常用于可复用的组件库中

class MyComponet extends React.Component {
	construcotr(props){
		super(props);
		this.inputRef = React.createRef();
	}
	render(){
		return <input type="text" ref={ this.inputRef } />
	}
	compoentDidMount(){
		this.inputRef.current.focus()
	}
} 

2.useRef
useRef 返回一个可变的ref对象,其 .current 属性被初始化为传入的参数。返回的 ref 对象在组件的整个生命周期内持续存在。

function TextInputWithFocusButton() {
	const inputEl = useRef < HTMLInputElement > null;
	const onButtonClick = () => {
		// current 指定已挂载到 DOM 上的文件输入元素
		if( inputEl.current ){
			inputEl.current.focus();
		}
	};
	return (
		<div>
			<input ref={ inputEl } type="text" />
			<button onClick={ onButtonClick } >Focus the input</button>
		</div>
	)
}

Vue
Vue 中提供 $ref 方法访问 dom,我们只需要跟 Node 添加 ref 属性即可:

<template>
  <div>
    <el-table ref="userTable"></el-table>
  </div>
</template>
<script>
  export default {
    data() {
      return {};
    },
    mounted() {
      const userTable = this.$refs.userTable.$el; // ??? $el是怎么来的???
    },
  };
</script>

14、组件

注意:不管在Vue还是React中,自定义组件名都需要大写
React会将小写字母开头的组件视为原生的DOM标签Vue中的组件的name 可以使用短横线分割法或者首字母大写命名

React
1.函数组件

import { useState } from “react”
const Demo = ( props ) => {
	const [ data, setData ] = useState([]); // 函数组件中使用 useState 初始化 state
	useEffect(() => {}, []); // 函数组件中使用 useEffct 模拟部分生命周期钩子函数
	return <div>Demo</div>
}

2.class 组件

class Demo extends React.Component {
	constructor(props) {
		super(props);
		this.state = {
			data: {}
		}
	}
	// 生命周期钩子函数
	componentDidMount(){
		console.log(' did mount ')
	}
	render() {
		return <div>Demo</div>
	}
}

3.全局组件
由于全局组件很难推理,React中没有实现全局组件,需要时 import 即可

4.局部组件
函数组件和class组件文件都可以很方便的import以创建局部组件

Vue

1.全局注册组件
Vue.use 或者 Vue.component

<seript>
	import Vue from 'Vue';
	Vue.component("Demo", {
		data() {},
		template: "<div>Demo compoment</div>"
	});
	new Vue({
		// do something
	})
</seript>
import Vue from "Vue";
new Vue({
	components: {
		Demo: {
			data(){},
			template: "<div>template</div>"
		}
	}
})

2.局部注册组件
import.Vue 文件

import Demo from './Demo.vue'
export default{
	components:{
		Demo
	}
}

15、组件间传参数

React
1.父子组件传递状态
使用props:

import { useState } from "react"
import { Button } from "antd"

// 父组件

const Parent = () => {
	const [ msg, setMessage ] = useState("")
	const changeMsg = () => {
		setMessage("message")
	};
	return (
		<div>
			<Child msg={msg} changeMsg={changMsg}></Child>
		</div>
	)
}

// 子组件

const Child = (props) => {
	const handleClick = () => {
		this.props.ChangeMsg();
	}
	return (
		<div>
			<div>{props.msg}</div>
			<Button onClick={ handleClick }></Button>
		</div>
	)
}

2.跨级组件传递状态
使用 context

// 父组件

const Context = React.createContext(); // 测试这里的入参是不是必须的
const Parent = () => {
	return (
		<div>
			<Context.Provider
				value={{ name:"ya", age: 18 }}
			></Context.Provider>
		</div>
	)
}

// 子组件

const Child = () => {
	const value = useContext(Context);
	return <div>This is {value.name}</div>
}

3.EventBus
React 借助 event 库实现事假总线
不推荐使用

Vue
1.父子组件传参数
使用props

// 父组件

<template>
	<Demo :msg="msg" @confirm="headleConfirm></Demo>
</template>
<script>
export default {
	data(){
		return {
			msg: [{ id:1,content:"a" }, { id: 2 }, content: "b" ]
		}
	},
	methods:{
		headleConfirm(param){
			console.log("子传父的数据", param)
		}
	}
}
</script>

// 子组件

<template>
	<div>
		<ul>
			<li v-for="item in msg" :key="item.id">{{ item.content }}</li>
		</ul>
		<el-button @click="clickBtn"></el-button> 
	</div>
</template>
<script>
export default {
	name: "DEMO",
	props: ["msg"],
	data(){
		return {}
	},
	methods:{
		clickBtn{
			this.$emit("confirm",{ params:{} })
		}
	}
}
</script>

2.跨级组件传递参数
使用 Provide Inject 选项

父组件中:

<template>
	<Demo :msg="msg"></Demo>
</template>
<script>
export default {
	provide(){
		return {
			hadleClick: () => {
				// do something
			}
		}
	}
}
</script>

子组件中:

<template>
	<div>
		<el-button @click="clickBtn"></el-button> 
	</div>
</template>
<script>
export default {
	name: "DEMO",
	inject: ["clickBtn"]
	data(){
		return {}
	},
}
</script>

3.EventBus
不推荐使用

16、插槽

插槽的作用是扩展组件

React
React 中没有插槽的概念,要实现插槽使用 props.children

// 父组件

const Parent = () => {
	return (
		<div>
			<div>这个是父组件</div>
			<Child>
				<div>这个是父组件传给子组件的组件</div>
			</Child>
		<div>
	)
}

// 子组件

const Child = ( props ) => {
	return (
		<div>
			<div>我是子组件-header</div>
			<div>{propos.children}</div>
			<div>我是子组件-footer</div>
		</div>
	)
}

Vue
Vue 中插槽使用slot标签
1.匿名插槽(单个插槽)
父组件将一些组件传入到子组件当中
父组件

<template>
	<div>
		<ChildDemo>
			<div>插入的内容</div>
		</ChildDemo>
	</div>
</template>
<script>
import ChildDemo from "./components/ChildDemo.vue"
export default {
	name: 'App',
	components:{
		ChildDemo
	},
}
</script>

子组件

<template>
	<div>
		<slot>
			<p>默认内容</p>
		</slot>
	</div>
</template>
<script>
import ChildDemo from "./components/ChildDemo.vue"
export default {
	name: 'ChildDemo',
	data(){
		return {
			data: [ ],
		}
	}
}
</script>

2.具名插槽(多个插槽)
具名插槽可以从父组件将node 插入到指定位置,同事插入多个节点

父组件

<template>
	<div id="app">
		<child-demo>
			<template v-slot:demoA>
				<div>aaaaa</div>
			</template>
			<template v-slot:demoB>
				<div>bbbbb</div>
			</template>
		</child-demo>
	</div>
</template>

<script>
import ChildDemo from "./components/ChildDemo.vue";

export default{
	components: {
		ChildDemo
	}
}
</script>

子组件

<template>
	<div>
		<slot name="demoA">
			<p>默认内容AAA</p>
		</slot>
		<slot name="demoB">
			<p>默认内容BBB</p>
		</slot>
	</div>
</template>

<script>
export default {
	name: "ChildDemo"
	data() {
		return {
			data: [ ]
		}
	}
}
</script>

如果使用 child 组件的时候,只会传入了 demoA,那 demoB 就会渲染默认内容

3.作用域插槽
作用域插槽时用来传递数据的插槽。子组件将数据传递到父组件将数据作为 v-slot 的一个特性绑定在子组件上传递给父组件,数据在子组件中更新的时候,父组件接收到的数据也会一同更新。

父组件:

<template>
	<div id="app">
		<child-demo>
			<template v-slot:demoA>
				<div>aaaaa</div>
			</template>
			<template v-slot:demoB="{ dataB }">
				<div>bbbbb {{ dataB }}</div>
				<ul v-for=" item in dataB " :key="item">
					<li>{{ item }}</li>
				</ul>
			</template>
		</child-demo>
	</div>
</template>
<script>
import ChildDemo from "./components/ChildDemo.vue";

export default {
	components: {
		ChildDemo,
	}
}
</script>

子组件:

<template>
<div>
	<slot name="demoA">
		<p>默认内容AAA</p>
	</slot>
	<slot name="demoB" :dataB="data">
		<p>默认内容BBB</p>
	</slot>
	<button @click="handleClick">add</button>
</div>
</template>
<script>
let count = 4;
export default {
	name: "ChlidDemo",
	data() {
		return {
			data: [1,2,3],
		}
	},
	methods:{
		handleClick() {
			this.data.push(count++)
			console.log(this.data)
		}
	}
}
</script>

16、状态管理

React
React 的状态管理工具比较多,常用的以下三种:Redux、Mobx、Dva、Reduxjs/toolkit 这里使用 reduxjs/toolkit 为例

安装:

npm i react-redux
npm i @reduxjs/toolkit

store:

import { configureStore } from "@reduxjs/toolkit"

import {
	TyedUseSelectorHook,
	useDispatch,
	useSelector,
} from "react-redux";
import counterSlice from "./features/counterslice";

// 使用 redux-persist
import storage from "redux-persist/lib/storage";
import { combinReducers } from "redux";
import { persistReducer } from "redux-persist"; // 持久化存储
import thunk from "redux-thunk";

const reducers = combineReducers({
	counter: counterSlice,
})
const persisConfig = {
	key: "root",
	storage,
}
const persistedReducer = persistReducer(persistConfig, reducers);
// 创建一个redux数据
const store = configureStore({
	reducer: persistedReducer,
	devTools: true,  // 注意 调试没事不能在生成环境使用
	middleware: [thunk]
})
export default store;

slice:

import { createSlice } from "@reduxjs/toolkit";

export const counterSlice = createSlice({
	name: "counter",
	initialState: {
		count: 1,
		title: "redux toolkit pre",
	},
	reducers: {
		increment(state,{ payload }){
			state.count = state.count + payload.step;
		},
		decrement(state){
			state.count -= 1;
		}
	}
})

// 导出 actions
export const { increment, decrement } = counterSlice.actions;
// 内置了thunk插件可以直接处理异步请求
export count asyncIncrement = ( payload: any ) => ( dispath: any ) => {
	setTimeout(dispath(increment(payload)),2000)
}
export default counterSlice.reducer;

访问store中的数据

import { useSelector } form "react-redux";
const { counter } = useSelector(state => state.counter);

更新store中的数据

import { useDispatch } from "reactRedux"
import increment from "./slice.js"
const dispath = useDispath();
dispath(increment({ step: newStep }))

Vue
Vue2的状态管理工具 Vuex,Vue3 适用的状态管理工具Pinia (Pinia 也可以在 vue2 中使用)
这里介绍Vuex的使用:
1.state 用于保存存储在store 中的数据
2.getters 用于获取 store 中的数据
3.mutation 用于修改更新 store 中的数据 只能执行同步操作
4.actions Action函数只接受一个和state相同方法和属性的context对象 可以执行异步操作
5.modules 将store 分割成为多个模块避免一个对象过于臃肿

引入vuex

import { createStore } from "vuex";
import Vue from "vue";
const store = createStore({
	state:{
		count: 0,
		todos: [
			{ id: 1,done: true },
			{ id: 2,done: false }
		]
	},
	getters:{
		donetodos( state ){
			return state.filter((todo) => todo.done)
		}
	},
	mutations: {
		increment( state ){
			state.count++;
		}
	},
	actions:{
		increment( context ){
			context.commit("increment")
		}
	}
})
Vue.use( store );

在组件中访问 store:

console.log(this.$store)

在组件中访问 getters:

const doneTodos = this.$store.getters.doneTodos()

在组件中提交 mutations:

this.$store.commit("increament");

在组件中提交 actions:
Action 通过 store.dispatch 方法触发

this.$store.dispatch("increament")

多个 modules:

const moduleA = {
	state: () => ({ ... }),
	mutations: { ... },
	action: { ... },
	getters: { ... }
}

const moduleB = {
	state: () => ({ ... }),
	mutations: { ... },
	action: { ... },
}

const store = createStore({
	modules:{
		a: moduleA,
		b: moduleB
	}
})

store.state.a // -> moduleA 的状态
store.state.b // -> moduleB 的状态

tips:
由于刷新后store中的数据会重新初始化,所以我们可以需要持久化存储;vuex搭配使用vuex-persistedstate,redux 搭配使用redux-persist。当然我们也可以结合原生的 Storage来说实现。

18、逻辑复用
React
class组件中使用mixin(已被弃用,使用方式类似于 vue 中的 mixin)、render props(渲染属性)、高阶组件(HOC)

HOC:
高阶组件是一个函数,入参是一个待包装的组件,返回值是一个包装后的组件。
HOC的封装:

import  React, { Component } from "react"
export default ( WrappedComponent ) => {
	return class extends Component {
		constructor(props){
			super(props)
			this.state = { count: 0 }
		}
		componentDidMount(){}
		const setCount = ( newCount ) => {
			this.setState({count: newCount})
		}
		render(){
			return (
				<div>
					<WrappedComponent state={this.state} setCount={this.getCount} {...this.props} />
				</div>
			)
		}
	}
}

HOC的使用:

import HOC from "./HOC.jsx";
import { Button } from "antd";
const SourceComponet = ( props ) => {
	return (
		<div>
			<div>count: {props.state.count}</div>
			<Button onClick={() => props.setCount(props.state.count + 1)}></Button>
		</div>
	)
}
const Demo = HOC(SourceCoponent);

我们可以写多个Demo组件,其中的count都是独立的互不影响

tips:
高阶组件的可读性比较差、嵌套深

render props:
render props是指一种在组件间使用一个值为函数的prop共享代码的简单技术(解决横切关注点问题)

将子组件当作 prop 传入,子组件可以获取到参数中的数据。

// Cat组件 展示一只猫咪图片 图片位置随着传入的prop值变化
class Cat extends React.Component{
	render(){
		const mouse = this.props.mouse;
		return (
			<img
				src="/cat.jpg"
				style={{ position: "absolute", left: mouse.x, top: mouse.y }}
			/>
		)
	}
}

// Mouse组件 监听鼠标位置变化 将鼠标位置数据传给 render prop
class Mouse extends React.Component{
	constructor( props ){
		super(props);
		this.handleMouseMove = this.handleMouseMove.bind( this )
		this.state = { x:0, y:0 }
	}

	handleMouseMove(event){
		this.setState({
			x: event.clientX,
			y: event.clientY
		})
	}
	
	render(){
		return (
			<div stye={{ height: "100vh" }} onMouseMove={this.handleMouseMove}>
				{/*
					使用 render prop 动态决定要渲染的内容,而不是给一个 <Mouse> 渲染结果的静态表示
				*/}
				{this.props.render(this.state)}
			</div>
		)
	}
}


// MouseTracker 组件 包含 Mouse组件,将Cat组件当作prop 传入 Mounse组件
class MouseTracker extends React.Component {
	render() {
		return (
			<div>
				<h1>鼠标移动!</h1>
				<Mouse render={ (mouse) => <Cat mouse={ mouse } /> }>
			</div>
		)
	}
}

此例中我们复用的是Mouse组件中的逻辑

tips:
1.render prop 只是一种模式,不是一定要使用名为 render 的 prop 来实现这种模式
2.日志功能是一个典型的横切关注点(横切代码中做个模块的关注点(系统的部分功能))案例

Hooks
1.函数组件中使用Hook
使用自定义hook实现真正的逻辑复用:

定义一个通用的 useRequest hook:

import { useEffect, useState } from "react";
import { Get } from "../api/server";

function useRequest(url:string){
	const [ data, setData ] = useState({});
	const [ err, setErr ] = useState({});
	const [ loading, setLoading ] = useState(false);
	useEffect(() => {
		const requestDate = async () => {
			setLoading(true);
			const res: {[ key: string: any ]} = await Get(url);
			console.log("resRequest:", res)
			if( res[1].code === "00000" ){
				setData(res[1].data)
			}else{
				setErr(res[1].description)
			}
			setLoading(false)
		}
		requestData()
	},[]);
	return [data, loading, err]
}
export default useRequest;

使用:

import useRequest from "./hooks/useRequset";
const Demo = () => {
	const [ data, loading ] = useRequst("/admin-service/api/v1/dictionaries")
	const [ data2, loading2 ] = useRequst("/admin-service/api/v1/factory_menu")
	return (
		<div>
			<Table loading={loading}></Table>
			<Table loading={loading2}></Table>
		</div>
	)
}

tips:
1.自定义hook使用use开头
2.自定义hook的经典案例就是 request 方法的封装

Vue
Vue 中使用 mixin 实现逻辑复用:
声明:

export default{
	methods:{
		async getUserInfo(){
			// get info
		}
	}
}

使用:

<script>
	import userinfoMixin from './userinfoMixin';
	export default {
		mixins: ['userinfoMixin']
		data(){
			return {}
		},
		mounted(){
			const userinfo = await this.getUserInfo()
		}
	}
</script>

18、css 文件引入
React

import "@/static/css/demo.less"

Vue

<setyle>
	@import url("./static/css.demo.less")
</setyle>
@import "./static/css.demo.less"

20、图片导入

React
通过 import 的方式引入:

import avatar from "@/static/img/avatar.png";
import { Avater } from "antd"

const Demo = () => {
	reurn (
		<div>
			<Avatar src={ avatar }></Avatar>
		</div>
	)
}

Vue
通过 import 的方式引入:

<template>
	<div>
		<img :src="avatar" />
	</div>
</template>
<script>
import avatar from "@/static/img/avatar.png";
export default {
	data(){
		return {
			avatar: avatar
		}
	}
}
</script>

21、scoped css

scoped css 是指只作用于当前组件的CSS。局部作用域的CSS在开发中非常重要,要搞清楚css的作用域,可以有效避免样式混乱、相互覆盖。

React
1.行内样式
可读性差不推荐

const Demo = () => {
  return (
    <div style={{ width: 100, height: 100 }}>
      <Button style={{ color: "red" }}></Button>
    </div>
  );
};

2.styled-components
styled-components 是第三方库,可以实现 scoped css:

import styled from "styled-components";
export const Demo = styled.div`
  width: 100px;
  height: 100px;
  button: {
    colof: red;
  }
`;

Vue
Vue 在Style标签中支持 scoped 属性,以实现局部作用域的css:

<style scoped>
  .box {
    width: 100px;
    height: 100px;
    background-color: #000000;
  }
</style>

style 标签中的css只对当前组件的元素起作用,不会污染到外部组件

22、不会渲染到真实DOM的标签

为了性能考虑通常我们不希望渲染太多的DOM节点,不会渲染到真实DOM的标签帮助我们将子列表分组,而无需向Dom中添加额外的节点:

React:

const Demo = () => {
	return (
		<div>
			<React.Fragment>
				<Child1></Child1>
				<Child2></Child2>
				<Child3></Child3>
			<React.Fragment>
		</div>
	)
}

Vue:

<template>
	<div>node</div>
</template>

template 标签上可以使用插槽;也可以用于循环

23、router

React
React中我们使用react-router、react-router-dom 实现
在 index.tsx 中引入 BrowserRouter:

import React from "react";
import ReactDOM from "react-dom/client";
import { BrowserRouter } from "react-router-dom";
const root = ReatDOM.createRoot(
	document.querySelector("#root") as HTMLElement
)

root.render(
	<BrowserRouter>
		<App />
	<BrowserRouter>
)

在App.jsx 文件中声明可用的 routes:

import { Routes, Route } from "react-router-dom";
import M1 from "./M1";
import M2 from "./M2";
import M3 from "./M3";
const App = () => {
	return (
		<div>
	      		<Routes>
	        		<Route path="/" element={<Home />}></Route>
	        		<Route path="/a" element={<M1 />}></Route>
	        		<Route path="/b" element={<M2 />}></Route>
	       		 <Route path="/c" element={<M3 />}></Route>
	      		</Routes>
	    	</div>
	)
}

使用 menu 组件 实现点击menu切换路由:

import { useNavigation,useLocation } from "react-router-dom";
// 路由跳转方法
const navigate = useNavigate();
// 当前 path 用于绑定到 Menu 组件的 defaultOenKey
const { pathname } = useLocation();

// 点击 menuItem
const clickItem = ( param: ItemObj ) => {
	navigate(param.key)
}

tips: react-router-dom 是对 react-router 的扩展,新增了DOM操作的API,提供了更多好用的API

Vue

Vue 中我们使用 vue-router实现
在index.js 中引入

import VueRouter from "vue-router";
let routes = [
{
	path: "/home",
	name: "",
}
]
let Routes = new VueRouter({
	mode: "history",
	routes: routes,
})
Vue.use(Routes); // use 方法的作用全局注入插件
new Vue({
	router: routes,
})

实现路由跳转:

this.$router.push({path: "/a"});

24、组件状态缓存 keep-alive

React
React 官方没有提供 keep-alive 的组件,不过提供了 Portal Api 我们可以自己封装,这里使用第三方的库 [react-activation] 来实现

import React, { useState } from "react";
import { Input } from "antd";
import KeepAlive from "react-activation";

const Keep = () => {
	const [ inputValue, setInputValue ] = useState("")
	const changeInput = (e) => {
		setInputValue(e.target.value)
	};
	return (
		<div>
			<div>{inputValue}/div>
			<Input
				placeholder="keepalive this value"
				value={inputValue}
				onChange={changeInput}
			></Input>
		</div>
	)
}

const KP = () => {
	return (
		<div>
			<KeepAlive>
				<Keep></Keep>
			</KeepAlive>
		</div>
	)
}
export default KP;

AliveScope 放在应用入口处,一般放在 Router 和 Provider 内部

import { AliveScope } from "react-activation";
const root = ReactDOM.creatRoot(
	document.querySelector("#root") as HTMLElement
);
root.render(
  <AliveScope>
    <App />
  </AliveScope>
)

Vue
Vue 提供 keep-alive 组件实现组件状态缓存

<template>
  <div>
      <keep-alive :max="10" :include="[]" >
        <router-view>
      </keep-alive>
  </div>
</template>
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值