React学习笔记(四)-- 脚手架

544 阅读20分钟

GitHub仓库--源码地址

一、react脚手架

1. 脚手架的概念:

  • xxx脚手架: 用来帮助程序员快速创建一个基于xxx库的模板项目

    • 1.包含了所有需要的配置(语法检查、jsx编译、devServer…)
    • 2.下载好了所有相关的依赖
    • 3.可以直接运行一个简单效果
  • react提供了一个用于创建react项目的脚手架库: create-react-app

  • 项目的整体技术架构为: react + webpack + es6 + eslint

  • 使用脚手架开发的项目的特点: 模块化, 组件化, 工程化

2. 包管理工具

3. 创建项目并启动

第一步,切换到想创项目的目录,使用命令:create-react-app 名字,
例子: npx create-react-app my-app

使用ts模板创建:npx create-react-app my-app --template typescript

第二步,进入项目文件夹:cd 名字/cd react_1

第三步,启动项目:npm start/yarn start

4. react脚手架项目结构

1635844184(1).png

  > node_modules---依赖所在的包
    > public ---- 静态资源文件夹
    >      favicon.icon ------ 网站页签图标
    >      index.html -------- 主页面
    >      logo192.png ------- logo图
    >      logo512.png ------- logo图
    >      manifest.json ----- 应用加壳的配置文件
    >      robots.txt -------- 爬虫协议文件
    > src ---- 源码文件夹
    >      App.css -------- App组件的样式
    >      App.js --------- App组件
    >      App.test.js ---- 用于给App做测试--几乎不用
    >      index.css ------ 样式
    >      index.js ------- 入口文件
    >      logo.svg ------- logo图
    >      reportWebVitals.js
    >          --- 页面性能分析文件(需要web-vitals库的支持)
    >      setupTests.js
    >          ---- 组件单元测试的文件(需要jest-dom库的支持)

1640252034(1).png

index.html

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<!-- %PUBLIC_URL%代表public文件夹的路径 -->
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<!-- 开启理想视口,用于做移动端网页的适配 -->
<meta name="viewport"
        content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no,viewport-fit=cover">

<!-- 用于配置浏览器页签+地址栏的颜色(仅支持安卓手机浏览器--而且有兼容性问题) -->
<meta name="theme-color" content="red" />
<!-- 描述网站内容,SEO优化 -->
<meta name="description" content="Web site created using create-react-app" />
<!-- 用于指定网页添加到手机主屏幕后的图标(只用于苹果手机) -->
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<!-- 应用加壳(写手机端网页时,给其套上一个壳让其变为手机客户端APP)时的配置文件 -->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<title>React App</title>
</head>

<body>
<!-- 若浏览器不支持js则展示标签中的内容 -->
<noscript>You need to enable JavaScript to run this app.</noscript>
<!-- 容器,组件往这里面放 -->
<div id="root"></div>
</body>
</html>

index.js

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
//  <React.StrictMode>用于校验一些错误,如:字符串形式的ref(将要被弃用,不再推荐写)
ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  document.getElementById('root')
);
reportWebVitals();

5. hello,react案例

1635853641(1).png

App.jsx

//创建“外壳”组件App
import React, { Component } from 'react'
// 这里是分别暴露,不是解构赋值,得到Component,后面类式组件可以省略React.
import Hello from './components/Hello'
import Welcome from './components/Welcome'
//若组件下的文件名是index.js/index.jsx,那么可以省略

//创建并暴露App组件
//class MyComponent extends React.Component{
export default class App extends Component {
	render() {
		return (
			<div>
				<Hello />
				<Welcome />
			</div>
		)
	}
}

index.js

//引入react核心库
import React from 'react'
//引入ReactDOM
import ReactDOM from 'react-dom'
//引入App组件
import App from './App'

//渲染App到页面
ReactDOM.render(<App/>,document.getElementById('root'))

Hello组件

index.jsx

