题外话
最近一月实习入职,体验了一把 994 的生活 (因为周三我学校还有课,一周实习四天),也算是马老师口中的 “身在福中” 了,虽然很是疲惫,但是确实收获颇多,公司里面很多很多大佬,于我眼界、技术、想法都提升增益了很多,特别是技术,我一个一直在学习 Vue 的菜虚鲲突然进了 React 项目组,羊入狼群,不知所措,不过还好的是有点 Vue 的底子,所以学起来也不算太难...
项目概述
这个月也一直在负责搭建一个类似蚂蚁金服的设计语言网站,受益匪浅,项目也差不多完成了,马上上线了,所以也花些时间理一理这个项目的技术实现,因为是公司项目,为了保密(不失业),所以我这里不会贴任何源代码,因为我这个项目与蚂蚁金服的 AntDesign 很是相似,所以我之后的介绍都以 AntDesign 为例
伏愿:不现鱼之相,仍识渔之意,一道非渔之鱼,君其飨之!
技术分析
先放一张 AntDesign 的官网截图
开始剖析:
其实整个站点结构极其简单,我已经都画出来了:
- 头部,底部,侧边栏:这三部分结构是内容都是不变的,最多就是不同的选项卡被选中的时候颜色变一下
- 正文内容,锚点定位栏,前后页跳转:这三部分随着页面的不同内容也会不同,所以这部分需要做定制化处理
技术栈
如果是初入门的切图仔,制作这个网站其实也是一点压力都没有的,估计压力比较大就是样式和自适应的问题,利用 HTML+CSS+一点点的 JS 只要写好一份页面,然后每一个页面对应一份 HTML 和 CSS,又因为整个站点的页面很相似,所以利用 CV 大法,可以很快地完成整个站点
所以目录结构可能就是:
三个文件夹:JS、CSS、HTML,然后每个页面一一对应
如果是这种方式写出来的代码,代码结构臃肿,可维护性差,(而且显得大公司就很没有水准……)
So,就有利用这样的方式:
- React:前端三大框架之一
- 数据驱动 DOM,将一些静态的数据抽离出来,避免臃肿重复的 DOM 结构
- 组件化,使各个页面相似的结构抽离出来,形成一个组件
- Mustache:前端模板引擎
类似 FreeMarker 模版引擎,不过不同于 FreeMaker 依托于 Java 相得益彰, Mustache 是根正苗红的前端模板引擎,十分友好好上手,语法和应用搭配 React 简直不要太爽
- Gulp+Sass:CSS 预处理器和自动构建工具
利用 Sass 再也不用写面条一样的臃肿丑陋的 CSS 了,其中的对于 CSS 变量,函数,CSS 结构的拓展优化非常得好,又因为浏览器只会识别 CSS 文件,这时候需要 Gulp 自动化构建工具了,Gulp 不仅仅可以将 Sass 文件打包成 CSS 文件,而且可以做到自动化构建,巴拉巴拉……具体可自行了解
- Eslint+Stylelint:代码规范
当进行大型项目开发的时候,项目组不止一个人在开发,而且项目在版本迭代的时候需要维护,这时候代码规范就极其重要了
技术实现
以上介绍了大概介绍了技术栈,那么接下来就是怎么实现蚂蚁金服的 AntDesign 的技术实现了!So,让我们一层层揭下AntDesign 的 衣服 面纱吧!
组件化
在前文中我就提到:
React 的好处之一:将各个页面相似的结构抽离出来,形成一个组件
AntDesign 站点网页的头部,底部,侧边栏,锚点定位栏,前后页跳转都是结构相同的部分,所以这些都可以抽离出来,放置在一个 Components
文件夹中,单独成一个组件,文件目录类似这样:
Components
|—— Header.js
|—— NavBar.js
|—— PageNav.js
|—— PageTurn.js
└── Footer.js
- 头部和尾部
头部和尾部结构并不复杂,而且多是静态的固定的数据,所以可以直接写,然后引入各个页面中
- 侧边栏
侧边栏可以直接写样式,通过重复的 DOM 结构堆砌而成
但是可以利用 React 的好处之二:数据驱动 DOM,将一些静态的数据抽离出来,避免臃肿重复的 DOM 结构
1.将侧边导航栏的内容数据抽成一个数组,独立在在一个独立的NavData.js
文件中然后export
出去
2.在NavBar.js
中引入NavData.js
,通过Array.map()
方法将数组依次遍历然后生成重复的相同DOM
Example:
export const allMenus = [
{
text: 'Ant Design',
type: 'design',
menus: [
{ text: '介绍', type: 'design1', url: '/design/introduce.html' },
{ text: '设计价值观', type: 'design2', url: '/design/design-values.html' },
{ text: '实践案例', type: 'design3', url: '/design/practice-case.html' },
],
},
{
text: '原则',
type: 'principle',
menus: [
...//省略原则的子选项
],
},
...//省略后续的菜单选项
];
可以将菜单栏的菜单如此抽出构成一个数组,然后将数组 allMenus
遍历生成 DOM
- 前后页跳转
前后页跳转中的内容就是左侧菜单栏中的子选项,只要获取当前的页面的前一项和后一项的菜单栏子选项就可以了,所以也可以同样利用刚刚抽离出来的数组allMenus
正文内容渲染
正文内容每页都不一样,所以是无法抽离成组件的,所以这个需要单独写,但是每一个 HTML 文件除<body></body>
标签其中的内容不一样,其他都可以复用,所以这时候 Mustache 就派上用场了:
- 每个页面单独写一个 JS 文件,写成一个 React 类,然后就等于写 HTML一样在类
render()
中写页面内容 DOM - 将页面的头部菜单类别、主菜单类别、子菜单类别、锚点内容作为类的属性一起
export
出去,这样就能在写锚点定位栏组件和页面跳转组件的时候使用props
接收数据 - 创建一个单独的 js 文件,来管理这么多页面
export
出来的数据内容,即类似上文的将导航栏抽成一个数组一样,同理,将所有的页面也抽成一个数组,之后就可以再使用Array.map()
方法,配合 Mustache 一一将 React 渲染成 html 文件
Example:
我用 AntDesign 菜单栏的第一项 'Ant Design' 的 '介绍' 页面为例:
每个页面有自己的头部菜单类别(headType)、主菜单类别(firstType)、子菜单类别(subType)、锚点内容(affixList),那么我们就可以直接在页面中定制化然后暴露出去:
import React, { Component } from 'react';
export default class Introduce extends Component {
render() {
return (
<div className="content">
{/* '介绍' 页面的 DOM 结构*/}
</div>
);
}
}
Introduce.headType = 'design';
Introduce.firstType = 'design';
Introduce.subType = 'design1';
Introduce.affixList = [
'设计资源',
'前端实现',
'谁在使用',
'社区评价',
'如何贡献',
];
以此类推,创建这么一个数组,来管理这么多的页面数据:
import Introduce from '../src/pages/design/Introduce';
import DesignValues from '../src/pages/design/DesignValues';
import PracticeCase from '../src/pages/design/PracticeCase';
...//将每个页面 export 的都引进来
export const allPages = [
{
mainCpn: Introduce,
title: '介绍',
name: 'design/introduce.html',
},
{
mainCpn: DesignValues,
title: '设计价值观',
name: 'design/design-values.html',
},
{
mainCpn: BarrierFree,
title: '实践案例',
name: 'design/practice-case.html',
},
...//省略其他页面
];
- 写一份
base.html
作为渲染的基础模版,在<body></body>
中定义一个变量,作为 Mustache 内容填充的插槽:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>{{title}}</title>
<link rel="stylesheet" href="/css/base.css?v={{version}}" />
</head>
<body>
<div id="app">{{{ssrHtml}}}</div>
</body>
<script src="https://cdn.bootcss.com/jquery/3.4.0/jquery.min.js"></script>
<script type="text/javascript" src="/js/base.js?v={{version}}"></script>
</html>
- 利用
fs
这个 node 模块读取base.html
的内容,然后作为Mustache.render()
方法的渲染对象参数,再将之前构建好的页面数组赋值给base.html
定义好的变量 ssrHtml ,再一一遍历作为被渲染对象,然后再使用fs
写出各个 HTML 文件
如此这般,最后便生成了整个项目的目录文件
哦,对了,为什么会引入了 Jquery?
因为这个项目有自适应,所以会有动画和操作 DOM ,而且 React 只是单纯地直接渲染 DOM ,没有操作数据来驱动 DOM ,所以没有和 Jq 冲突,所以可以放心地使用 Jq (其实用到 Jq 的地方也不多,就一些滚动监听之类的几个方法)
The End
其实有很多细节的,但是很多细节一聊就暴露我这个公司项目的源码啊什么的了,而且由于时间实力原因,可能很多问题讲不清晰,欢迎留言私信!定当改进加勉!
网友评论