基于webpack3.x从0开始搭建React开发环境

在开发react单页面的时候无可避免的要使用到webpack打包,今天就从零开始搭建一个react的开发环境。

需要实现的功能有:

  1. 使用Babel编译ES6
  2. 编译.jsx文件
  3. 实现热更新
  4. 编译CSS预处理文件Less,CSS后处理文件PostCSS
  5. 提取公共的CSS样式到一个公共的文件中
  6. 压缩js,减小js文件的大小
  7. 抽取公共的第三方js库,并且打包的时候第三方js文件的名称不会变,方便客户端缓存
  8. 压缩img等资源文件,过小的资源文件就编程dataURL的形式直接写在CSS中
  9. 可以访问静态的资源文件,图片等
  10. 代理

一、完成基本的目录结构与基本打包

  1. 新建一个reactWebpack文件夹,进入该文件夹并执行 npm init 初始化一个项目(生成package.json文件),新建如下目录结构:

    reactWebpack
    -- dist(打包后文件)
    -- src(业务源码)
        |-- index.html
        |-- app.js
    -- webpack.config.js(webpack配置文件)
    -- .babelrc(babel配置文件)
    -- package.json
  2. 在reactWebpack文件夹下安装以下依赖:

    npm install -D [email protected]

    如果全局环境下没有webpack那么建议也全局安装

    npm install -g [email protected]

  3. 修改webpack的配置文件 webpack.config.js ,如下:

    var path = require('path');
    
    module.exports = {
     entry: './src/index.js',
     output: {
       path: path.resolve(__dirname, 'dist'), // 这里需要绝对路径
       filename: '[name].js'
     }
    }
  4. 到这一步就可以使用webpack打包项目中依赖的js了,但是使用jsx和目前浏览器还不能完美支持的ES6语法还需要转译,将ES6等新的特性和jsx转换成ES5语法让浏览器不支持的特性得以支持。

    安装依赖 npm install babel-core babel-loader babel-preset-react babel-preset-2015

    修改webpck.config.js加上对js文件处理的loader部分:

    var path = require('path');
    
    module.exports = {
     entry: './src/index.js',
     output: {
       path: path.resolve(__dirname, 'dist'),
       filename: '[name].js'
     },
     module: {
       rules: [
         {
            test: /\.js|\.jsx$/, 
            use: ['babel-loader'], 
            exclude: path.resolve(__dirname, 'node_modules')
         }
       ]
     }
    }
  5. 修改babel配置文件.babelrc文件

    {
     "presets": ["es2015", "react"]
    }
  6. 配置打包html模板,先安装依赖:

    npm install -D html-webpack-plugin

    修改webpack.config.js文件如下

    var path = require('path');
    var HtmlWebpackPlugin = require('html-webpack-plugin');
    
    module.exports = {
     entry: './src/index.js',
     output: {
       path: path.resolve(__dirname, 'dist'),
       filename: '[name].js'
     },
     module: {
       rules: [
         {test: /\.js$/, use: 'babel-loader', exclude: path.resolve(__dirname, 'node_modules/')}    
       ]
     },
     plugins: [
         new HtmlWebpackPlugin({
             template: './src/index.html',
             filename: 'index.html',
             inject: true
         })
       ]
    }
  7. 安装项目依赖react等:

    npm install --save react react-dom react-router-dom

    修改index.html和app.js文件

    index.html 如下:

    <!DOCTYPE html>
    <html>
    <head>
     <meta charset="utf-8" />
     <title></title>
    </head>
    <body>
    <div id="container"></div>
    </body>
    </html>

    app.js 如下:

    import ReactDOM from 'react-dom'
    import React from 'react'
    
    ReactDOM.render(<div>我是组建</div>, document.getElementById('container'))

到这里基本的目录结构已经搭建完成,并且可以在根目录下执行webpack进行打包。打包完成后在dist文件夹下会有index.html和app.js文件。

