公司官网开发总结
一、项目需求
- 静态页面
- 产品中心-三个页面;
- 解决方案-三个页面;
- 关于我们、加入我们;
- 动态页面
- 首页-新闻资讯模块;
- 新闻资讯-公司动态、行业动态、行业政策;
二、准备工作
在开始工作之前,我们先考虑官网开发的方案。结合开发时间,经讨论,我们有以下四个方案备选:
- 前后端分离的方式,由前端切页面,并通过ajax请求后端的API拿数据,并渲染页面;
- 前端切页面,由java后端来套API接口前渲染页面;
- 采用基于vue的服务器渲染框架:nuxt.js;
- 采用node直接服务端渲染;
作为公司官网,需要考虑适度的SEO,以便搜索引擎收录。如果是前后端分离的结构,那么不利于搜索引擎收录,这样,第一点不能满足我们的需求,放弃。经与后端开发人员讨论,时间上不允许后端来处理页面,这样第二个方案也放弃掉了。经综合对比,我们认为第四个方案比第三个优,因为第三个方案的开发成本和维护成本较高,所以决定采用第四个方案来开发。
三、技术选型
由于我们选择了node进行服务端渲染页面,这样,我们需要处理前端和后端页面,这样的话,需要前后端各一套框架来进行开发。
兼容性考虑
兼容性的考虑,主要是前端方面,后端的话,只要我们的node版本够新,就能应用起最新的特性来。在考虑了各浏览器的市场占有率后,我们决定仅支持IE9+的浏览器以及标准浏览器;对IE9以下的浏览器不予支持;
后端框架选择
- 后端框架选择:热门的框架有koa和egg.js这两个。egg.js是对koa的二次封装,简化了很多我们需要配置的东西,而且社区活跃度也很高,生态也完善,所以我们后端就选择用node+egg.js来实现;
- 前端框架选择:因为我们主要业务逻辑在服务端渲染了,所以在前端,只需要实现部分的交互功能,前端我们引入vue.js来进行开发。一个是vue.js比较轻量,另一个考虑是团队对vue.js的掌握程度较好;
四、开发工具
- ESLint用来做代码规范;
- Gulp前端构建工具,主要对样式和JS文件进行压缩合并;
使用到的插件:
// 引入Gulp;
const gulp = require('gulp');
// 解析less样式预处理文件;
const less = require('gulp-less');
// 拼接样式和JS;
const concat = require('gulp-concat');
// JS的混淆;
const uglify = require('gulp-uglify');
// 压缩CSS文件;
const cleanCSS = require('gulp-clean-css');
// es6进行转码;
const babel = require('gulp-babel');
//
const watch = require('gulp-watch');
// 对CSS文件自动中前缀;
const LessAutoprefix = require('less-plugin-autoprefix');
const autoprefix = new LessAutoprefix({
browsers: ['last 2 versions']
});
// 浏览器自动刷新插件
const browserSync = require('browser-sync').create();
const reload = browserSync.reload;
// 清理样式和JS文件;
const clean = require('gulp-clean')
// 对样式文件里的图片进行转码;
const base64 = require('gulp-css-base64')
- 采用less对样式进行预处理,处理后采用Gulp进行压缩;
- 版本控制git;
- 项目开发时,需要配置
browser-sync
的代理,这样在样式或JS文件修改时,页面自动刷新;代码如下:
gulp.task('browser-sync', function () {
browserSync.init({
proxy: "http://localhost:7001"
});
gulp.watch('./client/src/less/**/*.less', ['concat']);
gulp.watch('./app/public/stylesheets/*.*').on('change', function () {
browserSync.reload()
})
gulp.watch('./client/src/scripts/*.js', ['js']).on('change', function () {
browserSync.reload()
});
gulp.watch('./app/view/**/*.*').on('change', function () {
browserSync.reload()
})
});
五、公共库选择
-
swiper
,用来做轮播图;
六、目录结构
- app文件夹,主要包括了项目后端的文件,里的结构如下:
app
├── controller
├── extend
├── middleware
├── public
├── router.js
├── service
└── view
- app/router.js 用于配置 URL 路由规则。
- app/controller/** 用于解析用户的输入,处理后返回相应的结果。
- app/service/** 用于编写业务逻辑层。
- app/middleware/** 用于编写中间件。
- app/public/** 用于放置静态资源,如样式、JS图片等。
- config/config.{env}.js 用于编写配置文件。
- config/plugin.js 用于配置需要加载的插件。
- app/view/** 用于放置模板文件,我们选用了ejs模板;
- client 前端文件夹,用来放置样式文件和JS文件的源文件,打包压缩后的,需要放入app文件夹下面的public目录里;
七、项目重点/难点
- 新闻资讯页面的分页功能,需要在后端进行处理;我们的分页要求如下:
- 每页显示5个页码,比如1-2-3-4-5;第6页的页码的话,就到下一组的第一个位置;
- 我们需要计算出当前页码所在的组,并计算该组第一个页码是什么;
处理代码如下:
pageData.start = (_page - _page % 5 + 1 + Math.ceil(_page / 5) * 5) % 5 + (Math.ceil(_page / 5) - 1) * 5;
_page:当前的页码;
pageData.start:指当前的页码所在组的开始页码;
- 同一页面,多组轮播图通过Tab标签切换时,需要在切换后再次执行
update()
方法,以更新该Tab下的轮播图,否则图片会变形; - 由于是前后端混合开发,很多代码需理清前端执行还是后端执行。如vue里面,绑定class时,
:class="<%=classA%>"
,我们本意是想把classA
绑定到class上,但实际上这样会报错。原因是,后端先渲染了模板,上面的代码就变成了:class="classA"
,但在前端部分,我们是未定义classA
这个变量的,要解决这个问题,需要在代码里再包一层引号::class="'<%=classA%>'"
,这样,后端渲染后变成了:class="'classA'"
,这时的classA就是一个样式类的字符串,而不是一个变量了。
八、性能优化
- 对静态文件进行永久缓存;
config.static = {
prefix: '',
dir: path.join(appInfo.baseDir, 'app/public'),
dynamic: true,
preload: false,
maxAge: 24 * 60 * 60 * 365,
buffer: false,
};
- 当样式和JS更新时,利用
Gulp-rev
及gulp-rev-collector
更新引用链接,动态添加版本后缀;该插件会自动遍历指定的文件夹里的文件,并将引用链接替换为更新后的; - 样式文件里的背景图片,通过Gulp插件转化为BASE64模式,减少请求;
九、部署
- 服务器需安装node 8.0以上版本;
- egg.js框架内置了 egg-cluster 来启动 Master 进程,Master 有足够的稳定性,不再需要使用 pm2 等进程守护模块。同时,框架也提供了 egg-scripts 来支持线上环境的运行和停止。
- 将项目文件剔除node_modules后打包,然后上传到服务器,利用
$ npm install --production
来安装生产包的依赖; - 利用node start来启动项目;默认会在7001端口启动项目,需要设置反向代理,才能通过80端口访问;
十、待改进
- 待添加单元测试;
- 安全性优化待进行;
- 配置文件未分开开发环境和生产环境,后期待分开;
- 代码规范需改进,如样式、js变量命名等;
网友评论