webpack+react打包后的文件如何在Apache服务器上部署?

今天按照网上的教程学习了webpack+react,虽然在浏览器中访问http://localhost:8080/可以看到效果,但还是有好多疑问。

  1. npm start webpack-dev-server --hot --inline后却找不到打包后的bundle.js文件,这个bundle.js文件在哪呢?

  2. npm run build打包后的bundle.js文件可以看到,但是在浏览器中访问index.html文件的绝对路径页面什么都没有,console中提示Download the React DevTools and use an HTTP server (instead of a file: URL) for a better development experience:,因为我的Apache服务器根目录是www,在浏览器中访问 http://localhost/public/index.html 也没有任何反应,console中提示Download the React DevTools for a better development experience:。难道我只能用http://localhost:8080/ 访问吗?怎么才能访问http://localhost/public/index.html 有效果,也就是说如何把打包后的文件放在Apache服务器中部署。

项目目录树:
图片描述

package.json文件 scripts部分:

"scripts": {
    "start": "webpack-dev-server --hot --inline",
    "build": "webpack --progress --profile --colors --config webpack.production.config.js"
  },

webpack.config.js:

let path = require('path');
//定义了一些文件夹的路径
let ROOT_PATH = path.resolve(__dirname);
let APP_PATH = path.resolve(ROOT_PATH, 'app');
let BUILD_PATH = path.resolve(ROOT_PATH, 'public');

module.exports = {
    devtool: 'eval-source-map',
    entry: __dirname + '/app/main.js',
    output: {
        path: BUILD_PATH,
        filename: 'bundle.js'
    },

    module: {
        loaders: [
            {
                test: /\.less$/,
                loaders: ['style', 'css', 'less','postcss'],
                include: APP_PATH
            },
            {
                test: /\.jsx?$/,
                loader: 'babel',
                include: APP_PATH,
                resolve: {
                    extensions: ['', '.js', '.jsx']
                }
            }
        ]
    },

    postcss: [
        require('autoprefixer')//调用autoprefixer插件
    ],

    devServer: {
        contentBase: './public',
        colors: true,
        historyApiFallback: true,
        inline: true
    }
}

webpack.production.config.js:

let path = require('path');
let webpack = require('webpack');
//定义了一些文件夹的路径
let ROOT_PATH = path.resolve(__dirname);
let APP_PATH = path.resolve(ROOT_PATH, 'app');
let BUILD_PATH = path.resolve(ROOT_PATH, 'public');
let ExtractTextPlugin = require('extract-text-webpack-plugin'); 

module.exports = {
    entry: __dirname + '/app/main.js',
    output: {
        path: BUILD_PATH,
        filename: 'bundle.js'
    },

    module: {
        loaders: [
            {
                test : /\.(less|css)$/,
                loader: ExtractTextPlugin.extract('style', 'css!less')
            },
            {
                test: /\.jsx?$/,
                loader: 'babel',
                include: APP_PATH,
                resolve: {
                    extensions: ['', '.js', '.jsx']
                }
            }
        ]
    },
    postcss: [
        require('autoprefixer')//调用autoprefixer插件
    ],
    plugins: [
        //new webpack.optimize.UglifyJsPlugin(),
        //new webpack.optimize.CommonsChunkPlugin('common.js'),
        new ExtractTextPlugin('css/[name].css')
    ]
}

.babelrc:

{
  "presets": ["react", "es2015"],
  "env": {
    // only enable it when process.env.NODE_ENV is 'development' or undefined
    "development": {
      "plugins": [["react-transform", {
        "transforms": [{
          "transform": "react-transform-hmr",
          // if you use React Native, pass "react-native" instead:
          "imports": ["react"],
          // this is important for Webpack HMR:
          "locals": ["module"]
        }]
        // note: you can put more transforms into array
        // this is just one of them!
      }]]
    }
  }
}

index.html

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <title>Webpack Sample Project</title>
    </head>
    <body>
        <div id="root"></div>
        <script src="bundle.js"></script>
    </body>
</html>

main.js入口文件

import React, {Component} from 'react';
import ReactDOM from 'react-dom';
class App extends Component{
    constructor(props) {
        super(props);
    }

    render() {
        let backgroundSrc = './imgs/gondel.jpg';
        return (
            <div>
                {backgroundSrc}
            </div>
        );
    }
}

ReactDOM.render(<App />,document.getElementById('root'));

访问http://localhost/public/index.html时浏览器错误提示:
图片描述

阅读 21k
4 个回答

我觉得你还没有弄清楚文件的路由是怎么回事

一个html文件中会纪录它要载入哪些脚本,然后就会向对应的URI(统一资源标识符)去请求这个资源。

比如你上面的html文件中纪录了请求src为bundle.js的文件,因为你正在访问http://localhost:8080/的地址,那么浏览器会将其解析为http://localhost:8080/bundle.js,而webpack-dev-server在运行的时候就会根据你的配置(具体就是output中的publicPath来决定)将其提供给访问者,而你没有配置的情况下(path是webpack,不是webpack-dev-server用的,用于production build的时候确定生成文件的位置),他就默认是webpack-dev-server当前监听的端口的根目录,于是你就能正常获得这个bundle.js。而在npm run watch的过程中,它是不会生成实际的脚本文件的,都是根据需要提供给访问者。