二、配置webpack热加载

  1. 安装如下依赖:

    npm install -D [email protected]

    注意:是2.7.1版本

  2. 在根目录下添加 dev.conf.js 文件:

    var webpack = require('webpack')
    var WebpackDevServer = require('webpack-dev-server')
    
    var config = require('./webpack.config.js')
    var port = '8899'
    var host = 'localhost'
    var options = {
     contentBase: './dist',
     hot: true,
     host: host,
     port: port // 这里最好加上port
    }
    
    WebpackDevServer.addDevServerEntrypoints(config, options)
    var compiler = webpack(config)
    var server = new WebpackDevServer(compiler, options)
    
    server.listen(port, function(){
     console.log(host + ':' + port + '启动成功')
    })
  3. 修改webpack.config.js文件,添加了两个插件:

    var path = require('path')
    var webpack = require('webpack')
    var HtmlWebpackPlugin = require('html-webpack-plugin')
    
    module.exports = {
     entry: './src/app.js',
     output: {
       path: path.resolve(__dirname, 'dist'),
       filename: '[name].js'
     },
     devtool: 'inline-source-map', // 生成map映射,方便定位错误
     module: {
       rules: [
         {
           test: /\.js$/, 
           loader: 'babel-loader' 
         }
       ]
     },
     plugins: [ 
       new webpack.NamedModulesPlugin(), // 新加
       new webpack.HotModuleReplacementPlugin(), // 新加
       new HtmlWebpackPlugin({
         template: './src/index.html',
         path: path.resolve(__dirname, 'dist'),
         chunks: ['main']
       })
     ]
    }

到这里开发环境配置完成,在根目录下运行 node dev.conf.js 即可启动一个开发服务器。

三、调整目录结构

在上面已经完成可webpak开发环境的配置,下面要配置打包环境。在开始配置打包环境之前需要调整一下目录的结构。因为在开发环境(dev)和打包(build)上线的环境有一部分不同,但是基础部分又是相同的,所以需要合理的计划一下。

主要分为三个配置文件 dev.conf.js(原webpack.config.js一部分内容)、build.conf.js和base.conf.js(原大部分webpack.config.js内容),这三个文件会被放在新建的build文件夹下。

调整后的文件目录如下:

reactWebpack
    --build
        |--base.conf.js
        |--dev.conf.js
        |--build.conf.js
    -- dist(打包后文件)
    -- src(业务源码)
        |-- index.html
        |-- app.js
    -- .babelrc(babel配置文件)
    -- package.json
  1. 修改base.conf.js文件如下:

    var path = require('path')
    var webpack = require('webpack')
    var HtmlWebpackPlugin = require('html-webpack-plugin')
    
    function resolve(dir){
     return path.resolve(__dirname, '../', dir)
    }
    
    module.exports = {
     entry: './src/app.js',
     output: {
       path: resolve('dist'),
       filename: '[name].js'
     },
     devtool: 'inline-source-map',
     module: {
       rules: [
         {
           test: /\.js$/, 
           loader: 'babel-loader',
           exclude: resolve('node_modules')
         }
       ]
     },
     plugins: [ 
       new HtmlWebpackPlugin({
         template: './src/index.html',
         path: resolve('dist'),
         chunks: ['main']
       })
     ]
    }
  2. 安装依赖 npm install -D webpack-merge并修改dev.conf.js文件如下:

    var webpack = require('webpack')
    var WebpackDevServer = require('webpack-dev-server')
    var merge = require('webpack-merge')
    
    // 开发环境配置,这部分配置在打包的时候是不需要的
    var webpackDevConfig = {
     plugins: [
       new webpack.NamedModulesPlugin(), 
       new webpack.HotModuleReplacementPlugin()
     ]
    }
    
    // 下面是服务器配置
    var config = merge(webpackDevConfig, require('./base.conf.js'))
    var options = {
     contentBase: '../dist',
     hot: true,
     host: 'localhost'
    }
    var port = '8899'
    
    WebpackDevServer.addDevServerEntrypoints(config, options)
    var compiler = webpack(config)
    var server = new WebpackDevServer(compiler, options)
    
    server.listen(port, function(){
     console.log('启动成功')
    })

上面的目录结构已经调整完成,在根目录下运行 node build/dev.conf.js 即可开启开发服务器。

四、配置完善的webpack打包部分

webpack是按照依赖打包的,但是如果不处理的话所有的依赖会被打包到一个文件中,也就是说无论是第三方依赖,还是自己写的业务逻辑都会在同一个打包好的文件(main.js)中。

