React学习笔记(一)

本文是React学习笔记的第一部分,介绍了React的基本概念、与其他前端框架的对比,重点探讨了虚拟DOM和Diff算法的重要性,以及创建基本的webpack4.x项目和使用JSX语法的细节。

React.js-Part1

总述

  • 介绍、React核心概念、webpack4.x、JSX语法、创建组件(2种方式)
  • 理解React中虚拟DOM的概念
  • 理解React中三种Diff算法的概念
  • 使用JS中createElement的方式创建虚拟DOM
  • 使用ReactDOM.render方法
  • 使用JSX语法并理解其本质

1.介绍

  • React 起源于 Facebook 的内部项目,因为该公司对市场上所有 JavaScript MVC 框架,都不满意,就决定自己写一套,用来架设 Instagram(照片交友) 的网站。做出来以后,发现这套东西很好用,就在2013年5月开源了
  • Angular1 2009 年 谷歌 MVC 不支持 组件化开发
  • 由于 React 的设计思想极其独特,属于革命性创新,性能出众,代码逻辑却非常简单。所以,越来越多的人开始关注和使用,认为它可能是将来 Web 开发的主流工具。
  • 搞清楚两个概念:
    • library(库):小而巧的库,Jquery,优点:船小好调头,可以很方便的从一个库切换到另外的库,但是代码几乎不会改变。
    • Framework(框架):大而全的是框架,提供了一整套的解决方案,所以项目中间想切换到另外的框架,比较困难。

2.前端三大主流框架

三大框架一大抄

  • Angular.js:出来最早的前端框架,学习曲线比较陡,NG1学习起来比较麻烦,NG2-NG5开始进行了了改革,也提供了组件化开发的概念,也支持TS(TypeScript)进行编辑。
  • Vue.js:最火(关注的人比较多),中国人开发的。
  • React.js:最流行(用的人比较多),设计很优秀。

3.React与Vue对比

组件化方面

  1. 什么是模块化:是从代码角度进行分析的;把一些可以复用的代码,抽离为单个的模块;便于项目的维护和开发。
  2. 什么是组件化:是从 Ul界面的角度进行分析的;把一些可复用的Ul元素,抽离为单独的组件(放到单独的文件中);便于项目的维护和开发。
  3. **组件化的好处:**随着项目规模的增大,手里的组件越来越多;很方便就能把现有的组件拼接为一个完整的页面。
  4. Vue是如何实现组件化:通过.vue文件,来创建对应的组件;
    • template 结构
    • script 行为
    • style 样式
  5. React如何使用组件化:React中有组件化的概念,但没有像Vue这样的模板文件;React中一切都是以js来实现的;所以JS要合格;ES6,ES7(async和await)要会用。

开发团队方面

  • React有FaceBook前端官方团队进行维护和更新,技术实力比较雄厚。
  • Vue 第一版,由尤雨溪专门维护,2.x版本 由尤雨溪 为主的开源团队进行开发和维护。

社区方面

  • React诞生较早,所以社区比较强大,一些常见问题、坑、最优解决方案,文档、博客在社区中都可以方便的找到。
  • Vue是近两年才火起来,社区相对较小。

移动APP开发体验方面

  • React,结合ReactNative,也无缝迁移到移动APP的开发体验(RN用的最多,也是最火最流行的,ins,facebook)。
  • Vue,结合Weex,提供了迁移到移动端APP开发的体验(Weex,阿里开发的,目前只是一个小玩具,并没有很成功的大案例)。

4.为什么学习React?

  1. 和Angualar1相比,React设计很优秀,一切基于JS并且实现了组件化开发的思想。
  2. 开发团队实力强悍,不必担心断更的情况。
  3. 社区和强大,很多问题都能找到对应的解决发那个。
  4. 提供了无缝转到ReactNative上的开发体验,让我们的技术能力得到了拓展;增强了核心竞争力。、
  5. 很多企业中,前端项目的技术选型采用的是React.js。

5.React中几个核心的概念(50%)

