磨刀不误砍材工,React项目的webpack配置

写在开头

作为一个用过ng,vue的开发者,不玩玩React不好面试啊,万一面试官问,你用了ng,vue怎么不用React呢?那不是尴尬了。
本着都试试的想法,而正好有一个应用正好想要重写(原来用的vue1.0),于是开始了React之路。

本文内容

  1. webpack2与webpack1的不同
  2. 项目结构配置
  3. webpack配置
  4. 资源服务器配置

如果你熟悉vue脚手架的webpack配置,或者熟悉webpack配置,那么你没必要看下去了。下面介绍的就是vue脚手架webpack配置。

webpack2

为了简单快速的搭建好webpack环境,直接从vue的配置复制过来修修改改,直接开始跑了。结果是可预见的,因为vue中使用的webpack 1.X版本,而最新下载的包已经是2.X版本。于是根据错误提示,首先发现webpack的loader配置已经发现了改变。其次发现不再支持除了官方定义的属性。

通过文档Migrating from v1 to v2

  • 可发现 moduleloaders属性改为了rules,通过在rules属性添加rule对象,配置rule.use来指定loader,也可使用rule.loader对象属性,文档介绍说loader属性是use属性的快照。在一个rule中使用多个loader,webpack将从右向左的顺序使用loader

Loaders can be chained by passing multiple loaders, which will be applied from right to left (last to first configured)

  • loaderquery也改成了options,如果还是使用query则将会收到警告。
  • loader名称需要手动添加-loader,也可以通过下面这种方式继续支持,只是不推荐。
resolveLoader: {
   moduleExtensions: ["-loader"]
}
  • 不在需要json-loader
  • 支持解析 import 和 exports 关键字了,不再需要 babel 对上面两个关键字进行编译。

更多更新请查看Migrating from v1 to v2

项目结构

+ react                                   项目目录
   + assets                               静态文件目录
   + build                                webpack配置目录
      - build.js                          项目发布脚本
      - config.js                         项目基本配置文件
      - dev-server.js                     热加载脚本
      - dev-server.js                     启动资源服务器脚本
      - webpack.base.conf.js              webpack共用配置
      - webpack.dev.conf.js               开发环境配置
      - webpack.build.conf.js             项目发布配置
   + node_modules
   + src                                  项目开发目录
      + conponents                        组件目录
      + styles                            样式组件目录
      - main.js                           入口文件
   + index.html                           HTML模板文件,供html-webpack-plugin插件使用
   + package.json                  

webpack配置

基础配置
// config.js      
const path = require('path')
let config = {
  root: path.join(__dirname, '../'), // 项目根目录
  assets: path.join(__dirname, '../assets'), // 静态文件目录
  assetsDirectory: 'assets', // 静态文件目录名称
  publicPath: '/', // webpack output.publicPath,网页中URL与本地文件的相对路径
  dist: path.join(__dirname, '../dist'), // 发布文件输出文件夹
  index: path.join(__dirname, '../dist/index.html') // 发布时HTML输出文件路径
}
module.exports = {
  dev: Object.assign({}, config, {
    port: 3333, // 资源服务器端口
    proxy: { // 双服务器开发时,代理请求到后端服务器,如在浏览器请求http://loaclhost:3333/api/users则会请求http://localhost:3000/api/users
      '/api': {
        target: 'http://localhost:3000',
        changeOrigin: true
      },
      '/img': {
        target: 'http://localhost:3000',
        changeOrigin: true
      },
      '/offline.manifest': {
        target: 'http://localhost:3000',
        changeOrigin: true
      }
  }}),
  pro: Object.assign({}, config, {
  })
}

