0 引言

最近公司有一个 php 的项目,要 port 到 node.js 来。我之前没有接触过这个项目,整个项目使用的是 yaf 框架。整个项目流程是调用服务端的业务数据,然后拼装数据,返回给前端;前端没有做到前后端分离,还有很多的页面在服务器端进行渲染 。

1 难度

1.1 php

php 之前没有写过几行代码,但是还能看懂。yaf 也没有接触过,只到听说的程度。然后还要把一个商品的详情页面从 php 导成 js 的,我觉得难度很大,因为接口竟然没有文档,只能看着 php 代码来了解。看了两天,有点门路了,开始动手。

1.2 js

nodejs使用的是 express 框架,框架不是我选的,公司有架构师选择,只能听命。最主要的是 express 确实不难。

1.3 再次申明

接手这一个项目我对这个项目的业务内容知识为零,同志们想想怎么把php导到js.

2 核心:php是同步的,js是异步

这个工作中,最有意思的是,php 是同步的,js 是异步的,把一大把同步的 php 代码导成异步的 js,这不就是证明 php 是不是最好的语言的战场吗?

各位,你们觉得这个事该怎么做快?

2.1 思路

当时,我就想到找个人好好给我讲讲这个项目的结构阿,字段啊,什么的?发现这样不行,为什么呢?

  1. 没有文档,包括接口
  2. 别人都很忙,没有时间给你讲
  3. 给自己的时间也不多(要在一个星期内搞定)
  4. 给你讲了一大堆业务,你也记不住

于是,我就直接上代码了,决定先写代码,业务知识什么以后再补。于是这场战役就是不关业务,较纯的关于代码的比较了。

2.2 如何做-代码

导代码,其实难度不大,就语言而言,php 和 js 相差不大,php 中也就array难以理解,因为能代表两种数据结构,返回值的时候就要好好看看,到底是返回 [] 还是 {},就这个犯了不少错,其它的就一般般了。

重点是 php 中对 业务API的访问是同步的,那叫一个简单,调用一个函数就可以了。而在 js 中就有一个大问题,js是异步的,你必须将使用结果写在回调中。一个回调还是没有问题,问题是一个函数中有多异步调用,这我还能忍。最后面一个问题直接击垮了我,因为异步,打断了写代码结构层次,而我又不懂业务,接不上了业务逻辑,这就要我了解业务,可惜基于上面的原因不行啊。

有人会说 promise 能解决你的问题,promise 顺序,但是怎么访问变量啊,几十个变量的修改,你是不是都要放在then中,还有是放在全局变量中,关键是then也破坏了整体代码结构,一眼看过去全是then,最后我也不得不放弃这个方案。

一直说async/await解决了回调的问题,自己也试过,挺好的。可是项目不让用啊。

直到我发现了co,随后又发现 bludbird 的 Promise.coroutine,才解决了我的一些问题,因为我就需要直译啊,就是照着php翻译到js过去。对比一下代码:

2.3 php业务代码