5.1虚拟DOM(Virtual Document Object Model)

  • DOM的本质是什么:浏览器中的概念,浏览器提供的API,用JS对象来表示页面上的元素,并提供了操作DOM对象的API(拿到div,加上颜色);
  • **什么是React中的虚拟DOM:**是框架中的概念,是程序员(封装框架的程序员)用JS对象来模拟页面上的DOM和DOM嵌套;
  • 为什么要实现虚拟DOM:为了实现页面中,DOM元素的高效更新。
  • DOM和虚拟DOM的区别:
    • **DOM:**浏览器中提供的概念,用JS对象,表示页面上的元素,并提供了操作元素的API。
    • **虚拟DOM:**是框架中的概念;开发框架的程序员,手动用JS对象来模拟DOM元素和嵌套关系。
      • 本质:用JS对象,来模拟DOM元素和嵌套关系。(面试)
      • 目的:就是为了实现页面元素的高效更新。(面试)
需求:点击列表表头,实现对应表格数据的排序

需求列表描述

  1. 表格中数据从哪儿来:从数据库查询来的。

  2. 查询到的数据,存放到哪儿了:数据在浏览器的内存中存放,而且是以对象数组的形式表示。

  3. 这些数据如何渲染到页面上?

    • 方案1:手动for循环整个数组,then 手动拼接str += ‘<tr></tr>’
    • 方案2:使用模板引擎,art.template(???)
  4. 思考上述方法的性能上的问题?【↓】

  5. 如果用户点击了【时间】,想按照时间从大到小排序:

    • 触发点击事件,在事件中,把内存中 对象数组,重新排序;
    • 当排完序后,页面是旧的,但内存中的 对象数组 是新的;
    • 想办法把最新的数组,重新渲染到页面上;(这一步有没有性能上的问题?)
  6. 分析和总结:上述方案,只是实现了把数据渲染的页面上的能力,但没把性能做到最优;

  7. 如何才能把性能做到最优:按需渲染页面(只重新渲染更新的数据对应的页面元素)。

  8. 如何实现按需渲染 更新?

    获取内存中,新旧两个DOM树,进行对比,得到需要被按需更新的DOM元素。

DOM树概念

一个网页的呈现过程:

  • 浏览器请求服务器获取页面HTML代码。
  • 浏览器要在内存中,解析DOM结构,并在浏览器内存中,渲染出一颗DOM树。
  • 浏览器把DOM树呈现到页面上。
  1. 如何获取新旧DOM树,从而实现DOM树的对比?

    分析:浏览器中,并没有直接提供获取DOM树的API;所以,我们无法拿到浏览器内存中的DOM树。

  2. 程序员可以手动模拟新旧两棵DOM树。

  3. 程序员如何手动模拟DOM树?如何模拟一个DOM元素。JS对象模拟DOM树。

<div id="mydiv" title="dd">
    content
    <p>ddd</p>
</div>
var div = {
	tagName:'div',
	attrs:{
		id:'mydiv',
		title:'dd',
		'data-index': '0'
	},
	children:[ //元素嵌套
        'content',
        {
            tagName:'p',
            attrs:{},
            children:[
                'ddd'
            ]
        }
    ]
}

12.程序员手动模拟的的新旧DOM树就是React中的虚拟DOM的概念。(用JS对象模拟新旧DOM树)

React中虚拟DOM总结:
  • 什么是虚拟DOM:用JS对象的形式,来模拟页面上DOM嵌套关系(本质)。(虚拟DOM是以JS对象的形式存在。)
  • 目的:实现页面元素的高效更新。

5.2Diff算法(different)

(新旧虚拟对象对比高效,才能真正实现高效)

  • **tree diff:**新旧两棵虚拟DOM树,逐层对比的过程,就是Tree Diff,(每一层节点进行对比);当整棵DOM逐层对比完毕,则所有需要被按需更新的元素,必然能够找到。

  • **component diff:**在进行Tree Diff时,每一层中,组件级别的对比,叫做Component Diff;

    • 如果对比前后,组件的类型相同,则暂时认为组件不需要被更新;
    • 如果对比前后,组件类型不同,则需要移除旧组件,创建新组件,并追加到页面上。
  • **element diff:**在进行组件对比时,如果两个组件类型相同,则需要进行元素级别的对比,这叫做Element Diff。

    从上到下,递进对比。

在这里插入图片描述
Diff算法,提供新旧DOM树对比的最优方案。

6.创建基本的webpack4.x项目