公共配置
// webpack.base.conf.js
const path = require('path')
const config = require('./config')
const projectRoot = path.resolve(__dirname, '../')
module.exports = {
  entry: {
    app: './src/main.js'
  },
  output: {
    path: config.pro.root,
    publicPath: process.env.NODE_ENV === 'production' ? config.pro.publicPath : config.dev.publicPath,
    filename: '[name].js'
  },
  resolve: {
    extensions: ['json', 'jsx', '.js'],
    alias: {
      'src': path.resolve(__dirname, '../src'),
      'assets': path.resolve(__dirname, '../assets'),
      'components': path.resolve(__dirname, '../src/components')
    },
    modules: [
      path.resolve(__dirname, '../node_modules')
    ]
  },
  module: {
    rules: [{
      test: /\.js$/,
      use: [
         {
          loader: 'babel-loader',
          options: {
            presets: [
              [
                "es2015",
                {
                  "modules": false
                }
              ],
              "react",
              "stage-0"
            ]
          }
        },
        'eslint-loader'
      ],
      include: [
        projectRoot
      ],
      exclude: [
        /node_modules/
      ]
    }]
  }
}
开发环境webpack配置
// webpack.dev.conf.js  
const webpack = require('webpack')
const merge = require('webpack-merge')
const baseConfig = require('./webpack.base.conf')
const HtmlWebpackPlugin = require('html-webpack-plugin')

Object.keys(baseConfig.entry).forEach(function (name) {
  // 这里在每个`entry`前添加./build/dev-client以接受webpack发出的事件,并处理
  baseConfig.entry[name] = ['./build/dev-client'].concat(baseConfig.entry[name])
})

module.exports = merge(baseConfig, {
  devtool: '#eval-source-map',
  plugins: [
    // 在前端页面中判断运行环境
    new webpack.DefinePlugin({
      'process.env': {NODE_ENV: '"development"'}
    }),
    new webpack.HotModuleReplacementPlugin(),
    // 在webpack 2中使用NoErrorsPlugin会有警告提示
    new webpack.NoEmitOnErrorsPlugin(),
    // 读取HTML模板文件,并输出HTML文件,开发环境实际输出到内存中
    new HtmlWebpackPlugin({
      filename: 'index.html',
      template: 'index.html',
      inject: true
    })
  ]
})

资源服务器配置

// dev-server.js

const express = require('express')
const webpack = require('webpack')
// 代理中间件
const proxyMiddleware = require('http-proxy-middleware')
// 热加载中间件
const devMiddleware = require('webpack-dev-middleware')
const hotMiddleware = require("webpack-hot-middleware")
// history API处理中间件
const history = require('connect-history-api-fallback')

const webpackConfig = require('./webpack.dev.conf')
const config = require('./config')

const port = process.env.PORT || config.dev.port
const proxyMap = config.dev.proxy

const app = express()
const compiler = webpack(webpackConfig)
// 开发环境,webpack不会把内容保存到本地,会储存在内存中
let devOpts = {
  publicPath: webpackConfig.output.publicPath,
  stats: {
    colors: true
  }
}
// 设置代理
Object.keys(proxyMap).forEach((key) => app.use(proxyMiddleware(key, proxyMap[key])))
let hotServer = hotMiddleware(compiler)
// 文件发生变化,发出重新加载事件
compiler.plugin('compilation', (compilation) => {
  compilation.plugin('html-webpack-plugin-after-emit', (data, cb) => {
    hotServer.publish({ action: 'reload' })
    cb()
  })
})
app.use(history())
app.use(devMiddleware(compiler, devOpts))
app.use(hotServer)
app.use('/assets', express.static(config.dev.assets))
app.listen(port, (err) => {
  console.log(err ? err : 'Listening at http://localhost:' + port + '\n')
})
事件处理
// dev-client.js
/* eslint-disable */
require('eventsource-polyfill')
var hotClient = require('webpack-hot-middleware/client?noInfo=true&reload=true')
// 处理热加载插件发出的事件,重新加载页面
hotClient.subscribe(function (event) {
  if (event.action === 'reload') {
    window.location.reload()
  }
})

写在最后

虽然基本上所有代码复制于vue,不过不知谁不是说了吗——没有复制,哪来创新呢。表面上是一个复制的过程,实际却是一次实践的过程,在这个过程中对如何自定义配置webpack,webpack的插件等有了更多的了解。

如果有幸被你看到这篇文章,请不要吐槽。

你可能感兴趣的:(磨刀不误砍材工,React项目的webpack配置)