React基础应用及常用代码

目录

基础知识

babel.config.js

js,html,css,Vue,react,angular,nodejs,webpack,less,ES6,commonjs等的关系

ECMAScript 6(ES6)

let、const、var 的区别

Webpack、npm、node关系

props和state区别

通用框架类

ES 6语法大全

let声明变量

let {}解构赋值

const声明常量

解构表达式

字符串扩展

 字符串模板

字符串插入变量和表达式。变量写在${}中,${}中可以放入js表达式

##函数优化

1、函数默认值

2、不定参数的函数

3、箭头函数

4、箭头函数结合解构表达式

##对象优化

1、key、value、entries

2、Object.assign

3、 声明对象简写

4、对象的函数属性简写

5、对象的扩展运算符

map和reduce方法

模块化

export

React

state的基本使用

setState()修改状态

从JSX中提取程序处理事件

React.Fragment

render()

Class类定义

class类式组件React.Component

prototype:实例属性|实例方法 

私有属性定义与存值函数|取值函数

static:静态属性|静态方法

class:使用注意点

class:类中this指向问题与规避方法

context

constructor() 构造函数

extends:单继承语法与原型链

React:Class类式组件定义

继承父类 初始state属性

React:Class类式组件-绑定点击事件

基本应用

import引入组件


基础知识

babel.config.js

Babel 将较新的 Javascript 转换为旧的 Javascript,以便较旧的浏览器(尤其是 IE11)可以理解它。

js,html,css,Vue,react,angular,nodejs,webpack,less,ES6,commonjs等的关系

前端开发根本只有三种语言,js,html和css

随着业务的复杂,前端也需要进行模块化开发,可是js本身不具备模块化语法,无法进行模块化.为了解决这个问题,出现了CommonJS,AMD,CMD,UMD和ES6规范,这些规范都包含了js模块化方案.从而解决了JS全局污染和依赖管理混乱问题.这些更多属于MVC中的M.

而Vue,react,angular更多属于构建用户界面的V的框架.提高页面开发效率.

less则聚焦于kuozcss语言,让css变成可编程.

WebPack可以看做是模块打包机:它做的事情是,分析你的项目结构,找到JavaScript模块以及其它的一些浏览器不能直接运行的拓展语言(Scss,TypeScript等),并将其转换和打包为合适的格式供浏览器使用。

Nodejs则扩展是js的使用场景,让js不仅能够在浏览器运行,同时也可以在各种平台上运行,其实内部本质是吧js翻译成了c++.

总结来说, 不管怎么变,一切都是围绕js,html和css进行扩展.

npm

      我们发现用到node环境的地方,都会出现npm这个名词,那么npm是干什么的?npm是node.js的包管理器,它为javascript开发人员提供了一个集中管理工具,开发人员可以将自己开发的代码包上传到npm的官网,npm通过注册表来维护每个包的信息,其他开发人员可以很方便的使用npm的指令下载和解压需要使用的代码包,无需关注每个包的依赖包,npm在下载时会自动将依赖包下载并管理起来,解放双手!

生成一个pakeage.json文件,这个文件中会记录项目中使用的包以及版本,实现项目的依赖版本管理,此后新增/更新的包都会记录在这个文件中,增加项目的移植性。

webpack主要有以下功能:

自定义插件 | webpack 中文文档 | webpack中文文档 | webpack中文网 (webpackjs.com)

代码转换:TypeScript 编译成 JavaScript、SCSS 编译成 CSS 等浏览器可以识别的格式/类型。

文件优化:压缩 JavaScript、CSS、HTML 代码,压缩合并图片等。

代码分割:提取多个页面的公共代码、提取首屏不需要执行部分的代码让其异步加载。

模块合并:在采用模块化的项目里会有很多个模块和文件,需要构建功能把模块分类合并成一个文件。

自动刷新:监听本地源代码的变化,自动重新构建、刷新浏览器,提升开发效率。

在前端项目中,通过webpack.config.js(默认使用此文件名)来控制webpack编译打包相关的配置,具体配置这里不赘述。

CommonJS

      前文讲了这么多概念,似乎一直没提到CommonJS,那么CommonJS是什么,跟前文提到的一些概念有什么关系?

      看过webpack配置文件的同学应该会发现webpck的配置文件中会使用到很多require和export关键词,而vue文件中则频繁使用的是import和export,我们知道这几个关键词是用于导入和导出模块,模块是什么?为何会有这种差异?这就涉及到模块化和CommonJS规范了。

模块化

      模块化开发是一种管理方式,是一种生产方式,一种解决问题的方案,一个模块就是实现特定功能的文件,有了模块,我们就可以更方便地使用别人的代码,想要什么功能,就加载什么模块,可以很好的解决命名冲突等各种问题。

      但是模块开发需要遵循一定的规范,因此诞生了CommonJS、AMD、CMD和ES6等模块化规范。

CommonJS规范

      node.js是基于CommonJS规范的,CommonJS是模块化的规范之一。