创建步骤等

  • E:\01.webpack-basevscode打开

  • 运行npm init -y 快速初始化

  • 在项目根目录创建src源代码目录和dist产品目录(发布的项目/产品放在这里)

  • 在src目录下创建index.html,main.jswebpack打包的入口文件

  • 使用npm安装webpack,运行npm i webpack webpack-cli -D,(cnpm i webpack -D)

    • 全局运行npm i cnpm -g(安装cnpm)
    • cli脚手架

    Error: Cannot find module ‘webpack-cli’,发现 webpack-cli需要全局安装 即可解决上述问题

    npm i webpack-cli -g

  • 注意:webpack 4.x 提供了约定大于配置的概念:目的是为了尽量减少配置文件的体积。

    • 默认约定:打包入口时src->index.js;打包的输出文件是dist->main.js
    • 4.x中新增了mode选项(必选项),可选值为development ,production

webpack.config.js【↓】

// 向外暴露一个打包的配置对象,因为webpack是基于Node构建的,所以webpack支持所有Node API和语法
module.exports = {
    mode : 'development' // 模式 development 开发环境 production 生产环境(压缩),4.x 新增
    // 在webpack 4.x中,有一个很大的特性,就是约定大于配置,默认的打包入口路径是src->index.js不用写entry,不再用main当入口
}
// export default {} //node中不支持
// 这是ES6中向外导出的API,与之对应的是import ** from '标识符'
1569930984976

index.html,手动引入<script src="../dist/main.js"></script>

Node 与 Chrome

Node.js是基于Chrome V8 引擎的JavaScript运行环境。

哪些ES6特性 Node 支持呢?

  • 如果Chrome浏览器支持哪些,则Node就支持哪些。export default {} //node中不支持

Babel

Babel支持所有ES6高级特性,是个转换器。

webpack-dev-server

实时打包刷新

package.json

"scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "dev": "webpack-dev-server" //这里
  },

安装npm i webpack-dev-server -g(有版本兼容问题,但是还可以用)

1569936588563

实时打包,等待中。Ctrl+C终止终止批处理操作吗(Y/N)?,输入Y

「wds」: Project is running at http://localhost:8080/   # 运行在本地8080端口
「wds」: webpack output is served from /  # webpack 的输出文件main.js(实时的)运行在根目录(/

1569935971287

没有main.js物理文件,但是可以访问,因为webpack-dev-server实时打包生成的main.js文件在内存(放在内存快)中,所以在项目根目录中看不见。

1569936008370

修改index.html<script><script src="/main.js"></script>

打包完成后自动打开浏览器

package.json

"scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "dev": "webpack-dev-server --open firefox --port 3000 --hot --host 127.0.0.1"
  }

--open自动打开浏览器(默认打开谷歌浏览器),--progress进度,--compress压缩,--host主机

--open firefox,--open iexplore

html-webpack-plugin(安装失败)

webpack-dev-server实时打包生成的main.js文件在内存(放在内存快)中。

将首页也放入内存。

npm i html-webpack-plugin -g

配置文件:webpack.config.js

const path = require('path');
// 导入 在内存中自动生成 index 的插件
const HtmlWebPackPlugin = require('html-webpack-plugin');
// 创建一个插件的实例对象
const htmlPlugin = new HtmlWebPackPlugin({
    template: path.join(__dirname, './src/index.html'), // 源文件,__dirname(双下划线)代表当前文件所属的目录,这里就是根目录
    filename: 'index.html' // 生成的内存中首页的名称
});

//向外暴露一个打包的配置对象,因为webpack是基于Node构建的,所以webpack支持所有Node API和语法
module.exports = {
    mode : 'development', // 模式 development 开发环境 production 生产环境(压缩)
    // 在webpack 4.x中,有一个很大的特性,就是约定大于配置,默认的打包入口路径是src->index.js不用写entry,不再用main当入口
    plugins: [
        htmlPlugin // 在外面new对象
    ]
}

安装插件问题

compilation.mainTemplate.applyPluginsWaterfall is not a function

