Webpack打包React踩到的坑

  学习React断断续续地也有两三周了,期间接触到了一大堆名词和工具,不得不承认React生态圈实在是太繁荣了,作为还没毕业的学生,本应该沉下心来多看看基础课程、刷一刷面试题好在秋招找一份不错的工作,但是一想到还有那么多好玩的框架没有学过,好用的工具没有用过,就心里直痒痒~

  刚刚解决了困扰了我好一段时间的问题,在网上查了好多资料都没找到,趁着中午休息时间记录下来吧。

  需求:公司项目使用APICloud将前端代码打包成原生应用,并使用React将应用组件化  

  问题:如何在本地直接打开html呈现React页面?

  下面简单介绍一下产生这个问题的源头:

  初学React时因为代码量较小,而且重点在学习语法,所以组件都是写在index.html里内联的script标签里,在<head>里使用browser.js将jsx语法转换为js,这样显然不利于模块化和复用,并且所有的组件内部都呈现在主页,很丑陋。

  那怎么解决呢?首先想到的是将组件代码以<script type="text/babel" src="xx.jsx" />的方式引入,但是在chrome下browser.js报跨域错,据说改下chrome的跨域配置或换用Firefox可以正常运行,但因为不是解决问题本身,没有太大意义,就没有尝试。  

  在知乎中看到有人回答类似问题,说这不是React正确的打开姿势,要在本地起一个服务器,将项目部署到React上即可。于是接触到了webpack,不得不说webpack很全能,也很强大,从css预处理,到js混淆,图片引用,只要配置得当,它全能帮你做好,这里webpack的配置文件等推荐使用Yeoman的generator-react-webpack自动生成,当初不知道这些工具,查文档一项一项调试真是吐血。。。总算配好了环境,可以开始愉快地开发了,所有的components自成文件,在需要的时候引入即可,各种依赖由webpack帮你完成,npm start 自动起一台支持热插拔、自动刷新的服务器,加上React-router以后也算个像模像样的SPA了。

  然而……项目中的前端部分要作为静态资源打包到APICloud,这就意味着在浏览器环境下(无服务器)的情况下,双击index.html要能呈现出和webpack-dev-server一样的效果,有了初学时的思维定式,这个问题一下子难住了我(其实现在想想好简单哦),用过webpack-dev-server的同学应该都知道,webpack热部署的原理是将index.html中引用的本地文件的路径替换为存放在内存中的实时编译的bundle.js文件,每当源文件发生改变,就重新编译改变的部分并重新加载到内存中。想要将项目本地化,首先就是将bundle.js写入文件系统(在generator-react-webpack生成的项目的默认配置下),命令如下

npm run --dev=dist

这时,所有的源文件以及依赖会被编译并存放到dist目录下,打开dist/index.html,竟然啥也没有!!!一夜回到解放前,打开chrome元素面板,<div ></div>里啥也没,我晕,打开编译+混淆后的bundle.js看了十几分钟,在入口处console.log几句话也都打出来了,就是render函数没效果,难道真的像知友说的那样打开姿势不对?React不能在本地跑?我才不信,回到webpack配置文件,去掉UglifyJsPlugin插件,重新编译,再看编译+未混淆的代码

_reactDom2.default.render(_react2.default.createElement(
        _reactRouter.Router,
        { history: _reactRouter.browserHistory },
        _react2.default.createElement(
            _reactRouter.Route,
            { path: '/', component: _Main2.default },
            _react2.default.createElement(_reactRouter.IndexRoute, { component: _LoginPage2.default }),
            _react2.default.createElement(_reactRouter.Route, { path: 'login', component: _LoginPage2.default }),
            _react2.default.createElement(
                _reactRouter.Route,
                { onEnter: requireLogin, path: 'student', component: _Student2.default },
                _react2.default.createElement(_reactRouter.IndexRoute, { component: _Info2.default }),
                _react2.default.createElement(_reactRouter.Route, { path: 'books', component: _BookList2.default })
            ),
            _react2.default.createElement(
                _reactRouter.Route,
                { path: 'admin', component: _Admin2.default },
                _react2.default.createElement(_reactRouter.IndexRoute, { component: _Info2.default }),
                _react2.default.createElement(_reactRouter.Route, { path: 'books', component: _BookList2.default })
            )
        )
    ), document.getElementById('app'));

噗。。一眼就看到了browserHistory,原来绕了一大圈子,还是服务器的锅,原来,我的项目使用了browserHistory做路由的history,而browserHistory本身就是依赖服务器的,如果要在本地跑,换成hashHistory应该就好了,重新编译,测试,通过,这下子,终于可以专心学习React本身了!