@Bios
2018-12-10T08:43:40.000000Z
字数 11012
阅读 1023
react
js
javascript函数方式定义
function Welcome(props) {
return <h1>hello,{props.name}</h1>
}
class定义组件
class Welcome extends React.Component {
render(){
return <h1>hello,{this.props.name}</h1>
}
}
调用组件
<Welcome name="Sara">
函数式组件不能声明状态 state。状态只能由内部初始化,内部改变
// Wrong
this.state.comment = 'Hello';
// Correct
this.setState({comment: 'Hello'});
// Wrong
this.setState({
counter: this.state.counter + this.props.increment,
});
// Correct
//该函数将接收先前的状态作为第一个参数,将此次更新被应用时的props做为第二个参数
this.setState((prevState, props) => ({
counter: prevState.counter + props.increment
}));
通常我们会为事件处理程序传递额外的参数。例如,若是 id 是你要删除那一行的id,以下两种方式都可以向事件处理程序传递参数:
<button onClick={(e) => this.deleteRow(id, e)}>Delete Row</button>
<button onClick={this.deleteRow.bind(this, id)}>Delete Row</button>
上面两个例子中,参数 e 作为 React 事件对象将会被作为第二个参数进行传递。通过箭头函数的方式,事件对象必须显式的进行传递,但是通过 bind的方式,事件对象以及更多的参数将会被隐式的进行传递。
隐式传递就是不传递,照样可以用
值得注意的是,通过 bind方式向监听函数传参,在类组件中定义的监听函数,事件对象 e 要排在所传递参数的后面,例如:
class Popper extends React.Component{
constructor(){
super();
this.state = {name:'Hello world!'};
}
preventPop(name, e){ //事件对象e要放在最后
e.preventDefault();
alert(name);
}
render(){
return (
<div>
<p>hello</p>
{/* Pass params via bind() method. */}
<a href="https://reactjs.org" onClick={this.preventPop.bind(this,this.state.name)}>Click</a>
</div>
);
}
}
当需要隐藏某个组件时,不需要触发一个hide函数,只需个组件传递个参数作为判断条件即可:
function WarningBanner(props) {
if (!props.warn) {
return null; // 如果!props.warn则返回null,此时组件就不渲染
}
return (
<div className="warning">
Warning!
</div>
);
}
class Page extends React.Component {
constructor(props) {
super(props);
this.state = {showWarning: true}
this.handleToggleClick = this.handleToggleClick.bind(this);
}
handleToggleClick() {
this.setState(prevState => ({
showWarning: !prevState.showWarning
}));
}
render() {
return (
<div>
<WarningBanner warn={this.state.showWarning} />
<button onClick={this.handleToggleClick}>
{this.state.showWarning ? 'Hide' : 'Show'}
</button>
</div>
);
}
}
ReactDOM.render(
<Page />,
document.getElementById('root')
);
你可以传递任何 {} 包裹的 JavaScript 表达式作为一个属性值。例如,在这个 JSX 中:
<MyComponent foo={1 + 2 + 3 + 4} />
对于 MyComponent
来说, props.foo
的值为 10,这是 1 + 2 + 3 + 4
表达式计算得出的。
if 语句和 for 循环在 JavaScript 中不是表达式,因此它们不能直接在 JSX 中使用,但是你可以将它们放在周围的代码中。
function NumberDescriber(props) {
let description;
if (props.number % 2 == 0) {
description = <strong>even</strong>;
} else {
description = <i>odd</i>;
}
return <div>{props.number} is an {description} number</div>;
}
<MyComponent message="hello world" />
<MyComponent message={'hello world'} />
<MyComponent message="<3" />
<MyComponent message={'<3'} />
如果你没有给属性传值,它默认为 true。因此下面两个 JSX 是等价的:
<MyTextBox autocomplete />
<MyTextBox autocomplete={true} />
一般情况下,我们不建议这样使用,因为它会与 ES6 对象简洁表示法 混淆。比如
{foo}
是{foo: foo}
的简写,而不是{foo:true}
。这里能这样用,是因为它符合 HTML 的做法。
如果你已经有了个 props 对象,并且想在 JSX 中传递它,你可以使用 ... 作为扩展操作符来传递整个属性对象。下面两个组件是等效的:
function App1() {
return <Greeting firstName="Ben" lastName="Hector" />;
}
function App2() {
const props = {firstName: 'Ben', lastName: 'Hector'};
return <Greeting {...props} />; // 解构赋值
}
class Demo extends Component{
static defaultProps = {
name: '默认值' // 当使用到name时,如果父组件没有传值,则显示为‘默认值’
}
static propTypes = {
age: React.propTypes.number,
name: React.propTypes.string // 约定需要的类型(string)
}
render(){
……
}
}
// 另一种方式
Demo.defaultProps = {
name: '默认值' // 当使用到name时,如果父组件没有传值,则显示为‘默认值’
}
Demo.propTypes = {
name: React.propTypes.string // 约定需要的类型(string)
}
propTypes类型约束只在开发阶段有效,发布时会自动移除。
使用 react 经常会遇到几个组件需要共用状态数据的情况。这种情况下,我们最好将这部分共享的状态提升至他们最近的父组件当中进行管理。
class Demo extends Component{
……
render(){
<div>
<ul>
{
arr.map((item,index) => <li key={index}>{item}</li>)
}
{
arr.map((item,index) => {
return(<li key={index}>{item}</li>)
})
}
</ul>
</div>
}
}
// react 元素
let eleObj = {
type: 'div',
props:{
className: 'red',
id: 'div',
children:[
'hello',
{
type:'span',
props:{
className:'blue',
children:['word']
}
}
]
}
}
// 模拟render函数
function render(eleObj, container){
// 解构出标签的类型和属性对象
let {type, props} = eleObj;
// 创建一个DOM元素
let ele = document.createElement(type);
// 循环属性对象
for (let attr in props){
if(attr == 'children'){
props[attr].forEach(item => {
if(typeof item == 'string'){
let textNode = document.createTextNode(item);
ele.appendChild(textNode);
} else {
// 递归调用
render(item, ele);
}
})
} else if(attr == 'className') {
ele.setAttribute('class',props[attr]);
} else {
ele.setAttribute(attr, props[attr]);
}
}
}
// 页面路由
window.location.href = 'http://www.baidu.com';
history.back();
// hash 路由
window.location = '#hash';
window.onhashchange = function(){
console.log('current hash:', window.location.hash);
}
// h5 路由
// 推进一个状态
history.pushState('name', 'title', '/path');
// 替换一个状态
history.replaceState('name', 'title', '/path');
// popstate
window.onpopstate = function(){
console.log(window.location.href);
console.log(window.location.pathname);
console.log(window.location.hash);
console.log(window.location.search);
}
// react-router
import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter as Router, Switch, Route, Link } from 'react-router-dom'
class A extends React.Component{
constructor(props){
super(props)
}
render(){
return (
<div>
Component A
<Switch>
// 在route里还能写JSX
<Route exact path={`${this.props.match.path}`} render={(route) => {
return <div>当前组件是不带参数的A</div>
}}/>
// a/sub 要写在 a/:id的前面,因为sub也有可能被当作id
<Route path={`${this.props.match.path}/sub`} render={(route) => {
return <div>当前组件是Sub</div>
}}/>
<Route path={`${this.props.match.path}/:id`} render={(route) => {
return <div>当前组件是带参数的A, 参数是:{route.match.params.id}</div>
}}/>
</Switch>
</div>
)
}
}
class B extends React.Component{
constructor(props){
super(props)
}
render(){
return <div>Component B</div>
}
}
class Wrapper extends React.Component{
constructor(props){
super(props)
}
render(){
return (
<div>
<Link to="/a">组件A</Link>
<br/>
<Link to="/a/123">带参数的组件A</Link>
<br/>
<Link to="/b">组件B</Link>
<br/>
<Link to="/a/sub">/a/sub</Link>
{this.props.children}
</div>
);
}
}
ReactDOM.render(
<Router>
<Wrapper>
<Route path="/a" component={A}/>
<Route path="/b" component={B}/>
</Wrapper>
</Router>,
document.getElementById('app')
);
// ref等于一个函数,表示当元素被挂载到页面中之后立即调用此函数,并传入渲染后的DOM元素
// this 指的是这个组件实例 也就是当页面渲染之后,this.div 也就表示的是 div这个元素
class A extends Component{
render(){
return (
<div ref= {ref=>this.div=ref}>hahahhah</div>
)
}
}
<li className={['mingxi-tit-one', this.state.buttonType === 1 && 'mingxi-active'].join(' ')}></li>
// 数组元素为className,
// && 符号为判断符,若条件成立则执行后面的内容
// join为数组的方法,将数组元素拼接为字符串,链接符为一个空字符串
yarn add node-sass-chokidar
// package.json
"build-css": "node-sass-chokidar src/ -o src/",
"watch-css": "npm run build-css && node-sass-chokidar src/ -o src/ --watch --recursive",
执行 npm run watch-css
修改端口:
// 新建.env.local
PORT = 2000
vue中 config/index.js
:
react:
//package.json
"proxy":{
"/api":{
"target": "https://m.weibo.cn",
"changeOrigin": true
}
}
//方式一:
import tsIcon from '../images/typescript.jpeg';
//方式二:
const tsIcon = require('../images/typescript.jpeg');
//使用方式一:
<img src={tsIcon} alt="" />
//使用方式二:
<div style={{background: `url(${tsIcon}) no-repeat`}}></div>
//使用方式三:
const styles = { test: { background: `url(${tsIcon}) no-repeat center` }}render() { return ( <div style={styles.test}></div> )}
handleInputChange(event) {
const target = event.target;
const value = target.type === 'checkbox' ? target.checked : target.value;
const name = target.name;
this.setState({
[name]: value
});
}
class App extends Component{
add = ()=>{
this.child.add(); // 调用子组件的方法
}
render(){
return (
<Child ref={instance => this.child = instance}>
)
}
}
父组件将函数作为props传递给子组件,子组件在需要时调用,将数据作为函数参数传回——回调(callback)
// 子组件
class Child extends Component{
call=()=>{
this.props.fun && this.props.fun('听到了吗?');
}
render(){
return(
我是子组件
<button onClick={()=>this.call()}>按钮</button>
)
}
}
// 父组件
class Parent extends Component{
listen= (callback)=> {
console.log(callback); // 听到了吗?
}
render(){
return(
<Child fun={this.listen} />
)
}
}
this.props.history.goBack();
<Route path="/Search/:category/:keyword?" component={...}/>
// app.js
import React, { Component } from 'react';
import {BrowserRouter as Router,Redirect,Route,Link,Switch} from 'react-router-dom';
import './app.css';
import Login from './views/login/index.js';
import Home from './views/home/index.js';
class App extends Component {
render() {
return (
<div className="app">
<Router>
<Switch>
<Route exact path="/login" component={Login} />
<Route exact path="/home" component={Home} />
</Switch>
</Router>
</div>
);
}
}
export default App;
// login.js
import React, { Component } from 'react';
import { Form, Icon, Input, Button, Checkbox } from 'antd';
import './index.css';
const FormItem = Form.Item;
class LoginForm extends Component {
constructor(props){
super(props);
this.state = {
pid: '1111'
}
}
handleSubmit = (e) => {
e.preventDefault();
this.props.form.validateFields((err, values) => {
if (!err) {
// params 只是个变量名可以换
this.props.history.push({pathname:'home',params:{pid:this.state.pid}});
}
});
}
render() {
....
}
}
// home.js
import React, { Component } from 'react';
export default class Home extends Component{
constructor(props){
super(props);
}
render(){
return (
<div>
home
{this.props.location.params.pid} // 获取参数
</div>
)
}
}
// app.js
<Route exact path="/home/:pid" component={Home} />
// home.js
{this.props.match.params.pid}
// login.js
this.props.history.push(`/home/${this.state.pid}`);
// login.js
this.props.history.push(`/home/?pid=${this.state.pid}`);
// home.js
getUrlParam = (name)=>{
// xxxx.com?param1=123¶m2=456
let queryString = window.location.search.split('?')[1] || '',
// (^|&) + name 以 name 或者 &name 开头
// ([^&]*) 非 & 符的0个或多个字符
// [^xyz] 负值字符集合。匹配未包含的任意字符。例如, '[^abc]' 可以匹配 "plain" 中的'p'、'l'、'i'、'n'。
// (&|$) 接上上面 就是 (0个或者多个字符)或者以&符结尾
reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)"),
result = queryString.match(reg);
// result : ['param1=123','','123','&']
return result ? decodeURIComponent(result[2]) : null
}
{this.getUrlParam('pid')}
第一:修改nginx配置
第二:react将BrowserRouter改成HashRouter
有人会问 webpack-dev-server 已经是热加载了,能做到只要代码修改了页面也自动更新了,为什么在 react 项目还要安装 react-hot-loader 呢?其实这两者的更新是有区别的,webpack-dev-server 的热加载是开发人员修改了代码,代码经过打包,重新刷新了整个页面。而 react-hot-loader 不会刷新整个页面,它只替换了修改的代码,做到了页面的局部刷新。但它需要依赖 webpack 的 HotModuleReplacement 热加载插件。
安装 react-hot-loader
npm install --save-dev react-hot-loader
在 webpack.config.js
的 entry 值里加上 react-hot-loader/patch
,一定要写在entry 的最前面,如果有 babel-polyfill
就写在babel-polyfill
的后面。
entry: [
'babel-polyfill',
'react-hot-loader/patch', //设置这里
__dirname + '/app/main.js'
]
在 webpack.config.js 中设置 devServer 的 hot 为 true
devServer: {
contentBase: './build',
port: '1188',
historyApiFallback: true,
inline: true,
hot: true, //设置这里
}
{
"presets": ['es2015', 'react'],
"plugins": ["react-hot-loader/babel"] //设置这里
}
在 webpack.config.js 的 plugins 里添加依赖的 HotModuleReplacement 插件
plugins: [
new HtmlWebpackPlugin({
template: __dirname + "/app/index.tmpl.html"
}),
new webpack.HotModuleReplacementPlugin() //设置这里
]
import React from 'react';
import ReactDOM from 'react-dom';
import Greeter from './greeter';
import "./main.css";
import { AppContainer } from 'react-hot-loader'; //设置这里
const render = (App) => {
ReactDOM.render(
<AppContainer>
<App />
</AppContainer>,
document.getElementById('root')
)
}
render(Greeter)
// Hot Module Replacement API
if (module.hot) {
module.hot.accept('./greeter', () => {
render(require('./greeter').default)
})
}
简写成这样试了一下也能成功
import React from 'react';
import ReactDOM from 'react-dom';
import Greeter from './greeter';
import "./main.css";
ReactDOM.render(
<App />,
document.getElementById('root')
)
if (module.hot) {
module.hot.accept()
}