前端学习之路


  • 首页

  • 归档

  • 分类

  • 标签

  • 搜索
close
小朱

小朱

前端学习之路

168 日志
37 分类
37 标签
RSS
GitHub
友情链接
  • 极客学院
  • caniuse
  • codepen
  • JS Bin
  • Babel在线编译
  • Iconfinder图标
  • 在线JSON格式化
  • 智能图像压缩

CSS-布局基础1

发表于 2016-09-18   |   分类于 CSS

display属性

大多数元素的默认值通常是 block 或 inline 。一个 block 元素通常被叫做块级元素。一个 inline 元素通常被叫做行内元素。注意是控制当前元素的,而不是其内部元素。

none:

  • 在不删除元素的情况下隐藏元素,不会保留元素本该显示的空间
  • visibility:hidden 会保留元素的空间

block:

  • 前后有换行符,如不指定宽度,会尽可能撑满容器
  • 高度,行高及顶和底边距都可控制
  • 常见块级元素:div、p、form、header、footer、section等

inline(默认):

  • 前后没有换行符、不撑满容器
  • 高,行高及顶和底边距不可改变
  • 包裹一些文字而不会打乱段落的布局

inline-block:

  • 将对象呈递为内联对象,但是对象的内容作为块对象呈递(准确地说,应用此特性的元素呈现为内联对象,周围元素保持在同一行,但可以设置宽度和高度地块元素的属性)
  • 可以实现浮动(float)的效果,如果HTML源代码中元素之间有空格,那么元素之间会产生空隙,使用float不会有空隙;但不能实现float的文字环绕图片
  • 如果不设置width和height,会按元素的内容大小显示
  • 这一行的高度为该行内最大高度的元素高度,vertical-align属性会影响到元素在垂直方向的对齐方式

list-item:

  • 此元素会作为列表显示,如果不设置width和height,每个列表项尽可能撑满容器

flex:

  • 详见下文

display其它属性值详细列表


Flexbox 布局

参考阮一峰的网络日志:
《Flex 布局教程:语法篇》
《Flex 布局教程:实例篇》

display: flex |inline-flex

指定Flex布局后,子元素的float、clear和vertical-align属性将失效

几个基本概念: 容器、项目、主轴、交叉轴

容器的属性

flex-direction

  • 主轴的方向(即项目的排列方向)
  • row | row-reverse | column | column-revers

flex-wrap

  • owrap | wrap | wrap-reverse
  • 一条轴线排不下,如何换行

flex-flow

  • flex-direction属性和flex-wrap属性的简写形式
  • 默认值为row nowrap

justify-content

  • 项目在主轴上的对齐方式
  • flex-start | flex-end | center | space-between | space-around

align-items

  • 项目在交叉轴上的对齐方式
  • flex-start | flex-end | center | baseline | stretch

align-content

  • 多根轴线的对齐方式。如果项目只有一根轴线,该属性不起作用
  • flex-start | flex-end | center | space-between | space-around | stretch

项目的属性

order

  • 项目的排列顺序,数值越小,排列越靠前
  • 整数,默认为0

flex-grow

  • 项目的放大比例,0表示如果存在剩余空间,也不放大
  • 整数,默认为0
  • 如果所有项目的属性都为1,则它们将等分剩余空间;如果一个项目的属性为2,其他项目都为1,则前者占据的剩余空间将比其他项多一倍

flex-shrink

  • 项目的缩小比例,1表示如果空间不足,该项目将缩小
  • 非负整数,默认为1
  • 如果所有项目的属性都为1,当空间不足时,都将等比例缩小;如果一个项目的属性为0,其他项目都为1,则空间不足时,前者不缩小

flex-basis

  • 在分配多余空间之前,项目占据的主轴空间,浏览器根据这个属性,计算主轴是否有多余空间
  • [length] | auto,默认为auto
  • 可以设为跟width或height属性一样的值,则项目将占据固定空间

flex

  • flex-grow, flex-shrink 和 flex-basis的简写,后两个属性可选
  • 默认值为 0 1 auto
  • none (0 0 auto) 表示无论窗口如何变化宽度不变
  • auto (1 1 auto) 表示空间足够时放大,空间不足时缩小
  • initial (0 1 auto) 表示空间足够时不放大,空间不足时缩小
  • 建议优先使用这个属性,因为浏览器会推算相关值

align-self

  • 允许单个项目有与其他项目不一样的对齐方式,可覆盖align-items属性
  • auto | flex-start | flex-end | center | baseline | stretch,默认值为auto
  • auto 表示继承父元素的align-items属性,如果没有父元素,则等同于stretch

多列布局

CSS3多列布局可以自动将内容按指定的列数排列,这种特性实现的布局效果和报纸、杂志排版非常相似。
常用属性如下:

column-width:

auto 默认值,元素多列的列宽由其它属性决定,没有显示设置的情况下,将默认显示一列
[length] 数值和长度单位组成,非负;如果单独使用,容器超出列宽时显示多列,小于列宽时显示一列

