博主最近在學習react redux,順便搭建了一個基於webpack的前端項目架構,在此寫文記錄一下,同時教會新入webpack坑的小夥伴們如何在項目中玩弄,額!玩轉webpack。
github demo傳送門:redux-demo 若是以爲寫的還能夠的話記得star (ง •̀_•́)ง 支持一下博主javascript
整個項目的目錄的話是跟普通react redux項目相同的目錄結構,目錄結構以下:css
-redux-demo -bin -routes -src -js -action -components -constants -page -reducers -less -template -views(項目開發視圖生成目錄) -build(項目開發打包目錄) -output(項目生產環境打包目錄) -app.js -config.js -util.js -webpack.config.js -webpack.deploy.js
以上項目目錄中build文件夾爲開發環境中的靜態資源的生成目錄,views爲開發環境下模板生成目錄,output則是生產環境中打包出來的靜態資源及模板目錄,webpack.config.js與webpack.deploy.js分別爲webpack的開發config文件及發佈config文件,util.js包含了一些關於file操做的公用方法,config.js則包含了包括cdn地址,文件入口與打包路徑等信息html
開發模式的config主要內容以下:前端
var path = require('path'), fs=require('fs'), configFile=require('./config.js'), util=require('./util.js'), webpack = require('webpack'), optimize = webpack.optimize, plugins=[],staticPath=configFile.STATICPATH||"/static"; //額外插件 //用以生產單獨的css文件 var ExtractTextPlugin = require('extract-text-webpack-plugin'); var extractLESS = new ExtractTextPlugin('css/[name].css'), HtmlWebpackPlugin = require('html-webpack-plugin'), viewList=util.getView(configFile.VIEWENTER),htmlList=[]; plugins.push(new optimize.CommonsChunkPlugin("common",'js/common.js')); plugins.push(extractLESS); for(var index in viewList){ plugins.push(new HtmlWebpackPlugin({ title: 'My App', filename: '../views/'+index+'.ejs', template: viewList[index], chunks: ["common",index] })); } module.exports = { entry: util.getEntry(configFile.JSENTER), output: { path: path.join(__dirname, '/build'), filename: 'js/[name].js', publicPath:staticPath }, plugins:plugins, module: { loaders: [{ test: /\.less$/, loader: extractLESS.extract(['css','less']) },{ test: /\.js$/, loader:"babel?sourceMap" },{ test: /\.(png|jpg)$/, loader: 'url-loader?limit=8192&name=/image/[name].[ext]' }] }, resolve: { root: path.resolve('./src') } }
咱們逐句分析一下這個config文件的內容及所作的事情
css的處理java
var ExtractTextPlugin = require('extract-text-webpack-plugin'); var extractLESS = new ExtractTextPlugin('css/[name].css'), loader: extractLESS.extract(['css','less'])
對於css我不但願嵌入到html中因此引入了extract-text-webpack-plugin中間件它能夠將插件中引入的css生成一個獨立位置ExtractTextPlugin傳參位置相對於webpack output中的path。這裏對於路徑其實要注意一下的,由於webpack開發的話模板是HtmlWebpackPlugin動態生成js css等靜態資源引用的,不管開發仍是部署環境請都配置上publicPath,同時配置打包路徑不要使用../css/[name.css]這種相對路徑,若是不配置上並且用這樣連接,你會發現你的模板裏的引用會變成這樣自/build/../css/[name.css],額!感受仍是至關的坑(不知道是否是博主的使用方式有問題,知道的能夠留言指正一下)。
模板處理node
var HtmlWebpackPlugin = require('html-webpack-plugin'), plugins=[], //獲取模板文件夾下全部的模板的文件路徑 viewList=util.getView(configFile.VIEWENTER); //循環遍歷模板路徑對象,生成HtmlWebpackPlugin實例 for(var index in viewList){ plugins.push(new HtmlWebpackPlugin({ title: 'My App', filename: '../views/'+index+'.ejs', template: viewList[index], chunks: ["common",index] })); }
這一段代碼就是經過HtmlWebpackPlugin來進行模板處理的,實際上很簡單獲取須要打包的的文件夾下的全部模板文件而後循環遍歷生成HtmlWebpackPlugin的實例放入到plugins的列表當中,其中引入的模塊限於公用模塊及模板同名的模塊,而HtmlWebpackPlugin所作的也至關簡單只是單純的將js文件插入到尾部,將css引用插入到頭部,也能夠本身指定位置,具體方法能夠參考HtmlWebpackPlugin的文檔,不過目前看來這樣簡單的配置也可以知足基本開發了。
在博主研究webpack的過程當中發現不少的文章中並不會在開發環境下加入HtmlWebpackPlugin,但實際上HtmlWebpackPlugin的打包並不會去解析現有的js和css的link引用只會簡單的追加引用,好比下面的模板:react
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> </head> <body> 404 <script type="text/javascript" src="../js/error.js"></script></body> </body> </html>
打包後:webpack
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> </head> <body> 404 <script type="text/javascript" src="../js/error.js"></script></body> <script type="text/javascript" src="../js/error_4588335f1991526d80b8.js"></script></body> </body> </html>
爲何博主要這麼強調這個,由於不少比較傳統的前端開發都是不會在開發過程當中對模板進行處理,只會在部署的時候對模板進行靜態資源連接處理和其餘一些處理,這也是我在一開始研究webpack的時候的一個誤區琢磨了很久關於webpack的打包部署,實際上webpack就是但願全部的文件都是經由其動態處理生成的。因此開發環境中會有一個template目錄用於咱們開發還有一個views用來存儲處理後的的模板文件。全部的這樣子開發,啓動開發環境
template中error.ejs:git
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> </head> <body> 404 </body> </html>
生成views中的error.ejsgithub
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> </head> <body> 404 <script type="text/javascript" src="../js/error.js"></script></body> </html>
後端模板文件夾指定爲views就行了,這樣就能愉快的開發了,也不會影響到後續的打包部署。
以後是js的處理
plugins.push(new optimize.CommonsChunkPlugin("common",'js/common.js')); module.exports = { entry: util.getEntry(configFile.JSENTER), output: { path: path.join(__dirname, '/build'), filename: 'js/[name].js', publicPath:staticPath }, plugins:plugins, module: { loaders: [{ test: /\.less$/, loader: extractLESS.extract(['css','less']) },{ test: /\.js$/, loader:"babel?sourceMap" },{ test: /\.(png|jpg)$/, loader: 'url-loader?limit=8192&name=/image/[name].[ext]' }] }, resolve: { root: path.resolve('./src') } }
CommonsChunkPlugin指定生成公用js文件,第一個是模塊的命名,第二個是指定存儲路徑,必定要記得指定模塊名,否則HtmlWebpackPlugin 的chunks: ["common",index]是不會把common打包進去的,由於不指定對於它來講就是沒有這個模塊。其餘的話就是最基礎的webpack的配置,不懂得請詳細閱讀網上相關的webpack基礎入門文章。
生產環境中的config文件webpack.deploy.js:
var path = require('path'), fs=require('fs'), configFile=require('./config.js'), util=require('./util.js'), webpack = require('webpack'), optimize = webpack.optimize, plugins=[],staticPath=configFile.STATICPATH||"/static", cdnPath=configFile.CDN||"", publicPath=cdnPath+staticPath,outputPath=configFile.OUTPUT||"/output"; //額外插件 //用以生產單獨的css文件 var ExtractTextPlugin = require('extract-text-webpack-plugin'), extractLESS = new ExtractTextPlugin('css/[name]_[hash].css'), HtmlWebpackPlugin = require('html-webpack-plugin'), viewList=util.getView(configFile.VIEWENTER),htmlList=[]; //清空打包生產後的文件 util.rmdirSync(path.join(__dirname, outputPath)); for(var index in viewList){ plugins.push(new HtmlWebpackPlugin({ title: 'My App', filename: '../views/'+index+'.ejs', template: viewList[index], chunks: ["common",index] })); } plugins.push(new optimize.CommonsChunkPlugin("common",'js/common_[hash].js')); plugins.push(extractLESS); module.exports = { entry: util.getEntry(configFile.JSENTER), output: { path: path.join(__dirname, outputPath+'/static'), filename: 'js/[name]_[hash].js', publicPath:publicPath }, plugins:plugins, module: { loaders: [{ test: /\.less$/, loader: extractLESS.extract(['css','less']) },{ test: /\.js$/, loader:"babel?sourceMap" },{ test: /\.(png|jpg)$/, loader: 'url-loader?limit=8192&name=/image/[hash].[ext]' }, { test: /\.html$/, loader: "html" }] }, resolve: { root: path.resolve('./src') } }
實際上由於開發環境中就已經對模板中的靜態資源進行處理了,因此部署環境配置就很簡單了
獲取config中cdn路徑配置把他整合到publicPath中
var cdnPath=configFile.CDN||"", publicPath=cdnPath+staticPath,outputPath=configFile.OUTPUT||"/output";
爲資源打上hash值
//這裏指定css文件的hash值 var ExtractTextPlugin = require('extract-text-webpack-plugin'), extractLESS = new ExtractTextPlugin('css/[name]_[hash].css'), //這裏指定公用文件的hash值 plugins.push(new optimize.CommonsChunkPlugin("common",'js/common_[hash].js')); module.exports = { entry: util.getEntry(configFile.JSENTER), output: { path: path.join(__dirname, outputPath+'/static'), //這裏指定js的文件hash值 filename: 'js/[name]_[hash].js', publicPath:publicPath }, plugins:plugins, module: { loaders: [{ test: /\.less$/, loader: extractLESS.extract(['css','less']) },{ test: /\.js$/, loader:"babel?sourceMap" },{ test: /\.(png|jpg)$/, //這裏指定圖片路徑的hash值 loader: 'url-loader?limit=8192&name=/image/[hash].[ext]' }, { test: /\.html$/, loader: "html" }] }, resolve: { root: path.resolve('./src') } }
這樣基本上就行了,固然還有涉及到cdn資源上傳的一些問題,這裏沒有,可使用gulp-sftp,也有sftp-webpack-plugin試試。
首先配置好package.json的命令行
"scripts": { "start": "node app", "dev": "webpack --watch", "deploy": "webpack --config webpack.deploy.js -p" }
"dev": "webpack --watch"配置Dev啓動監聽文件變化運行webpack,--watch目前新版本應該沒有內存泄漏問題了
"deploy": "webpack --config webpack.deploy.js -p"配置deploy -p傳參表示對js,css內容進行壓縮混淆
"start": "node app" 配置express啓動
而後用webstorm打開項目目錄點擊npm的dev,固然你也能夠選擇其餘編輯器,只要開發的時候命令行運行npm run dev命令就行了
最後你就能夠愉快的開始新的搬磚之旅了。
ps:博主用ExtractTextPlugin發現 extractLESS.extract(['css','less'])若是調換成extractLESS.extract(['less','css'])的話啓動就會報錯估計是less跟css-loader不兼容,結果到如今都用不了惟一css名,不知道有沒有誰解決過這個問題,有的話請留言告知,雖然博主實際工程中並不用less,可是仍是很想知道緣由。