Authored by 陈峰

商品列表和详情页优化

@@ -98,12 +98,14 @@ try { @@ -98,12 +98,14 @@ try {
98 const setYohoData = require('./doraemon/middleware/set-yoho-data'); 98 const setYohoData = require('./doraemon/middleware/set-yoho-data');
99 const errorHanlder = require('./doraemon/middleware/error-handler'); 99 const errorHanlder = require('./doraemon/middleware/error-handler');
100 const setPageInfo = require('./doraemon/middleware/set-pageinfo'); 100 const setPageInfo = require('./doraemon/middleware/set-pageinfo');
  101 + const layoutTools = require('./doraemon/middleware/layout-tools');
101 102
102 103
103 // YOHO 前置中间件 104 // YOHO 前置中间件
104 app.use(setYohoData()); 105 app.use(setYohoData());
105 app.use(user()); 106 app.use(user());
106 app.use(setPageInfo()); 107 app.use(setPageInfo());
  108 + app.use(layoutTools());
107 109
108 require('./dispatch')(app); 110 require('./dispatch')(app);
109 111
@@ -18,9 +18,9 @@ module.exports = { @@ -18,9 +18,9 @@ module.exports = {
18 app_type: 1 18 app_type: 1
19 }, 19 },
20 domains: { 20 domains: {
21 - api: 'http://api-test3.yohops.com:9999/',  
22 - service: 'http://service-test3.yohops.com:9999/',  
23 - singleApi: 'http://api-test3.yohops.com:9999/' 21 + // api: 'http://api-test3.yohops.com:9999/',
  22 + // service: 'http://service-test3.yohops.com:9999/',
  23 + // singleApi: 'http://api-test3.yohops.com:9999/'
24 24
25 // api: 'http://api-test2.yohops.com:9999/', 25 // api: 'http://api-test2.yohops.com:9999/',
26 // service: 'http://service-test2.yohops.com:9999/', 26 // service: 'http://service-test2.yohops.com:9999/',
@@ -29,9 +29,9 @@ module.exports = { @@ -29,9 +29,9 @@ module.exports = {
29 // service: 'http://dev-service.yohops.com:9999/', 29 // service: 'http://dev-service.yohops.com:9999/',
30 // singleApi: 'http://192.168.102.27:8092/' 30 // singleApi: 'http://192.168.102.27:8092/'
31 // // 31 // //
32 - // api: 'http://api.yoho.cn/',  
33 - // service: 'http://service.yoho.cn/',  
34 - // singleApi: 'http://single.yoho.cn/' 32 + api: 'http://api.yoho.cn/',
  33 + service: 'http://service.yoho.cn/',
  34 + singleApi: 'http://single.yoho.cn/'
35 }, 35 },
36 subDomains: { 36 subDomains: {
37 host: '.m.yohoblk.com', 37 host: '.m.yohoblk.com',
  1 +// 在这个文件中配置 DNS 预读域名
  2 +
  3 +module.exports = {
  4 + hosts: [
  5 + '//cdn.yoho.cn',
  6 + '//static.yohobuy.com',
  7 + '//img10.static.yhbimg.com',
  8 + '//img11.static.yhbimg.com',
  9 + '//img12.static.yhbimg.com',
  10 + '//img13.static.yhbimg.com',
  11 + ]
  12 +};
  13 +
  1 +'use strict';
  2 +
  3 +const dnsPrefetch = require('../../config/dns-prefetch');
  4 +
  5 +module.exports = () => {
  6 + return (req, res, next) => {
  7 + Object.assign(res.locals, {
  8 + dnsPrefetch: dnsPrefetch
  9 + });
  10 +
  11 + next();
  12 + };
  13 +};
@@ -9,10 +9,14 @@ @@ -9,10 +9,14 @@
9 <meta name="apple-mobile-web-app-status-bar-style" content="black"> 9 <meta name="apple-mobile-web-app-status-bar-style" content="black">
10 <meta content="telephone=no" name="format-detection"> 10 <meta content="telephone=no" name="format-detection">
11 <meta content="email=no" name="format-detection"> 11 <meta content="email=no" name="format-detection">
12 - <link rel="dns-prefetch" href="//cdn.yoho.cn">  
13 - <link rel="dns-prefetch" href="//static.yohobuy.com"> 12 + {{#dnsPrefetch.hosts}}
  13 + <link rel="dns-prefetch" href="{{this}}">
  14 + {{/dnsPrefetch.hosts}}
14 <script type="text/javascript"> 15 <script type="text/javascript">
15 (function(d,c){var e=d.documentElement,a="orientationchange" in window?"orientationchange":"resize",b=function(){var f=e.clientWidth;if(!f){return}if(f>=750){e.style.fontSize="40px"}else{e.style.fontSize=40*(f/750)+"px"}};if(!d.addEventListener){return}b();c.addEventListener(a,b,false);d.addEventListener("DOMContentLoaded",b,false)})(document,window); 16 (function(d,c){var e=d.documentElement,a="orientationchange" in window?"orientationchange":"resize",b=function(){var f=e.clientWidth;if(!f){return}if(f>=750){e.style.fontSize="40px"}else{e.style.fontSize=40*(f/750)+"px"}};if(!d.addEventListener){return}b();c.addEventListener(a,b,false);d.addEventListener("DOMContentLoaded",b,false)})(document,window);
  17 + if (/YohoBuy/i.test(navigator.userAgent || '')) {
  18 + window.appBaseLogs = {device: {},status: [],events: []}
  19 + }
16 </script> 20 </script>
17 {{#if devEnv}} 21 {{#if devEnv}}
18 <link rel="stylesheet" media="all" href="//{{devHost}}:5004/index.css"> 22 <link rel="stylesheet" media="all" href="//{{devHost}}:5004/index.css">
@@ -37,6 +37,10 @@ const getImgUrl = function(src, width = 300, height = 300, mode = 2) { @@ -37,6 +37,10 @@ const getImgUrl = function(src, width = 300, height = 300, mode = 2) {
37 }).replace(/https?:/, '') + '/interlace/1' : ''; 37 }).replace(/https?:/, '') + '/interlace/1' : '';
38 }; 38 };
39 39
  40 +const replaceHttp = function(src) {
  41 + return src.replace(/https?:/, '');
  42 +};
  43 +
40 // 退换货 申请 成功, 打开 modal 44 // 退换货 申请 成功, 打开 modal
41 const applySuccuss = function(type, applyId) { 45 const applySuccuss = function(type, applyId) {
42 yoho.store.set('orderDetail', true); 46 yoho.store.set('orderDetail', true);
@@ -115,5 +119,6 @@ export default { @@ -115,5 +119,6 @@ export default {
115 getImgUrl, 119 getImgUrl,
116 applySuccuss, 120 applySuccuss,
117 visibilitychange, 121 visibilitychange,
118 - getChannel 122 + getChannel,
  123 + replaceHttp
119 }; 124 };
@@ -116,15 +116,45 @@ function BlkHrefBinding(el, binding) { @@ -116,15 +116,45 @@ function BlkHrefBinding(el, binding) {
116 el.href = `${protocol}${value}`; 116 el.href = `${protocol}${value}`;
117 } 117 }
118 118
  119 +function lazyHtmlReplace(html, lazy) {
  120 + let renderImgIndex = 5;
  121 +
  122 + return html
  123 + .replace(/<img [^>]*src=['"]([^'"]+)[^>]*>/gi, (imgHtml, src) => {
  124 + if (src.indexOf('?') < 0) {
  125 + src += '?imageMogr2/thumbnail/750x/quality/60/interlace/1/format/webp';
  126 + }
  127 + if (lazy) {
  128 + renderImgIndex--;
  129 + }
  130 + src = util.replaceHttp(src);
  131 + return (lazy && renderImgIndex < 0) ? `<img v-lazy.imgLazyContainer="'${src}'" />` : `<img src="${src}" />`;
  132 + });
  133 +}
  134 +
119 export default (Vue) => { 135 export default (Vue) => {
120 Vue.directive('lazy-html', { 136 Vue.directive('lazy-html', {
121 bind(el, binding) { 137 bind(el, binding) {
122 // TODO 首屏幕不使用 138 // TODO 首屏幕不使用
123 - let html = binding.value  
124 - .replace(/<img [^>]*src=['"]([^'"]+)[^>]*>/gi, '<img src="$1">')  
125 - .replace(/https?:/gi, '');  
126 -  
127 - el.innerHTML = html; 139 + setTimeout(() => {
  140 + let html = lazyHtmlReplace(binding.value, true);
  141 +
  142 + try {
  143 + let res = Vue.compile(`<div>${html}</div>`);
  144 + let component = new Vue({
  145 + render: res.render,
  146 + staticRenderFns: res.staticRenderFns
  147 + });
  148 +
  149 + Vue.nextTick(() => {
  150 + component.$mount(el);
  151 + });
  152 +
  153 + } catch (e) {
  154 + html = lazyHtmlReplace(binding.value);
  155 + el.innerHTML = html;
  156 + }
  157 + }, 500);
128 } 158 }
129 }); 159 });
130 Vue.directive('img-src', { 160 Vue.directive('img-src', {
1 import Vue from 'vue'; 1 import Vue from 'vue';
  2 +import lazyload from 'vue-lazyload';
2 import prefers from 'me/prefers.vue'; 3 import prefers from 'me/prefers.vue';
3 import yoho from 'yoho'; 4 import yoho from 'yoho';
4 5
5 6
  7 +Vue.use(lazyload, {
  8 + preLoad: 3
  9 +});
  10 +
6 yoho.ready(() => { 11 yoho.ready(() => {
7 new Vue({ 12 new Vue({
8 el: '#prefer', 13 el: '#prefer',
@@ -4,7 +4,7 @@ import app from 'product/detail/index.vue'; @@ -4,7 +4,7 @@ import app from 'product/detail/index.vue';
4 import yoho from 'yoho'; 4 import yoho from 'yoho';
5 5
6 Vue.use(lazyload, { 6 Vue.use(lazyload, {
7 - preLoad: 3 7 + preLoad: 2
8 }); 8 });
9 9
10 yoho.ready(() => { 10 yoho.ready(() => {
@@ -5,7 +5,7 @@ @@ -5,7 +5,7 @@
5 <div class="product-item" v-for="item in list" :key="item.url"> 5 <div class="product-item" v-for="item in list" :key="item.url">
6 <a :href="item.url"> 6 <a :href="item.url">
7 <a v-good-href="item"> 7 <a v-good-href="item">
8 - <img v-img-src="{src: item.default_images, width: 250, height: 335}"> 8 + <img-lazy :src="{src: item.default_images, width: 250, height: 335}" :lazy="lazy"></img-lazy>
9 <p class="product-name">{{item.product_name}}</p> 9 <p class="product-name">{{item.product_name}}</p>
10 </a> 10 </a>
11 </a> 11 </a>
@@ -18,9 +18,19 @@ @@ -18,9 +18,19 @@
18 </div> 18 </div>
19 </template> 19 </template>
20 <script> 20 <script>
21 - export default {  
22 - props: ['list', 'title']  
23 - }; 21 +import ImgLazy from 'component/tool/img-lazy.vue';
  22 +
  23 +export default {
  24 + props: {
  25 + list: {},
  26 + title: {},
  27 + lazy: {
  28 + type: Boolean,
  29 + default: false
  30 + }
  31 + },
  32 + components: {ImgLazy}
  33 +};
24 </script> 34 </script>
25 <style> 35 <style>
26 .title-wrap { 36 .title-wrap {
  1 +<template>
  2 + <img v-if="lazy" v-lazy="currentSrc" alt="">
  3 + <img v-else :src="currentSrc" alt="">
  4 +</template>
  5 +
  6 +<script>
  7 +import util from 'common/util';
  8 +
  9 +export default {
  10 + name: 'img-lazy',
  11 + props: {
  12 + src: {
  13 + type: [Object, String]
  14 + },
  15 + lazy: {
  16 + type: Boolean,
  17 + default: true
  18 + }
  19 + },
  20 + computed: {
  21 + currentSrc() {
  22 + if (typeof this.src === 'string') {
  23 + return util.replaceHttp(this.src);
  24 + } else if (typeof this.src === 'object') {
  25 + let {src, width, height, mode} = this.src;
  26 +
  27 + return util.getImgUrl(src, width, height, mode);
  28 + }
  29 + }
  30 + }
  31 +};
  32 +</script>
1 <template> 1 <template>
2 <div> 2 <div>
3 <div> 3 <div>
4 - <div v-if="block.text" class="text-block a" v-lazy-html="block.text.data.text"> 4 + <div class="text-block a">
  5 + <div v-if="block.text" v-lazy-html="block.text.data.text"></div>
5 </div> 6 </div>
6 -  
7 <div v-if="block.singleImage"> 7 <div v-if="block.singleImage">
8 <div v-for="(item, index) in block.singleImage.data" :key="index"> 8 <div v-for="(item, index) in block.singleImage.data" :key="index">
9 <img :title="item.title" 9 <img :title="item.title"
1 <template> 1 <template>
2 - <prefer-list :title="title" :list="list"></prefer-list> 2 + <prefer-list :title="title" :list="list" :lazy="true"></prefer-list>
3 </template> 3 </template>
4 4
5 <script> 5 <script>
@@ -246,7 +246,7 @@ @@ -246,7 +246,7 @@
246 </show-box> 246 </show-box>
247 247
248 <show-box class="prefer-detail" :is-last="preferList.length > 0" v-if="preferList.length > 0"> 248 <show-box class="prefer-detail" :is-last="preferList.length > 0" v-if="preferList.length > 0">
249 - <prefer-list :title="preferTitle" :list="preferList"></prefer-list> 249 + <prefer-list :title="preferTitle" :list="preferList" :lazy="true"></prefer-list>
250 </show-box> 250 </show-box>
251 251
252 <div v-if="isApp && isReady"> 252 <div v-if="isApp && isReady">
@@ -372,7 +372,9 @@ @@ -372,7 +372,9 @@
372 372
373 getMightLike(skn, shopId) { 373 getMightLike(skn, shopId) {
374 $.get('/product/mightLike', {skn, shopId}).then(res => { 374 $.get('/product/mightLike', {skn, shopId}).then(res => {
375 - this.preferList = res.data || []; 375 + setTimeout(() => {
  376 + this.preferList = res.data || [];
  377 + }, 500);
376 }); 378 });
377 }, 379 },
378 380