根据commonjs规范,一个单独的文件就是一个模块。加载模块使用require方法,该方法读取一个文件并执行,最后返回文件内部的exports对象

使用方式:

// 导入

require("module");

require("../app.js");

// 导出

exports.getStoreInfo = function() {};

module.exports = someValue;

CommonJS使用require加载模块时是使用同步加载的方式,是专门为服务器端设计的模块化规范,不适用于浏览器端。

AMD规范

      如果是浏览器环境,要从服务器端加载模块,这时就必须采用非同步模式,因此浏览器端一般采用AMD规范,require.js即基于AMD规范。

AMD 即Asynchronous Module Definition,中文名是“异步模块定义”的意思,它采用异步方式加载模块,模块的加载不影响它后面语句的运行,所有依赖这个模块的语句,都定义在一个回调函数中,等到加载完成之后,这个回调函数才会运行

使用方式:

// 定义

define("module", ["dep1", "dep2"], function(d1, d2) {...});

// 加载模块

require(["module", "../app"], function(module, app) {...});

使用AMD规范可以实现在浏览器端异步加载多个模块,并且可以并行加载,但是其存在代码阅读和书写困难、开发成本高等问题。

CMD规范

      CMD跟AMD相似,也是采用异步加载模块的方式。它们的区别是:CMD相当于按需加载,定义一个模块的时候不需要立即制定依赖模块,在需要的时候require就可以了,比较方便;而AMD则相反,定义模块的时候需要制定依赖模块,并以形参的方式引入回调函数中。

使用方式:

define(function(require, exports, module) {

  var a = require('./a');

  a.doSomething();

  // 依赖就近书写,什么时候用到什么时候引入

  var b = require('./b');

  b.doSomething();

});

sea.js即使用了CMD规范。

ES6规范

      ES6模块的设计思想,是尽量的静态化,使得编译时就能确定模块的依赖关系,以及输入和输出的变量。commonjs和AMD模块,都只能在运行时确定这些东西。比如,commonjs模块就是对象,输入时必须查找对象属性。

ES6 在语言标准的层面上,实现了模块功能,而且实现得相当简单,完全可以取代 CommonJS 和 AMD 规范,成为浏览器和服务器通用的模块解决方案。

使用方式:

// 导入

import "/app";

import React from “react”;

import { Component } from “react”;

// 导出

export function multiply() {...};

export var year = 2018;

export default ...

...

      node的默认模块格式是CommonJS,目前还没决定怎么支持ES6模块。所以,只能通过Babel这样的转码器,在Node里面使用ES6模块。

总结

      通过前文中对基本概念的介绍,相信大家已经大概理解这些概念和vue之间的关系了:vue是基于ES6的JS框架;ES6是JavaScript的一套标准规范;webpack可以对前端项目进行编译、压缩和打包,webpack是使用node开发,运行在node环境中的一个工具node遵循CommonJS规范;npm是node的包管理器。

      本质上vue不依赖node、webpack、npm等,但是在实现前端工程化的进程中,这些技术可以让开发人员将更多的注意力放在代码编写中,不再过多的关注兼容性、规范化、编译打包甚至依赖版本管理等,很大地提升了开发效率。

原文链接:https://blog.csdn.net/u011244839/article/details/103654979

ECMAScript 6(ES6)

ECMAScript 6(ES6),也被称为 ECMAScript 2015(ES2015),是 JavaScript 语言的一个版本。ES6 是对 JavaScript 语言的一系列重大更新,引入了许多新的语法特性和功能,以改进 JavaScript 的可读性、可维护性和开发效率。在技术上,ES6 是 ECMAScript 标准的第六个版本。

let、const、var 的区别

1. 使用 var 声明的变量,其作用域为该语句所在的函数内,且存在变量提升现象。

2. 使用 let 声明的变量,其作用域为该语句所在的代码块内,不存在变量提升。

3. 使用 const 声明的是常量,在后面出现的代码中不能再修改该常量的值。

Webpack、npm、node关系

Webpack 是一个前端资源加载/打包工具。它将根据模块的依赖关系进行静态分析,然后将这些模块按照指定的规则生成对应的静态资源,可以有效减少由新特性带来的浏览器的兼容性问题。

npm是Node.js 官方提供 Node 生态系统的包管理工具,自带依赖和版本的控制,让开发者专注于业务。是 Node.js 包的标准发布平台,用于 Node.js 包的发布、传播、依赖控制

node是指基于V8引擎提供了一种可以让JS代码跑在后端的能力

webpack将npm中安装的包打包成更小的浏览器可读的静态资源

webpack依赖于node环境,但它只是一个前端的打包工具,打包的是静态资源,和后台没有关系

props和state区别

1.props不能被其所在的组件修改,从父组件传递进来的属性不会在组件内部更改;props意为属性,只可父组件改变子组件的props,而子组件不能自更新,props是子组件暴露给外部的公有接口

2.state只能在所在组件内部更改,或在外部调用setState函数对状态进行间接修改。