public static function getProductInfo($productId, $goodsId, $uid, $vipLevel = 0, $productSkn = null)
{
$goodsInfo = array();
$statGoodsInfo = array();
$banner = array();
$baseInfo = ItemData::baseInfo($productId, $uid, $productSkn);//异步
if(empty($baseInfo['productName']) && empty($baseInfo['erpProductId']) && empty($baseInfo['productPriceBo'])) {
return array();
}
$baseInfo['uid'] = $uid;
$productId = $baseInfo['id'];
$goodsInfo['name'] = $baseInfo['productName'];
$goodsInfo['skn'] = $baseInfo['erpProductId'];
$goodsInfo['productId'] = $productId;
$goodsInfo['maxSortId'] = $baseInfo['maxSortId'];
$goodsInfo['smallSortId'] = $baseInfo['smallSortId'];
$goodsInfo['promotionId'] = $baseInfo['isPromotion'];
$goodsInfo['goCartUrl'] = Helpers::url('/shopping/cart');
$brandId = 0;
//设置并发请求数据
self::setMultiResourceByProductBaseInfo($baseInfo);//异步
if(isset($baseInfo['brand']['id'])) {
$brandId = $baseInfo['brand']['id'];
}
//收藏喜欢
$favoriteData = self::getProductFavoriteData($uid, $productId, $brandId);//异步 // 商品标签
$goodsInfo['tags'] = self::getTagsDataByProductInfo($baseInfo);//异步 // 商品促销短语
if (!empty($baseInfo['salesPhrase'])) {
$goodsInfo['saleTip'] = $baseInfo['salesPhrase'];
} // 商品价格
if (isset($baseInfo['productPriceBo'])) {
$goodsInfo['marketPrice'] = $baseInfo['productPriceBo']['formatMarketPrice'];
$goodsInfo['hasOtherPrice'] = true;//非市场价格
$goodsInfo['salePrice'] = $baseInfo['productPriceBo']['formatSalesPrice'];
if($goodsInfo['marketPrice'] == $goodsInfo['salePrice']) {//价格相同,只显示市场价格
unset($goodsInfo['salePrice']);
$goodsInfo['hasOtherPrice'] = false;
}
} //VIP数据
$goodsInfo['vipPrice'] = self::getVipDataByProductBaseInfo($baseInfo, $vipLevel, $uid);//异步
//促销活动banner
$goodsInfo['activity'] = self::getActivityDataByProductBaseInfo($baseInfo);//异步 if (isset($baseInfo['productPriceBo']['yohoCoinNum']) && $baseInfo['productPriceBo']['yohoCoinNum'] !== 0) {
array_push($goodsInfo['activity'],
array('type' => '返YOHO币', 'des' => '每件返 ' . $baseInfo['productPriceBo']['yohoCoinNum'] . '个 YOHO币')
);
} // 上市期
if (isset($baseInfo['expectArrivalTime']) && !empty($baseInfo['expectArrivalTime'])) {
$goodsInfo['arrivalDate'] = $baseInfo['expectArrivalTime'] . '月';
$goodsInfo['presalePrice'] = $baseInfo['productPriceBo']['formatSalesPrice'];
unset($goodsInfo['salePrice']);
$goodsInfo['hasOtherPrice'] = false;
} //商品咨询和评论数据
$consultComment = self::getConsultCommentDataByProductInfo($baseInfo);//异步
// 品牌信息
if (!empty($baseInfo['brand'])) {
$goodsInfo['brandImg'] = Helpers::getImageUrl($baseInfo['brand']['brandIco'], 47, 47);
$goodsInfo['brandName'] = $baseInfo['brand']['brandName'];
$goodsInfo['brandUrl'] = Helpers::url('', array(), $baseInfo['brand']['brandDomain']);
$banner = self::getBrandDataByProductBaseInfo($baseInfo);//异步
if(isset($banner['isCollect']) && isset($favoriteData['brand'])) {
$banner['isCollect'] = $favoriteData['brand'];
}
} //sku商品信息
$skuData = self::getSkuDataByProductBaseInfo($baseInfo);//异步
$goodsInfo['img'] = $skuData['defaultImage'];
$goodsInfo['colors'] = $skuData['skuGoods'];
$totalStorageNum = $skuData['totalStorageNum']; // 是否收藏
$goodsInfo['isCollect'] = $favoriteData['product'];
// 限购商品
if ($baseInfo['isLimitBuy'] === 'Y') {
// 是否开售
$isBeginSale = (isset($baseInfo['saleStatus']) && $baseInfo['saleStatus'] == 1);
// 限购商品有关的展示状态
$showStatus = 1;
if (isset($baseInfo['showStatus'])) {
$showStatus = intval($baseInfo['showStatus']);
} $fashTopGoods = self::getFashionTopGoodsStatus($uid, $showStatus, $isBeginSale);//异步
//潮流尖货状态
$goodsInfo['fashionTopGoods'] = array(
'getLimitedCode' => $fashTopGoods['getLimitedCode'],//限购码状态
'hasLimitedCode' => $fashTopGoods['hasLimitedCode'],//是否已经获取限购码
'limitedCodeSoldOut'=> $fashTopGoods['limitedCodeSoldOut'],//限购码是否已经抢光
'getLimitedCodeDis' => $fashTopGoods['getLimitedCodeDis'],//限购码是否失效
);
if($fashTopGoods['soldOut']) {
$goodsInfo['soldOut'] = $fashTopGoods['soldOut'];
$totalStorageNum = 0;//改总数为已售磬
} else {
$goodsInfo['openSoon'] = $fashTopGoods['openSoon'];//即将开售
$goodsInfo['dis'] = $fashTopGoods['dis'];//是否失效
$goodsInfo['buyNow'] = $fashTopGoods['buyNow'];//是否立即购买
}
} $soldOut = $baseInfo['status'] == 0 || $totalStorageNum === 0;
$notForSale = $baseInfo['attribute'] == 2;//非卖品
$virtualGoods = $baseInfo['attribute'] == 3;//虚拟商品
if (!$soldOut && !$notForSale && !$virtualGoods) {
$goodsInfo['addToCart'] = true;
//立即购买或者即将开售存在
if((isset($goodsInfo['buyNow']) && $goodsInfo['buyNow']) ||
(isset($goodsInfo['openSoon']) && $goodsInfo['openSoon'])) {
unset($goodsInfo['addToCart']);
}
}// 非卖品
elseif ($notForSale) {
$goodsInfo['notForSale'] = true;
}
// 已售磬
elseif ($soldOut) {
$goodsInfo['soldOut'] = true;
unset($goodsInfo['fashionTopGoods']);
}
//虚拟商品
else if($virtualGoods) {
$goodsInfo['buyNow'] = true;//是否立即购买
$goodsInfo['buyNowBase'] = Helpers::url('/ticket', array(), 'shopping');
$goodsInfo['virtualGoods'] = $virtualGoods;
if(isset($goodsInfo['salePrice'])) {
$goodsInfo['advancePrice'] = $goodsInfo['salePrice'];//先行价格
unset($goodsInfo['salePrice']);
}
}
//去掉即将售罄
if(empty($totalStorageNum) || $soldOut) {
if(isset($goodsInfo['tags']['isFew'])) {
unset($goodsInfo['tags']['isFew']);//去掉即将售罄
}
}
//分享相关
$goodsInfo['weixinUrl'] = Helpers::url($_SERVER['REQUEST_URI'], array(),'item');
$goodsInfo['sharedTitle'] = $goodsInfo['name'];
$goodsInfo['shareImg'] = $goodsInfo['img'];
$goodsInfo['shareDesc'] = $baseInfo['phrase']; //统计需要的商品信息
$statGoodsInfo['uid'] = $uid;
$statGoodsInfo['skn'] = $baseInfo['erpProductId'];
$statGoodsInfo['productId'] = $productId;
$statGoodsInfo['productName'] = str_replace("'", "’",$goodsInfo['name']);
$statGoodsInfo['brandName'] = empty($goodsInfo['brandName'])? '' : str_replace("'", "’", $goodsInfo['brandName']);
$statGoodsInfo['marketPrice'] = str_replace('¥', '', $goodsInfo['marketPrice']);
if(isset($goodsInfo['salePrice'])) {
$statGoodsInfo['salePrice'] = str_replace('¥', '', $goodsInfo['salePrice']);
} else {
$statGoodsInfo['salePrice'] = str_replace('¥', '', $goodsInfo['marketPrice']);
} if (!empty($banner['brandId'])) {
$domainBrand = BrandsModel::getBrandByDomain($banner['brandDomain']);
if (!empty($domainBrand['type']) && !empty($domainBrand['shopId'])) {
switch (intval($domainBrand['type'])) {
case 1:
//多品店不显示
$banner = array();
break;
case 2:
//单品店显示新版的店铺banner
$basisData = ShopModel::basisTemplate($domainBrand['shopId']);
$banner['bgImg'] = empty($basisData['shopTopBanner']['banner']) ?
$banner['bgImg'] : $basisData['shopTopBanner']['banner'];
break;
}
}
} $statGoodsInfo['imageUrl'] = $goodsInfo['img'];
$statGoodsInfo['productUrl'] = $goodsInfo['weixinUrl'];
$statGoodsInfo['smallSortId'] = $goodsInfo['smallSortId'];
$statGoodsInfo['soldOut'] = intval($soldOut);
return array('goodsInfo'=> $goodsInfo,'consultComment' => $consultComment, 'banner'=> $banner,'statGoodsInfo' => $statGoodsInfo);
}