import React,{Component} from 'react'
//为了防止css类名冲突使用的方法,将index.css该为index.module.css并用from引入,
//最后在写类名是用{}书写变量,但如果用less的话可以避免冲突。
import hello from './index.module.css'

export default class Hello extends Component{
	render(){
		return <h2 className={hello.title}>Hello,React!</h2>
	}
}

index.module.css

.title{
	background-color: orange;
}

Welcome组件

index.jsx

import React,{Component} from 'react'
import './index.css'

export default class Welcome extends Component{
	render(){
		return <h2 className="title">Welcome</h2>
	}
}

index.css

.title{
	background-color: skyblue;
}

6. 功能界面的组件化编码流程(通用)

    1. 拆分组件: 拆分界面,抽取组件
    1. 实现静态组件: 使用组件实现静态页面效果
    1. 实现动态组件

    • 3.1 动态显示初始化数据

      • 3.1.1 数据类型
      • 3.1.2 数据名称
      • 3.1.2 保存在哪个组件?
    • 3.2 交互(从绑定事件监听开始)

二、 组件的组合使用-TodoList

GitHub仓库--源码地址

功能: 组件化实现此功能

  1. 显示所有todo列表
  2. 输入文本, 点击按钮显示到列表的首位, 并清除输入的文本

输入图片说明

1.拆分组件、实现静态组件,注意:className、style的写法
2.动态初始化列表,如何确定将数据放在哪个组件的state中?
    ——某个组件使用:放在其自身的state中
    ——某些组件使用:放在他们共同的父组件state中(官方称此操作为:状态提升)
3.关于父子之间通信:
    1.【父组件】给【子组件】传递数据:通过props传递
    2.【子组件】给【父组件】传递数据:通过props传递,要求父提前给子传递一个函数
4.注意defaultChecked 和 checked的区别,类似的还有:defaultValue 和 value
5.状态在哪里,操作状态的方法就在哪里

三、 脚手架配置代理

React 本身只关注于页面,并不包含发送 Ajax 请求的代码,所以一般都是集成第三方的包,或者自己封装的, 自己封装的话,比较麻烦,而且也可能考虑不全.

常用的有两个库,一个是JQuery,一个是 axios

  1. JQuery: 这个比较重,因为 Ajax 服务也只是它这个库里的一小块功能,它主要做的还是 DOM 操作,而这不利于 React ,不推荐使用
  2. axios :封装XmlHttpRequest对象的ajax,这个就比较轻,而且采用 Promise 风格,代码的逻辑会相对清晰,推荐使用

因此我们这里采用 axios 来发送客户端请求---axios文档

在脚手架中安装axios:

yarn add axios
import axios from 'axios'

以前,我们在发送请求的时候,经常会遇到一个很重要的问题:跨域!

image-20210827091119837

在我以前的学习中,基本上都需要操作后端服务器代码才能解决跨域的问题,配置请求头,利用 script,这些都需要后端服务器的配合,因此我们前端需要自己解决这个问题的话,就需要这个技术了:代理

在说代理之前,先谈谈为什么会出现跨域?

这个应该是源于浏览器的同源策略。所谓同源(即指在同一个域)就是两个页面具有相同的协议,主机和端口号, 当一个请求 URL 的协议、域名、端口三者之间任意一个与当前页面 URL 不同即为跨域 。

也就是说 xxx:3000xxx:4000 会有跨域问题,xxx:3000abc:3000 有跨域问题

那接下来我们采用配置代理的方式去解决这个问题

关于跨域的问题解决方案,在之后的文章会有总结 ~

1. 全局代理

第一种方法,我把它叫做全局代理,因为它直接将代理配置在了配置文件 package.json

"proxy":"http://localhost:5000"  
// "proxy":"请求的地址"

这样配置代理时,首先会在原请求地址上访问,如果访问不到文件,就会转发到这里配置的地址上去请求

转发

我们需要做的就是在我们的请求代码中,将请求的地址改到转发的地址,即可

