React的基本学习(一)——概念了解、babel配置、简单使用

前言:

今天开始了解一个前端语言框架React

 

 

 

 

 

 


代码之前:

什么是React:

React 是一个用于构建用户界面的 JAVASCRIPT 库。

React 主要用于构建UI,很多人认为 React 是 MVC 中的 V(视图)。

React 起源于 Facebook 的内部项目,用来架设 Instagram 的网站,并于 2013 年 5 月开源。

React 拥有较高的性能,代码逻辑非常简单,越来越多的人已开始关注和使用它。

 

  • Framework(框架):大而全,提供了一整套的解决方案。
  • library(库):小而巧,可以很方便的切换库,只提供特定的API

核心概念:

虚拟DOM:

何为DOM?

DOM(Document Object Model),DOM全拼为Document Object Model(文档对象模型)是一种用于HTML和XML文档的编程接口,它给文档提供了一种结构化的表示方法,可以改变文档的内容和呈现方式,是一种浏览器中的概念。

换句话说,DOM是针对HTML和XML的API。 可以理解为DOM就是一系列功能集合。

HTML DOM 将 HTML 文档视作树结构。这种结构被称为节点树

通过 HTML DOM,树中的所有节点均可通过 JavaScript 进行访问。所有 HTML 元素(节点)均可被修改,也可以创建或删除节点。

HTML DOM将html元素定义为对象,API以对象方法和对象属性的形式实现。
可直接调用DOM实现的方法,进行DOM操作,例如:

getElementById()
返回带有指定 ID 的元素。
getElementsByTagName()
返回包含带有指定标签名称的所有元素的节点列表(集合/节点数组)。
getElementsByClassName()
返回包含带有指定类名的所有元素的节点列表。
appendChild()
把新的子节点添加到指定节点。
removeChild()
删除子节点。
replaceChild()
替换子节点。
insertBefore()
在指定的子节点前面插入新的子节点。
createAttribute()
创建属性节点。
createElement()
创建元素节点。
createTextNode()
创建文本节点。
getAttribute()
返回指定的属性值。
setAttribute()
把指定属性设置或修改为指定的值。

参考什么是DOM(个人理解)W3school

 

何为虚拟DOM?

虚拟DOM是框架中的概念。

DOM中的API是由浏览器来提供的,而虚拟DOM中的API是由框架来提供的,用来模拟页面上DOM元素和DOM元素的嵌套。

为什么要用虚拟DOM:为了实现页面上DOM元素的高效更新。

一个网页呈现的过程:

  1. 浏览器请求服务器获取页面HTML代码
  2. 浏览器在缓存中解析DOM结构,并在浏览器缓存中,渲染出一颗DOM数
  3. 浏览器把DOM数呈现在页面上
   用我们传统的开发模式,原生JS或JQ操作DOM时,浏览器会从构建DOM树开始从头到尾执行一遍流程。在一次操作中,我需要更新10个DOM节点,浏览器收到第一个DOM请求后并不知道还有9次更新操作,因此会马上执行流程,最终执行10次。例如,第一次计算完,紧接着下一个DOM更新请求,这个节点的坐标值就变了,前一次计算为无用功。计算DOM节点坐标值等都是白白浪费的性能。即使计算机硬件一直在迭代更新,操作DOM的代价仍旧是昂贵的,频繁操作还是会出现页面卡顿,影响用户体验。
为了解决性能问题,我们可以手动创造一颗DOM树,并与旧DOM树做对比,最后只变化一部分。

手动创建DOM树的思路示例:

DOM结构

<ul id="list">
    <li class="item">item1</li>
    <li class="item">item2</li>
</ul>

用js表示

        {
            tag:'ul',
            attrs:{
                id:'list'
            },
            children:{
                {
                    tag:'li',
                    attrs:{className:'item'}, //class是js保留字,所以只能叫className
                    children:['item1']
                },
                {
                    tag:'li',
                    attrs:{className:'item'},
                    children:['item2']
                }
            }
        }

