init
|
@ -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)
|
|
@ -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'))
|
||||
```
|
|
@ -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;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -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'))
|
||||
```
|
|
@ -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
|
||||
```
|
|
@ -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"]
|
||||
}
|
||||
```
|
|
@ -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('---------------2生命周期钩子函数componentDidUpdate')
|
||||
if(prevProps.count!=this.props.count){
|
||||
this.setState({})
|
||||
}
|
||||
}
|
||||
render(){
|
||||
console.warn('---------------1生命周期钩子函数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('---------------2生命周期钩子函数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('---------------1生命周期钩子函数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
|
|
@ -0,0 +1 @@
|
|||
diff对比最小粒度:标签
|
|
@ -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>
|
||||
)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -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中,重新渲染到页面.
|
||||
|
||||
|
||||
|
After Width: | Height: | Size: 100 KiB |
After Width: | Height: | Size: 666 KiB |
After Width: | Height: | Size: 44 KiB |
After Width: | Height: | Size: 117 KiB |
|
@ -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 导航
|
After Width: | Height: | Size: 309 KiB |
After Width: | Height: | Size: 223 KiB |
After Width: | Height: | Size: 394 KiB |
After Width: | Height: | Size: 375 KiB |
After Width: | Height: | Size: 315 KiB |
After Width: | Height: | Size: 162 KiB |
|
@ -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':''}
|
||||
})
|
||||
)
|
||||
}
|
||||
```
|
|
@ -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)
|
|
@ -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
|
|
@ -0,0 +1,7 @@
|
|||
获取数据
|
||||
1. 安装axios:npm i axios
|
||||
2. 在Index组件中导入axios
|
||||
3. 在state中添加轮播图数据:swipers
|
||||
4. 新建一个方法getSwipers用来获取轮播图数据,并更新swipers状态
|
||||
5. 在componentDidMount钩子函数中调用该方法
|
||||
6. 使用获取到的数据渲染轮播图
|
|
@ -0,0 +1,5 @@
|
|||
安装
|
||||
```
|
||||
npm i node-sass
|
||||
```
|
||||
修改文件名为scss
|
|
@ -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.
|
|
@ -0,0 +1,19 @@
|
|||
使用JavaScript编写CSS的统称,用来解决CSS样式冲突,覆盖的问题.
|
||||
CSS-Model:BEM(Block 块,Element元素,Modifier三部分组成)
|
||||
```
|
||||
import styles from './index.module.css'
|
||||
<div className={styles.test}>测试样式覆盖</div>
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -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>
|
||||
```
|
||||
|
||||
- 除了数据 property,Vue 实例还暴露了一些有用的实例 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"
|
||||
```
|
||||
|
||||

|
||||
|
|
@ -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>
|
||||
```
|
||||
|
||||
|
||||
|
|
@ -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>
|
||||
```
|
||||
|
|
@ -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'
|
||||
}
|
||||
}
|
||||
```
|
||||
|
|
@ -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
|
||||
|
|
@ -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 = ''
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
|
@ -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>
|
||||
|
||||
```
|
||||
|
|
@ -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' }
|
||||
]
|
||||
}
|
||||
})
|
||||
```
|
||||
|
|
@ -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
|
|
@ -0,0 +1,109 @@
|
|||
### 目录查看
|
||||
|
||||
1. node_modules:安装依赖打包
|
||||
2. public:图片文件,index.html
|
||||
3. src:
|
||||
1. api:调用接口使用的js文件
|
||||
2. assets:图片文件
|
||||
3. components:vue组件文件页面处理
|
||||
4. config:request.js,请求配置token设置。
|
||||
5. filters:index.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. forgetPassowrd:fogetPassword.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/' // 路径重写
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
```
|
||||
|
After Width: | Height: | Size: 49 KiB |
|
@ -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
|
||||
```
|
||||
|
|
@ -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>
|
|
@ -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
|
||||
}
|
||||
})
|
||||
```
|
||||
|