//原请求,存在跨域问题
axios.get('http://localhost:5000/students')
//解决方案1:全局代理
//package.json,注意端口号,为请求的地址
"proxy":"http://localhost:5000"  
//注意端口号,将请求的地址改到转发的地址
axios.get('http://localhost:3000/students')

但是这样会有一些问题,它会先向我们请求的地址,也就是这里的 3000 端口下请求数据,如果在 3000 端口中存在我们需要访问的文件,会直接返回,不会再去转发。 同时因为这种方式采用的是全局配置的关系,导致只能转发到一个地址,不能配置多个代理

说明:

  1. 优点:配置简单,前端请求资源时可以不加任何前缀。
  2. 缺点:不能配置多个代理。
  3. 工作方式:上述方式配置代理,当请求了3000不存在的资源时,那么该请求会转发给5000 (优先匹配前端资源)

2. 单独配置

// 单独配置代理,端口号后面需要加配置的前缀,这里端口号仍为本地端口号
    axios.get('http://localhost:3000/api1/students')
    axios.get('http://localhost:3000/api2/cars')
  1. 第一步:创建代理配置文件

    src下创建配置文件:src/setupProxy.js
    

注意:这个文件只能叫这个名字,脚手架在启动的时候,会自动执行这些文件

  1. 编写setupProxy.js配置具体代理规则:

    const proxy = require('http-proxy-middleware')
    
    module.exports = function(app) {
      app.use(
        proxy('/api1', {  //api1是需要转发的请求(所有带有/api1前缀的请求都会转发给5000)
          target: 'http://localhost:5000', //配置转发目标地址(能返回数据的服务器地址)
          changeOrigin: true, //控制服务器接收到的请求头中host字段的值
          /*
          	changeOrigin设置为true时,服务器收到的请求头中的host为:localhost:5000
          	changeOrigin设置为false时,服务器收到的请求头中的host为:localhost:3000
          	changeOrigin默认值为false,但我们一般将changeOrigin值设为true
          */
          pathRewrite: {'^/api1': ''} //去除请求前缀,保证交给后台服务器的是正常请求地址(必须配置)
        }),
        proxy('/api2', { 
          target: 'http://localhost:5001',
          changeOrigin: true,
          pathRewrite: {'^/api2': ''}
        })
      )
    }
    

说明:

  1. 优点:可以配置多个代理,可以灵活的控制请求是否走代理。

  2. 缺点:配置繁琐,前端请求资源时必须加前缀。

    • target 属性用于配置转发目标地址,也就是我们数据的地址
    • changeOrigin 属性用于控制服务器收到的请求头中 host 字段,可以理解为一个伪装效果,为 true 时,收到的 host 就为请求数据的地址
    • pathRewrite 属性用于去除请求前缀,因为我们通过代理请求时,需要在请求地址前添加一个标志,但是实际的地址是不存在这个标志的,所以我们一定要去除这个前缀,这里采用的有点类似于正则替换的方式

四、 案例—github用户搜索

GitHub仓库--源码地址

1. 解构赋值的高级写法

获取this.keyWordElement.value 的值:

// const value = this.keyWordElement.value  等于下面的写法
// const { value } = this.keyWordElement 等于下面的写法
// const { keyWordElement: { value } } = this
//这种写法只声明了最后一个value,keyWordElement未被定义
//(连续解构赋值+重命名,最后结构的值可以用:重命名)
const { keyWordElement: { value: keyWord } } = this

输入图片说明

请求地址:api.github.com/search/user…

五、 消息发布订阅

在昨天写的 Github 案例中,我们采用的是 axios 发送请求来获取数据,同时我们需要将数据从 Search 中传入给 App,再由 App 组件再将数据传递给 List 组件,这个过程会显得多此一举。同时我们要将 state 状态存放在 App 组件当中,但是这些 state 状态都是在 List 组件中使用的,在 Search 组件中做的,只是更新这些数据,那这样也会显得很没有必要,我们完全可以将 state 状态存放在 List 组件中,但是这样我们又会遇到技术难题,兄弟组件间的数据通信。那这里我们就学习一下如何利用消息订阅发布来解决兄弟组件间的通信

