天天看點

express+webpack+react搭建項目

原文:http://www.cnblogs.com/feeiluuo/p/5580806.html

簡單介紹一下webpack

webpack的功能很多,打包js\css\html,壓縮,編譯less\sass,自動生成版本号等等,因為可以使用CommonJS等規範,可以和react很好地配合使用。

它的使用方法比gulp要複雜,但是能做的事情也要比gulp更多一些~~

webpack自己有插件,常用的比如

commonsPlugin、UglifyJsPlugin、ExtractTextPlugin、HtmlWebpackPlugin

等,實際項目中,也需要使用node自帶的一些子產品,比如

path、glob

等,這些子產品具體在下面講。

項目目錄

express+webpack+react搭建項目

經過webpack打包後,生成的html、css、js檔案都放在dist檔案夾下,以

dist/css、dist/js、dist/html

這樣的方式。

express搭建

安裝和使用就不說了,網上教程很詳細,這裡隻提一點,

express4.0

版本以上,把指令工具分離出來到

express-generator

了,需要另外安裝,

npm install -g express-generator

,否則項目搭建會出問題滴。

node安裝react

這個環境中,react是使用node安裝的,

npm install react --save-dev

,由于react 0.14版本把react拆分為

react

react-dom

,是以還需要将react-dom安裝一下,

npm install react-dom --save-dev

在檔案中直接引用就可以:

var React = require('react');
var ReactDom = require('react-dom');
           

分離以後,

react package

中包含

React.createElement、createClass、Component, .PropTypes,Children

這些API,而

react-dom

中包含

ReactDOM.render、unmountComponentAtNode、findDOMNode

注意對應使用

React

ReactDOM

調用。

另注意,react聲明元件時,第一個字母必須大寫。

webpack的安裝和配置

安裝

  • 安裝webpack:

    npm install webpack --save-dev

  • 安裝各種loader:

    webpack需要的loader有:

    html-loader、css-loader、style-loader、url-loader、jsx-loader、babel-loader等

    ,安裝方式

    npm install ***** --save-dev

    ,在安裝這些loader之前,需要先安裝

    file-loader

    ,在安裝

    babel-loader

    之前,需要先安裝

    babel-core

  • 安裝插件:

    常用插件

    commonsPlugin、UglifyJsPlugin、ExtractTextPlugin、HtmlWebpackPlugin

    等,

    ExtractTextPlugin、HtmlWebpackPlugin

    需要先

    npm install安裝

    commonsPlugin、UglifyJsPlugin

    為webpack自帶,無需額外安裝。

多頁面打包

當項目是單頁面時,可以直接寫死entry的入口檔案,也可以直接寫死打包出的html頁面的名稱和路徑,但當項目是多頁面時,把入口檔案和html打包名稱路徑等寫死就非常麻煩了,這時可以使用node子產品

glob

。使用方式見下面demo~

自動生成js和css的引用

html頁面裡不需要手動引入js和css,這裡webpack配置了生成帶引用的html,會自動把所需引用加入到html中

express+webpack+react搭建項目

是以一個簡單的html就像這樣,不需要寫

<link>

<script>

标簽

配置

貼一個

webpack.config.js

的demo~

/*
功能:打封包件,提取公共部分,并生成帶js\css引用的html頁面

打包前的檔案,靜态資源放在public/src下,html在views下
打包後統一放在public/dist裡

使用的node子產品:path、glob
使用的webpack插件:commonsPlugin   ExtractTextPlugin   HtmlWebpackPlugin

網上有說開啟webpack觀察者模式會導緻記憶體占用過高,可以用gulp調用webpack的方式解決
但是貌似這個項目并沒有這種問題~
 */


/**************************引入webpack***********************************/
var webpack = require('webpack');


/**************************引入node子產品path、glob*******************************/
var path = require('path'); 
//該子產品用于傳回比對指定模式的檔案名或目錄,
//由于本項目為多頁面,是以需要多個入口檔案和多個html
//需要這個子產品擷取檔案放入數組,需要時循環
var glob = require('glob');  


/****************************設定預設路徑*******************************/
/*
設定預設路徑distPath,在module.exports中的output的path處使用
所有打包出的檔案,路徑都在這個基礎上繼續
寫在這裡是因為比較突出。。直接寫在output的path當然也是可以的
 */
var distPath = path.join(__dirname,'/public/dist/');  


/*****************************聲明getEntry函數**************************/
/*
該函數使用glob的方法,拆分檔案路徑
目前有兩個地方使用了這個方法:
1. 循環view檔案夾,生成多個html打包的conf配置;
2. 循環js入口檔案
由于module.exprots中的entry項是個對象,是以這裡把entry設為{}
參數url為傳進來的需要擷取的檔案目錄的路徑
最後傳回的entry的格式:
{
  login : './public/src/js/Entry/user/login.js',
  register : './public/src/js/Entry/user/register.js'
  *******
}
在自己的實際項目中,按實際情況可以有其他處理方式~
 */
var getEntry = function (url) { 
    var entry = {}; 
    glob.sync(url).forEach(function (name) { 
        /*
        循環所有檔案,對檔案名做處理,并放入entry數組中,傳回entry
         */
        if(name.indexOf('views') != ){
            //是html頁面
            var n = name.substring(,name.lastIndexOf('.'));
        }else{
            //不是html頁面  這裡實際上隻有js頁面需要處理
            var n = name.substring((name.lastIndexOf('/') + ),name.lastIndexOf('.'));
        }
        var name = __dirname + name.substring(); 
        if(n.indexOf('.') != ){
            entry[n] = name; 
        }   
    }); 
    return entry;
};


