WebPack2配置随记

接触前端还不到一年,之前公司有一个UI的工程由我来独立完成,犹豫了几秒后决定选用react,花几天时间看完了阮一峰老师的React 技术栈系列教程就开始动手了,其中在webpack的配置上花了很多时间,因此整理了一下当成一个学习笔记吧

webpack2大部分的配置还是和webpack1一样,官方文档中有一篇从1迁移到2的指南

一、安装webpack相关组件以及热加载模块

npm install -D webpack webpack-dev-middleware webpack-dev-server webpack-hot-middleware babel-preset-react-hmre

在webpack1的时候是使用的react-hot-loader,后来无意间发现了这篇文章The Death of React Hot Loader(只看懂了标题),果断弃用之,选用react-transform-hmr

二、配置实例

如下方的简单示例

const webpack = require('webpack');
module.exports = {
  entry: {
    app: 'entrance.js'
  },
  output: {
    path: 'fvrd/dist',
    filename: '[name].js',
  },
  module: {
    rules:[{
      test: /\.css$/,
      use:['style-loader', 'css-loader']
    }],
  },
  plugins: [
    new webpack.DefinePlugin({
      'process.env': {
        NODE_ENV: JSON.stringify('production')
      }
    })
  ],
};
  1. entry是页面入口文件配置,Webpack 会分析入口文件,解析包含依赖关系的各个文件,当入口文件不止一个时,可以将其配置为数组,output 是输出项配置,如例子中的输出为app.js
  2. module为加载器配置,例子中定义了.css文件的加载规则,会生成style标签放在head中。webpack1的时候use中的-loader后缀可以不写,webpack2必须要写
  3. plugins为插件配置,例子中定义了一个环境变量,在代码中console.log(process.env.NODE_ENV)将打印production

三、重要的插件

  • DefinePlugin
    注入环境变量,如在代码中需要开发和生产环境执行不一样的代码,可以使用此插件
new webpack.DefinePlugin({
  'process.env': {
     NODE_ENV: JSON.stringify('production')
   }
 })
 //代码中
 if(process.env.NODE_ENV==='production'){
    //do something
 }else{
    //do something
 }
  • DllPlugin DllReferencePlugin
    解决打包速度慢的大招。第三方库是不经常变动的,在一个配置文件中使用DllPlugin把第三方库或者自己写的公共组件打包在一起并生成一个json文件,在另外一个配置文件中使用DllReferencePlugin就不会重复打包第三方库了。缺点是至少需要两个webpack配置文件
//一个配置文件
entry: {
    lib: ['react','react-dom','react-router','redux','...'],
}
new webpack.DllPlugin({
     path: path.join(__dirname, 'fvrd/dist/manifest.json'),
     name: '[name]',//在这个例子中将生成lib.js
     context: __dirname,
})
//另一个配置文件
new webpack.DllReferencePlugin({
  context: __dirname,
  manifest: require('./fvrd/dist/manifest.json'),
})
  • ProvidePlugin
    用来定义全局变量
//以后使用jquery无需再require('jquery')
new webpack.ProvidePlugin({
 $: 'jquery',
  jQuery: 'jquery',
  'window.jQuery': 'jquery',
  'window.$': 'jquery',
})
  • UglifyJsPlugin
    压缩代码
new webpack.optimize.UglifyJsPlugin({//压缩代码
  output: {
    comments: false, //不移除注释
  },
  compress: {
    warnings: false//忽略警告,某些没有用到的组件在打包时会被移除,这时会产生警告,无需在意,webpack1默认true,webpack2默认false
  },
})
  • HtmlWebpackPlugin
    非默认组件,需要一个默认的index.html,js和css会自动插入此模板,不过通过DllPlugin生成的公共包需要自己手动引入
//npm install html-webpack-plugin --save-dev
let HtmlWebpackPlugin = require('html-webpack-plugin');
//...
new HtmlWebpackPlugin({
  favicon:'./src/images/icon_logo.png', //favicon路径
  filename: '../index.html', //生成的html存放路径,相对于 path
  template: './src/template/index.html', //html模板路径
  inject: true,
  hash: true,
  minify: {//压缩HTML文件
    removeComments:true,    //移除HTML中的注释
    collapseWhitespace:true    //删除空白符与换行符
  }
})
  • ExtractTextPlugin
    主要用来将css文件分离开