标异步的地方在函数中有十几处,你怎样解决呢?看懂业务后重构?

2.4 导成 javascript 的业务代码

这是js代码,使用 yield 解决问题

const detailDataPkg = (origin, uid, vipLevel, ua) => {

    return co(function*() {
let result = {}; // 结果输出 // 商品名称
if (!origin.productName) {
return result;
} origin.uid = uid;
result.name = origin.productName;
result.skn = origin.erpProductId;
result.productId = origin.id;
result.maxSortId = origin.maxSortId;
result.smallSortId = origin.smallSortId;
result.promotionId = origin.isPromotion;
result.goCartUrl = helpers.urlFormat('/shopping/cart'); // 接口处理数据,设置并发请求数据
yield setMultiResourceByProductBaseInfo(origin); let brandId = 0; if (origin.brand.id) {
brandId = origin.brand.id;
} // 处理收藏喜欢数据
let favoriteData = yield getProductFavoriteData(uid, result.productId, brandId); // 商品标签
result.tags = getTagsDataByProductInfo(origin); // 商品促销短语
if (origin.salesPhase) {
result.saleTip = origin.salesPhrase;
} // 商品价格
if (origin.productPriceBo) {
result.marketPrice = origin.productPriceBo.formatMarketPrice;
result.hasOtherPrice = true;
result.salePrice = origin.productPriceBo.formatSalesPrice;
if (result.marketPrice === result.salePrice) {
delete result.salePrice;
result.hasOtherPrice = false;
}
} // VIP数据
result.vipPrice = getVipDataByProductBaseInfo(origin, vipLevel, uid); // 促销活动banner
result.activity = yield getActivityDataByProductBaseInfo(origin); const C_VALUE = {
type: '返YOHO币',
des: '每件返 ',
rest: '个 YOHO币'
}; if (origin.productPriceBo.yohoCoinNum && origin.productPriceBo.yohoCoinNum !== 0) {
result.activity.push({
type: C_VALUE.type,
des: `${C_VALUE.des}${origin.productPriceBo.yohoCoinNum}${C_VALUE.rest}`
});
} // 上市期
if (origin.expectArrivalTime) {
result.arrivalDate = `${origin.expectArrivalTime}月`;
result.presalePrice = origin.productPriceBo.formatSalesPrice;
delete result.salePrice;
result.hasOtherPrice = false;
} // 商品咨询和评论数据,当前为空
let consultComment = getConsultCommentDataByProductInfo(origin); // 品牌信息
let banner = null; if (origin.brand) {
result.brandImg = helpers.image(origin.brand.brandIco, 47, 47);
result.brandName = origin.brand.brandName;
result.brandUrl = helpers.urlFormat('', {}, origin.brand.brandDomain);
banner = yield getBrandDataByProductBaseInfo(origin);
if (banner.isCollect && favoriteData.brand) {
banner.isCollect = favoriteData.brand;
}
} // sku商品信息
let skuData = getSkuDataByProductBaseInfo(origin); result.img = skuData.defaultImage;
result.colors = skuData.skuGoods;
let totalStorageNum = skuData.totalStorageNum; // 是否收藏
result.isCollect = favoriteData.product; if (origin.isLimitBuy === 'y') {
// 是否开售
let isBeginSale = !!(origin.saleStatus && origin.saleStatus === 1); // 限购商品有关的展示状态
let showStatus = 1; if (origin.showStatus) {
showStatus = parseInt(origin.showStatus);
} let fashTopGoods = getFashionTopGoodsStatus(uid, showStatus, isBeginSale); result.fashTopGoods = {
getLimitedCode: fashTopGoods.getLimitedCode, // 限购码状态
hasLimitedCode: fashTopGoods.hasLimitedCode, // 是否已经获取限购码
limitedCodeSoldOut: fashTopGoods.limitedCodeSoldOut, // 限购码是否已经抢光
getLimitedCodeDis: fashTopGoods.getLimitedCodeDis // 限购码是否失效
}; if (fashTopGoods.soldOut) {
result.soldOut = fashTopGoods.soldOut;
totalStorageNum = 0; // 改总数为已售磬
} else {
result.openSoon = fashTopGoods.openSoon; // 即将开售
result.dis = fashTopGoods.dis; // 是否失效
result.buyNow = fashTopGoods.buyNow; // 是否立即购买
}
} let soldOut = !!(origin.status === 0 || totalStorageNum === 0); let notForSale = origin.attribute === 2; // 非卖品 let virtualGoods = origin.attribute === 3; // 虚拟商品 if (!soldOut && !notForSale && !virtualGoods) {
result.addToCart = true; // 立即购买或者即将开售存在
if (result.buyNow || result.openSoon) {
delete result.addToCart;
}
} else if (notForSale) {
// 非卖品
result.notForSale = true;
} else if (soldOut) {
// 已售磬
result.soldOut = true;
delete result.fashTopGoods;
} else if (virtualGoods) {
// 虚拟商品
result.buyNow = true; // 是否立即购买
result.buyNowBase = helpers.urlFormat('/ticket', {}, 'shopping');
result.virtualGoods = virtualGoods;
if (result.salePrice) {
result.advancePrice = result.salePrice; // 先行价格
delete result.salePrice;
}
} // 去掉即将售罄
if (totalStorageNum || soldOut) {
if (result.tags.isFew) {
delete result.tags.isFew; // 去掉即将售罄
}
} // 分享相关,产品的链接
result.weixinUrl = helpers.urlFormat(origin.productUrl, {}, 'item');
result.shareTitle = result.name;
result.shareImg = result.img;
result.shareDesc = result.phrase; // 统计需要的商品信息
let statGoodsInfo = {}; statGoodsInfo.uid = uid;
statGoodsInfo.skn = origin.erpProductId;
statGoodsInfo.productId = origin.id;
statGoodsInfo.productName = result.name.replace('\'', '’');
statGoodsInfo.brandName = (result.brandName || '').replace('\'', '’');
statGoodsInfo.marketPrice = result.marketPrice.replace('¥', '');
if (result.salePrice) {
statGoodsInfo.salePrice = result.salePrice.replace('¥', '');
} else {
statGoodsInfo.salePrice = result.marketPrice.replace('¥', '');
} if (banner.brandId) {
let domainBrand = yield BrandData.getBrandByDomainAsync(banner.brandDomain); if (domainBrand.type && domainBrand.shopId) {
switch (parseInt(domainBrand.type)) {
case 1:
{
// 多品店不显示
banner = [];
break;
}
case 2:
{
// TODO:单品店显示新版的店铺banner,item.php 210
let basisData = ShopModel.basisTemplate(domainBrand.shopId); banner.bgImg = basisData.shopTopBanner.banner || banner.bgImg;
break;
} }
}
} statGoodsInfo.imageUrl = result.img;
statGoodsInfo.productUrl = result.weixinUrl;
statGoodsInfo.smallSortId = result.smallSortId;
statGoodsInfo.soldOut = parseInt(soldOut); return {
goodsInfo: result,
consultComment: consultComment,
banner: banner,
statGoodsInfo: statGoodsInfo
};
})();
};

