React
1. 概述
2. 安装配置
操作系统:MAC OS
开发工具:VS CODE
2.1 安装工具
-
node
官网下载安装:https://nodejs.org/en/
安完后,就可以使用npm命令了 -
cnpm
npm命令下载的包,都是境外的,速度不够,可用cnpm命令从镜像下载
命令:sudo npm install -g cnpm --registry=https://registry.npm.taobao.org --verbose
2.2 配置
-
创建目录A,用于后续练习操作
-
进入目录A,配置webpack
-
快速初始化,会创建一个package.json文件
cnpm init -y
-
安装webpack及其客户端(用于打包)
测试时发现最新版间有不兼容,导致运行web-dev-server时出错:Cannot find module 'webpack-cli/bin/config-yargs' 这里给出一个版本匹配: cnpm i webpack@4.44.2 webpack-cli@3.3.12 webpack-dev-server@3.11.0 -D 安装最新版(不指定版本,可能会不兼容): cnpm i webpack webpack-cli webpack-dev-server -D
这里的WARN 和 “gyp: No Xcode or CLT version detected!”,可以忽略 -
查看webpack版本
$ node_modules/.bin/webpack -v
-
3. 开发使用
3.1 入门案例
-
在2.2节的目录A下创建:
- 目录:src, dist (源代码、产品目录)
- 文件:src/index.html, src/index.js, ./webpack.config.js
index.js是打包入口路径
webpack.config.js(若不配置,使用webpack打包时会报错):
module.exports = { mode: "development",// 'development' or 'production' } // development为开发模式,不会压缩打包 // production为产品模式,会压缩打包
-
首页index.html:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>first step</title> <script src="../../dist/main.js"></script> </head> <body> <h1>Home Page</h1> </body> </html>
-
index.js:
console.log("Hello,World!")
-
打包
会在./dist目录下,生产main.js文件 ![image-20201029114427678](https://star-pb.oss-cn-shanghai.aliyuncs.com/mkd/02_Java/03_%E5%89%8D%E7%AB%AF%E6%8A%80%E6%9C%AF/React/image-20201029114427678.png)
node_modules/.bin/webpack -
用浏览器打开index.html
3.2 自动打包
-
使用前面已安装的webpack-dev-server,可以实现自动打包
-
配置package.json
添加运行脚本 "dev": "webpack-dev-server"
-
运行项目
npm run dev
上图给出了访问路径,打开它,看到的是项目目录:
点击src目录:
-
此时修改index.js中打印日志的内容,发现网页控制台(Console)没有跟着变。
因为webpack-dev-server将打包后的main.js放到了根目录,托管于内存,所以本地也是看不到的。
修改index.html即可:
-
webpack-dev-server命令,还支持很多参数
- --open <browser_name>:自动打开浏览器,不指定名称时(如firefox),就是默认系统的浏览器
- --port <prot_num>:指定运行端口
- --hot:
3.3 首页内存化
我们在3.2节完成后,访问到的是项目目录,不是首页index.html。
下面,使用html-webpack-plugin实现自动打开首页。
-
安装html-webpack-plugin
cnpm i html-webpack-plugin -D
-
修改webpack.config.js,添加插件的引用
const path = require('path') // 引入路径 const HtmlPlugin = require('html-webpack-plugin') // 引入html-webpack-plugin // 创建一个插件对象 const htmlPlugin = new HtmlPlugin( { template: path.join(__dirname, './src/index.html'), // 指定来源文件 filename: 'index.html' // 指定放到内存后的文件名 } ) module.exports = { mode: "development", // 'development' or 'production' plugins: [ htmlPlugin // 配置插件 ] }
-
运行项目: npm run dev
此时可以自动打开首页index.html
查看页面元素,可以发现body页签中多了一行,这是html-webpack-plugin所创建的脚本,所以,可以将前面自己定义的那行拿掉
3.4 React渲染
此小节介绍使用react完成页面的渲染
简单案例
-
安装react模块
cnpm i react react-dom -S -
修改index.html,添加一个div元素,并给定id
-
修改index.js,完成元素的创建及其渲染
import React from 'react' import ReactDOM from 'react-dom' // 创建虚拟DOM元素 // 参数1:字符串,元素类型 // 参数2:对象,元素属性 // 参数3:子节点 // 参数n:其他子节点 var myh3 = React.createElement('h3', { id: 'myh3' }, '只是个h3') // 渲染页面 // 参数1:元素对象 // 参数2:页面容器 ReactDOM.render(myh3, document.getElementById('topDiv'))
-
运行项目,成功渲染:
元素嵌套
接着上面的案例,如果在topDiv下,同时存在一段文本,和一个h3标签,要怎么实现呢?
通过调用createElement第四个开始的参数即可
效果:
3.5 JSX语法
JSX语法:符合 xml 规范的 JS 语法,语法格式比HTML严谨很多
简单案例
3.4中讲述到的元素嵌套,需要我们手动创建一个元素对象,然后相互引用,最后再渲染。
这种方式可以更简化,我们直接写HTML即可。
简化方法需要通过babel工具完成转化,本质还是调用了createElement方法。
-
安装babel工具
cnpm i @babel/core babel-loader @babel/plugin-transform-runtime -D
cnpm i @babel/preset-env @babel/preset-stage-0 @babel/preset-react -D
cnpm i @babel/plugin-proposal-class-properties -D (不装最后一个会报错syntax 'class Properties' isn't currently enabled)
-
在项目根目录下创建babel的配置文件(json格式):.babelrc,来引用上面下载的工具
{ "presets": [ "@babel/preset-env", "@babel/preset-react" ], "plugins": [ "@babel/plugin-transform-runtime", "@babel/plugin-proposal-class-properties" ] }
-
修改index.js,直接使用HTML语言
import React from 'react' import ReactDOM from 'react-dom' var mydiv = <div>只是个DIV</div> ReactDOM.render(mydiv, document.getElementById('topDiv'))
-
修改webpack.config.js,添加需要使用babel处理的规则:
module.exports = { ..., // 配置第三方模块规则 // webpack默认只打包js/jsx文件,png等无法处理 module: { rules: [ { test: /\.js|jsx/, use: 'babel-loader', exclude: /node_modules/ } ] } }
test: 表示判定规则,这里是js、jsx结尾的文件
use: 使用babel-loader处理
exclude: 排除的文件(夹) -
运行项目:
JSX中使用JS
如何在JSX中渲染变量呢?
使用花括号{}
import React from 'react'
import ReactDOM from 'react-dom'
var title = "啦啦啦"
var num = 2
var bool = false
var arrayOne = ["成龙", "李小龙", "释小龙"]
var arrayFinal = [];
arrayOne.forEach(item => {
let temp = <h4>{item}</h4>
arrayFinal.push(temp)
})
ReactDOM.render(<div>tttt<hr />
<h3 title={title}>猜标题</h3><hr />
<h3>{num * 10}</h3><hr />
<h3>{bool ? "不是啊" : "是啊"}</h3><hr />
{arrayFinal}<hr />
{arrayOne.map(item => { return <h5>{item}</h5> })}<hr />
{arrayOne.map(item => <h5>{item}</h5>)}
</div>, document.getElementById("topDiv"))
注意点
- JSX中创建DOM时,必须由唯一根元素包裹
- JSX中的标签必须成对,或自闭和,如
<hr/>
3.6 组件
下面介绍两种创建组件的方式
JS对象
简单案例
直接使用标签引用JS对象,即完成了组件化
JS对象必须有返回值
import React from 'react'
import ReactDOM from 'react-dom'
function Hello() {
return <h3>Hello</h3>
}
ReactDOM.render(<div><Hello></Hello></div>, document.getElementById("topDiv"))
属性传递
如何向组件传递属性呢?
import ...
function Hello(id) {
console.log(id)
return <h3>Hello</h3>
}
ReactDOM.render(<div><Hello id="hello"></Hello></div>, document.getElementById("topDiv"))
若属性很多,可以采用下示方法(...<对象>):
import ...
function Hello(props) {
console.log(props)
return <h3>Hello</h3>
}
var dog = {
this.name: "阿黄",
age: 3
}
ReactDOM.render(<div><Hello ...dog></Hello></div>, document.getElementById("topDiv"))
组件抽离
一般会将一个组件单独的抽离为一个文件,然后再引用即可。
-
创建./src/components/Hello.js
import React from 'react' export default function Hello(props) { console.log(props) return <h3>Hello</h3> }
-
修改index.js
import React from 'react' import ReactDOM from 'react-dom' import Hello from './components/Hello' var student = { name: "学生呢", age: 20, grade: 1 } ReactDOM.render(<div><Hello {...student}></Hello></div>, document.getElementById("topDiv"))
使用import,导入Hello.js
-
如果我们新建的组件文件名为Hello.jsx,在项目启动后就会报错,找不到该文件.
需要在webpack.config.js中配置处理规则:module.exports = { ..., resolve: { extensions: [ '.jsx', '.js', ] } }
会按这里配置的扩展名,依次查找。
或者在import时,就指定扩展名 import Hello from './components/Hello.jsx'
-
设置根目录
在导入组件时,也可使用绝对路径-
修改webpack.config.js,指定路径别名
module.exports = { ..., resolve: { ..., alias: { '@': path.join(__dirname, './src') } }
-
在导入时,使用别名@
import Hello from '@/components/Hello'
-
属性/方法
function Teacher(name, age) {
this.name = name // 实例属性
this.age = age // 实例属性
}
Teacher.school = "宁海" // 静态属性
console.log(Teacher.school)
var teacher = new Teacher("李老师", 22)
console.log(teacher)
// 实例方法
Teacher.prototype.teach = function () {
console.log(this.name + "教学ing...")
}
teacher.teach()
// 静态方法
Teacher.teach = function () {
console.log("教学ing...")
}
Teacher.teach()
实例对象不能访问静态属性及方法
class对象
为了更方便开发,诞生了class关键字,其本质还是被转成上面的JS对象被处理
属性/方法
有点类似java类
在class内部,只能编写 构造器、静态属性、实例方法、静态方法
class Student {
// 构造器,初始化实例属性
constructor(name, age) {
this.name = name
this.age = age
}
// 静态属性
static school = "宁海"
// 实例方法
study() {
console.log(this.name + "学习ing...")
}
// 静态方法
static study() {
console.log("学习ing...")
}
}
var student = new Student("小明", 10)
console.log(Student.school)
console.log(student)
student.study()
Student.study()
继承
class之间可以继承,与java一致
class Person {
constructor(name, age) {
this.name = name
this.age = age
}
}
class Student extends Person {
constructor(name, age, school) {
super(name, age)
this.school = school
}
}
var student = new Student("小明", 10, '宁海')
console.log(student)
- 继承关键字: extends
- 要调用父类方法,需通过super关键字
- super必须在this前面调用
创建组件
使用class关键字创建组件的规范:
// 继承Component
class <组件名> extends React.Component {
// 必需有render方法
render(){
// 必需有返回值
return ...
}
}
示例
import React from 'react'
import ReactDOM from 'react-dom'
class Student extends React.Component {
constructor(name, age) {
this.name = name
this.age = age
}
render() {
return <h4>学习a </h4>
}
}
var student = new Student("小明", 10)
console.log(student)
ReactDOM.render(<div><Student></Student></div>, document.getElementById("topDiv"))
属性传递
与JS对象类似,不过class有个属性props,专门用来接收所有外部属性
import React from 'react'
import ReactDOM from 'react-dom'
class Student extends React.Component {
constructor() {
super()
}
render() {
console.log(this)
return <div><h4>学习a</h4><h4>{this.props.name + ":" + this.props.sex}</h4></div>
}
}
var teacher = {
name: "李老师",
sex: "man"
}
ReactDOM.render(<div><Student {...teacher}></Student></div >, document.getElementById("topDiv"))
this.state
前面介绍的属性传递,是外部向class传递属性。
如果需要定义class自己的属性,可以通过this.state实现
import React from 'react'
import ReactDOM from 'react-dom'
class Student extends React.Component {
constructor() {
super()
this.state = {
firstMsg: 'Welcome!'
}
}
render() {
console.log(this.state.firstMsg)
return <div><h4>学习a</h4></div>
}
}
ReactDOM.render(<div><Student></Student></div >, document.getElementById("topDiv"))
<div onKeyDown={(e) => this.doEnter(e)}>
<h1>Login</h1>
<div className="form">
<div className="item">
<i className="fa fa-user-circle-o" aria-hidden="true" />
<input type="text" placeholder="Username" ref="username" />
</div>
<div className="item">
<i className="fa fa-key" aria-hidden="true" />
<input type="password" placeholder="Password" ref="password" />
</div>
</div>
<button onClick={() => this.doLogin()}>Login</button>
</div>