react打包后图片丢失_给 React 组件自动加上 react-hot-loader
首发于我的博客:https://www.ahonn.me/post/54
在 React 项目中使用 Webpack HMR 时,通常会使用 react-hot-loader 来进行局部热更新。但使用 react-hot-loader 需要对原有代码进行修改,这对多入口的老项目非常的不友好。
为了使用上 HMR 这一“激动人心”的功能,需要在构建时在原有代码上自动添加 react-hot-loader 的相关代码。
因此我们需要创建一个 webpack loader 来在 babel 处理前将 react-hot-loader 相关代码添加到源
码中。
react-hot-loader 的用法
react-hot-loader 的用法非常简单,安装 react-hot-loader
后在 babel 配置中加上 react-hot-loader/babel
插件:
// .babelrc
{"plugins": ["react-hot-loader/babel"]
}
并且在根组件文件中添加如下代码即可:
// App.js
import 'react-hot-loader';
import { hot } from 'react-hot-loader/root';
const App = () => <div>Hello World!</div>;
export default hot(App);
通过 webpack loader 修改代码
这里我们需要创建一个 webpack loader 来做这件事情,在代码被 babel-loader
处理前进行插入与修改。
Webpack Loader 用于对模块源码的转换,所以这里我们在源码顶部添加 import 'react-hot-loader'
与 import { hot } from 'react-hot-loader/root'
,并将默认导出修改为 export default hot(xxx)
。
如何对模块源码进行分析并修改呢?答案是 @babel/parser 与 @babel/traverse,通过 @babel/parser 解析为 AST 之后使用 @babel/traverse 遍历节点并进行修改。
将模块代码转换为 AST
const ast = parse(source, {sourceType: 'module',plugins: ['jsx'],
});
对于 React 我们需要启用 jsx 插件(.babelrc 中需要有 babel-preset-react)才能正确的解析代码。如果使用了其他特性的话也需要启用相对应的插件,具体有哪些插件可以查看@babel/parser · Babel。
解析成 AST 之后我们就可以进行一些操作了,首先我们先把导入 react-hot-loader 的代码都加到模块源码的顶部。
导入 react-hot-loader
ast.program.body.unshift(// insert `import 'react-hot-loader';`t.importDeclaration([], t.stringLiteral('react-hot-loader')),// insert `import { hot } from 'react-hot-loader/root';`t.importDeclaration([t.importSpecifier(t.identifier('hot'), t.identifier('hot'))],t.stringLiteral('react-hot-loader/root'))
);
这里的 t
指的是 @babel/types
,这个包包含了一些用于判断与创建 AST 节点对象的工具方法。上述代码在模块代码的最顶端导入了 react-hot-loader
以及从 react-hot-loader/root
导入需要调用的 hot
方法。
导入完之后我们需要对导出进行修改,将默认导出的组件包上一层 HOC。
修改模块导出
traverse(ast, {ExportDefaultDeclaration: (path) => {path.node.declaration = t.callExpression(t.identifier(identifier),[path.node.declaration]);},
});
这样就能将 export default xxx
修改为 export default hot(xxx)
。但现实世界是残酷的,我们还可能遇到这样的代码:
export default const App = () => {};
export default class App {};
// 或者这样
export default () => {}
export default class {}
对于直接导出匿名函数或者类的,我们无能为力(其实也是有办法的,给它命一个名再导出,但是我不想这样干,使用暂时先不管了)。
但是对于导出非匿名的函数或者类的话,我们就可以进行修改了。把 export default
抽到底部,再把组件调用 hot()
之后进行导出。
稍微修改一下实现:
const insertNodes = [];
traverse(ast, {const { declaration } = path.node;// 如果是 export default const App = () => {} 或者 export default class App {}if (t.isClassDeclaration(declaration) || t.isFunctionDeclaration(declaration)) {if (t.isIdentifier(declaration.id)) {path.replaceWith(declaration);// 在源码尾部进行默认导出insertNodes.push(t.ExportDefaultDeclaration(t.callExpression(t.identifier(identifier),[declaration.id])));}return;}// 如果是 export default Appif (t.isIdentifier(declaration)) {path.node.declaration = t.callExpression(t.identifier(identifier),[path.node.declaration]);return;}
});
if (insertNodes.length > 0) {ast.program.body.push(...insertNodes);
}
好的,我们完成了解析代码为 AST 与修改 AST。是时候把它转回代码交给下一个 loader了。
AST 转换为代码
与 @babel/parser 类似,babel 也提供了将 AST 转换回代码的包: @babel/generator。不需要什么乱七八糟的魔法或者咒语,只需要 const { code } = generate(ast);
就可以获得崭新的 hot exportd 组件代码了。将它返回之后就可以愉快的使用 webpack HMR 了!!
还有一些坑
- 导入了就必须调用,没有例外
如果对每个模块代码都进行以上操作的话,会发现页面上会提示 hot update was not successful
。这是因为在调用 ReactDOM.render()
的文件中 import { hot } from 'react-hot-loader'
之后没有进行调用。所以我们需要添加一些判断,只在导出 React 组件的模块中进行代码修改。
- hot exportd 的组件被继承无效
如果 A 组件继承 B 组件,而 B 组件被自动添加了 react-hot-loader 相关代码的话,A 组件将无法继承 B 组件的 state 与 methods。这个时候 B 组件已经不是 B 组件了,而是 hot()
这个 HOC 返回的 ExportedComponent。理论上被继承的组件也是不应该调用 hot()
的,因此我们需要添加配置函数,用来判断是否需要修改模块代码。
基于以上的实现以及发现的坑点,我写了一个 react-hot-export-loader 用来给 React 组件自动加上 react-hot-loader,并且添加了一些判断或者配置来避免上面的坑。具体的使用方式这里就不在赘述了,请移步 README.md。
写在最后
其实按照一般套路,我们只需要在项目的入口处加上几句 react-hot-loader 代码就可以了。但是无奈的是有些时候总是不会按照套路出牌,例如 webpack 打包的逻辑是公共的,打包的是多入口,或者入口你根本就不知道是什么样的。所以才会出现这样一篇文章,这里权当做记录解决这一问题的方案,顺带输出一个 loader 给有缘人。
react打包后图片丢失_给 React 组件自动加上 react-hot-loader相关推荐
- react打包后图片丢失_使用 webpack 搭建 React 项目
简评:相信很多开发者在入门 react 的时候都是使用 create-react-app 或 react-slingshot 这些脚手架来快速创建应用,当有特殊需求,需要修改 eject 出来的 we ...
- react打包后图片丢失_手写Webpack从0编译Vue/React项目
当前前端开发,90%的项目都是Vue和React,然而70%的同学都基于脚手架创建项目,因为脚手架会包含项目基本框架.webpack配置.scss/sass/less解析.babel配置.DevSer ...
- react打包后图片丢失_宜信技术实践|指尖前端重构(React)技术调研分析
一.为什么选择React React是当前前端应用最广泛的框架.三大SPA框架 Angular.React.Vue比较. Angular出现最早,但其在原理上并没有React创新的性能优化,且自身相对 ...
- react打包后图片丢失_手搭一个 React,Typescript,Koa,GraphQL 环境
本文系原创,转载请附带作者信息:yhlben 项目地址:https://github.com/yhlben/cdfang-spider 前言 在实际的开发过程中,从零开始初始化一个项目往往很麻烦,所以 ...
- react打包后图片丢失_如何快速构建React组件库
俗话说:"麻雀虽小,五脏俱全",搭建一个组件库,知之非难,行之不易,涉及到的技术方方面面,犹如海面风平浪静,实则暗礁险滩,处处惊险〜 目前团队内已经有较为成熟的 Vue 技术栈的 ...
- react打包后图片丢失_React系列四 - React脚手架
一. 认识脚手架 1.1. 前端工程的复杂化 如果我们只是开发几个小的demo程序,那么永远不需要考虑一些复杂的问题: 比如目录结构如何组织划分: 比如如何管理文件之间的相互依赖: 比如如何管理第三方 ...
- react打包后图片丢失_React中型项目的优化实践
本文可能涉及的内容-- 项目介绍 整个项目大概有60+个页面,用到的组件大概150+,package里面的依赖大概有70+个,应该勉强算得上是一个中型的React的项目了. 下面给大家看看我们现在bu ...
- vite+vue3打包后图片404问题:已解决
let content = [{title:"每周菜单",cont:[{url:"/procurementchildren/meke_menu",name:&q ...
- vue-webpack打包后图片找不到
一.问题描述 项目在使用webpack打包后,很常见的一个问题就是dev环境可见的图片在打包之后找不到文件. 二.异常原因 1.图片未被打包 当使用的图片url是动态拼接的,可能会出现webpack没 ...
- react 打包体积过大_解决 webpack 打包文件体积过大
webpack 把我们所有的文件都打包成一个 JS 文件,这样即使你是小项目,打包后的文件也会非常大.下面就来讲下如何从多个方面进行优化. 去除不必要的插件 刚开始用 webpack 的时候,开发环境 ...
最新文章
- Android Fragment 你应该知道的一切
- SpringMVC获取请求参数-POJO类型参数
- 用sqoop将mysql的数据导入到hive表中,原理分析
- delphi7 安装delphi 5 delphi 6控件
- 仿淘宝分页按钮效果简单美观易使用的JS分页控件
- DM8168 DMM(2)
- Linux 命令(128)—— useradd 命令
- python运维方法_Python运维开发基础09-函数基础【转】
- SpringBoot整合Sharding-JDBC实现水平分表
- 全球130多个国家的货币代码对照表
- NJUPT【电工电子基础实验】
- 二级c语言考试怎么调试程序,计算机二级C语言上机考试操作步骤及流程和注意事项...
- sim3在orbslam2与gis中的应用
- Android 实现图片闪烁效果
- 数据库 -- 基础操作(二)
- Wangle中EchoClient分析
- 初入编程 - HTML + CSS
- 计算机打字不盲打可以吗,能盲打了,为什么打字速度还不快?现在才40左右,怎样才能达到60以上呢?...
- 包含空格的项目的文件/路径部分需要用括号括起来
- 画布渐变以及svg图形
热门文章
- 看不见的“网” ,一文读懂阿里云基础设施网络
- 凌云一周看点 | 混合云多Region架构;云上用户定制化网络;边缘云全站加速;什么是操作系统的云原生...
- 2020阿里云线上峰会预告 | 凌云时刻
- OTA时代来了!由新一代私有云揭开序幕
- linux如何实现c语言程序,在Linux下如何利用C语言来实现一个Sniffer
- oracle数据库导dump,oracle数据导入,导出dump文件
- 【故障诊断分析】基于matlab GUI小波包能量可视化设计【含Matlab源码 1788期】
- 【优化算法】杂草优化算法(IWO)【含Matlab源码 1076期】
- python中的字典操作_python中的字典以及相关操作
- java后台处理跨域问题_用cros解决前后端分离的跨域问题