总结:简单来说,props是父组件传递过来的属性,比如列表页跳转到新增页,新增页的props都是列表传递过来的,并且我们在新增页面是无法去修改props属性的

所以当我在新增页面想要联动去修改一些属性我们要写到state里面才能取到最新变更后的属性

通用框架类

ES 6语法大全

什么是ECMAScript?

ECMAScript是浏览器脚本语言的规范,而我们熟知的js语言,如JavaScript则是规范的具体实现。es6就好比Java的jdk。

let声明变量

var声明的变量往往会越域;let声明的变量有严格局部作用域

{ var a = 1; let b = 2; }

console.log(a) // 1

console.log(b) // Uncaught ReferenceError: b is not defined

var 可以声明多次;let只能声明一次

var会变量提升;let不存在变量提升

console.log(x); var x = 10; // undefined

console.log(y); let y = 12; // Uncaught ReferenceError: Cannot access 'y' before initialization

所以 let更加规范 建议使用

let {}解构赋值

let { fadeAnim } = this.state;

的Equals

let fadeAnim = this.state.fadeAnim;

这就是所谓的解构赋值

const声明常量

声明之后不允许改变

const a = 1; console.log(a) //1

a = 2; console.log(a) // Uncaught TypeError: Assignment to constant variable.

一旦声明必须初始化,否则会报错

解构表达式

1、数组解构

let arr = [1, 2, 3]; let [a, b, c] = arr;

console.log(a, b, c) //1,2,3

2 对象解构

const person = { name: "qiyue", age: 23, language: ['java', 'js', 'css'] }

const { name, age, language } = person console.log(name, age, language) //qiyue 23 (3) ["java", "js", "css"]

const person = { name: "qiyue", age: 23, language: ['java', 'js', 'css'] } //从person里解析出name的值在赋值给abc

const { name:abc, age, language } = person

console.log(abc, age, language) //qiyue 23 (3) ["java", "js", "css"]

字符串扩展

let str = "hello.vue"; console.log(str.startsWith("hello")) //true

console.log(str.endsWith("vue")) //true

console.log(str.includes("e")) //true console.log(str.includes("hello")) //true

 字符串模板

let str = `<span>hello world</span>`

console.log(str) // <span>hello world</span>

字符串插入变量和表达式。变量写在${}中,${}中可以放入js表达式

let info = `我是${abc},今年${age}`console.log(info) //我是qiyue,今年23 let info = `我是${abc},今年${age}`console.log(info) //我是qiyue,今年23 let info = `我是${abc},今年${age}`console.log(info) //我是qiyue,今年23 function fun() { return "这是一个函数"; } let info = `我是${abc},今年${age + 10},我想说:${fun()}` console.log(info) //我是qiyue,今年33,我想说:这是一个函数

##函数优化

1、函数默认值

直接给参数写上默认值,没传就会自动使用默认值

function add(a, b = 1) { return a + b; } console.log(add(10)) //11

2、不定参数的函数

不定参数用了表示不确定的参数个数,形如...变量名,由...加上要给具名参数标识符组成。具名参数只能放在参数列表的最后,并且有且只有一个不定参数

function fun(...params) {

console.log(params.length) }

fun(1, 2) // 2

fun(1, 2, 3, 4) //4

3、箭头函数

//以前

var sum = function (a, b) { c = a + b return c }

console.log(sum(2, 3)) // 5

//箭头函数

var sum2 = (a, b) => a + b;

console.log(sum2(2, 4)) // 6

4、箭头函数结合解构表达式

//以前 function hello(person) { console.log("hello" + person.name) }

hello(person); //helloqiyue //箭头函数

let hello2 = params => console.log("hello" + person.name) hello2(person) //helloqiyue //箭头函数加解构表达式

var hello3 = ({ name }) => console.log("hello" + name) hello3(person) //helloqiyue

##对象优化

1、key、value、entries

es6给Object扩展了许多新的方法,如

  • key(obj):获取对象的所有key形成的数组

  • value(obj):获取对象的所有value形成的数组

  • entries(obj):获取对象所有的key和value形成的二维数组

const person = { name: "qiyue", age: 23, language: ["java", "js", "css"] } console.log(Object.keys(person)) //["name","age","language"]

console.log(Object.values(person)) // ["qiyue",23,Array(3)]

console.log(Object.entries(person)) //[Array(2),Array(2),Array(2)]

2、Object.assign

Object.assign方法的第一个参数是目标对象,后面的参数都是源对象;将源对象的属性赋值到目标对象中

const target = { a: 1 }

const source1 = { b: 2 }

const source2 = { c: 3 }

Object.assign(target, source1, source2);

console.log(target) //{a: 1, b: 2, c: 3}

3、 声明对象简写

//以前 const name = 'sanyue' const age = 21 //将属性值name,age分别赋给person1对象的name,age,后面是属性值

const person1 = { name: name, age: age }

console.log(person1) //{name: "sanyue", age: 21} //es6:属性名和属性值变量名一样,可以省略 const person2 = {name,age} console.log(person2) //{name: "sanyue", age: 21}