//webpack2需要安装beta版
//npm install --save-dev extract-text-webpack-plugin@beta
let ExtractTextPlugin = require('extract-text-webpack-plugin');
//...
module:{
    rules:[{
      test: /\.less$/,
      use: ExtractTextPlugin.extract({
        fallback: 'style-loader',
        use:[{
          loader: 'css-loader',
          options: {
            sourceMap: true
          }
        },{
          loader: 'less-loader',
          options: {
            sourceMap: true
          }
        }]
      })
    }]
}
//plugins中
new ExtractTextPlugin('[name].css')
  • HotModuleReplacementPlugin NoEmitOnErrorsPlugin
    开发环境中使用,一个热替换插件,一个定义遇到错误继续
new webpack.HotModuleReplacementPlugin(),
new webpack.NoEmitOnErrorsPlugin()

下面为我的工程中所使用的配置文件

四、DllPlugin配置

使用DllPlugin将第三方公共模块打包在一起,可以极快的提高打包速度,在生产和开发环境都需要用到

//webpack.config.dll.js
const webpack = require('webpack');
const path = require('path');
module.exports = {
  entry: {
    lib: [//公共组件
      'react',
      'react-dom',
      'react-router',
      'react-bootstrap',
      'redux',
      'react-redux',
      'redux-thunk',
      'react-intl',
      'intl',
      'react-dnd',
      'react-dnd-html5-backend',
      'immutable',
      'antd',
      'moment',
      'isomorphic-fetch',
      'pure-render-decorator',
      // 'lodash',
    ],
  },
  output: {
    path: 'fvrd/dist',
    filename: '[name].js',
    library: '[name]',
  },
  module: {
    rules:[{
      test: /\.css$/,
      use:['style-loader', 'css-loader', 'autoprefixer-loader']
    },{
      test: /\.less$/,
      use:['style-loader', 'css-loader', 'autoprefixer-loader', 'less-loader']
    }],
  },
  plugins: [
    new webpack.DefinePlugin({
      'process.env': {
        NODE_ENV: JSON.stringify('production')
      }
    }),
    new webpack.DllPlugin({
      path: path.join(__dirname, 'fvrd/dist/manifest.json'),
      name: '[name]',
      context: __dirname,
    }),
    new webpack.optimize.UglifyJsPlugin({
      compress: {
        warnings: false
      },
      mangle: {
        except: ['exports', 'require']
      }
    })
  ],
};

五、生产环境配置

在这里遇到了一个问题,由于react热加载模块是一个babel插件,且只用在开发模式下,因此在.babelrc中需要如下配置

"env": {
        "development": {
            "presets": ["react-hmre"]
        }
    }

然而无论实在命令行中还是配置文件中定义的环境变量在webpack配置文件中都获取不到,后来在配置文件中加上process.env.BABEL_ENV='production';后问题解决

//webpack.config.dist.js
let path = require('path');
let webpack = require('webpack');
let ExtractTextPlugin = require('extract-text-webpack-plugin');
let HtmlWebpackPlugin = require('html-webpack-plugin');