要解决上面的问题,我们可以借助发布订阅的机制,我们可以将 App 文件中的所有状态和方法全部去除,因为本来就不是在 App 组件中直接使用这些方法的,App 组件只是一个中间媒介而已

我们先简单的说一下消息订阅和发布的机制

就拿我们平常订杂志来说,我们和出版社说我们要订一年的足球周刊,那每次有新的足球周刊,它都会寄来给你。

换到代码层面上,我们订阅了一个消息假设为 A,当另一个人发布了 A 消息时,因为我们订阅了消息 A ,那么我们就可以拿到 A 消息,并获取数据

那我们要怎么实现呢?

首先引入 pubsub-js

我们需要先安装这个库

yarn add pubsub-js

引入这个库

import PubSub from 'pubsub-js'

订阅消息

我们通过 subscribe 来订阅消息,它接收两个参数,第一个参数是消息的名称,第二个是消息成功的回调,回调中也接受两个参数,一个是消息名称,一个是返回的数据

var token = PubSub.subscribe('search',(msg,data)=>{
  console.log(msg,data);
})

发布消息

我们采用 publish 来发布消息,用法如下

PubSub.publish('search',{name:'tom',age:18})

有了这些基础,我们可以完善我们之前写的 GitHub 案例

将数据的更新通过 publish 来传递,例如在发送请求之前,我们需要出现 loading 字样

// 之前的写法
this.props.updateAppState({ isFirst: false, isLoading: true })
// 改为发布订阅方式
PubSub.publish('search',{ isFirst: false, isLoading: true })

这样我们就能成功的在请求之前发送消息,我们只需要在 List 组件中订阅一下这个消息即可,并将返回的数据用于更新状态即可

componentDidMount(){
// 在挂载完成后订阅消息,回调函数中第一个参数也是消息名,如果不想用可以用_占位
        this.token = PubSub.subscribe('search',(_,stateObj)=>{
                this.setState(stateObj)
        })
}
componentWillUnmount(){
        // 当组件即将被销毁掉的时候取消订阅
        PubSub.unsubscribe(this.token)
}

六、 全局事件总线

events常用的API

  • 创建EventEmitter对象:eventBus
  • 发出事件:eventBus.emit("事件名称", 参数列表);
  • 监听事件:eventBus.addListener("事件名称", 监听函数)
  • 移除事件:eventBus.removeListener("事件名称", 监听函数); 首先引入 events

我们需要先安装这个库

yarn add events

引入这个库

import { EventEmitter } from 'events';

使用案例:

import React, { PureComponent } from "react";

import { EventEmitter } from "events";

// 事件总线: event bus,利用导出的EventEmitter创建对象
const eventBus = new EventEmitter();

class Home extends PureComponent {
  // 要在组件挂载时进行事件监听
  componentDidMount() {
    //eventBus.addListener(事件名称,监听函数);
    eventBus.addListener("sayHello", this.handleSayHelloListener);
  }

  // 要在组件即将卸载时取消时事件监听
  componentWillUnmount() {
    // eventBus.removeListener(事件名称, 监听函数);
    eventBus.removeListener("sayHello", this.handleSayHelloListener);
  }

  // 传递几个参数就用几个变量接收
  handleSayHelloListener(num, message) {
    console.log(num, message);
  }

  render() {
    return <div>Home</div>;
  }
}

class Profile extends PureComponent {
  // 发送数据
  emmitEvent() {
    // eventBus.emit(事件名称,参数列表);
    eventBus.emit("sayHello", 123, "Hello Home");
  }

  render() {
    return (
      <div>
        Profile
        <button onClick={(e) => this.emmitEvent()}>点击了profile按钮</button>
      </div>
    );
  }
}

export default class App extends PureComponent {
  render() {
    return (
      <div>
        <Home />
        <Profile />
      </div>
    );
  }
}

七、 Fetch

1. github.github.io/fetch/

2. segmentfault.com/a/119000000…