4、对象的函数属性简写

let person3 = { name: "qiyue",

//以前

eat: function (food) { console.log(this.name + "在吃" + food); },

//箭头函数中this不能使用,用对象.属性

eat2: food => console.log(person3.name + "在吃" + food),

eat3(food) { console.log(this.name + "在吃" + food) }

}

person3.eat("苹果") //qiyue在吃苹果

person3.eat2("香蕉") // qiyue在吃香蕉

person3.eat3("西瓜") //qiyue在吃西瓜

5、对象的扩展运算符

扩展运算符(...)用于取出参数对象所有可遍历属性然后拷贝到当前对象

//拷贝对象(深拷贝)

let p1 = { name: "qiyue", age: 23 }

let obj = { ...p1 }

console.log(obj)//{name: "qiyue", age: 23} //合并对象

let age1 = { age: 24 }

let name1 = { name: "qiyue" }

let p2 = {} p2 = { ...age1, ...name1 } console.log(p2) //{age: 24, name: "qiyue"} //如果p2中原本有name,age属性会被覆盖

map和reduce方法

1、map():接收一个函数,将原数组中的所有元素用这个函数处理后放入新数组返回

let arr = ["1", "3", "4", "23"] arr = arr.map(item => item * 2) console.log(arr) //[2, 6, 8, 46]

2、reduce():为数组中的每一个元素依次执行回调函数,不包括数组中被删除或未被赋值的元素

语法:arr.reduce(callbace,[initialValue])

callback(执行数组中每个值的函数,包含四个参数)

  • previousValue(上一次调用回调返回的值,或者是提供的初始值(initialValue))

  • currentValue(数组中当前被处理的元素)、

  • index(当前元素在数组中的索引)

  • array(调用reduce的数组)

initialValue(作为第一次调用callback的第一个参数)

let arr1 = [2, 40, -10, 6] let result = arr1.reduce((a, b) => { return a + b }, 10) console.log(result)//48

模块化

什么是模块化:模块化就是把代码进行拆分,方便重复利用。类似Java中的导包,要使用一个包,必须先导包。二Js中没有包的概念,换来的就是模块

模块的功能主要有两个命令构成:export和import

  • export命令用于规定模块的对外接口,export不仅可以导出对象,一切js变量都可以导出。比如:基本类型变量、函数、数组、对象

  • import命令用于导入其他模块提供的功能

export

export default 是ES6 模块化系统中用来导出模块的默认成员。当在一个模块中只导出一个成员时,可以使用 export default 将其导出为默认成员,在导入时可以忽略成员名称,直接导入默认成员,比如:

// 导出默认成员 export default function add(a, b) { return a + b; } // 导入默认成员 import sum from './util'; console.log(sum(1, 2)); // 3

注意:一个模块只能导出一个默认成员。如果一个模块需要导出多个成员,可以使用 export 语法分别导出,比如:

// 导出多个成员 export function add(a, b) {   return a + b; } export function subtract(a, b) {   return a - b; } // 导入多个成员 import { add, subtract } from './util'; console.log(add(1, 2)); // 3 console.log(subtract(2, 1)); // 1

React

state的基本使用

  • 状态(state)即数据,是组件内的私有数据,只可以在组件的内部使用

  • state的值是对象,意为一个组件中有多个数据

初始化state:

setState()修改状态

  • 状态是可以改变的

  • 语法:this.setState({要修改的数据})

  • 不要直接修改state中的值

  • setState()作用:修改state 更新UI(页面渲染新数据)

  • 数据驱动视图

从JSX中提取程序处理事件

  • JSX中掺杂过多JS逻辑代码,会显得非常混乱

  • 推荐︰将逻辑抽离到单独的方法中,保证JSX结构清晰

方法一:

事件绑定 this指向

  • 箭头函数 1、利用箭头函数自身不绑定this的特点 2、render()方法中的 this为组件实例,可以获取到setState)

方法二

利用bind方法(Function.prototype.bind0)

1、利用ES5中的bind方法,将事件处理程序中的this与组件实例绑定到一起

方法三

class的实例方法(推荐)

1、利用箭头函数形式的class实例方法

2、注意:该语法是实验性语法,但是,由于babel的存在可以直接使用

React.Fragment

react return里返回多个元素值要有父标签包裹。

React.Fragment组件能够在不额外创建 DOM 元素的情况下,让 render()方法中返回多个元素。相当于空标签<></>。

render()

基本用法

import React, { Component, Fragment } from "react";

import "./App.css";

class App extends Component {

  render() {

    return (

      <>

        <div>

          <input />

          <button>按钮</button>

        </div>

        <ul>

          <li>哈</li>

          <li>咯</li>

        </ul>

      </>

    );

  }

}

export default App;

一、React中的render()

1、render():是 class 组件中唯一必须实现的方法

2、当 render 被调用时,它会检查 this.props 和 this.state 的变化并返回以下类型之一:

(1)React 元素。通常通过 JSX 创建。例如,<div /> 会被 React 渲染为 DOM 节点,<MyComponent /> 会被 React 渲染为自定义组件,无论是 <div /> 还是 <MyComponent /> 均为 React 元素。

(2)数组或 fragments。 使得 render 方法可以返回多个元素。

(3)Portals。可以渲染子节点到不同的 DOM 子树中。

(4)字符串或数值类型。它们在 DOM 中会被渲染为文本节点

(5)布尔类型或 null。什么都不渲染。(主要用于支持返回 test && <Child /> 的模式,其中 test 为布尔类型。)

3、render() 函数应该为纯函数,这意味着在不修改组件 state 的情况下,每次调用时都返回相同的结果,并且它不会直接与浏览器交互。

4、如需与浏览器进行交互,请在 componentDidMount() 或其他生命周期方法中执行你的操作。保持 render() 为纯函数,可以使组件更容易使用、维护。

5、在React.Component类的子类中会重新定义(实现)

6、在react中,触发render的有4条路径。

以下假设shouldComponentUpdate都是按照默认返回true的方式。

(1)首次渲染Initial Render

(2)调用this.setState (并不是一次setState会触发一次render,React可能会合并操作,再一次性进行render)

(3)父组件发生更新(一般就是props发生改变,但是就算props没有改变或者父子组件之间没有数据交换也会触发render)

(4)调用this.forceUpdate

Class类定义

        通过构造函数生成JavaScript对象实例的方式,固然可以满足实际开发需求,但是,这种写法跟传统的面向对象语言(比如 C++ 和 Java)差异很大,很容易让新学习这门语言的程序员感到困惑。所以,ES6引入了新的Class(类)这个概念,作为对象的模板。通过class关键字,可以定义类,然后通过new class类名的方式创建对象。

        那么,如何定义一个类呢?语法其实非常简单,

class 类名{

//构造器-固定写法

constructor(参数列表){

//todo:做一些实例属性初始化的操作

this.propertyName = 参数1;

...

this.propertyName = 参数n;

}

//实例成员

methodName(参数列表){

//toDo:实现一些功能

}

}

class类式组件React.Component

        React框架允许开发者基于ES6的Class类和extends继承语法定义类式组件,只需要继承React核心库react.js中暴露出来的React.Component父类即可

prototype:实例属性|实例方法 

        以下通过class类的语法,将上面回顾部分中的Point对象模板重构为ES6-Class风格的类。示例代码如下

class Point{

        //构造器

        constructor(x,y) {

          //初始化实例属性x,y

          this.x = x;

            this.y = y;

            this.name = "Point-Function";

        }

        //定义实例方法

        getName(){

            return this.name;

        }

        getX(){

            return this.x;

        }

        getY(){

            return this.y;

        }

        getLocation(){

            return `(${this.x},${this.y})`;

        }

        toString(){

            return `x=${this.x},y=${this.y}`;

        }

    }

    //定义Point类的对象

    const point = new Point(100,150);

    console.log(point.name);

    console.log(point.getName());

    console.log(point.getLocation());

私有属性定义与存值函数|取值函数

        熟悉Java后端编程语言的朋友应当十分了解,每当我们使用Java去定义一个类时,总会将成员属性私有化,并为类内部的成员属性添加getter/setter方法,体现的是类定义的一种封装思想,可以提升变量访问的安全性。

        ES6的class-类语法规则中包含了如何实现类实例属性的私有化——在属性名之前通过#符号修饰,声明为一个私有属性,同时也可以提供接口供外部对象访问、修改私有属性——这就是存值函数(set函数)和取值函数(get函数)。

        以下,我们将为Point类定义一个私有属性version,并借助get/set函数来获取、修改它。

class Point{

        //私有属性

        #version = 'v1.0';

        //存值函数

        set version(version){

            //设置#version私有属性的值之前,可以做一些过滤、预处理操作

            if(typeof version === "undefined" || version == null) return;

            this.#version = version;

        }

        //取值函数

        get version(){

            return this.#version;

        }

        //静态属性

        static msg = "Point_class";

        //静态方法

        static showMsg(){

            console.log(this);

            console.log(Point.msg);

        }

        //构造器

        constructor(x,y) {

            //初始化实例属性x,y

            this.x = x;

            this.y = y;

            this.name = "Point-Function";

        }

        //定义实例方法

        getName(){

            return this.name;

        }

        getX(){

            return this.x;

        }

        getY(){

            return this.y;

        }

        getLocation(){

            return `(${this.x},${this.y})`;

        }

        toString(){

            return `x=${this.x},y=${this.y}`;

        }

    }

    //定义Point类的对象

    const point = new Point(100,150);

    //修改私有成员属性的值

    point.version = 'v2.0';

    console.log(point.version);

static:静态属性|静态方法

class Point{

        //静态属性

        static msg = "Point_class";

        //静态方法

        static showMsg(){

            console.log(this);

            console.log(Point.msg);

        }

        //构造器

