react学习笔记系列(一)

概念区分:

  • library(库):小而巧的库,只提供了特定的API。
  • Framework(框架):大而全的是框架;框架提供了一整套的解决方案(全家桶);

react和vue做对比:(对比不完全)

  • 组件化方面:
  • 模块化:是从代码角度分析,把一些可复用的代码,抽离为单个的模块;便于项目的维护和开发;
  • 模块化的好处:
  1. 解决命名冲突:在早期,使用立即执行函数实现模块化是常见的手段,通过函数作用域解决了命名冲突、污染全局作用域的问题
  2. 提高复用性
  3. 提高代码可维护性
  • 组件化:是从UI界面角度分析,把一些可复用的UI元素,抽离为单独的组件;便于项目的维护和开发;
  • 组件化的好处:
  1. 随着项目规模的增大,手里的组件越来越多;很方便就能把现有的组件,拼接为一个完整的页面;
  • vue组件化开发:通过 `.vue` 文件,来创建对应的组件;template模板 script行为 style样式
  • react组件化开发:React中有组件化的概念,但是,并没有像vue这样的组件模板文件;React中,一切都是以JS来表现的;因此要学习React,JS要合格;ES6 和 ES7 (async  和 await) 要会用;
  • 开发团队方面
  • React是由FaceBook前端官方团队进行维护和更新的;因此,React的维护开发团队,技术实力比较雄厚;
  • Vue:第一版,主要是有作者 尤雨溪 专门进行维护的,当 Vue更新到 2.x 版本后,也有了一个以 尤雨溪 为主导的开源小团队,进行相关的开发和维护;
  • 移动APP开发体验:
  • React,结合 ReactNative,也提供了无缝迁移到 移动App的开发体验
  • Vue,结合 Weex 这门技术,提供了 迁移到 移动端App开发的体验

REACT核心概念

  • 虚拟DOM(Virtual Document Object Model)
  • DOM本质:浏览器中的概念,用JS对象来表示 页面上的元素,并提供了操作 DOM 对象的API;
  • 虚拟DOM:是框架中的概念,是程序员 用JS对象来模拟 页面上的 DOM 和 DOM嵌套
  • 作用:为了实现页面中, DOM 元素的高效更新
  • 用JS模拟DOM元素:
<div id="first" class="myfirst">
    this is a test!
    <p>test2</p>
</div>
var div={
    tagName:"div",
    attrs:{
        id:"first",
        class:"myfirst"
    },
    //模拟子节点:
    children:{
        "this is a test!",
        {
            tagName:"p",
            attrs:{}
            children:{
                "test2"
            }
        }
    }
}

react的Diff算法:(具体没细看呢)

  • tree diff:新旧两棵DOM树,逐层对比的过程,就是 Tree Diff; 当整颗DOM逐层对比完毕,则所有需要被按需更新的元素,必然能够找到;
  • component diff:在进行Tree Diff的时候,每一层中,组件级别的对比,叫做 Component Diff;(比如一个轮播图组件)
  • element diff:在进行组件对比的时候,如果两个组件类型相同,则需要进行 元素级别的对比,这叫做 Element Diff;(一个轮播图组件肯定是由很多元素组成的,比如div等等的对比)

快速创建一个webpack4.x的项目:

1. 运行`npm init` 快速初始化项目(生成package.json文件
2. 在项目根目录创建`src`源代码目录和`dist`产品目录
3. 在 src 目录下创建 `index.html`,并且引入<script src="../dist/main.js"></script>
4. 使用 cnpm 安装 webpack ,运行`cnpm i webpack webpack-cli -D`
   + 如何安装 `cnpm`: 全局运行 `npm i cnpm -g`
5.创建webpack配置文件:webpack.config.js 
6.创建src下的index.js文件(它是入口文件)
7.按自己所需配置package.json下的scripts属性:运行脚本的npm命令行缩写

8.为了实现MHR所以安装webpack-dev-server,配置项可以写在webpack.config.js里面或者通常写在package.json的scripts里的dev里面

9.scripts的dev里配置其他:--open 自动打开浏览器  --hot模块热替换  --host修改域名 --port修改端口号。
注意:webpack 4.x 提供了 约定大于配置的概念;目的是为了尽量减少 配置文件的体积;
   + 默认约定了:
   + 打包的入口是`src` -> `index.js`
   + 打包的输出文件是`dist` -> `main.js`(当然这两个文件可以额外配置,只不过就是覆盖了默认配置罢了)
   + 4.x 中 webpack.config.js新增了 `mode` 选项(为必选项),可选的值为:`development` 和 `production`;(如果不修改默认,那么配置文件里只写mode就可以跑了)

  +webpack.config.js里面的的一个小技巧:带s的都是数组,不带s的都是对象

  +进行到步骤8之后发现热替换其实没有成功,因为修改index.js里面的内容并没有实现更新,因为webpack-dev-server打包好的main.js是托管到了内存中,所以在项目根目录中看不到;但是可以认为,在项目根目录里有一个看不见的main.js,此时我们修改index.html里面的script的src为src="/main.js",可以发现更新了。

这样也带来一个问题思考。加入main.js没有托管到内存中而是放在物理磁盘里面,那么我们每次修改index.js都要访问磁盘,显然低效且损耗大,所以webpack-dev-server把打包好的main.js托管到了内存里,而index.html却没有托管进去!把首页也托管到内存里则:

10.使用插件html-webpack-plugin,(插件使用:导入-->实例化-->放到暴露出去的配置对象的plugins数组里),该插件能自动加上位于根目录下的script所以不必再用步骤8中手动添加script了! 

在项目中使用react

基本步骤:install react react-dom-->index.js引入包react react-dom-->创建虚拟DOM元素、渲染元素(中间有一步去index.html里面创建div容器)-->

  • react:专门用于创建组件和虚拟DOM的,同时组件的生命周期都在这个包中
  • react-dom:专门进行DOM操作的,最主要的应用场景,就是`ReactDOM.render()`
  • React.createElement();参数列表
  1. 字符串类型的参数,表示要创建的标签的名称
  2. 对象类型的参数, 表示 创建的元素的属性节点,没有属性内容就写null即可
  3. 子节点(包括其他虚拟DOM ,获取,文本子节点)
  4. 参数n:其他子节点
  • ReactDOM.reader()参数列表:
  1. 表示要渲染的虚拟DOM对象
  2. 指定容器,这里不能直接放 容器元素的Id字符串,需要放一个容器的DOM对象(因为webpack(的 htmlwebpackplugin插件)会把将index.js打包好为main.js注入到index.html里面所以直接document.getElementById即可)

---->使用上述方式其实写标记语言非常麻烦,而js文件中不能写这种类似于HTML的标记否则打包失败,可以使用babel来转换这些JS中的标签---->JSX语法:本质还是在运行的时候转换成了React.createElement()形式执行

基本步骤续:

运行`cnpm i babel-core babel-loader babel-plugin-transform-runtime -D`(转换操作)

运行`cnpm i babel-preset-env babel-preset-stage-0 -D`(语法操作)

安装能够识别转换jsx语法的包 `babel-preset-react` 

webpack.config.js配置babel-loader:(千万别忘记添加 exclude 排除项)-->配置.babelrc文件-->配置resolve,extensions让项目中使用某些文件时可以省略文件名(.js .jsx .json),以及alias配置根目录别名:这样每次使用的时候尽量用@引入根目录下的组件,避免路径出现错误

JSX语法:

  • 本质:并不是直接把 jsx 渲染到页面上,而是 内部先转换成了 createElement 形式,再渲染的;
  • JS代码写到 { } 中,xml语法写到<>里面
  • 字符串数组转换成jsx语法采坑
  1. const arr=['beijing','shanghai'];
    const namearr=[];
    arr.forEach((item)=>{
        const temp=`<h2>${item}</h2>`//这样并不可以  因为只是单纯字符串
        namearr.push(temp)
    })
    //应该是:
    <h2>{item}</h2>
    //然后在ReactDOM.render里写{{namearr}}

  2. //在ReactDOM.render里面直接写
    arr.map((item)=>{
      return <h2>{item}</h2> //一定要写return 不然会渲染不出来,
    //或者说只有一个返回值时可以省略return和花括号,arr.map((item)=> <h2>{item}</h2>)
    //坑:切记不要 写了花括号又没写return这种半截语法!!!
    })

    注意:react中,需要把key添加给被forEach或map或for循环直接控制的元素:key={item};

  •  关于jsx注释:推荐使用`{ /* 这是注释 */ }`
  • 为 jsx 中的元素添加class类名:需要使用`className` 来替代 `class`;`htmlFor`替换label的`for`属性(for 属性规定 label 与哪个表单元素绑定。)
  • 在JSX创建DOM的时候,所有的节点,必须有唯一的根元素进行包裹;(vue也是)
  • 在 jsx 语法中,标签必须 成对出现,如果是单标签,则必须自闭和!

 React创建组件的方式

  • 构造函数方式创建组件,使用时用标签即可;用**构造函数**创建出来的组件:叫做“无状态组件”
  1. 首字母一般大写
  2. 必须返回一个合法的jsx虚拟DOM元素或者返回null,则表示此组件是空的,什么都不会渲染
  3. 传参方式:在function People(props)参数接收后直接使用即可:props.xxx
  4. 没有私有数据(this.state)
  • 使用class关键字创建组件:
  1. 自定义构造器中必须使用super继承父类构造器
  2. 必须包含render函数:渲染当前组件对应的虚拟DOM元素。且必须返回可用的虚拟DOM元素
  3. 传参方式:不需要格外接收,直接this.props.xxx使用即可
  4. 拥有私有数据:this.state
  5. class组件内部this表示:当前组件的实例对象
  • 有关于ES6的class创建类(面向对象编程的新方式)时的注意点:
  1. constructor基本使用:(可参考阮一峰老师的ES6入门):但凡创建了一个类在实例化(new)它的时候就会调用constructor,如果不写就自动生成一个。
  2. 实例属性:定义在constructor中的属性,且使用this挂到实例对象上的属性;实例方法:不被static关键字修饰的方法,实例对象可以调用。
  3. 静态属性:使用static关键字修饰的属性,调用时People.count;静态方法:被static关键字修饰的方法,调用时People.show();
  4. class的继承:使用extends关键字实现继承,如果没有写constructor,那么可以直接继承父类的实例属性和实例方法。一旦要自定义构造器,则一定要写super(name,age)来继承父类的构造器。具体来说:ES5中继承是先创建子类的实例对象this,然后把父类方法添加到this上面(Parent.apply(this)),所以出现了两次调用父类构造函数的现象(,或者称父类覆盖子类),而ES6子类是没有实例对象this的必须先用super继承,否则没有this可用。

注意:

在 class 的 { } 区间内,只能写 构造器、静态方法和静态属性、实例方法;

在类内部使用componentDidMount函数时有时需要用到静态方法静态属性,在这个钩子里使用则直接this.constructor.打点掉用或者解构赋值即可,注意:this.constructor就是这个类(所以能用啊)

class其实只是个语法糖,class 关键字内部,还是用 原来的配方实现的;

两种创建组件的方式对比:

  1. 利用构造函数创建的组件:叫做“无状态组件”
  2. 利用class关键字创建的组件:叫做“有状态组件”
  3. state有无,生命周期函数有无
  4. props使用

props和state(vue:data)对比:

  1. props是外界传过来的值,且只读;注:不管是vue还是react,组件中的props永远是只读的不能被重新赋值
  2. state是组件内部的状态,可读可写;

组件中样式的使用:

  1. 行内样式使用:普通写行内样式直接字符串形式就可以了,jsx语法中必须写成style={{color:'red'}}对象形式(vue也是对象形式,但是是单{}),带有连字符的属性写成驼峰样式{{fontSize:'15px'}},因为属性允许含有连字符但是要写成:‘font'+'-'+'size'如果是数值类型的样式可以不用引号包裹,如果是字符串类型的样式值必须用引号包裹;style={{ color: 'red', fontSize: '35px', zIndex: 3}}
  2. 样式抽离:抽离为js文件,里面直接是style对象,使用的地方引用即可。
  3. 样式抽离:抽离为单独的样式表模块:css文件,引入style-loader css-loader并且配置rules;问题:直接导入 css 样式表,默认是在全局上,整个项目都生效;
  4. 思考:Vue 组件中的样式表,有没有 冲突的问题???
    答案: Vue 组件中的样式表,也有冲突的问题;但是,可以使用 <style scoped></style>有了作用域
    疑问:React 中,有没有类似于 scoped 这样的指令呢?
    答案:没有;因为 在 React 中,根本就没有指令的概念;
    可以在 css-loader 之后,通过 ? 追加参数,其中,有个固定的参数,叫做 modules , 表示为 普通的 CSS 样式表,启用模块化。但是:css模块化只针对类名和ID名起作用

    在webpack.config.js中使用`localIdentName`自定义生成的类名格式,可选的参数有:

       - [path]  表示样式表 `相对于项目根目录` 所在路径
       - [name]  表示 样式表文件名称
       - [local]  表示样式的类名定义名称
       - [hash:length]  表示32位的hash值
       - 例子:`{ test: /\.css$/, use: ['style-loader', 'css-loader?modules&localIdentName=[path][name]-[local]-[hash:5]'] }`     LOCAL IDENT  NAME

    在css文件中使用 `:local()` 和 `:global()`:

    - `:local()`包裹的类名,是被模块化的类名,只能通过`className={cssObj.类名}`来使用

         同时,`:local`默认可以不写,这样,默认在样式表中定义的类名,都是被模块化的类名;

    - `:global()`包裹的类名,是全局生效的,不会被 `css-modules` 控制,定义的类名是什么,就是使用定义的类名`className="类名"`

    如果在引用某个包的时候,这个包被安装到了 node_modules 目录中(bootstrap), 则,可以省略 node_modules 这一层目录,直接以包名开始引入自己的 模块 或 样式表import bootcss from 'bootstrap/dist/css/bootstrap.css' 见步骤5,但是设置css的时候已经设置了模块化,在使用的时候显然不方便:多个类名必须用数组join方法变成一串类名 那么我们可以自己规定:

    第三方的 样式表,都是以 .css 结尾, 这样,我们不要为 普通的 .css 启用模块化(此时引入bootstrap:import 'bootstrap/dist/css/bootstrap.css' 即可,用的时候不用对象了直接上字符串单引号);而自己的样式表,都要以 .scss 或 .less 结尾, 只为 .scss 或 .less 文件启用模块化

     

  5. 样式抽离:使用bootstrap ;安装url-loader(webpack学习笔记-2-file-loader 和 url-loader)-->引入import bootcss from 'bootstrap/dist/css/bootstrap.css'
  6. 样式抽离:使用less scss;安装sass-loader 以及它依赖的node-sass-->配置rules启用模块化以及类名规范

React 中绑定事件:

  • 事件的名称都是React的提供的,因此名称的首字母必须大写`onClick`、`onMouseOver`(小驼峰命名)
  • 为事件提供的处理函数,必须是如下格式 :onClick= { function } 直接在里面写匿名函数,只接受function,不接受函数返回值即(函数的调用即myClickHandler())
  • 在类里定义实例方法,然后要在类里使用必须用this:onClick={this.myClickHandler}
  • 用的最多的事件绑定形式为:箭头函数+箭头函数赋值给函数名
   <button onClick={ ('传参') => this.show('传参') }>按钮</button>
//其实这种并不方便,因为有参数的时候需要一致前后有参数
//建议:
<button onClick={  this.show }>按钮</button>//的确需要传参的时候传参

   // 事件的处理函数,需要定义为 一个箭头函数,然后赋值给 函数名称
   show = (arg1) => {
       console.log('show方法' + arg1)
   }
  • 在React中,如果想要修改 state 中的数据,推荐使用 `this.setState({ })`
  • ★在this.setState({})中只会把对应的state状态更新,而不会覆盖其他的state状态
  • ★this.setState({})方法的执行是异步的,如果在this.setState({})修改状态后,又想拿到最新的state状态需要使用this.setState({},callback)在callback回调里面拿即可
  • 绑定文本框与state中的值:react是单向数据流:在 Vue 中,默认提供了`v-model`指令,可以很方便的实现 `数据的双向绑定`;,在 React 中,默认只是`单向数据流`,也就是 只能把 state 上的数据绑定到 页面,无法把 页面中数据的变化,自动同步回 state ; 如果需要把 页面上数据的变化,保存到 state,则需要程序员手动监听`onChange`事件,拿到最新的数据,手动调用`this.setState({  })` 更改回去;(即react实现数据双向绑定)
  • 注:onChange事件中获取文本框的值有两种方案:
  1. 通过事件参数e(e.target.value)
  2. React 中,也有 `ref`, 在要获取的元素内添加ref,获取时用`this.refs.引用名称`(和 Vue 中差不多,vue 为页面上的元素提供了 `ref` 的属性,如果想要获取 元素引用,则需要使用`this.$refs.引用名称`)

生命周期函数:

生命周期的概念:每个组件的实例,从 创建、到运行、直到销毁,在这个过程中,会出发一些列 事件,这些事件就叫做组件的生命周期函数;

  • vue生命周期函数

  • react生命周期函数

  - **组件创建阶段**:特点:一辈子只执行一次

  > componentWillMount: 
  > render:
  > componentDidMount: 

  - **组件运行阶段**:按需,根据 props 属性 或 state 状态的改变,有选择性的 执行 0 到多次

  > componentWillReceiveProps:
  > shouldComponentUpdate:
  > componentWillUpdate: 
  > render: 
  > componentDidUpdate: 

  - **组件销毁阶段**:一辈子只执行一次

  > componentWillUnmount: 

组件生命周期的执行顺序:

1. **Mounting:**
   - constructor()
   - componentWillMount()
   - render()
   - componentDidMount()
2. **Updating:**
   - componentWillReceiveProps(nextProps)
   - shouldComponentUpdate(nextProps, nextState)
   - componentWillUpdate(nextProps, nextState)
   - render()
   - componentDidUpdate(prevProps, prevState)
3. **Unmounting:**
   - componentWillUnmount()

关于shouldComponentUpdate

react包中有pureComponent可以继承,那么它跟component有什么差呢?其实就是在shouldComponentUpdate这个生命周期钩子函数里做了一点优化,进行了一次浅对比避免了 不必要的渲染:
React PureComponent 学习及浅比较详解

// pureComponent中shouldComponentUpdate源码
if (this._compositeType === CompositeTypes.PureClass) {

  shouldUpdate = !shallowEqual(prevProps, nextProps)

  || !shallowEqual(inst.state, nextState);

}
// shallowEqual源码
    // 1. hasOwnProperty 判断对象中是否含有指定属性,区别于in,它忽略从原型链上继承的属性
    // 2. 下面用的Object.is实际源码中用的是polyfill
    const shallowEqual=(objA,objB)=>{
        const hasOwn=Object.prototype.hasOwnProperty;
        if (Object.is(objA,objB)) return true;
        if (typeof (objA)!=='object'||objA===null||typeof (objB)!=='object'||objB===null) return false;
        const keysA=Object.keys(objA);
        const keysB=Object.keys(objB);
        if (keysA.length!==keysB.length) return false;
        for (let i=0;i<keysA.length;i++){
            if (!hasOwn.call(objB,keysA[i])||Object.is(objA[keysA[i]],objB[keysA[i]])) return false;
        }
        return true;
    }

shallowEqual 会比较 Object.keys(state | props) 的长度是否一致,每一个 key 是否两者都有,并且是否是 一个引用,也就是只比较了 第一层 的值,确实很浅,所以深层的嵌套数据是对比不出来的。

React-router使用:

References:

新手理解react-router关键点解析:了解match参数

react router路由传参

开始一个React项目(三)路由基础(v4)

简单使用:

import { BrowserRouter as Router, Route, Link } from 'react-router-dom'
  • react-router和react-router-dom区别:react-router和react-router-dom的区别,推荐使用react-router-dom,react-router-dom引入了一些<Link> <BrowserRouter>这样的dom类组件,更加方便使用
  • 两种模式:BrowserRouter即history模式(官网推荐),HashRouter即hash模式,两者可以在引入的时候直接指定
  • <Router></Router>相当于一个盒子,里面只能包含一个子元素。

  • <Link></Link>标签和<a></a>标签类似,点击<Link></Link>标签中的元素可以实现组件跳转。跳转到哪个组件要看path的值"/"代表根路径,to:path

  • <Link>和<NavLink>区别:如果仅仅需要匹配路由,使用Link就可以了,而NavLink的不同在于可以给当前选中的路由添加样式, 比如activeStyleactiveClassName

  • switch使用,如果有重复路由,只匹配第一个路由

  • 路由配置的具体体现:<Route> 配置Route path, component ,exact,strict

  • Redirect使用,放在switch的最后,用来处理所有都不匹配的情况下的路由处理.React-Router实战:重定向Redirect

  • 路由传参的三种方式以及对比:react router路由传参

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值