首先 fetch 也是一种发送请求的方式,它是在 xhr 之外的一种,我们平常用的 Jquery 和 axios 都是封装了 xhr 的第三方库,而 fetch 是官方自带的库,同时它也采用的是 Promise 的方式,大大简化了写法

如何使用呢?

fetch('http://xxx')
  .then(response => response.json())
  .then(json => console.log(json))
  .catch(err => console.log('Request Failed', err)); 

它的使用方法和 axios 非常的类似,都是返回 Promise 对象,但是不同的是, fetch 关注分离,它在第一次请求时,不会直接返回数据,会先返回联系服务器的状态,在第二步中才能够获取到数据

我们需要在第一次 then 中返回 response.json() 因为这个是包含数据的 promise 对象,再调用一次 then 方法即可实现

但是这么多次的调用 then 并不是我们所期望的,相信看过之前生成器的文章的伙伴,已经有了想法。

我们可以利用 asyncawait 配合使用,来简化代码

可以将 await 理解成一个自动执行的 then 方法,这样清晰多了

async function getJSON() {
  let url = 'https://xxx';
  try {
    let response = await fetch(url);
    const data = await reasponse.json();
  } catch (error) {
    console.log('Request Failed', error);
  }
}

最后关于错误对象的获取可以采用 try...catch 来实现

关于 fetch 的更多内容

八、React中axios使用和封装

1. 安装并使用

GitHub: github.com/axios/axios

axios中文文档:axios-js.com/zh-cn/docs/…

yarn add axios
import axios from 'axios';

九、React中的样式

1. 内联样式

内联样式是官方推荐的一种css样式的写法: style 接受一个采用小驼峰命名属性的 JavaScript 对象,而不是 CSS 字符串; 并且可以引用state中的状态来设置相关的样式;

内联样式的优点:

  1. 内联样式, 样式之间不会有冲突
  2. 可以动态获取当前state中的状态 内联样式的缺点
  3. 写法上都需要使用驼峰标识
  4. 某些样式没有提示
  5. 大量的样式, 代码混乱
  6. 某些样式无法编写(比如伪类/伪元素) 所以官方依然是希望内联合适和普通的css来结合编写;
import React, { PureComponent } from "react";

export default class App extends PureComponent {
  state = {
    color: "purple",
  };

  render() {
    const pStyle = {
      color: this.state.color,
      textDecoration: "underline",
    };

    return (
      <div>
        <h2 style={{ fontSize: "50px", color: "red" }}>我是标题</h2>
        <p style={pStyle}>我是一段文字描述</p>
      </div>
    );
  }
}

2. 普通的css

  • 普通的css我们通常会编写到一个单独的文件,之后再进行引入。
  • 这样的编写方式和普通的网页开发中编写方式是一致的。
  • 如果我们按照普通的网页标准去编写,那么也不会有太大的问题。
  • 但是组件化开发中我们总是希望组件是一个独立的模块,即便是样式也只是在自己内部生效,不会相互影响;
  • 但是普通的css都属于全局的css,样式之间会相互影响;
  • 这种编写方式最大的问题是样式之间会相互层叠掉

1640680248(1).png

//app
import React, { PureComponent } from 'react';

import './style.css';

import Home from '../home';
import Profile from '../profile';

export default class App extends PureComponent {
  render() {
    return (
      <div id="app">
        App
        <h2 className="title">我是App的title</h2>
        <Home/>
        <Profile/>
      </div>
    )
  }
}
//style.css
#app > .title {
  color: blue;
}

//home
import React, { PureComponent } from 'react';

import './style.css';

export default class Home extends PureComponent {
  render() {
    return (
      <div className="home">
        <h2 className="title">我是home的标题</h2>
        <div className="banner">
          <span>轮播图</span>
        </div>
      </div>
    )
  }
}
//style.css
.home .title {
  font-size: 30px;
  color: red;
}

.home .banner {
  color: orange;
}

//profile
import React, { PureComponent } from 'react';

import './style.css';