        constructor(x,y) {

            //初始化实例属性x,y

            this.x = x;

            this.y = y;

            this.name = "Point-Function";

        }

        //定义实例方法

        getName(){

            return this.name;

        }

        getX(){

            return this.x;

        }

        getY(){

            return this.y;

        }

        getLocation(){

            return `(${this.x},${this.y})`;

        }

        toString(){

            return `x=${this.x},y=${this.y}`;

        }

    }

    //调用静态成员

    Point.showMsg();

class:使用注意点

        为简化类型判断、类初始化操作,ES6新增了in关键字、static静态代码块之类的内容,具体可见:ES6 入门教程。下面,我们来看一些class类使用时的一些注意点,因为这可能影响到之后我们编写React的class类式组件。

        ①严格模式:JavaScript 严格模式(strict mode)即在严格的条件下运行。ES5中经常是通过"use strict" 指令实现,例如:JQuery脚本库源码中也开启了严格模式。

严格模式会对原来的代码产生什么影响呢?首要一点就是,我们无法使用未声明的变量,这意味着JavaScript会脱离以往“懒散”的代码结构,以更高的标准运行在浏览器上。

        ②不存在变量提升。意味着,Class类的使用,必须在定义之后,如下的写法是不被允许的。

③name属性。本质上,ES6 的类只是 ES5 的构造函数的一层包装,所以函数的许多特性都被Class继承,包括name属性。name属性总是对应紧跟在class关键字后面的类名,除非你对它进行修改操作。

        还有一些其它注意点或者技巧,如:Generator方法等,我们这里用不太到,暂时先跳过。

class:类中this指向问题与规避方法

还有一个重要的问题就是:Class类中的this指向问题。这将会影响到后续:在定义React类式组件过程中,尤其是事件回调方法的使用。

        我们知道,如果类的成员方法内部含有this,那么它默认指向当前类的实例。但是,一旦我们开始尝试单独去调用类的成员方法,那么就很可能报错——原因:会导致成员方法的this指向发生改变。举个例子,

class Point{

        constructor(x,y) {

            this.x = x;

            this.y = y;

        }

        getX(){

            console.log(this);

            return this.x;

        }

        getY(){

            console.log(this);

            return this.y;

        }

    }

    const point = new Point(100,150);

    console.log(point.getX());//100

    //下面做一个对象解构

    const { getY } = point;

    console.log(getY());//expect value:150,real value:直接报错

我们看一下输出结果,很明显,在解构之后,getY()方法在独立调用时,this的指向变了,已经变为undefined。

为什么是undefined呢?至少我们预期的,不是Point类的对象point,那它也应该是window全局对象啊。我们可能忽略了一点,上面有提到,ES6的class是在严格模式下执行的,我们无法去访问未定义的变量。而恰好,class内部的这个全局范围内,this变量是未定义的,并非指向window对象,所以是undefined。

 那么,如何避免上述问题,让this指向我们想要指的那个地方呢?

        有以下两种解决方法:

        方法①:在constructor()构造器中显示调用bind()方法,修改this的指向。修改后的代码如下,

class Point{

        constructor(x,y) {

            this.x = x;

            this.y = y;

            //bind-显示绑定this指向

            this.getY = this.getY.bind(this);

        }

        getX(){

            console.log(this);

            return this.x;

        }

        getY(){

            console.log(this);

            return this.y;

        }

    }

    const point = new Point(100,150);

    console.log(point.getX());//100

    //下面做一个对象解构

    const { getY } = point;

    console.log(getY());//expect value:150,real value:150.

方法②:

使用箭头函数,箭头函数内部的this总是指向定义时所在的对象。且不看如何理解,我们先看下如何编写代码。

class Point{

        constructor(x,y) {

            this.x = x;

            this.y = y;

        }

        getX(){

            console.log(this);

            return this.x;

        }

        getY = ()=>{

            console.log(this);

            return this.y;

        }

    }

    const point = new Point(100,150);

    console.log(point.getX());//100

    //下面做一个对象解构

    const { getY } = point;

    console.log(getY());//expect value:150,real value:150.

可以看到,现在确实是预期的输出结果。那么是什么原因呢?先明确一点,箭头函数中的this,是在函数定义时候绑定的,并且是固定不可变的;之前会报错的形式,其this是在执行函数的时候绑定的。箭头函数在什么时候被定义呢?显然是和类一起定义的,因此,箭头函数内部的this会总是固定指向实例对象。

context

在react中,context是一个无需为每层组件手动添加props就能在组件树之间进行数据传递的方法;context提供了一种在组件之间共享指定值的方式,而且不必显式的通过组件树的逐层传递props。

constructor() 构造函数

方法格式如下:

constructor(props)

在 React 组件挂载之前,会调用它的构造函数 constructor()。

React.Component 子类实现构造函数时,应在其他语句之前前调用 super(props)。

在 React 中,构造函数仅用于以下两种情况:

  • 通过给 this.state 赋值对象来初始化内部 state。

  • 为事件处理函数绑定实例。

