前言

eg:代表代码对照 若文章有误,欢迎读者留言反馈

CSS-in-JS技术

CSS-in-JS简介

CSS-in-JS是一种技术,而不是一个具体的库实现。简单来说CSS-in-JS就是将应用的CSS样式写在JavaScript文件里面,而不是独立为一些css,scss或less之类的文件,这样你就可以在CSS中使用一些属于JS的诸如模块声明,变量定义,函数调用和条件判断等语言特性来提供灵活的可扩展的样式定义。CSS-in-JS在React社区的热度是最高的,这是因为React本身不会管用户怎么去为组件定义样式的问题,而Vue有属于框架自己的一套定义样式的方案。

styled-components 应该是CSS-in-JS最热门的一个库,通过styled-components,你可以使用ES6的标签模板字符串语法,为需要styled-Component定义一系列CSS属性,当该组件的JS代码被解析执行的时候,styled-components会动态生成一个CSS选择器,并把对应的CSS样式通过style标签的形式插入到head标签里面。动态生成的CSS选择器会有一小段哈希值来保证全局唯一性来避免样式发生冲突。

普通外链样式写法 css less sass都是这么用

import './demo.css'

1
2
3
4
5
6
7
8
9
10
11
12
13
// 引入css模块
import './demo.css'

class StyledDemo extends Component {
render() {
return (
<div>
<h3>StyledDemo</h3>
<div className='box1'>hello world</div>
</div>
)
}
}
1
2
3
4
.box1{
background-color: pink;
font-size: 18px;
}

学习使用CSS-in-JS

用js代码来取代css样式代码,常见,样式文件后缀名应为js或者jsx

安装插件

npm i -S styled-components

定义样式,引入styled,css-in-js技术,样式写在js文件或者jsx文件中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 引入styled,css-in-js技术,样式写在js文件或者jsx文件中
import styled from 'styled-components'

// DivStyled是导出的样式组件名 styled.div将要替换一个div
export const DivStyled = styled.div`
background-color: red;
font-size: 20px;
`
// 第二个div样式组件继承DivStyled eg:有公共样式的时候,可以直接继承过来
export const DivStyled2 = styled(DivStyled)`
/* background-color: red;
font-size: 20px; */
height: 40px;
`

组件中使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import React, { Component } from 'react'

// 引入styled.css中的样式组件名,作为标签使用
import { DivStyled, DivStyled2 } from './demo1'

class StyledDemo extends Component {
render() {
return (
<div>
<h3>StyledDemo</h3>
<DivStyled>123</DivStyled>
<DivStyled2>1234</DivStyled2>
</div>
)
}
}

export default StyledDemo

设置传递属性值过来,支持变量、默认值
组件的某个变量传递给样式组件,外链的样式 css、sass、less都不支持,只要CSS-in-JS可以

样式文件

1
2
3
4
5
6
7
// 引入styled,css-in-js技术,样式写在js文件或者jsx文件中
import styled from 'styled-components'

export const DivStyled3 = styled.div`
background-color: blue;
font-size: ${props => props.font || '18px'}; //如果有参数传递过来就使用,如果没有就使用默认值
`

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 引入styled.css中的样式组件名,作为标签使用
import { DivStyled3 } from './demo1'

class StyledDemo extends Component {
state = {
font: '32px' //js组件中的变量
}
render() {
return (
<div>
<h3>StyledDemo</h3>
{/* 支持js组件中的变量【DivStyled3就相当于子组件,传递给样式组件中】 */}
<DivStyled3 font={this.state.font}>12345</DivStyled3>
</div>
)
}
}

redux的模块化

现在我们已经能够很好的进行redux的数据管理,但是有一个缺点就是所有的代码都写在一个文件中,需要按照模块化开发的规则进行对代码拆分。

目录结构

redux模块化目录

整个home页面中的reducer逻辑会合并到store中的reducer中,同时home页面的actions、reducer均依赖actionTypes的自定义规则,home调用的是actions中导出的方法

安装插件
react-redux就是redux给我们提供一些高阶组件[引入connect组件],能解决的问题是:使用它以后我们不需要在每个组件中再去手动订阅数据的更新了。

npm i -S redux react-redux

定义Provider,在程序主文件index.js文件中,定义Provider,此处类似于之前跨组件通信处的Provider一样,旨在让全局的组件共享store中的数据

index.js文件

1
2
3
4
5
6
7
8
9
10
11
12
13
import { Provider } from 'react-redux'
import store from './store'
import { HashRouter } from 'react-router-dom'
import App3 from './App3'

ReactDOM.render(
<Provider store={store}>
<HashRouter>
<App3 />
</HashRouter>
</Provider>,
document.getElementById('root')
)

App3.js文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import React, { Component } from 'react'
import { Switch, Route } from 'react-router-dom'
import Home from './03views/home/Home.jsx'
export default class App3 extends Component {
render() {
return (
<div>
<h3>App3</h3>
<Switch>
<Route path="/home" component={Home} />
</Switch>
</div>
)
}
}

在store文件夹中创建store.js文件,创建store并抛出store(全局共享数据)

1
2
3
4
import { createStore } from 'redux'
import reducer from './reducer'
const store = createStore(reducer)
export default store

在store目录下的reducer.js文件中合并每个页面reducer.js逻辑

