仓库地址:gitee.com/dreamer2011…javascript
本项目适用于移动端H5
混合开发的React项目
,功能包括:css
字体自动适配
,默认按照UI设计稿750*1334
(webpack里能够配置UI稿原始尺寸)上下适配各类手机设备;sprite
雪碧图和相关 sprite.less
,无需手动建立雪碧图和手写less;webpack4最新构建
,分离压缩代码、去除重复引用的css代码、开发环境生成sourceMap方便调试、生产环境移除console.log打印信息等构建,并生成相关gz包
,优化页面资源加载;Fetch封装 dataService 服务
,统一格式进行相关接口的GET, POST请求;FetchData.jsx
, 进行组件数据预加载和状态注入;React router components
进行 lazing load 懒加载
, 带有组件预加载loading
和加载失败error提示
,增长页面友好提示,性能也更加优化;liveServer
对生成的PROD生产环境代码,进行预先运行检查;browserHistory
路由模式,相关本地环境配置已经自动配置(上线时,须要在服务器配置nginx
)自动生成
文件夹保存当前打包生成的tar包
, 防止线上部署失败; 有备份的话能够回滚以前的代码版本;若是抛却相关app h5设置,可看成 PC 项目,兼容 ie9+
;手机端调试面板
,功能至关于打开 PC 控制台,能够很方便地查看 console, network, cookie, localStorage 等关键调试信息;Sentry监控代码异常错误上报
,访问 f5fe3d6e599849bfa1d1f0c26d1c3213@sentry.io/1729437,查看报…Bundle分析报告report
Local Server:html
yarn dev
#or
npm run dev
复制代码
build bundle:java
yarn build
#or
npm run build
复制代码
生成 tar 包node
# 默认生产 cdn.tar.gz
yarn tar
#or
npm run tar
# 能够自定义tar包名
yarn tar react
#or
npm run tar react
复制代码
测试打包后要上线的代码:react
yarn start
#or
npm run start
复制代码
webpack.config.jswebpack
{
test: /\.less$/,
use: [
isProduction ? MiniCssExtractPlugin.loader : 'style-loader',
'css-loader',
'less-loader',
// 配置相关移动端 VW 布局
{
loader: 'postcss-less-loader',
options: {
ident: 'postcss',
plugins: () => [
postcssPxToViewport({
viewportWidth: 750, // (Number) The width of the viewport.
viewportHeight: 1334, // (Number) The height of the viewport.
unitPrecision: 3, // (Number) The decimal numbers to allow the REM units to grow to.
viewportUnit: 'vw', // (String) Expected units.
selectorBlackList: ['.ignore', '.hairlines'], // (Array) The selectors to ignore and leave as px.
minPixelValue: 1, // (Number) Set the minimum pixel value to replace.
mediaQuery: false // (Boolean) Allow px to be converted in media queries.
})
]
}
}
]
}
复制代码
webpack.config.jsnginx
// 图片整合成雪碧图
const SpritesmithPlugin = require('webpack-spritesmith');
// customerTemplate
const templateFunction = function(data) {
// console.log('---', data)
const shared = `.sprite_ico { background-image: url(I);display:inline-block;background-size: Wpx Hpx;}`
.replace('I', data.sprites[0].image)
.replace('W', data.spritesheet.width)
.replace('H', data.spritesheet.height);
const perSprite = data.sprites
.map(function(sprite) {
return `.sprite_ico_N { width: Wpx; height: Hpx; background-position: Xpx Ypx;}`
.replace('N', sprite.name)
.replace('W', sprite.width)
.replace('H', sprite.height)
.replace('X', sprite.offset_x)
.replace('Y', sprite.offset_y);
})
.join('\n');
return "//out:false" + '\n'+shared + '\n' + perSprite;
};
// 雪碧图
plugins.push(
new SpritesmithPlugin({
src: {
//下面的路径,根据本身的实际路径配置
cwd: path.resolve(__dirname, './src/assets/icons'),
glob: '*.png'
},
// 输出雪碧图文件及样式文件
target: {
//下面的路径,根据本身的实际路径配置
image: path.resolve(__dirname, './src/assets/sprite.png'),
css: [
[
path.resolve(__dirname, './src/less/sprite.less'),
{
format: 'function_based_template'
}
]
]
// css: path.resolve(__dirname, './src/less/sprite.less')
},
// 自定义模板
customTemplates: {
function_based_template: templateFunction
},
// 样式文件中调用雪碧图地址写法
apiOptions: {
// 这个路径根据本身页面配置
cssImageRef: '../assets/sprite.png'
},
spritesmithOptions: {
// algorithm: 'top-down'
padding: 5
}
})
);
复制代码
icon格式须要是
png
, 把须要生成雪碧图的小icon放置在目录src/assets/icons
下面,而后执行yarn build
,就能够在src\less\sprite.less
下去找本身的图标ICON的className
;好比:git
使用的时候,直接引入 sprite_ico sprite_ico_guzhi
便可;无需配置引入sprite.less
文件;github
<span className="sprite_ico sprite_ico_guzhi"></span>
复制代码
React 组件必须首字母大写,其余请参考官网
cdn --------------构建后的资源
public --------------静态资源
src --------------全部React资源
actions --------------- 相关action
assets --------------- 全部静态图片资源
components ------------ 公共组件
dataService----------- 请求服务
less ------------------ less文件
pages ----------------- 全部入口文件下的子文件
reducers -------------- reducer
router ---------------- 路由设置
store ----------------- 状态存储
utils ----------------- 公共方法
.....其他都是App.jsx login.jsx等入口文件
tar --------------构建备份的tar
....
复制代码
index.html
<script src="https://cdn.ravenjs.com/3.26.4/raven.min.js" crossorigin="anonymous"></script>
// 代码错误监控 Raven.config('https://f5fe3d6e599849bfa1d1f0c26d1c3213@sentry.io/1729437').install();
复制代码
添加 ==React ErrorBoundary== 组件,具体能够参考官方文档 :
import React from 'react';
import oops from "@assets/oops.png";
// catch JavaScript errors anywhere in their child component tree, log those errors, and display a fallback UI
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { error: null };
}
componentDidCatch(error, errorInfo) {
this.setState({ error });
Raven.captureException(error, { extra: errorInfo });
}
render() {
if (this.state.error) {
//render fallback UI
return (
<div className="snap" onClick={() => Raven.lastEventId() && Raven.showReportDialog()}>
<img src={oops} />
<p>We're sorry — something's gone wrong.</p>
<p>Our team has been notified, but click here fill out a report.</p>
</div>
);
} else {
//when there's not an error, render children untouched
return this.props.children;
}
}
}
export default ErrorBoundary;
复制代码
此时若是咱们如今故意改错一些东西,则会提示 fallback UI:
点击文字,用户能够主动发一个 issue 给咱们:
目前报错信息会上传到 sentry.io,也能够建立本身的私服;
index.html
//在手机端进行查看控制台相关资源
(function () {
//推荐 CDN 加载
const src = 'https://cdn.jsdelivr.net/npm/eruda@1.5.8/eruda.min.js';
// var src = 'node_modules/eruda/eruda.min.js';
if (!/debug=true/.test(window.location) && localStorage.getItem('debug') != 'true') return;
document.write('<scr' + 'ipt src="' + src + '"></scr' + 'ipt>');
document.write('<scr' + 'ipt>eruda.init();</scr' + 'ipt>');
})();
复制代码
启动项目后,访问 http://192.168.1.100:3002/?debug=true , 在手机上如图:
<script src="https://hammerjs.github.io/dist/hammer.js"></script>
复制代码
举例:
import React from 'react';
class HammerPan extends React.Component {
render() {
return <div className="myElement">888</div>;
}
componentDidMount() {
const myElement = document.querySelector('.myElement');
// create a simple instance
// by default, it only adds horizontal recognizers
const mc = new Hammer(myElement);
// let the pan gesture support all directions.
// this will block the vertical scrolling on a touch-device while on the element
mc.get('pan').set({ direction: Hammer.DIRECTION_ALL });
// listen to events...
mc.on('panleft panright panup pandown tap press', ev => {
myElement.textContent = ev.type + ' gesture detected.';
});
}
}
export default HammerPan;
复制代码
启动项目后,访问 http://192.168.1.100:3002/pan,便可查看
# NPM
npm install --save-dev webpack-bundle-analyzer
# Yarn
yarn add -D webpack-bundle-analyzer
复制代码
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
module.exports = {
plugins: [
new BundleAnalyzerPlugin()
]
}
复制代码
package.json
"scripts": {
"dev": "webpack-dev-server --mode development",
"build": "bash ./rm.sh && webpack --mode production --profile --json > stats.json && yarn tar",
"start": "node ./server.js",
"tar": "bash ./tar.sh"
}
复制代码