大家可以看到,使用 yield 之后,代码几乎就是跟php完全一样,我在写的过程中,也了解到了业务,同时完成了任务,就是这样。

3 结论

语言之争没有必要,重要的怎样解决问题的。通过使用 yield 使我较快的完成这项任务,节省了大量的时间,而我也终于知道了为什么 yield 如此的有用,为什么大家想方设法的去解决回调问题,因为在没有达到一定量级时,你确实没有感觉到回调的痛苦。多了回调确实很麻烦。

实际情况来看,还是yield很爽的更多相关文章

  1. 很多事情就像看A片,看的人觉得很爽,做的人未必。

    http://m.jingdianju.com/wzgs/shenghuo/201307185135.html 转载自: 从这个角度上来说,我不太赞成过于关注第一份工作的薪水,更没有必要攀比第一份工作 ...

  2. 常用的sublime text插件(很爽哦)

    个人比较懒,平时喜欢用webstorm,但是因为webstorm打开实在太慢了,并且太看设备,所以本人编辑简单的文件依然会选择使用sublime,虽然网上有很多关于此类插件的分享了,但是感觉都是片段, ...

  3. 常用的sublime text 3插件(很爽哦)

    个人比较懒,平时喜欢用webstorm,但是因为webstorm打开实在太慢了,并且太看设备,所以本人编辑简单的文件依然会选择使用sublime,虽然网上有很多关于此类插件的分享了,但是感觉都是片段, ...

  4. 用Python写了一个postgresql函数,感觉很爽

    用Python写了一个postgresql函数,感觉很爽 CREATE LANGUAGE plpythonu; postgresql函数 CREATE OR REPLACE FUNCTION myfu ...

  5. WPF制作Logo,很爽,今后在应用程序中加入Logo轻松,省事!

    原文:WPF制作Logo,很爽,今后在应用程序中加入Logo轻松,省事! 这是效果: XAML代码:<Viewbox Width="723.955078" Height=&q ...

  6. 宝马男砍人不慎刀落反被杀 防卫过当or故意伤害(在生命受到威胁的情况下,已经很难判断对方意图了,而且假如于莫是老弱妇幼,可能现在死的就是于莫了)

    如果被砍的是周律师他就不会说是防为过当吧,宝马车主跑回自己的车边时最危险,不知道他车上还有什么刀枪之类的.这如果判防卫过当,恶人会更恶,老实人连防卫都不敢了. 不知道在这个没有法治的国家会是如何判案的 ...

  7. Java 处理表格,真的很爽!

    一个简单又快速的表格处理库 大家好,我是鱼皮. 处理 Excel 表格是开发中经常遇到的需求,比如表格合并.筛选表格中的某些行列.修改单元格数据等. 今天给大家分享一个 Java 处理表格的工具库,不 ...

  8. 你可以使用 play framework 做5件很爽的事情http://www.anool.net/?p=629

    1.绑定HTTP参数到JAVA方法里的参数. 使用PLAY可以很简单的从JAVA代码中检索HTTP参数.只要把方法参数申明成和HTTP参数相同既可. 比如,这个request: Http代码 /art ...

  9. 介绍一个很爽的 php 字符串特定检索函数---strpos()

    大家在用 php 开发的时候 是否 有遇到过,对于一个获取的字符串,如果想要特定检测它是否 含有某个特定的字符或者子字符串,总是找不到好方法,或者根本做不到,迫于无奈而使用foreach. 函数: s ...