[webpack4 TypeError: compilation.mainTemplate.applyPluginsWaterfall is not a function ](https://www.skiy.net/201803014976.html)这个是因为官方 html-webpack-plugin 没有及时更新支持 webpack4 导致的问题:

解决方案:npm install webpack-contrib/html-webpack-plugin --save-dev(连接失败)

vscode:Ctrl+B关闭侧边栏

先运行cnpm install安装所有依赖项。

7.在项目中使用React(不使用脚手架)

拆分终端

1569985147545
  1. 运行cnpm i react react-dom -S安装

    • react:专门用于创建组件(React是组件化的)和虚拟DOM(用JS对象表示的DOM),同时包括组件的生命周期。
    • react-dom:专门进行DOM操作的,主要应用场景,就是ReactDOM.render()
  2. index.html页面中,创建容器:

    <!-- 创建一个容器,将来渲染的虚拟DOM会放到这个容器内 -->
    <div id="app"></div>
    

    Target container is not a DOM element.出现此错误的原因是我将webpack生成的js文件放在了head,此时DOM还没有建立完毕,因此出现 not a DOM element 的错误,所以将js文件放在HTML底部就可以了。(html-webpack-plugin实时创建首页,会自动在底部引入main.js,插件安装失败,自己的没有。)

  3. 导入包:

    //1. 这两个导入的时候,接收的成员名称必须这么写
    import React from 'react'; // 创建组件,虚拟DOM元素,生命周期
    import ReactDOM from 'react-dom'; // 把创建好的组件和虚拟DOM放到页面上展示
    
  4. 创建虚拟DOM元素:

    //2. 创建虚拟DOM元素
    /**
     * param1: 创建的元素的类型,字符串,表示元素名称
     * param2:是一个对象或null,表示当前这个DOM元素的属性
     * param3:子节点(文本子节点或其他虚拟DOM)
     * paramn:其他子节点
     * <h1 id="myh1" title="this is a h1">这是一个大大的H1</h1>
    */
    // const myh1 = React.createElement('h1', null, '这是一个大大的H1');// 调用方法有返回值。这是一个 JS 对象。
    const myh1 = React.createElement('h1', {id:'myh1',title:'this is a h1'}, '这是一个大大的H1');// 调用方法有返回值。
    
  5. 渲染:

    // 3. 使用ReactDOM 把虚拟DOM渲染到页面上
    /**
     * param1:要渲染的那个虚拟DOM元素
     * param2:指定页面上一个容器,是一个DOM元素,而不是选择器
    */
    ReactDOM.render(myh1, document.getElementById('app')); 
    

8.JSX语法

如何启用JSX语法?(安装babel插件)

  • 安装babel插件

    • 运行npm i babel-core babel-loader@7 babel-plugin-transform-runtime -D

    • 运行npm i babel-preset-env babel-preset-stage-0 -Dpreset->语法)

      × Install fail! Error: EPERM: operation not permitted, symlink 'E:\01.webpack-base\node_mod
      ules\_babel-plugin-syntax-trailing-function-commas@6.22.0@babel-plugin-syntax-trailing-function-commas' -> 'E:\01.webpack-base\node_modules\_babel-preset-env@1.7.0@babel-preset-env\node_modules\babel-plugin-syntax-trailing-function-commas'
      

      我的是因为,node_modules目录下有同名文件夹,所以报错。

      删除之后安装成功。

    Cannot find module '@babel/core'
    babel-loader@8 requires Babel 7.x (the package '@babel/core'). If you'd like to use Babel 6.x ('babel-core'), you should install 'babel-loader@7'.
    
    • 最简单的办法是:用loader7代替8。npm i babel-loader@7(成功)
    • babel-loader最新版从类似于babel-core这样的改成@babel/core

    @babel/core、@babel/present-env、@babel/runtime、@babel/plugin-transform-runtime、@babel/plugin-proposal-class-properties、@babel/preset-react。然后在.babelrc文件中添加:(未尝试)

    {
       "presets": ["@babel/present-env", "@babel/preset-react`"],
       "plugins": ["@babel/transform-runtime", "@babel/plugin-proposal-class-properties"]
    }
    
  1. 安装能够识别转换 JSX 语法的包babel-preset-react(这个很重要)
    • 运行npm i babel-preset-react -D
  • 装了第三方loader,需要在webpack.config.js配置文件中配置module规则,添加babel-loader配置项(与modeplugins平级,别忘记加exclude。)

    webpack 默认只能打包处理 .js 后缀名类型的文件;像.png.vue.css无法主动处理,所有要配置第三方的loader

    module: { // 所有第三方模块的匹配规则,webpack默认只能打包出js文件,
            rules: [
                { test: /\.js|jsx$/, use: 'babel-loader', exclude:/node_modules/} 
                //正则,use:多个用数组,exclude:排除项(千万别忘记添加)
            ] //数组,以 s 结尾的,都是对象
    }
    
  • 添加babel的配置文件.babelrc,一般放到项目的根目录。.babelirc文件(符合json格式,双引号,不能写注释)安装的presets语法文件,plugin插件文件。

    {
        "presets": ["env", "stage-0", "react"],
        "plugins": ["transform-runtime"]
    }
    

