最近写了一个react的小项目,记录下这次开发的经验做个总结
用react18+react-router-dom +ant-mobile+redux+axios+webpack开发了一个移动端的项目
用了最新版的react跟路由 发现react对我这种小白不太友好,react-router-dom6 更新了一些东西,我搜到的很多文档都是旧的,官方的文档怎么都没找到,因为我之前没有怎么接触过react的项目,自己瞎搞不断的报错。后面去尝试还是成功了,内心还是开心的,花了研究去试错,能成功就是最大的鼓励
路由
// 首页APP.js 的代码
// react-router-dom6 里面用的Routes跟Route 好像支持嵌套了 element之前的是component 这些都不一样的
const App = () => (
<Provider store={store} >
<BrowserRouter>
<Routes>
<Route path='/login' element={<LoginPage/>} ></Route>
<Route path='/orange/add' element={<Add/>} ></Route>
<Route path='/list/detail' element={<ListDetail/>} ></Route>
<Route path='/list/detail/:id' element={<ListDetail/>} ></Route>
<Route path='/my/createTask' element={<CreateTask/>} ></Route>
<Route path='/my/taskManage' element={<TaskManage/>} ></Route>
<Route path='/my/businessManagement' element={<BusinessManagement/>} ></Route>
<Route path='/my/bussinessSearch' element={<BussinessSearch/>} ></Route>
<Route path='/my/memberSearch' element={<MemberSearch/>} ></Route>
<Route path='/my/myExternal' element={<MyExternal/>} ></Route>
<Route path='/my/recommendRegister' element={<RecommendRegister/>} ></Route>
<Route path='/my/recommendRegister/:id' element={<RecommendRegister/>} ></Route>
<Route path='/my/buyAuth' element={<BuyAuth/>} ></Route>
<Route path='/my/contant' element={<Contant/>} ></Route>
<Route path='/my/updateUser' element={<UpdateUser/>} ></Route>
<Route path='/my/realNameAuth' element={<RealNameAuth/>} ></Route>
<Route path='/my/surenShow' element={<SurenShow/>} ></Route>
<Route path='/my/createShop' element={<CreateShop/>} ></Route>
<Route path='/my/createShop/:shopId' element={<CreateShop/>} ></Route>
<Route path='/reset' element={<Reset/>} ></Route>
<Route path='/query/search' element={<Search/>} ></Route>
</Routes>
<PrivateRoute path='/'>
<KeepAlive>
<Nav />
</KeepAlive>
</PrivateRoute>
</BrowserRouter>
</Provider>
路由的模块 通过PrivateRoute 这个组件包裹 拦截如果未登录的话 就跳登录页 登录了就显示子组件(也就是Nav 我把 主页都放到Nav 这个组件)
//PrivateRoute 组件
// 这里通过token 判断有没有登录 通过useEffect这个hook 监听isLogin 来实现 是否登录的跳转
function PrivateRoute({children, ...rest}) {
const isLogin = getSession('isLogin')
const navigate = useNavigate()
const {pathname} = useLocation()
const dispath = useDispatch()
// console.log(pathname)
useEffect(()=>{
const token = getSession('token')
if(pathname == '/query/search'){
navigate('/query/search')
return
}
if(token == ''|| token == undefined){
dispath({type:'LOGIN_FAIL'})
navigate('/login')
}else{
if(pathname == '/login'){
navigate('/')
return
}
navigate(pathname)
}
// if(!isLogin && pathname != '/login' && pathname != '/reset'){
// navigate('/login')
// }else if(isLogin && pathname == '/login'){
// }
},[isLogin])
return (
<>
{
isLogin ? children
:(
<Routes><Route {...rest}
element = {
<Navigate to='/login' replace={true} />}
/>
</Routes>)
}
</>
getSession 这个方法 我刚开始是存到sessionStorage 里面 后来测试的时候发现微信进去每次都要重新登录,好像每次进去都是不同的session (类似于浏览器的打开新的标签页,后来改了里面的存储 没有改方法名字有几个页面用到)
别名
在开发的时候我喜欢直接 引入对应的文件夹 比如 组件的就是view下面的,api 就是api 下面的,工具类的就是until 文件夹里面的,图片就是image,不想写../view ,用相对路径这样找的话每个页面都写比较麻烦,就去webpack.config.js
配置别名,首先要eject暴露webpack 的配置,然后在webpack.config.js文件修改,直接改并不能改掉,(这里说直接改的意思是不修改其他的文件,只用path.resolve(__dirname,'src/view')
),我发现这样改并不生效,我查看了src 的写法,是在paths.js 文件里面写了一些变量暴露出来,我也试着这么写,感觉react没有vue.config.js 这种文件配置起来比较方便,我查到react-app-rewired customize-cra 这个可以实现类似vue.config.js的写法配置 ,可惜没有成功,我便没有继续鼓捣了。
二次封装axios
当我开始对接口的时候,我用了axios ,由于axios是返回了所有的报文,我要用的只是报文的body里面的data,就简单的二次封装下,顺便 请求的时候显示loading 刚开始我想着 直接显示组件,react 报错不能在component之外使用 标签,这也能理解,一个纯处理数据的函数里面显示标签也不符合设计思想,我就想能不能在APP.js 里面放个loading的组件,拦截请求的时候调用,通过redux 去修改某个属性让它显示隐藏的,但是我发现并不行,APP.js 只会在第一次渲染的时候去拿属性值,并没有监听属性值动态改变,写vue 写习惯了吧,以为全局的会响应去触发。我就去看ant-mobile 的loading是怎么实现的,我发现 它是通过document.createElement塞入页面的,得了,我自己也可以动态的实现一个,写了一个动态生成loading的方法,简单的实现了一下。在状态码为401 的时候清除登录的信息 然后跳转到登录页,这里没有比较好的方法用window.location.href去跳转 ,这里又搞不到路由,只能用最原始的方法了。问了几个朋友他们也是这么处理的。
打包
什么都没有配置 打包出来的文件贼大,我没想到整个项目打包出来居然有6M,光图片就占了5M,js占了500K,我发现是某些png 文件有500KB,有10几个,这就占了很大一部分,我就去查找插件能不能压缩下png,找了一个插件 压缩图片的,很鸡肋,只压到了400多KB,没啥用的,这个插件后面把我坑死了,朋友帮我把图片的大小通过一个网站切到了200200 ,体积一下子从500K 到了70K,我发现页面显示是50px ,默认图片是10001000的难怪那么大 ,也用不上这么大,看来以后这方面的优化也要考虑上,解决完图片的问题,就想着把js 分割了,一个500多K 的还是很大的,查了一下发现要配置splitChunk,其实这玩意基本是默认的吧,为啥不写到create-react-app 里面去,配置完,打包到服务器发现 页面莫名其妙的多了 一行文字,我全局搜索没找到哪里有,查了半天都不知到哪来的
开发环境只有第一次显示会 刷新就没了,服务器上一直都有,刷新也有,后面我发现是 我没有覆盖打包出来的asset-manifest.json文件导致映射对不上,这问题出现好几次,后面因为那个图片压缩的包导致出现了一次,我把那个包去除就没了,后来一次是我装了一个包,没有用也会出现,我经常碰到这种奇怪的问题
我想的是路由按需加载,点到的时候就去引入,这样首页就不用加载那么多,第一次用react,全都用了函数组件,查到文档都是要用到类组件,果断放弃 ,也没有时间让我去研究了。
后面就是调试 兼容性的问题 ios 的兼容性问题 真的很多,奈何我用的是安卓,盲猜 想用 safari window版去调试 发现 打不开 一直是空白的,苹果8以下的打开都是空白的,这玩意着急用 我也没多少时间去弄。
我感觉webpack 对于我这种小白不太友好,我就去拥抱vite,我将这个项目的src 复制出来弄到vite 里面去,也碰到了一些问题,刚开始 是ts 的问题 因为我之前粘ant-mobile 多多少少会有一些ts 的语法,虽然我没有写ts的方法,vite 就不解析了,就开始报错,我将后缀改成tsx 就没问题了,配置好别名,运行发现不识别require,require is no define
webpack 帮我们处理了2种模式混入的,图片的加载都习惯写 <img src={require('image/logo.jgp')} />
vite的话就要import的方式去使用,但是我好多地方都用了并不想修改,就查文档,看看有没有奇迹,果然让我找到一个插件,也是坑我的一个东西
import requireTransform from 'vite-plugin-require-transform';
requireTransform({
fileRegex: /.js$|.tsx$/
}),
这个包 在本地运行的时候没问题 ,但是打包的时候坑死我了,怎么都没想到是它有问题
报这个错误 我以为是不兼容require,又装了commonJS 的包,结果还是不行
运行没问题,打包有问题,运行的时候是用了go语言的esbuild 然而打包的时候是rollup ,我又查了rollup 的问题,然而并没有什么卵用,后来我去群里问了大佬,有大佬说是这个包对node 版本不兼容,让我查这个包要用的版本,我查了一下这个包是2年前发布的,应该要用2年前的node吧。我重新安装node 的时候出了问题,我也不想重新安装node 我怕旧版本的node 不支持vite 毕竟vite2年前好像还没出来,我以为是react的问题,我重新生成一个react,运行,打包,没有问题,那就是安装的包有问题,又重新孤岛了一遍,在装到require转换这个插件的时候,我试着屏蔽去打包,结果成功了,那就是这个包有问题,我想着要不去社区反应下,看看有没有别的包可以代替,没有的话自己写一个包试一试,反正都是不会,学一学也没多大的事,主要是这个玩意搞完了 ,这个事拿来玩的,不赶时间可以好好的玩,结果再社区发现另一个包
我改成这个包,运行没问题 ,打包成功了,困扰我很久的问题终于解决了
我发现vite打包出来的文件有点杂,图片跟css文件js 文件在同一个文件下,可能习惯了webpack 那种 图片在image ,css文件在css 文件夹下面,js 在js 文件夹下面比较直观,也比较方便,比如我修改的时候 只改了css跟js ,上传服务器的时候 只覆盖css 跟js 文件,图片到后面基本不修改了。
我就去查vite的文档,打包用的是rollup 就要去配置rollup 的选项
rollupOptions:{
output:{
chunkFileNames:'static/js/[name]-[hash].js',
entryFileNames:"static/js/[name]-[hash].js",
assetFileNames:"static/css/[name].[hash].[ext]",
manualChunks(id) { //静态资源分拆打包
if (id.includes('node_modules')) {
return id.toString().split('node_modules/')[1].split('/')[0].toString();
}
}
}
}
这样配置 还是有些问题,css 会跑到图片里面去,js 会分割成n个 然后压缩成压缩包
plugins: [
react(),
legacyPlugin({
targets: ['chrome 52'], // 需要兼容的目标列表,可以设置多个
additionalLegacyPolyfills: ['regenerator-runtime/runtime'] // 面向IE11时需要此插件
}),
viteCompression({ //gzip压缩
verbose: true,
disable: false,
threshold: 10240,
algorithm: 'gzip',
ext: '.gz',
}),
vitePluginRequire({
// @fileRegex RegExp
// optional:default file processing rules are as follows
// fileRegex:/(.jsx?|.tsx?|.vue)$/
}),
// requireTransform({
// fileRegex: /.js$|.tsx$/
// }),
]
顺便做下兼容的,打包出来发现 有2个js 文件在图片里面 是Polyfill文件 ,头大,还是没法按照自己的想法来,可能是自己太菜了,查不到有啥包可以这样,只能这样吧,要么后期自己写个包玩玩。
后面又出现那个问题了
我以为是我为了搞兼容ie的时候装了一些包导致的,删除了一些没用的包也没找到,回滚到前几个版本也存在这个问题,找了好久 ,发现我打出来的包没有这个问题,但是服务器返回的页面是有的,感觉像是有些标签没有闭合导致的,我就看了一下index.html文件
我发现 是生成的link 标签没有闭合 ,可能是这个导致的,我上网找了一下
参考了这篇文档
www.cnblogs.com/zealGIS/p/1…
试了一下,果然是因为标签没闭合导致的这个问题 但是 这个是重新将打包好的文件 重新用正则去关闭标签,但是没有压缩,我又在下面写了一些正则去压缩,后面我想去除标签跟标签的空格的时候
result = result.replace(/>\s+</g,'><')
然后 又gg 了 那个情况又出现了,我发现了 可能官网就是这么压缩导致了标签没又闭合 后面只去除空格就没有这个问题了