• 来一道Vue-cli 3的面试题测测你的前端技术段位!!!

    0_1547197333593_0dad088c-f242-4c94-b89b-d5cb29e8a96b-image.png
    1、说说Vue-cli这个版本号的来源?
    2、他表示的是本地版本号吗?
    3、从vue-cli源码中解析正确流程?

    posted in 讨论区
  • 一文搞懂单页应用原理Vue项目History模式路由机制

    Vue 单页应用(spa)前端路由实现原理

    写在前面:通常 SPA 中前端路由有2种实现方式:

    • window.history
    • location.hash

    下面就来介绍下这两种方式具体怎么实现的

    一.history

    1.history基本介绍

    window.history 对象包含浏览器的历史,window.history 对象在编写时可不使用 window 这个前缀。history是实现SPA前端路由是一种主流方法,它有几个原始方法:

    • history.back() - 与在浏览器点击后退按钮相同
    • history.forward() - 与在浏览器中点击按钮向前相同
    • history.go(n) - 接受一个整数作为参数,移动到该整数指定的页面,比如go(1)相当于forward(),go(-1)相当于back(),go(0)相当于刷新当前页面
    • 如果移动的位置超出了访问历史的边界,以上三个方法并不报错,而是静默失败

    在HTML5,history对象提出了 pushState() 方法和 replaceState() 方法,这两个方法可以用来向历史栈中添加数据,就好像 url 变化了一样(过去只有 url 变化历史栈才会变化),这样就可以很好的模拟浏览历史和前进后退了,现在的前端路由也是基于这个原理实现的。

    2.history.pushState

    pushState(stateObj, title, url) 方法向历史栈中写入数据,其第一个参数是要写入的数据对象(不大于640kB),第二个参数是页面的 title, 第三个参数是 url (相对路径)。

    • stateObj :一个与指定网址相关的状态对象,popstate事件触发时,该对象会传入回调函数。如果不需要这个对象,此* 处可以填null。
    • title:新页面的标题,但是所有浏览器目前都忽略这个值,因此这里可以填null。
    • url:新的网址,必须与当前页面处在同一个域。浏览器的地址栏将显示这个网址。

    关于pushState,有几个值得注意的地方:

    • pushState方法不会触发页面刷新,只是导致history对象发生变化,地址栏会有反应,只有当触发前进后退等事件(back()和forward()等)时浏览器才会刷新
    • 这里的 url 是受到同源策略限制的,防止恶意脚本模仿其他网站 url 用来欺骗用户,所以当违背同源策略时将会报错

    3.history.replaceState

    replaceState(stateObj, title, url) 和pushState的区别就在于它不是写入而是替换修改浏览历史中当前纪录,其余和 pushState一模一样。

    4.popstate事件

    • 定义:每当同一个文档的浏览历史(即history对象)出现变化时,就会触发popstate事件。
    • 注意:仅仅调用pushState方法或replaceState方法 ,并不会触发该事件,只有用户点击浏览器倒退按钮和前进按钮,或者使用JavaScript调用back、forward、go方法时才会触发。另外,该事件只针对同一个文档,如果浏览历史的切换,导致加载不同的文档,该事件也不会触发。
    • 用法:使用的时候,可以为popstate事件指定回调函数。这个回调函数的参数是一个event事件对象,它的state属性指向pushState和replaceState方法为当前URL所提供的状态对象(即这两个方法的第一个参数)。

    5.history实现spa前端路由代码

    <a class="spa">abc.html</a>
    <a class="spa">123.html</a>
    <a href="/song" class="spa ">song</a>
    
      // 注册路由
      document.querySelectorAll('.spa').forEach(item => {
        item.addEventListener('click', e => {
          e.preventDefault();
          let link = item.textContent;
          if (!!(window.history && history.pushState)) {
            // 支持History API
            window.history.pushState({name: 'history'}, link, link);
          } else {
            // 不支持,可使用一些Polyfill库来实现
          }
        }, false)
      });
    
      // 监听路由
      window.addEventListener('popstate', e => {
        console.log({
          location: location.href,
          state: e.state
        })
      }, false)
    

    popstate监听函数里打印的e.state便是history.pushState()里传入的第一个参数,在这里即为{name: 'history'}

    二.Hash

    1.Hash基本介绍

    url 中可以带有一个 hash http://localhost:9000/#/song.html

    window 对象中有一个事件是 onhashchange,以下几种情况都会触发这个事件:

    • 直接更改浏览器地址,在最后面增加或改变#hash;
    • 通过改变location.href或location.hash的值;
    • 通过触发点击带锚点的链接;
    • 浏览器前进后退可能导致hash的变化,前提是两个网页地址中的hash值不同。

    2.Hash实现spa前端路由代码

    <a href="/song" class="spa">song</a>
    <a href="/abc" class="spa">abc</a>
    <a href="/123" class="spa">123</a>
    <a href="/hash" class="spa">hash</a>
    
      document.querySelectorAll('.spa').forEach(item => {
        item.addEventListener('click', e => {
          e.preventDefault();
          let link = item.textContent;
          location.hash = link;
        }, false)
      });
      // 监听路由
      window.addEventListener('hashchange', e => {
        console.log({
          location: location.href,
          hash: location.hash
        })
      }, false)
    

    hash模式与history模式,这两种模式都是通过浏览器接口实现的,除此之外vue-router还为非浏览器环境准备了一个abstract模式,其原理为用一个数组stack模拟出浏览器历史记录栈的功能。当然,以上只是一些核心逻辑,为保证系统的鲁棒性源码中还有大量的辅助逻辑,也很值得学习。

    三、两种模式比较

    • pushState设置的新URL可以是与当前URL同源的任意URL;而hash只可修改#后面的部分,故只可设置与当前同文档的URL

    • pushState设置的新URL可以与当前URL一模一样,这样也会把记录添加到栈中;而hash设置的新值必须与原来不一样才会触发记录添加到栈中

    • pushState通过stateObject可以添加任意类型的数据到记录中;而hash只可添加短字符串

    • pushState可额外设置title属性供后续使用

    四、history模式的一个问题

    我们知道对于单页应用来讲,理想的使用场景是仅在进入应用时加载index.html,后续在的网络操作通过Ajax完成,不会根据URL重新请求页面,但是难免遇到特殊情况,比如用户直接在地址栏中输入并回车,浏览器重启重新加载应用等。

    hash模式仅改变hash部分的内容,而hash部分是不会包含在HTTP请求中的:

    http://oursite.com/#/user/id // 如重新请求只会发送http://oursite.com/
    故在hash模式下遇到根据URL请求页面的情况不会有问题。

    而history模式则会将URL修改得就和正常请求后端的URL一样
    http://oursite.com/user/id
    在此情况下重新向后端发送请求,如后端没有配置对应/user/id的路由处理,则会返回404错误。

    官方推荐的解决办法是在服务端增加一个覆盖所有情况的候选资源:如果 URL 匹配不到任何静态资源,则应该返回同一个 index.html 页面,这个页面就是你 app 依赖的页面。同时这么做以后,服务器就不再返回 404 错误页面,因为对于所有路径都会返回 index.html 文件。为了避免这种情况,在 Vue 应用里面覆盖所有的路由情况,然后在给出一个 404 页面。或者,如果是用 Node.js 作后台,可以使用服务端的路由来匹配 URL,当没有匹配到路由的时候返回 404,从而实现 fallback。

    参考资料:
    1、浏览器History API :https://developer.mozilla.org/zh-CN/docs/Web/API/History_API
    2、解决History模式访问404的方案:https://router.vuejs.org/zh/guide/essentials/history-mode.html#后端配置例子

    posted in 博客
  • 面试王者号:你真的知道什么是响应式布局?什么是自适应布局吗?

    待更新。。。。。。

    posted in 博客
  • RE: 这是一个菜鸟写BUG的录记帖。

    技术是不值钱的,只有技术创造了产品产生了价值才是值钱的--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老师让我把这个操作一遍,下次再来吹牛逼。

    posted in 博客
  • RE: 这是一个菜鸟写BUG的录记帖。

    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命令所导入的文件会且只会与相关页面进行绑定并提供服务,也就是说,页面不会加载不需要的代码。

    posted in 博客
  • RE: 这是一个菜鸟写BUG的录记帖。

    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封装的方式。

    posted in 博客
  • RE: 这是一个菜鸟写BUG的录记帖。

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

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

    异步渲染:时间分片特性

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

    posted in 博客
  • RE: 这是一个菜鸟写BUG的录记帖。

    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 还没研究,以后再补上

    posted in 博客
  • RE: 这是一个菜鸟写BUG的录记帖。

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

    posted in 博客

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