博主最近在学习react redux,顺便搭建了一个基于webpack的前端项目架构,在此写文记录一下,同时教会新入webpack坑的小伙伴们如何在项目中玩弄,额!玩转webpack。
github demo传送门:redux-demo 如果觉得写的还可以的话记得star (ง •̀_•́)ง 支持一下博主

项目结构

整个项目的目录的话是跟普通react redux项目相同的目录结构,目录结构如下:

-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地址,文件入口与打包路径等信息

开发环境下webpack config

开发模式的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的处理

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],额!感觉还是相当的坑(不知道是不是博主的使用方式有问题,知道的可以留言指正一下)。
模板处理

  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引用只会简单的追加引用,比如下面的模板:

<!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>

打包后:

<!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:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
    404
</body>
</html>

生成views中的error.ejs

<!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,但是还是很想知道原因。


AlfredMou
455 声望11 粉丝