移动Web端
通用
HTML部分
- style放样式表里, 在头部(head)引入
- Javascript在尾部(body结束前)引入
添加必要的mate标签,利于SEO
<meta name="keywords" content="">
<meta name="description" content="不超过150个字符">
...
尽量减少标签数量,减少不必要的嵌套
- 结构清晰,对于流量型项目要尽量使用语义化标签
<h1>标题</h1>
<p>段落</p>
<a>超链接</a>
<ul>
<li>1.有序列表</li>
<li>2.有序列表</li>
<li>3.有序列表</li>
</ul>
...
Javascript部分
变量采用驼峰命名发
let doctorId = null;
私有变量以”_”开头
let _price = 0.00;
常量全部大写
const ADMIN_ROLE = 16;
函数功能保证单一,减少耦合性,同时注释说明 ```javascript 单行注释 // xxxx
多行注释 /**
- xxx
- xxx */
函数注释 /**
- 以星号开头,紧跟一个空格,第一行为函数说明
- @param {类型} 参数 单独类型的参数
- @param {[类型|类型|类型]} 参数 多种类型的参数
- @param {类型} [可选参数] 参数 可选参数用[]包起来
- @return {类型} 说明
- @author 作者 创建时间 修改时间(短日期)改别人代码要留名
- @example 举例(如果需要) */ ```
CSS部分
使用css的缩写属性
padding: 20px 30px;✅
padding-top: 20px;padding-left: 30px;padding-right: 30px;padding-bottom:20px;❎
border: 1px solid #e2e2e2;✅
border-width: 1px;border-style: solid;border-color:#e2e2e2;❎
...
颜色书写完全
color: #ff99aa;✅
color: #f9a;❎
命名规范
容器: container
页头:header
内容:content/container
页面主体:main
页尾:footer
导航:nav
侧栏:sidebar
栏目:column
页面外围控制整体佈局宽度:wrapper
左右中:left right center
导航:nav
主导航:mainnav
子导航:subnav
顶导航:topnav
边导航:sidebar
左导航:leftsidebar
右导航:rightsidebar
菜单:menu
子菜单:submenu
标题: title
摘要: summary
标志:logo
广告:banner
登陆:login
登录条:loginbar
注册:register
搜索:search
功能区:shop
标题:title
加入:joinus
状态:status
按钮:btn
滚动:scroll
标籤页:tab
文章列表:list
提示信息:msg
当前的: current
小技巧:tips
图标: icon
注释:note
指南:guild
服务:service
热点:hot
新闻:news
下载:download
投票:vote
合作伙伴:partner
友情链接:link
版权:copy
...
移动端适配方式
常用的适配方式有rem、vw。
vw的特点是矢量不失真,原生支持使用简单,PC端同移动端。
rem的特点是移动PC同时支持,需要针对不同尺寸平台做媒体查询。
如何选择?
当项目仅一套样式且使用在移动端上建议vw,其他情况自行选择
React项目
脚手架的选择
脚手架的选择有umi、create-react-app。
umi的特点是功能齐全,包含了路由、状态管理、现成的构建等。
create-react-app的特点是轻量,可自定义强。
如何选择?
当使用antd(蚂蚁开源)生态技术时推荐使用umi,其他时候自行选择。
umi项目目录结构
├── README.md // 项目说明、注意事项等
├── config // 项目配置
│ └── config.ts
├── dist // build目录
├── mock // 模拟数据
├── node_modules
├── package.json
├── public
│ └── favicon.png
├── src
│ ├── app.ts // 入口文件
│ ├── assets // 静态资源
│ │ ├── fonts // 字体
│ │ │ └── DIN-Bold.otf
│ │ └── images // 图片
│ │ ├── default
│ │ ├── i_circle
│ │ ├── i_login
│ │ └── i_qa
│ ├── components // 组件
│ │ ├── userModal
│ │ │ ├── userModal.less
│ │ │ └── userModal.tsx
│ │ └── videoItem
│ │ ├── videoItem.less
│ │ └── videoItem.tsx
│ ├── global.less // 全局样式
│ ├── locales // 国际化
│ ├── models // 状态管理数据集
│ │ ├── groupDisease.ts
│ │ ├── groupIndexData.ts
│ ├── pages // 页面
│ │ ├── message
│ │ │ ├── message.less
│ │ │ └── message.tsx
│ │ ├── my
│ │ │ ├── my.less
│ │ │ └── my.tsx
│ └── utils // 工具箱
│ ├── isWeichat.ts
│ ├── request.ts
│ ├── storage.ts
│ ├── tools.ts
│ └── type.d.ts
├── .umirc.ts // 配置文件
├── tsconfig.json
├── typings.d.ts
└── yarn.lock
umi项目配置
import { defineConfig } from 'umi';
const { REACT_APP_ENV } = process.env;
const proxy: any = {
dev: {
'/api/': {
target: 'http://proxy.pule.com',
changeOrigin: true,
pathRewrite: { '^': '' },
},
},
test: {
'/api/': {
target: 'https://yyk.pule.com',
changeOrigin: true,
pathRewrite: { '^': '' },
},
},
pre: {
'/api/': {
target: 'your pre url',
changeOrigin: true,
pathRewrite: { '^': '' },
},
},
};
export default defineConfig({
nodeModulesTransform: {
type: 'none',
},
proxy: proxy[REACT_APP_ENV || 'dev'], // 依赖库: "cross-env": "^7.0.2"
routes: [
{ path: '/test', component: '@/pages/test/test' }, // 测试页
{ path: '/', component: '@/pages/tabBar/tabBar' }, // tabbar
{ path: '/login', component: '@/pages/login/login' }, // 登录页
{ path: '/bindPhone', component: './bindPhone/bindPhone' }, // 绑定手机
{ path: '/loginCode', component: './loginCode/loginCode' }, // 验证码登录
{ component: '@/pages/404' },
],
dva: {},
antd: {},
externals: {
TCPlayer: 'window.TCPlayer',
},
locale: {
default: 'zh-CN',
},
metas: [
{
name: 'apple-mobile-web-app-capable',
content: 'yes',
},
{
name: 'fullscreen',
content: 'yes',
},
{
name: 'x5-fullscreen',
content: 'true',
},
],
styles: ['https://imgcache.qq.com/open/qcloud/video/tcplayer/tcplayer.css'],
headScripts: [
'https://res.wx.qq.com/open/js/jweixin-1.6.0.js',
'https://imgcache.qq.com/open/qcloud/video/tcplayer/libs/hls.min.0.13.2m.js',
'https://imgcache.qq.com/open/qcloud/video/tcplayer/tcplayer.v4.1.min.js',
],
theme: {
'fill-grey': '#F9F9F9',
'color-text-base': '#4A4A4A', // 基本
'color-text-strong': '#404C56', //加重
'color-text-bold': '#3C5061', //粗体
'color-text-secondary': '#666666', // 辅助色
'color-text-caption': '#999999', // 辅助描述
},
});
其他
- 函数组件与类组件
当使用到一些特殊的钩子函数的时候使用函数组件,比方umi的useMode、useSelector, useDispatch等
当运用了异步修改状态推荐使用类组件
this.setState({
...
}, () => {
// 执行异步操作
});
- 页面之间传参方式(url传参与state传参)
当页面的入口有多个的时候,比如可分享的页面,建议使用url传参,通过从url读取参数,向后端请求数据
当参数较为简单,且入口固定可使用state传参
以umi的history举例
// url 传参
history.push(`/askDoctorForm?gid=${props.gid}&did=${doctor.id}`);
const params = history.location.query; // 获取参数
// state 传参
history.push(`/askDoctorForm`, {
gid: props.gid,
did: doctor.id,
});
const params = history.location.state; // 获取参数
- 持久状态、全局状态和局部状态的使用场景
持久状态:能够持久保存在浏览器中的数据,常用localStorage实现。使用场景:保存用户账户信息,登录态等数
全局状态:全局共享的常量数据,常用来保存一些配置、权限、类型枚举等信息
局部状态:单一组件或者部分组件用到的数据, 单一组件推荐使用react自有的state, 多组件推荐使用状态管理(redux等)
- 依赖的引入顺序
官方库>第三方库>自定义文件>自定义组件>样式
// 库
import React, { useState, useEffect, useRef } from 'react';
import moment from 'moment';
import { history, useLocation, useParams } from 'umi';
import { Toast } from 'antd-mobile';
// 自定义文件
import { rightConfig, WECHAT_MP } from '@/utils/configData';
import { OrderCreateRespType } from '@/utils/type';
import newRequest from '@/utils/request';
import isWeichat from '@/utils/isWeichat';
import global from '@/utils/global';
// 自定义组件
import NavBar from '@/components/navbar/navbar';
import DoctorConsult from '@/components/doctorConsult/doctorConsult';
import VideoItem, { EpisodeProp } from '@/components/videoItem/videoItem';
// 样式
import styles from './courseDetail.less';
- 类型检测(写接口)的必要性
建议使用接口(interface),使用接口有以下几个优点:
- 约束函数或者类的参数输入,提供了类型检测
- IDE能为编写代码提供警告和快捷输入
- 能够清晰明了的查到组件所需的数据以及类型
- 提高代码的严谨性
interface UserProp {
ownerAvatar?: string;
ownerName?: string;
ownerRole?: number;
ownerIsDoctor?: boolean;
createTime?: string;
}
interface TopicOwnerProp {
hasOption?: boolean;
hasZan?: boolean;
hasAt?: boolean;
sizeType?: string;
user: UserProp;
avatarClick?: () => void;
optionClick?: (user: UserProp) => void;
}
export default (props: TopicOwnerProp) => {
return <div>...</div>
}
一些常用的文件命名
- 全局变量:global.ts
- 工具集:utils.ts
- 配置数据:config.ts
- 公共请求:commonRequest.ts(图片上传、登录校验等)
- 类型接口:type.d.ts
Vue项目(TODO)
原生项目(TODO)
App(flutter)端(TODO)
代码规范
- 包的导入顺序:dart: > package: > 项目相关
- export 放在所有导入语句之后
- 以驼峰命名,类名首字母大写, 私有变量以”_”开头
- 使用///文档注释成员和类型
- 避免缩写
- 不要使用.length来判断集合为空(可以使用isEmpty)
- 不要显示为参数设置null值
- 推荐使用final关键字创建只读属性
- 尽可能在定义变量的时候初始化变量值
- 构造函数不要使用 new
- async中需要存在有用效果
- 使用Future
作为无法回值异步成员的返回类型 - 要注意var 和 const的使用时机
- 方法名避免以”get”开头
切图规范
- 凡是不能确地页面高度的都使用可滑动组件(ListView, SingleChildScroll…)
- 使用
materail
主题都要考虑appbar
返回键在Android和Ios的统一, 建议全部使用leading
自定义返回图标, 并且注意返回图标与背景色区分开来 - 使用
TextField
注意屏幕被撑起问题, 键盘类型, 是否自动更正等 - 明确设计主题色, 并分离出单独的配置文件
- 图片占位符问题, 确保图片请求时图片区域视觉友好
- UI块抽离成组件,避免多层嵌套造成阅读障碍