技术胖的前端路线图
红色:精通,且要有对应的实战经验
蓝色:熟练,达到基础练习量
灰色:了解,课程听完即可
黑色:视频学习地址
开源地址:http://gitee.com/jishupang/web_tlas
Simple React Snippets插件:react代码的快速生成器
科学上网?在谷歌浏览器中安装React Developer Tools插件
Easy Mock:如果没有后端数据,可以使用Easy Mock给自己造一个数据
// 安装node
// 判断node与npm有没有安装成功和版本号
node -v
npm -v
// 安装react脚手架
npm install -g create-react-app
// 创建react项目
// 方法一:使用cmd,先使用mkdir创建文件夹,在里面运行下面这条命令
// 方法二:使用vscode里面的terminal运行以下命令
create-react-app 项目名
// 运行react项目
npm run start
// 安装axios
npm install axios // 安装到项目目录中,且不添加任何依赖
npm install -g axios // 安装到全局,npm安装目录下的perfix
npm install -save axios // 安装到项目目录中,并在package.json中的dependencies(生产环境:已发布)中添加依赖(相应的版本)
npm install -save-dev axios // 安装到项目目录中,并在package.json中的dev-dependencies(开发环境中:测试,项目管理)中添加依赖(相应的版本)
npm install react-transition-group --save // 安装动画库
// 项目目录
src/index.js // 项目的入口文件
src/App.js : // 使用模块化编程进行的一个方法模块
src/serviceWorker.js // 移动端开发的,PWA使用这个文件会离线浏览的功能。
// {Component}是一种解构赋值的的写法,相当于 Component = React.Componment
import React,{Component} from 'react'
// 相当于
import React from 'react'
const Component = React.Component()
// 虚拟DOM:快速反应动作,不会占用页面的渲染机制
// jsx语法,在一个 js 组件中,遇到 < > 就当成html来解析,遇到 {} 就当做js来解析
// 使用ReactDOM方式创建元素并挂载到页面中
var child1 = React.createElement('li',null,'JSPang.com')
var child2 = React.createElement('li',null,'I love React')
var root = React.createElement('ul',{className:'my-list',child1,child2})
// 使用jsx方式创建类组件并挂载到对应的位置
class App extends Component{
// 类组件渲染到页面的内容
render() {
return (
// jsx的语法:遇到 < > 就当成html来解析,遇到 {} 就当做js来解析
<ul className='my-list'>
<li>JS1</li>
<li>JS2</li>
</ul>
)
}
}
定义:在某一时刻可以自动执行的函数
// 初始周期
构造函数constructor(set props&state)
// 挂载周期
// componentWillMount 是组件挂载前执行的(可用于 打印日志 和 查询数据库 操作)
// render 是state和props发生改变时会进行的渲染
// componentDidMount 是组件挂载完成后执行的(也可用于 打印日志 和 查询数据库 操作)
componentWillMount --> render --> componentDidMount
// 组件更新周期
// componentWillReceiveProps : 当子组件接受props时执行,当在顶层组件时是不会执行的(组件第一次存在于dom中,函数不会被执行,如果已经存在于dom中,函数才会被执行)
// shouldComponentUpdate : 组件(props或state)更新之前进行执行的,必须返回一个boolean值,返回true会继续向下执行其他生命周期,返回false则不会向下执行了
// componentWillUpdate : 在shouldComponentUpdate后执行,render前执行
// render : state和props发生改变时会进行的渲染
// componentDidUpdate : 改变完成后执行
props : componentWillReceiveProps -- shouldComponentUpdate -- componentWillUpdate -- render -- componentDidUpdate
states : shouldComponentUpdate -- componentWillUpdate -- render -- componentDidUpdate
// 组件删除周期
componentWillUnmount : 组件被删除时执行
DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Web site created using create-react-app"
/>
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<title>React Apptitle>
head>
<body>
<noscript>如果js代码受到阻止或没有跑成功会显示的内容noscript>
<div id="root">div>
<div>knb dmb d ddiv>
body>
html>
项目入口文件
import React from 'react'
import ReactDOM from 'react-dom'
import XiaoJieJie from './XiaoJieJie'
// 将 组件挂载到public/index.html 下面id="root"的div
ReactDOM.render(<XiaoJieJie/>,document.getElementById('root'))
类组件:XiaoJieJie父组件
// {Component}是一种解构赋值的的写法,相当于 Component = React.Componment
import React,{Component,Fragment} from 'react'
import XiaoJieJieItem from './XiaoJieJieItem' // 引入新的组件
import axios from 'axios'
import './index.css'
// 在js中写html的写法(类组件和函数组件)
// 类组件
class XiaoJieJie extends Component{
// 初始周期:构造函数(set props&state)
constructor(props){
super(props) // props调用的是父类Component的
this.state = {
inputValue : '',
// react只能遍历数组类型的数据,不能遍历对象类型的数据
servicelist : ['指甲油','种睫毛']
}
}
// 挂载周期:componentWillMount --> render --> componentDidMount
// componentWillMount : 组件挂载前执行的(可用于 打印日志 和 查询数据库 操作)
// render : state和props发生改变时会进行的渲染
// componentDidMount : 组件挂载完成后执行的(也可用于 打印日志 和 查询数据库 操作)
// componentWillMount(){
// console.log("componentWillMount -- 组件将要挂载到页面时执行")
// }
// 用于请求远程数据,使用componentDidMount生命周期比较合适,只加载一次,后续不再进行请求
componentDidMount(){
// axios请求远程数据
// https://web-api.junjin.im/v3/web/wbbr/bgeda是要访问的后台接口
axios.post('https://baidu.com')
.then((res)=>{
console.log('axios获取数据成功'+JSON.stringify(res))
})
.catch((error)=>{
console.log('axios获取数据失败'+error)
})
// console.log("componentDidMount -- 组件挂载到页面后执行")
}
// 组件更新周期
// props : componentWillReceiveProps -- shouldComponentUpdate -- componentWillUpdate -- render -- componentDidUpdate
// states : shouldComponentUpdate -- componentWillUpdate -- render -- componentDidUpdate
// componentWillReceiveProps : 当子组件接受props时执行,当在顶层组件时是不会执行的(组件第一次存在于dom中,函数不会被执行,如果已经存在于dom中,函数才会被执行)
// shouldComponentUpdate : 组件(props或state)更新之前进行执行的,必须返回一个boolean值,返回true会继续向下执行其他生命周期,返回false则不会向下执行了
// componentWillUpdate : 在shouldComponentUpdate后执行,render前执行
// render : state和props发生改变时会进行的渲染
// componentDidUpdate : 改变完成后执行
// componentWillReceiveProps(){
// console.log("componentWillReceiveProps")
// }
// shouldComponentUpdate(){
// // 必须返回一个boolean值
// // 如果return true 就会继续往下执行componentWillUpdate和render了
// // 如果return false 就不会往下执行componentWillUpdate和render
// return false
// }
// componentWillUpdate(){
// console.log("2 -- componentWillUpdate")
// }
// componentDidUpdate(){
// console.log("4 -- componentDidUpdate")
// }
// 组件删除周期
// componentWillUnmount : 组件被删除时执行
// 类组件渲染到页面的内容
// jsx的语法:遇到 < > 就当成html来解析,遇到 {} 就当做js来解析
render(){
console.log("3 -- render -- state和props发生改变时会进行的渲染")
return (
// 虚拟DOM只能由一个根标签,且标签必须闭合
// 可以使用Fragment,也可以使用<>空标签
<Fragment>
<div>
{/* onChange事件绑定,当向输入框中输入内容时,会调用his.inputChange方法 */}
{/* react事件要绑定this(指针+时间绑定问题),bind(this),这样inputChange方法中的this就会指向当前XiaoJieJie组件了,使用箭头函数就不需要考虑绑定问题了 */}
<label htmlFor="servicelabel">增加服务:</label>
<input
id="servicelabel" // label中的htmlFor用来绑定点击被激活的id,点击label就会激活输入框
className="serviceinputCss" // 引入css,需要使用className="serviceinputCss",不能直接使用class,以免跟类组件的class重名
value={this.state.inputValue}
onChange={this.inputChange.bind(this)}
ref = {(input) => { // 使用ref箭头函数对输入框中的值进行绑定,input相当于输入的内容
this.input = input
}}
/>
<button onClick={this.addService.bind(this)}>增加服务</button>
</div>
{/* 给ul进行ref绑定,这样就可以获取ul这个对象 */}
<ul ref={(ul) => {this.ul = ul}}>
{
// 使用map来进行循环遍历数组
// map函数内部使用的是一个箭头函数,前面一个括号是参数,后面一个大括号是执行的函数
// item是数组中的每一项,index是每一项的索引值(索引是从0开始的)
this.state.servicelist.map((item,index) => {
// map循环遍历需要有一个key值,不能只使用索引值,因为index索引值会重复,可以加上item项来减少重复的可能
return ( // 只有return加一个小括号,才支持html标签换行
//
// key={index+item}
// onClick={this.deleteItem.bind(this,index)} // 删除单个服务项,要传递一个index索引项
// dangerouslySetInnerHTML={{__html:item}} // 将每一项都使用dangerouslySetInnerHTML来解析,这样可以将输入html代码解析,显示成对应的样式,如烫睫毛
,会显示成一个黑体加粗的烫睫毛
// >
//
<XiaoJieJieItem
key = {index+item} // 用来循环时有一个唯一的key值
serviceitem = {item} // 父组件给子组件传值:属性={值}的方式
index = {index}
deleteItem = {this.deleteItem.bind(this)} // 父组件给子组件传方法:属性={this.方法名.bind(this)}的方式
/>
)
})
}
</ul>
</Fragment>
)
}
// 改变输入框中的值
inputChange(e){
// this.state.inputValue = e.target.value 在react框架中这是一个错误的方法
// 需要使用setState的方法赋值
this.setState({
inputValue : this.input.value // 使用ref箭头函数对输入框中的值进行绑定
// inputValue : e.target.value // e.target.value 获取输入框的内容
})
}
// 添加服务按钮
addService(){
// 需要使用setState的方法赋值
if(this.state.inputValue === ''){
alert('您还没有输入任何服务')
}else{
// 虚拟DOM,渲染是需要时间的
// this.setState是一个异步的方法,是浏览器在空闲的时候才会执行的内容
this.setState({
// ... :表示扩展运算符,相当于将数组中的每一个值都放到新数组中
// servicelist:['指甲油','种睫毛',this.state.inputValue],用中括号括住表示在数组中再添加一个值
servicelist:[...this.state.servicelist,this.state.inputValue],
inputValue:''
},() => { // 如果遇到ref绑定错误或获取数据不准确,填setState的坑,在setState后面提供了一个回调函数,在这里面来进行后续的一些操作
console.log(this.ul.querySelectorAll('li').length) // 使用ref获取ul对象,使用querySelectorAll来获取所有li的子节点
})
}
}
// 删除单个服务项
deleteItem(index){
// 一个超级大的坑
// 正确写法是获取一个局部的newservicelist,将其赋值给声明的变量中,然后再操作局部数组的值,最后再使用setState来提交赋值
// 错误写法:不能直接操作 this.state.servicelist 中的值,因为后期做性能优化的时候比较麻烦
let newservicelist = this.state.servicelist
newservicelist.splice(index,1) // splice方法代表从index索引项开始删除一个值
this.setState({
servicelist:newservicelist
})
}
}
// ES6导出语法
export default XiaoJieJie
类组件:XiaoJieJieItem子组件
// 拆分组件
// 单向数据流:子组件只能接受父组件传的值,但是不能直接干预父组件,就是不能给父组件直接传值
// 父组件可以给子组件传值:
// 子组件调用父组件传的值:{this.props.serviceitem}
// 解决单向数据流的问题:父组件给子组件传递一个方法,通过子组件调用父组件的方法来解决
// 父组件给子组件传方法:
// 子组件调用父组件传的方法:{this.props.deleteItem}
import React, { Component } from 'react'
// 父组件对子组件传值进行数据校验:PropTypes
import PropTypes from 'prop-types'
class XiaoJieJieItem extends Component {
constructor(props){
super(props)
// 在构造函数中给函数名进行绑定,方便以后的性能优化,在下面就只需要使用this.handleClick,就不需要再写bind(this)
this.handleClick = this.handleClick.bind(this)
}
// 进行性能优化
// 如果输入框中的值不等于父组件传的值,就继续执行后续的render等生命周期
// 如果相等,就说明没有在操作输入框了,就不继续执行后续的生命周期了
shouldComponentUpdate(nextProps,nextState){
if( nextProps.serviceitem != this.props.serviceitem){
return true
}else{
return false
}
}
// 在接受父组件传来的props时执行
// componentWillReceiveProps(){
// console.log("componentWillReceiveProps")
// }
// componentWillUnmount : 组件被删除时执行
// componentWillUnmount(){
// console.log("组件被删除时执行")
// }
// 函数式编程:在类组件中写方法
// 好处:代码清晰,前端自动化测试更加方便
render() {
return (
// 使用 “{this.props.调用子组件时传值的属性}” 来获取父组件给子组件传的值
<li onClick={this.handleClick}>
{this.props.avname}为你服务--{this.props.serviceitem}
</li>
);
}
handleClick(){
// 调用父组件的删除方法
this.props.deleteItem(this.props.index)
}
}
// PropTypes用来对传递的数据进行校验
// isRequired代表这个数据必须由父组件传递,不传递就会报错
XiaoJieJieItem.propTypes = { // 上面的p是小写 propTypes ,下面的P是大写 PropTypes
avname:PropTypes.string.isRequired,
serviceitem:PropTypes.string, // serviceitem是string类型的
index:PropTypes.number, // index是number类型的
deleteItem:PropTypes.func // deleteItem是func方法
}
// 如果使用了isRequired又不想传值,就需要在子组件中设置一个默认的值
XiaoJieJieItem.defaultProps = {
avname:'小敏'
}
export default XiaoJieJieItem;
import React, { Component } from 'react'
import './index.css'
class HelloComponent extends Component {
// 构造器调用一次
constructor(props) {
// 在super前不能调用this
super(props);
// 解决updateWeather中this的指向问题
this.updateWeather = this.updateWeather.bind(this)
this.state={
content:'前端js框架列表',
// isHot:true,
// react只能遍历数组类型的数据,不能遍历对象类型的数据
list:['Angular','React','Vue'],
}
}
// 可以将state中的值直接写到类组件中
state={isHot:true}
// render调用1+n次,第一次调用是初始化调用,n是状态更新的次数
render() {
// 使用解构赋值的方式先取到isHot
// const {isHot} = this.state.isHot
return (
// 虚拟DOM只能由一个根标签,且标签必须闭合
<>
{/*
给html标签加样式
方式一:className,要引入对应的css文件
方式二:内联样式,style={{'key':'value'}}
*/}
<h2 id='atguigu' className='title' style={{'color':'green'}}>
<span>{this.state.content}</span>
{/* onClick={this.updateWeather} 不能写成 onClick={this.updateWeather()} 等被点击的时候才调用函数,而不是直接调用函数然后进行赋值*/}
<span>今天天气很{this.state.isHot?'炎热':'凉爽'}</span><button onClick={this.updateWeather}>点击</button>
</h2>
<ul>
{/* 大括号里面不能写js语句【if,for,switch】,只能写js表达式【表达式都会有一个只,如a,a+b,demo(1),arr.map(),function test(){}】 */}
{
this.state.list.map((item,index) => {
return <li key={item+index}>{item}</li>
})
}
</ul>
</>
);
}
// updateWeather放在HelloComponent原型上,通过实例调用
// 由于updateWeather是onCLick的回调,不是通过实例调用的,是直接调用
// 类中的方法默认开启了局部的严格模式,所以updateWeather中的this为undefined
// 但当调用时采用bind(this),绑定了this,this就指向HelloComponent组件实例了
updateWeather(){
const isHot = this.state.isHot
// 状态不能直接更改,需要借助setState来修改状态,且更新是一种替换,而不是更新
this.setState({
isHot:!isHot
})
}
}
export default HelloComponent;
import React, { Component } from 'react'
import {CSSTransition,TransitionGroup} from 'react-transition-group'
import './index.css'
class Boss extends Component {
constructor(props) {
super(props);
this.state = {
isShow : true
}
this.toToggole=this.toToggole.bind(this) // 让this指向正确
}
render() {
return (
<>
<CSSTransition
in={this.state.isShow} // 开关打开:显示文字
timeout={2000} // 显示出来的时间2s
classNames="boss-text" // 这里是index.css中的样式前缀
unmountOnExit // DOM元素退场时直接给元素删除
>
{/* Boss级任务 -- 孙悟空 */}
<div>Boss级任务 -- 孙悟空</div>
</CSSTransition>
<div>
<button onClick={this.toToggole}>召唤Boss</button>
</div>
</>
);
}
toToggole(){
this.setState({
isShow : this.state.isShow ? false : true
})
}
}
export default Boss;
.serviceinputCss{
border: 3px solid #ae7000;
}
.show{
/*
opacity: 1;
transition: all 1.5s ease-in; // 动画形式消出现
*/
animation: show-item 2s ease-out forwards;
}
.hide{
animation: hide-item 2s ease-out forwards; /* forwards 可以在动画运行到最后一帧时停止,否则会跳到第一帧 */
}
/* 使用@keyframes创建动画 */
@keyframes show-item{
/* 添加关键帧 */
0%{
opacity: 0;
color: yellow;
}
50%{
opacity: 0.5;
color: red;
}
100%{
opacity: 1;
color: green;
}
}
@keyframes hide-item{
/* 添加关键帧 */
0%{
opacity: 1;
color: yellow;
}
50%{
opacity: 0.5;
color: red;
}
100%{
opacity: 0;
color: green;
}
}
/* enter是指动画入场的样式 */
boss-text-enter{
opacity: 0;
}
/* enter-active是指动画入场到结束之前的样式 */
boss-text-enter-active{
opacity: 1;
transition: opacity 2000ms;
}
/* enter-done是指动画入场结束后的样式 */
boss-text-enter-done{
opacity: 1;
}
/* exit是指动画出场的样式 */
boss-exit-enter{
opacity: 1;
}
/* exit-active是指动画出场到结束之前的样式 */
boss-text-exit-active{
opacity: 0;
transition: opacity 2000ms;
}
/* exit-done是指动画出场结束后的样式 */
boss-text-exit-done{
opacity: 0;
}