export default class Profile extends PureComponent {
  render() {
    return (
      <div className="profile">
          <h2 className="title">我是Profile的标题</h2>
          <ul className="settings">
            <li>设置列表1</li>
            <li>设置列表2</li>
            <li>设置列表3</li>
          </ul>
      </div>
    )
  }
}
//style.css
.profile .title {
  color: yellow;
}

3. css modules

  • css modules并不是React特有的解决方案,而是所有使用了类似于webpack配置的环境下都可以使用的。
  • 但是,如果在其他项目中使用个,那么我们需要自己来进行配置,比如配置webpack.config.js中的modules: true等。
  • React的脚手架已经内置了css modules的配置:.css/.less/.scss 等样式文件都修改成 .module.css/.module.less/.module.scss 等; 之后就可以引用并且进行使用了;
  • css modules确实解决了局部作用域的问题,也是很多人喜欢在React中使用的一种方案。 但是这种方案也有自己的缺陷:
  • 引用的类名,不能使用连接符(.home-title),在JavaScript中是不识别的;
  • 所有的className都必须使用{style.className} 的形式来编写;
  • 不方便动态来修改某些样式,依然需要使用内联样式的方式;

1640681213(1).png

//app
import React, { PureComponent } from 'react';

import appStyle from './style.module.css';

import Home from '../home';
import Profile from '../profile';

export default class App extends PureComponent {
  render() {
    return (
      <div id="app">
        App
        <h2 className={appStyle.title}>我是App的title</h2>
        <Home/>
        <Profile/>
      </div>
    )
  }
}

//style.module.css
.title {
  color: blue;
}

//home
import React, { PureComponent } from 'react';

import homeStyle from './style.module.css';

export default class Home extends PureComponent {
  render() {
    return (
      <div className="home">
        <h2 className={homeStyle.title}>我是home的标题</h2>
        <div className={homeStyle.banner}>
          <span>轮播图</span>
        </div>
      </div>
    )
  }
}

//style.module.css
.title {
  font-size: 30px;
  color: red;
}

.banner {
  color: orange;
}

//profile
import React, { PureComponent } from 'react';

import style from './style.module.css';

export default class Profile extends PureComponent {
  constructor(props) {
    super(props);

    this.state = {
      color: "purple"
    }
  }

  render() {
    return (
      <div className="profile">
        <h2 className={style.title} style={{color: this.state.color}}>我是Profile的标题</h2>
        <ul className={style.settings}>
          <li className={style.settingItem}>设置列表1</li>
          <li>设置列表2</li>
          <li>设置列表3</li>
        </ul>
      </div>
    )
  }
}

//style.module.css
.title {
  color: yellow;
}

4. CSS in JS

  • 实际上,官方文档也有提到过CSS in JS这种方案:
    1. “CSS-in-JS” 是指一种模式,其中 CSS 由 JavaScript 生成而不是在外部文件中定义;
    2. 注意此功能并不是 React 的一部分,而是由第三方库提供。 React 对样式如何定义并没有明确态度;
  • 在传统的前端开发中,我们通常会将结构(HTML)、样式(CSS)、逻辑(JavaScript)进行分离。
    1. 但是在前面的学习中,我们就提到过,React的思想中认为逻辑本身和UI是无法分离的,所以才会有了JSX的语法。
    2. 样式呢?样式也是属于UI的一部分;
    3. 事实上CSS-in-JS的模式就是一种将样式(CSS)也写入到JavaScript中的方式,并且可以方便的使用JavaScript的状态;
    4. 所以React有被人称之为 All in JS;

认识styled-components

  • CSS-in-JS通过JavaScript来为CSS赋予一些能力,包括类似于CSS预处理器一样的样式嵌套、函数定义、逻辑复用、动态修 改状态等等;
  • 虽然CSS预处理器也具备某些能力,但是获取动态状态依然是一个不好处理的点;
  • 所以,目前可以说CSS-in-JS是React编写CSS最为受欢迎的一种解决方案; 目前比较流行的CSS-in-JS的库有哪些呢
  • styled-components
  • emotion
  • glamorous 目前可以说styled-components依然是社区最流行的CSS-in-JS库,