我们用JS对象的形式,来模拟页面上DOM嵌套关系(虚拟DOM是以JS对象的形式存在的),这就是虚拟DOM

  • 本质:用JS对象来模拟DOM元素和嵌套关系
  • 目的:实现页面元素的高效更新

diff算法:

我们会有两个虚拟DOM(js对象,new/old进行比较diff),用户交互我们操作数据变化new虚拟DOM,old虚拟DOM会映射成实际DOM(js对象生成的DOM文档)通过DOM fragment操作给浏览器渲染。当修改new虚拟DOM,会把newDOM和oldDOM通过diff算法比较,得出diff结果数据表(用4种变换情况表示)。再把diff结果表通过DOM fragment更新到浏览器DOM中。

 

 

创建基本的webpack项目

关于webpack,我以前聊过,可以先复习一下。

  • 运行 npm init -y 快速初始化项目
  •  新建目录src用来存放所有的项目源代码,创建目录dist(build) 项目发布目录
  • 在src下创建一个页面,index.html,并且使用 !+Tab 快捷生成标准 html,再新建一个index.js一会作为webpack的入口文件。
//index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <h1>这是首页!</h1>
</body>
</html> 


//index.js
console.log('OK!')

然后,我们使用 npm i webpack -D 或者 cnpm i webpack -D(需要配置好淘宝源) 来安装webpack,并且使用同样的 npm i webpack-cli -D来安装webpack-cli,现在的webpack都是4以上的版本,需要这个cli来帮助打包(在4.x版本后,webpack的命令行进行打包由webpack-cli负责)

安装好之后我们创建一个 webpack.config.js 来配置webpack

//下面有node的语法,因为webpack是基于node构建的,支持node API和语法
//向外暴露一个打包的配置对象
module.exports={
    mode:'development'  
}

设置好模式为development

  • 模式问题:通过选择 developmentproduction 或 none 之中的一个,来设置 mode 参数,你可以启用 webpack 内置在相应环境下的优化。其默认值为 production,最直观的区别就是production下的dist/main.js的代码会被压缩,development不会。
  • node:Node.js是一个基于Chrome V8引擎的JavaScript运行环境,Node.js使用了一个事件驱动、非阻塞式I/O模型,使其轻量又高效。Chrome之所以能够运行js代码,是因为有V8引擎,而node,就好比是V8引擎的化身。

现在就可以打包了,很多人可能奇怪,为什么没有在配置文件中定好entry入口,这是因为在webpack 4.x中,有一个很大的特性,就是 约定大于配置,webpack默认打包入口路径是 src/index.js。如果想要修改也很简单,手动在配置文件中指定好entry即可。

接下来在命令行启动webpack就可以进行打包,但是很多人会遇到不能识别webpack的问题,这是因为webpack没有全局安装导致的,可以全局安装一下来解决,我使用另一个方法:

接下来在package.json中的script下加入如下一行

    "dev":"webpack",

然后启动webpack时,就使用命令:

npm run dev

使用命令之后成功执行,在我们的dist下就生成了打包文件

接下来我们在index.html文件中加入引用打包后的js文件

    <script src="../dist/main.js"></script>

最后整个index.html如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="../dist/main.js"></script>
</head>
<body>
    <h1>这是首页!</h1>
</body>
</html>

在浏览器打开

成功。

webpack-dev-server

类似于nodejs的nodemon,webpack也有一个可以实时监测文件改变实时更新打包文件的工具——webpack-dev-server

使用命令安装:

npm i webpack-dev-server -D

安装完成之后打开 package.json,写入新内容

{
  "name": "test",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "dev": "webpack-dev-server",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": { 
    "webpack": "^4.42.0",
    "webpack-cli": "^3.3.11",
    "webpack-dev-server": "^3.10.3"
  }
}

执行的时候使用 npm run dev 即可启动

启动成功后可以看到输出:

i 「wds」: Project is running at http://localhost:8080/
i 「wds」: webpack output is served from /
i 「wds」: Content not from webpack is served from F:\reacttt\test
i 「wdm」: Hash: a9f2f24cf9f4090022ff

可以理解为webpack被托管在这个服务器上,开放于本机8080端口,我们访问本机8080端口,会发现我们访问了根目录

