前阶段做react项目,项目的架构使用create-react-app去创建的项目,技术选型react4+antd,来构建项目。在开发阶段基本上是在本地进行的测试,在package.json中配置的代理,这样就可以在本地调试服务器上的接口,接口已经对接成功,现在到了要发布测试了,不能在本地搞了,就选择使用webpack打包工具,将项目进行打包,然后丢到Tomcat下就能运行。理想是美好的,现实是残酷的。
兴致满满的将项目打包,丢到Tomcat下居然是404,。一看到404就能猜出是路径的问题。由于之前没有处理过这种发布的问题,所以有点慌。有点不知所措,于是上网搜索了一下,再结合项目的具体环境,调试了一通,最后努力是没有白费,成功了,所以在这里也给大家分享一下具体实现。
先看一下项目的结构目录:
这是一个完整的项目配置,在我们使用脚手架创建项目的时候,执行
npm init
就会生成一一个基本的react项目结构,在里面可以增加自己的模块,我所有的代码都放到了src下,是只有一个主页面index.html的,其他的全部是js文件和css文件,根据不同的模块划分不同的js。
package.json文件中的配置
下面是一个完整的package.json配置(这里是我项目中用到的依赖),这里没什么好说的,主要是在项目初始化的时候会写进去script属性,在我们打包项目的时候就会用到 build命令。
{
"name": "react-router-history",
"version": "0.1.0",
"private": true,
"proxy": "http://192.168.20.217:8082",
"dependencies": {
"@uiw/react-codemirror": "^1.0.28",
"antd": "^3.21.2",
"axios": "^0.19.0",
"echarts": "^4.2.1",
"echarts-for-react": "^2.0.15-beta.0",
"events": "^3.0.0",
"intl": "^1.2.5",
"lodash": "^4.17.15",
"moment": "^2.24.0",
"qs": "^6.9.1",
"react": "^16.9.0",
"react-bootstrap": "^1.0.0-beta.11",
"react-cookies": "^0.1.1",
"react-dom": "^16.9.0",
"react-emit": "^2.1.0",
"react-intl": "^3.4.0",
"react-intl-universal": "^2.2.1",
"react-redux": "^7.1.0",
"react-router-dom": "^5.0.1",
"react-router-redux": "^4.0.8",
"react-scripts": "3.0.1",
"redux-saga": "^1.0.5"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": "react-app"
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"devDependencies": {
"babel-plugin-jsx-control-statements": "^4.0.0",
"css-loader": "^3.2.0",
"jsx-control-statements": "^3.2.8",
"less": "^3.10.3",
"less-loader": "^5.0.0",
"style-loader": "^1.0.0",
"url-loader": "^2.1.0"
},
"devServer": {
"contentBase": "./public",
"historyApiFallback": true,
"inline": true,
"port": 8080
}
}
webpack.config.js配置
使用webpack进行打包,那就需要对webpack进行配置,主要配置入口文件和出口文件以及一些loader解析。
//引入路径
const path = require("path");
//js 压缩插件配置
const uglify = require("uglifyjs-webpack-plugin");
//定义出口文件
module.exports = {
//配置入口文件
entry: './src/index.js',
//出口文件配置
output: {
//输出路径配置
path: path.resolve(__dirname, "dist"),
filename: "bundle.js"
},
//模块,解读css文件,图片转换,压缩等
module: {
rules: [
{
test: "/\.css$/",
test: /\.css$/,
use: ['style-loader', 'css-loader']
},
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
loaders: ['babel-loader?presets[]=@babel/react,presets[]=@babel/env']
},
{
test: [/\.gif$/, /\.jpe?g$/, /\.png$/],
loader: 'url-loader',
options: {
limit: 10000, //该大小以下图片会转成base64
},
}
]
},
//插件,用于生成模板和个性功能
plugins: [
new uglify()
]
};
我输出的是单文件,配置的也是单文件,但当你执行
npm run build
会生成一个build文件,并且在build文件下生成一些其他文件
没想到居然生成了那么多文件,不过文件大小还是能接受的
打开build文件就可以看到它所生成的一些文件,static下存放的是css和js文件。index.html中会自动引入我们所需要的js文件
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="shortcut icon" href="./favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><link rel="manifest" href="./manifest.json"/><title>SAMP</title><link href="./static/css/8.59c49306.chunk.css" rel="stylesheet"><link href="./static/css/main.2493b0ba.chunk.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div><script>!function(d){function e(e){for(var f,a,c=e[0],r=e[1],t=e[2],n=0,b=[];n<c.length;n++)a=c[n],u[a]&&b.push(u[a][0]),u[a]=0;for(f in r)Object.prototype.hasOwnProperty.call(r,f)&&(d[f]=r[f]);for(p&&p(e);b.length;)b.shift()();return i.push.apply(i,t||[]),o()}function o(){for(var e,f=0;f<i.length;f++){for(var a=i[f],c=!0,r=1;r<a.length;r++){var t=a[r];0!==u[t]&&(c=!1)}c&&(i.splice(f--,1),e=l(l.s=a[0]))}return e}var a={},u={7:0},i=[];function l(e){if(a[e])return a[e].exports;var f=a[e]={i:e,l:!1,exports:{}};return d[e].call(f.exports,f,f.exports,l),f.l=!0,f.exports}l.e=function(t){var e=[],a=u[t];if(0!==a)if(a)e.push(a[2]);else{var f=new Promise(function(e,f){a=u[t]=[e,f]});e.push(a[2]=f);var c,n=document.createElement("script");n.charset="utf-8",n.timeout=120,l.nc&&n.setAttribute("nonce",l.nc),n.src=l.p+"static/js/"+({}[t]||t)+"."+{0:"8042c679",1:"672af22c",2:"447bdd02",3:"003c1cef",4:"200718e9",5:"74895228",9:"b29ea776",10:"17638c3f",11:"d08aa3e5",12:"96082dcd",13:"aaf55086",14:"d0740c7c",15:"b7f01cb7",16:"dc657f8e",17:"f6b8e2bc",18:"d4208516",19:"ca41b9b7",20:"e8e4ab9a",21:"2c0f22e2",22:"7f02d756",23:"4058de66",24:"9315b171",25:"b106ec53",26:"a287ec23",27:"df9fd31c",28:"477408d2",29:"62de0592",30:"327eaf59",31:"4e3c84bd",32:"300e07ef",33:"0a9f0074",34:"0f8fa885",35:"b818b961",36:"06289a47",37:"121fe697",38:"03e3e1ef",39:"218f431b",40:"ac7f6709",41:"4ace9b9f",42:"79361df5",43:"fb024e75",44:"a0e809da",45:"83a13f53",46:"f5c8cd24",47:"278ffaa3",48:"70968b0c",49:"ac4ea846",50:"a2d64416",51:"42d60ff0",52:"88a1fa2f",53:"7afa40ee",54:"cfe82ac2",55:"8424731e",56:"f0ab384a",57:"98eb00f0",58:"a7bd6edd",59:"bce430d8",60:"ad425b06",61:"b137b3b1",62:"c63a1f65",63:"fa5fcf89",64:"8497f906",65:"598bcc7a",66:"0b74b419",67:"9080c73d",68:"65dc69e5",69:"890c4f5a",70:"273725bd",71:"4c118d89",72:"090be0d7",73:"112a8098",74:"9bebb642",75:"a6c51f5c",76:"bbe8fa27",77:"01a984ed",78:"9a5b0232",79:"4c7a30dc",80:"d8ac79fa",81:"0a36a950",82:"7f5ce888",83:"d909aed6",84:"3578be64",85:"9bd05dce",86:"e0ec2722",87:"4ebd97ee",88:"635e8f52",89:"a1839553",90:"cf99e222",91:"dc18d15e",92:"3defa7bb",93:"a61bbf9a",94:"1dfd6845",95:"b5f25802",96:"9ebf4931",97:"f5ec4240",98:"344c292b",99:"0b623248",100:"c1ef542e",101:"7cb3f744",102:"856b602b",103:"45fd06e4",104:"2b5f4b02",105:"b4db42b6",106:"ba7108d6",107:"3b0d5e58",108:"c5833bd5",109:"e4cc84c7",110:"1e11950a",111:"2b68abd1",112:"8587fea1",113:"1db5dc31",114:"a745ba9c",115:"69f6f36e",116:"49f50f80",117:"32df4d43",118:"cf9e7596",119:"759752f3",120:"f4cb3942",121:"86ab13ee",122:"f55c5bcd"}[t]+".chunk.js",c=function(e){n.onerror=n.onload=null,clearTimeout(b);var f=u[t];if(0!==f){if(f){var a=e&&("load"===e.type?"missing":e.type),c=e&&e.target&&e.target.src,r=new Error("Loading chunk "+t+" failed.\n("+a+": "+c+")");r.type=a,r.request=c,f[1](r)}u[t]=void 0}};var b=setTimeout(function(){c({type:"timeout",target:n})},12e4);n.onerror=n.onload=c,document.head.appendChild(n)}return Promise.all(e)},l.m=d,l.c=a,l.d=function(e,f,a){l.o(e,f)||Object.defineProperty(e,f,{enumerable:!0,get:a})},l.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},l.t=function(f,e){if(1&e&&(f=l(f)),8&e)return f;if(4&e&&"object"==typeof f&&f&&f.__esModule)return f;var a=Object.create(null);if(l.r(a),Object.defineProperty(a,"default",{enumerable:!0,value:f}),2&e&&"string"!=typeof f)for(var c in f)l.d(a,c,function(e){return f[e]}.bind(null,c));return a},l.n=function(e){var f=e&&e.__esModule?function(){return e.default}:function(){return e};return l.d(f,"a",f),f},l.o=function(e,f){return Object.prototype.hasOwnProperty.call(e,f)},l.p="./",l.oe=function(e){throw console.error(e),e};var f=window.webpackJsonp=window.webpackJsonp||[],c=f.push.bind(f);f.push=e,f=f.slice();for(var r=0;r<f.length;r++)e(f[r]);var p=c;o()}([])</script><script src="./static/js/8.3ed6cbe4.chunk.js"></script><script src="./static/js/main.9e1bd088.chunk.js"></script></body></html>
拉到最后可以看到他主要是引入了两个主要的js文件,那么其他的js文件应该是在这两个js文件中被引入的。
编译不错问题的话,说明你的webpack的配置基本上没有问题。
那接下来就是发布到Tomcat下。
我们一般默认的都是在tomcat/webapps下创建一个文件,名称一般命名为项目名字(比如这里我叫samp),然后将编译之后即build下的代码copy到项目samp文件之下。
在这里有个坑就是,在使用webpack的配置时,默认打包是在"/"下即tomcat/webapps/ROOT下,这就有点扯了,因为ROOT下有一堆自己的配置,所以我么需要将代码放到webapps下。
即tomcat/webapps/samp目录下,这样就会有个问题就是访问的时候会出现404.
所以我们需要改一下path,可以在这个路径下修改代码。在上面的编译过程中,大家也看到了他的提示信息,我是改为"./"。
在本地代码中,找到samp/node-modules/react-scripts/config/paths.js中
大概是45行,需要将之前的"/"改为"./"。
为什么修改这个文件,可以看一下webpack中的配置,因为我们使用的path就是从这里面来的,所以找到这个文件就可以修改一下。
修改之后重新build,然后将代码传到Tomcat下。
再通过http://ip:端口号/samp(项目名称)就可以访问了。
当然这个路径也需要你再react路由中进行配置。
import React, { Component } from 'react';
import { Route, Switch } from 'react-router-dom'
import LoignLayout from './LoginLayout'
import IndexLayout from './IndexLayout'
class Layouts extends Component {
constructor(props) {
super(props);
this.state = {}
}
render() {
return (
<Switch>
<Route path="/samp/" component={LoignLayout} />
<Route path="/samp/index/" component={IndexLayout} />
</Switch>
);
}
}
export default Layouts;
那么当我们在地址栏输入地址的时候就能正常访问了。