美文网首页
搭建 webpack + React 开发环境

搭建 webpack + React 开发环境

作者: 赵碧菡 | 来源:发表于2017-11-14 17:05 被阅读0次

1、初始化环境
2、安装插件
3、配置webpack.config.js 和 webpack.production.config.js 文件
4、遇到的问题及解决办法
5、源文件

一、 初始化 npm 环境

首先保证有 node 和 npm 环境,运行node -vnpm -v查看

进入项目目录,运行npm init按照步骤填写最终生成package.json文件,所有使用 npm 做依赖管理的项目,根目录下都会有一个这个文件,该文件描述了项目的基本信息以及一些第三方依赖项(插件)。

二、 安装插件

已知我们将使用 webpack 作为构建工具,那么就需要安装相应插件,运行 npm install webpack webpack-dev-server --save-dev 来安装两个插件。

又已知我们将使用 React ,也需要安装相应插件,运行 npm i react react-dom --save来安装两个插件。其中iinstall的简写形式。

安装完成之后,查看package.json可看到多了devDependenciesdependencies两项,根目录也多了一个node_modules文件夹。

--save 和 --save-dev 的区别

npm i时使用--save--save-dev,可分别将依赖(插件)记录到package.json中的dependenciesdevDependencies下面。

dependencies下记录的是项目在运行时必须依赖的插件,常见的例如react jquery等,即及时项目打包好了、上线了,这些也是需要用的,否则程序无法正常执行。

devDependencies下记录的是项目在开发过程中使用的插件,例如这里我们开发过程中需要使用webpack打包,而我在工作中使用fis3打包,但是一旦项目打包发布、上线了之后,webpackfis3就都没有用了。

三、 配置 webpack.config.js

文件格式

webpack.config.js 就是一个普通的 js 文件,符合 commonJS 规范。最后输出一个对象,即module.exports = {...}

输入 & 输出

新建./app/index.jsx作为入口文件, webpack 支持多个入口文件,可查阅文档。

输出就是一个bundle.js,js 和 css 都在里面,不过只有在开发环境下才用,发布代码的时候,肯定不能只有这么一个文件。

module

针对不同类型的文件,使用不同的loader,当然使用之前要安装,例如npm i style-loader css-loader --save-dev。该项目代码中,我们用到的文件格式有:js/jsx 代码、css/less 代码、图片、字体文件。

less 是 css 的语法糖,可以更高效低冗余的写 css。

在加载 css/less 时用到了postcss,主要使用autoprefixer功能,帮助自动加 css3 的浏览器前缀,非常好用。

编译 es6 和 jsx 语法时,用到家喻户晓的babel,另外还需增加一个.babelrc的配置文件。

plugins

使用 html 模板(需要npm i html-webpack-plugin --save-dev),这样可以将输出的文件名(如./bundle.js)自动注入到 html 中,不用我们自己手写。手写的话,一旦修改就需要改两个地方。

使用热加载和自动打开浏览器插件

devServer

对 webpack-dev-server 的配置

npm start

在 package.json 中,输入以下代码,将这一串命令封装为npm start,这样就可以运行项目代码了。

  "scripts": {
    "start": "set NODE_ENV=dev && webpack-dev-server --progress --colors"
  }

代码中NODE_ENV=dev代表当前是开发环境下,这里的"dev"可被 js 代码中的process.env.NODE_ENV得到并做一些其他处理。

定义环境全局变量

以下定义,可使得代码通过__DEV__得到当前是不是开发模式。

    new webpack.DefinePlugin({
      __DEV__: JSON.stringify(JSON.parse((process.env.NODE_ENV == 'dev') || 'false'))
    })

四、 配置 webpack.production.config.js

开发环境下,可以不用考虑系统的性能,更多考虑的是如何增加开发效率。而发布系统时,就需要考虑发布之后的系统的性能,包括加载速度、缓存等。下面介绍发布用配置代码和开发用的不一样的地方。

发布到 ./build 文件夹中

path: __dirname + "/build",

vendor

将第三方依赖单独打包。即将 node_modules 文件夹中的代码打包为 vendor.js 将我们自己写的业务代码打包为 app.js。这样有助于缓存,因为在项目维护过程中,第三方依赖不经常变化,而业务代码会经常变化。

md5后缀

为每个打包出来的文件都加md5后缀,例如"/js/[name].[chunkhash:8].js",可使文件强缓存。

分目录

打包出来的不同类型的文件,放在不同目录下,例如图片文件将放在img/目录下

Copyright

自动为打包出来的代码增加 copyright 内容

分模块

new webpack.optimize.OccurenceOrderPlugin(),

压缩代码

使用 Uglify 压缩代码,其中warnings: false是去掉代码中的 warning

分离 css 和 js 文件

开发环境下,css 代码是放在整个打包出来的那个 bundle.js 文件中的,发布环境下当然不能混淆在一起,使用new ExtractTextPlugin('/css/[name].[chunkhash:8].css'),将 css 代码分离出来。

npm run build

