Часть 2: компонентно-ориентированное программирование React
1 Основное понимание и использование
Отладка с помощью React DevTools
React Developer Tools
важная точка
-
Имена компонентов должны быть написаны с заглавной буквы
-
Виртуальный элемент DOM может иметь только один корневой элемент.
-
Виртуальные элементы DOM должны иметь закрывающие теги < />
Базовый процесс рендеринга тегов компонента класса
-
React внутренне создает объекты экземпляра компонента
-
Вызовите render(), чтобы получить виртуальный DOM и преобразовать его в настоящий DOM.
-
Вставить внутрь указанного элемента страницы
1.1 Определение компонентов в React
Функционально объявить компоненты
<script type="text/babel">
//1.创建函数式组件
function MyComponent(){
console.log(this); //此处的this是undefined,因为babel编译后开启了严格模式
return <h2>我是用函数定义的组件(适用于【简单组件】的定义)</h2>
}
//2.渲染组件到页面
ReactDOM.render(<MyComponent/>,document.getElementById('test'))
</script>
После выполнения ReactDOM.render(<MyComponent/>
..., что произошло
-
React анализирует тег компонента и находит компонент MyComponent.
-
Обнаружено, что компонент определяется с помощью функции, а затем вызывается функция для преобразования возвращенного виртуального DOM в реальный DOM, который затем отображается на странице.
Компоненты класса (следующие примеры относятся к компонентам класса)
<script type="text/babel">
//1.创建类式组件
//必须继承Component类,必须要写render,必须有返回值
class MyComponent extends React.Component {
render(){
//render是放在哪里的?—— MyComponent的原型对象上,供实例使用。
//render中的this是谁?—— MyComponent的实例对象 <=> MyComponent组件实例对象。
console.log('render中的this:',this);
return <h2>我是用类定义的组件(适用于【复杂组件】的定义)</h2>
}
}
//2.渲染组件到页面
ReactDOM.render(<MyComponent/>,document.getElementById('test'))
</script>
После выполнения ReactDOM.render(<MyComponent/>
..., что произошло
-
React анализирует тег компонента и находит компонент MyComponent.
-
Обнаружено, что компонент определяется с помощью класса, а затем заново создается экземпляр класса, и метод рендеринга на прототипе вызывается через экземпляр.
-
Преобразуйте виртуальный DOM, возвращенный рендерингом, в реальный DOM, а затем отобразите его на странице.
Простые и сложные компоненты
Компоненты с состоянием — это сложные компоненты, компоненты без состояния — это простые компоненты.
Страница, управляемая состоянием: данные помещаются в состояние компонента, и изменение данных будет управлять отображением страницы.
Состояние состояния находится в экземпляре компонента
2 Один из трех основных атрибутов экземпляров компонентов: состояние
2.1 Понимание состояния
-
состояние является наиболее важным атрибутом объекта компонента, а значение является объектом (может содержать несколько комбинаций ключ:значение)
-
Компонент вызывается
状态机
, и соответствующее отображение страницы обновляется путем обновления состояния компонента (повторного рендеринга компонента) -
Данные помещаются в состояние, а состояние управляет отображением страницы! Отображение страницы, управляемое состоянием! Отображение страницы, управляемое состоянием!
2.2 На что следует обратить внимание
this в методе рендеринга в компоненте является объектом экземпляра компонента
В пользовательском методе компонента это не определено (используется как обратный вызов), как это решить?
-
Обязательная привязка этого: через bind() объекта функции
-
this.changeWeather = this.changeWeather.bind(this)
Таким образом, после написания вызывается метод на самом экземпляре объекта вместо того, чтобы висеть на прототипе -
оператор присваивания + стрелочная функция
推荐
Данные о состоянии, которые нельзя изменить или обновить напрямую, необходимо setState
обновлять с помощью
Методы в классе в основном используются как обратные вызовы событий, так что разберитесь с этим.
Если вы хотите использовать его, вам нужно написать много методов в конструкторе this.changeWeather = this.changeWeather.bind(this)
, что слишком громоздко (упростите конструктор, сохраните или вызовите это----упрощенное написание)
2.3 Пример кода
Обычно используйте bind() объекта функции
<script type="text/babel">
//1.创建组件
class Weather extends React.Component {
//构造器调用几次? ———— 1次
constructor(props) {
console.log('constructor');
super(props)
//初始化状态
this.state = {
isHot: false, wind: '微风' }
//解决changeWeather中this指向问题---原型上得有,然后生成这个实例身上自身的方法
this.changeWeather = this.changeWeather.bind(this)
}
//render调用几次? ———— 1+n次 1是初始化的那次 n是状态更新的次数
render() {
console.log('render');
//读取状态
const {
isHot, wind } = this.state
//绑定事件不用写括号,不然就是把返回值交给onClick,我们是要把整个回调函数交给onClick
//这边现在调用的changeWeather方法是实例自身上的
return <h1 onClick={
this.changeWeather}>今天天气很{
isHot ? '炎热' : '凉爽'},{
wind}</h1>
}
//changeWeather调用几次? ———— 点几次调几次
changeWeather() {
//changeWeather放在哪里? ———— Weather的原型对象上,供实例使用
//由于changeWeather是作为onClick的回调,所以不是通过实例调用的,是直接调用this应该是window
//但类中的方法默认开启了局部的严格模式,所以changeWeather中的this为undefined
console.log('changeWeather');
//获取原来的isHot值
const isHot = this.state.isHot
//严重注意:状态必须通过setState进行更新,且更新是一种合并,不是替换。
this.setState({
isHot: !isHot })
console.log(this);
//严重注意:状态(state)不可直接更改,下面这行就是直接更改!!!
//this.state.isHot = !isHot //这是错误的写法
}
}
//2.渲染组件到页面
ReactDOM.render(<Weather />, document.getElementById('test'))
</script>
Сокращенный метод: форма оператора присваивания + стрелочная функция , важно! важный! важный!
Оператор присваивания, написанный в классе, помещается в его собственный экземпляр, а затем метод записывается как стрелочная функция, this из стрелочной функции является объектом экземпляра, поэтому конструктор можно упростить.
(Цель написания конструктора — настроить некоторые атрибуты инициализации. Теперь напишите атрибуты как операторы присваивания и поместите их в класс, и вы получите состояние напрямую)
//1.创建组件
class Weather extends React.Component{
//初始化状态
state = {
isHot:false,wind:'微风'}
render(){
const {
isHot,wind} = this.state
return <h1 onClick={
this.changeWeather}>今天天气很{
isHot ? '炎热' : '凉爽'},{
wind}</h1>
}
//自定义方法————要用赋值语句的形式+箭头函数
changeWeather = ()=>{
const isHot = this.state.isHot
this.setState({
isHot:!isHot})
}
}
//2.渲染组件到页面
ReactDOM.render(<Weather/>,document.getElementById('test'))
3 Один из трех основных атрибутов экземпляров компонентов: свойства
3.1 понимание реквизита
-
Каждый объект компонента будет иметь атрибут props (сокращение от properties).
-
Все свойства тегов компонентов хранятся в свойствах
3.2 Роль и внимание реквизита
-
Передача изменяющихся данных снаружи компонента внутрь компонента через атрибут метки
-
Примечание. Не изменяйте данные реквизита внутри компонента, реквизит доступен только для чтения.
-
Если входящие данные слишком длинные, полученные данные объекта могут быть записаны как
{...p}
входящие. -
Вы можете ограничить входящий тип данных, если вы его не передаете, вы также можете указать значение по умолчанию
-
Добавляйте атрибуты в сам класс, а не в экземпляр класса, используйте static static
3.3 Пример кода
Компоненты класса используют свойства, самый простой способ использования
//创建组件
class Person extends React.Component{
render(){
// console.log(this);
const {
name,age,sex} = this.props
return (
<ul>
<li>姓名:{
name}</li>
<li>性别:{
sex}</li>
<li>年龄:{
age+1}</li>
</ul>
)
}
}
//渲染组件到页面
ReactDOM.render(<Person name="jerry" age={
19} sex="男"/>,document.getElementById('test1'))
ReactDOM.render(<Person name="tom" age={
18} sex="女"/>,document.getElementById('test2'))
//上面这样写有一些问题:信息很多要写太长,能不能批量操作
const p = {
name:'老刘',age:18,sex:'女'}
// console.log('@',...p);
// ReactDOM.render(<Person name={p.name} age={p.age} sex={p.sex}/>,document.getElementById('test3'))
//此处使用赋值解构方式,使得代码更简洁,上面的语法糖,关键在于捞回来的数据要一样才能这样写
ReactDOM.render(<Person {
...p}/>,document.getElementById('test3'))
//...p在原生js本身是错误的,但是{...p}可以,相当于复制了一个对象。在react中的{}作为分隔符使用,但是我们引入了react加上babel就可以直接分解对象,也就是可以直接写...p,但是注意仅可以作为标签的属性使用,不是随便可以使用的
//对传入数据的类型进行限制,此处限制可以换成typrScript
<script type="text/javascript" src="../js/prop-types.js"></script>
Person.propTypes = {
name:PropTypes.string.isRequired, //限制name必传,且为字符串---16版本之后不在React上面取了,引入prop-types依赖包
sex:PropTypes.string,//限制sex为字符串
age:PropTypes.number,//限制age为数值
speak:PropTypes.func,//限制speak为函数
}
//不传的话,指定默认标签属性值
Person.defaultProps = {
sex:'男',//sex默认值为男
age:18 //age默认值为18
}
//props简写,把类型限制和默认值写在类里面
static propTypes = {
name:PropTypes.string.isRequired, //限制name必传,且为字符串
sex:PropTypes.string,//限制sex为字符串
age:PropTypes.number,//限制age为数值
}
static defaultProps = {
sex:'男',//sex默认值为男
age:18 //age默认值为18
}
Функциональные компоненты используют реквизиты, потому что функции могут получать параметры
//创建组件
function Person (props){
const {
name,age,sex} = props
return (
<ul>
<li>姓名:{
name}</li>
<li>性别:{
sex}</li>
<li>年龄:{
age}</li>
</ul>
)
}
//渲染组件到页面
ReactDOM.render(<Person name="jerry"/>,document.getElementById('test1'))
//函数的类型限制要写在外面
4 Один из трех основных атрибутов экземпляров компонентов: refs
4.1 Понимание ссылок
Теги в компонентах могут определять ссылки для идентификации самих себя, подобно собственным идентификаторам, ссылки собирают узлы реального дома.
4.2 Пример кода
Есть проблема с ref() в строковой форме
不推荐,将被淘汰
: это неэффективно.
//展示左侧输入框的数据
showData = ()=>{
const {
input1} = this.refs
alert(input1.value)
}
//展示右侧输入框的数据
showData2 = ()=>{
const {
input2} = this.refs
alert(input2.value)
}
render(){
return(
<div>
<input ref="input1" type="text" placeholder="点击按钮提示数据"/>
<button onClick={
this.showData}>点我提示左侧的数据</button>
<input ref="input2" onBlur={
this.showData2} type="text" placeholder="失去焦点提示数据"/>
</div>
)
}
}
Ссылка в форме обратного вызова хранит реальный узел DOM в экземпляре компонента this, а обратный вызов напрямую записывается как интровертная функция, которая не имеет никакого эффекта.
/**下面的this指的是组件实例,我直接this.input1 = c 意思是给实例上的input1赋值,之后直接通过调用打印得到*/
//回调函数的内联写法,会有问题,就是更新数据会重新调用两次函数,第一次传入null,第二次才是传入dom节点,这个是每次更新生成新的函数
//展示左侧输入框的数据
showData = ()=>{
const {
input1} = this//在实例身上取
alert(input1.value)
}
//展示右侧输入框的数据
showData2 = ()=>{
const {
input2} = this
alert(input2.value)
}
render(){
return(
<div>
<input ref={
c => this.input1 = c } type="text" placeholder="点击按钮提示数据"/>
<button onClick={
this.showData}>点我提示左侧的数据</button>
<input onBlur={
this.showData2} ref={
c => this.input2 = c } type="text" placeholder="失去焦点提示数据"/>
</div>
)
}
}
//回调函数写成class的绑定函数,就不会频繁调用,不会生成新的函数
saveInput = (c)=>{
this.input1 = c;
console.log('@',c);
}
render(){
return(
<div>
<input ref={
this.saveInput} type="text"/><br/><br/>
<button onClick={
this.showData}>点我提示左侧的数据</button>
<input onBlur={
this.showData2} ref={
c => this.input2 = c } type="text" placeholder="失去焦点提示数据"/>
</div>
)
}
createRef создает контейнер ссылки
最推荐的
/*React.createRef调用后可以返回一个容器,该容器可以存储被ref所标识的节点,该容器是“专人专用”的*/
//用几个就要创建几个容器,一个容器只能放一个ref标识的节点
myRef = React.createRef()
myRef2 = React.createRef()
//展示左侧输入框的数据
showData = ()=>{
// 取出保存在容器里的节点
alert(this.myRef.current.value);
}
//展示右侧输入框的数据
showData2 = ()=>{
alert(this.myRef2.current.value);
}
render(){
return(
<div>
{
/*把ref所在的节点存储到刚才实例身上生成的容器myRef里面*/}
<input ref={
this.myRef} type="text" placeholder="点击按钮提示数据"/>
<button onClick={
this.showData}>点我提示左侧的数据</button>
<input onBlur={
this.showData2} ref={
this.myRef2} type="text" placeholder="失去焦点提示数据"/>
</div>
)
}
5 Обработка событий и сбор данных формы
5.1 Обработка событий
Укажите функцию обработчика событий по onXxx
атрибуту (обратите внимание на регистр) (native: onclick
, React: onClick
)
-
React использует пользовательские (синтетические события, а не нативные события DOM) — для лучшей совместимости.
-
События в React обрабатываются посредством делегирования событий (делегирование самому внешнему элементу компонента) — для большей эффективности.
Получите объект элемента DOM, в котором произошло событие, через event.target ----- не злоупотребляйте ref
//不过度使用ref:发生事件的元素正好是我要操作的元素时,就可以避免使用ref(受控组件可以发生事件与操作的元素不同)
//失去焦点的时候react会帮你调用这个函数,并把事件对象event传进去
//就可以拿到发生事件的事件源event.target,event.target就是input框
showData2 = (event) => {
alert(event.target.value);
}
render() {
return (
<div>
<input ref={
this.myRef} type="text" placeholder="点击按钮提示数据" />
<button onClick={
this.showData}>点我提示左侧的数据</button>
<input onBlur={
this.showData2} type="text" placeholder="失去焦点提示数据" />
</div>
)
}
5.2 Классификация компонентов формы
Управляемый компонент, dom класса ввода, будет поддерживать вещи в состоянии, когда вы их вводите, и выводит их из состояния, когда вы их используете (не пишите ref)
state = {
//初始化状态
username:'', //用户名
password:'' //密码
}
//保存用户名到状态中
saveUsername = (event)=>{
this.setState({
username:event.target.value})
}
//保存密码到状态中
savePassword = (event)=>{
this.setState({
password:event.target.value})
}
//表单提交的回调
handleSubmit = (event)=>{
event.preventDefault() //阻止表单提交
const {
username,password} = this.state
alert(`你输入的用户名是:${
username},你输入的密码是:${
password}`)
}
render(){
return(
<form onSubmit={
this.handleSubmit}>
用户名:<input onChange={
this.saveUsername} type="text" name="username"/>
密码:<input onChange={
this.savePassword} type="password" name="password"/>
<button>登录</button>
</form>
)
}
Неконтролируемые компоненты, готовые к использованию, требуется реф.
handleSubmit = (event)=>{
event.preventDefault() //阻止表单提交
const {
username,password} = this
alert(`你输入的用户名是:${
username.value},你输入的密码是:${
password.value}`)
}
render(){
return(
<form onSubmit={
this.handleSubmit}>
用户名:<input ref={
c => this.username = c} type="text" name="username"/>
密码:<input ref={
c => this.password = c} type="password" name="password"/>
<button>登录</button>
</form>
)
}
6 Функции высшего порядка и каррирование функций
Стрелочная функция, сохраняющая входные данные в состояние в управляемом компоненте, была написана слишком много раз, если много всего, напишите кучу стрелочных функций.
Напишите функцию saveFormData('username') и сообщите ей параметры
onChange ={this.saveFormData('username')}, что означает использование возвращаемого значения saveFormData в качестве обратного вызова onChange. Было бы здорово, если бы возвращаемое значение было функцией, а возвращаемая функция - настоящим обратным вызовом
Не говорите, писать ли круглые скобки в обратных вызовах функций, сводка такова, что функция должна быть передана XXX в качестве обратного вызова.
saveFormData — это функция высшего порядка (возвращаемое значение — это функция)
//保存表单数据到状态中
saveFormData = (dataType) => {
return (event) => {
//基本功 对象里面读取变量[]
this.setState({
[dataType]: event.target.value })
}
}
render() {
return (
<form onSubmit={
this.handleSubmit}>
用户名:<input onChange={
this.saveFormData('username')} type="text" name="username" />
密码:<input onChange={
this.saveFormData('password')} type="password" name="password" />
<button>登录</button>
</form>
)
}
6.1 Функции высшего порядка
Функция является функцией высшего порядка, если она удовлетворяет любой из следующих двух спецификаций.
-
Если функция A принимает функцию в качестве параметра, то A можно назвать функцией высшего порядка.
-
Если возвращаемое значение функции A все еще является функцией, то A можно назвать функцией более высокого порядка.
Общие функции высокого порядка: Promise, setTimeout, arr.map() и т. д.
6.2 Каррирование функций
Метод продолжения возврата функции через вызов функции реализует форму кодирования функции окончательной унифицированной обработки принятых параметров.
function sum(a){
return (b)=>{
return c=>{
return a+b+c} }}
6.3 Реализовать привязку событий без каррирования функций
Используйте функцию обратного вызова напрямую, потому что она сама возвращает функцию
<input onChange={
event => this.saveFormData('username',event) } type="text" name="username"/>
7 жизненный цикл
Компоненты проходят определенные этапы от создания до смерти.
Компоненты React содержат ряд функций-ловушек (функций обратного вызова жизненного цикла), которые будут вызываться в определенные моменты времени.
Когда мы определяем компоненты, мы будем выполнять определенную работу в определенных функциях обратного вызова жизненного цикла.
7.1 Жизненный цикл React (старый)
Порядок вызова каждого хука жизненного цикла
Фаза инициализации : запускается ReactDOM.render()
-
конструктор()
-
компонентWillMount()
-
оказывать()
-
компонентDidMount() ==>
常用
В общем, componentDidMount()
сделайте некоторую инициализацию в хуке, например: запустите таймер, отправьте сетевые запросы, подпишитесь на сообщения и т. д.
Фаза обновления : запускается this.setState() внутри компонента или рендеринга родительского компонента.
Один из трех маршрутов: маршрут setState()
-
shouldComponentUpdate() Компонент должен быть обновлен. Если вы его не напишете, нижний слой автоматически заполнит один, и возвращаемое значение будет true. Напишите его сами и не забудьте вернуть значение
-
компонент componentWillUpdate() будет обновлен
-
render() ===>
必须使用
один из -
компонент componentDidUpdate() будет обновлен
Второй из трех маршрутов: маршрут forceUpdate():
forceUpdate() Принудительное обновление: если вы хотите обновить компонент без изменения состояния, пропустите shouldComponentUpdate()
Третий из трех маршрутов: рендеринг родительского компонента
Напишите конструктор componentWillReceiveProps в подкомпоненте, что означает, что подкомпонент будет вызываться, когда получит переданные параметры (примечание: он не будет вызываться при первой передаче!!!), а также может получать пропсы параметры
Размонтировать компонент : активируется ReactDOM.unmountComponentAtNode(
卸载节点上的组件
).
- componentWillUnmount() ===>
常用
компонент будет размонтирован
Как правило, в этом хуке сначала делайте некоторые вещи, такие как: закрытие таймера, отписка и т. д.
7.2 Жизненный цикл React (новое)
Три конструкции UNSAFE_
начинаются с: UNSAFE_compinentWillMount()
, UNSAFE_componentWillReceiveProps()
,UNSAFE_componentWillUpdate()
Обозначение: конструкторы с UNSAFE_ захотят добавить, за исключением того, что они будут удалены (эти три хука являются устаревшими).
Зачем добавлять эти префиксы: Многие неправильно понимают и злоупотребляют, особенно когда новый асинхронный рендеринг выпускается позже, это приведет к ошибкам. Добавление префикса увеличивает стоимость использования
Резюме: в старом жизненном цикле будут отброшены три хука, а в новом добавятся 2 хука.
Фаза инициализации : активируется ReactDOM.render() — начальный рендеринг
-
конструктор()
-
getDerivedStateFromProps()
Получить производное состояние из реквизита ===>不常用
getDerivedStateFromProps() не для экземпляров, поэтому он определен как статический метод в классе и должен возвращать значение: объект состояния или ноль, а параметры, которые могут быть получены, - это свойства и состояние.
А если возвращается статусный объект, то обновление не сработает, а возвращаемый объект является основным, если возвращаемый объект пишется не сам по себе, а полученный реквизит, то это объяснение получения производного состояния из реквизита, простыми словами После использования этого хука значение состояния зависит от переданного в реквизите и влияет на обновление
-
оказывать()
-
компонентDidMount() ====>
常用
Фаза обновления : запускается this.setState() внутри компонента или рендеринга родительского компонента.
-
getDerivedStateFromProps() Получить производное состояние от Props
-
shouldComponentUpdate() Компонент должен быть обновлен
-
оказывать()
-
getSnapshotBeforeUpdate()
Сделайте снимок перед обновлением
getSnapshotBeforeUpdate() должен возвращать значение моментального снимка или null, любое значение может быть использовано в качестве значения моментального снимка, возврат к componentDidUpdate
Смысл получения снимка: щелкните несколько фотографий, чтобы выполнить некоторые операции до того, как компонент будет обновлен на странице
- компонентDidUpdate()
componentDidUpdate() получает три параметра (preProps, preState, snapshotValue), а последний параметр передается из хука getSnapshotBeforeUpdate.
Размонтировать компонент : активируется ReactDOM.unmountComponentAtNode()
- компонентWillUnmount() ===>
常用
7.3 Важные крючки
-
render
: инициализация рендеринга или обновление вызовов рендеринга. -
componentDidMount()
: Включить мониторинг: отправить запрос ajax -
componentWillUnmount()
: выполнить некоторые отделочные работы, например: очистить таймер
7.4 Хуки, которые скоро устаревают
-
компонентWillMount
-
компонентWillReceiveProps
-
компонентWillUpdate
ps
: При использовании сейчас будет предупреждение, а версия в будущем может нуждаться в префиксе UNSAFE_, а в будущем от нее могут вообще отказаться, поэтому не рекомендуется использовать
Предполагается, что команда React считает, что увеличение стоимости использования косвенно повлияет на нас, давайте адаптируемся к новому хуку, поэтому добавьте это
8 ключей в React/VUE
Классические вопросы на собеседовании:
-
Какова функция ключа в React/VUE (каков внутренний принцип ключа)
-
Почему лучше не использовать индекс для ключа при обходе списка
8.1 Роль ключа в виртуальном DOM
Проще говоря: ключ — это идентификатор виртуального DOM-объекта, и ключ играет чрезвычайно важную роль в обновлении отображения.
Подробно: при изменении данных в состоянии react будет генерировать по ним 新数据
, 新的虚拟DOM
а потом react сравнивать 新虚拟DOM
с 旧虚拟DOM
diff.Правила сравнения такие:
-
Тот же ключ, что и новый виртуальный DOM, находится в старом виртуальном DOM:
若虚拟DOM中内容没变,直接使用之前的真实DOM 若虚拟DON中的内容变了,则生成新的真实DOM,随后替换掉页面中之前的真实DOM
-
Тот же ключ, что и новый виртуальный DOM, не был найден в старом виртуальном DOM:
根据数据创建新的真实DOM,随后渲染到页面
8.2 Возможные проблемы, вызванные использованием индекса в качестве ключа
-
Если вы выполняете операции над данными: добавляете в обратном порядке, удаляете в обратном порядке и т. д.:
会产生没有必要的真实DOM更新 ==>界面效果没问题,但是效率低
-
Если структура также содержит DOM входного класса
会产生错误DOM更新 ===>界面有问题
-
Уведомление:
如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅用于渲染列表用于展示,使用index作为key是没有问题的
8.3 Как выбрать ключ во время разработки
-
В качестве ключа лучше всего использовать уникальный идентификатор каждого фрагмента данных, например, идентификатор, номер мобильного телефона, идентификационный номер, номер студента и т. д.
-
Если установлено, что это просто отображение стихов, можно также использовать указатель
//慢动作回放----使用index索引值作为key
初始数据:
{
id:1,name:'小张',age:18},
{
id:2,name:'小李',age:19},
初始的虚拟DOM:
<li key=0>小张---18<input type="text"/></li>
<li key=1>小李---19<input type="text"/></li>
更新后的数据:
{
id:3,name:'小王',age:20},
{
id:1,name:'小张',age:18},
{
id:2,name:'小李',age:19},
更新数据后的虚拟DOM:
<li key=0>小王---20<input type="text"/></li>
<li key=1>小张---18<input type="text"/></li>
<li key=2>小李---19<input type="text"/></li>
-----------------------------------------------------------------
//慢动作回放----使用id唯一标识作为key
初始数据:
{
id:1,name:'小张',age:18},
{
id:2,name:'小李',age:19},
初始的虚拟DOM:
<li key=1>小张---18<input type="text"/></li>
<li key=2>小李---19<input type="text"/></li>
更新后的数据:
{
id:3,name:'小王',age:20},
{
id:1,name:'小张',age:18},
{
id:2,name:'小李',age:19},
更新数据后的虚拟DOM:
<li key=3>小王---20<input type="text"/></li>
<li key=1>小张---18<input type="text"/></li>
<li key=2>小李---19<input type="text"/></li>