1
2
3
4
5
6
7
8
9
10
// 引入combineReducers方法,把所有的模块中的reducer(逻辑)做一个合并 然后返回给store
import { combineReducers } from 'redux'

// 引入home页面中的逻辑
import homeReducer from '../03views/home/reducer'

const reducer = combineReducers({ // 模块化
home: homeReducer, // home是命名空间 state.home.xxx
})
export default reducer

到actionTypes.js文件中定义规则

1
export const increType = 'home/afaefafefe' // 规则这里自定义,没有限制

书写reducer逻辑【store中的reducer是负责整合,这个写在home目录下的reducer.js中】

1
2
3
4
5
6
7
8
9
10
11
12
13
import { increType } from './actionTypes'
const defaultState = {
num: 100
}
const reducer = (state = defaultState, actions) => {
// if可以替换为switch
if (actions.type === increType) {
return { num: state.num + actions.incre } // +逻辑
}
return state
}

export default reducer // 抛出

书写actions.js文件与其仓库reducer.js对接

1
2
3
4
5
6
7
8
9
// 引入自定义规则
import { increType } from './actionTypes'
// type为规则,incre是负责传递实参到reducer.js
export const increActions = (incre) => {
return {
type: increType,
incre
}
}

引入到Home组件中使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import React, { Component } from 'react'
// 引入connect订阅数据更新
import { connect } from 'react-redux'
// 引入increActions方法,返回的是对象包含触发规则
import { increActions } from './actions'

class Home extends Component {
render() {
return (
<div>
<h4>Home</h4>
<p>{this.props.mynum}</p>
<button onClick={() => this.props.handleClick(1)}>+1</button>
</div>
)
}
}

const mapStateToProps = state => ({
mynum: state.home.num // 中间插上命名空间的名字
})
const mapDispatchToProps = dispatch => ({
handleClick(a) {
dispatch(increActions(a))
}
})
export default connect(mapStateToProps, mapDispatchToProps)(Home)

immutable.js

Immutable.js出自Facebook,是最流行的不可变数据结构的实现之一。它实现了完全的持久化数据结构,使用结构共享。所有的更新操作都会返回新的值,但是在内部结构是共享的,来减少内存占用(和垃圾回收的失效)。

持久化数据结构:这里说的持久化是用来描述一种数据结构,指一个数据,在被修改时,仍然能够保持修改前的状态,即不可变类型。

结构共享:ImmutableJS使用先进的tries(字典树)技术实现结构共享来解决性能问题,当我们对一个Immutable对象进行操作的时候,ImmutableJS会只clone该节点以及它的祖先节点,其他保持不变,这样可以共享相同的部分,大大提高性能。

使用immutable优缺点:

  • 优点:

    • 降低mutable带来的复杂度
    • 节省内存
    • 历史追溯性
    • 拥抱函数式编程
  • 缺点:

    • 需要重新学习api
    • 资源包大小增加(源码5000行左右)
    • 容易与原生对象混淆:由于api与原生不同,混用的话容易出错

安装插件

npm i -S immutable

常用Api

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
// Map(): 原生object转Map对象

const map1 = Map({ a: 1, b: 2, c: 3})
const map2 = Map({ a: 1, b: 2, c: 3})

console.log(map1 === map2) // false
console.log(map1.equals(map2)) // true

// List(): 原生array转List对象

const list1 = List([1, 2]);
const list2 = list1.push(3, 4, 5);
// 获取值
console.log(list2.get(0));

const list1 = List([ 1, 2, 3 ]);
const list2 = List([ 4, 5, 6 ]);
const list3 = list2.concat(list1);
console.log(list3.toArray())

// fromJS(): 原生js转immutable对象
const imState = fromJS({
name: 'lisi',
users: ['aa', 'bb']
})

// 获取数据
console.log(imState.get('users').get(1))
console.log(imState.getIn(['users', 0]))

// toJS(): immutable对象转原生js 不推荐使用
const state = imState.toJS()
console.log(state);

// set/setIn/update/updateIn 修改数据
const newState = imState.set('name', 'zhangsan')
const newState = imState.setIn(['name'], 'zhangsan')
const newState = imState.update('count', value => value + 1)
const newState = imState.updateIn(['count'], value => value + 1)

注意:组件中state使用的时候不建议直接把state转换成immutable对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import React, { Component } from 'react'
import immutable from 'immutable'
export default class ImmuDemo extends Component {
// 不建议你在State中使用ImmutableJS。一般用在Reducer层。
// 因为 state 本身必须是 plain object,但是里面的值可以是 immutable 的数据
constructor(props){
super(props)
this.username = React.createRef()
this.state = {
arr:immutable.fromJS([
{id:1,name:'大壮'},
{id:2,name:'二翠'},
])
}
}
handleClick = () => {
var obj = {
id: new Date().getTime(),
name:this.username.current.value
}
this.setState({
arr:immutable.fromJS([...this.state.arr.toArray(),obj])
})
}
render() {
return (
<div>
<h3>ImmuDemo案例</h3>
<input type="text" ref={ this.username }/>
<button onClick={ this.handleClick }>添加</button>
<ul>{ this.state.arr.map(item=>{
return <li key={ item.get('id') }>姓名:{ item.get('name') }</li>
})}</ul>
</div>
)
}
}