注意,我们的webpack-dev-server 打包好的 出口文件,就比如此例中的main.js,不是dist中的main.js,是一个存在于内存中的,看不到的main.js,它存在于根目录中。

所以,这个存在于内存中的隐身的 main.js,才是可以实时更新的出口文件,而/dist/main.js不是,所以我们要让 index.html 指向这个根目录的 main.js。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="/main.js"></script>
</head>
<body>
    <h1>这是首页!</h1>
</body>
</html> 

然后启动,然后更改index.js文件内容,页面及打包文件也可以同步更新了。

另外补充一点,在package.json中的dev值里面可以加一些参数,例如:

    "dev": "webpack-dev-server --open --port 3000 ...",

常见参数有:

  • –open:自动打开浏览器(后面可以加浏览器名称来指定哪一个,否则就是默认)
  • –port:指定访问端口

配置html-webpack-plugin

webpack-dev-server是将我们的打包出口文件放到内存中了,那么我们可不可以将首页文件——index.html也放到内存中呢?可以的,这里需要借助一个插件——html-webpack-plugin

npm i html-webpack-plugin -D
cnpm i html-webpack-plugin -D

安装好之后,package.json中会有记录

接下来在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"),
    //生成的内存中首页的名称
    filename:'index.html'
})



//下面有node的语法,因为webpack是基于node构建的,支持node API和语法
//向外暴露一个打包的配置对象
module.exports={
    mode:'development',  
    //在webpack 4.x中,有一个很大的特性,就是 约定大于配置
    //默认打包入口路径是 src/index.js
    plugins:[
        htmlPlugin
    ]
}

这样一来,就根据/src/index.html在内存中生成了一个index.html,我们直接启动,打开对应IP端口,就直接访问到了主页,这就是那个内存中的文件。

查看他的源代码,我们发现

它自动生成了一句话:

<script type="text/javascript" src="main.js"></script></body>

没错,这个内存中的index.html自动找到了webpack-dev-server生成的内存中的main.js,这样一来就不需要我们的那句手动添加的<script>了。

 

 


Hello,React!

使用React渲染最基本的虚拟DOM到页面上

运行 npm i react react-dom -S 安装包

  • –save (简写为: -S),安装模块后,模块的名称将加入到dependencies(生产阶段的依赖)如:执行以下命令后npm install gulp –save 或 npm install gulp –S在package.json文件中dependencies 属性里就会有如下体现:

    “dependencies”: {

    “gulp”: “^3.9.1”

    }

  • –save-dev (简称为:-D), 安装模块后,模块名称将加入到devDependencies(开发阶段的依赖),所以开发阶段一般使用它如:执行如下命令npm install gulp –save-dev 或 npm install gulp –D

    package.json 文件的 devDependencies属性:

    “devDependencies”: {

    “gulp”: “^3.9.1”

    }

  • react:专门用于创建组件和虚拟DOM的,同时组件的生命周期都在这个包中
  • 专门进行DOM操作的,最主要的应用场景,就是 ReactDOM.render()

接下来我们来使用React

代码奉上:

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

//创建虚拟DOM元素
//参数1:创建元素类型,字符串,表示元素的名称
//参数2:是一个对象或null,表示 当前这个DOM元素的属性
//参数3:子节点(包括其他虚拟DOM或文本子节点)
//参数n:其他子节点
//<h1>这是一个h1</h1>
const myh1=React.createElement('h1',null,'这是一个h1')

//使用ReactDOM将虚拟dom渲染(render)到页面上
//参数1:要渲染的那个虚拟DOM元素
//参数2:DOM元素
ReactDOM.render(myh1,document.getElementById('app'))


#index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <!--创建一个容器,渲染的虚拟DOM会放到容器内显示-->
    <div id="app"></div>
</body>
</html> 

可以看到审查元素中就有我们在index.js中定义的h1

如果我要给h1标签加一些属性呢?

修改createElement即可

//<h1 id="myh1" title="This is a h1">这是一个h1</h1>
const myh1=React.createElement('h1',{id:'myh1',title:'This is a h1'},'这是一个h1')