column-count:

auto 默认值,表示元素只有一列,依靠浏览器计算自动设置
[integer] 正整数值,定义想要的列数和最大列数,当容器没有足够空间来包含具有指定宽度的列数,元素列数会自动往下计算;如果单独使用,显示固定列数
列数 = (容器宽度 - 列间距)/列宽 向下取整

columns:

column-width和column-count的缩写

column-gap:

normal 默认值,一般情况相当于1em
[length] 浮点数据和单位标识符组成的长度值,非负,常用px、em单位的任何整数值;
如果同时设置了column-width属性,可能会导致列被撑破,使列数减1

column-rule:

定义列之间的边框宽度、样式、颜色,类似border属性,但不占用任何空间位置
[column-rule-width]|[column-rule-style]|[column-rule-color]
默认值分别为 medium | none | 前景色color值
如果column-rule-width宽度超过列间距,列边框就会消失

column-span:

定义一个分列元素中的子元素能跨多少列
none 默认值,表示不跨任何列
all 表示跨所有列,并定位在列的Z轴之上

column-fill:

定义多列中每一列的高度是否统一
auto 默认值,各列的高度随其内容的变化自动变化
balance 各列的高度将会根据内容最多的一列的高度进行统一

Ant Design 组件学习经验

发表于 2016-09-14   |   分类于 Ant Design

Tree组件

参考 TreeSelect

异步加载数据

使用redux时,TreeSelect异步加载数据需要通过触发action调用后台获取数据,根据reducer更新的state显示出加载的数据。需添加loadData属性,值为一个方法,如onLoadData,内容如下。
这个函数的返回值好像必须是一个Promise对象,如果不这么写,会报Uncaught TypeError: Cannot read property 'then' of undefined(…)。

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
onLoadData(treeNode) {
return new Promise((resolve) => {
// 树形显示的数据在this.state.treeData中,在this.state.treeData中找到当前操作节点
const pathArr = treeNode.props.pos.split('-');
let findTreeNode = null;
for (let i=1,len=pathArr.length; i < len ; i++) {
if( i === 1){
findTreeNode = this.state.treeData[pathArr[i]];
} else {
findTreeNode = findTreeNode.children[pathArr[i]];
}
}
// 如果该节点没有子节点,表示没加载过其子节点,进行加载
if (findTreeNode.children.length <= 0) {
// 设置展开的节点为当前节点
this.state.openKey = treeNode.props.eventKey;
// 组织后台查询条件
const queryData = {
LEVEL: 1, // TODO 不知道什么规则
_extWhere: this.state.queryData._extWhere,
PID: treeNode.props.eventKey,
NAME: treeNode.props.title,
checkstate: ''
}
const dict = {
dictId: this.state.dictId,
dictData: this.props.dict.dictData
}
// 发起action,获取数据
this.props.updataDictByPid(this.props.servId, this.props.itemCode, objectUtils.clone(dict), queryData, this.props.servType, this.props.opeCode, this.props.queryServId);
}
resolve();
});
}

Form组件

参考 Form 和 rc-form

使用mapPropsToFields和onFieldsChange的校验问题

Form组件如下方式使用,获取不到校验信息了,浏览器的控制台会有打印的校验结果,但没有反映在页面上,但如果不使用mapPropsToFields是好使的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 必填校验
const validateArray = [{
rules: [{
required: true,
message: '邮箱必填'
}],
trigger: ['onChange']
}];
let fieldPropsOptions = {
validate: validateArray
};
...
<FormItem
label="email"
validateStatus={getFieldError('email') ? 'success' : 'error'}
help={isFieldValidating('email') ? '' : errMessage}
>
<Input {...getFieldProps('email', fieldPropsOptions)} />;
</FormItem>
...
NewForm = Form.create({onFieldsChange, mapPropsToFields})(NewForm);

解决方法是调用Form组件是传入一个props及更新该props的方法,如formErrItem、updateFormErrItem,在onFieldsChange中添加

1
2
3
4
5
6
7
8
9
function onFieldsChange(props, fields){
// 调用父组件更新formErrItem的方法
const newFormErrItem = {
...props.formErrItem,
...fields,
};
props.updateFormErrItem(newFormErrItem);
...
}

在Form组件中通过,this.props.formErrItem可获取校验信息。

ref引用

在使用Form组件时,在Form.create时需要添加withRef: true才能获取到组件内部定义的方法或state。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// MyForm.jsx中,定义了 MyForm 组件,是对Form组件的封装,返回的是antd的Form组件。
class MyForm extends Component {
render() {
return <Form />;
}
}
MyForm = Form.create({onFieldsChange, mapPropsToFields, withRef: true})(MyForm);
export default MyForm;
// App.jsx中,引用了MyForm组件,如果要引用到MyForm组件内部定义的方法或state,需要使用如下方式。
class App extends Component {
clickHandler() {
// 引用MyForm组件中定义的方法或state
const formRef = this.refs.myForm.refs.wrappedComponent.refs.formWrappedComponent;
}
render() {
return (
<div>
<MyForm ref="myForm" />
<Button onClick={this.clickHandler.bind(this)}>点击</Button>
</div>
);
}
}