安装styled-components:

yarn add styled-components

ES6标签模板字符串

ES6中增加了模板字符串的语法,这个对于很多人来说都会使用。 但是模板字符串还有另外一种用法:标签模板字符串(Tagged Template Literals)。

我们一起来看一个普通的JavaScript的函数:

1640682147(1).png

正常情况下,我们都是通过 函数名() 方式来进行调用的,其实函数还有另外 一种调用方式: 如果我们在调用的时候插入其他的变量:

  • 模板字符串被拆分了;
  • 第一个元素是数组,是被模块字符串拆分的字符串组合;
  • 后面的元素是一个个模块字符串传入的内容; 在styled component中,就是通过这种方式来解析模块字符串,最终生成 我们想要的样式的
<!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>
  
  <script>
    // 1.模板字符串的基本使用
    const name = "why";
    const age = 18;
    const message = `my name is ${name}`;

    // 2.标签模板字符串: 可以通过模板字符串的方式对一个函数进行调用
    function test(...args) {
      console.log(args);
    }

    // test("aaa", "ccc");

    // test`aaaa`;
    test`my name is ${name}, age is ${age}`;

    test`
      font-size: 15px;
      color: red;
    `
  </script>
</body>
</html>

1640684547(1).png

styled的基本使用

安装vscode插件:vscode-styled-components

styled-components的本质是通过函数的调用,最终创建出一个组件:

  • 这个组件会被自动添加上一个不重复的 class;
  • styled-components会给该class添加相关的样式; 另外,它支持类似于CSS预处理器一样的样式嵌套:
  • 支持直接子代选择器或后代选择器,并且直接编写样式;
  • 可以通过&符号获取当前元素;
  • 直接伪类选择器、伪元素等;

1640685280(1).png

案例

//index.jsx
import React, { PureComponent } from 'react';

import { 
  HomeWrapper,
  TitleWrapper
} from "./style";

export default class Home extends PureComponent {
  render() {
    return (
      <HomeWrapper>
        <TitleWrapper>我是home的标题</TitleWrapper>
        <div className="banner">
          <span>轮播图1</span>
          <span className="active">轮播图2</span>
          <span>轮播图3</span>
          <span>轮播图4</span>
        </div>
      </HomeWrapper>
    )
  }
}

//style.js
import styled from "styled-components";

export const HomeWrapper = styled.div`
  font-size: 22px;
  color: red;

  .banner {
    background-color: blue;

    span {
      color: #fff;
      /* &.表示既是span又是.active */
      &.active {
        color: red;
      }

      &:hover {
        color: orange;
      }

      &::after {
        content: "zgc";
      }
    }

    /* .active {
      color: #f00;
    } */
  }
`;

export const TitleWrapper = styled.h2`
  text-decoration: underline;
  /* 这里是读取下方主题的属性 */
  color: ${(props) => props.theme.themeColor};
  font-size: ${(props) => props.theme.fontSize};
`;

1640689075(1).png

props、attrs属性

通过props、attrs可以设置属性

  • props穿透

1640682329(1).png

  • props可以被传递给styled组件
    1. 获取props需要通过${}传入一个插值函数,props会作为该函数的参数;
    2. 这种方式可以有效的解决动态样式的问题;
  • 添加attrs属性 : image.png

案例

import React, { PureComponent } from "react";
import styled from "styled-components";

/**
 * 特点:
 *  1.props穿透:可以在styled组件中传入属性,会穿透给原生html
 *  2.attrs的使用
 *  3.传入state作为props属性
 */
const HYLi = styled.li`
  color: ${(props) => props.color};
  font-size: 22px;
`;

const HYInput = styled.input.attrs({
  placeholder: "zgc",
  bColor: "red",
  // 在这里面写的属性会和在styled组件中通过props传递的属性会结合起来传递到``中的props
})`
  background-color: lightblue;
  border-color: ${(props) => props.bColor};
  color: ${(props) => props.color};
  //点击后边框变橙色
  &:focus {
    outline-color: orange;
  }
`;