如果不初始化 state 或不进行方法绑定,则不需要为 React 组件实现构造函数。

在 constructor() 函数中不要调用 setState() 方法。如果你的组件需要使用内部 state,请直接在构造函数中为 this.state 赋值初始 state:

constructor(props) {   super(props);   // 不要在这里调用 this.setState()   this.state = { counter: 0 };   this.handleClick = this.handleClick.bind(this);}

只能在构造函数中直接为 this.state 赋值。如需在其他方法中赋值,你应使用 this.setState() 替代。

extends:单继承语法与原型链

   ES5中的传统继承方式比较复杂,方式种类内容也很多,包括:原型链继承、组合继承、寄生组合式继承等等,可以自行搜索学习。下面看ES6中的继承语法。

        ES6的class继承语法借助extends关键字实现,规则十分之简单,规则如下,

class 父类{

    constructor(){

    }

}

class 子类 extends 父类{

    constructor(){

        super();//必须显示调用父类构造函数

    }

}

注意到,子类通过extends关键字继承父类时,在构造函数内部,必须首先通过super关键字,调用父类的构造方法,这一动作的目的是:用来新建一个父类的实例对象。如果不做,那么程序就会报错。为什么呢?

        原因在于:ES6规定,子类自己的this对象,必须要先通过父类的构造函数完成塑造,得到与父类相同的实例属性和方法,然后在对其加工,添加子类拓展出来的新的实例属性和方法。换言之,父类实例的创建必须在子类实例之前,或者说子类实例构建的原材料就是父类实例。所以,必须先得到这个原材料——父类实例,否则编译器它“巧妇难为无米之炊”啊,你想让它凭空捏造出来一个子类实例,它自然做不到,就会报错了。

        在之后,通过extends关键字继承React.Component父类时,也一定要注意这一点。

        另一点是,在子类的构造函数中,只有调用super()之后,才可以使用this关键字,否则会报错。这是因为子类实例的构建,必须先完成父类的继承,只有super()方法才能让子类实例继承父类。

        遵循如上规则的示例代码如下,

class Point {

  constructor(x, y) {

    this.x = x;

    this.y = y;

  }

}

class ColorPoint extends Point {

  constructor(x, y, color) {

    //step-1

    super(x, y);

    //step-2

    this.color = color;

    this.color = color;

}

 关于继承的特点,概括起来为:

【1】父类的私有属性子类无法继承;

【2】父类的实例属性、实例方法、静态属性、静态方法都会被继承。

        除上面的内容之外,我们再谈一谈`原型链`。

        我们知道,大多数浏览的ES5语法实现过程中,每个对象都有一个__proto__属性,指向对应的构造函数的prototype属性。其中:__proto__属性,是对象才有的;而prototype,是函数才有的。从网上摘录的一句比较清楚的解释如下,

举个例子,我们来探索一下prototype和__proto__之间的关系,示例代码如下,

function Point(x, y) {

        //成员属性

        this.x = x;

        this.y = y;

    }

    //挂载实例属性

    Point.prototype.name = "Point-Function";

    //挂载实例方法

    Point.prototype.getName = function (){

        return this.name;

    }

    Point.prototype.getX = function() {

        return this.x;

    }

    Point.prototype.getY = function (){

        return this.y;

    }

    Point.prototype.getLocation = function (){

        return `(${this.x},${this.y})`;

    }

    Point.prototype.toString = function() {

        return "x=" + this.x + ",y=" + this.y;

    };

    const point = new Point(100,150);

    //比较point.__proto__ === Point.prototype

    console.log(point.__proto__ === Point.prototype);

    //打印point.__proto__

    console.log(point);

    //打印Point.prototype

    console.log(Point.prototype);

    console.log(Point.prototype.prototype);

  打印结果如下,

可以看到,

        ①Point.prototype完全等价于point.__proto__属性,表示的是同一个对象。

        ②当对Point构造函数取两次prototype,打印的结果为undefined。

        我们来结合之前的描述,尝试通过画图来对上面的打印结果进行解释。

如上图所示,即为Point构造函数、Point原型对象、Point对象三者之间的存在的关系。

        之前有提到:ES6的class类可以视为ES5的function构造函数的一种语法糖,其prototype属性在class上依旧存在。那么,在ES6的extends语法之下,如何描述上述这3者之间的关系呢?从网上摘录出来的一个解释如下,

(2)分析类A本身与类B之间的继承关系,主要是两条线:①构造函数的继承线;②方法的继承线。之所以这样区分,是因为(1)中,我们已经验证,Class A和Class B,就相当于是ES5中的构造函数function A和function B,本质上数据类型还是function;此外,类的实例方法是挂载到构造函数的原型对象prototype上的,所以要分两条线进行分析。其关系如下图所示,

 至此,关于extends类的继承和原型链关系分析部分的内容就结束了。除此之外,ES6还允许我们去继承原生内置构造函数,像:Error、Date、Array等(ES5中是不可以的,无法实现的),其基本语法规则和上述jextends继承语法规则一致,就不在此赘述了。

React:Class类式组件定义

至此,关于ES6中class类语法规则的主要内容已经回顾完毕,下面叙述如何基于class类语法规则,定义一个React的类式组件。

        React官网给出了一个简单例子如下,

class Welcome extends React.Component {

  render() {

    return <h1>Hello, {this.props.name}</h1>;

  }

}

主要思路就是:继承React核心库react.js中开发出来的类类型React.Component父类,并重写render()实例方法,基于JSX语法规则返回一个Virtual DOM-虚拟DOM元素。

         

继承父类 初始state属性

React:Class类式组件-绑定点击事件

严格来讲,此部分数据React事件处理部分的内容。我们在此处简单使用一下,来对应上面有讲到的关于Class-类中this指向的问题,在React类式组件定义中的应用。先看一下,官网关于使用部分的介绍,如下图所示,

React 元素的事件处理和 DOM 元素的很相似,但是有一点语法上的不同

  • React 事件的命名采用小驼峰式 (camelCase) ,而不是纯小写

  • 使用JSX 语法时你需要传入一个函数作为事件处理函数,而不是一个字符串

那么,我们在使用时,只需要遵循上述规则,并绑定好事件,提供事件处理函数即可。先截个图,回忆一下原生DOM中的事件属性和描述信息。

需求描述:为上面的三栏布局,每一栏添加一个点击事件,点击事件触发之后,打印对应的组件显示文字内容。

        实现思路:由于是JSX创建的虚拟DOM对象,那么,我们将onclick转换为小驼峰命名格式,并绑定到对应的JSX-VDOM节点上即可;此外,我们将事件回调函数写到React的class类式组件内部。

        分步实现:

        (1)尝试为Left组件,绑定点击事件,示例代码如下,

(2)尝试点击Left子组件,但是发现控制台报错,显示当前this为undefined。

(3)立即想到,之前有介绍关于ES6-class类内部的this指向问题。由于:①函数的定义形式,规定了它内部的this指向是在运行时,由调用者确定的;②此处是在点击事件内部触发的回调函数,导致当前实例函数并不是由组件对象发起调用的,因此,在严格模式下,对应的this就变成了undefined。

                那么,我们借助之前的思路,将handlerClick()实例方法的定义形式转换为箭头函数的形式,使其内部的this在handlerClick定义时就确定为当前类的对象——组件实例。修改代码如下所示,

  

基本应用

import引入组件

import基本语法

在React中,使用import 语句引入外部文件或模块的语法如下:

import[模块名)from"路径/模块名"

其中,大括号0是必需的,用于指定要引入的具体模块。路径/模块名是指要引入的文件或模块的位置。当只需要引入默认导出时,可以省略大括号。

、常见引入方式

在 React 中,根据引入的对象类型和位置的不同,可以分为以下几种常见的引入方式:

1.引入默认导出

如果被引入的模块使用了默认导出 (export default),则可以使用以下方式进行引入:

import 模块名 from“路径/模块名"

例如,引入一个名为 Button 的组件:

import Button from./components/Button”

2.引入命名导出

当被引入的模块使用了命名导出 (export)时,需要使用import 语句中的大括号0指定要引入的具体模块。

import[模块名]from“路径/模块名

例如,同时引入一个名为 Button 的组件和一个名为 Input 的组件:

import [ Button, Input ] from./components

3.引入所有导出

有时候,需要引入一个模块中的所有导出。可以使用以下语法:

import*as 变量名 from“路径/模块名”

例如,引入一个名为 components 的模块中的所有导出:

"./components~import *as components from

引入后,可以通过 components 变量来访问该模块中的所有导出。

三、实例应用

下面通过一个实例来演示 import 语句的使用。假设我们有一个 React 应用,需要引入一个名为 Button 的组件和一个名为Input 的组件,同时还需要引入一个名为 styles的CSS样式文件。

首先,在项目的根目录下创建一个名为 components 的文件夹,并在该文件夹下分别创建 Button.js、Input.js 两个组件文件,代码如下:

Button.js:

jsx

import React fromreact"

const Button = 0=> return <button>Click me</button>:

export default Button;

Input.js:

jsx

import React from "react”

const Input = (=>[

return <input type="text” />;

export default Input;

接下来,在项目的根目录下创建一个名为 styles 的文件夹,并在该文件夹下创建一个名为 button.css 的 CSS 样式文件,内容如下:

button.css:

css

button

backgroundcolor: blue;color: white;padding: 10px 20px;border: none;

cursor: pointer;

最后,在项目的入口文件(通常是 App.js 或 index.js)中引入所需的组件和样

式文件:

jsx

import React from"react”import Button from “./components/Button'import Input from "./components/Input";"./styles/button.css"import

const App = 0=>{return

<div>

<Button />

<Input />

</div>

export default App;

这样,我们就成功地引入了 Button 和 Input 组件以及 button.css 样式文件,可以在 App 组件中使用它们了。

四、总结

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

HELLO XF

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值