let ROOT_PATH = path.resolve(__dirname);
let APP_PATH = path.resolve(ROOT_PATH, 'src');
let APP_FILE = path.resolve(APP_PATH, 'app');
let BUILD_PATH = path.resolve(ROOT_PATH, 'fvrd/dist');
process.env.BABEL_ENV='production';
module.exports = {
  entry: {
    app: ['babel-polyfill',APP_FILE],
  },
  output: {
    publicPath: '/fvrd/dist/',
    path: BUILD_PATH,
    filename: '[name].js',
    chunkFilename: '[name].[chunkhash:5].min.js',
  },
  module: {
    rules:[{
      test: /\.css$/,
      use: ExtractTextPlugin.extract({
        fallback: 'style-loader',
        use:[{
          loader: "css-loader",
          options: {
            sourceMap: true
          }
        }]
      })
    },{
      test: /\.less$/,
      use: ExtractTextPlugin.extract({
        fallback: 'style-loader',
        use:[{
          loader: 'css-loader',
          options: {
            sourceMap: true
          }
        },{
          loader: 'less-loader',
          options: {
            sourceMap: true
          }
        }]
      })
    }, {
      test: /\.(eot|woff|svg|ttf|woff2|gif|appcache)(\?|$)/,
      exclude: /^node_modules$/,
      use:[{
        loader: 'file-loader',
        options: {
          name: '[name].[ext]',
        }
      }],
    }, {
      test: /\.(png|jpg|gif)$/,
      exclude: /^node_modules$/,
      use:[{
        loader: 'url-loader',
        options: {
          name: 'images/[hash:8].[name].[ext]',
          limit: '8192'
        }
      }],
    }, {
      test: /\.jsx?$/,
      exclude: /^node_modules$/,
      use:['babel-loader'],
    }],
  },
  plugins: [
    new webpack.DefinePlugin({
      'process.env': {
        NODE_ENV: JSON.stringify('production'),
        BABEL_ENV: JSON.stringify('production'),
      }
    }),
    new HtmlWebpackPlugin({
      favicon:'./src/images/icon_logo.png',
      filename: '../index.html',
      template: './src/template/index.html',
      inject: true,
      hash: true,
      minify: {
        removeComments:true,
        collapseWhitespace:true
      }
    }),
    new ExtractTextPlugin('[name].css'),
    new webpack.DllReferencePlugin({
      context: __dirname,
      manifest: require('./fvrd/dist/manifest.json'),//此处路径为上面webpack.config.dll.js中DllPlugin插件中的path
    }),
    new webpack.optimize.UglifyJsPlugin({
      output: {
        comments: false,
      },
      compress: {
        warnings: false
      },
    })
  ],
  resolve: {
    extensions: ['.js', '.jsx', '.css'] //后缀名自动补全
  }
};

六、开发环境配置

开发环境实现react的热加载,有两个配置文件(当然可以配置为一个)

//webpack.config.hot.js
let path = require('path');
let webpack = require('webpack');

let ROOT_PATH = path.resolve(__dirname);
let APP_PATH = path.resolve(ROOT_PATH, 'src');
let APP_FILE = path.resolve(APP_PATH, 'app');
let BUILD_PATH = path.resolve(ROOT_PATH, '/fvrd/dist');

module.exports = {
  entry: {
    app: [
      'webpack-dev-server/client?http://172.16.16.30:8088',
      'webpack/hot/only-dev-server',
      'babel-polyfill',
      APP_FILE,
    ],
  },
  output: {
    publicPath: '/fvrd/dist/', //webpack-dev-server访问的路径
    path: BUILD_PATH, //发布文件地址
    filename: '[name].js', //编译后的文件名字
    chunkFilename: '[name].[chunkhash:5].min.js',
  },
  module: {
    rules:[{
      test: /\.css$/,
      use:['style-loader', 'css-loader', 'autoprefixer-loader']
    },{
      test: /\.less$/,
      use:['style-loader', 'css-loader', 'autoprefixer-loader', 'less-loader']
    }, {
      test: /\.(eot|woff|svg|ttf|woff2|gif|appcache)(\?|$)/,
      exclude: /^node_modules$/,
      use:[{
        loader: 'file-loader',
        options: {
          name: '[name].[ext]',
        }
      }],
    }, {
      test: /\.(png|jpg|gif)$/,
      exclude: /^node_modules$/,
      use:[{
        loader: 'url-loader',
        options: {
          name: 'images/[hash:8].[name].[ext]',
          limit: '8192'
        }
      }],
    }, {
      test: /\.jsx?$/,
      exclude: /^node_modules$/,
      use:['babel-loader'],
    }],
  },
  plugins: [
    new webpack.DefinePlugin({
      'process.env': {
        NODE_ENV: JSON.stringify('development')
      }
    }),
    new webpack.DllReferencePlugin({
      context: __dirname,
      manifest: require('./fvrd/dist/manifest.json'),
    }),
    new webpack.HotModuleReplacementPlugin(),
    new webpack.NamedModulesPlugin(),
    new webpack.NoEmitOnErrorsPlugin(),
  ],
  resolve: {
    extensions: ['.js', '.jsx', '.css'] //后缀名自动补全
  }
};
//server_hot.js
let webpack = require('webpack');
let WebpackDevServer = require('webpack-dev-server');
let config = require('./webpack.config.hot');

let server = new WebpackDevServer(webpack(config), {
  publicPath: config.output.publicPath,
  hot: true,
  historyApiFallback: true,
  inline: true,
  // progress: true,
  stats: {
    colors: true
  },
});

//将其他路由,全部返回index.html
server.app.get('*', function(req, res) {
  res.sendFile(__dirname + '/index.html')
});
server.listen(8088, function() {
  console.log('监听8088端口')
});