假如你把webpack-dev-server的output的publicPath定义为"/js/",那么你就应该去http://localhost:8080/js/bundle.js来获得这个资源。

那么问题来了,你有没有遇到过,比如你网页在前端做路由的时候来到了比如http://localhost:8080/here/的地址,然后修改了代码导致自动刷新然后提示你找不到bundle.js的时候,只有前往http://localhost:8080/才能刷新成功?那是因为根据解析规则,此时类似src="bundle.js"的uri会被解析为http://localhost:8080/here/bundle.js,发现问题了没,你没有指定绝对路径就会出这种错。所以我建议你将这个地址改为src="http://localhost:8080/bundle.js"或者简单点(也便于以后部署)src="/bundle.js"。这个知识点在开发网页时候很常用的,比如a标签的href,你可以试一下都是同样的逻辑。

当你react app开发完成之后,实际部署的过程中,需要部署。用户实际访问的顺序是这样的,用户先访问你的服务器,然后你返回一个html文件给他,上面纪录了需要加载的脚本文件的uri,你就要保证从一个uri能够访问到这个bundle.js,这种访问是最简单的对于文件的访问,不管是通过浏览器的开发者工具还是直接访问,你都应该看到实际获得了这个文件。
那么你可以打开,比如chrome的开发工具,来到network这一块,查看这个html加载的时候尝试去哪里获取js文件,根据你的html的写法,http://localhost/public/index.html是你访问使用的地址,src="bundle.js",那么浏览器访问bundle.js的地址就是http://localhost/public/bundle.js,至于你到底有没有把bundle.js放在这个路由下,我就不知道啦。你也可以通过浏览器地址栏直接输入http://localhost/public/bundle.js来看看是否实际访问到了这个文件。
这是很正常的文件可访问测试,和其他时候你需要使用一些静态资源(图片什么的)都是一样的测试方法。

另外需要关注的就是webpack实际打包文件(不是webpack-dev-server)的时候,output中的publicPath仍然有用。比如你通过react-router做module的按需加载,打个比方吧,你的app分为三个大块,一个是index块,一个是用户中心块,一个是网站功能块,假设他们都很大,各有300K。因为用户首先访问的肯定是index块,你肯定就希望不要打出一个900K的包(实际更大,因为你肯定会引入很多其他的包),而是按需加载。那么给用户中心块和网站功能块做了按需加载之后,他们就会被生成0.js,1.js这样的会被按需加载的模块,在访问到对应路由的时候才会临时从服务器上下载。
此时你在webpack设置中output里的publicPath就会起作用,比如我设置的"/js/",我网站假设是http://www.test.com/,那么当app需要0.js这个模块的时候,虽然html里面没有写,但是就会自动去http://www.test.com/js/0.js这个uri去获取脚本

  1. 执行期间不会直接产生bundle.js,要hot loader的缘故,而且打包时还要压缩或丑化,需要时间。

  2. 打包后bundle.js与index.html放在apache或其他www服务器中,就可以浏览使用。没用到服务器的简单功能,用浏览器开就行了。你要不要看一下你的bundle.js里有什么代码?因为你的webpack设定档看起大概是网上找来的,不知哪个环节有问题。

你如果想省麻烦可以直接用create-react-app,官网出的专案生成工具,里面自带webpack,打包(build)设定也调好了。

webpack的设定与react实际关系不大,用个js苑例代码看能不能执行就知道了,也不用服务器。下面贴个ES6范例,我这webpack的设定是抄material-ui专案来的,有点旧(二、三个月),刚测了一下还能用:

webpack-production.config.js(我把注释去掉)

const webpack = require('webpack');
const path = require('path');
const buildPath = path.resolve(__dirname, 'build');
const nodeModulesPath = path.resolve(__dirname, 'node_modules');

const config = {
  entry: [path.join(__dirname, '/src/index.js')],
  devtool: 'source-map',
  output: {
    path: buildPath, 
    filename: 'bundle.js', 
  },
  plugins: [
    new webpack.optimize.UglifyJsPlugin({
      compress: {
        warnings: false,
      },
    }),
    new webpack.NoErrorsPlugin(),

  ],
  module: {
    loaders: [
      {
        test: /\.js$/, // All .js files
        loaders: ['babel-loader'], 
        exclude: [nodeModulesPath],
      }
    ],
  },
};

module.exports = config;

范例代码:

const test = (value) => {console.log(value)}
test(123)

bundle.js

!function(o){function r(n){if(t[n])return t[n].exports;var e=t[n]={exports:{},id:n,loaded:!1};return o[n].call(e.exports,e,e.exports,r),e.loaded=!0,e.exports}var t={};return r.m=o,r.c=t,r.p="",r(0)}([function(o,r,t){o.exports=t(1)},function(o,r){"use strict";var t=function(o){console.log(o)};t(123)}]);
//# sourceMappingURL=bundle.js.map
新手上路,请多包涵

你好,能给一个如何把React项目部署到后台服务器上的教程吗?网上没搜到啊!!!!

打开cmd cd空格项目拖进去,webpack空格-p编译 将编译后的文件放在apache服务器htdocs文件夹中,继续再cmd中webpack-dev-server发布 然后在浏览器输入apach设置的端口号直接打开就没问题了。

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题