一、前言
之前介绍了webpack+react+redux+es6开发模式,这个项目对于一个独立的功能节点来说是没有问题的。假如伴随着源源不断的需求,前段项目会涌现出更多的功能节点,需要独立部署运行。为了更好地管理这些独立的功能节点,我们需要利用webpack进行单独的打包处理,针对不同的功能节点生成不同的配置文件
二、项目结构
1.项目demo目录图片示例
2.apps目录图片示例(project1和project2分别表示两个不同的功能节点)
三、npm-scripts 命令
package.json部分示
接下来,会说明下每个命令的用途。
四、webpack的dll功能
Dll是Webpack最近新加的功能,我简单介绍一下。
Dll就是一个纯纯的依赖库,它本身不能运行,是用来给你的app引用的。
打包dll的时候,Webpack会将所有包含的库做一个索引,写在一个manifest文件中,而引用dll的代码(dll user)在打包的时候,只需要读取这个manifest文件,就可以了。
这么一来有几个好处:
-
Dll打包以后是独立存在的,只要其包含的库没有增减、升级,hash也不会变化,因此线上的dll代码不需要随着版本发布频繁更新。
-
App部分代码修改后,只需要编译app部分的代码,dll部分,只要包含的库没有增减、升级,就不需要重新打包。这样也大大提高了每次编译的速度。
-
假设你有多个项目,使用了相同的一些依赖库,它们就可以共用一个dll。
1.webpack.config.js中关于Dll的配置如下
webpack.DllPlugin
的选项中,path
是manifest文件的输出路径;name
是dll暴露的对象名,要跟output.library
保持一致;context
是解析包路径的上下文,这个要跟接下来配置的dll user一致。
如何进行打包呢,接下来就会用到上面提到的scripts命令(package.json文件中)。这里要说的命令是"dll project1"对应的命令,如下。
webpack --display-error-details --progress --colors -p --config config.project1.dll.js
该命令会加载config.project1.dll.js,对应的配置内容如下:
var config = require('./webpack.config'); module.exports = config.webpackdll({ key:'project1' //根目录 ,rootPath:__dirname });
执行"npm rundll project1"命令,会输出两个文件一个是打包好的vendor.dll.js,一个就是manifest-vendor.json。由于在config.project1.dll.js中配置的key值是功能节点的名称"project1",所以生成的两个文件在apps/project1目录下,如下图所示。
其中manifest-vendor.json大致的样子如下图所示。
Webpack将每个库都进行了编号索引,之后的dll user可以读取这个文件,直接用id来引用。文件内容中的"name"对应的值'dll_vendor'就是webpack.config.js中关于Dll的配置中DllPlugin 中关于 name值的配置,如下所示。
new webpack.DllPlugin({ path: options.manifestPath||'apps/'+key+'/manifest-[name].json',//manifest.json 文件的输出路径,这个文件会用于后续的业务代码打包 name: options.manifestName||'dll_[name]',// dll 暴露的对象名,要跟 output.library 保持一致 context: options.manifestContext||rootPath,//解析包路径的上下文,这个要跟接下来配置的 webpack.config.js 一致 }),
2.关于Dll user的配置
DllReferencePlugin
的选项中,context
需要跟之前保持一致,这个用来指导Webpack匹配manifest中库的路径;manifest
用来引入刚才输出的manifest文件。
参考:http://www.tuicool.com/articles/NZ77re
五、开发环境
开发人员开发项目的时候,使用 webpack devserver启动项目。这里接着说一个命令"dev-project1",如下。
webpack-dev-server --devtool eval --display-error-details --progress --colors --hot --inline --config config.project1.js
config.project1.js的配置内容如下。
var config = require('./webpack.config'); //打印启动信息 config.initPrint(__filename); var path = require('path'); var cwd = process.cwd();//返回运行当前脚本的工作目录的路径 var baseRoot = "./apps/project1/src/" var options = { //定义标识名(如果有多个应用,可以写成 应用的名称) key : 'project1',//定义各模块的入口 modules : { bundle: './project1-index.js'//入口 } //根目录 ,rootPath:__dirname //webpack-dev-server启动的端口,port:8000 //相对路径 //,path: path.join(__dirname,'') //编译后的路径 //,filename: '[name]/build/bundle.js' //定义各种加载器 //,loaders: [] //定义各种插件 //,plugins:[] //定义路径别名,即:require(alias) ,alias: { "styles": path.join(cwd,baseRoot + "styles"),"less": path.join(cwd,baseRoot + "styles/less"),"actions": path.join(cwd,baseRoot + "actions"),"constants": path.join(cwd,baseRoot + "constants"),"images": path.join(cwd,baseRoot + "images"),"reducers": path.join(cwd,baseRoot + "reducers"),"stores": path.join(cwd,baseRoot + "stores"),"modules": path.join(cwd,baseRoot + "modules"),"components": path.join(cwd,baseRoot + "components"),"utils": path.join(cwd,baseRoot + "utils"),"routes": path.join(cwd,baseRoot + "routes"),"trd": path.join(cwd,baseRoot + "trd"),} } //服务端产出路径 if(process.env.NODE_ENV==='prod') {//生产环境 options.publicPath = '/project1-front'; }else if(process.env.NODE_ENV==='test') {//测试环境 //options.publicPath = '/icop-sampleapp-frontend'; }else{//本地开发环境 } //构建webpack配置 module.exports = config.webpack(options);
配置中的注解写的很明确了,就不在啰嗦了。关于webpack.config.js中的webpack方法配置如下。
执行 "npm run dev-project1"命令,启动工程,浏览器中输入“http://localhost:8000/apps/project1”,效果如下。
六、生产环境
发布到生产环境上的程序是稳定的,不允许开发人员直接操作。为了提升服务访问的效率,前端工程放到了要tomcat工程下。tomcat项目下的代码还是我们开发人员所看到的的代码?不是了已经!!!最后我要说的命令就是 "build-project1",如下所示。
set NODE_ENV=prod&&webpack --display-error-details --progress --colors -p --config config.project1.js
这个命令中设置了环境变量 NODE_ENV=prod,根据这个变量的配置,会有不同的 服务端产出路径 的配置。例如, config.project1.js中的示例代码,如下。
//服务端产出路径 if(process.env.NODE_ENV==='prod') {//生产环境 options.publicPath = '/project1-front'; }else if(process.env.NODE_ENV==='test') {//测试环境 //options.publicPath = '/icop-sampleapp-frontend'; }else{//本地开发环境 }
那么本地开发环境中对应的这个路径是啥呢?webpack.config.js中的webpack方法中有这样的配置,如下。
output: { path: options.path || path.join(rootPath,'apps/'+key)//编译后的输出路径,filename: options.filename || '/build/bundle.js',publicPath: options.publicPath || '/apps/'+key//服务端的路径,chunkFilename: "/build/[name]_[hash].chunk.js" },
也就是默认的publicPath是'/apps/project1'(因为传递的key值是 'project1')。执行命令"npm run build-project1",会在project1目录下生成 build文件夹,内如如下。
这些打包出来的文件是 tomcat中对应项目需要的。并且前端项目 project1中index.html也引入了其中的一些文件,如下所示。
如何把我们前端项目 发布到 tomcat上呢?很简单的,新建一个web工程,工程的名称要注意, 要和 生产环境对应的 服务器产出路径一致,即 "project1-front"。web.xml做一下简单配置,如下。
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1"> <display-name>project1-front</display-name> <welcome-file-list> <welcome-file>index.html</welcome-file> </welcome-file-list> </web-app>
接下把前端项目功能节点project1目录下面的文件(不包含"src"目录)copy到刚刚新建的web项目(project1-front)中的 WebContent 目录下,如下所示。
最后启动web工程,浏览器中输入"http://localhost:8029/project1-front",效果如下。
七、webpack的CommonsChunkPlugin提取公共代码,以及懒加载方式
相关链接:http://webpack.github.io/docs/long-term-caching.html
在webpack.config.js中webpack方法对应的配置如下所示。
路由各个功能节点的引入方式,变成如下。
比较一下,使用懒加载方式(project1)和不是懒加载方式(project2)build之后产生文件的差异。
apps/project1/build目录
apps/project2/build目录
很明显,懒加载方式可以更好的解决性能问题,加快页面加载速度。不同的功能模块加载对应的chunk文件就可以了。
八、nodejs中exports与module.exports的区别
http://cnodejs.org/topic/5231a630101e574521e45ef8
http://zihua.li/2012/03/use-module-exports-or-exports-in-node/
http://blog.csdn.net/zhou_xiao_cheng/article/details/52759632
http://www.cnblogs.com/pigtail/archive/2013/01/14/2859555.html
九、教训
开始用懒加载模式的时候,启动工程,出现如下错误。
vendor.dll.js:1 Uncaught Invariant Violation: Minified React error #130; visit http://facebook.github.io/react/docs/error-decoder.html?invariant=130&args[]=object&args[]= for the full message or use the non-minified dev environment for full errors and additional helpful warnings.
找了好长时间的原因,竟然是有一个js文件的导出方式的问题,如下。
然而懒加载的时候,使用的require。这也就是为啥 import可以,require不行的原因了。坑了我好久啊!
试了一下,要改成module.exports = ModuleRouters。这样才不会报错。
十、项目下载地址
https://github.com/hjzgg/webpack-react-redux-next