打开package.json,查看以下代码。npm startnpm run build分别是运行代码和打包项目。另外,"start"、"test"可以不用run

  "scripts": {
    "start": "set NODE_ENV=dev && webpack-dev-server --progress --colors",
    "build": "rd/s/q build &&  set NODE_ENV=production && webpack --config ./webpack.production.config.js --progress --colors"
  },

这两个命令主要有以下区别:

  • 前者中默认使用 webpack.config.js 作为配置文件,而后者中强制使用 webpack.production.config.js 作为配置文件
  • 前者NODE_ENV=dev而后者NODE_ENV=production,标识不同的环境。而这个"dev" "production"可以在代码中通过process.env.NODE_ENV获取。
最小化压缩 React

以下配置可以告诉 React 当前是生产环境,请最小化压缩 js ,即把开发环境中的一些提示、警告、判断通通去掉,直流以下发布之后可用的代码。

    new webpack.DefinePlugin({
      'process.env':{
        'NODE_ENV': JSON.stringify(process.env.NODE_ENV)
      }
    }),

.


五、配置文件遇到的问题

问题 1
图片.png

解决办法:

修改
 resolve: {
        extensions: ['','.js', '.jsx']
      },
为
 resolve: {
        extensions: ['.js', '.jsx']
      },
问题 2
图片.png

原因:Webpack 2.1.0-beta23 之后的config里不能直接包含自定义配置项
解决:将postcssdevServer替换成以下写法即可

 plugins: [
        new webpack.LoaderOptionsPlugin({
            options: {
              postcss: function () {
                return [precss, autoprefixer];
              },
              devServer: {
                colors: true, //终端中输出结果为彩色
                historyApiFallback: true, //不跳转
                inline: true, //实时刷新
                hot: true  // 使用热加载插件 HotModuleReplacementPlugin
                 }
          }
    })
]
问题 3
图片.png

修改并配置文件如下:

// 方法1
                test:/\.(css|less)$/,
                use:[
                   'style-loader',
                   'css-loader',
                   {
                    loader:'postcss-loader',
                    options:{
                        plugins:function(){
                            return [
                                require('postcss-import')()     // 这里
                            ]
                        }
                   }

// 方法2 ------------------------------------------------------------
                test: /\.(css|less)$/,
                exclude: __dirname + '/node_modules/',   
                use: [
                    'style-loader',
                     {
                         loader: 'css-loader', 
                         options: { importLoaders: 1 } 
                    },
                    {
                         loader:'postcss-loader',
                         options:{ident:'postcss-ident'}
                    },
                    'less-loader'
                  ]
问题 4

webpack 图片的处理,图片不显示问题
解决方法:自己设置路径name=./app/static/img/[name].[ext],publicPath一般最好不写

 test: /\.(png|gif|jpg|jpeg|bmp)$/i,
 use: "url-loader?limit=5000&name=./app/static/img/[name].[ext]" 
问题 5

webpack 对象扩展运算符报错
原因:babel-preset-es2015组件并不支持该特性,而babel-preset-stage-3能支持该特性
解决办法 :安装 babel-preset-stage-3 插件,并配置presets

"presets": ["react", "es2015","stage-3"],

.


六、 配置文件

webpack.config.js

var path = require('path');
var webpack = require('webpack');  // 自己安装的插件
var HtmlWebpackPlugin = require('html-webpack-plugin'); // 将输出文件自动注入到html页面中,不用手动引入js文件
var OpenBrowserPlugin = require('open-browser-webpack-plugin');

module.exports = {
    entry: path.resolve(__dirname, './app/index.jsx'),   // 解析成绝对路径
    output: {
        filename: "bundle.js"
    },
    resolve: {
        extensions: ['.js', '.jsx']
    },
    module: {
        rules: [
            {
                test: /\.(js|jsx)$/,     //要解析什么类的文件
                use: {
                    loader: "babel-loader",   //需要的插件
                    options: {
                        presets: ['es2015', 'react']    //解析什么语言这里是es6 react
                    }
                },
                exclude: __dirname + '/node_modules/'      //屏蔽不需要处理的文件(文件夹)
            },
             {
                test: /\.(css|less)$/,
                exclude: __dirname + '/node_modules/',     //屏蔽不需要处理的文件(文件夹)
                use: [
                    'style-loader',
                     {
                         loader: 'css-loader', 
                         options: { importLoaders: 1 } 
                    },
                    {
                         loader:'postcss-loader',
                         options:{ident:'postcss-ident'}
                    },
                    'less-loader'
                ]
            },
            {
               test: /\.(png|gif|jpg|jpeg|bmp)$/i,
               use: "url-loader?limit=5000&name=./app/static/img/[name].[ext]"  // 限制大小5kb   
            },
            {
                test: /\.(png|woff|woff2|svg|ttf|eot)($|\?)/i,
                use: 'url-loader?limit=5000'    // 限制大小小于5k           
            }

        ]
    },
    plugins: [
        new webpack.LoaderOptionsPlugin({
            options: {
                postcss: function () {
                    return [precss, autoprefixer];
                }
            }
        }),
        // html 模板插件
        new HtmlWebpackPlugin({
            template: __dirname + '/app/index.tmpl.html'
        }),
        // 热加载 修改页面后自动刷新
        new webpack.HotModuleReplacementPlugin(),

        // 打开浏览器
        new OpenBrowserPlugin({
            url: 'http://localhost:8080'
        }),

        new webpack.DefinePlugin({   // 判断是否是开发环境
            __DEV__: JSON.stringify(JSON.parse((process.env.NODE_ENV == 'dev') || 'false'))
        })
    ],
 devServer: {
        proxy: {
              // 凡是 `/api` 开头的 http 请求,都会被代理到 localhost:3000 上
            '/api': {
              target: 'http://localhost:3000',
              secure: false
            }
          },
          contentBase: "./public", //本地服务器所加载的页面所在的目录
        //   colors: true, //终端中输出结果为彩色
          historyApiFallback: true, //不跳转
          inline: true, //实时刷新
          hot: true  // 使用热加载插件 HotModuleReplacementPlugin
      }

}

webpack.production.config.js

var pkg = require('./package.json')
var path = require('path')
var webpack = require('webpack');
var HtmlWebpackPlugin = require('html-webpack-plugin');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
// 发布
module.exports = {
  entry: {
    app: path.resolve(__dirname, 'app/index.jsx'),    // 自己写的代码
    // 将 第三方依赖(node_modules中的) 单独打包  项目维护中第三方代码不经常改变
    vendor: Object.keys(pkg.dependencies)    
  },
  output: {
    path: __dirname + "/build/",    // 将文件打包到该文件下
    filename: "js/[name].[chunkhash:8].js"
  },

  resolve:{
      extensions:['.js','.jsx']
  },

  module: {
    rules: [
        {
            test: /\.(js|jsx)$/,     //要解析什么类的文件
            use: {
                loader: "babel-loader",   //需要的插件
                options: {
                    presets: ['es2015', 'react']    //解析什么语言这里是es6 react
                }
            },
            exclude: __dirname + '/node_modules/'      //屏蔽不需要处理的文件(文件夹)

        },
        {
            test: /\.css$/,
            use: ExtractTextPlugin.extract([ 'css-loader', 'postcss-loader' ])
          },
          {
            test: /\.less$/i,
            use: ExtractTextPlugin.extract([ 'css-loader', 'less-loader' ])
          },
        {
             test: /\.(png|gif|jpg|jpeg|bmp)$/i,
             use: "url-loader?limit=5000&name=./app/static/img/[name].[ext]"  // 限制大小5kb   
        },
        {
            test: /\.(png|woff|woff2|svg|ttf|eot)($|\?)/i,
            use: 'url-loader?limit=5000'    // 限制大小小于5k           
        }

    ]
},


  plugins: [

    new webpack.LoaderOptionsPlugin({
        options: {
            postcss: function () {
                return [precss, autoprefixer];
            }
        }
    }),
    // webpack 内置的 banner-plugin     可以写标注
    new webpack.BannerPlugin("Copyright"),

    // html 模板插件
    new HtmlWebpackPlugin({
        template: __dirname + '/app/index.tmpl.html'
    }),

    // 定义为生产环境,编译 React 时压缩到最小
    new webpack.DefinePlugin({
      'process.env':{
        'NODE_ENV': JSON.stringify(process.env.NODE_ENV)
      }
    }),

    // 为组件分配ID,通过这个插件webpack可以分析和优先考虑使用最多的模块,并为它们分配最小的ID
    // new webpack.optimize.OccurenceOrderPlugin(),
    
    new webpack.optimize.UglifyJsPlugin({
        compress: {
          warnings: false       // 把搜索的警告去掉
        }
    }),
    
    // 分离CSS和JS文件
    new ExtractTextPlugin('/css/[name].[chunkhash:8].css'), 
    
    // 提供公共代码
    new webpack.optimize.CommonsChunkPlugin({
      name: 'vendor',
      filename: '/js/[name].[chunkhash:8].js'
    }),

    // 可在业务 js 代码中使用 __DEV__ 判断是否是dev模式(dev模式下可以提示错误、测试报告等, production模式不提示)
    new webpack.DefinePlugin({
      __DEV__: JSON.stringify(JSON.parse((process.env.NODE_ENV == 'dev') || 'false'))
    })
  ],
  devServer: {
        proxy: {
              // 凡是 `/api` 开头的 http 请求,都会被代理到 localhost:3000 上
            '/api': {
              target: 'http://localhost:3000',
              secure: false
            }
          },
          contentBase: "./public", //本地服务器所加载的页面所在的目录
        //   colors: true, //终端中输出结果为彩色
          historyApiFallback: true, //不跳转
          inline: true, //实时刷新
          hot: true  // 使用热加载插件 HotModuleReplacementPlugin
      }
}

相关文章

网友评论

      本文标题: 搭建 webpack + React 开发环境

      本文链接:https://www.haomeiwen.com/subject/mirfvxtx.html