总结一下上面的步骤:

  1. 导入react和reactDOM包
  2. 创建虚拟DOM并返回给一个对象(在react中,一切都是由js对象来表现的)
  3. 将虚拟DOM渲染到指定页面上的DOM元素,当做对象

虚拟DOM嵌套:

很简单,代码奉上:

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

//创建虚拟DOM元素
//参数1:创建元素类型,字符串,表示元素的名称
//参数2:是一个对象或null,表示 当前这个DOM元素的属性
//参数3:子节点(包括其他虚拟DOM或文本子节点)
//参数n:其他子节点
//<h1 id="myh1" title="This is a h1">这是一个h1</h1>
const myh1=React.createElement('h1',{id:'myh1',title:'This is a h1'},'这是一个h1')

const mydiv=React.createElement('div',null,'这是一个div元素',myh1)

//使用ReactDOM将虚拟dom渲染(render)到页面上
//参数1:要渲染的那个虚拟DOM元素
//参数2:DOM元素 
ReactDOM.render(mydiv,document.getElementById('app'))

index.html的代码不变

 

在React项目中启用JSX语法

上面的React.createElement可以创造虚拟DOM元素,但是效率却不客观。

不得不说,HTML是最优秀的标记语言。

用户可以直接在js中写html代码,通过babel来转换JS中的标签。这种在JS中,混合写入类似于HTML的语法,叫做JSX语法:符合XML规范的JS。

JSX语法的本质,还是在运行的时候,被转换成 React.createElement 的形式来执行
安装babel插件

好,我们先来安装 babel 插件:

npm i babel-core babel-loader babel-plugin-transform-runtime -D
npm i babel-preset-env babel-preset-stage-0 -D
npm i babel-preset-react -D

上面推荐使用淘宝源——cnpm 安装,会快点

上面是6个包

  • babel-core:babel的内核
  • babel-loader:babel 转换工具
  • babel-plugin-transform-runtime:插件
  • babel-preset-env:语法 env
  • babel-preset-stage-0:语法 stage-0
  • babel-preset-react:能够识别转换jsx语法

进行配置:

#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"),
    //生成的内存中首页的名称
    filename:'index.html'
})



//下面有node的语法,因为webpack是基于node构建的,支持node API和语法
/*webpack默认只能打包处理.js后缀名类型的文件;像 .png、.vue 无法主动处理,
所以要配置第三方的loader*/
//向外暴露一个打包的配置对象
module.exports={
    mode:'development',  
    //在webpack 4.x中,有一个很大的特性,就是 约定大于配置
    //默认打包入口路径是 src/index.js
    plugins:[
        htmlPlugin
    ],
    module:{   //所有第三方模块的配置规则
        rules:[  //第三方匹配规则
            //千万别忘记exclude排除项
            {test: /\.js|jsx$/,use:'babel-loader',exclude:/node_modules/}
        ]
    }
}

然后新建一个文件,.babelrc

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

babel配置完成

JS中直接使用HTML

有了babel,就可以直接在JS中写HTML了,我们修改index.js如下:

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

//创建虚拟DOM元素
//const mydiv=React.createElement('div',{id:'mydiv',title:'div aaa'},'这是一个div元素')

//直接写HTML
//使用babel来转换这些JS中的标签
//这种在JS中,混合写入类似于HTML的语法,叫做JSX语法:符合XML规范的JS
//JSX语法的本质,还是在运行的时候,被转换成 React.createElement 的形式来执行
const mydiv=<div id="mydiv" title="div aaa" >这是一个div元素</div>


//使用ReactDOM将虚拟dom渲染(render)到页面上
ReactDOM.render(mydiv,document.getElementById('app'))

使用npm run dev启动,即可发现mydiv语句成功执行,即我们成功在JS中使用了HTML!

有的朋友是不是报错了?关于babel-loader不能用?对,这是个大坑,因为babel-loader版本过高的原因导致不相配,解决方法就是使用命令 npm install babel-loader@7.1.5 -D ,安装一个较低版本的babel-loader即可(会自动覆盖已经安装的babel-loader)

 

 

 

 

 

 

 

 

 

 

发表评论