Table组件

参考 Table

单选

如果Table组件中要进行单选,需要进行如下配置:

  • 将Table组件rowSelection属性的type值设置为radio,当前选中的数据由radioIndex控制
  • 注意,ant-design的Table组件,对于单选类型的,无法自己管理radioIndex值,如果想要自己管理radioIndex值,需要在Table组件中添加如下代码
    1
    2
    3
    4
    5
    if (('radioIndex' in nextProps) && nextProps.radioIndex !== null ) {
    this.setState({
    radioIndex: nextProps.radioIndex || null
    });
    }

行样式

Table组件的rowClassName属性,如:rowClassName={(record, index) => record['_delete_'] ? 'ant-table-row-delete' : '' }

不显示页码

Table组件的pagination属性为false

pagination

pagination.pageSize不为10、20、30、40中的值时,分页处显示的只有一个值,如15,而不是像10 条/页这样
需要给Table组件传入的pagination中有自定义的pageSizeOptions属性,该属性的值为所有需要显示的每页条数的数组

列表勾选行样式问题

在勾选列表行的checkbox或是列表可编辑的下拉框时,点击所在的行会发生颜色抖动,直接运行官方例子也有这个问题。后来发现可能是Chrome浏览器的问题,如果用360浏览器就不会有这个问题。很奇怪!

React-学习经验2

发表于 2016-09-13   |   分类于 React

MVVM(Model-View-ViewModel)

MVC—>MVP—>MVVM
MVVM框架的由来是MVP(Model-View-Presenter)模式与WPF(Windows Presentation Foundation)结合的应用方式时发展演变过来的一种新型架构框架。它立足于原有MVP框架并且把WPF的新特性糅合进去,以应对客户日益复杂的需求变化。
低耦合 可重用性 独立开发 可测试

零散知识

  • ReactDOM.render(Component,dom) 执行后,这个dom还会存在,这个Component是在最外层。
    感觉很奇怪
    1
    2
    // 在componentDidMount方法中
    ReactDOM.render(this.state.customScript.buttonAndPopoverComponents(), document.getElementById(<Button type="primary">自定义添加的按钮</Button>,'ANTD_CDP_TABLE1_ZLC-btnBar_after'));

解析jsx

1
2
3
4
5
return (
<div className="commentBox">
Hello, world! I am a CommentBox.
</div>
);
1
2
3
4
5
return (
React.createElement('div', {className: "commentBox"},
"Hello, world! I am a CommentBox."
)
);

Babel 设置匹配后缀,文件后缀为.jsx,就不会对<报错。Babel之所以可以按照各种要求编译JavaScript代码文件,是因为使用了各种插件和预设(预设是插件的打包封装)。

key 的作用

今天遇到了一个奇怪的现象,下面代码是在同一个页面中,点击不同按钮时显示单选或多选树,结果出现了错误,debugger调试时看到是组件在更新时出了问题,更新时没有找到正确的组件,先显示单选树再显示多选树会在单选的树上更新的,由于没有多选的一些属性而报错。解决办法就是放开注释的key属性,这样更新时就能找到相应的组件了。这是我第一次体会到key的作用。

善于 key 值得使用,如果不同的路由进入同一个组件,但要清空原有的所有状态,相当于是一个新组件的效果,使用不同的 key 值即可达到效果。

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
...
render() {
return (
<div>
{
this.props.multi ?
<Tree
// key="tree-multi"
checkable
onCheck={this.onCheck}
checkedKeys={this.state.selectedKeys}
>
{loop(this.props.userData)}
</Tree>
:
<Tree
// key="tree-single"
onSelect={this.onSelect}
>
{loop(this.props.userData)}
</Tree>
}
</div>
);
}

引用图片

使用require静态资源,打包时会自动打入,不需要再处理静态资源。

1
<img src={require('../../resources/images/logo.png')} />
1
2
const url = `url(${require("../../resources/images/banner-home.jpg")})`;
<div style={{backgroundImage: url}} />

ES6-学习

发表于 2016-09-13   |   分类于 ES6

最近看了《ES6标准入门》第二版,在对语法有了初步了解的基础上,首先建议真正开发前看一下该书第21章——编程风格,从开始就养成良好的编程风格。

ES6语法

编程风格

let取代var,const优先
静态字符串使用单引号或反引号
为变量赋数组优先使用解构赋值
函数的参数如果是对象优先使用解构赋值
如果函数的返回多个值,优先使用对象的解构赋值而不是数组的解构赋值
单行定义的对象,最后一个成员不以逗号结尾,多行定义的对象,最后一个成员以逗号结尾
对象尽量静态化,可以使用Object.assign()添加属性
使用扩展运算符(…)赋值数组
需要使用函数表达式的场合尽量使用箭头函数
函数的所有配置项应集中在一个对象,防在最后一个参数
只有模拟实体对象时才使用Object,如果只是要key:value的数据结构则使用Map
不要export default 与 普通的export同时使用