export default class Profile extends PureComponent {
  state = {
    color: "red",
  };

  render() {
    return (
      <div>
        <HYInput type="password" color={this.state.color} />
        <h2>我是Profile的标题</h2>
        <ul>
          <HYLi color={this.state.color}>设置列表1</HYLi>
          <li>设置列表2</li>
          <li>设置列表3</li>
        </ul>
      </div>
    );
  }
}

1640690434(1).png

styled高级特性

  • 支持样式的继承

1640682412(1).png

  • styled设置主题

1640682421(1).png

案例

import React, { PureComponent } from "react";

import Home from "../home";
import Profile from "../profile";
import styled, { ThemeProvider } from "styled-components";

const HYButton = styled.button`
  padding: 10px 20px;
  border-color: blue;
  color: red;
`;

// 不继承
// const HYPrimaryButton = styled.button`
//   padding: 10px 20px;
//   border-color: blue;
//   color: #fff;
//   background-color: green;
// `

// 继承,并重写需要更改的属性以及添加新属性
const HYPrimaryButton = styled(HYButton)`
  color: #fff;
  background-color: green;
`;

export default class App extends PureComponent {
  render() {
    return (
    //给其包裹的组件设置主题,通过props.theme.xxx读取
      <ThemeProvider theme={{ themeColor: "yellow", fontSize: "50px" }}>
        <Home />
        <hr />
        <Profile />
        <HYButton>我是普通的按钮</HYButton>
        <HYPrimaryButton>我是主要的按钮</HYPrimaryButton>
      </ThemeProvider>
    );
  }
}

vue中添加class

vue中添加class是一件非常简单的事情: 你可以通过传入一个对象: 1640682485(1).png

你也可以传入一个数组: 1640682498(1).png

甚至是对象和数组混合使用:

1640682546(1).png

React中添加class

React在JSX给了我们开发者足够多的灵活性,你可以像编写JavaScript代码一样,通过一些逻辑来决定是否添加某些class:

1640682602(1).png

这个时候我们可以借助于一个第三方的库:classnames 很明显,这是一个用于动态添加classnames的一个库.

yarn add classnames 1640682613(1).png

import React, { PureComponent } from "react";

import classNames from "classnames";

// 知识点: classnames
export default class App extends PureComponent {
  state = {
    isActive: true,
    isBar: false,
  };

  render() {
    const { isActive, isBar } = this.state;
    const errClass = "error";
    const warnClass = 10;

    return (
      <div>
        {/* 原生React中添加class方法 */}

        {/* 给一个标签添加多个类名 */}
        <h2 className={"foo bar active title"}>我是标题1</h2>
        {/* 注意:用js动态判断时要在引号内加空格,否则类名连在一起 */}
        <h2 className={"title" + (isActive ? " active" : "")}>我是标题2</h2>
        {/* 或者用数组方法,用 " "分隔 */}
        <h2 className={["title", isActive ? "active" : ""].join(" ")}>
          我是标题3
        </h2>

        {/* classnames库添加class */}

        {/* 给一个标签添加多个类名 */}
        <h2 className="foo bar active title">我是标题4</h2>
        {/* 给一个标签添加多个类名 */}
        <h2 className={classNames("foo", "bar", "active", "title")}>
          我是标题5
        </h2>
        {/* classNames({ 类名: 布尔值( 显示类名: 布尔值为true) },必定显示的类名) */}
        <h2 className={classNames({ active: isActive, bar: isBar }, "title")}>
          我是标题6
        </h2>
        <h2
          className={classNames("foo", errClass, warnClass, {
            active: isActive,
          })}
        >
          我是标题7
        </h2>
        <h2 className={classNames(["active", "title"])}>我是标题8</h2>
        <h2 className={classNames(["active", "title", { bar: isBar }])}>
          我是标题9
        </h2>
      </div>
    );
  }
}

1640693160(1).png