下面这篇东西,分享的是我如何对「商城」前端进行改版。当然改版的版本还没上线,我开始写这个东西是因为厦门的夏天出门实在太难熬。只好待在家里听歌. 看股票.翻文档......这是一个真实项目升级的过程,也许目前搭建的架构还不完美,不妨分享出来大家看看......
2017年6月中旬接到了任务,对现有「商城」前端进行升级,升级目标是:
(1)、整站实现pc端、pad端、移动端的响应式。
(2)、「商城流程」上实现前后端完全分离,不考虑SEO。
(3)、「商城首页」「手机宣传页」需要考虑SEO优化。
这一段时间,对项目做最大改动是:
(1)、引入"CSS预处理器"——sass。
(2)、引入ES6,并要求后续JS代码尽量用ES6。
(3)、在「商城流程」上引入vue + vuex + vue-router做单页应用。
(4)、在「商城首页」「手机宣传页」等考虑SEO的页面上,引入EJS模板引擎,构建工具用webpack进行多页应用构建。
这篇文章,我想介绍一下改动中的第(4)点,目前已经基于这个环境,做出了是可以演示的demo。
用webpack搭建多页应用
搭建环境的时候,主要考虑以下几点:
- 共用头部、底部等模板
- 自定义SEO元素
- 提高开发体验,实现热加载
- 实现「手机宣传页」多页构建,如美图T8输出:【t8/index.html,t8/index.css,t8/index.js】、美图M8输出【m8/index.html,m8/index.css,m8/index.js】
- 构建后公共代码抽离,抽离公共的css、js,如输出为:commons/js/bundle.js、commons/stylesheet/common.css
出于对「公共头部」、「公共底部」、「自定义SEO元素」的考虑,在项目中我们引入了EJS模板引擎。
出于对公共代码的抽离我们再webpack中引入了extract-text-webpack-plugin、html-webpack-plugin
具体的做法用代码展示:
目录结构:
/common/layout 定义共用的模板
/common/js 定义共用的js
/public/scss 定义全局共用的样式
/src/ 放手机活动页目录如/src/t8...、/src/m8...
具体文件如图:
1、在/common/layout/layout.ejs定义好输出输出的模板(可定义SEO、公共头部、底部等)
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0,minimal-ui" name="viewport">
<title><%= title %></title>
<meta name="keywords" content="<%= keyword %>" />
<meta name="description" content="<%= description %>" />
</head>
<body>
<%= header %>
<%= subMenus%>
<%= content %>
<%= footer%>
</body>
</html>
2、在/common/layout/render.js 中引入header.ejs、footer.ejs等,并定义render方法:
import header from './header.ejs';
import subMenus from './submenus.ejs';
import footer from './footer.ejs';
import layout from './layout.ejs';
export default {
render: ({title, keyword, description, content, menus}) => {
const renderData = {
title,
keyword,
description,
menus,
header: header(),
subMenus: menus ? subMenus({menus}) : null,
footer: footer(),
content: content(),
};
return layout(renderData);
}
};
3、在/src/t8/render.js中定义具体的SEO元素信息、集体手机活动页信息t8/page.ejs等等,并export layout.render方法,给webpack提供要构建的html内容
import layout from 'layout';
import content from './page.ejs';
const title = '美图T8_双像素黑科技_美图拍照手机_美图官网';
const keyword = '美图T8,美图T8手机,美图T8拍照手机,1200万双像素智能手机,美图拍照手机,双像素黑科技,美颜手机,美颜视频手机,4G全网通,美图秀秀手机,美图手机,美图官网';
const description = '全新美图手机美图T8,前置1200万双像素摄像头,夜间自拍更高清。美图T8为夜而生。.......';
// 菜单配置
const menus = {};
export default layout.render({title, keyword, description, content, menus});
4、webpack配置文件;
因为美图有多个手机产品,每个手机产品多需要构建成单独的页面,因此采用了webpack多页应用构建的方式。具体做法如下:
在entry中定义需要构建的页面如:
如:
entry: {
pageA: "./pageA",
pageB: "./pageB"
}
当然,我们不想每次新增一个活动页(如:新增/src/m8),后都要修改配置文件;因此我们用了nodejs的glob来自动读取/src/下的所有文件夹中有render.js的文件夹,这样就不用修改webpack配置了。(当然,根据你的需求进行修改,如参考compass改为读取/src/下不带“_”下划线的文件夹进行构建)。
具体代码如下:
//entries函数
var _getEntries= function () {
// 获取顶级目录
var jsDir = path.resolve('src');
var pattern = jsDir + '/**/index.js';
var entryFiles = glob.sync(pattern, {nodir: true});
for (var i = 0; i < entryFiles.length; i++) {
// 设置entry
var filePath = entryFiles[i];
var filename = filePath.replace(jsDir+'/', '').replace('/index.js', '');
_entries[filename] = filePath;
var templatePath = filePath.replace('/index.js', '/render.js');
// 设置模板
const htmlPlugin = new HtmlWebpackPlugin({
filename: `${filename}/index.html`,
template: templatePath,
chunks: [ 'common', filename],
hash: true, // 为静态资源生成hash值
xhtml: true,
});
_plugins.push(htmlPlugin);
}
}
/* 设置入口页面 */
_getEntries();
/* 设置公共代码 */
_entries['common'] = ['jquery', './common/js/common.func', './common/js/common.page'];
/* 抽离公共js */
_plugins.push(
new webpack.optimize.CommonsChunkPlugin({
name: 'common',
filename: 'statics/js/[name].js',
minChunks: 4
})
);
/* 抽离公共css */
_plugins.push(
new ExtractTextPlugin({
filename: 'statics/css/[name].css?[contenthash]'
})
);
/* 配置压缩 */
_plugins.push(
new webpack.optimize.UglifyJsPlugin({
compress:{
warnings: false,
drop_debugger: true,
drop_console: true
}
})
);
构建出来的页面内容如下,满足了我们的需求,最大化的共用了代码:
太阳快下山了,我要出去浪了.......回来再完善,后面会在git上共享,webpack多页环境的代码.......