近几年,前端各类框架工具层出不穷,从两三年前仍是一个jQuery搞定全站,到以后requirejs/seajs,node,gulp/webpack,Angular/React/Vue,RN/weex的不断涌现,彻底颠覆了原来的前端开发模式。css
那么这些框架和工具给咱们到底带来了什么好处呢?其实我认为最核心莫过于这两点:模块化开发、自动化工程。而本次前端重构所围绕的核心问题就是自动化工程,将原有的gulp版本的项目利用webpack完全改造,顺利消灭了既繁琐又易错的人工操做。html
咱们先来看下奇货商城以前的开发流程:前端
从上图能够看出,咱们奇货前端开发以前存在的一些痛点:node
以上这些痛点,形成的重复性无用功,既浪费精力又着实让人蛋疼,而通过此次的框架重构,只需一键操做,就可完成联调和发布的部署。省心省力还不会出错。webpack
先看一下改版后,奇货商城的开发流程:es6
从上图能够看到,咱们通过改版后作到了:web
下面咱们看看如何实现。express
下面是部分主要目录结构:npm
├── build (全部的webpack配置项) │ ├── build.js │ ├── dev-client.js │ ├── dev-server.js │ ├── utils.js (★入口配置,生成文件配置,vm生成都靠这个文件) │ ├── webpack.base.conf.js (基础配置) │ ├── webpack.dev.conf.js (开发模式配置) │ └── webpack.prod.conf.js (生成环境配置) ├── config (node环境变量,入口文件的配置) │ ├── dev.env.js │ ├── entry.js (页面文件列表) │ ├── index.js (★主配置文件) │ ├── prod.env.js │ └── skinEntry.js (皮肤文件列表) ├── dist (打包后生成的文件夹,已所有转成vm) │ ├── goods │ │ ├── detail.vm │ ├── index.vm │ └── static (打包后-静态资源文件) │ ├── css │ ├── js │ └── skins (打包后皮肤文件夹) │ ├── default │ │ ├── default.1184b4d7.js │ │ ├── default.f07ae9df.css │ │ └── default.html │ ├── huotu │ └── pay ├── mock ├── package.json ├── routes ├── src (源文件) │ ├── js │ │ ├── components │ │ ├── goods │ │ │ ├── detail.js │ │ │ └── skins │ │ │ ├── default.js │ │ │ ├── huotu.js │ │ ├── index.js │ ├── less │ │ ├── components │ │ ├── goods │ │ │ ├── detail.less │ │ │ └── skins │ │ │ ├── default.less │ │ │ ├── huotu.less │ ├── index.less │ └── pages │ ├── components │ ├── goods │ │ ├── detail.html │ │ └── skins │ │ ├── default.html │ │ ├── huotu.html │ └── index.html ├── static │ └── images └── unit (公共库) ├── common (业务组件) │ ├── js │ └── less ├── layout (公共页面) │ ├── footer.html │ └── header.html └── lib (第三方组件)
以上是咱们奇商城的前端目录结构。json
webpack的一些必用的loader和plugin,例如less-loader, style-loader, file-loader, html-loader, 还有UglifyJsPlugin, ExtractTextPlugin, OptimizeCSSPlugin
等等,在这里就不详细展开了。
咱们重点说说如下几点核心:
经过node脚原本调用webpack,而不是直接在命令行启动webpack,会有这么几个用处:
webpack-merge
模块作抽离,方便维护;这货能够说是整个构建过程里,核心中的核心了。
自动生成vm、开发环境调用本地资源,以及皮肤文件的管理都有这个插件的功。部分代码:
new HtmlWebpackPlugin({ filename: process.env.NODE_ENV === 'production' ? path + name + '.vm' : path + name + '.html', template: template, inject: false, chunks: [pathBuild + name, 'vendor', 'manifest'] })
经过判断node环境变量,决定生成vm仍是本地html;
经过这个插件实现了js模块打包,公共模块提取,客户端缓存&增量发布,皮肤文件生成。部分代码:
for (let i = 0; i < entry.length; i++) { let item = entry[i] let path = item.path let name = item.name let pathBuild = path.replace(/\//g, '-'); result[pathBuild + name] = './src/js/' + path + name + '.js' } for (let i = 0; i < skinEntry.length; i++) { let item = skinEntry[i] let path = item.path let name = item.name if (process.env.NODE_ENV === 'production') { result['../skins/' + path + name] = './src/js/goods/skins/' + name + '.js' } else { result['skins/' + path + name] = './src/js/goods/skins/' + name + '.js' } } Object.assign(result, { vendor: ['@unit/common/js/base', '@unit/common/js/util'] }) // 公共文件提取 new webpack.optimize.CommonsChunkPlugin({ name: 'vendor', // 注意不要.js后缀 chunks: utils.computeChunks(entryConfig, '') }) // 避免修改业务代码致使vendor的md5改变,保留文件缓存 new webpack.optimize.CommonsChunkPlugin({ name: 'manifest', chunks: ['vendor'] })
自动化部署是在打包服务器经过脚本实现的,先经过npm命令打包前端工程,而后将代码copy到后端工程中,最后打包后端项目,再发布。
webpack官方文档并无如何引入公共html文件的说明,这一点是在翻了N多资料后才发现的,最终的方案是:
去掉webpack.config.js文件中配置的全局html-loader,这样html模版文件就不会被html-loader解析,咱们可使用ejs语法嵌入其余html页面和图片资源。由于没了全局的html-loader解析html文件,使用ejs语法嵌入的资源返回的是ejs代码,还须要使用html-loader来解析成html代码。
(html-loader!)表示引用html-loader这个加载器来解析
<%= require('html-loader!../layout/header.html') %>
可是这样将全局html-loader去掉后,又碰到了下面的问题。
vm中有时须要直接引用后端的变量,如${cssUrl}
,就像这样:
这时候webpack打包竟然就报错了,报错了:
出现这个问题的缘由应该是因为HtmlWebpackPlugin这个插件引用的模版默认是ejs,当不使用全局html-loader的时候,模板文件实际上是以ejs解析的,而${cssUrl}
在ejs中也识别为一个变量,固然就报错了。
这过程当中,整个周末都在想这个问题,甚至已经开始考虑用gulp+webpack的方案了。。
又翻了不少资料,忽然想到既然是ejs模板,能够尝试了一些ejs去写,而不是非要把这个模板以html的方式loader进来,而后就有了以下方法:
<link href="<%= '${cssUrl}' %>" rel="stylesheet">
这时候就被识别为一个字符串了!成功解决。
上面的方法解决的其实也是挺丑的,由于本地开发的时候须要引用本地文件的,上线的时候又得傻乎乎地去一个个地方去替换:
<!-- <link href="<%= skinCss %>" rel="stylesheet"> --> <link href="/skins/pay/pay.css" rel="stylesheet">
而后立刻试了下,在模板文件中用ejs去读node环境变量process.env.NODE_ENV
,果真能取到值,就有了下面这个相对完美的方案:
<% if (process.env.NODE_ENV === 'production') { skinCss = '${cssUrl}'; } else { skinCss = '/skins/pay/pay.css'; } %> <link href="<%= skinCss %>" rel="stylesheet">
其中production
就是利用node启动webpack时配置的,在这里派上了大用场。
到这里,咱们奇货商城已经实现了前端工程自动化,不再用一遍又一遍地去vm里修改路径,人工去记着改了哪些文件,要上传哪些静态资源。更加不用担忧漏传什么资源文件而致使线上bug辣。:)