class:
class MyComponent extends React.Component {
/* Adding state and binding custom methods */
constructor() {
super()
this.state = { ... }
this.customMethodOne = this.customMethodOne.bind(this)
this.customMethodTwo = this.customMethodTwo.bind(this)
}
/* Lifecycle Methods */
componentDidMount() { ...}
componentWillUnmount() { ... }
/* Custom methods */
customMethodOne() { ... }
customMethodTwo() { ... }
render() { return { ... }}
}
function:
function Input() {
const [input, setInput] = React.useState("");
return <input onChange={(e) => setInput(e.target.value)} value={input} />;
}
- Rendering with JSX . How JSX work?(compile to createElement function)
- complex JSX
<div>
{props.children.map((child) =>
React.createElement(child, { open, toggle }) // or <Child { open, toggle } />
)}
</div>
- Components, Elements, Props, and State
- state management(redux, react query, reselect etc), Selectors(derived state) and how to avoid unnecessary updates reselecthttps://tkdodo.eu/blog/why-you-want-react-query
- hooks
componentDidMount() { ... }
useEffect(() => { ... }, [])
componentWillUnmount() { ... }
useEffect(() => { return () => { ... } }, [])
componentDidUpdate() { ... }
useEffect(() => { ... })
custom hook(可以返回 data,这样可以作为一个数据容器给展示组件使用)
function useKeyPress(targetKey) {
const [keyPressed, setKeyPressed] = React.useState(false);
function handleDown({ key }) {
if (key === targetKey) {
setKeyPressed(true);
}
}
function handleUp({ key }) {
if (key === targetKey) {
setKeyPressed(false);
}
}
React.useEffect(() => {
window.addEventListener("keydown", handleDown);
window.addEventListener("keyup", handleUp);
return () => {
window.removeEventListener("keydown", handleDown);
window.removeEventListener("keyup", handleUp);
};
}, []);
return keyPressed;
}
- lifecycle
- HOC
function withStyles(Component) {
return props => {
const style = { padding: '0.2rem', margin: '1rem' }
return <Component style={style} {...props} />
}
}
- context
- how to implement a popup, dialog, select...?
- Container component and Presentational component(关注点分离思想)
- render props(named slots in vue)
<Title render={() => <h1>I am a render prop!</h1>} />
const Title = (props) => props.render();
- write react(vue) on your own
<script>
export default {
name: "MyComponent",
props: {
// props
},
data() {
// data
},
computed: {
// computed properties
},
watch: {
// properties to watch
},
methods: {
// methods
},
created() {
// lifecyle methods like created
},
// ...
};
</script>
- Composables
<template>
<div class="App">
<Count :count="count" :increment="increment" :decrement="decrement" />
<div id="divider" />
<Width :width="width" />
</div>
</template>
<script>
import { ref, onMounted, onBeforeUnmount } from "vue";
import Count from "./components/Count.vue";
import Width from "./components/Width.vue";
export default {
name: "App",
setup() {
const count = ref(0);
const width = ref(0);
const increment = () => {
count.value++;
};
const decrement = () => {
count.value--;
};
const handleResize = () => {
width.value = window.innerWidth;
};
onMounted(() => {
handleResize();
window.addEventListener("resize", handleResize);
});
onBeforeUnmount(() => {
window.removeEventListener("resize", handleResize);
});
return {
count,
width,
increment,
decrement,
};
},
components: {
Count,
Width,
},
};
</script>
->
import { ref } from "vue";
export function useCounter(initialCount = 0) {
const count = ref(initialCount);
function increment() {
count.value++;
}
function decrement() {
count.value--;
}
return {
count,
increment,
decrement,
};
}
and
import { ref, onMounted, onBeforeUnmount } from "vue";
export function useWidth() {
const width = ref(0);
function handleResize() {
width.value = window.innerWidth;
}
onMounted(() => {
handleResize();
window.addEventListener("resize", handleResize);
});
onBeforeUnmount(() => {
window.removeEventListener("resize", handleResize);
});
return {
width,
};
}
and then
<template>
<div class="App">
<Count :count="count" :increment="increment" :decrement="decrement" />
<div id="divider" />
<Width :width="width" />
</div>
</template>
<script>
import Count from "./components/Count.vue";
import Width from "./components/Width.vue";
import { useCounter } from "./composables/useCounter";
import { useWidth } from "./composables/useWidth";
export default {
name: "App",
components: {
Count,
Width,
},
setup() {
const { count, increment, decrement } = useCounter(0);
const { width } = useWidth();
return {
count,
increment,
decrement,
width,
};
},
};
</script>
- Script Setup
- slot
- State Management(Pinia)
- events handling
- Provide/Inject
- Dynamic Components
- Reactive state
- custom hook(可以返回 data,这样可以作为一个数据容器给展示组件使用)
- Container component and Presentational component(关注点分离思想)
- 动态组件
<component :is="tabs[currentTab]" class="tab"></component>
PS: React 动态组件就超级简单了
<this.state.x />
- 内置组件(KeepAlive)
- Provide/Inject
- render function
<script setup>
import { h } from "vue";
const { message } = defineProps(["message"]);
const render = () => {
return h(
"div",
{
class: "render-card",
},
[
h(
"header",
{
class: "card-header card-header-title",
},
message
),
]
);
};
</script>
or
<template>
<render />
</template>
<script setup lang="jsx">
const { message } = defineProps(["message"]);
const render = (
<div class="render-card">
<header class="card-header card-header-title">{message}</header>
</div>
);
</script>
- renderless component (类似于自定义 hook 作为数据源)
<template>
<slot :checkbox="checkbox" :toggleCheckbox="toggleCheckbox"></slot>
</template>
<script setup>
import { ref } from "vue";
const checkbox = ref(false);
const toggleCheckbox = () => {
checkbox.value = !checkbox.value;
};
</script>
use:
<template>
<ToggleComponent v-slot="{ checkbox, toggleCheckbox }">
<div class="comp">
<label class="switch">
<input type="checkbox" :value="checkbox" @click="toggleCheckbox" />
<div class="slider rounded" :class="checkbox ? 'active' : ''"></div>
</label>
</div>
</ToggleComponent>
</template>
<script setup>
import ToggleComponent from "./components/ToggleComponent";
</script>
- split component into chunks
import { defineAsyncComponent } from "vue";
const AsyncComp = defineAsyncComponent(() => {
return new Promise((resolve, reject) => {
// ...load component from the server
resolve(/* loaded component */);
});
});
or:
import { defineAsyncComponent } from "vue";
export const AsyncComp = defineAsyncComponent(() =>
import("./components/MyComponent.vue")
);
advanced usage:
import { defineAsyncComponent } from "vue";
import Loading from "./Loading.vue";
import Error from "./Error.vue";
export const AsyncModal = defineAsyncComponent({
loader: () => import("./Modal.vue"),
loadingComponent: Loading,
errorComponent: Error,
});