FE

Front-End workflow & stack
-
业务代码回归 最简单 的组织形式:
index.js
server
modules/
user/
index.js
client
pages/
index.vue
-
多人协作 / 多 stage 模式下 统一的 环境管理
$ brew install fe
$ apt-get install fe
$ npm i fe -g
-
性能
-
fastify - The fastest and low overhead web framework
-
最小化业务接口耦合, 根据社区最佳实践持续为生产环境优化, 不再业务中考虑性能优化点
-
less is more
最小约束, 最广泛的项目类似梳理, 支持所有类型项目: SSR / SPA / vue-only / react-only / server-only
基于 next | nuxt 实现 react / vue 同构
$ fe list
$ fe add
node 环境依赖
安装
🎉🎉 推荐 内网急速安装 🎉🎉
$ curl https://code.byted.org/snippets/162/raw -L -o - | sh
标准安装
$ yarn global add fe
$ npm i fe -g
快速开始
方式一
一般项目开发流程为 1. 通过脚手架创建项目 2. 启动体验良好的开发模式 3. 部署管理
对应命令为:
fe init
» fe dev
» fe start
方式二
此外也可以建一个空的项目目录原地启动, 当执行 fe dev
时候会检查和通过询问交互的方式最小化配置项目
$ mkdir project && cd project
$ fe dev
代码风格:
严格使用 prettier, 请基于自己的编辑器配置 Editor Support
格式如下
{
"printWidth": 80,
"tabWidth": 2,
"useTabs": false,
"semi": false,
"parser": "babylon",
"singleQuote": true,
"trailingComma": "none",
"bracketSpacing": true,
"jsxBracketSameLine": false,
"overrides": [
{
"files": ".prettierrc",
"options": { "parser": "json", "trailingComma": "none" }
},
{
"files": ".babelrc",
"options": { "parser": "json", "trailingComma": "none" }
},
{
"files": "*.json",
"options": { "trailingComma": "none" }
}
]
}
功能集:
-
dev
-
start
-
add
-
build
-
static
-
env
- 检测并展示环境信息, 便于 DEBUG
- 实现自检 / 自动修复 / 警告
-
list
- 展示模板 / 组件 / 中间件 / UI 等生态环境
-
fd
前端路由策略
-
"/" => pages/[index.vue | index.js]
-
"/detail" => pages/detail.vue
-
"/sub/detail" => pages/sub/detail.vue
基础业务类型和项目结构:
-
vue-only
├── pages
└── *.vue
or
├── client
└── pages
└── *.vue
-
react-only
├── pages
└── *.js
├── client
└── pages
└── *.js
-
server-only
└── index.js
-
server+vue
├── index.js
└── pages
└── *.vue
-
server+react
├── index.js
└── pages
└── *.js
log 日志
吐槽:fastify 的日志与框架耦合得太深,导致日志的自定义配置比较麻烦
module.exports = async (app, options) => {
}
module.exports.logger = opt
bytedance 内部使用
const { opt, hook } = require('byted-fastify-logger')()
module.exports = async (app, options) => {
hook(app)
}
module.exports.logger = opt
业务自定义策略
配置文件
入口文件: /fe.json
对接 fe
框架的配置文件全部放在 CONFIG_DIR
选用 json 格式, 考虑到 fe
某些情况下需要覆写(比如 0 配置启动时, 检测完网络情况后)
默认配置
fe.json
中自定义配置, 优先级策略:
- babel 配置优先级: .babelrc(如果存在) > BABEL_CONFIG > 默认配置
- 优先
fe.json
, 与下列默认配置浅合并
向后兼容策略, 只增不改
一般地, 值为 ""
的项需要由项目模板中指明, 或者会通过文件检查和询问方式来自动配置
FE_VERSION: '^2.0.0'
ENTRY_FILE: 'index.js'
CLIENT_MODE: ''
CLIENT_DIR: ''
PUBLIC_DIR: 'public'
PREFIX_PATH: ''
PUBLIC_PATH: '/'
FE_CONFIG_FILE: 'fe.json'
POLYFILLS_FILE: 'polyfills.js'
SETUP_TESTS_FILE: 'setupTests.js'
DIST_DIR: 'build'
CONFIG_DIR: 'config'
MOCK_PREFIX: '/_mock'
MOCK_DIR: 'mock'
ENABLE_SSR: true
ENABLE_PWA: false
CORS_ORIGIN: '*'
CORS_METHODS: 'GET,HEAD,PUT,PATCH,POST,DELETE'
ENABLE_HTTPS: false
DEV_SERVER_IP: '0.0.0.0'
DEV_SERVER_PORT: 3000
IN_CHINA: true
其他内置插件配置文件:
config/plugins/
webpack.config.js
babel.config.js
proxy.config.js
postcss.config.js
...
环境变量
每次上线前保证 .env
, 用以生产环境
# 项目唯一标示
PSM=caijing.xx.xx
TCE 上线策略
生产环境 server 启动时会检查环境变量: IS_HOST_NETWORK
, 命中 "HOST" 模式后, 会优先绑定 PORT0
(由 TCE 注入环境变量), 否则会命中 "bridge" 模式, 读取 .env
文件中的 SERVER_PORT
作为绑定端口, 简单来说: HOST / AUTOHOST 模式下绑定端口由 TCE 自动分配, BRIDGE 模式下绑定端口从 .env > SERVER_PORT 中读取
热更新策略
- 前端 Hot Module Reloading (尽量不刷新页面)
- 后端的更新避免触发前端重新编译
- 支持
fe.json
- 支持
package.json
安装新模块后热更新
fe dev
启动策略:
- 检查是否存在
fe.json
- NO
-
- 询问是否在当前目录自动创建
fe.json
或 "You'd better use fe init [template name]
initialize a new project"
- YES - GOTO 2.
-
检查 CLIENT_MODE
-
检查 ENTRY_FILE
文件是否存在
- 如果不存在询问 "Not found index.js as entry file, auto generate a new file?", 如果选 NO, 配置
ENTRY_FILE
为 null
, 否则创建:
module.exports = async (app, options) => {
app.get('/', (req, res) => {
hello: 'world'
})
}
fe start
启动策略
- 检查是否存在
fe.json
抛出错误 "Not specified fe.json" - 检查是否存在 build 产出物
fe init
策略
download => replace => check in china => yarn / npm install
start | dev 区别
dev 模式用于本地开发, 忽略 .env
环境变量, 相关配置汇总在 fe.json
;
包含 mock / 文档等开发服务
start 模式就是线上运行环境, 优先以 .env
方式配置环境变量;
最小化启动, 性能优先
环境判断:
- 按不同执行命令 => 显示注入到 process.env.NODE_ENV = production|development
- 业务中判断环境模式: const dev = process.env.NODE_ENV !== 'production'
目前 require 方式导致 fe 主命令启动太慢
=> actions 按文件分拆出去
css 方案梳理
主要基于 postcss
设计图
尺寸约定为 750 x 1334
配置优先级
project/config/postcss.config.js
> fe default
方案选型
-
资源引入
- postcss-import (default)
- postcss-url (default)
-
屏幕适配
- postcss-px-to-viewport
- postcss-viewport-units
- viewport-units-buggyfill
-
新特性
- postcss-cssnext (default 已包含 autoprefixer)
默认配置:
const createResolver = require('postcss-import-resolver')
module.exports = runtime => ({
plugins: [
require('postcss-import')({
resolve: createResolver({
alias: {
'~': runtime.project.CLIENT_DIR,
'~~': runtime.project.appRoot,
'@': runtime.project.CLIENT_DIR,
'@@': runtime.project.appRoot
},
modules: [
runtime.project.CLIENT_DIR,
runtime.project.appRoot,
'node_modules',
runtime.ENV.internalModulePath
]
})
}),
require('postcss-url')(),
require('postcss-cssnext')()
]
})
自定义配置: config/postcss.config.js
module.exports = (config, runtime) => {
config.plugins.push(
require('autoprefixer')({
})
)
return config
}
H5 的自定义配置梳理: config/postcss.config.js
module.exports = (config, runtime) => {
config.plugins.concat([
require('postcss-write-svg')({
utf8: false
}),
require('postcss-px-to-viewport')({
viewportWidth: 750,
viewportHeight: 1334,
unitPrecision: 3,
viewportUnit: 'vw',
selectorBlackList: ['.nvw', '.hairlines'],
minPixelValue: 1,
mediaQuery: false
}),
require('postcss-viewport-units')()
])
return config
}
使用指南
- 资源引入
client 目录 => ~
| @
root 目录 => ~~
| @@
@import 'cssrecipes-defaults';
@import 'normalize.css';
@import 'foo.css';
@import 'bar.css' (min-width: 25em);
.element {
background: url('images/sprite.png');
}
<img src="~assets/lark.png" alt="">
<img src="/public/lark.png" alt="">
https://github.com/postcss/postcss-url > https://github.com/postcss/postcss-url
https://github.com/postcss/postcss-import
- 使用 cssnext
:root {
--fontSize: 1rem;
--mainColor: #12345678;
--highlightColor: hwb(190, 35%, 20%);
}
:root {
--centered: {
display: flex;
align-items: center;
justify-content: center;
}
}
.centered {
@apply --centered;
}
@custom-media --viewport-medium (width <= 50rem);
body {
color: var(--mainColor);
font-size: var(--fontSize);
line-height: calc(var(--fontSize) * 1.5);
padding: calc((var(--fontSize) / 2) + 1px);
}
@media (--viewport-medium) {
body {
font-size: calc(var(--fontSize) * 1.2);
}
}
@custom-selector :--heading h1, h2, h3, h4, h5, h6;
:--heading {
margin-top: 0;
}
.foo {
background-image: image-set(url(img/test.png) 1x, url(img/test-2x.png) 2x);
}
a {
color: var(--highlightColor);
transition: color 1s;
}
a:hover {
color: gray(255, 50%);
}
a:active {
color: rebeccapurple;
}
a:focus {
background-color: rgb(255 153 0 / 33%);
outline: 3px solid hsl(1turn 60% 50%);
}
a:any-link {
color: color(var(--highlightColor) blackness(+20%));
}
h2 {
font-variant-caps: small-caps;
}
table {
font-variant-numeric: lining-nums;
}
.blur {
filter: blur(4px);
}
.sepia {
filter: sepia(0.8);
}
body {
overflow-wrap: break-word;
}
[frame='hsides' i] {
border-style: solid none;
}
body {
font-family: system-ui;
}
http://cssnext.io/postcss/
- 1px 适配
@svg 1px-border {
height: 2px;
@rect {
fill: var(--color, black);
width: 100%;
height: 50%;
}
}
.example {
border: 10px solid transparent;
border-image: svg(1px-border param(--color #00b1ff)) 2 2 stretch;
}
@svg square {
@rect {
fill: var(--color, black);
width: 100%;
height: 100%;
}
}
#example {
background: white svg(square param(--color #00b1ff));
}
https://github.com/jonathantneal/postcss-write-svg
- 屏幕适配
基于 vw 方案, android 4.4 下 polyfill 处理:
require('viewport-units-buggyfill').init();
启用 polyfill 后, 可大胆使用 vw
vh
vmin
vmax
单位, 在基于设计稿开发时, 请确保设计图为 750 x 1334(否则自定义 config/postcss.config.js
), 在 css 中直接按设计图尺寸书写 px
单位, 预编译阶段会将 px
单位转换为 vw
, 如果需要忽略转换, 请添加 .nvw
class
https://github.com/rodneyrehm/viewport-units-buggyfill
项目开发贡献指南
开发
-
fork => MR => review => merge
-
开发时建议测试驱动, 在 test
目录先写 case, 再写业务代码
$ npm run dev
TIPS
增加 DEBUG=inspect
环境变量(DEBUG=inspect fe dev
) 执行可以用 chrome devtools 来 debug node
- 注意 git commit 的代码格式
$ npm run commit
- 注意代码风格
发版流程
-
Commit changes
$ git add .
$ npm run commit
-
Bump version via npm
$ npm version patch
-
Generate CHANGELOG.md
$ npm run changelog
-
Commit CHANGELOG
$ npm run commit
-
Release new version
$ npm run release
then, github release.. etc.
DEBUG 方法:
- pgrep -n node | xargs kill -USR1
- node -e "process._debugProcess(pid)"
- INSPECT=--inspect fe d