这会有很多问题,例如:

  • 不方便客户端缓存,客户端肯定会缓存main.js,但是如果我们修改了业务代码然后打包上线,这个main.js名称没有改变,而内容却是修改了,并且客户端缓存了main.js所以不一定会去获取新的main.js,这样就导致了新的代码并没有及时的被使用
  • 没有将第三方代码和业务代码分离,分离后第三方代码是可以让客户端缓存以节省加载时间,因为我们修改的是自己的业务代码
  • 上面的配置还没有去除注释、警告和压缩代码以减小代码体积

    1. 压缩代码JS代码

    修改build.conf.js文件如下:

    var path = require('path')
    var webpack = require('webpack')
    var merge = require('webpack-merge')
    var baseConfig = require('./base.conf.js')
    
    var buildConfig = {
     plugins: [
       new webpack.DefinePlugin({
         'process.env': {
           'NODE_ENV': JSON.stringify('production')
         }
       }),
       new webpack.optimize.UglifyJsPlugin() // 新加
     ]
    }
    
    module.exports = merge(buildConfig, baseConfig)

在根目录下执行webpack build/build.conf.js,即可看到打包出的文件会小很多。

  1. 提取第三方代码单独打包

    修改代码如下:

    var path = require('path')
    var webpack = require('webpack')
    var merge = require('webpack-merge')
    var baseConfig = require('./base.conf.js')
    
    var buildConfig = {
     plugins: [
       new webpack.DefinePlugin({
         'process.env': {
           'NODE_ENV': JSON.stringify('production')
         }
       }),
       new webpack.optimize.CommonsChunkPlugin({
         name: 'vender', // 指定公共 bundle 的名称
         minChunks: function (module, count) {
           console.log(count)
           return (
             module.resource &&
             /\.js$/.test(module.resource) &&
             module.resource.indexOf(
               path.join(__dirname, '../node_modules')
             ) === 0
           )
         }
       }),
       new webpack.optimize.CommonsChunkPlugin({
         name: 'manifest', // 提取webpack的运行时代码
         chunks: ['vender']
       }),
       new webpack.optimize.UglifyJsPlugin()
     ]
    }
    
    module.exports = merge(buildConfig, baseConfig)
  2. 为文件名添加hash,当修改的时候文件名发生变化,让客户端及时获取变化的文件,同时缓存没有改变的文件。

    修改base.conf.js文件如下:

    var path = require('path')
    var webpack = require('webpack')
    var HtmlWebpackPlugin = require('html-webpack-plugin')
    
    function resolve(dir){
     return path.resolve(__dirname, '../', dir)
    }
    
    module.exports = {
     entry: './src/app.js',
     output: {
       path: resolve('dist'),
       filename: '[name]-[chunkHash].js' // 新加 chunkHash内容改变的时候chunkHash才会改变
     },
     devtool: 'inline-source-map',
     module: {
       rules: [
         {
           test: /\.js$/, 
           loader: 'babel-loader',
           exclude: resolve('node_modules')
         }
       ]
     },
     plugins: [ 
       new HtmlWebpackPlugin({
         template: './src/index.html',
         path: resolve('dist'),
         chunks: ['main']
       })
     ]
    }
  3. 因为加上了chunkHash新生成的文件不会覆盖老文件,那么就会产生很多文件,并不能立马找到目标文件。这时可以考虑先清空打包的目标文件夹dist

    安装 npm install -D clean-webpack-plugin

    修改 build/build.conf.js文件如下

    var path = require('path')
    var webpack = require('webpack')
    var merge = require('webpack-merge')
    var baseConfig = require('./base.conf.js')
    var Clean = require('clean-webpack-plugin')
    
    var buildConfig = {
     plugins: [
       new webpack.DefinePlugin({
         'process.env': {
           'NODE_ENV': JSON.stringify('production')
         }
       }),
       new webpack.optimize.CommonsChunkPlugin({
         name: 'vender', // 指定公共 bundle 的名称
         minChunks: function (module, count) {
           console.log(count)
           return (
             module.resource &&
             /\.js$/.test(module.resource) &&
             module.resource.indexOf(
               path.join(__dirname, '../node_modules')
             ) === 0
           )
         }
       }),
       new webpack.optimize.CommonsChunkPlugin({
         name: 'manifest',
         chunks: ['vender']
       }),
       new webpack.optimize.UglifyJsPlugin(),
       new Clean(['dist']) // 新加
     ]
    }
    
    module.exports = merge(buildConfig, baseConfig)

待续。。。。。。。。。。。。。。。。。。。。。。。。。。。

后续:1.CSS预处理器和后处理器的编译 2. 图片的加载 5. 跨域代理的配置 6. 依据react-router做代码分割

参考:

webpack中文文档

​ vue-cli生成的webpack配置文件

猜你喜欢

转载自blog.csdn.net/lettertiger/article/details/80386282