以下是我在项目开发中使用到的、个人认为比较有用的ES6语法,总结如下:

let和const

let、const与var相比:

使用未声明的变量,直接报错
重复声明同一个变量,直接报错
是块级作用域
必须声明 ‘use strict’ 后才能使用let声明变量

用了 let、const,typeof 就不那么安全了。
声明宣称一个名字的存在,定义则为这个名字分配存储空间,而初始化则是为名字分配的存储空间赋初值。

对象的扩展

Object.keys() – 返回对象的键组成的数组
Object.values() – 返回对象的值组成的数组
Object.assign() – 对象的合并

Generator函数

1
2
3
4
5
6
7
8
9
10
11
// 定义
function* helloWorldGenerator() {
yield 'hello';
yield 'world';
return 'ending';
}
// 调用
var hw = helloWorldGenerator();
hw.next();// {value: 'hello', done: false}
hw.next();// {value: 'world', done: false}
hw.next();// {value: 'ending', done: true}

调用Generator函数后,函数并不执行,返回的也不是函数运行结果,而是一个指向内部状态的指针对象(Iterator对象),必须调用next方法,是的指针移向下一个状态。总之,Generator函数是分段执行的,yield语句是暂停执行的标记,而next方法可以恢复执行。
Generator函数可以达到多个return的效果。
for…of循环、扩展运算符(…)、解构赋值和Array.from方法内部调用的都是遍历器接口,因此可以将Generator函数返回的Iterator对象作为参数使用。
调用Generator函数的返回结果可以类似数组使用

1
[...hw()]// ['hello', 'world', 'ending']

字符串的扩展

includes() – 返回布尔值,表示是否找到了参数字符串
startsWith() – 返回布尔值,表示参数字符串是否在源字符串的头部
endsWith() – 返回布尔值,表示参数字符串是否在源字符串的尾部

Class的取值函数(getter)和赋值函数(setter)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class MyClass {
constructor() {
// ...
}
get prop() {
return 'getter';
}
set prop(value) {
console.log('setter: '+value);
}
}
let inst = new MyClass();
inst.prop = 123; // 调用 setter
inst.prop; // 调用 getter

ES7语法

对象的解构赋值

对象的解构赋值是ES7的语法,需要

1
2
3
需要安装依赖 npm install babel-preset-stage-0 --save-dev
并配置预设 presets: ['es2015', 'stage-0', 'react']
使用 const newObj = { ...oldObj };

async await

1
2
3
4
5
6
7
8
9
10
11
async search(item) {
try {
// 会等待setState执行之后再向下执行
await this.setState({
query: item
});
this.fetchData();
} catch (error) {
console.log(error);
}
}

React-警告

发表于 2016-09-12   |   分类于 React

请求数据放在componentDidMount方法中的警告

需要请求数据的,或者使用redux触发action请求数据,最终会更新组件的state时,这种操作最好放在componentDidMount中,我以前总认为这种准备工作要放在constructor方法中,会报下面的警告。

