This commit is contained in:
1708-huayu 2023-10-19 14:19:45 +08:00
commit 639450a8c6
42 changed files with 2837 additions and 0 deletions

14
npm/npm镜像设置.md Normal file
View File

@ -0,0 +1,14 @@
查看当前镜像
```apl
npm help config
npm config get registry
```
设置全局镜像
```shell
npm config set registry https://registry.npm.taobao.org --global
```
版本管理:[GitHub - nvm-sh/nvm: Node Version Manager - POSIX-compliant bash script to manage multiple active node.js versions](https://github.com/nvm-sh/nvm#usage)

79
react/1.概述.md Normal file
View File

@ -0,0 +1,79 @@
# 原生JavaScript,dom
1. 原生JavaScript操作DOM繁琐效率低
2. 使用JavaScript直接操作DOM,浏览器会进行大量的重绘重排.
3. 没有组件化编码方案,代码复用率低
# React的特点
用于构建用户界面的javaScript库,操作DOM呈现页面
## 声明式
相对命令式.
## *组件式
## 学习一次随处使用
开发web应用
开发移动端原生应用,react-native
VR开发react360
浏览器安装React Developer Tool
#### 模块
1. 理解:向外提供特定功能的js程序,一般就是一个js文件
2. 为什么要拆成模块:随着业务逻辑增加,代码越来越多且复杂
3. 作用:复用js,简化js编写,提高js运行效率
#### 组件
1. 理解:用来实现局部功能效果的代码和资源的集合(html,css,js,image等等)
2. 为什么:一个界面的功能更复杂
3. 作用:复用编码,简化项目编码,提高运行效率.
# 使用
npm升级
```shell
npm install -g npm@9.2.0
npm i create-create-app -g
```
## 创建项目
```shell
npx create-react-app my-app
```
```shell
npm i react react-dom
```
## 引入 react和react-dom两个js文件
## 创建React元素
React.createElement('元素名称,标签h,p',属性,子节点,子节点,子节点...)
## 渲染react元素到页面中
ReactDOM.render(要渲染的元素,渲染挂载点)
```html
<html>
<div id= 'root'></div>
<body>
<script src= "./node_modules/react/umd/react.development.js"></script>
<script src="./node_modules/react-dom/umd/react-dom.development.js"></script>
<script>
// const title = React.createElement('h1',null,'hello xian')
// const title = React.createElement('p',{title:"我是段落",id:"p1"},'hello xian')
const title = React.createElement('p',{title:"我是段落",id:"p1"},'hello xian',React.createElement('span',null,'hello hua'))
ReactDOM.render(title,document.getElementById('root'))
</script>
</body>
</html>
```
# React脚手架的使用
```shell
npx create-react-app my-app
npm start
```
引入改成导入
```
// es6中模块语法
import React from 'react'
import ReactDOM from 'react-dom'
```
```html
import React from 'react';
import ReactDOM from 'react-dom';
const title = React.createElement('p',{title:"我是段落",id:"p1"},'hello xian',React.createElement('span',null,'hello hua'))
ReactDOM.render(title,document.getElementById('root'))
```

106
react/2.jsx.md Normal file
View File

@ -0,0 +1,106 @@
# createElement
劣势
1. 繁琐不简洁
2. 不直观,不能一眼看出代码表述
3. 不优雅,用户体验差
# jsx
语法与html类似
JSX 是JavaScript XML的简写,表示在JavaScript代码中写xml(html)格式的代码.React的核心内容
优势:声明式语法更加直观,与html结构相同,降低成本,提高开发效率
React完全利用js语言自身能力来编写UI,而不是造轮子增强HTML功能(没有自定义指令).
```html
import React from 'react';
import ReactDOM from 'react-dom';
// const title = React.createElement('p',{title:"我是段落",id:"p1"},'hello xian',React.createElement('span',null,'hello hua'))
// jsx
const title = <p title='我是段落' id='p1'>hello xian<span>hello hua</span></p>
ReactDOM.render(title,document.getElementById('root'))
```
# 脚手架中使用jsx语法
1. JSX不是标准的ECMAScript语法,是他的拓展
2. 需要使用babel(@babel/preset-react)编译后,才能在浏览器中使用
3. create-react-app脚手架中已经默认有了该配置无需配置.
# 基本用法
1. 使用驼峰命名规则
2. 特殊的属性名 class->className,for->htmlFor,tabindex->tabIndex
3. 没有子节点可以使用/结尾
4. 推荐在小括号内写JSX
```html
const title = (<p className='title' title='我是段落' id='p1'>hello xian<span>hello hua</span></p>)
```
### 嵌入js
{JavaScript表达式}
```html
const name = "----------------------------"
const title = (<p className='title' title='我是段落' id='p1'>hello xian {name}<span>hello hua</span></p>)
```
### jsx条件渲染
根据条件渲染特定的jsx结构
if else ,三目,逻辑与运算符
```html
const isLoading = false
const loadData= ()=>{
// if(isLoading){
// return <div>loading...</div>
// }
// return <div>数据加载完成,此处显示加载后的数据</div>
// return isLoading?(<div>loading...</div>):(<div>数据加载完成,此处显示加载后的数据</div>)
return isLoading && (<div>loading...</div>)
}
const title = (
<h1>
条件渲染{loadData()}
</h1>
)
```
### jsx 列表渲染
```html
const songs = [
{id:1,name:'有为歌'},
{id:2,name:'水手'},
{id:3,name:'都是月亮惹的祸'}
]
const list = (
<ul>
{songs.map(item=><li key={item.id}>{item.name}</li>)}
</ul>
)
ReactDOM.render(list,document.getElementById('root'))
```
### jsx的样式处理
#### 行内样式 style
```html
const listStyle = (
<h1 style={{color:'red',backgroundColor:'skyblue'}}>行内样式</h1>
<h1 className = "title">类样式</h1>
)
```
#### 类名 className
```html
import './index.css'
const listStyle = (
<h1 className = "title">类样式</h1>
)
```
css
```html
.title{
text-align: center;
}
```

343
react/3.react组件.md Normal file
View File

@ -0,0 +1,343 @@
# 组件介绍
组件表示页面中的部分功能
组合多个组件实现完整的页面功能
可复用,独立(相互之间互不影响),可组合
# 组件的创建方式
## 使用函数创建组件
函数名称必须以大写字母开头
必须有返回值,返回组件结构
如果返回null表示不渲染任何内容
渲染组件:使用函数名作为组件标签名
```html
// function Hello(){
// return (<div>展示的返回函数组件</div>)
// }
const Hello =()=><div>展示的返回函数组件</div>
ReactDOM.render(<Hello />,document.getElementById('root'))
```
## 使用类创建组件
类名称必须以大写字母开头
类组件必须继承React.Component父类
必须提供render方法
render方法必须有返回值.
```html
class HelloClass extends React.Component{
render(){
return (<div>leizujian</div>)
}
}
ReactDOM.render(<HelloClass />,document.getElementById('root'))
```
## 抽离到独立的js文件
1. 创建HelloRender.js文件
2. 在HelloRender.js文件中导入React
3. 创建组件
4. 在HelloRender.js中导出该组件
5. 在index.js中导入HelloRender组件
6. 渲染组件
```html
import HelloRender from './learn/HelloRender'
ReactDOM.render(<HelloRender />,document.getElementById('root'))
```
```
import React from "react"
class HelloRender extends React.Component{
render(){
return (
<div>抽离到js文件中的组件</div>
)
}
}
export default HelloRender
```
# 事件处理
驼峰命名
on+事件名称={事件处理程序}
## 类
```html
class AppOnClick extends React.Component{
handleClick(){
console.log('单机')
}
render(){
return (
<button onClick={this.handleClick}>点击</button>
)
}
}
ReactDOM.render(<AppOnClick />,document.getElementById('root'))
```
## 函数
```html
function AppOnClickFun(){
function handleClick(){
console.log('单机function')
}
return (
<button onClick={handleClick}>点击</button>
)
}
ReactDOM.render(<AppOnClickFun />,document.getElementById('root'))
```
## 事件对象
```html
class AppOnClickUrl extends React.Component{
handleClick(e){
// 阻止浏览器默认跳转行为
e.preventDefault()
console.log('单机,阻止浏览器默认跳转行为')
}
render(){
return (
<a href="http://www.baidu.com" onClick={this.handleClick}>点击</a>
)
}
}
ReactDOM.render(<AppOnClickUrl />,document.getElementById('root'))
```
## 有状态组件和无状态组件
状态(state)即数据
函数组件又叫无状态组件,只负责数据展示(静)
类组件又叫有状态组件,负责更新UI,让页面动起来
## 组件中的state和setState
```html
class AppState extends React.Component{
constructor(){
super()
this.state= {
count:0
}
}
// state= {
// count:0
// }
render(){
return (
<div>有状态组件,{this.state.count}</div>
)
}
}
ReactDOM.render(<AppState />,document.getElementById('root'))
```
修改状态
```html
class AppState extends React.Component{
constructor(){
super()
this.state= {
count:0,
test:'a'
}
}
// state= {
// count:0
// }
render(){
return (
<div>有状态组件,{this.state.count},{this.state.test}
<button onClick={()=>{
this.setState({
count:this.state.count+1
})
}}>+1</button>
</div>
)
}
}
ReactDOM.render(<AppState />,document.getElementById('root'))
```
事件处理程序中this的值为undefined,希望this指向组件实例
事件绑定this指向
1. 箭头函数,利用箭头函数自身不绑定this的特点.
```html
class AppStateJTThis extends React.Component{
constructor(){
super()
this.state= {
count:0
}
}
onIncrement(){
console.info('事件处理this'+this)
this.setState({
count:this.state.count+1
})
}
render(){
return (
<div>有状态组件,{this.state.count}
<button onClick={()=>this.onIncrement()}>+1</button>
</div>
)
}
}
ReactDOM.render(<AppStateJTThis />,document.getElementById('root'))
```
2. Function.prototype.bind()利用ESS中bind方法,将时间处理程序中的this与组件实例绑定到一起
```html
class AppStateJTThis extends React.Component{
constructor(){
super()
this.onIncrement= this.onIncrement.bind(this)
this.state= {
count:0
}
}
onIncrement(){
console.info('事件处理this'+this)
this.setState({
count:this.state.count+1
})
}
render(){
return (
<div>有状态组件,{this.state.count}
<button onClick={this.onIncrement}>+1</button>
</div>
)
}
}
ReactDOM.render(<AppStateJTThis />,document.getElementById('root'))
```
3. class实例方法
```html
class AppStateJTThis extends React.Component{
constructor(){
super()
this.state= {
count:0
}
}
onIncrement=()=>{
console.info('事件处理this'+this)
this.setState({
count:this.state.count+1
})
}
render(){
return (
<div>有状态组件,{this.state.count}
<button onClick={this.onIncrement}>+1</button>
</div>
)
}
}
ReactDOM.render(<AppStateJTThis />,document.getElementById('root'))
```
方法是一个特殊的属性.
类中的方法默认开启了局部严格模式,所以changeWeather中的this为undefined,
use strict 使用严格模式
实例调用,直接调用(严格模式下,this为undefined)
组件内的标签可以定义ref属性来标识自己
箭头函数中的this找外层的this.
## 事件处理
1. 通过onXxx属性指定事件处理函数,必须驼峰
1. React使用的是自定义合成事件,而不是使用原生DOM事件,为了更好兼容性
2. React中的事件是通过事件委托方式处理的(委托给组件最外层的元素),为了更高效
2. 可以通过event.target得到发生事件的DOM元素对象,避免过度使用ref
# 表单处理
## 受控组件,其值受到React控制的表单元素
1. html中表单元素是可输入的,也就是有自己的可变状态
2. react中可变状态通常通过state处理,并且只能通过setState()来处理
3. react将state与表单元素值value绑定到一起,由state的值来控制表单元素的值
4. 类似vue中双向绑定
### 使用步骤
1. 在state中添加一个状态,作为表单元素的value值(控制表单元素的来源)
2. 给表单元素绑定change事件,将表单元素的值设置为state值(控制表单元素值的变化)
### 使用一个事件处理程序同时处理多个表单元素
1. 给表单添加一个name属性,名称与state相同
2. 根据表单元素类型获取对应值
3. 在change事件处理程序中通过name来修改对应的state
```html
class AppInput extends React.Component{
constructor(){
super()
this.state= {
txt:'请输入值',
context:'',
city:'bj',
isChecked:false
}
}
handleClick=e=>{
console.info('事件处理this'+this)
const target = e.target;
const value = target.type === 'checkbox'?target.checked:target.value
const name = target.name
this.setState({
[name]: value
})
console.info('name:'+name+',value:'+value)
}
render(){
return (
<div><input type='text' name='txt' value = {this.state.txt} onChange={this.handleClick}/>
<br/>
<textarea value={this.state.context} name= 'context' onChange={this.handleClick}/>
<br/>
<select value={this.state.city} onChange={this.handleClick} name = 'city'>
<option value="bj">北京</option>
<option value="sh">上海</option>
<option value="gz">广州</option>
</select>
<input type='checkbox' name='isChecked' checked = {this.state.isChecked} onChange={this.handleClick}/>
</div>
)
}
}
ReactDOM.render(<AppInput />,document.getElementById('root'))
```
### 高阶函数
如果一个函数符合下面2个规范中的任何一个,那该函数就是高阶函数.
1. 若A函数,接收的参数是一个函数,那么A就可以称之为高阶函数
2. 若A函数,调用的返回值依然是一个函数,那么A就可以称之为高阶函数
函数的柯里化:通过函数调用继续返回函数的方式,实现多次接收参数最后统一处理的函数编码形式.
## 非受控组件
借助于ref,使用原生DOM方式来获取表单元素值
ref作用:获取DOM或组件
```html
class APPRef extends React.Component{
constructor(){
super()
// 创建ref对象
this.txtRef = React.createRef()
}
getTxt=()=>{
console.log('文本框值为',this.txtRef.current.value)
}
render(){
return(
<div>
<input type="text"ref = {this.txtRef}/>
<button onClick= {this.getTxt}>获取文本信息</button>
</div>
)
}
}
ReactDOM.render(<APPRef />,document.getElementById('root'))
```

View File

@ -0,0 +1,99 @@
```html
import React from "react";
class CommentList extends React.Component {
state = {
comments: [
// { id: 1, name: 'shi', content: '沙发' },
// { id: 2, name: 'xiao', content: '板凳' },
// { id: 3, name: 'hua', content: '地板' }
],
userName:'',
userContext:''
}
renderList() {
const {comments}= this.state
if (comments.length === 0) {
return (<div className="no-Comment">暂无评论</div>)
}
return (<ul>
{
comments.map(item => (
<li key={item.id}>
<h4>评论人:{item.name}</h4>
<p>评论:{item.content}</p>
</li>
))
}
</ul>)
// return this.state.comments.length === 0 ? (<div className="no-Comment">暂无评论</div>) :
// (<ul>
// {
// this.state.comments.map(item => (
// <li key={item.id}>
// <h4>评论人:{item.name}</h4>
// <p>评论:{item.content}</p>
// </li>
// ))
// }
// </ul>)
}
handleForm=(e)=>{
const {name,value}= e.target
this.setState({
[name]:value
})
}
addComment=()=>{
const {comments,userName,userContext}= this.state
// 非空校验
if(userName.trim()===""||userContext.trim()===""){
alert('请输入评论内容')
return
}
const newComments = [{
id:Math.random(),
name:userName,
content:userContext
},...comments]
this.setState({
comments:newComments,
// 清空文本空
userContext:'',
userName:''
})
}
render() {
const {userName,userContext}= this.state
return (
<div className="app">
<div>
<input className="user" type="text" name="userName" value={userName} placeholder="请输入评论人" onChange={this.handleForm} />
<br />
<textarea className="content" cols="30" rows="10" name="userContext" value={this.state.userContext} placeholder="请输入评论内容" onChange={this.handleForm} />
<br />
<button onClick={this.addComment}>发表评论内容</button>
</div>
{/* {this.state.comments.length === 0 ? (<div className="no-Comment">暂无评论</div>) :
(<ul>
{
this.state.comments.map(item => (
<li key={item.id}>
<h4>评论人:{item.name}</h4>
<p>评论:{item.content}</p>
</li>
))
}
</ul>)
} */}
{this.renderList()}
</div>
)
}
}
export default CommentList
```

294
react/5.组件进阶.md Normal file
View File

@ -0,0 +1,294 @@
# 组件通讯
多组件之间共享通讯
## props接收传递给组件的数据
传递数据:给组件标签添加属性
函数组件 props
```html
const HelloProps = (props)=>{
console.log(props)
return (
<div>props:{props.name}</div>
)
}
ReactDOM.render(<HelloProps name= "hua"/>,document.getElementById('root'))
```
类组件 this.props
```html
class HelloProps extends React.Component{
render(){
return (<div>props:{this.props.name},{this.props.role}</div>)
}
}
ReactDOM.render(<HelloProps name= "hua" role="shi"/>,document.getElementById('root'))
```
## 组件特点
1. 可以是任意值
```html
class HelloProps extends React.Component{
render(){
console.log(this.props)
return (<div>props:{this.props.name},{this.props.role} {this.props.tag}</div>)
}
}
ReactDOM.render(<HelloProps name= "hua" role="shi" age = {19}
colors = {['red','green','blue']}
fn={()=>console.log('我可以传函数')}
tag={<p>jsx</p>}
/>,document.getElementById('root'))
```
2. 只读
3. 在使用类组件时,如果写了构造函数,应该将props传递给super(),否则,无法在构造函数中获取到props
```html
class HelloProps extends React.Component{
constructor(props){
super(props)
console.log(this.props)
}
render(){
console.log(this.props)
return (<div>props:{this.props.name},{this.props.role} {this.props.tag}</div>)
}
}
ReactDOM.render(<HelloProps name= "hua" role="shi" age = {19}
colors = {['red','green','blue']}
fn={()=>console.log('我可以传函数')}
tag={<p>jsx</p>}
/>,document.getElementById('root'))
```
## 通讯的三种方式
### 父组件传递给子组件
1. 父组件提供传递的state数据
2. 给子组件添加属性,值为state中的数据
3. 子组件中通过props接收父组件中传递的数据
```html
class Parent extends React.Component{
state = {lastName:'师'}
render(){
return (
<div className='parent'>
父组件:
<Child name = {this.state.lastName}/>
</div>
)
}
}
// 子组件
const Child = (props)=>{
return (
<div className='child'>
<p>子组件接收到父组件的数据 {props.name}</p>
</div>
)
}
ReactDOM.render(<Parent />,document.getElementById('root'))
```
### 子组件传递给父组件
利用回调函数,父组件提供回调,子组件调用,将要传递的数据作为回调函数的参数.
1. 父组件提供一个回调函数(用于接收数据)
2. 将改函数作为属性的值,传递给子组件
3. 子组件通过props调用回调函数
4. 将子组件的数据作为参数传递给回调函数
```html
class Parent extends React.Component{
state={
parentMsg:''
}
getChildMsg = data=>{
console.log('接收到子组件传递过来的数据',data)
this.setState({
parentMsg:data
})
}
render(){
return (
<div className='parent'>
父组件:{this.state.parentMsg}
<Child name = {this.getChildMsg}/>
</div>
)
}
}
// 子组件
class Child extends React.Component{
state = {
msg:"回调传递"
}
handleClick=()=>{
//
this.props.name(this.state.msg)
}
render(){
return (
<div className='child'>
<p>子组件发送到父组件</p><button onClick={this.handleClick}>点击</button>
</div>
)
}
}
ReactDOM.render(<Parent />,document.getElementById('root'))
```
## 兄弟组件间的通讯
将共享状态提升到最近的公共父组件中,由公共父组件管理这个状态
状态提升
公共父组件职责:1.提供共享状态2.提供操作共享状态的方法
要通讯的子组件只能通过props接收状态或者操作状态的方法
```html
class Counter extends React.Component{
state={
count:0
}
onIncrement=()=>{
this.setState({
count:this.state.count+1
})
}
render(){
return(
<div>
<Child1 count={this.state.count}></Child1>
<Child2 onIncrement={this.onIncrement}></Child2>
</div>
)
}
}
const Child1=(props)=>{
return <h3>计数器:{props.count}</h3>
}
const Child2=(props)=>{
return <button onClick = {()=>props.onIncrement()}>+1</button>
}
ReactDOM.render(<Counter />,document.getElementById('root'))
```
# Context跨组件传递数据
1. 使用React.createContext()创建Provider,Consumer
2. 使用Provide组件作为父节点
3. 设置value属性,表示要传递的数据
4. 调用Consumer组件来接收数据
```html
const { Provider, Consumer } = React.createContext();
class ContextApp extends React.Component {
render() {
return (
<Provider value='pink'>
<div className="app">
<Node />
</div>
</Provider>
)
}
}
const Node = props => {
return (
<div className="node">
<SubNode />
</div>
)
}
const SubNode = props => {
return (
<div className="subNode">
<Child />
</div>
)
}
const Child = props => {
return <div className="child" ><Consumer>{data=><span>我是子节点---{data}</span>}</Consumer></div>
}
```
# props深入
## children表示组件标签的子节点.当组件标签有子节点时,props就会有该属性.
children 属性与普通props一样,值可以是任意值(文本,React元素,组件,甚至是函数)
```html
class ChildProps extends React.Component{
render(){
console.log(this.props)
return (
<div><h2>组件标签的子节点{this.props.children}</h2></div>
)
}
}
ReactDOM.render(<ChildProps>传递到props的child</ChildProps>,document.getElementById('root'))
```
```html
class ChildProps extends React.Component{
render(){
console.log(this.props)
return (
<div><h2>组件标签的子节点</h2>{this.props.children}</div>
)
}
}
const Test = ()=> <button>组件按钮</button>
ReactDOM.render(<ChildProps><Test/><p>传递到props的child</p></ChildProps>,document.getElementById('root'))
```
函数
```html
class ChildProps extends React.Component{
render(){
this.props.children()
console.log(this.props)
return (
<div><h2>组件标签的子节点</h2></div>
)
}
}
ReactDOM.render(<ChildProps>{()=>console.info("这是一个函数")}</ChildProps>,document.getElementById('root'))
```
## prop 校验
1. 安装prop-types (npm i prop-types 或者 yarn add prop-types)
2. 导入prop-types包
3. 使用组件名.propTypes={}来给组件的props添加校验规则
4. 校验规则通过PropTypes对象指定
```html
import React from "react"
import PropTypes from "prop-types"
class PropCheck extends React.Component{
render(){
const arr = this.props.colors
console.info(arr)
const lis = arr.map((item,index)=><li key={index}>{item}</li>)
return (
<ul>{lis}</ul>
)
}
}
PropCheck.protoTypes= {
colors:PropTypes.array
}
export default PropCheck
ReactDOM.render(<PropCheck colors = {['red','black']} />,document.getElementById('root'))
```
## prop 默认值
```html
class LPropCheck extends React.Component{
render(){
// const arr = this.props.colors
// console.info(arr)
// const lis = arr.map((item,index)=><li key={index}>{item}</li>)
// return (
// <ul>{lis}</ul>
// )
return <div><h2>test</h2>{this.props.colors}</div>
}
}
LPropCheck.propTypes= {
colors:PropTypes.array
}
LPropCheck.defaultProps= {
colors:["red"]
}
```

View File

@ -0,0 +1,269 @@
只有类组件才有生命周期
1. 创建
```html
class LifeCreate extends React.Component{
constructor(props){
super(props)
console.warn('1.生命周期钩子函数constructor')
}
componentDidMount(){
console.warn('3.生命周期钩子函数componentDidMount')
}
render(){
console.warn('2.生命周期钩子函数render')
return (<div><h3>hello</h3></div>)
}
}
```
2. 更新
New props,setState,forceUpdate
```html
class LifeUpdate extends React.Component{
constructor(props){
super(props)
console.warn('1.生命周期钩子函数constructor')
this.state = {
count:0
}
}
componentDidMount(){
console.log(document.getElementById('root'))
console.warn('3.生命周期钩子函数componentDidMount')
}
handlerClick= ()=>{
// this.setState({
// count:this.state.count+1
// })
this.forceUpdate()
}
render(){
console.warn('2.生命周期钩子函数render')
return (<div><Counter count = {this.state.count}/><button onClick={this.handlerClick}>hello</button></div>)
}
}
class Counter extends React.Component{
componentDidUpdate(prevProps){
console.warn('---------------生命周期钩子函数componentDidUpdate')
if(prevProps.count!=this.props.count){
this.setState({})
}
}
render(){
console.warn('---------------生命周期钩子函数render')
return (<p>点击次数{this.props.count}</p>)
}
}
```
3. 卸载
```html
class LifeEnd extends React.Component{
constructor(props){
super(props)
console.warn('1.生命周期钩子函数constructor')
this.state = {
count:0
}
}
componentDidMount(){
console.log(document.getElementById('root'))
console.warn('3.生命周期钩子函数componentDidMount')
}
handlerClick= ()=>{
this.setState({
count:this.state.count+1
})
// this.forceUpdate()
}
render(){
console.warn('2.生命周期钩子函数render')
return (<div>
{this.state.count>3?("goodby"):<Counter count = {this.state.count}/>}
<button onClick={this.handlerClick}>hello</button></div>)
}
}
class Counter extends React.Component{
componentDidUpdate(prevProps){
console.warn('---------------生命周期钩子函数componentDidUpdate')
if(prevProps.count!==this.props.count){
this.setState({})
}
}
componentDidMount(){
this.timerId = setInterval(()=>{
console.log("定时器在执行")
},500)
}
componentWillUnmount(){
clearInterval(this.timerId)
console.warn("结束")
}
render(){
console.warn('---------------生命周期钩子函数render')
return (<p>点击次数{this.props.count}</p>)
}
}
```
# 组件复用
复用组件的状态逻辑1.state2.操作state的方法.
## render props
将要复用的state和操作state的方法封装到一个组件中
添加一个值为函数的prop,通过函数参数来获取(需要组件内部来实现)
使用该函数的返回值,作为要渲染的UI内容
```html
import img from './img/20200205205324.jpg'
import PropTypes from "prop-types"
class RepeatUse extends React.Component {
render(){
return (
<div>
<h1>render props 复用模式</h1>
<Mouse >{mouse=>{return (<p>X:{mouse.x},Y{mouse.y}</p>)}}</Mouse>
<Mouse >{mouse=>{return <img src={img} alt="" style={{position:'absolute',top:mouse.y-350,left:mouse.x-350}} />}}</Mouse>
</div>
)
}
}
class Mouse extends React.Component {
state = {
x: 0, y: 0
}
handleMouseMove = e => {
this.setState({
x: e.clientX,
y: e.clientY
})
}
// 监听鼠标移动
componentDidMount() {
window.addEventListener('mousemove', this.handleMouseMove)
}
componentWillUnmount(){
window.removeEventListener('mousemove', this.handleMouseMove)
}
render(){
return this.props.children(this.state)
}
}
Mouse.propTypes={
children:PropTypes.func.isRequired
}
```
## 高阶组件HOC
是一个函数,接收要包装的组件,返回增强后的组件
1. 创建函数以with开头
2. 指定函数参数,参数大写开头
3. 函数内部创建类组件,提供复用状态逻辑代码
4. 在组件中渲染参数组件,同时通过prop传递给参数组件(high order component)
5. 调用高阶组件,传入要增强的组件,通过返回值拿到增强后的组件并将其渲染到页面.
```html
class RepeatUse extends React.Component {
render(){
return (
<div>
<h1>render props 复用模式</h1>
<MoousePosition ></MoousePosition>
</div>
)
}
}
function withMouse(WrappedComponent){
class Mouse extends React.Component {
state = {
x: 0, y: 0
}
handleMouseMove = e => {
this.setState({
x: e.clientX,
y: e.clientY
})
}
// 监听鼠标移动
componentDidMount() {
window.addEventListener('mousemove', this.handleMouseMove)
}
componentWillUnmount(){
window.removeEventListener('mousemove', this.handleMouseMove)
}
render(){
// return this.props.children(this.state)
return <WrappedComponent {...this.state}></WrappedComponent>
}
}
return Mouse
}
const Position = props=>(
<p>
鼠标当前位置(x:{props.x},y:{props.y})
</p>
)
const MoousePosition = withMouse(Position)
```
### 设置displayName便于区分
```html
class RepeatUse extends React.Component {
render(){
return (
<div>
<h1>render props 复用模式</h1>
<MoousePosition ></MoousePosition>
{/* <MoousePosition >{mouse=>{return (<p>X:{mouse.x},Y{mouse.y}</p>)}}</MoousePosition> */}
{/* <MoousePosition >{mouse=>{return <img src={img} alt="" style={{position:'absolute',top:mouse.y-350,left:mouse.x-350}} />}}</MoousePosition> */}
</div>
)
}
}
function getDisplayName(WrappedComponent){
return WrappedComponent.displayName || WrappedComponent.name || 'Component'
}
function withMouse(WrappedComponent){
class Mouse extends React.Component {
state = {
x: 0, y: 0
}
handleMouseMove = e => {
this.setState({
x: e.clientX,
y: e.clientY
})
}
// 监听鼠标移动,仅挂载执行一次
componentDidMount() {
window.addEventListener('mousemove', this.handleMouseMove)
}
componentWillUnmount(){
window.removeEventListener('mousemove', this.handleMouseMove)
}
render(){
// return this.props.children(this.state)
return <WrappedComponent {...this.state}></WrappedComponent>
}
}
// 设置展示名称
Mouse.displayName = `WithMouse${getDisplayName(WrappedComponent)}`
return Mouse
}
const Position = props=>(
<p>
鼠标当前位置(x:{props.x},y:{props.y})
</p>
)
const MoousePosition = withMouse(Position)
```
## 高阶组件传递 props
```html
render(){
// return this.props.children(this.state)
return <WrappedComponent {...this.state}{...this.props}></WrappedComponent>
}
```
## 组件极简模型
(state,props)=>UI

View File

@ -0,0 +1 @@
diff对比最小粒度:标签

View File

@ -0,0 +1,56 @@
setState异步更新(同一方法中多次调用只会render一次),设置值后立即获取,只能获取到原始值
获取最新的state
```html
this.setState((state,props)=> {
return {
count:state.count+1
}
})
```
setState第二个参数作为回调参数,在状态更新,后立即执行.
```html
class StateApp extends React.Component{
constructor(){
super()
this.state= {
count:0
}
}
onIncrement=()=>{
console.info('事件处理this'+this)
// this.setState({
// count:this.state.count+1
// })
// this.setState({
// count:this.state.count+1
// })
this.setState((state,props)=> {
return {
count:state.count+1
}
})
this.setState((state,props)=> {
return {
count:state.count+1
}
},()=>{
console.log('状态更新后回调函数',this.state.count)//4
console.log(document.getElementById('root').innerText)
})
console.info(this.state.count)///2
}
render(){
return (
<div>有状态组件,{this.state.count}
<button onClick={this.onIncrement}>+1</button>
</div>
)
}
}
```

View File

@ -0,0 +1,58 @@
jsx语法->createElement()->React元素
父组件更新,子组件跟着更新.
# 组件优化
### 减轻state,与渲染无关的不要放在state中.
### 避免不必要的重新渲染.
钩子函数 shouldComponentUpdate(nextProps,nextState)
根据返回值判断是否需要渲染该值.
触发时机:更新阶段的钩子函数,组件重新渲染前执行(shouldComponentUpdate->render)
或者使用纯组件
```html
class StateApp extends React.Component{
constructor(){
super()
this.state= {
count:0
}
}
shouldComponentUpdate(nextProps,nextState){
console.info('最新是内容'+nextState.count)
console.info('上次是内容'+this.state.count)
return nextState.count!==this.setState.count
}
onIncrement=()=>{
console.info('事件处理this'+this)
this.setState((state,props)=> {
return {
count:state.count+1
}
})
console.info(this.state.count)
}
render(){
return (
<div>有状态组件,{this.state.count}
<button onClick={this.onIncrement}>+1</button>
</div>
)
}
}
```
## 纯组件 PureComponent
比较props和state,只有这两者变化才会更新.
纯组件内部的对比是shallow compare 浅层对比
对于引用类型来说:只比较对象地址.
# 虚拟DOM和Diff算法
虚拟DOM=state+jsx
虚拟DOM:本质上就是一个js对象,用来描述你希望在屏幕上看到的内容(UI)
虚拟DOM让react脱离了浏览器的束缚
## diff算法
1. 初次渲染是,React会根据初始state(Model),创建一个虚拟DOM对象(树)
2. 根据虚拟DOM生成真正的DOM,渲染到页面中
3. 当数据变化后(setState),重新根据新的数据,创建新的虚拟DOM对象树
4. 与上一次得到的虚拟DOM对象,使用Diff算法对比(找不同),得到需要更新的内容
5. 最终,React只将变化的内容(patch)到DOM中,重新渲染到页面.

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 666 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 KiB

View File

@ -0,0 +1,21 @@
1. 使用vite创建自己的项目
```shell
npm create vite@latest react-route-v6-learn -- --template react
```
2. 安装路由
```
npm install react-router-dom localforage match-sorter sort-by
```
3. 启动
```
npm run dev
```
nested 嵌套
navigate 导航

Binary file not shown.

After

Width:  |  Height:  |  Size: 309 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 223 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 394 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 375 KiB

BIN
react/目录1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 315 KiB

BIN
react/目录2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 162 KiB

View File

@ -0,0 +1,44 @@
# 代理:
### 方式一
只能使用一种代理
package.json文件中添加
"proxy":"http://localhost:5000"
自身没有的资源会请求5000,自身请求http://localhost:3000/
### 方式二
src根目录下创建文件setupProxy.js
```html
const proxy = require('http-proxy-middleware')
module.exports=function(app){
app.use(
proxy('/api1',{
target:'http://localhost:5000',
changeOrigin:true,
pathRewrite:{'^/api1':''}
}),
proxy('/api2',{
target:'http://localhost:5001',
changeOrigin:true,
pathRewrite:{'^/api2':''}
})
)
}
```
```
const {createProxyMiddleware} = require('http-proxy-middleware')
module.exports=function(app){
app.use(
createProxyMiddleware('/api1',{
target:'https://github.com',
changeOrigin:false,
pathRewrite:{'^/api1':''}
}),
createProxyMiddleware('/api2',{
target:'http://localhost:5001',
changeOrigin:true,
pathRewrite:{'^/api2':''}
})
)
}
```

39
react/路由/9.路由.md Normal file
View File

@ -0,0 +1,39 @@
npm i react-router-dom@5
为有效使用单页面管理原来的多页面的功能,前端路由应运而生.
从一个视图跳转到另一个视图,是URL路径与组件的对应关系.
1. 安装路由 npm i react-router-dom
2. 导入 import { BrowserRouter as Router,Route,Link } from 'react-router-dom';
3. 使用Router包裹整个组件
4. 使用Link(解析为a标签)组件作为导航菜单路由入口
5. 使用Route配置路由的规则和要展示的组件路由出口
## 路由执行过程
1. 点击Link组件(a标签),修改了浏览器地址栏中url
2. React路由监听到地址栏url的变化
3. React路由内部遍历所有Route组件,使用路由规则(path)与pathname进行匹配
4. 当路由规则(path)能够匹配地址栏中的pathname时,就展示改route组件内容
## 编程式导航
通过js代码来实现页面跳转 this.pops.history.push('/home')
history是react路由提供的,用于获取浏览器历史记录的相关信息
push(path):跳转到某个页面,参数path表示要跳转的路径
go(n)前进或者后退到某个页面(后退用-)
<Redirect to="/home"></Redirect>
<Navigate to="/about"></Navigate>
## 默认路由
```
<Route path ="/" element={<LoginApp/>} ></Route>
```
默认情况下React是模糊匹配模式
精确匹配添加exact
```html
<Route exact path ="/home" element={<HomeApp/>} ></Route>
```
React路由一切都是组件,可以像思考组件一样思考路由.
HashRouter 路径中包含#,#后所有资源都不发送服务器.
路由组件(<Header/>):放到pages文件夹下,传入大量props信息
一般组件(<Route path="/about" component={About} />):放到components,默认不传props内容,无history
NavLink 默认点击高亮(activeClassName)

View File

@ -0,0 +1,34 @@
1. React核心库:react,react-dom,react-router-dom
2. 脚手架:create-react-app
3. 数据请求:axios
4. ui组件库:antd-mobile
5. 其他组件库:react-virtualized长列表,formik-yup表单和表单验证,react-spring动画功能
6. 百度地图api
创建并导入数据库 hkzf
在api目录中执行 npm start
8080
npx create-react-app hkzf-mobile
npm install antd-mobile --save
npm install react-router-dom
对象简写方式
```
{message:message}=>{message}
```
09
14
15
27

View File

@ -0,0 +1,7 @@
获取数据
1. 安装axios:npm i axios
2. 在Index组件中导入axios
3. 在state中添加轮播图数据:swipers
4. 新建一个方法getSwipers用来获取轮播图数据,并更新swipers状态
5. 在componentDidMount钩子函数中调用该方法
6. 使用获取到的数据渲染轮播图

View File

@ -0,0 +1,5 @@
安装
```
npm i node-sass
```
修改文件名为scss

View File

@ -0,0 +1,23 @@
1. state中添加租房小组数据group
2. 新建一个方法getGroups用来获取数据,并更新groups状态
3. 在componentDidMount钩子函数中调用该方法
4. 使用过去到的数据渲染租房小组数据
# H5中的地理位置API
```html
navigator.geolocation.getCurrentPosition(position=>{
console.log("当前位置信息",position)
})
```
懒渲染
可视区域渲染,大列表
react-virtualized
1. 将获取到的cityList和cityIndex添加为组件的状态数据
2. 修改List组件的rowCount为cityIndex的长度
3. 将rowRender函数添加到组件内部,以便在函数中获取到状态数据cityList和cityIndex
4. 修改List组件的RowRender为组件中的rowRender方法
5. 修改rowRender方法中渲染的每行格式结构和样式.
6. 修改list组件的rowHeight为函数,动态计算每一行的高度
7.

View File

@ -0,0 +1,19 @@
使用JavaScript编写CSS的统称,用来解决CSS样式冲突,覆盖的问题.
CSS-Model:BEM(Block 块,Element元素,Modifier三部分组成)
```
import styles from './index.module.css'
<div className={styles.test}>测试样式覆盖</div>
```

206
vue/1-vue官网学习.md Normal file
View File

@ -0,0 +1,206 @@
#### 绑定 v-bind
v-bind attribute :将这个元素节点的 `title` attribute 和 Vue 实例的 `message` property 保持一致
```javascript
<div id="app-2">
<span v-bind:title="message">
鼠标悬停几秒钟查看此处动态绑定的提示信息!
</span>
</div>
var app2 = new Vue({
el: '#app-2',
data: {
message: '页面加载于 ' + new Date().toLocaleString()
}
})
```
在控制台输入改变信息app2.message = '新消息'
#### 控制元素显示 v-if
```js
<div id="app-3">
<p v-if="seen">现在你看到我了</p>
</div>
var app3 = new Vue({
el: '#app-3',
data: {
seen: true
}
})
```
直接在控制台改变属性app3.seen = false
#### 循环展示 v-for
```js
<div id="app-4">
<ol>
<li v-for="todo in todos">
{{ todo.text }}
</li>
</ol>
</div>
var app4 = new Vue({
el: '#app-4',
data: {
todos: [
{ text: '学习 JavaScript' },
{ text: '学习 Vue' },
{ text: '整个牛项目' }
]
}
})
```
在控制台改变内容app4.todos.push({ text: '新项目' })
#### 添加一个事件监听器 v-on
```javascript
<div id="app-5">
<p>{{ message }}</p>
<button v-on:click="reverseMessage">反转消息</button>
</div>
var app5 = new Vue({
el: '#app-5',
data: {
message: 'Hello Vue.js!'
},
methods: {
reverseMessage: function () {
this.message = this.message.split('').reverse().join('')
}
}
})
```
#### 表单输入和应用状态之间的双向绑定 v-model
```js
<div id="app-6">
<p>{{ message }}</p>
<input v-model="message">
</div>
var app6 = new Vue({
el: '#app-6',
data: {
message: 'Hello Vue!'
}
})
```
#### 组件
- 任意类型的应用界面都可以抽象为一个组件树。
- 子单元通过 prop 接口与父单元进行了良好的解耦。
- 所有的 Vue 组件都是 Vue 实例。
```js
Vue.component('todo-item', {
// todo-item 组件现在接受一个
// "prop",类似于一个自定义 attribute。
// 这个 prop 名为 todo。
props: ['todo'],
template: '<li>{{ todo.text }}</li>'
})
<div id="app-7">
<ol>
<!--
现在我们为每个 todo-item 提供 todo 对象
todo 对象是变量,即其内容可以是动态的。
我们也需要为每个组件提供一个“key”稍后再
作详细解释。
-->
<todo-item
v-for="item in groceryList"
v-bind:todo="item"
v-bind:key="item.id"
></todo-item>
</ol>
</div>
var app7 = new Vue({
el: '#app-7',
data: {
groceryList: [
{ id: 0, text: '蔬菜' },
{ id: 1, text: '奶酪' },
{ id: 2, text: '随便其它什么人吃的东西' }
]
}
})
```
#### 响应式
- 当一个 Vue 实例被创建时,它将 `data` 对象中的所有的 property 加入到 Vue 的**响应式系统**中。当这些 property 的值发生改变时,视图将会产生“响应”,即匹配更新为新的值。
- 值得注意的是只有当实例被创建时就已经存在于 `data` 中的 property 才是**响应式**的。
- `Object.freeze()`,这会阻止修改现有的 property也意味着响应系统无法再*追踪*变化。
```js
var obj = {
foo: 'bar'
}
Object.freeze(obj)
new Vue({
el: '#app',
data: obj
})
<div id="app">
<p>{{ foo }}</p>
<!-- 这里的 `foo` 不会更新! -->
<button v-on:click="foo = 'baz'">Change it</button>
</div>
```
- 除了数据 propertyVue 实例还暴露了一些有用的实例 property 与方法。它们都有前缀 `$`,以便与用户定义的 property 区分开来。
```js
var data = { a: 1 }
var vm = new Vue({
el: '#example',
data: data
})
vm.$data === data // => true
vm.$el === document.getElementById('example') // => true
// $watch 是一个实例方法
vm.$watch('a', function (newValue, oldValue) {
// 这个回调将在 `vm.a` 改变后调用
})
```
#### 生命周期钩子
- 生命周期钩子的 `this` 上下文指向调用它的 Vue 实例。
- 不要在选项 property 或回调上使用箭头函数
```js
new Vue({
data: {
a: 1
},
created: function () {
// `this` 指向 vm 实例
console.log('a is: ' + this.a)
}
})
// => "a is: 1"
```
![](img\lifecycle.png)

110
vue/2-模板语法.md Normal file
View File

@ -0,0 +1,110 @@
如果你熟悉虚拟 DOM 并且偏爱 JavaScript 的原始力量,你也可以不用模板,[直接写渲染 (render) 函数](https://cn.vuejs.org/v2/guide/render-function.html),使用可选的 JSX 语法。
#### 数据绑定
- 数据绑定最常见的形式就是使用“Mustache”语法 (双大括号) 的文本插值。
- 通过使用 [v-once 指令](https://cn.vuejs.org/v2/api/#v-once)你也能执行一次性地插值当数据改变时插值处的内容不会更新类似Object.freeze()。但请留心这会影响到该节点上的其它数据绑定。
```js
<span>Message: {{ msg }}</span>
<span v-once>这个将不会改变: {{ msg }}</span>
```
#### v-html
- 双大括号会将数据解释为普通文本,而非 HTML 代码。为了输出真正的 HTML你需要使用 v-html 指令
```js
<p>Using mustaches: {{ rawHtml }}</p>
<p>Using v-html directive: <span v-html="rawHtml"></span></p>
```
#### v-bind
- Mustache 语法不能作用在 HTML attribute 上,遇到这种情况应该使用 v-bind
```js
<div v-bind:id="dynamicId"></div>
// 如果 isButtonDisabled 的值是 null、undefined 或 false则 disabled attribute 甚至不会被包含在渲染出来的 <button> 元素中。
<button v-bind:disabled="isButtonDisabled">Button</button>
```
#### javascript语法
```
{{ number + 1 }}
{{ ok ? 'YES' : 'NO' }}
{{ message.split('').reverse().join('') }}
<div v-bind:id="'list-' + id"></div>
```
- 数据绑定支持javascript语法
- 每个绑定都只能包含**单个表达式**
- 模板表达式都被放在沙盒中,只能访问[全局变量的一个白名单](https://github.com/vuejs/vue/blob/v2.6.10/src/core/instance/proxy.js#L9),如 `Math``Date` 。你不应该在模板表达式中试图访问用户定义的全局变量。
```js
<!-- 这是语句,不是表达式 -->
{{ var a = 1 }}
<!-- 流控制也不会生效,请使用三元表达式 -->
{{ if (ok) { return message } }}
```
#### 指令v-
- 指令的职责是,当表达式的值改变时,将其产生的连带影响,响应式地作用于 DOM。
```js
<p v-if="seen">现在你看到我了</p>
```
#### 参数
```js
<a v-bind:href="url">...</a>
// 在这里 href 是参数,告知 v-bind 指令将该元素的 href attribute 与表达式 url 的值绑定。
<a v-on:click="doSomething">...</a>
// 在这里参数是监听的事件名。
<a v-bind:[attributeName]="url"> ... </a>
// 这里的 attributeName 会被作为一个 JavaScript 表达式进行动态求值,求得的值将会作为最终的参数来使用。例如,如果你的 Vue 实例有一个 data property // attributeName其值为 "href",那么这个绑定将等价于 v-bind:href。
<a v-on:[eventName]="doSomething"> ... </a>
// 当 eventName 的值为 "focus" 时v-on:[eventName] 将等价于 v-on:focus。
```
- 在 DOM 中使用模板时 (直接在一个 HTML 文件里撰写模板),还需要避免使用大写字符来命名键名,因为浏览器会把 attribute 名全部强制转为小写
```js
<!--
在 DOM 中使用模板时这段代码会被转换为 `v-bind:[someattr]`
除非在实例中有一个名为“someattr”的 property否则代码不会工作。
-->
<a v-bind:[someAttr]="value"> ... </a>
```
#### 修饰符
- 修饰符 (modifier) 是以半角句号 `.` 指明的特殊后缀,用于指出一个指令应该以特殊方式绑定。例如,`.prevent` 修饰符告诉 `v-on` 指令对于触发的事件调用 `event.preventDefault()`
```js
<form v-on:submit.prevent="onSubmit">...</form>
```
#### 缩写:@
```js
<!-- v-bind 完整语法 -->
<a v-bind:href="url">...</a>
<!-- 缩写 -->
<a :href="url">...</a>
<!-- 动态参数的缩写 (2.6.0+) -->
<a :[key]="url"> ... </a>
<!-- v-on 完整语法 -->
<a v-on:click="doSomething">...</a>
<!-- 缩写 -->
<a @click="doSomething">...</a>
<!-- 动态参数的缩写 (2.6.0+) -->
<a @[event]="doSomething"> ... </a>
```

View File

@ -0,0 +1,154 @@
## computed
#### 计算属性
- 计算属性是基于它们的响应式依赖进行缓存的
```js
<div id="example">
<p>Original message: "{{ message }}"</p>
<p>Computed reversed message: "{{ reversedMessage }}"</p>
</div>
var vm = new Vue({
el: '#example',
data: {
message: 'Hello'
},
computed: {
// 计算属性的 getter
reversedMessage: function () {
// `this` 指向 vm 实例
return this.message.split('').reverse().join('')
}
}
})
<p>Reversed message: "{{ reversedMessage() }}"</p>
// 在组件中
methods: {
reversedMessage: function () {
return this.message.split('').reverse().join('')
}
}
```
#### 监听属性
- 类似watch
```js
<div id="demo">{{ fullName }}</div>
// watch
var vm = new Vue({
el: '#demo',
data: {
firstName: 'Foo',
lastName: 'Bar',
fullName: 'Foo Bar'
},
watch: {
firstName: function (val) {
this.fullName = val + ' ' + this.lastName
},
lastName: function (val) {
this.fullName = this.firstName + ' ' + val
}
}
})
//
var vm = new Vue({
el: '#demo',
data: {
firstName: 'Foo',
lastName: 'Bar'
},
computed: {
fullName: function () {
return this.firstName + ' ' + this.lastName
}
}
})
```
#### set
```js
computed: {
fullName: {
// getter
get: function () {
return this.firstName + ' ' + this.lastName
},
// setter
set: function (newValue) {
var names = newValue.split(' ')
this.firstName = names[0]
this.lastName = names[names.length - 1]
}
}
}
// 现在再运行 vm.fullName = 'John Doe' 时setter 会被调用vm.firstName 和 vm.lastName 也会相应地被更新。
```
## 监听器
- 当需要在数据变化时执行异步或开销较大的操作时,这个方式是最有用的。
- 在这个示例中,使用 `watch` 选项允许我们执行异步操作 (访问一个 API),限制我们执行该操作的频率,并在我们得到最终结果前,设置中间状态。这些都是计算属性无法做到的。
```js
<div id="watch-example">
<p>
Ask a yes/no question:
<input v-model="question">
</p>
<p>{{ answer }}</p>
</div>
<!-- 因为 AJAX 库和通用工具的生态已经相当丰富Vue 核心代码没有重复 -->
<!-- 提供这些功能以保持精简。这也可以让你自由选择自己更熟悉的工具。 -->
<script src="https://cdn.jsdelivr.net/npm/axios@0.12.0/dist/axios.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/lodash@4.13.1/lodash.min.js"></script>
<script>
var watchExampleVM = new Vue({
el: '#watch-example',
data: {
question: '',
answer: 'I cannot give you an answer until you ask a question!'
},
watch: {
// 如果 `question` 发生改变,这个函数就会运行
question: function (newQuestion, oldQuestion) {
this.answer = 'Waiting for you to stop typing...'
this.debouncedGetAnswer()
}
},
created: function () {
// `_.debounce` 是一个通过 Lodash 限制操作频率的函数。
// 在这个例子中,我们希望限制访问 yesno.wtf/api 的频率
// AJAX 请求直到用户输入完毕才会发出。想要了解更多关于
// `_.debounce` 函数 (及其近亲 `_.throttle`) 的知识,
// 请参考https://lodash.com/docs#debounce
this.debouncedGetAnswer = _.debounce(this.getAnswer, 500)
},
methods: {
getAnswer: function () {
if (this.question.indexOf('?') === -1) {
this.answer = 'Questions usually contain a question mark. ;-)'
return
}
this.answer = 'Thinking...'
var vm = this
axios.get('https://yesno.wtf/api')
.then(function (response) {
vm.answer = _.capitalize(response.data.answer)
})
.catch(function (error) {
vm.answer = 'Error! Could not reach the API. ' + error
})
}
}
})
</script>
```

View File

@ -0,0 +1,86 @@
### v-bind:class
#### 属性
- 我们可以传给 `v-bind:class` 一个对象,以动态地切换 class
```js
<div
class="static"
v-bind:class="{ active: isActive, 'text-danger': hasError }"
></div>
data: {
isActive: true,
hasError: false
}
```
#### 对象
```js
<div class="static active"></div>
//绑定的数据对象不必内联定义在模板里:
<div v-bind:class="classObject"></div>
data: {
classObject: {
active: true,
'text-danger': false
}
}
data: {
isActive: true,
error: null
},
computed: {
classObject: function () {
return {
active: this.isActive && !this.error,
'text-danger': this.error && this.error.type === 'fatal'
}
}
}
```
#### 数组
```js
<div v-bind:class="[activeClass, errorClass]"></div>
data: {
activeClass: 'active',
errorClass: 'text-danger'
}
<div class="active text-danger"></div>
<div v-bind:class="[isActive ? activeClass : '', errorClass]"></div>
<div v-bind:class="[{ active: isActive }, errorClass]"></div>
```
#### 用在组件上
组件中信息不会被覆盖而是追加
### 绑定内联样式 v-bind:style
- CSS property 名可以用驼峰式 (camelCase) 或短横线分隔 (kebab-case记得用引号括起来) 来命名
```html
<div v-bind:style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>
<div v-bind:style="styleObject"></div>
<div v-bind:style="[baseStyles, overridingStyles]"></div>
```
```js
data: {
activeColor: 'red',
fontSize: 30
}
data: {
styleObject: {
color: 'red',
fontSize: '13px'
}
}
```

71
vue/5-条件渲染.md Normal file
View File

@ -0,0 +1,71 @@
#### v-if
- 当 `v-if``v-for` 一起使用时,`v-for` 具有比 `v-if` 更高的优先级。不推荐两个一起使用
```html
<h1 v-if="awesome">Vue is awesome!</h1>
<h1 v-else>Oh no 😢</h1>
<template v-if="ok">
<h1>Title</h1>
<p>Paragraph 1</p>
<p>Paragraph 2</p>
</template>
<div v-if="Math.random() > 0.5">
Now you see me
</div>
<div v-else>
Now you don't
</div>
<div v-if="type === 'A'">
A
</div>
<div v-else-if="type === 'B'">
B
</div>
<div v-else-if="type === 'C'">
C
</div>
<div v-else>
Not A/B/C
</div>
```
#### key管理可复用元素
- 那么在上面的代码中切换 `loginType` 将不会清除用户已经输入的内容。因为两个模板使用了相同的元素,`<input>` 不会被替换掉——仅仅是替换了它的 `placeholder`
```html
<template v-if="loginType === 'username'">
<label>Username</label>
<input placeholder="Enter your username">
</template>
<template v-else>
<label>Email</label>
<input placeholder="Enter your email address">
</template>
```
- 这样也不总是符合实际需求,所以 Vue 为你提供了一种方式来表达“这两个元素是完全独立的,不要复用它们”。只需添加一个具有唯一值的 `key` attribute 即可
```html
<template v-if="loginType === 'username'">
<label>Username</label>
<input placeholder="Enter your username" key="username-input">
</template>
<template v-else>
<label>Email</label>
<input placeholder="Enter your email address" key="email-input">
</template>
```
#### v-show
```html
<h1 v-show="ok">Hello!</h1>
```
- v-show` 不支持 `<template>` 元素,也不支持 `v-else

221
vue/6-列表渲染.md Normal file
View File

@ -0,0 +1,221 @@
#### v-for
```html
<ul id="example-1">
<li v-for="item in items" :key="item.message">
{{ item.message }}
</li>
</ul>
```
```js
var example1 = new Vue({
el: '#example-1',
data: {
items: [
{ message: 'Foo' },
{ message: 'Bar' }
]
}
})
```
```html
<ul id="example-2">
<li v-for="(item, index) in items">
{{ parentMessage }} - {{ index }} - {{ item.message }}
</li>
</ul>
```
```js
var example2 = new Vue({
el: '#example-2',
data: {
parentMessage: 'Parent',
items: [
{ message: 'Foo' },
{ message: 'Bar' }
]
}
})
```
### v-for遍历一个对象的元素
```html
<ul id="v-for-object" class="demo">
<li v-for="value in object">
{{ value }}
</li>
</ul>
<div v-for="(value, name) in object">
{{ name }}: {{ value }}
</div>
<div v-for="(value, name, index) in object">
{{ index }}. {{ name }}: {{ value }}
</div>
<div v-for="item in items" v-bind:key="item.id">
<!-- 内容 -->
</div>
```
```js
new Vue({
el: '#v-for-object',
data: {
object: {
title: 'How to do lists in Vue',
author: 'Jane Doe',
publishedAt: '2016-04-10'
}
}
})
```
#### 显示过滤/排序后的结果
- 有时,我们想要显示一个数组经过过滤或排序后的版本,而不实际变更或重置原始数据。在这种情况下,可以创建一个计算属性,来返回过滤或排序后的数组。
```html
<li v-for="n in evenNumbers">{{ n }}</li>
```
```js
data: {
numbers: [ 1, 2, 3, 4, 5 ]
},
computed: {
evenNumbers: function () {
return this.numbers.filter(function (number) {
return number % 2 === 0
})
}
}
```
- 在计算属性不适用的情况下 (例如,在嵌套 `v-for` 循环中) 可以使用一个方法
```html
<ul v-for="set in sets">
<li v-for="n in even(set)">{{ n }}</li>
</ul>
```
```js
data: {
sets: [[ 1, 2, 3, 4, 5 ], [6, 7, 8, 9, 10]]
},
methods: {
even: function (numbers) {
return numbers.filter(function (number) {
return number % 2 === 0
})
}
}
```
```html
<div>
<span v-for="n in 10">{{ n }} </span>
</div>
<ul>
<template v-for="item in items">
<li>{{ item.msg }}</li>
<li class="divider" role="presentation"></li>
</template>
</ul>
```
#### v-for,v-if 一同使用
- 当它们处于同一节点,`v-for` 的优先级比 `v-if` 更高,这意味着 `v-if` 将分别重复运行于每个 `v-for` 循环中。当你只想为*部分*项渲染节点时,这种优先级的机制会十分有用。
-
```html
<--代码将只渲染未完成的 todo-->
<li v-for="todo in todos" v-if="!todo.isComplete">
{{ todo }}
</li>
<ul v-if="todos.length">
<li v-for="todo in todos">
{{ todo }}
</li>
</ul>
<p v-else>No todos left!</p>
```
#### v-for在组件中
```html
<div id="todo-list-example">
<form v-on:submit.prevent="addNewTodo">
<label for="new-todo">Add a todo</label>
<input
v-model="newTodoText"
id="new-todo"
placeholder="E.g. Feed the cat"
>
<button>Add</button>
</form>
<ul>
<li
is="todo-item"
v-for="(todo, index) in todos"
v-bind:key="todo.id"
v-bind:title="todo.title"
v-on:remove="todos.splice(index, 1)"
></li>
</ul>
</div>
```
```js
Vue.component('todo-item', {
template: '\
<li>\
{{ title }}\
<button v-on:click="$emit(\'remove\')">Remove</button>\
</li>\
',
props: ['title']
})
new Vue({
el: '#todo-list-example',
data: {
newTodoText: '',
todos: [
{
id: 1,
title: 'Do the dishes',
},
{
id: 2,
title: 'Take out the trash',
},
{
id: 3,
title: 'Mow the lawn'
}
],
nextTodoId: 4
},
methods: {
addNewTodo: function () {
this.todos.push({
id: this.nextTodoId++,
title: this.newTodoText
})
this.newTodoText = ''
}
}
})
```

125
vue/7-事件处理.md Normal file
View File

@ -0,0 +1,125 @@
```html
<div id="example-1">
<button v-on:click="counter += 1">Add 1</button>
<p>The button above has been clicked {{ counter }} times.</p>
</div>
<div id="example-2">
<!-- `greet` 是在下面定义的方法名 -->
<button v-on:click="greet">Greet</button>
</div>
```
```js
var example1 = new Vue({
el: '#example-1',
data: {
counter: 0
}
})
var example2 = new Vue({
el: '#example-2',
data: {
name: 'Vue.js'
},
// 在 `methods` 对象中定义方法
methods: {
greet: function (event) {
// `this` 在方法里指向当前 Vue 实例
alert('Hello ' + this.name + '!')
// `event` 是原生 DOM 事件
if (event) {
alert(event.target.tagName)
}
}
}
})
// 也可以用 JavaScript 直接调用方法
example2.greet() // => 'Hello Vue.js!'
```
#### 内联处理器中的方法调用
- 有时也需要在内联语句处理器中访问原始的 DOM 事件。可以用特殊变量 `$event` 把它传入方法
```html
<div id="example-3">
<button v-on:click="say('hi')">Say hi</button>
<button v-on:click="say('what')">Say what</button>
<button v-on:click="warn('Form cannot be submitted yet.', $event)">
Submit
</button>
</div>
```
```js
new Vue({
el: '#example-3',
methods: {
say: function (message) {
alert(message)
},
warn: function (message, event) {
// 现在我们可以访问原生事件对象
if (event) {
event.preventDefault()
}
alert(message)
}
}
})
```
#### [事件修饰符](https://cn.vuejs.org/v2/guide/events.html#事件修饰符)
- 键盘按键处理
- 鼠标按键处理
```html
<!-- 阻止单击事件继续传播 -->
<a v-on:click.stop="doThis"></a>
<!-- 提交事件不再重载页面 -->
<form v-on:submit.prevent="onSubmit"></form>
<!-- 修饰符可以串联 -->
<a v-on:click.stop.prevent="doThat"></a>
<!-- 只有修饰符 -->
<form v-on:submit.prevent></form>
<!-- 添加事件监听器时使用事件捕获模式 -->
<!-- 即内部元素触发的事件先在此处理,然后才交由内部元素进行处理 -->
<div v-on:click.capture="doThis">...</div>
<!-- 只当在 event.target 是当前元素自身时触发处理函数 -->
<!-- 即事件不是从内部元素触发的 -->
<div v-on:click.self="doThat">...</div>
<!-- 点击事件将只会触发一次 -->
<a v-on:click.once="doThis"></a>
<!-- 滚动事件的默认行为 (即滚动行为) 将会立即触发 -->
<!-- 而不会等待 `onScroll` 完成 -->
<!-- 这其中包含 `event.preventDefault()` 的情况 -->
<div v-on:scroll.passive="onScroll">...</div>
<!-- 只有在 `key``Enter` 时调用 `vm.submit()` -->
<input v-on:keyup.enter="submit">
<input v-on:keyup.page-down="onPageDown">
<input v-on:keyup.13="submit">
// 可以使用 `v-on:keyup.f1`
Vue.config.keyCodes.f1 = 112
<!-- Alt + C -->
<input v-on:keyup.alt.67="clear">
<!-- Ctrl + Click -->
<div v-on:click.ctrl="doSomething">Do something</div>
```

View File

@ -0,0 +1,27 @@
## v-model
```html
<div id="example-6">
<select v-model="selected">
<option v-for="option in options" v-bind:value="option.value">
{{ option.text }}
</option>
</select>
<span>Selected: {{ selected }}</span>
</div>
```
```js
new Vue({
el: 'example-6',
data: {
selected: 'A',
options: [
{ text: 'One', value: 'A' },
{ text: 'Two', value: 'B' },
{ text: 'Three', value: 'C' }
]
}
})
```

56
vue/9-组件基础.md Normal file
View File

@ -0,0 +1,56 @@
- 因为组件是可复用的 Vue 实例,所以它们与 `new Vue` 接收相同的选项,例如 `data`、`computed`、`watch`、`methods` 以及生命周期钩子等。仅有的例外是像 `el` 这样根实例特有的选项。
- 一个组件的 `data` 选项必须是一个函数,因此每个实例可以维护一份被返回对象的独立的拷贝
- 通过prop向组件传递数据`v-on` 监听子组件实例的任意事件
- [从一个 API 获取博文列表](https://codesandbox.io/s/github/vuejs/vuejs.org/tree/master/src/v2/examples/vue-20-component-blog-post-example)
```js
// 定义一个名为 button-counter 的新组件
Vue.component('button-counter', {
data: function () {
return {
count: 0
}
},
template: '<button v-on:click="count++">You clicked me {{ count }} times.</button>'
})
Vue.component('blog-post', {
props: ['title'],
template: '<h3>{{ title }}</h3>'
})
```
```html
<div id="components-demo">
<button-counter></button-counter>
</div>
<blog-post title="My journey with Vue"></blog-post>
<blog-post title="Blogging with Vue"></blog-post>
<blog-post title="Why Vue is so fun"></blog-post>
<blog-post
v-for="post in posts"
v-bind:key="post.id"
v-bind:title="post.title"
></blog-post>
```
```html
new Vue({ el: '#components-demo' })
new Vue({
el: '#blog-post-demo',
data: {
posts: [
{ id: 1, title: 'My journey with Vue' },
{ id: 2, title: 'Blogging with Vue' },
{ id: 3, title: 'Why Vue is so fun' }
]
}
})
```
## [动态组件](https://cn.vuejs.org/v2/guide/components.html#动态组件)
https://codesandbox.io/s/github/vuejs/vuejs.org/tree/master/src/v2/examples/vue-20-dynamic-components

109
vue/erp-web.md Normal file
View File

@ -0,0 +1,109 @@
### 目录查看
1. node_modules安装依赖打包
2. public图片文件index.html
3. src:
1. api:调用接口使用的js文件
2. assets:图片文件
3. componentsvue组件文件页面处理
4. configrequest.js请求配置token设置。
5. filtersindex.js时间格式化变量转换。
6. router
1. index.js,从接口获取路由配置之
2. index备份.js配置路由备份
3. permission.js权限校验是否有token遍历路由权限校验
4. router.config.js免密登录白名单
7. store:用户信息和权限处理
1. nodules/user.js
2. getters.js
3. index.js
8. styles样式处理
9. utils工具类
10. views
1. dashboard页面排版vue
2. forgetPassowrdfogetPassword.vue
3. login:login.vue
4. register:register.vue
5. webView:webView.vue
11. App.vue
12. main.js:websocket
13. registerServiceWorker.js:生产环境配置
14. .env不同环境变量配置
15. label.config.js
16. package.json包管理,启动打包
17. vue.config.js:
vue-cli-service serve
### 总结
当配置baseURL时代理不生效。
```js
const request = axios.create({
timeout: 30000, // 请求超时时间
baseURL: process.env.VUE_APP_URL,
withCredentials: true
});
proxy: {
'/': { // 代理api
target: 'http://localhost:8085', // 服务器api地址
changeOrigin: true//, // 是否跨域
// pathRewrite: {
// '^/': 'http://qiankun.bj-fanuc.com.cn/finance-admin/charts/selectChartDataByPageModel' // 路径重写
// }
}
}
```
## 启动跟踪
```js
npm run dev
```
文件package.json
```json
"dev": "webpack-dev-server --inline --progress --module-bind production --config build/webpack.dev.conf.js "
```
文件build/webpack.dev.conf.js
```js
// these devServer options should be customized in /config/index.js
devServer: {
clientLogLevel: 'warning',
historyApiFallback: {
rewrites: [
{ from: /.*/, to: path.posix.join(config.dev.assetsPublicPath, 'index.html') },
],
},
hot: true,
contentBase: false, // since we use CopyWebpackPlugin.
compress: true,
host: HOST || config.dev.host,
port: PORT || config.dev.port,
open: config.dev.autoOpenBrowser,
overlay: config.dev.errorOverlay
? { warnings: false, errors: true }
: false,
publicPath: config.dev.assetsPublicPath,
proxy: config.dev.proxyTable,
quiet: true, // necessary for FriendlyErrorsPlugin
watchOptions: {
poll: config.dev.poll,
},
proxy: {
'/*': { // 代理api
target: 'http://localhost8080/', // 服务器api地址
changeOrigin: true, // 是否跨域
pathRewrite: {
'^/*': 'http://localhost:8081/' // 路径重写
}
}
}
},
```

BIN
vue/img/lifecycle.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

98
vue/webpack打包.md Normal file
View File

@ -0,0 +1,98 @@
## 基本参数
[基本参数说明](https://webpack.docschina.org/concepts)
1. 入口(entry)
2. 输出(output)
3. loader
4. 插件(plugin)
5. 模式(mode)
6. 浏览器兼容性(browser compatibility)
7. 环境(environment)
## 默认配置
1. 入口起点为 `src/index.js`
2. 然后会在 `dist/main.js` 输出结果
3. 并且在生产环境开启压缩和优化
4. 通常你的项目还需要继续扩展此能力,为此你可以在项目根目录下创建一个 `webpack.config.js` 文件,然后 webpack 会自动使用它。
[webpack.config.js配置](https://webpack.docschina.org/configuration)
**“webpack 配置的可扩展”** 是指,这些配置可以重复使用,并且可以与其他配置组合使用。这是一种流行的技术,用于将关注点从环境(environment)、构建目标(build target)、运行时(runtime)中分离。然后使用专门的工具(如 [webpack-merge](https://github.com/survivejs/webpack-merge))将它们合并起来。
- 使用 JavaScript 控制流表达式,例如 `?:` 操作符
- 精心编写的 **模块** 提供了可靠的抽象和封装界限,使得应用程序中每个模块都具备了条理清晰的设计和明确的目的。
- 应保证 loader 的先后顺序:[`'style-loader'`](https://webpack.docschina.org/loaders/style-loader) 在前,而 [`'css-loader'`](https://webpack.docschina.org/loaders/css-loader) 在后
## 基础文件
1. index.html是主入口
2. app.js是我们写的模块,并依据CommonJS规范导出这个模块
3. main.js其实是一个组件,它的目的是将我们写的一些代码模块返回并插入到页面中
不兼容问题升级
```js
npm i -D html-webpack-plugin@webpack-contrib/html-webpack-plugin
```
临时删除一些错误包
```
"eslint-loader": "^2.0.0",
"element-ui": "^2.15.7",
"less-loader": "6.0.0",
"jest": "^27.4.5",
"jest-serializer-vue": "^0.3.0",
"babel-jest": "^27.4.5",
"vue-jest": "^1.0.2",
"friendly-errors-webpack-plugin": "2.0.0-beta.2",
"uglifyjs-webpack-plugin": "2.2.0",
"babel-plugin-transform-runtime": "^7.14.0",
"@babel/core": "^7.12.3",
"@babel/plugin-transform-runtime": "7.12.13",
"@babel/preset-env": "^7.16.7",
"babel-eslint": "^10.0.1",
"babel-helper-vue-jsx-merge-props": "^2.0.3",
"babel-plugin-dynamic-import-node": "^1.2.0",
"babel-plugin-syntax-jsx": "^6.18.0",
"babel-plugin-transform-es2015-modules-commonjs": "^6.26.0",
"babel-plugin-transform-vue-jsx": "4.0.1",
"babel-preset-env": "^1.3.2",
"babel-preset-stage-2": "^6.22.0",
"babel-register": "^6.22.0",
"@babel/preset-env":"7.16.7"
"babel-plugin-react-intl": "^8.2.15",
npm instal babel-preset-env
npm install babel-preset-stage-2
"babel-plugin-transform-runtime": "^6.23.0",
"babel-plugin-transform-vue-jsx": "^4.0.1",
"babel-preset-env": "^1.7.0",
"babel-preset-stage-2": "^6.24.1",
-----------------------------------
"@babel/core": "^7.16.7",
"@babel/preset-env": "^7.16.7",
"babel-loader": "^8.2.3",
"babel-preset-stage-2": "7.0.0-beta.3",
"babel-runtime": "6.26.0",
"babel-helper-vue-jsx-merge-props": "^2.0.3",
"babel-plugin-syntax-jsx": "^6.18.0",
"babel-plugin-transform-runtime": "^6.23.0",
"babel-plugin-transform-vue-jsx": "^3.7.0",
"babel-preset-env": "^1.7.0",
"@babel/preset-stage-2": "^7.8.3",
TypeError: src/main.js: this.setDynamic is not a function
```

27
vue/websocket.html Normal file
View File

@ -0,0 +1,27 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>WebSocket</title>
</head>
<body>
<script>
var ws = new WebSocket("ws://192.168.60.109:8081/socket/123");
ws.onopen = function() {
document.write('<span>ws: ' + ws.url + ' opened</span><br/>');
ws.send('Hello, websocket!');
};
ws.onmessage = function(evt) {
document.write('<span>ws: '+ evt.data + '</span><br/>');
};
ws.onclose = function(evt) {
document.write('<span font="yellow">ws: closed</span><br/>');
console.log(evt);
};
ws.onerror = function(evt) {
document.write('<span font="darkred">ws: closed</span><br/>');
console.log(evt);
};
</script>
</body>
</html>

View File

@ -0,0 +1,36 @@
组件类似与java中的类(对象)。
#### 全局组件
```js
Vue.component('component-a', { /* ... */ })
Vue.component('component-b', { /* ... */ })
Vue.component('component-c', { /* ... */ })
new Vue({ el: '#app' })
```
```html
<div id="app">
<component-a></component-a>
<component-b></component-b>
<component-c></component-c>
</div>
```
#### 局部组件
```js
var ComponentA = { /* ... */ }
var ComponentB = { /* ... */ }
var ComponentC = { /* ... */ }
new Vue({
el: '#app',
components: {
'component-a': ComponentA,
'component-b': ComponentB
}
})
```