七、打包

在package.json中定义如下

"scripts": {
    "hot": "node server_hot.js",
    "dll": "webpack --config webpack.config.dll.js --progress --colors -p",
    "dist": "webpack --config webpack.config.dist.js --progress --colors -p"
  }
#生产环境打包
npm run dll
npm run dist

#开发环境
npm run dll
npm run hot

八、其余配置文件以及代码结构

package.json

{
  "name": "fvrd_ui",
  "version": "3.1.0",
  "description": "react-fvrd_ui",
  "main": "./server_hot.js",
  "scripts": {
    "hot": "node server_hot.js",
    "dll": "webpack --config webpack.config.dll.js --progress --colors -p",
    "dist": "webpack --config webpack.config.dist.js --progress --colors -p"
  },
  "author": "stsc team",
  "dependencies": {
    "antd": "^2.5.2",
    "bootstrap": "^3.3.7",
    "immutable": "^3.8.1",
    "intl": "^1.2.5",
    "moment": "^2.17.1",
    "pure-render-decorator": "^1.2.1",
    "rc-pagination": "^1.7.0",
    "react": "^15.3.2",
    "react-bootstrap": "^0.30.7",
    "react-dnd": "^2.1.4",
    "react-dnd-html5-backend": "^2.1.2",
    "react-dom": "^15.3.2",
    "react-intl": "^2.2.3",
    "react-redux": "^5.0.2",
    "react-router": "^3.0.0",
    "redux": "^3.6.0",
    "redux-thunk": "^2.1.0"
  },
  "devDependencies": {
    "autoprefixer-loader": "^3.2.0",
    "babel-core": "^6.23.1",
    "babel-loader": "^6.2.8",
    "babel-plugin-import": "^1.1.0",
    "babel-plugin-react-intl": "^2.3.1",
    "babel-plugin-react-transform": "^2.0.2",
    "babel-plugin-transform-class-properties": "^6.23.0",
    "babel-plugin-transform-decorators-legacy": "^1.3.4",
    "babel-plugin-transform-object-assign": "^6.8.0",
    "babel-polyfill": "^6.23.0",
    "babel-preset-latest": "^6.16.0",
    "babel-preset-react": "^6.23.0",
    "babel-preset-react-hmre": "^1.1.1",
    "babel-preset-stage-3": "^6.17.0",
    "body-parser": "^1.15.1",
    "chunk-manifest-webpack-plugin": "^1.0.0",
    "css-loader": "^0.26.1",
    "express": "^4.14.0",
    "extract-text-webpack-plugin": "^2.0.0-rc.3",
    "file-loader": "^0.10.0",
    "html-webpack-plugin": "^2.22.0",
    "jsx-loader": "^0.13.2",
    "less": "^2.6.1",
    "less-loader": "^2.2.3",
    "postcss-loader": "^1.3.0",
    "react-transform-catch-errors": "^1.0.2",
    "react-transform-hmr": "^1.0.4",
    "redbox-react": "^1.3.3",
    "redux-promise": "^0.5.3",
    "resolve-url-loader": "^1.6.0",
    "style-loader": "^0.13.1",
    "url-loader": "^0.5.7",
    "webpack": "^2.2.1",
    "webpack-dev-middleware": "^1.8.4",
    "webpack-dev-server": "^2.3.0",
    "webpack-hot-middleware": "^2.13.2"
  }
}

.babelrc

{
    "presets": [["latest", {"modules": false}], "react", "stage-3"],
    "plugins": [
        "transform-decorators-legacy",
        "transform-class-properties",
        ["import", {"libraryName": "antd","style": true}],
        ["react-intl", {"messagesDir": "./fvrd/dist/"}]
    ],
    "env": {
        "development": {
            "presets": ["react-hmre"]
        }
    }
}

打包脚本 package.sh

#!/bin/sh
svn_version=`svn info 2>/dev/null | grep "Revision:" | sed 's/Revision: //'`
version="fvrd_ui_r3.1.0_$svn_version"
chmod +x node_modules/.bin/*
npm run dll
npm run dist
mkdir $version
cp -r fvrd $version
cp install.sh $version
find $version -name ".svn" -exec rm -fr {} \;
tar -zcvf $version.tar.gz $version

WebPack2配置随记_第1张图片 WebPack2配置随记_第2张图片

你可能感兴趣的:(前端小记)