随机推荐

  1. vs2015编译mysql c++ connector

    目前MySQL Connector/C++的binary版本最高只支持VS2008,VS2015需要下载源码自行编译. 1.CMAKE 到官网下载最新的稳定版本 把bin目录添加到环境变量PATH中 ...

  2. linux mint运行docker

    1,sudo apt-get install docker.io 或者sudo apt-get install docker* 2,安装好之后 sudo docker -d 启动进程提示: yimiy ...

  3. Oracle 11g RAC使用Manual和Policy Managed方法配置(转)

    原文地址:http://czmmiao.iteye.com/blog/2128771 软件环境: 操作系统:Red Hat Enterprise Linux 5.4(Tikanga)Oracle:11 ...

  4. 数据库设计的误区—>CHAR与VARCHAR

    字符型字段是数据库表中最常见的字段,而字符型字段又分为定长和变长两种.一般来说,VARCHAR类型用于存储内容长度变化较大的数据,CHAR类型用于存储内容长度没有变化或变化不大的数据. 在数据的内部存 ...

  5. FP Tree算法原理总结

    在Apriori算法原理总结中,我们对Apriori算法的原理做了总结.作为一个挖掘频繁项集的算法,Apriori算法需要多次扫描数据,I/O是很大的瓶颈.为了解决这个问题,FP Tree算法(也称F ...

  6. 同步、异步、阻塞、非阻塞IO

    在网上看到一篇对这四个概念比较清晰的分析的文章:http://blog.csdn.net/historyasamirror/article/details/5778378.结合自己一直在学习Java ...

  7. 安装Ubuntu时的硬盘分区

    根目录 大小:60G~100G(用来安装程序) 新分区的类型:主分区 新分区的位置:空间起始位置 用于:EXT4日志文件系统 挂载点:"/" 大小:4G 新分区的类型:逻辑分区 新 ...

  8. log4jdbc打印完整SQL

    一.log4jdbc简单介绍: log4jdbc是工作在jdbc层的一个日志框架,能够记录SQL及数据库连接执行信息. 一般的SQL日志会把占位符和参数值分开打印,log4jdbc则会记录数据库执行的 ...

  9. java版二叉树算法实现

    import java.util.ArrayList; class BinaryTree { private static class TreeNode { int data; TreeNode le ...

  10. .net 网站应对压力的一些方案总结

    开年比较空,抽时间写个博文,总结下自己工作里的一些应对网站访问压力的技术方案. 自己项目现在大概一天50W的pv.已从前端到后端的顺序总结下自己用的一些方案. 一. 前端页面: 1.首先减少资源的大小 ...