代码规范文档
背景
目前前端团队开发出现技术断层显现比较严重,由于不同开发人员承担不同的产品线业务,加上长期没有进行技术评审和代码评审,导致开发人员的代码风格差别很大,长期以往会出现代码可读性差的问题,这无疑会增加其他维护人员的开发成本,导致整个团队开发低效;
再有如果长期封闭解决问题,好的经验得不到沉淀,一些问题也不能及时暴露,对于个人的成长和团队发展也都是一种阻碍。
目的
- 打造可扩展性高的团队项目
开发人员
- 提升开发人员的规范意识和能力
规范化开发,也是以团队的力量更好的帮助开发人员更为高效,更为有质量地进行开发
- 提升前端的
基础代码规范
基础代码规范目前已经有很多成型的工具可以帮助开发人员在开发时培养
基本的规范习惯,针对目前的项目基本代码规范的实施分为两种方式
- 利用规范工具
针对于新增的小型项目,目前构建工具中已经添加eshint,针对新开发的项
目可以利用工具,在开发过程中随时进行代码检查;
- 规范建议
已经存在的项目,例如godman,如果利用工具检查代码规范之后修改工作
量会很大,针对这种情况,需要继续参与开发的同学通过参考基础代码规范建议来进行开发;因为开发时间有限,建议部分可以理解为是工具检查中的子集,只建议比较重要规范问题;
基础规范建议
基础规范包括h5规范,css规范和js规范; js规范中还包括了es6规范;
CSS规范
- 选择器
【必选】不要在文件中使用内联式引入样式,不管是定义在样式便签里还是直接定义在元素上,这样会很难追踪样式规则;除非必须要用的场景,如进度条进度控制;
示例:<p style=“color: red”>
解释:这样会很难追踪样式规则,样式很多的情况下,不方便统一管理
【建议】只出现一次的元素用id,其他的用class;
解释:使用id的选择器的性能是最高的,所以如果有单独的元素,尽量用id;该建议主要使用与多页系统;
【建议】尽量指向明确,不要用标签选择器
示例: a { color : red }
解释: 标签的选择效率非常地,建议指向明确的id或者class元素
- 加载
【必选】杜绝使用import的方式加载css
解释:import是串行加载。用link代替
js规范
- 变量
【建议】声明包含元素的数组与对象,只有当内部元素的形式较为简单时,才允许写在一行。元素复杂的情况,还是应该换行书写。
// good
var arr1 = [];
var arr2 = [1, 2, 3];
var obj1 = {};
var obj2 = {name: 'obj'};
var obj3 = {
name: 'obj',
age: 20,
sex: 1
};
// bad
var arr1 = [ ];
var arr2 = [ 1, 2, 3 ];
var obj1 = { };
var obj2 = { name: 'obj' };
var obj3 = {name: 'obj', age: 20, sex: 1};
【建议】一个 var 声明多个变量,容易导致较长的行长度,并且在修改时容易造成逗号和分号的混淆。
// good
var hangModules = [];
var missModules = [];
var visited = {};
// bad
var hangModules = [],
missModules = [],
visited = {};
【建议】多行判断语句拆分写
if (user.isAuthenticated()
&& user.isInRole('admin')
&& user.hasAuthority('add-admin')
|| user.hasAuthority('delete-admin')
) {
// Code
}
- 命名
【建议】变量名,函数用驼峰的方式,变量名为名词,函数名为动词
var loadingModules = {};
function stringFormat(source) {
}
【建议】常量使用全字母大写,单词间下划线分隔的方式命名
var HTML_ENTITY = {};
【建议】类使用pascal命名
function TextNode(options) {
}
【必选】自定义事件的事件名必须全小写。
解释:在 JavaScript 广泛应用的浏览器环境,绝大多数 DOM 事件名称都是全小写的。为了遵循大多数 JavaScript 开发者的习惯,在设计自定义事件时,事件名也应该全小写。
- 条件
【必选】禁止使用==进行条件判断,使用===
解释:因为js为弱类型选择器,用==有隐藏的风险,如0==false
【建议】使用尽可能简单的表达式
字符串
// good
if (!name) {
}
// bad
if (name === '') {
}
数组
// good
if (collection.length) {
// ......
}
// bad
if (collection.length > 0) {
// ......
}
布尔
// good
if (!notTrue) {
// ......
}
// bad
if (notTrue === false) {
// ......
}
null或者undefined
// good
if (noValue == null) {
// ......
}
// bad
if (noValue === null || typeof noValue === 'undefined') {
// ......
}
【建议】不要在循环体中包含函数表达式,事先将函数提取到循环体外。
解释:循环体中的函数表达式,运行过程中会生成循环次数个函数对象。
// good
function clicker() {
// ......
}
for (var i = 0, len = elements.length; i < len; i++) {
var element = elements[i];
addListener(element, 'click', clicker);
}
// bad
for (var i = 0, len = elements.length; i < len; i++) {
var element = elements[i];
addListener(element, 'click', function () {});
}
[建议] 对循环内多次使用的不变值,在循环外用变量缓存。
解释:循环体中的会产生多个变量对象
// good
var width = wrap.offsetWidth + 'px';
for (var i = 0, len = elements.length; i < len; i++) {
var element = elements[i];
element.style.width = width;
// ......
}
// bad
for (var i = 0, len = elements.length; i < len; i++) {
var element = elements[i];
element.style.width = wrap.offsetWidth + 'px';
// ......
}
[建议] 对有序集合进行遍历时,缓存 length。
解释:虽然现代浏览器都对数组长度进行了缓存,但对于一些宿主对象和老旧浏览器的数组对象,在每次 length 访问时会动态计算元素个数,此时缓存 length 能有效提高程序性能。
for (var i = 0, len = elements.length; i < len; i++) {
var element = elements[i];
// ......
}
- 类型转化
[建议] 转换成 string 时,使用 + ''
解释:
// good
num + '';
// bad
new String(num);
num.toString();
String(num);
[建议] 转换成 number 时,通常使用 +
解释:
// good
+str;
// bad
Number(str);
[建议] string 转换成 number,要转换的字符串结尾包含非数字并期望忽略时,使用 parseInt。
var width = '200px';
parseInt(width, 10);
【建议】 转换成 boolean 时,使用 !!。
var num = 3.14;
!!num;
- 对象
【必选】声明对象时用对象字面量{}声明
解释:new Object会调用对象构造函数,初始化对象,效率较空对象低
// good
var obj = {};
// bad
var obj = new Object();
【必选】不允许修改和扩展任何原生对象和宿主对象的原型。
String.prototype.trim = function () {
};
【建议】属性访问时,尽量使用
解释:通常在 JavaScript 中声明的对象,属性命名是使用 Camel 命名法,用 . 来访问更清晰简洁。部分特殊的属性(比如来自后端的JSON),可以通过 [expr] 方式访问。
info.age;
info['more-info'];
【建议】for in 遍历对象时, 使用 hasOwnProperty 过滤掉原型中的属性。
var newInfo = {};
for (var key in info) {
if (info.hasOwnProperty(key)) {
newInfo[key] = info[key];
}
}
- 数组
【必选】使用数组字面量 [] 创建新数组,除非想要创建的是指定长度的数组。
解释: 效率更高
// good
var arr = [];
// bad
var arr = new Array();
【必选】 遍历数组不使用 for in。
解释:数组对象可能存在数字以外的属性, 这种情况下 for in 不会得到正确结果.
var arr = ['a', 'b', 'c'];
arr.other = 'other things';
// good
for (var i = 0, len = arr.length; i < len; i++) {
console.log(i);
}
// bad
for (i in arr) {
console.log(i);
}
- 函数
【建议】 一个函数的长度控制在 50 行以内。
解释:将过多的逻辑单元混在一个大函数中,易导致难以维护。一个清晰易懂的函数应该完成单一的逻辑单元。复杂的操作应进一步抽取,通过函数的调用来体现流程。
【建议】 一个函数的参数控制在 6 个以内。
解释:除去不定长参数以外,函数具备不同逻辑意义的参数建议控制在 6 个以内,过多参数会导致维护难度增大。某些情况下,如使用 AMD Loader 的 require 加载多个模块时,其 callback 可能会存在较多参数,因此对函数参数的个数不做强制限制。
- 闭包
【建议】 在适当的时候将闭包内大对象置为 null。
解释:虽然现在浏览器已经优化了垃圾回收机制,但是某些复杂情况下,造成循环引用,垃圾无法回收
// good
for (var i = 0, len = arr.length; i < len; i++) {
console.log(i);
}
// bad
for (i in arr) {
console.log(i);
}
- 动态特性
【必选】 避免使用直接 eval 函数。
- DOM
【建议】操作dom的时候尽量避免reflow
解释: js最大的执行性能主要消耗在DOM操作上,所以要尽量避免这种情
况,会引起reflow的场景如下:
- DOM元素的添加、修改(内容)、删除。
- 应用新的样式或者修改任何影响元素布局的属性。
- Resize浏览器窗口、滚动页面。
- 读取元素的某些属性(offsetLeft、offsetTop、offsetHeight、offsetWidth、scrollTop/Left/Width/Height、clientTop/Left/Width/Height、get***putedStyle()、currentStyle(in IE)) 。
ES6规范
- 命名
【建议】导出单一类时,确保你的文件名就是你的类名
// file contents
class CheckBox {
// ...
}
module.exports = CheckBox;
// in some other file
// bad
const CheckBox = require('./checkBox');
// bad
const CheckBox = require('./check_box');
// good
const CheckBox = require('./CheckBox');
【建议】导出一个默认小驼峰命名的函数时,文件名应该就是导出的方法名
function makeStyleGuide() {
}
export default makeStyleGuide;
- 变量
【建议】为引用使用const 关键字,而不是var
// bad
var a = 1;
var b = 2;
// good
const a = 1;
const b = 2;
【建议】如果你必须修改引用,使用 let 代替 var
// bad
var count = 1;
if (true) {
count += 1;
}
// good, use the let.
let count = 1;
if (true) {
count += 1;
}
- 对象
【建议】使用定义对象属性的简短形式
const lukeSkywalker = 'Luke Skywalker';
// bad
const obj = {
lukeSkywalker: lukeSkywalker
};
// good
const obj = {
lukeSkywalker
};
- 数组
【必选】使用 ... 来拷贝数组
// bad
const len = items.length;
const itemsCopy = [];
let i;
for (i = 0; i < len; i++) {
itemsCopy[i] = items[i];
}
// good
const itemsCopy = [...items];
【建议】使用 Array.from 将类数组对象转换为数组
const foo = document.querySelectorAll('.foo');
const nodes = Array.from(foo);
- 解构赋值
【建议】访问或使用对象的多个属性时请使用对象的解构赋值
解释:解构赋值避免了为这些属性创建临时变量或对象。
// bad
function getFullName(user) {
const firstName = user.firstName;
const lastName = user.lastName;
return `${firstName} ${lastName}`;
}
// good
function getFullName(obj) {
const { firstName, lastName } = obj;
return `${firstName} ${lastName}`;
}
// best
function getFullName({ firstName, lastName }) {
return `${firstName} ${lastName}`;
}
【建议】使用数组解构赋值
const arr = [1, 2, 3, 4];
// bad
const first = arr[0];
const second = arr[1];
// good
const [first, second] = arr;
【建议】函数有多个返回值时使用对象解构,而不是数组解构
// bad
function processInput(input) {
// then a miracle o***urs
return [left, right, top, bottom];
}
// the caller needs to think about the order of return data
const [left, __, top] = processInput(input);
// good
function processInput(input) {
// then a miracle o***urs
return { left, right, top, bottom };
}
// the caller selects only the data they need
const { left, right } = processInput(input);
- 字符串
【建议】编程构建字符串时,使用字符串模板而不是字符串连接
解释: 模板给你一个可读的字符串,简洁的语法与适当的换行和字符串插值特性
// bad
function sayHi(name) {
return 'How are you, ' + name + '?';
}
// bad
function sayHi(name) {
return ['How are you, ', name, '?'].join();
}
// good
function sayHi(name) {
return `How are you, ${name}?`;
}
- 函数
【建议】使用函数声明而不是函数表达式
解释:函数声明拥有函数名,在调用栈中更加容易识别。并且,函数声明会整体提升,而函数表达式只会提升变量本身。这条规则也可以这样描述,始终使用箭头函数来代替函数表达式。
// bad
const foo = function () {
};
// good
function foo() {
}
【必选】永远不要使用 arguments,使用 ... 操作符来代替
解释:... 操作符可以明确指定你需要哪些参数,并且得到的是一个真实的数组,而不是 arguments 这样的类数组对象。
// bad
function concatenateAll() {
const args = Array.prototype.slice.call(arguments);
return args.join('');
}
// good
function concatenateAll(...args) {
return args.join('');
}
【必选】使用函数参数默认值语法,而不是修改函数的实参
// really bad
function handleThings(opts) {
opts = opts || {};
// ...
}
// still bad
function handleThings(opts) {
if (opts === void 0) {
opts = {};
}
// ...
}
// good
function handleThings(opts = {}) {
// ...
}
【必选】当必须使用函数表达式时(例如传递一个匿名函数时),请使用箭头函数
解释: 箭头函数提供了更简洁的语法,并且箭头函数中 this 对象的指向是不变的,this 对象绑定定义时所在的对象,这通常是我们想要的。如果该函数的逻辑非常复杂,请将该函数提取为一个函数声明。
// bad
[1, 2, 3].map(function (x) {
return x * x;
});
// good
[1, 2, 3].map((x) => {
return x * x
});
- 继承
【必选】总是使用 class 关键字,避免直接修改 prototype
解释: class 语法更简洁,也更易理解。
// bad
function Queue(contents = []) {
this._queue = [...contents];
}
Queue.prototype.pop = function() {
const value = this._queue[0];
this._queue.splice(0, 1);
return value;
}
// good
class Queue {
constructor(contents = []) {
this._queue = [...contents];
}
pop() {
const value = this._queue[0];
this._queue.splice(0, 1);
return value;
}
}
- 模块
【建议】总是在非标准的模块系统中使用标准的 import 和 export 语法,我们总是可以将标准的模块语法转换成支持特定模块加载器的语法。
// bad
const AirbnbStyleGuide = require('./AirbnbStyleGuide');
module.exports = AirbnbStyleGuide.es6;
// ok
import AirbnbStyleGuide from './AirbnbStyleGuide';
export default AirbnbStyleGuide.es6;
// best
import { es6 } from './AirbnbStyleGuide';
export default es6;
【必选】不要使用通配符 * 的 import
解释:这样确保了只有一个默认的 export 项
// bad
import * as AirbnbStyleGuide from './AirbnbStyleGuide';
// good
import AirbnbStyleGuide from './AirbnbStyleGuide';
【必选】不要直接从一个 import 上 export
解释:虽然一行代码看起来更简洁,但是有一个明确的 import 和一个明确的 export 使得代码行为更加明确。
// bad
// filename es6.js
export default { es6 } from './airbnbStyleGuide';
// good
// filename es6.js
import { es6 } from './AirbnbStyleGuide';
export default es6;
框架规范
jQuery
1.变量
【建议】为 jQuery 对象命名时添加 $ 前缀
// bad
const sidebar = $('.sidebar');
// good
const $sidebar = $('.sidebar');
2.选择器
【建议】尽量ID选择器。实际运用的是js的document.getElementById(),所以速度较其他选择器快。
【建议】使用类选择器时不要指定元素的类型
// bad
var $products = $("div.products"); // slow
// good
var $products = $(".products"); // fast
【建议】对DOM查询使用级联的 $('.sidebar ul') ,在指定作用域进行查询时使用 find
解释:ID父亲容器下面查找子元素请用.find()方法。这样做快的原因是通过id选择元素不会使用Sizzle引擎
// bad
$('ul', '.sidebar').hide();
// bad
$('.sidebar').find('ul').hide();
// good
$('.sidebar ul').hide();
// good
$('.sidebar > ul').hide();
// good
$sidebar.find('ul').hide();
【建议】多级查找中,右边尽量指定得详细点而左边则尽量简单点。
// bad
$("div.data .gonzalez");
// good
$(".data td.gonzalez");
【必选】避免使用万能选择器
// bad
$('div.container > *');
// good
$('div.container').children();
【必选】ID已经表示唯一了,背后使用的是document.getElementById(),所以不要和其他选择器混淆了。
// bad
$('#outer #inner');
$('div#inner');
$('.outer-container #inner');
// good
$('#inner');
3.DOM操作
【建议】操作任何元素前先将其从文档卸载,然后再贴回去
解释:将元素从文档卸载后对其的操作不会再引起重绘等对页面性能造成较大影响的问题
var $myList = $("#list-container > ul").detach();
//...a lot of ***plicated things on $myList
$myList.appendTo("#list-container");
【建议】使用连接字符串或数组join(),然后再append()。
// bad
var $myList = $("#list");
for(var i = 0; i < 10000; i++){
$myList.append("<li>"+i+"</li>");
}
// good
var $myList = $("#list");
var list = "";
for(var i = 0; i < 10000; i++){
list += "<li>"+i+"</li>";
}
$myList.html(list);
// even faster
var array = [];
for(var i = 0; i < 10000; i++){
array[i] = "<li>"+i+"</li>";
}
$myList.html(array.join(''));
4.事件
【建议】不要用匿名函数来做事件的回调。
解释:匿名函数不易调试维护测试和复用。
// bad
$("#myLink").on("click", function(){...});
// good
function myLinkClickHandler(){...}
$("#myLink").on("click", myLinkClickHandler);
【建议】如果可能尽量在绑定事件处理程序时使用一个命名空间,这样可以方便地取消绑定而不会影响其他绑定。
$("#myLink").on("click.mySpecialClick", myEventHandler);
// 之后,让我们优雅地解除绑定
$("#myLink").unbind("click.mySpecialClick");
【建议】利用事件委托来提高性能和节省内存开销
// bad
// you are attaching an event to all the links under the list.
$("#list a").on("click", myClickHandler);
// good
// only one event handler is attached to the parent.
$("#list").on("click", "a", myClickHandler);
【必选】事件绑定之后不再需要此事件时要解绑
5.其他
【建议】不要将CSS与jQuery杂揉
// bad
$("#mydiv").css({'color':red, 'font-weight':'bold'});
// good
.error {
color: red;
font-weight: bold;
}
$("#mydiv").addClass("error");
6.兼容性
【必选】如果要兼容IE678 请不要用jQuery 2.x版本
7.Ajax异步操作
【必选】不要在链接里面嵌参数,请使用专门的参数设置来传递
// bad
$.ajax({
url: "something.php?param1=test1¶m2=test2",
....
});
// good
$.ajax({
url: "something.php",
data: { param1: test1, param2: test2 }
});
React规范
React规范包括react的基本代码规范,各个生命周期函数的使用规范以及react中组件抽取的规范;下面对于每个规范进行说明;
基本代码规范
- 基本规则
【必选】每个文件只包含一个 React 组件
【必选】除非是从一个非 JSX 文件中初始化 app,否则不要使用
React.createElement
// bad
const Listing = React.createClass({
render() {
return <div />;
}
});
// good
class Listing extends React.***ponent {
render() {
return <div />;
}
}
- 命名
【推荐】使用 jsx 作为 React 组件的扩展名
【必选】文件命名采用帕斯卡命名法,如:ReservationCard.jsx
【推荐】组件引用采用帕斯卡命名法,其实例采用驼峰式命名法
// bad
const reservationCard = require('./ReservationCard');
// good
const ReservationCard = require('./ReservationCard');
// bad
const ReservationItem = <ReservationCard />;
// good
const reservationItem = <ReservationCard />;
- 声明
【必选】不要通过 displayName 来命名组件,通过引用来命名组件
// bad
export default React.createClass({
displayName: 'ReservationCard',
// stuff goes here
});
// good
export default class ReservationCard extends React.***ponent {
}
- 引号
【必选】对于 JSX 使用双引号,对其它所有 JS 属性使用单引号
解释:因为 JSX 属性不能包含被转移的引号,并且双引号使得如 "don't" 一样的连接词很容易被输入。常规的 HTML 属性也应该使用双引号而不是单引号,JSX 属性反映了这个约定。
// bad
<Foo bar='bar' />
// good
<Foo bar="bar" />
// bad
<Foo style={{ left: "20px" }} />
// good
<Foo style={{ left: '20px' }} />
- 属性
【必选】属性名采用驼峰式命名法
// bad
<Foo
UserName="hello"
phone_number={12345678}
/>
// good
<Foo
userName="hello"
phoneNumber={12345678}
/>
7.项目相关
【必选】如无特殊原因,禁止在React中使用jQuery等其他第三方框架下的功能插件
【必选】组件应保持独立性,只接受定义好的输入参数作为变化因子,产出对应的输出,组件不受外部环境的影响,同样组件内不能改变外部环境
// bad
class extends React.***ponent {
render() {
let color = window.config.backgrondColor;
...
}
});
// good
class extends React.***ponent {
render() {
let color = this.props.backgroundColor;
...
}
});
【必选】组件接受的参数定义必须是明确的基础数据或者特定的数据结构,不能笼统的传入一个对象
// bad
<Foo
data={this.state.data}
/>
// good
<Foo
color={this.state.data.color}
content={this.state.data.content}
/>
React各生命周期规范
一个React组件的生命周期分为三个部分:实例化、存在期和销毁时。
- 实例化
- getDefualtProps
设置组件默认的props,该方法只会被调用一次;
【必选】属于组件本身的Props要在该方法中定义好,或者es6的写法,在构造函数中定义
解释:在组件调用关系复杂的时候,设置默认Props可以方便区分该组件和父组件的props,可读性更高
var Hello = React.creatClass({
getDefaultProps: function(){
return {
name: 'pomy',
git: 'dwqs'
}
},
render: function(){
return (
<div>Hello,{this.props.name},git username is {this.props.dwqs}</div>
)
}
});
ReactDOM.render(<Hello />, document.body);
- getInitailState
对于组件的每个实例来说,这个方法的调用有且只有一次,用来初始化每个实例的 state,在这个方法里,可以访问组件的 props。每一个React组件都有自己的 state,其与 props 的区别在于 state只存在组件的内部,props 在所有实例中共享。
getInitialState 和 getDefaultPops 的调用是有区别的,getDefaultPops 是对于组件类来说只调用一次,后续该类的应用都不会被调用,而 getInitialState 是对于每个组件实例来讲都会调用,并且只调一次。
- ***ponentWillMount
【建议】如果要修改state在改方法中修改
解释:该方法是修改state的最后一次机会,在这里修改state是不会触发render的;
- render
render方法返回的结果并不是真正的DOM元素,而是一个虚拟的表现,
类似于一个DOM tree的结构的对象。react之所以效率高,就是这个原因。
【建议】不要在render方法中再修改props和state;
var LikeButton = React.createClass({
getInitialState: function() {
return {liked: false};
},
handleClick: function(event) {
this.setState({liked: !this.state.liked});
},
//good
***ponentWillMount : function(){
this.setState({
liked : true
})
},
//bad
render: function() {
this.setState({
liked : true
})
var text = this.state.liked ? 'like' : 'haven\'t liked';
return (
<p onClick={this.handleClick}>
You {text} this. Click to toggle.
</p>
);
}
});
- ***ponentDidMount
【建议】在该方法内获取后端数据
解释:在该方法中DOM节点已经渲染完成,获取数据之后可以直接可以直接插入
【建议】在该方法中操作真实DOM
解释:该方法中DOM已经渲染完成,在这个方法中可以访问到,使用ref属性
- 存在期
- ***ponentWillReceiveProps
组件的props通过父组件更改了,该方法会被调用;
【建议】在该方法中更新state,触发组件的重新渲染
解释:
- should***ponentUpdate
如果确定props或者state的改变不需要重新渲染,可以通过在这个方法里返回false在阻止掉后面流程;
【建议】尽量在每个组件中实现该方法
解释:该方法可以灵活的控制该组件是否要被重新渲染,在一般的react项目中,经常出现的场景是父组件的重新渲染会导致子组件的重新渲染,当组件嵌套关系复杂的时候,可能要执行多次无用的render,这对于性能的消耗很大;
should***ponentUpdate: function(nextProps, nextState){
return this.state.checked === nextState.checked;
//return false 则不更新组件
}
【必选】不要在该方法内调用setState
解释: 会引起循环引用
- ***ponentWillUpdate
这个方法和 ***ponentWillMount 类似,在组件接收到了新的 props 或
者 state 即将进行重新渲染前,***ponentWillUpdate(object nextProps, object nextState) 会被调用;
【必选】不要在此方面里再去更新 props 或者 state。
解释:同理render,在该方法中更新Props和state可能会引起死循环;
- render
- ***ponentDidUpdate
这个方法和 ***ponentDidMount 类似,在组件重新被渲染之后,
***ponentDidUpdate(object prevProps, object prevState) 会被调用。不过 ***ponentDidMount只在实例化之后被调用一次,而***ponentDidUpdate会在每次render之后被调用;可以在这里访问并修改 DOM。
react组件抽取规范
利用mvvm框架编程的最大特点就是组件化,合理的抽取组件可以大大提升开发效率;那么如何能够合理的抽取组件,首先需要明白组件具有哪些特性,根据这些特性确定组件,然庵后抽取
组件特性
- 页面组件和通用组件
最常见的组件是页面的组成单位,多个组件组成一个页面, 这样的组件叫做页面
组件;一般的页面组件更和具体业务挂钩,所以也叫业务组件;
不属于页面组成部分,但是被多个页面使用的基础组件叫做通用组件,如弹窗,输入框等;这部分组件也被叫做是基础组件;
- 组件具有复用性
组件可以被多次使用,如果一个组件只被一个页面所用则不能称之为组件
- 组件和组件之间是独立的
抽取的组件是保持各个组件之间是独立的,如果两个组件之间有频繁的联动则抽取为一个组件;
组件抽取原则
组件的抽取要遵循组件的特性,好的组件应该符合以下原则
- 复用性较高
一般的,组件的复用次数如果达到2次以上,即可抽取为组件
- 抽取的组件不可太复杂
不同的组件承担职责不同,有的基础组件,如错误弹窗就比较简单,只需要接受输
入展示组件即可,但是一些业务组件由于业务关系会变得很复杂,这时候要对业务组件进行拆分
- 组件输入要可控明确
好的组件其输入,如props是可控的,输入的个数一般控制在10个以内,如果有
太多的输入,则要考虑是否要分拆组件;
- 组件之间是相互独立的
所谓组件独立是指组件自己处理自己逻辑,自己请求自己的数据,尽管有父组件子
组件的关系,但是父组件不会影响子组件的逻辑;但是父组件的重新渲染会导致子组件重新渲染;并且父组件和子组件之间不要有太复杂的数据交互,如很复杂的props,或者是子组件要调用父组件的回调方法多次,这种情况考虑合为一个组件
- 通用组件的抽取不以页面为单位
通用组件不是属于任何一个页面的,虽然是可以被多次调用,但是却不是页面的组
成部分,通用组件要做到尽可能的输出简单;业务组件则是以页面为单位;