这是一个菜鸟写BUG的录记帖。


  • React高级班

    初来乍到,拜个码头


  • React高级班

    自己一直租着阿里服务器用来学习,主要是确实很方便,可能是linux系统的原因吧,搭建React的过程很不顺利。然后在网上找各种的问题解决方案,在这里记录一部分。

    安装完node最新版本,node-v一直是0.84(反正很低的版本)。n进入版本选择是选择不了版本的。
    node 版本管理工具 n 无效的原理及解决方案

    使用 create-react-app 快速构建 React 开发环境
    步骤:
    1、 npm install -g create-react-app
    2、 create-react-app my-app
    3、 进入目录 -> cd my-app
    4、 Npm start 运行项目


  • React高级班

    那么我们可以进来看看都是什么东西了,本萌新还是比较喜欢用sublime,终于搭建完环境之后开森的我发现整个my-app是FTP不下来的,然后仔细研究了一下,应该只有src和public文件有用,应该只FTP他们两个就可以了。
    那些都是大佬,这么小白的问题应该没晓得说。☺


  • administrators

    @任意 Old Song 说过,要用nvm和npm且都要配置国内镜像。


  • React高级班

    @worldsong 好的,谢谢老师😢


  • React高级班

    This post is deleted!

  • React高级班

    用sublime写React高亮显示是不对的,解决办法。
    shift+ctrl+p => 输入 install package => React ES6 Snippets =》回车安装
    点击有下脚JavaScript修改为JavaScript(Babel),大功告成


  • administrators

    @任意 镜像是指:资源镜像站点;有各种资源,比如node、npm、pip、docker等等。
    你这里需要的是: http://songfens.club/topic/18/你的Node环境有问题?这篇文章可以帮到你!


  • React高级班

    @worldsong 😆 谢谢,老师。


  • React高级班

    之前的react高级课程听的确实有点费劲,就没有跟下去了,今天听起来轻松了许多,在此写写笔记。顺便保持一个不定时更新的好习惯。


  • React高级班

    ref步骤
    <input ref={ (input)=>{this.textIput=input;} } />
    // 设置个鱼线this.textIput
    1.定义
    fun(){
    this.textIput.focus();
    }
    2.绑定
    constructor(props){
    super(props);
    this.fun=this.fun.bind(this);
    }
    3.使用onClick = {this.fun}
    4.可以父组件调用
    componentDidMount(){
    this.textInput.fun();//父组件没有fun这个方法,用钓鱼从子组件内部调出来了。
    }//父组件自动的获取子组件input焦点。
    render(){
    return(
    <TextInput ref={ (input)=>{this.textInput=input;} } />
    )
    }

    注意:
    类组建class是可以直接用钩子的,在函数组建里要先声明:
    function TextInput(props){
    let testInput = null; //函数组建先声明
    return(
    <input ref={ (input)=>{testInput=input} } />
    )
    }
    建议class组建

    可传递的ref属性 //这里依然糊涂,以后补上,说不定哪天脑瓜子一亮想通了。

    from表单的提交一般是非受控组建,这话课堂里old song说的,记一下。
    <input defaultValue="默认值" /> //记性不好也记一下

    事件绑定
    方式一
    constructor(props){
    super(props);
    this.clickFun = this.clickFun.bind(this);
    };
    clickFun(){};
    方式二
    clickFun = () = >{};
    方式三(行内绑定)
    <button onClick={ this.clickFun.bind(this) }>按钮</button>
    方式四(箭头函数方法)
    <button onClick={ () => this.clickFun() }>按钮</button>
    注意:
    在render方法中使用箭头函数也会在每次组件渲染时创建一个新的函数,可能会影响性能。

    //call原始调用
    function fun( text ){
    console.log(this + text);
    }
    hello.call('renyi','hello'); //第一个参数指的是this。

    //bind绑定
    var box = {
    name: 'renyi',
    fun:function( thing ){
    console.log( this.name + thing );
    }
    }
    var bundHello = box.fun.bind( box );
    bundHello( 100 ); //bind绑定的是box,相当于相当于bundHello.call( box , 100 );

    /*
    box.fun(100);
    bundHello(100);
    var bundHello = box.fun;//会报错,没有name这个属性,this是window。
    bundHello( 100 ); //相当于bundHello.call( window, 100 );
    */

    //apply 还没研究,以后再补上


  • React高级班

    虚拟+DOM+与+DIFF+算法,这堂课相信我,这是一堂让我睡着了好多次的课程,然后又退回去重新看。只是学习了一丢丢。以后能听懂在回来听吧。
    (只记录了这一丢丢)
    创建的时候:constructor构造函数。
    挂载的时候:componentDidMount。
    卸载的时候:componentWillUnmount。
    更新的时候:componentDidUpdate.

    节点从下往上更新。
    key有暂时储存此节点的能力,避免了生死轮回。

    异步渲染:时间分片特性

    这堂课听得有点难度,插个旗子,感觉自己等级可以做这个任务的时候在回来做。先去下堂课打怪升级。


  • React高级班

    setState异步的原理与更新策略

    import React, { Component } from 'react';
    //绕过React通过addEventListener直接添加的事件处理函数和setTimeout/setInterval产生的异步调用。
    export default class App extends Component {
    constructor(props){
    super(props);
    this.state = {
    num:0
    }
    }

    //0,0,2,3
    componentDidMount(){
    this.setState({num:this.state.num+1},()=>{ //回调函数里面去执行,0 0 1 3 4 (原0 0 2 3)
    this.setState({num:this.state.num+1});
    console.log(this.state.num,'结果立即生效');
    });
    console.log(this.state.num,'壹');
    this.setState({num:this.state.num+1});
    console.log(this.state.num,'贰');
    setTimeout(()=>{ //这两次this.stat的值同步更新了
    this.setState({num:this.state.num+1});
    console.log(this.state.num,'叁');
    this.setState({num:this.state.num+1});
    console.log(this.state.num,'肆');
    });
    this.refs.button.addEventListener('click', this.fun); //refs摆脱了react控制,非受控组件(原生JS)。
    }

    fun = () => {
    console.log('button')
    this.setState({num:this.state.num+1});
    this.setState({num:this.state.num+1});
    }

    render(){
    //0,0,2,3 return null
    return (
    <div>
    <span>{this.state.num}</span> |
    <button onClick={this.fun}>异步按钮</button> |
    <button ref='button'>同步按钮(refs)</button>
    </div>
    );
    }
    }

    //传统式的setState与函数式的setState一定不要混用

    函数式写法
    componentDidMount(){
    this.setState((state,props)=>{
    return {num:state.num + 1};
    });
    //是同步执行的
    }

    没有导致state的值发生变化的setState是否导致重渲染!【会】(state和props依然会被修改,但不会重绘)

    性能优化,避免多余的重新渲染。
    //在render函数调用前判断:如果前后state中Number不变,通过return false阻止render调用
    shouldComponentUpdate(nextProps,nextState){
    if(nextState.Number == this.state.Number){
    return false
    }
    }

    子组件避免多余的重新渲染
    shouldComponentUpdate(nextProps,nextState){
    if(nextProps.number == this.props.number){
    return false
    }
    return true
    }

    复杂的props和state的数据相同如何避免重复渲染
    //对象对比的是引用,所有return false
    这里听得有点难度,插个旗子,感觉自己等级可以做这个任务的时候在回来做。先去下堂课打怪升级。自我感觉这里有部分基础知识没有掌握。

    建议使用immutable.js(老师这里留了个传送门)

    总结:setState不会立刻改变React组件中的state的值,他会放到下一轮的事件循环中去改变。setTimeout,事件监听的方式,setState(nextState,callback),Promisc封装的方式。


  • React高级班

    Node做服务端渲染
    客户端渲染存在几个问题:用户初次访问的体验不好,对 SEO 不友好。
    //不是说客户端做的越来越多,为什么使用服务器端渲染。
    但这里有两个问题:
    在 js 文件加载完成并执行以前,我们只看到一片空白,
    一旦 js 文件中有错误,也可能导致页面空白。
    无论哪种情况,对用户来说,都是糟糕的体验。
    而服务器端渲染能够改善用户体验:
    用户能马上看到页面内容 - 而不是等待 js 文件下载、执行完成后才能看到,
    哪怕 js 中有错误,也只会导致页面交互问题,而不是一片空白。

    服务器端渲染流程
    传统的服务器端渲染大致是这样一个流程:
    从数据库读取数据,
    编译模板并生成 HTML,
    返回 HTML 给客户端,
    浏览器解析 HTML 并下载 HTML 页面中的脚本然后执行。
    React.js 的服务器端渲染也是同理,只不过,这里模板换成了 React.js 组件,JavaScript 事件处理器的绑定则由 React.js 操作:
    从数据库读取数据,
    编译 React.js 组件并生成 HTML 代码,
    返回 HTML 给客户端(浏览器),
    浏览器解析 HTML 并下载 HTML 页面中的 React.js 代码然后注入事件处理器。

    $ mkdir react-server-render //创建个react-server-render文件夹
    $ cd react-server-render
    $ npm install yarn -g
    $ yarn init -y
    $ yarn add react react-dom //安装react和react-dom

    npm list -g --depth=0
    //查看是否安装webpack
    //查看是否安装了yarn,如果没有 npm install -g yarn

    webpack 来打包 安装 webpack:(没有全局安装)
    $ yarn add webpack webpack-cli --dev
    found incompatible module 什么鬼?版本低了
    $ nvm list //查看安装版本
    $ nvm use 10.3.0 //选择版本

    安装:npm install -g npx //npm 5.0以上已经有npx功能了,可以不安装。
    $ npx webpack //打包完成,默认生产环境。
    $ npx webpack --mode development //开发模式的打包

    搭建node服务端渲染
    const http = require('http')
    const server = http.createServer(function (request, response) {
    response.writeHead(200, { 'Content-Type': 'text/html' })
    response.write('hello react.js')
    response.end()
    })
    server.listen(4200)
    console.log('Server is listening on 4200')
    返回字符串,不是页面

    搭建node服务端渲染,返回页面
    const http = require('http')
    const fs = require('fs') //文件读取的方式
    const path = require('path') //路径处理模块

    /*
    //所有的请求都返回index.html
    const server = http.createServer(function (request, response) {
    response.writeHead(200, { 'Content-Type': 'text/html' })
    fs.createReadStream(path.join(__dirname, 'index.html')).pipe(response);
    })
    */

    //区分不同的请求,分别响应html,js。
    const server = http.createServer(function (request, response) {
    //路由
    if(request.url === '/'){ //如果是根目录,就返回index.html
    response.writeHead(200, { 'Content-Type': 'text/html' })
    //流的方式,不明白,先听着。
    fs.createReadStream(path.join(__dirname, 'index.html')).pipe(response); //__dirname兼容windows和linux环境
    }else{
    if (/.js/.test(request.url)) { //响应的是JS文件
    response.writeHead(200, { 'Content-Type': 'application/javascript' })
    fs.createReadStream(path.join(__dirname, ${request.url})).pipe(response)
    } else {
    response.writeHead(404)
    response.end()
    }
    }

    })
    server.listen(4200)
    console.log('Server is listening on 4200')

    ReactDOMServer(模板服务端渲染,数据客户端渲染)
    renderToString()
    renderToStaticMarkup()

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    const http = require('http')
    const fs = require('fs') //文件读取的方式
    const path = require('path') //路径处理模块
    const ReactDOMServer = require('react-dom/server') //引入服务端渲染
    const React = require('react') //react模块
    const App = require('./src/App.js') //引入组件

    const server = http.createServer(function (request, response) {
    //路由
    if(request.url === '/'){
    response.writeHead(200, { 'Content-Type': 'text/html' })
    //把react组件App转换成我们的页面。模板+数据,这里是数据,需要一个模板。
    response.write( ReactDOMServer.renderToString(React.createElement(App)) );
    response.end();
    }else{
    if (/.js/.test(request.url)) { //响应的是JS文件
    response.writeHead(200, { 'Content-Type': 'application/javascript' })
    fs.createReadStream(path.join(__dirname, ${request.url})).pipe(response)
    } else {
    response.writeHead(404)
    response.end()
    }
    }

    })
    server.listen(4200)
    console.log('Server is listening on 4200')

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    const http = require('http')
    const fs = require('fs') //文件读取的方式
    const path = require('path') //路径处理模块

    const ReactDOMServer = require('react-dom/server') //引入服务端渲染
    const React = require('react') //react模块
    const App = require('./src/App.js') //引入组件

    function genHTML (body) {
    return <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>React 服务器端渲染</title> </head> <body> <div id="root">${body}</div> <script src="dist/main.js"></script> </body> </html>
    }

    const server = http.createServer(function (request, response) {
    //路由
    if(request.url === '/'){
    /*
    response.writeHead(200, { 'Content-Type': 'text/html' })
    //把react组件App转换成我们的页面。模板+数据,这里是数据,需要一个模板。
    response.write( ReactDOMServer.renderToString(React.createElement(App)) );
    response.end();
    */
    let body = ReactDOMServer.renderToString(React.createElement(App))
    response.writeHead(200, { 'Content-Type': 'text/html' })
    response.write(genHTML(body));//把app模块在HTML里
    response.end()
    }else{
    if (/.js/.test(request.url)) { //响应的是JS文件
    response.writeHead(200, { 'Content-Type': 'application/javascript' })
    fs.createReadStream(path.join(__dirname, ${request.url})).pipe(response)
    } else {
    response.writeHead(404)
    response.end()
    }
    }

    })
    server.listen(4200)
    console.log('Server is listening on 4200')

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    警告:render可能会修改DOM节点。React v17。
    const React = require('react')
    const ReactDOM = require('react-dom')
    const App = require('./App')
    ReactDOM.render(React.createElement(App), document.getElementById('root'))

    const React = require('react')
    const ReactDOM = require('react-dom')
    const App = require('./App')
    ReactDOM.hydrate(React.createElement(App), document.getElementById('root'))

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    React服务端渲染的框架Next
    英文版:https://github.com/zeit/next.js
    中文版:https://github.com/accforgit/DayLearnNote/blob/master/translation/Next.js-README.md
    Next.js是一个用于React应用的极简的服务端渲染框架。

    $ yarn add react react-dom next

    在你的 package.json文件中添加如下代码:
    {
    "scripts": {
    "dev": "next",
    "build": "next build",
    "start": "next start"
    }
    }

    pages 单独做服务端渲染的

    然后,在控制台输入 npm run dev命令,打开 http://localhost:3000即可看到程序已经运行,你当然也可以使用其他的端口号,可以使用这条命令:npm run dev -- -p <your port here>。

    增量更新

    .next文件是用来部署的,部署dist就可以了。

    代码自动分割:
    你所声明的每个import命令所导入的文件会且只会与相关页面进行绑定并提供服务,也就是说,页面不会加载不需要的代码。


  • React高级班

    技术是不值钱的,只有技术创造了产品产生了价值才是值钱的--old song
    我觉得这一句话在职业生涯中很有道理。

    学习链接:http://reacttraining.cn/web/example/basic
    简书:https://www.jianshu.com/p/53820bb5ee78
    老规矩,各种安装啥啥啥的搞起。
    npm install -g create-react-app //这行就免了吧,小伙伴们肯定有的
    create-react-app demo-app
    cd demo-app
    yarn add react-router-dom 或者 npm install react-router-dom
    初始化完毕了,然后很流利的npm start。

    基本用法:
    <Link exact to="./urlName">button</Link>//exact 默认
    <Route path="./urlName" component={控件}/>

    Route 与Redirect 不能写同一个URL

    奥,这里还问了derry老师一个被挨打的问题:
    const Box =()=>(); 为啥不是 ()=>{};
    答案:这你是在用 JSX ,JSX的语法最外层可以有() 包裹。你这段代码和 ()=>{} 没有区别,用 {} 需要明确写 return 关键字, 用() 默认会有 return值。

    import React, { Component } from 'react'
    import {
    BrowserRouter as Router,
    Route,
    Link
    } from 'react-router-dom';
    import Box from './box.js';

    class BasicExample extends Component{
    constructor(props){
    super(props);
    this.state={
    num:'123456',
    }
    }
    Home = ( {match} ) =>(
    <box>
    <Box num={this.state.num} />
    </box>
    )
    About = ( {match} ) =>(
    <div>
    <h2>About</h2>
    <h2>{match.url}</h2>
    <em>获取URL: /about </em>
    </div>
    )
    Topice = ( {match} ) =>(
    <div>
    <h3>列表</h3>
    <ul>
    <Link to={${match.url}/box1}><li>box1</li></Link>
    <Link to={${match.url}/box2}><li>box2</li></Link>
    <Link to={${match.url}/box3}><li>box3</li></Link>
    </ul>
    <hr />
    <Route path={${match.url}/:Id} component={this.Topic}/>
    <Route exact path={match.url} render={() => (
    <div>
    <h3>请选择一个主题。</h3>
    <em>默认加载的模块</em>
    </div>
    )}/>
    </div>
    )
    Topic = ({ match }) => (
    <div>
    <h3>{match.params.Id}</h3>
    </div>
    )
    render(){
    return(
    <Router>
    <div>
    <ul>
    <li><Link to="/">Home</Link></li>
    <li><Link to="/about">About</Link></li>
    <li><Link to="/topice">Topice</Link></li>
    </ul>
    <hr/>
    <Route exact path="/" component={this.Home} />
    <Route path="/about" component={this.About} />
    <Route path="/topice" component={this.Topice} />
    </div>
    </Router>
    )
    }
    }

    export default BasicExample

    动态匹配,比较简单:
    const Box = ()=>(
    <Router>
    <div>
    <h2>账号:URL获取</h2>
    <hr />
    <ul>
    <Link to="box1"><li>box1</li></Link>
    <Link to="box2"><li>box2</li></Link>
    </ul>
    <hr />
    <Route path="/:id" component={Child} />
    </div>
    </Router>
    );

    const Child = ({ match }) => (
    <div>
    <h3>match.url: {match.url}</h3>
    <h3>match.params.id: {match.params.id}</h3>
    </div>
    )
    export default Box;

    replace替换新的历史记录:
    <li><Link to="/about" replace={false} >About</Link></li>

    符合url的NavLink的CSS样式:
    <NavLink to="/topics" activeClassName="linkClass" >Topics</NavLink>

    <NavLink
    to="/faq"
    activeStyle={{
    fontWeight: 'bold',
    color: 'red'
    }}

    FAQs</NavLink>

    exact初始化选择的Route:
    <Route exact path="/" component={Home}/>

    判断这个URL是否我想要的:
    <NavLink to="/123" isActive={oddEvent} >Event 123</NavLink>
    if(url === 'now') {
    this.setState({
    show: true
    })
    }
    const isActive = { this.state.show }

    isActive: func
    <NavLink to="/events/123" isActive={oddEvent} >Event 123</NavLink>
    <Route path="/events/:eventID" component={Event123} />

    const Event123 = ({ match }) => (
    <div>
    <h3>match.url: {match.url}</h3>
    {console.log('组件里的console.log(match.params): ',match.params)}
    <h3>match.params.eventID: {match.params.eventID}</h3>
    </div>
    )

    const oddEvent = (match, location) => {
    if (!match) {
    return false
    }
    console.log(match.url)
    console.log('event里的console.log(match.params): ',match.params)
    console.log(match.params.eventID) //不知道为什么获取不到。API是假的吧
    const eventID = parseInt(match.params.eventID)
    console.log(eventID)
    return !isNaN(eventID) && eventID % 2 === 1
    }

    路由三种渲染方法:
    <Route component={box} />
    <Route render={()=><div>home</div> } />
    <Route children /> //不管路由是否匹配都会渲染对应组件

    路由重定:
    <NavLink to="/box">Box</NavLink>

    <Box path="/box" component={Box1}/>
    <Route path="/login" component={Box2} />

    const Box = ({ component: Component, ...rest }) => (
    <Route {...rest} render={props => (
    false ? (
    <Component {...props}/>
    ) : (
    <Redirect to={{
    pathname: '/login',
    state: { from: props.location } //不懂,留个坑
    }}/>
    )
    )}/>
    )
    const Box1 = () => <h2>Box1</h2>
    class Box2 extends React.Component {
    state = {
    redirectToReferrer: false
    }
    render() {
    console.log(this.state)
    return (
    <div>
    <h2>Box2</h2>
    </div>
    )
    }
    }

    Prompt离开页面提示
    class Box extends React.Component {
    render() {
    return (
    <div>
    <h2>Box2</h2>
    <Prompt when={true} message="您确定要离开当前页面吗?"/>
    </div>
    )
    }
    }

    children这个简单就不赘述了
    <Route children={({ match, ...rest }) => (
    <div}}>children</div>
    )}/>

    Switch组件
    import { Switch } from "react-router-dom";
    <Switch>
    <Route path="/" component={Test1} />
    <Route path="/Test" component={Test2} />
    </Switch>
    //它的特性是我们只渲染所匹配到的第一个路由组件,一般界面渲染的时候,会渲染所有匹配到的路由组件。它的孩子节点只能是Route组件或者Redirect组件。

    URL的匹配
    <Route path="/users/:id" component={User}/>
    strict
    <Route strict path="/one/" component={About}/>
    /one 不可以 /one/ 可以 /one/two 可以
    <Route exact strict path="/one" component={About}/>
    /one 可以 /one/ 不可以 /one/two 不可以

    <StaticRouter>没看明白。
    https://github.com/react-translate-team/react-router-CN/blob/master/packages/react-router-dom/docs/guides/server-rendering.md
    song老师让我把这个操作一遍,下次再来吹牛逼。


Log in to reply
 

Looks like your connection to 新前端社区 was lost, please wait while we try to reconnect.