控制终端cls->清空

JSX语法的本质:

这种在JS中混合写入类似于 HTML 的语法,叫做JSX语法,符合XML规范(比HTML严格,单边标签要自闭合)的JS。

并不是直接把 JSX 直接渲染到页面上,而是内部先转换成了 createElement 的形式,再渲染的。

// 渲染 页面上的DOM结构,最好的方式,就是写HTML代码
// 注意:在js文件中,默认不能写这种类似与HTML的标记,否则打包失败
// 可以使用babel来转换这些JS标签,转换成React.createElement的形式
// 大家注意:这种在JS中混合写入类似于 HTML 的语法,叫做JSX语法,符合XML规范的JS。
// JSX 语法的本质,还是在运行的时候,被转换成 React.createElement 的形式来执行的
const mytext = <div id="mydiv" title="div aaa">这是一个div元素</div>;
let a = 10;
ReactDOM.render(<div>{a + 2}</div>, document.getElementById('app')); 

在JSX 中混合写入 JS 表达式

在 JSX 语法中,要把 JS 代码写到{ }中 :(相当于EL表达式/插值表达式)

渲染什么情况下需要使用 { }

​ 当我们需要在 JSX 控制的区域内,写 JS 表达式了,则需要把 JS 代码写到 { }中。

  • 渲染数组变量、字符串变量

  • 渲染布尔值

  • 为属性绑定值

    let title = "dd"
    ReactDOM.render(<div title={title}>{a + 2}<hr/>{boo.toString()}</div>, document.getElementById('app')); 
    
    
  • 渲染 JSX 元素,不要想成一个标签,是一个JS对象,也需要用 {} 插入 JSX 中,不需要双引号"",加上双引号就变成字符串了。

  • 渲染 JSX 元素数组,(key属性问题)

    const h1 = <h1>哈哈哈</h1>;//不要想成一个标签,是一个JS对象,也需要用 {} 插入JSX中
    const arr = [
        <h2>this is h2</h2>,
        <h3>this is h3</h3>
    ]
    ReactDOM.render(<div title={title}>
        {h1}
        {arr}
    </div>, document.getElementById('app')); 
    
    
  • 将普通字符串数组,转换为 JSX 数组并渲染到页面上。

    数组的forEach没有返回值,map方法返回新数组。

    const arrStr = ["a", "b", "c"];
    ReactDOM.render(<div title={title}>
        {arrStr.map((item)=>{
            return <h3>{item}</h3>
        })};
    // 或者
    ReactDOM.render(<div title={title}>
        {arrStr.map((item)=> <h3>{item}</h3>)};
    
    

    箭头函数右边如果只有一行,可以不用花括号{},也不需要写return

    JavaScript 语句后面应该加分号吗?下一行首字符是[、(、+、-、/五个符号之一的,前一行必须加分号;[1, 2, 3].forEach(...);

 Warning: Each child in a list should have a unique "key" prop.

  • react 中 key 的作用:

    如果需要保存数据的状态,需要提供唯一标识。(key 的主要效果是diff算法可以有效辨识变化的虚拟DOM)。key 要加给直接被 map,forEach直接控制的元素(最外层的元素)。

    {arrStr.map((item, index)=>{
        return <div key={item+index} ><h3>{item}</h3></div>// React中需要把 key 添加给被forEach 或 map 或 for 循环直接控制的元素(最外层div)。
        })}
    
    

JSX中书写注释

{/* 单行注释 */}
{
    // 多行注释,不推荐
}

JSX注意点

  • **特殊属性名:**为 JSX 中的元素添加 class类名,需要使用className来替代classhtmlFor替换 labelfor属性。(因为classfor是 JS 里面的关键字)。

    <p className="myelem">1111111</p>
    <label htmlFor="ooo">111</label>
    
    
  • 在 JSX 创建DOM的时候,所有的节点,必须有唯一的根元素进行包裹。

  • 在 JSX 语法中,标签必须成对出现,如果是单标签,则必须自闭合

相关文章

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值