## 组件化和React
发现bug,解决bug的能力和效率
### 组件化
### 题目
1. 说一下对组件化的理解
2. JSX本质是什么
3. JSX和vdom的关系
4. 说一下setState的过程
5. 阐述对React和vue的认识
#### 用React todolist
命令:
npm i create-react-app -g (sudo:mac和linux)
create-react-app react-test
cd react-test
npm start
~~~
import React,{Component} from "react";
import logo from "./logo.svg";
import "./App.css"
import Todo form ".components/todo/index.js"
class App extends Component{
render(){
return (
<div className="App">
<header className="header">
<img src={logo}>
</header>
<Todo/>
</div>
)
}
}
~~~
componets/todo/index.js
~~~
import React,{Component} from "react";
import List from "./list/index.js";
import Input from "./input/index.js"
class Todo extends Component{
constructor(props){
super(props);
this.state={
list: [],
}
}
render(){
return(
<div>
<p>this is todo component</p>
<Input addTitle={this.addTitle.bind(this)} />
<List data={this.state.list}/>
</div>
)
}
addTitle(title){
const currentList = this.state.list;
this.setState({
list: currentList.concat(title),
})
}
}
export default Todo;
~~~
todo文件夹中建一个list文件夹,在添加index.js
~~~
import React,{Component} from "react";
class List extends Component{
constructor(props){
super(props)
}
render(){
const list = this.props.data;
return (
<ul>
{
list.map((item,index)=>{
return <li key="index">{list}</li>
})
}
</ul>
)
}
}
export default List;
~~~
react规定,所有的遍历里面都有key
todo文件夹下建一个input文件夹,index.js
~~~
import React ,{Component} from "react";
class Input extends Component{
constructor(props){
super(props);
this.state={
title:''
}
}
render(){
return(
<div>
<input value={this.state.title} onChange = {this.changeHandle.bind(this)}
<button onClick={this.clickHandle.bind(this)}>Submit</button>
</div>
)
}
changeHandle(e){
this.setState({
title:e.target.value
})
clickHandle(){
const title = this.state.title;
//把值拿到,把title添加进列表
const addTitle = this.props.addTitle;
addTitle(title);//重点
this.setState({
title:''
})
}
}
}
export default Input;
~~~
bind(this)//绑定当前组件的实例
react:必须用这种**构建方式**去做
vue:也可以这么做(组件化),但为了方便,简单,快速,突出重点,选择了一种简单的cdn引文件方式做,(初学者)
但是:**复杂的配置是为了解决问题,让后面的使用更加简单**看似复杂的东西,是为了解决后面更复杂的问题,为了让后面的使用更加简单。
react:看似写起来很麻烦,但讲起来特别简单,结构特别清晰,组件直接的引用逻辑上特别干练,这种方式来做大型的项目开发才是最好的方式(不一定是react,vue也可以用这种方式)
react:
1. 数据和视图分离 数据保存在state中,视图在render方法中
2. 通过修改数据驱动视图变化,不是直接操作dom
3. react和vue的不同:react是组件化的,组件化的每个组件都支持上两条,vue整个就是mvvm的架构,两个特点就是在外部体现的,但是也是支持组件的,vue是先有了mvvm特点之后,在mvvm的基础上把组件抽象出来而已。
4. 所以讲mvvm拿vue来讲,将组件化拿react讲,特点非常鲜明
### 说一下对组件化的理解
(组件可以封装很多很多东西,然后外界都不用关心,想用的时候可以直接用,而且可以定义一次复用很多次)(面向对象:封装,复用,多态)
组件的封装就是为了给外界多次使用
**所谓组件化,就是把页面拆分成多个组件(component),每个组件依赖的css,JavaScript、模板、图片等资源放在一起开发和维护。组件是资源独立的,组件在系统内部可复用,组件和组件之间可以嵌套。**
1. 组件的封装
* 视图
* 数据
* 变化逻辑(数据驱动视图变化)(黑盒)(组件内部的变化逻辑,外界不需知道,)
以上三点调用者都可以不用知道,只需将符合要求的数据传过来就行
2. 组件的复用
复用:就需要像组件传一些东西(不同属性)
* props传递
### 问题解答
1. 组件的封装:封装视图,数据,变化逻辑
2. 组件的复用:props传递(达到复用的效果)、复用(封装一次,复用多次)
*****
### JSX本质是什么
react把jsx真正的推广开来,好用,简单,功能强大;语法糖
1. JSX语法
2. JSX解析成JS
3. 独立的标准(不仅仅是react的从属,可以用到其他库)
解答:
1. JSX语法
* html形式(所有的html标签,jsx都支持)
* 引入js变量和表达式(大括号中,可以引入变量,表达式,注释等)
jsx中的** 注释**{/* */} :原因:jsx中可以在**大括号中加入js变量或表达式**
* if.. else...判断
~~~
{ show?<img/>:'' }
}
~~~
* 循环
~~~
{ list.map((item,index)=>{
return <li key={index}>{item}</li>
})
~~~
* style和className
//第一个大括号用来表示是js表达式,第二个大括号表示对象格式
//class是保留字
~~~
const styleConfig = {
fontSize:'40px',
color:'blue'
}
return(
<div className="container">
<p style={styleConfig}></p>
//第一个大括号用来表示是js表达式,第二个大括号表示对象格式
<p style={{fontSize:'40px',color:‘blue’}}></p>
</div>
)
~~~
* 事件
~~~
import React,{Component} from "react";
import logo from "./logo.svg";
import "./App.css"
import Todo form ".components/todo/index.js"
class App extends Component{
render(){
const show = true;
return (
<div className="App">
<header className="header">
<img src={logo}>
</header>
{/* <Todo/> *}/
{
show?<img/>:''}
}
</div>
)
}
}
~~~
* JSX语法根本无法被浏览器所解析(vue的指令也无法被浏览器解析,只是一种表述方式)
* 那么他是如何在浏览器中运行?(像vue的模板一样,最终转为js来运行的)(依赖 import react)
### JSX解析 -本质:解析成js代码
JSX是一个语法糖,真正的是要解析成js代码来执行,React.createElement的用法(用来)
~~~
/* jsx代码*/
var profile = <div>
<img src="avatar.png" className="profile" />
<h3>{[user.fistName,user.lastName].join(' ')}</h3>
</div>
//解析结果
var profile = React.createElement('div',null,
React.createElement("img",{src:"avatar.png",className:"profile"}),
React.createElement("h3",null,[user.fistName,user.lastName].join(' ')),
)
//标签名 属性 子元素
//React.createElement参数说明
React.createElement('div',{id:'div'},child1,child2,child2);
React.createElement('div',{id:'div'},[...]);
~~~
**vdom中h()函数,返回vnode,vue中_c(),返回vnode,React.createElement类似(标签、属性、子元素)**
### JSX解析
1. JSX其实是语法糖(写是这么写,实际不是这么回事)
2. 开发环境下会将JSX编译成js代码(和vue2.0相似,模板是在开发环境下写的,在上线之前要变异成render函数),真正上线的是js代码
3. JSX的写法大大降低了学习成本和编码工作量
4. 同时,JSX也会增加debug成本
### JSX 独立的标准(不从属于react)
1. JSX是React引入的,但不是React独有的
2. React已经将它作为一个独立标准开放,其他项目也可用
3. React.createElement(核心函数,将jsx转为js)是可以自定义修改的
4. 说明:(可信,可用)本身功能已经完备;和其他标准兼容和扩展性没问题
5. 前端有什么标准??
~~~
npm init
npm i babel -g
npm install --save-dev babel-plugin-transform-react-jsx
~~~
.babelrc
~~~
{"plugins":["transform-react-jsx"]}
~~~
babel -- plugins transform-react-jsx demo.jsx
解析之后
~~~
class Input extends Component{
render(){
return React.createElement(
"div",
null,
React.createElement("input",{value:this.state.title,onChange:thies.changeHandle.bind(this)}),
React,createElement("button",{onClick:this.clickHandle.bind(this)},"submit")
)
}
}
~~~
修改React.createElement 改成h函数
~~~
/* @jsx h */
~~~
### 问题解答(jsx本质)
1. jsx语法(标签,js表达式,判断,循环,事件绑定)
2. jsx是语法糖,需被解析成js才能运行(本质)(vue的模板也需要被解析成js代码才能运行;jsx这个语法糖最终被解析成js的形式:React.createElement核心函数,标签属性子元素,联系到vdom的h函数)
3. JSX是独立的标准,可被其他项目使用(preact)
*****
### jsx和vdom的关系
vdom是伴随react而来的
1. 分析:为何需要vdom
2. React.createElement和h
3. 何时patch?
4. 自定义组件的解析
#### 为何需要vdom
1. vdom是react初次推广开来的,结合jsx
2. jsx就是模板,最终要渲染成html(类似于vue模板必须渲染成html)(必须使用vdom的原因,所有渲染成html都是通过vdom执行的,没有jsx直接操作dom的说法)
3. 初次渲染+修改state后的re-render
4. 正好符合vdom的应用场景(表格,数据变化,表格变化)
#### 再次回顾vdom
1. 介绍snabbdom
2. 核心api:h函数和patch函数
#### React.createElement和h函数
都是返回vnode
~~~
var profile = React.createElement('div',null,
React.createElement('img',{src:'avatar.png',className:'profile'}),
React.createElement('h3',null,[])
)
var vnode = h('div#conatainer.two.classes',{on:{click:someFun}},[
h('span',{style:{fontWeight:'bold'}},'this is bold'),
h('a',{props:{href:'./foo'}},"i will take your places")
])
~~~
vnode的样式
~~~
{
tag:'div',
attrs:{},
children:[
{tag:'img',attrs:{src:"avatar.png",className:"profile"},children:[]},
{tag:'h3',attrs:{},children:[]},
]
}
~~~
#### 何时patch
1. 初次渲染--**ReactDOM.render(<App/>,container)**;(vnode:jsx要转换成js代码的执行,这个js代码执行时要返回vnode,<App/>返回jsx结构,就要转换为js代码执行,返回vnode)
2. 会触发patch(container,vnode)
3. re-render--setState
4. 会触发patch(vnode,newVnode)(对比,渲染时vdom干的事)
~~~
//patch(container,vnode)
ReactDOM.render(<App />,document.getElementById('root'));//触发第一个用法
//patch(vnode,newVnode)
this.setState({
list:currentList.concat(title)
})
~~~
#### 自定义组件的处理(<App /> <Input/>..)
~~~
<div>
<p>this is demo</p>
<Input dataTitle={this.addTitle.bind(this)} />
<List data={this.state.list}>
</div>
~~~
~~~
function render(){
return React.createElement(
"div",
null,
React.createElement(
'p',
null,
'this is demo'
),
React.createElement(Input,{addTitle:this.addTitle.bind(this)}),
React.createElement(List,{data:this.state.list}),
)
}
~~~
return React.createElement(** "div"**, null,‘’)
React.createElement(**Input**,{addTitle:this.addTitle.bind(this)}),
React.createElement(**List**,{data:this.state.list}),
创建普通标签,标签名是字符串,**创建自定义组件,要传入自定义组件的构造函数**(Input,List)
#### 自定义组件的解析
1. 'div',直接渲染<div>即可,vdom可以做到
2. Input和List,是自定义组件(class),**vdom默认不认识**
3. 因此Input和List定义的时候**必须声明render函数**
4. 根据props初始化实例,然后执行实例的render'函数
5. render函数返回的还是vnode对象(render函数是jsx,转换成js后返回vnode)
~~~
var list = new List({data:this.state.list});
var vnode = list.render();
~~~
#### 示例演示
jsx ==> js ==> vnode ==> html
~~~
var app = <App />;
~~~
~~~
React.createElement(App,null)//App是一个构造函数
var app = new App();
return app.render();
~~~
App
~~~
class App extends Component{
render(){
return(<div><Todo/></div>)
}
}
~~~
~~~
React.createElement("div",null,React.createElement(Todo,null));
var todo = new Todo();
todo.render();
~~~
Todo
~~~
<div>
<Input dataTitle={this.addTitle.bind(this)} />
<List data={this.state.list}>
</div>
~~~
~~~
React.createElement('div',null,
React.createElement(Input,{dataTitle: this.dataTitle.bind(this)}),
React.createElement(List,{data: this.state.list}),
)
var input = new Input({dataTitle: this.dataTitle.bind(this));
input.render();
var list = new List({data: this.state.list})
list.render();
~~~
~~~
<ul>
{
list.map((item,index)=>{
return <li key="index">{list}</li>
})
}
</ul>
~~~
~~~
React.createElement('ul',null,list.map((item,index)=>{
return React.createElement('li',{key: index},item)
}))
~~~
此时没有任何自定义组件了
不管组件嵌套多少层,不管从最外层到最里层要经过多少层render函数的调用,最终所有自定义的组件,都是靠html原生的标签支撑的,从一层一层的render函数,最终将自定义的组件转换为最底层的html标签,渲染页面
#### 问题解答( jsx和vdom的关系)
1. 为何需要vdom:jsx(语法糖)需要(最终)渲染成html(需要vdom,因为jsx需要转换成js代码),数据驱动视图(js不可能亲自操作dom,需要中间层vdom来操作渲染vdom)
2. React.createElement和h,都生成vnode(不一样:h函数第一个参数是标签名称。React.createElement的第一个参数两种:html标签名称,自定义组件构造函数的名称)
3. 何时patch:ReactDom.render()(初次渲染)和setState(re-render)(再次渲染)
4. 自定义组件的解析:初始化实例,然后执行render
### 说一下React setState的过程(异步)
不参与开发,只能了解大概,说到重点就行
1. setState的异步
2. vue修改属性也是异步
3. setState的过程
#### setState的异步
~~~
addTitle(title){
const currentList = this.state.list;
console.log(this.state.list);//['a','b']
this.setState({
list:currentList.concat(list);//'c'
})
console.log(this.state.list);//['a','b']
}
~~~
#### setState为何需要异步?
1. 可能会一次执行多次setState
2. 你无法规定,限制用户如何使用setState
3. 没必要每次setState都重新渲染,考虑性能(dom操作非常昂贵的)
4. 即便是每次重新渲染,用户也看不到中间的效果(js的执行和dom渲染时单线程的,将a修改1,2,3,4,5,6,7,8,9,10,js修改过程中,dom渲染时卡顿的,用户看不到过程)
5. 只看到最后的结果即可
#### vue修改属性也是异步的
vue和react两个相互竞争,功能类似的框架,vue修改属性和react的setState也是功能基本相似的,两者功能基本一样
1. 效果、原因和setState一样
2. 对比记忆,印象深刻
3. 复习vue的渲染流程
#### vue的整个实现流程
1. 解析模板成render函数
2. 响应式开始监听
3. 首次渲染,显示页面,且绑定依赖
4. data属性变化,触发rerender
* 修改属性,被响应式的set监听到(没有异步)
* **set中执行updateComponent(异步)**
* updateComponent重新执行vm.render()
* 生成vnode和preVnode,通过patch进行对比
* 渲染到html中
#### setState的过程
1. 每个组件实例,都要renderComponent方法(expends Component,继承的Component中的方法)
2. 执行renderComponent 会**重新执行实例的render**,(重新按照最新的数据,生成最新的vnode)
3. render函数返回newVnode,然后拿到preVnode
4. 执行patch(preVnode,newVnode)
setState后面可加回调函数,
~~~
addTitle(title){
const currentList = this.state.list;
console.log(this.state.list);//['a','b']
this.setState({
list:currentList.concat(list);//'c'
},()=>{
console.log(this.state.list);//['a','b','c']
//不用手动调用
this.renderCompoent();
})
console.log(this.state.list);//['a','b']
}
~~~
~~~
class Component{
constructor(props){},
//模拟
renderComponent(){
const prevVnode = this._vnode;
const newVnode = this.render();
patch(prevVnode , newVnode );
this._vnode = newVnode;
}
}
~~~
#### 问题解答(setState)
1. setState 异步:效果,原因(提供效率,汇集只渲染一次)
2. vue修改属性也是异步:效果,原因(位置:修改后set监听到,执行updateComponent时)(避免用户重复修改数据,页面不能每次修改就渲染一次,通过异步汇总结果)
3. setState的过程,最终走到patch(preVnode,newVnode)
## 总结
看穿现象,看到本质
1. 说一下对组件化的理解(组件的封装,组件的复用)
2. jsx的本质(jsx语法,语法糖,解析成js才能运行,vue的模板也需要解析成js,独立标准,其他项目也能用)
3. jsx和dom的关系(为何使用vdom,React.createelement和h都生成vdom,何时patch:ReactDom.render和setState,自定义组件的解析:实例化,render)
4. 说一下setState的过程(异步,vue修改属性也是异步,setState的过程,最终走到patch)
5. 阐述自己对React和vue的认识
## React和vue的对比
技术选型没有绝对的对与错,考虑的因素也特别多(不是自己做,团队,小组,学习成本等),要有自己的主见
1. 两者的本质区别
* vue-本质是mvvm框架(最外层),由mvc发展而来
* React-本质是前端组件化框架(最外层),由后的组件化发展而来
* 但并不妨碍他们两者都能实现相同的功能
2. 看模板和组件化的区别
* vue-使用模板(最初由angular(4.或5了)提出,非常详细)(各种指令 vif vfor)
* React - 使用jsx,(jsx已经标准化)
* 模板语法上,更加倾向于JSX(标准化)
* 模板分离上,更倾向于vue
模板的区别
~~~
<div>
<h1 v-if="ok">Yes</h1>
<h1 v-else>No</h1>
</div>
<ul id="xx">
<li v-for="(item in items)">{{item.message}}</li>
</ul>
~~~
~~~
<div>
{ok?<h1>Yes</h1>:<h1>No</h1>}
</div>
<ul id="xx">
{
items.map((item,index)=>{
return <li key ={index}>{item.message}</li>
})
}
</ul>
~~~
vue:v-if v-else现学,v-if="ok" ok是js变量,用引号引起来,想html本身的属性
jsx:大括号里的能放js变量表达式,虽然模板和数据进行了分离,但模板和js混在一起,未分离
* 模板应该和js逻辑分离
* 回顾“开发封闭原则”
组件化的区别
* React本身就是组件化,没有组件化就不是React
* vue也支持组件化,不过是在mvvm上的扩展
* vue组件化的文档(非常多,不是简单易用)
* 对于组件化,更倾向于react,做的更彻底清晰
3. 两者的共同点
* 都支持组件化(不支持组件化,就像js不支持模块化,维护代码会多,很麻烦)
* 都是数据驱动视图,数据和视图的分离
4. 总结问题的答案
* 国内使用,首推vue。文档更易读,易学,社区够大(基础知识不是那么扎实)
* 如果团队水平较高,推荐使用React,组件化和jsx
*
- 空白目录
- 双樾
- JS基础知识
- JS-WEB-API
- 开发环境
- 运行环境
- ES6
- 原型
- 异步
- 虚拟dom
- mvvm
- 组件化和React
- hybrid
- 其他
- 补充
- 技巧
- 快乐动起来呀
- css
- 掘金小册子
- js基础知识
- ES6知识点
- JS异步
- JS进阶知识
- 思考题
- DevTools Tips
- 浏览器基础知识
- 浏览器缓存机制0
- 浏览器渲染原理
- 安全防范知识点0
- 从V8中看JS性能优化0
- 性能优化琐碎事
- Webpack性能优化0
- 实现小型打包工具0
- React和Vue
- Vue生命周期
- vue基础知识点
- Vue响应式
- vue高级
- React基础
- Vue.js技术解密
- 准备工作
- 数据驱动
- new Vue()
- vue实例挂载
- 组件化
- 深入响应式原理
- 编译
- 扩展
- Vue Router
- Vuex