库和框架对比
- library(库):小而巧,只提供特定API,可以很简单的从一个库切换到另一个库,但是代码几乎不变
- FrameWork(框架):大而全,提供一整套的解决方案
模块化和组件化对比
模块化:
- 从代码的角度来进行分析的
- 把一些可复用的代码,抽离成单个模块,便于项目的维护和开发
组件化:
从UI界面的角度来进行分析的
把一些可复用的UI元素,抽离成单独的组件,便于项目的维护和开发
随着项目的增大,手里的组件越来越多,很方便的就能把现有的组件拼接成一个完成的页面
Vue是如何实现组件化的
通过.vue
文件来创建对应的组件
- template:结构
- script:行为
- style:样式
React是如何实现组件化的
- React有组件化的概念,但是,并没有像Vue这样的组件模版文件;React中,一切都是以JS来表现的
- 也就是说,结构,行为,样式,都是使用JS来实现的
React中核心的概念
虚拟DOM
- Virtual Document Object Model
- Dom的本质:是浏览器中的概念,用JS对象来表示页面上的元素,并提供了操作DOM对象的API
- React中的虚拟DOM:是框架中的概念,程序员用JS对象来模拟页面上的DOM和DOM嵌套
- 为什么要使用虚拟DOM(虚拟DOM的目的):为了实现页面中DOM元素的高效更新
DOM的按需更新:
- 现在有一个每列都支持排序的表格,点击表头即可进行排序
- 一般的做法都是将数据以对象数组的形式储存在浏览器中,一旦用户点击就对对象数组进行排序,然后进行重新更新
- 但是有点排序可能只是交换了两行数据,并不是大量行进行重新排序,这样就会带来性能问题
- 这样就需要按需更新
如何实现按需更新:
- 获取到内存中两个新旧两颗DOM树,进行对比。得到需要更新的DOM元素
- 浏览器中,并没有直接提供获取DOM树的API,因此我们无法拿到浏览器内存中的DOM树。
- 但是程序员可以使用JS对象手动模拟两颗DOM树 , 这两颗手动模拟的DOM树,就是React中虚拟DOM的概念
使用JS模拟DOM树:
1 | <div id="mydiv" title="mytitle" data-index=0> |
使用JS对象表示如下:
1 | var div = { |
Diff算法
- DOM树是每一层组成的
- 一层是由每一个组件组成的
- 一个组件是由每一个元素组成的
tree diff
- 逐层对比两个DOM
- 当逐层对比完毕,所有需要被更新的DOM元素必然能被找到
component diff
- 在tree diff的时候,每一层中组件级别的对比
- 如果对比前后,组件的类型相同,则暂时认为此组件不需要被更新
- 如果对比前后,组件的类型不同,则需要移除旧组件,创建新组件,并追加到页面上
element diff
- 如果进行组件对比的时候,如果两个组件类型相同,则需要进行元素级别的对比
react使用
1 | npm i react react-dom -S |
- React:专门用于创建组件和虚拟DOM的,同时组件的生命周期都在这个包中
- react-dom:专门进行DOM操作的,最主要的应用场景就是
ReactDOM.render()
基本使用
1 | <!-- 创建一个容器,将来渲染的虚拟DOM会放在容器内显示 --> |
1 | // 创建组件,虚拟DOM元素,生命周期 |
JSX
安装babel插件
1 | npm i babel -core babel-loader babel-plugin-transform-runtime -D |
安装能是被转换jsx语法的包
1 | npm i babel-preset-react -D |
上面的写法太过复杂,可以使用
babel
来转换HTML标签这种在JS中混合可写入类似与HTML的语法,叫做
JSX
,也就是符合XML规范的JS- JSX本质还是在运行的时候转成React.createElement来执行
1 | const myh1 = React.createElement('h1',{id:'myh1',title:'this is a h1'},'hello world') |
外部循环
1 | const arrName = ['czj','hyl','gzr','dsz'] |
内部循环
- 这里的key和vue中的key一摸一样
- 需要注意的是,react中的key只能加在map控制的第一层元素
1 | const arrName = ['czj','hyl','gzr','dsz'] |
JSX的属性名冲突
- class变成className
- for 变成htmlFor
1 | ReactDOM.render(<div className="aaa"><label htmlFor='bbb'></label></div>, |
JSX根节点
使用JSX创建的节点,必须是由一个根节点包裹起来
JSX的编译时的解析原则
- 遇到
<>
就把它当成html标签编译 - 遇到
{}
就把它内部代码当成JS代码编译
react创建组件的两种方式
使用构造函数来创建
- 如果要接收外界传递的数据,需要在构造函数的参数列表中使用
props
来接受, - 构造函数必须首字母大写
- 构造函数必须要向外return一个合法的JSX创建的虚拟DOM
1 | // 构造函数首字母必须大写 |
使用ES6的特性 : 展开运算符
1 | const dog = { |
使用class关键字来创建
- 在class组件内部,this表示当前组件的实例对象
- 在class关键字创建的组件中,如果需要外部传递props参数,不需要接收,直接使用this.props.XXX访问即可
1 | class Hello extends React.Component { |
两种方法的区别
使用构造函数创建出来的组件,叫做
无状态组件
,使用class关键字创建的组件,叫做有状态组件
无状态组件和有状态组件的区别就是:
- 有没有state属性
- 有没有生命周期函数
使用class创建的组件拥有自己的私有数据,但是function创建的组件,只有props,没有自己的私有数据和生命周期函数
1 | class Hello extends React.Component { |
props和state之间的区别
- 从数据来源上看,props的数据都是外界传递过来的,state的数据是组件私有的
- props中的数据都是只读的,不能重新赋值;state中的数据,都是可读可写的
使用函数式组件的优点
- 模块化代码 — 可以在整个项目范围内复用你的代码段;
- 只依赖于 props — 默认没有 state;
- 更便于单元测试 — 对测试工具 enzyme/jest 更友好的测试接口;
- 更便于 Mock 数据 — 可以对不同场景方便的进行数据 Mock。
评论列表案例
基本架构
1 | // 评论项组件 |
组件中的style
- JSX中,如果要写行内样式,style属性的值不能是字符串,应该是一个对象
{ { color:"red",fontSize:"25px" } }
:第一层括号表示里面的是JS代码,第二层括号表示这是一个对象
1 | function CommentItem(props) { |
一般会把对象提取出来:
1 | // 一般会把itemStyle单独放在一个文件中 |
CSS文件模块化
1 | // css/commentitem.css |
1 | // 直接导入的css是全局的,会作用于整个项目 |
我们可以开启全局模块化,把CSS文件当成模块暴露出去,接着我们这里就可以使用cssobj对象了
1 | import cssobj from '@/css/commentitem.css' |
注意:css模块化只针对类选择器
和ID选择器
生效,不会对标签选择器
生效
React中绑定事件
事件名称都是React提供的
为事件提供处理函数格式如下
1
onClick = { function }
用的最多的事件绑定形式为
1
2
3
4
5
6<button onClick={ ()=> this.show('传参') }>按钮</button>
// 事件的处理函数,需要定义为一个箭头函数,然后赋值给函数名称
show = (arg1) => {
console.log('show方法' + arg1)
}在React中,如果想要修改state的数据,推荐使用
this.setState( {} )
,注意
this.setState( {} )
是异步的如果要想编写同步代码,就可以使用
this.setState( {} )
的第二参数:回调函数1
2
3
4
5
6myClickHandler = (name)=> {
this.setState(
{name:name},
()=>{console.log(name)}
)
}
基本使用:行内写函数
1 | import React from 'react' |
进阶使用:将函数提取出来
1 | import React from 'react' |
最常使用:使用箭头函数
1 | import React from 'react' |
React的state和页面元素的单项绑定
- 单项绑定是指state值和页面元素的单项绑定,即
state -> 页面元素
。state的值的变化能自动导致页面元素发生改变,但是页面的改变不会对state的值造成影响。(这里的自动
是指不做任务多余操作,如果你添加了监听函数当然就属于例外) - 默认情况下,在React中,如果页面上的表单元素绑定了state上的状态值,那么,每当state上的状态值改变时,必然会自动把最新的状态值自动同步到页面上
使用readOnly属性
1 | import React from 'react' |
使用onChange处理函数
在onChange函数中,获取文本框的值有两种方案:
- 通过
事件参数e
来获取 - 通过
ref属性
获取
1 | import React from 'react' |
Vue的生命周期
什么是生命周期:
- 一个组件从创建到被销毁的全部时间就是生命周期
什么是生命周期函数:
- RPG中,1-4级角色有1个装备栏,5级角色有2两个装备栏。那么,当角色A从4级变到5的瞬间,就会执行某个函数,导致你的装备栏的容量加一。那么这个函数就是生命周期函数。
- 也就是说,当组件从某一个状态转变成另一个状态的时候,会调用的函数,就是生命周期函数。
生命周期:
每个组件的实例,从创建到运行,直到销毁,在这个过程中,会发出一些事件,这些事件就叫做组件的生命周期函数。
new Vue():这时new了一个Vue实例对象,此时,就会进入组件的创建过程
init Event & LifeCycle:初始化组件的生命周期函数,当执行完这一步之后,组件的生命周期函数就已经全部初始化好了,等待着依次去调用
beforeCreate
:这是第一个生命周期函数,此时组件的data和methods以及页面DOM结构都还没有初始化,所以此阶段什么都做不了init injections & reactivity:这个阶段中,正在初始化date和methods中的数据以及方法。(此时DOM还没有被渲染)
created
:这个组件创建阶段第二个生命周期函数,组件的data和methods已经可用了。但是页面还没有渲染出来。在此阶段经常会发起AJax请求。has ”el“ option?:判断是否有el属性
has “template”?:判断是否有template属性
- yes:compile template into render function
- No:compile el’s outerHTML as template
- 4和5步骤正在编译模版部分,把data上的数据拿到,并且解析执行模版结构中的指令,当所有指令被解析完毕,那么模版就被渲染到内存中了
- 当模版 编译完成后,模版页面还没有挂载到页面上,只是存在于内存中,所以5步骤执行后,用户还是看不到页面
beforeMount
:当模版在内存中编译完成,会执行实例创建阶段的第三个生命周期函数。此时内存中的模版结构还没有真正渲染到页面上,此时页面看不到真实的数据。Crete vm.$el and replace “el” with it:把内存中渲染好的模版结构替换到真实页面上
Mounted
:组件创建阶段的最后一个生命周期函数。此时页面已经真正的渲染完毕了,用户可以真正看到页面了。当这个生命周期函数执行完毕之后,组件就离开了创建阶段,进入到运行中
阶段。如果用到了第三方UI插件,而且需要初始化插件,那么必须在这个生命周期函数进行初始化
mounted:已经将数据挂载到页面上。
Beforeupdate
:组件运行中的生命周期函数。会根据data 的数据变化,有选择性的触发0次或者N次。当执行此生命周期的时候,数据是最新的,但是页面是旧的。
Virtual DOM re-render and patch:正在根据最新的data数据,重新渲染模版中的数据结构,并把渲染好的模版结构替换到页面上。
Updated
:组件运行中的生命周期函数。此时,数据是最新的,同时页面也是新的。当data数据变化时,页面更新的步骤:
- 拿到最新的data数据
- 根据最新的data数据,在内存中重新渲染一颗新的DOM树
- 把旧的页面移除,同时把新的DOM树渲染出来
beforeDestory
:当执行此生命周期函数时,组件即将被销毁,但是还没有真正开始销毁。此时组件还是正常使用。data,methods还是可以正常使用TearDown watcher.child componenets and event listeners:销毁组件的过程
Destoryed:组件已经销毁
destoryed
:组件已经完成销毁。此时组件的data和methods已经不可使用分
React组件的生命周期
分成三部分
- 组件创建阶段:一辈子只执行依次
- componentWillMount
- render
- componentDidMount
- 组件运行阶段:按需,根据props属性或state状态的改变,有选择性的执行0到多次
- componentWillReceiveProps
- shouldComponentUpdate
- componentWillUpdate
- render
- componentDidupdate
- 组件销毁阶段:一辈子只执行一次
- componentWillUnmount