/******************************使用webpack的插件********************************/
/*
    commonsPlugin,把公共部分提取出來
 */
var commonsPlugin = new webpack.optimize.CommonsChunkPlugin({
    // 提取出的公共子產品的名稱,js會打包為common.js,css為common.css
    // common.js會按照module.exports中output的路徑打包,
    // common.css會按照ExtractTextPlugin插件設定的路徑打包
    //如果按照網上的例子直接寫為common.js,
    //會導緻提取出來的公共css被打包成css/js/common.js/css
    name: 'common',   
    //chunks----從哪些檔案中提取
    //目前這裡不需要設定,因為所有js檔案都需要被提取
    //chunks: getEntry('./public/src/js/Entry/*/**.js')
    
}); 
/*
    ExtractTextPlugin,打出單獨的css包
*/
var ExtractTextPlugin = require("extract-text-webpack-plugin");  
/*
    HtmlWebpackPlugin,打包html
*/
var HtmlWebpackPlugin = require('html-webpack-plugin');  




/***********************設定module.exports中的plugins***************************/
/*
    定義一個數組,module.exports中的plugins項可以直接使用這個數組
 */
var plugins = []; 
/*
    添加打包公共檔案插件的調用
 */
plugins.push(commonsPlugin); 
/*
    調用ExtractTextPlugin,把單獨的css打到dist/css/下面,該路徑也是從distPath開始
    [name]為引用這個css檔案的js檔案的入口檔案打包後的名字,即入口檔案output後的名字
 */
plugins.push(new ExtractTextPlugin("css/[name].css")); 
/*
    加載jq,否則項目中使用jquery會報錯'$ is not defined',
    用jquery('#**')這樣的方式使用jquery當然也是不行滴~
 */
plugins.push(new webpack.ProvidePlugin({ 
    $: 'jquery'
}));



/**********************擷取所有html檔案,生成HtmlWebpackPlugin插件需要的conf配置**************************/
/*
調用getEntry,傳遞路徑為打包前的html檔案
 */
var pages = getEntry('./views/*/**'); 
/*循環pages*/
for(var chunkname in pages){  
    /*
        這裡使用webpack的HtmlWebpackPlugin插件
        conf為該插件的配置項
        将每個檔案的conf循環插入plugins,可以實作多頁面打包
    */
  var conf = {
    filename: 'html/'+chunkname+'.html',  //打包後的html存放路徑,也是從distPath開始
    template: pages[chunkname], //檔案模闆,就是打包前的html檔案
    inject: true, //可以對head和body做修改
    //設定該頁面引用的檔案,隻有符合條件的才會被引用
    //這裡是'common'和頁面同名的js\css檔案
    chunks : ['jquery','react','react-dom','common', chunkname.substring(chunkname.indexOf('/')+)],
    minify: { //壓縮HTML
        removeComments: true,
        collapseWhitespace: false
    },
    hash: true, //版本号,打出來的html中對css和js的引用自帶版本号
  }
  //把每個conf循環插入plugins
  plugins.push(new HtmlWebpackPlugin(conf));
}


/****************************添加對js和css的壓縮*************************/
plugins.push(new webpack.optimize.UglifyJsPlugin({    
             compress: {
                 warnings: false
             },
             except: ['$', 'require']    //排除關鍵字,不然會把這些都壓縮替換
         })
)



/**********************module.exports的entry配置*******************************/

//擷取所有入口檔案
var entryJS = getEntry('./public/src/js/Entry/*/**.js');
/*
把react\react-dom-jquery單獨打包,如果不寫的話,會把這些都打到common.js裡
可以解決common.js體積過大的問題~
*/
entryJS['react'] = ['react'];
entryJS['react-dom'] = ['react-dom'];
entryJS['jquery'] = ['jquery'];



/****************************webpack的總體配置******************************/
module.exports = {
    //入口檔案,這裡循環所有入口檔案,不需要每個都寫出來
    entry: entryJS,
    output: {
        //打封包件存放的絕對路徑,html、css、js都會按這個路徑打包
        path: distPath,  
        //網站運作時的通路路徑,不設定的話,打包出的html中的預設引用的路徑會是相對路徑
        publicPath: "/public/dist/",  
        //打包後的檔案名 
        filename: 'js/[name].js'  
    },
    resolve: {
        //require檔案的時候不需要寫字尾了,可以自動補全
        extensions: ['', '.js', '.jsx','.css']
    },
    module: {
        loaders: [//定義一系列加載器
            {test: /\.html$/,loader: "html"},  /*html*/
            {test: /\.js$/, loader: "babel"},      /*es6 to es5*/
            {test: /\.jsx$/,loader: 'jsx-loader'},    /*jsx to js,es5 to es6*/
            {test: /\.css$/, loader: ExtractTextPlugin.extract("style-loader", "css-loader")},                      /*css to css*/
            {test: /\.(jpg|png)$/, loader: "url?limit=8192"},  //limit=8192表示圖檔大小機關是k  小于這個值走内聯大于這個值走外聯             /*images 打包*/
            {test: /\.less$/, loader: "style!css!less"}                 /*less to css*/
        ]
    },
    plugins: plugins , //使用插件
    //watch: true //開啟觀察者模式
};
           

未添加的功能:

圖檔打包,按需加載,react熱替換

後面陸續加上~

ITDogFire--sky

繼續閱讀