1
Warning: setState(...): Cannot update during an existing state transition (such as within `render` or another component's constructor). Render methods should be a pure function of props and state; constructor side-effects are an anti-pattern, but can be moved to `componentWillMount`.

类似数组的组件没有key属性的警告

类似数组的组件,如Col、li等,有多个值时需要加key属性,否则会报如下警告。

1
Warning: Each child in an array or iterator should have a unique "key" prop. Check the render method of `AdvSearch`.

react组件销毁后渲染其它组件报警告

可以将react组件销毁后渲染其它组件报警告

1
2
3
4
5
const dom = document.getElementById('item_code');
if (dom !== null) {
ReactDOM.unmountComponentAtNode(dom);
ReactDOM.render(<Input placeholder="这是新渲染的组件"/>, dom);
}

IE11下报的render中Object类型错误

1
Unhandled promise rejection Invariant Violation: Objects are not valid as a React child (found: [object Generator]). If you meant to render a collection of children, use an array instead or wrap the object using createFragment(object) from the React add-ons. Check the render method of `QueryChoose`.

原因是jsx中调用generator函数返回的结果没有进行解构。将

1
this.state.customScript['customRender']()

改为

1
[...this.state.customScript['customRender']()]

IE11下报的deprecated错误

1
2
Warning: isMounted(...) is deprecated in plain JavaScript React classes. Instead, make sure to clean up subscriptions and pending requests in componentWillUnmount to prevent memory leaks.
Warning: replaceState(...) is deprecated in plain JavaScript React classes. Refactor your code to use setState instead (see https://github.com/facebook/react/issues/3236).

这个很奇怪,是发现在根组件App的render方法中有console.log(this);导致的警告,不明白为什么。

React-学习经验1

发表于 2016-09-12   |   分类于 React

React基础知识

React 是一个声明式、高效、灵活的、创建用户界面的JavaScript库,本质是将图形界面(GUI)函数化。

Universal渲染 指的是一套代码可以同时在服务端和客户端渲染。

Redux 是一个JavaScript状态容器,提供可预测的状态管理。

Webpack 是当下最热门的前端资源模块化管理和打包工具。

开发服务器是可以为程序提供资源服务的服务器。通常情况下,在页面中引入的脚本文件等静态资源都放在硬盘中,使用了开发服务器,这些资源将被读到内存里,然后可以通过开发服务器的端口访问这些资源。webpack-dev-server就是开发服务器。

组件 是一个函数或类,它决定了如何把数据变为视图。ReactElement是一个普通对象,描述了组件实例或DOM节点;组件实例则是组件类的实例化对象。

ReactElement 就是大名鼎鼎的“虚拟DOM”,其本质是一个不可变对象,描述了一个组件的实例或一个DOM节点,包含组件的类型、属性以及子元素等信息。ReactElement不是组件的实例,不能在ReactElement中调用React组件的任何方法,它只是告诉React你想在屏幕上显示什么。JSX中的闭合标签就是ReactElement。对一个组件而言,props就是输入,ReactElement就是输出,也就是render方法的返回值。

组件实例 是组件类的实例化对象,同样被用来管理内部状态、处理生命周期函数。ReactDOM.render方法返回的就是组件实例,某些组件方法中的this也指向组件实例,利用组件的Refs也可以获取组件实例。无状态函数是没有实例化对象的,因此无法使用生命周期函数,也没有内部状态。

JSX返回多个组件

JSX中返回多个组件,如果不想包面包一层父节点,可以使用数组,注意要有key属性。

1
2
3
4
const arr = [
<h3 key={0}>第一个组件</h3>,
<h2 key={1}>第二个组件</h2>,
];

组件间交互方式

我个人常用的组件间交互的方式有:

  • 如果是父子关系的组件,可以通过props由父组件将属性和回调方法传递给子组件进行交互
  • 如果是兄弟关系的组件,可以将需要交互的属性和方法提取到共同的父组件中,通过props传递给这两个兄弟组件进行交互
  • 如果是兄弟关系的组件,如A、B,且需要在B中使用A的属性,这个属性是A特有的,不能直接提取到公共父组件中,那么可以在父组件的state中添加一个属性,如form,初始值为null,在A的可以componentDidMount方法中对父组件的form属性进行赋值,这样就可以让B使用A特有的属性
  • 可以使用redux进行交互

render方法外渲染组件

在render方法外也可以渲染组件,但需要在render方法中预留好div,如

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 正常渲染的组件
class CommonComponent extends Component {
render() {
return (
<div>
<Button>正常显示的组件</Button>
<div id="specialComponent"></div>
</div>
);
}
}
// 在其他地方,个人认为应该在componentDidMount方法中
componentDidMount() {
const dom = document.getElementById('specialComponent');
if (dom !== null) {
ReactDOM.render(<Input placeholder="render外渲染的组件" />, dom);
}
}

注意:
如果采用这种特殊方式渲染的组件引用到了其父组件的props或state,还需要在componentDidUpdate方法中进行同样的在操作,否则这个特殊方式渲染的组件不会刷新。

1
2
3
4
5
6
componentDidUpdate() {
const dom = document.getElementById('specialComponent');
if (dom !== null) {
ReactDOM.render(<Input placeholder="render外渲染的组件" />, dom);
}
}

应避免使用style属性

React可以通过给组件设置style属性,进行CSS样式控制,但是不建议这么做,最好是通过指定className来进行样式控制,方便维护。

使用数据结构解决问题

如果组件本身的属性无法支持某些功能,可以考虑通过定义数据结构进行解决,如对属性值进行拼接,再拆分,如菜单的key以ID%|%NAME的形式出现。

与redux关联的组件ref问题

如果组件(如QueryChoose)组件,是与redux相关联的,对于使用react-redux的情况,也就是组件外调用了connect方法,需要在connect方法的mapStateToProps参数的返回对象中,添加ref属性(如ref:’innerQueryChoose’),在外层组件A引用到QueryChoose时指定ref属性(如ref:’wapperQueryChoose’),则在A中调用QueryChoose组件的方法或属性时,这样引用。

1
2
// A组件中
this.refs.wapperQueryChoose.refs.innerQueryChoose.someMethod();

setTimeout更新state

有时使用this.setState()更新组件的状态,不会反映到页面上,具体什么时候可以反映到页面上还不清楚,目前发现会出现这种现象的地方有两处:Tabs.TabPane内部的组件、Modal内部的组件。需要使用setTimeout才能达到更新状态的效果。

1
2
3
4
5
setTimeout(() => {
this.setState({
newState
});
},0);

带着信息跳转路由

跳转到新路由时,需要带有某些信息,并且不想在url中显示这些信息

1
2
3
4
this.context.router.push({
pathname: 'xxxx',// 跳转的新路由
state: {}// 信息放在这里
});

在路由跳转的页面中,获取这些信息

1
const urlInfo = this.props.location.state;

Smart Components and Dumb Components

也有叫Container Components and Presentational components的。

Smart component:
是连接Redux的组件(@connect),一般不可复用,类似MVC的C层

描述事物怎样工作
不提供DOM标签和样式
提供数据,进行数据获取
发起action
存放在containers目录

Dumb component:
是纯粹的组件,一般可复用,类似MVC的V层

描述事物的样子
不依赖应用
直接收props,包括数据和回调方法
很少有自己的state,有也是跟UI相关的
存放在components目录

两者的共同点是:无状态,或者说状态提取到上层,统一由 redux 的 store 来管理。redux state -> Smart component -> Dumb component -> Dumb component(通过 props 传递)。在实践中,少量 Dumb component 允许自带 UI 状态信息(组件 unmount 后,不需要保留 UI 状态)。
值得注意的是:Smart component 是应用更新状态的最小单元。实践中,可以将 route handlers 作为 Smart component,一个 Smart component 对应一个 reducer。

ref回调函数

今天无意看到了ref可以使用回调函数的用法,这个回调函数在组件安装后立即执行,被引用的组件作为一个参数传递,且回调函数可以立即使用这个组件,或保存供以后使用(或实现这两种行为)。

1
<TextInput ref={(node) => (this._input = node)} />

将函数传递到父组件的state中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
+---------------------------------------------------------+
| +---------+ |
| |C | A |
| +---------+ |
+---------------------------------------------------------+
| |
| Dashboard |
| |
| |
| +---------------------+ +----------------------+ |
| | B | | | |
| | + + | +---------> | |
| | | | | | | |
| | | + | | +-------------> | |
| | | | + | | | | |
| | | | | | | | | |
| +-+---+----+-----+----+ +----------------------+ |
| |
+---------------------------------------------------------+

A组件是B、C组件的公共祖先组件,C组件需要用到B组件的方法method,首先想到的是把method放到A中定义,然后传入B和C。但是如果不使用ref在A中定义无法获取B私有的状态和其它信息,而且实际中C是A的第一层子组件,B是A的第。我想到的解决方法是:A定义一个方法update接收参数更新自己的state,将update方法传入B中,B将method作为参数调用update方法,这样就将method定义为了A的state的一个属性,再传入C中,即可实现C中调用B中定义的方法。

修改state的问题

特别注意,当state中的属性是一个对象是,获取该对象进行属性值的修改后,页面当前不会按新状态显示,从react调试工具看该组件的状态时是修改后的值,可能操作几下页面会按新状态显示。原因是react不能直接修改state,如果是属性值是对象,需要进行深拷贝,修改值后在setState就没问题了。

依赖更新的问题

我和同事是同一套代码,没有任何不同,由于他npm install太慢,我把我的node_modules打成压缩包发给他,他的项目启动后会报一些奇怪的警告,我的就没有。执行完npm update就好了,很不明白这是为什么。

还有一个奇怪的问题,更新了antd依赖后,使用Tabs组件直接使用定义的内容可以显示,而调用一个方法返回会正常显示切换标签,切换后内容是空的,不报任何错误或警告。将整个node_modules删除后,重新安装依赖就正常了。

Controller View Pattern

The ReactJS Controller View Pattern

refs取不到组件的方法

refs如果能取到组件的state和props,但是取不到组件定义的方法,可能是因为这些方法没有在构造函数中bind(this)。

关于setState

  • 如果setState函数的新状态没有改变当前状态,也会重新执行render方法
  • setState第二个参数可以是个回调函数,如果有些操作要求在界面重新渲染完成后进行,可以放在回调函数中
    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
    class CompB extends React.Component {
    render(){
    console.log("触发了CompB重新render")
    return (
    <div>
    <h1>{this.props.value}</h1>
    </div>
    );
    }
    }
    class CompA extends React.Component {
    constructor(props) {
    super(props);
    this.state = {
    value: 'Hello, world!',
    }
    this.onClick = this.onClick.bind(this);
    }
    onClick() {
    this.setState({
    value: '你好!'
    }, () => console.log("CompA的setState回调!"));
    }
    render(){
    console.log("触发了CompA重新render")
    return (
    <div>
    <h1>{this.state.value}</h1>
    <button onClick={this.onClick}>点击</button>
    <hr/>
    <CompB value={this.state.value} />
    </div>
    );
    }
    }

forceUpdate函数

  • 会导致组件本身及其包含的所有级别的子组件重新读取、计算与渲染,与其同级的无关组件不会重新渲染
  • 所有UI组件的生命周期函数都会按生命周期规则来执行,如先进入componentWillUpdate再进入render
  • 不会调用shouldComponentUpdate来检查是否允许重新渲染
  • 可以提供一个回调函数,这个回调函数将在所有组件渲染完成后被调用
    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
    40
    41
    42
    43
    class CompB extends React.Component {
    componentWillUpdate(){
    console.log("触发了CompB的componentWillUpdate:",this.props.value)
    }
    render(){
    console.log("触发了CompB重新render:",this.props.value)
    return (
    <div>
    <h1>{this.props.value}</h1>
    </div>
    );
    }
    }
    class CompA extends React.Component {
    constructor(props) {
    super(props);
    this.state = {
    value: 'Hello, world!',
    }
    this.onClick = this.onClick.bind(this);
    }
    shouldComponentUpdate() {
    console.log("进入shouldComponentUpdate");
    }
    onClick() {
    this.forceUpdate(() => console.log("渲染完成!!!"));
    }
    render(){
    console.log("触发了CompA重新render")
    return (
    <div>
    <h1>{this.state.value}</h1>
    <button onClick={this.onClick}>点击</button>
    <hr/>
    <CompB value="CompA的子组件" />
    </div>
    );
    }
    }
    ReactDOM.render(
    <div><CompA /><CompB value="与CompA无关的组件"/></div>,
    document.getElementById('example')
    );

关于render函数

可以在自己的代码中调用这个函数以重新刷新组件,注意只刷新了组件本身,不会刷新子组件,但这不是一个好办法。如在点击按钮的onClick方法中可直接调用this.render()方法。

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
class CompB extends React.Component {
render(){
console.log("触发了CompB重新render")
return (
<div>
<h1>{this.props.value}</h1>
</div>
);
}
}
class CompA extends React.Component {
constructor(props) {
super(props);
this.state = {
value: 'Hello, world!',
}
this.onClick = this.onClick.bind(this);
}
onClick() {
this.render();
}
render(){
console.log("触发了CompA重新render")
return (
<div>
<h1>{this.state.value}</h1>
<button onClick={this.onClick}>点击</button>
<hr/>
<CompB value={this.state.value} />
</div>
);
}
}

组件定义外的代码

在React和React Native都发现,在每次系统刷新时(刷新浏览器或重新打开App程序),个人感觉会执行一遍所有文件(在几类文件中都输出了日志),就像此时是对所有的组件进行一遍声明一样,后面用到的地方才是正式的引用。这里需要注意的是,所有的文件都会在系统刷新时执行一遍,如果在组件定义外部进行了一些操作,如获取数据,那这个时候获取对不对,如果获取数据需要用户登录信息,这是很可能没有登录信息,有没有做处理。

children属性

如果使用了{this.props.children}显示子组件,想给子组件传递一些属性,可以使用

1
{React.cloneElement(this.props.children, {changeOpenState: this.changeOpenState})}

注意此时this.props.children是Object,如果this.props.children是数组,则需要遍历每个元素执行上面的cloneElement。

监听浏览器窗口大小变化

1
2
3
4
5
6
7
8
9
handleResize() {
console.log("输出")
}
componentDidMount() {
window.addEventListener('resize', this.handleResize);
}
componentWillUnmount() {
window.removeEventListener('resize', this.handleResize);
}

巧用setTimeout

项目中遇到一个问题,点击按钮执行了一个更新值的操作,然后调用保存方法,发现保存方法中获取的值并不是更新后的值,感觉是更新后的值没有执行完页面刷新呢,就进入了保存方法。由于setTimeout是在下一轮开始时进行,所以使用setTimeout可使更新在本轮刷新完成后在执行保存操作,达到保存更新后的效果。

1
2
3
4
5
6
7
8
export function callSaveClickHandler() {
const newData = {
'COL1': '改变一个字段值',
};
this.props.cardActions.modifyCardChangedData(this.state.servDef.servDef.SERV_ID, newData, true, this.props.servType);
// 这样做的目的是样保存操作在下一轮开始时执行,这样才能获取到页面更新的内容
setTimeout(()=>this.refs.btnBar.saveClickHandler(), 0);
}

巧用路由

/list路由对应的是List组件,该组件主体是一个Tabs组件,默认选中的是第一个Tab。在什么都不处理的情况下,选中不同的Tab不会改变路由,当进入下一个页面并想返回时,总是返回到同一个路由,并且每次选中的只能是第一个Tab,这样的用户体验不好,用户想要的是在进入其它页面前选中的是哪个Tab,返回到上一页面也应该选中哪个Tab。

我的解决方法是,在点击不同Tab时添加回调函数,在里面根据当前选中哪个Tab更新一次路由(this.context.router.replace),也就是路由为/list/tab1、/list/tab2、…这种形式,这样无论是哪种方式进入到相应的路由,只要从路由中取出第二个斜杠后的值,就可以知道当前页面应该选中哪个Tab了。这样相比两个独立页面的好处:非 tab 部分都是公用的,无需重新渲染;点击 tab 切换后滚动条不会回到顶部。

阻止默认事件

项目中遇到的问题是,在使用Table组件时,定义了行点击事件,这时行首部的复选框、行中带连接的文字、输入框等,点击的时候都会触发行点击事件。解决方式是在Table行上的组件外包一层div,并添加如下点击事件。

1
2
3
4
function stopPropagation(e) {
e.stopPropagation();
e.nativeEvent.stopImmediatePropagation();
}

Context

使用props可以进行上下层组件间属性的传递,使用context可以实现跨层组件间属性的传递。适合使用context的场景包括床底登录信息、当前语言以及主题信息等。而且,react-redux的Provider组件就使用了context来传递store。

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
40
41
42
43
44
class Level2 extends React.Component {
render() {
return (
<div id="parent2">
<p>{this.context.name}</p>
</div>
);
}
};
Level2.contextTypes = {
name: React.PropTypes.string.isRequired,
};
class Level1 extends React.Component {
render() {
return (
<div id="parent1">
<Level2 />
</div>
);
}
};
class Root extends React.Component {
// 注意如果name的属性值为null,会报警告,如下图所示
getChildContext() {
return {name: '顶层属性'};
}
render() {
return (
<div id="parent">
<Level1 />
</div>
);
}
};
Root.childContextTypes = {
name: React.PropTypes.string.isRequired,
};
ReactDOM.render(
<Root />,
document.getElementById('example')
);

在使用中发现了一个问题,在一个组件提供的context值改变时,使用这个值的子节点并没有接受到context的改变,没有达到要跨级共享数据的效果。网上说可以在这个提供context组件值改变时执行setState这样就会更新了,但通常使用context都是在组件顶层,这样进行整个重新渲染感觉不太好。

HTML5-基础

发表于 2016-09-04   |   分类于 HTML

最近看的《HTML秘籍》,对HTML5有了初步的了解,总结如下。

推出理由

  • Web浏览器之间兼容性低
  • 文档结构不明确
  • Web应用程序功能受限制

主要原理

  • 标准不应该引入导致已有网页无法工作的改变
  • 标准化了非官方、但广泛应用的技术
  • 以实用为目的

最佳实践

  • 包含可选的<html><body><head>元素
  • 标签全部小写
  • 为属性值加引号

元素家族

新增的元素

用于构建页面的语义元素 <article><aside><figcaption><figure><footer><header><nav><section><details><summary>

用于标识文本的语义元素 <mark><time><wbr>

Web表单及交互 <input><datalist><keygen><meter><progress><command><menu><output>

音频、视频及插件 <autio><video><source><embed>

Canvas <canvas>

非英语支持 <bdo><rp><rt><ruby>

删除的元素

为网页添加样式的元素,如 <big><center><font><tt><strike>

HTML框架,如 <frameset><frame>,但保留了 <iframe>

冗余或容易导致误会的元素,如 <acronym><applet>

改变的元素

将旧元素用于新目的,如 <small><hr><s>

停止从格式角度看问题,转换成具有真实逻辑含义的元素,如 <b><i> 分别被 <strong><em> 取代

调整的元素

调整了元素的使用规则,如 <address><cite>

标准化的元素

<embed><wbr>

构建更好的表单

改良了input元素,url、email、date、number、range等类型

占位水印 placeholder、自动获取焦点 autofocus

增加了表单验证,required、pattern、自定义验证

进度条 <progerss>、计量条 <meter>

新增功能

音频、视频、canvas

File API,有说 File API 不是HTML5规范的内容,但得到了浏览器较好的支持

Web存储,localStorage 和 sessionStorage

通过描述文件缓存资源

服务器发送事件

WebSocket

地理定位

Web Worker

零散知识

文字循环滚动

1
<marquee behavior="scroll" direction="row">啦啦啦,真的会动耶!</marquee>

学习计划

发表于 2016-09-03   |   分类于 前端知识

Web前端知识

学习路径

Developer Roadmaps
Front-End-Checklist

参考博客

阮一峰的网络日志
Andrew Ray’s Blog
React Native指南汇集了各类react-native学习资源、开源App和组件
leehey’s blog
有情怀的程序猿

HTML5

《HTML5秘籍》

CSS3

《图解CSS3:核心技术与案例实战》

JavaScript

《JavaScript高级程序设计》
《JavaScript语言精粹》

ES6

《ES6标准入门 2》

jQuery

…

React

《React精髓》
《React导学》
《React:引领未来的用户界面开发框架》
《React与Redux开发实例精解》
JavaScript 全栈工程师培训教程

React-Router

React Router 使用教程
react-router-tutorial
react-router

Redux

Redux 入门教程(一):基本用法
Redux 入门教程(二):中间件与异步操作
Redux 入门教程(三):React-Redux 的用法

NPM

其它

《Web全栈工程师的自我修养》
《哈佛情商课:献给奋斗中的你》
《精益创业》
《关键对话》
…

Webpack

快速上手
一小时包教会
webpack-demos

Git

Git远程操作详解
Git教程

Node

…


设计

《写给大家看的设计书》
《精益创业UX篇 高效用户体验设计》
《Web表单设计:点石成金的艺术》
《用户体验要素》


移动端知识

React Native

《React Native 跨平台移动应用开发》
《React Native 入门与实战》
《React Native 开发指南》

小程序

《微信小程序入门指南》

1…1617
© 2021 小朱
由 Hexo 强力驱动
主题 - NexT.Pisces