Giter Site home page Giter Site logo

jade.github.io's People

Contributors

jade05 avatar

Stargazers

 avatar  avatar  avatar

Watchers

 avatar

jade.github.io's Issues

开发一个本地上传图片控件你需要知道的知识点

接了一个「常规」需求:开发一个本地上传图片控件,需要支持三种上传方式:

  1. 支持打开本地目录,选择本地图片上传
  2. 支持拖曳图片上传
  3. 支持微信截图上传

我们先从工程角度来看一下用户上传图片的流程是怎样子的:

  1. 用户选择了一张本地图片,或者拖曳了一张图片,或者通过微信截图了一张图片。这时,我们需要知道用户所选择图片的信息,比如图片的内容、图片的大小、图片的类型等。
  2. 用户上传图片。这时,我们需要保存图片。
  3. 用户查看图片。这时,我们需要把第2步中保存的图片展示给用户。

如何获取到图片信息

通过input 打开本地目录,选择本地图片上传

通过type为file的input标签选择本地图片上传,每次选择不同图片的时候会触发onChange事件。为什么我要强调不同图片,因为当两次选择的图片是一样的情况下,onChange事件无法触发。解决方法:每次处理完后手动置空input的value。

  <input id='img-input' type='file' accept='image/*' onChange={this.bindChooseEvents} />
  // 置空input value
  clearImgInputValue () {
    document.getElementById('img-input').value = ''
  }
  bindChooseEvents = (e) => {
    console.log('choose a image')

    // 获取File对象
    const file = e.target.files[0]
    let size = file.size

    if (!file.type.match('image.*')) {
      AntMessage.warning('File\'s type is not supported. Images only.')
      this.clearImgInputValue()
      return;
    }

    if (size > maxSize) {
      AntMessage.warning('The size of the image is too large')
      this.clearImgInputValue()
      return;
    }

    /* eslint-disable */
    const reader = new FileReader()  // FileReader
    /* eslint-disable */
    // 将图片转换为base64
    reader.readAsDataURL(file)
    reader.onload = (arg) => {
      // 获取到base64图片内容
      const fileStream = arg.target.result
      /**
       * overwrite do something
       * */
      this.clearImgInputValue()
    }
  }

拖曳图片上传

监听拖曳事件,通过拖曳相关的DataTransfer对象获取图片信息。

  <div id='drop-zone' style={{width: '100px', height: '100px'}}>Drop Zone</div>
  bindDragEvents = (e) => {
    const handleDragOver = (event) => {
      event.stopPropagation()
      event.preventDefault()
      event.dataTransfer.dropEffect = 'copy'
    }

    // 必须阻止dragenter和dragover事件的默认行为,这样才能触发 drop 事件
    const handleFileSelect = (event) => {
      event.stopPropagation()
      event.preventDefault()

      const files = event.dataTransfer.files // 文件对象
      const file = files[0]
      const size = file.size
      const type = file.type

      if (!type.match('image.*')) {
        AntMessage.warning('File\'s type is not supported. Images only.')
        return;
      }

      if (size > maxSize) {
        AntMessage.warning('The size of the image is too large')
        return;
      }

      /* eslint-disable */
      const reader = new FileReader()
      /* eslint-disable */
      // 将图片转换为base64
      reader.readAsDataURL(file)
      reader.onload = (arg) => {
        // 获取到base64图片内容
        const fileStream = arg.target.result
        /**
         * overwrite do something
         * */
      }
    }

    const dropZone = document.getElementById('drop-zone');
    dropZone.addEventListener('dragover', handleDragOver, false);
    dropZone.addEventListener('drop', handleFileSelect, false);
  }

微信截图上传

监听paste事件,通过剪贴板对象clipboardData获取图片信息。

  bindClipEvents() {
    document.addEventListener('paste', (e) => {
      console.log('paste a image')
      const clipboard = e.clipboardData

      // 有无内容
      if (!clipboard.items || !clipboard.items.length) {
        AntMessage.warning('No content in the clipboard')
        return;
      }

      let item = clipboard.items[0]
      if (item.kind === 'file' && item.type.match('image.*')) {
          // 获取图片文件
          let imgFile = item.getAsFile()

          if (imgFile.size > maxSize) {
            AntMessage.warning('The size of the image is too large')
            return;
          }

          const reader = new FileReader()
          // 将图片转换为base64
          reader.readAsDataURL(imgFile)
          reader.onload = (arg) => {
            // 获取到base64图片内容
            const fileStream = arg.target.result
            /**
             * overwrite do something
             * */
          }
      } else {
        AntMessage.warning('File\'s type is not supported. Images only.')
      }
    }, false)
  }

如何保存

图片保存一般都采用独立图片独立域名服务器,不会傻乎乎地把图片保存在web服务器上也不会直接存在项目表的数据库中。这样做有什么好处呢?

  1. 图片访问是I/O密集型操作,很消耗服务器资源,从Web服务器独立出来后,能够减少Web服务器压力
  2. 便于扩容、容灾和数据迁移
  3. 浏览器有同域名下的并发策略限制
  4. 请求图片一般并不需要cookie,但是浏览器发起的所有同域名请求时,http头部都会自动带上cookie信息,导致浪费带宽
  5. 方便对图片访问做负载均衡,可以对图片应用各种缓存策略
  6. 方便迁移CDN
    ...

总结&优化

流程图:

image

不足:

项目实践中我们虽然采用了独立图片服务器,下载过程只是通过Web服务器去数据库拿到图片地址,但是我们的上传操作仍旧经过了Web服务器,需要Web服务器上的应用程序来处理,所以上传过程仍旧对Web服务器造成压力。所幸的是,我们对图片上传大小进行了1M的大小限制,同时作为内部系统没啥访问压力,而且图片上传功能也并不是很高发的行为,所以这么做基本也不会有啥问题。但是最好的方案还是不管下载上传都直接走独立图片服务器,避免对Web服务器造成额外的压力。

后续,探究图片服务器架构 & 如何应对大文件的上传 & Blob Buffer Stream三者之间的关系。

本文参考资料:

  1. 理解DOMString、Document、FormData、Blob、File、ArrayBuffer数据类型
  2. 复制粘贴的高级玩法
  3. 使用FileReader.readAsArrayBuffer()在浏览器中处理大文件
  4. 大型网站图片服务器架构的演进

探索热更新对代码打包结果的影响(一)

简介:本项目以express搭建服务端,本地开发配合webpack-dev-middlerware、wepack-hot-middleware、webpack.HotModuleReplacementPlugin实现热加载刷新。生产打包在dist目录下,开发打包热加载替换目录为/examples。

问题发现

问题描述

现象:本地代码通过生产配置文件(webpack-build.config.js)打包生成的js包能够被页面引用到并且入口正常,通过开发配置文件(webpack-develop.config.js)打包生成的js包虽然也能够被页面引用到但是入口不正常。

具体的例子:
入口文件 - examples/test/index.js

const Test = {
  text: 'hello world!'
}

module.exports = Test

html - example/test/index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>test</title>
</head>
<body>
    Test
    <!-- <script src="../../dist/components/test.js"></script> -->
    <script src="/examples/test.js"></script>
    <script>
      console.log(test)
      console.log(test.text)
    </script>
</body>
</html>

webpack-develop.config.js:

entry: {
    test: [path.resolve(__dirname, 'examples/test/index.js'), 'webpack-hot-middleware/client?path=/examples/__what&reload=true'],
  },
  output: {
    filename: '[name].js',
    publicPath: '/examples/',
    library: '[name]',
    libraryTarget: 'umd',
  },

本地起服务,在浏览器中打开/examples/test/index.html,按照预期结果,控制台应该输出{text: "hello world!"} hello world!。但是控制台却输出了如下结果:
image

第一反应是:js文件没有加载到。但是通过浏览器面板查看后可以看到test.js已经被正确加载到,而且通过http://localhost:3000/examples/test.js可以看到,examples/test/index.js也已经打包进来:
02

问题分析

懵,就是取不到,明明就在那里-.-。更神奇的是,如果我不用webpack-develop.config.js打包,而是用webpack-build.config.js直接打包到物理磁盘(注:因本地开发是基于webpack-dev-middleware和hot-middleware,开发打包的结果并不存在于物理磁盘,只存在于内存中),然后更换examples/test/index.html的引用入路径为 ‘../../dist/components/test.js’,即生产打包后的物理文件路径,就一切正常,不会出现加载但是无法引用的问题。

webpack-develop.config.js和webpack-build.config.js有啥区别?区别还是有的,但是其他入口的页面(注:本项目可理解为是多入口页面)也是基于这两个配置文件的,本地开发和生产打包后皆正常。仔细排查,会发现可以通过以下两个角度解决问题:

  1. webpack-develop.config.js和webpack-build.config.js是有区别的,build配置里头使用ExtractTextPlugin、UglifyJSPlugin,而develop没有使用但有热加载插件;
  2. 其它入口的页面都是正常的,唯独examples/test/index.js这种情况有问题,比较后会发现引入的使用方式不同,其他入口文件都是引用了模块文件后自执行代码片段输出dom,而examples/test/index.js作为打包入口最后通过module.exports = Test,输出Test,然后example/test/index.html通过 script 标签引入后直接去取全局变量里头的Test引用,取引用失败。而事实上我们知道,通过umd打包后,如果页面通过script标签直接引入,入口文件module.exports输出对象实际就是挂在window上的。(图片里头的this在浏览器全局环境中就是window)
    03

从第1个入口处进行排查,除去热加载功能后,就一切正常了。结合以上两点,我们有理由猜测导致上述问题的原因很可能是热加载过程中导致入口文件module.exports输出的对象无法被正确挂载到window上。而至于真实情况是就是没有挂载还是挂载后被冲掉(复写)了,则需要分析源码才能知道。

问题解决

去掉热更新,本地开发一切正常。

后记:直接去掉实在太残暴,而且热更新作为提高开发效率的利器就因为这一问题就直接去掉实在可惜,所以为了不影响其他入口的热更新,本地开发模式可以只对指定入口禁用热更新,webpack-develop.config.js 在指定entry去掉 'webpack-hot-middleware/client?path=/examples/__what&reload=true'即可。

深入探讨

我们需要从webpack热更新的原理说起

AMP项目实战分享

目录

  1. AMP是什么
  2. 我们为什么选择AMP
  3. AMP项目开发
  4. 我们踩过的"坑"儿
  5. 如何优化AMP SEO页面
  6. 谈谈AMP的局限性
  7. eamp框架,让开发AMP项目更简单

AMP是什么

Google 前沿的 AMP 「 Accelerated Mobile Pages 」技术,能使用户从搜索引擎当中进入我们页面的体验得到一个极大的提升。确切地说,AMP并不是一门新的技术,它只是一种能让页面打开得更快的解决方案。你只要会HTML、CSS、略懂JS,就可以开发你自己的AMP页面。

有关AMP更多内容可以参看官网

我们为什么选择AMP

看看AMP能给我们带来什么:

  1. AMP能够带来SEO排名优化。
  2. AMP Cache能够让我们充分借助Google CDN Cache的优势。虽然我们内部已经做了很多优化,包括DNS预热(有关DNS预热可参见我们组同事写的一篇文章——网站性能优化——DNS预热与合并HTTP请求),但如果能有Google全球CDN支持就更是件好事。
  3. Google搜索结果对AMP页面有预加载处理,能让用户更快地到达我们的着陆页。

所以,作为携程的海外业务部门,我们率先试水了AMP项目。

AMP项目开发

AMP项目开发和普通站点的开发模式几乎一样,但是为了最大限度提升性能,AMP项目页面有不少要求。比如:

  1. 为了避免 JavaScript 延缓页面渲染,AMP页面不能包含自己编写的JavaScript。
  2. CSS都必须内联,以减少HTTP的请求,并且有50KB的大小限制。
  3. AMP 页面允许第三方 JavaScript,但仅限在沙盒环境下的 iframe 中。
  4. 用户几乎可以使用所有原生的HTML标签,但是对img等会产生外部资源依赖的标签,只能使用amp-img自定义标签。

基于以上几点,页面上所有交互逻辑都必须通过css实现,无法依赖JS。对于实现复杂的交互,AMP会显得力不从心。但是这其实是和AMP原则相一致的,JS丰富了页面,但JS也是页面优化噩梦的开始。What Google AMP means for the JavaScript community这篇文章将阐述JavaScript对性能的影响。

当开发完成后,必须保证页面是符合AMP规范的,只有符合AMP规范的页面才会被搜索引擎收录。在Chrome中安装AMP Validator插件,当你的页面是完全符合AMP规范的时候,Chrome Validator AMP按钮会呈现绿色。如下图:
image

当页面通过验证以后,我们还需要在link中配置amphtml和canonical,让Google搜索发现我们的页面。

如果同一个页面同时具有非AMP版本和AMP版本:

为非AMP页面添加以下标记:

<link rel="amphtml" href="https://www.example.com/url/to/amp/document.html">

为AMP页面添加以下标记:

<link rel="canonical" href="https://www.example.com/url/to/full/document.html">

只有一个版本的网页:

如果只有一个版本的网页,并且该版本是 AMP 网页,则仍要为其添加规范链接,该链接会指向其自身:

<link rel="canonical" href="https://www.example.com/url/to/amp/document.html">

至此,AMP页面开发工作基本完成,可以发布了。

我们踩过的"坑"儿

AMP有不少限制要求,开发中难免碰到不好解决的问题。以下对我们碰到过的问题及解决方法进行分享。

  1. AMP对表达式复杂度有限制要求,最大值是50。复杂度50是什么概念呢,比如以下例子,只有纯粹的字符串连接,但是以下复杂度已经接近是46,如果再增加一个连接,会达到53的复杂度,控制台会直接报错:
<a href='<%- searchData.defaultSearchUrl %>'
  [href]='"/m/hotels/" + location.concatResult
    + "/?checkin=" + date.selectedStartDate
    + "&checkout=" + date.selectedEndDate
    + "&starID=" + activeStarKeys.keys
    + "&adult=" + guestsSelectResult.adultsNum
    + "&children=" + guestsSelectResult.childNum
    + "&ages=" + guestsSelectResult.childAges'>

image

对于此问题,除非AMP放开复杂度限制,否则我们能做的只能是尽量提前运算,当需要某个计算结果的时候,可以直接使用。比如例子中,location.concatResult就是在location组件内部先进行concat,再将concat的结果拼接到href。此外,可以尽量简化交互,减少参数。

此问题在AMP开发中势必会碰到,详细讨论可以看ISSUES-11434

  1. AMP限制编写JavaScript,也就不允许我们读写Cookie、LocalStorage,但是记录用户行为是很重要的事情,比如一些表单信息,用户的选择,等用户下次打开我们的页面时,能够显示用户上一次的状态。我们的解决方法是通过http set-cookie方式解决前端无法记录cookie的问题。

  2. amp-form只能提交ajax post请求,无法做到以post表单形式跳转。所以在开发过程中尽量避免出现post表单形式的请求,一般改用ajax post加页面跳转的形式来提交。在迫不得已的情况下,可以考虑通过增加非amp的中间路由,在中间页中构造表单并自动提交数据。

  3. AMP CROS:用户最终访问的是AMP Cache,在AMP launch新版本之前,命中AMP Cache,页面地址并非是真实地址而是Google AMP Cache地址,如果页面上有额外的异步请求,就会有跨域限制,所以我们要在服务端开启跨域,返回头设置AMP-Access-Control-Allow-Source-Origin。

  4. amp-iframe有sandbox属性,用来指定iframe内部的站点权限,默认值为空。如果希望iframe内部可以执行js脚本,则需要设置成"allow-scripts";如果需要添加内部发送同域请求的权限,则需要设置成"allow-scripts allow-same-origin";但如果amp-iframe的src是同域站点,那么sandbox属性必须不能包含allow-same-origin,这样做杜绝了脱离amp控件发送请求的可能性。

  5. AMP下统计页面埋点必须基于amp自带的统计控件,目前amp封装了市面上大部分的第三方统计系统。但是由于公司内部的统计工具没有amp对应的控件,所以无法接入。对于页面埋点统计,开发者要评估自家的统计工具。

如何优化AMP页面

最大化提速

通过对非SEO数据异步化加载,让AMP更快。

amp-install-serviceworker,让你打开的不仅仅是一个amp页面

AMP页面作为搜索排名优化页面的同时,还兼具引流功能。虽然AMP页面能让用户从搜索结果中最快速地到达我们的landing页,但是只有用户最终从landing页又回到原始页面走完必要的业务流程,才是有效的转化。例如,在我们的业务中,用户可以通过搜索引擎快速到达酒店列表、酒店详情的AMP页面,但只有从酒店详情AMP页面跳转到支付页(非AMP),并完成支付,才算转化成功。如果原始页面体验不好,用户依旧可能中途流失。这似乎不是AMP的"错",但AMP确实还能再做些什么。

image

通过amp-install-serviceworker安装原始站点的sw.js,提前加载好原始页面的资源,当用户从AMP页面跳出,进入原始主站的时候,让主站体验更好,从而提高实际转化。

谈谈AMP的局限性

实践后,我们发现AMP有以下局限性,希望对于打算了解AMP这一技术,或者有计划接入AMP技术的团队,可以起到借鉴作用。

AMP不支持touch事件,影响交互体验

AMP官方给我们的解释是:因为不是所有的浏览器都支持passive event,为了最大化浏览器的支持和效能,所以touch事件只开放给AMP内部组件使用,比如amp-carousel。

AMP没有开放获取cookie的权限

因为第三方会缓存AMP页面,出于安全考虑,AMP页面上不开放cookie。不过最新消息是,AMP官方回应后续可能会考虑开放cookie。

只能使用AMP提供的组件

虽然AMP提供了不少组件(并且在持续新增中),但是开发者无法自己书写JS去实现交互,不适合开发复杂的交互。

eamp框架,让开发AMP项目更简单

eamp是从我们AMP SEO项目中提取出来的简化版框架,能够让我们快速开启AMP Node项目,使用者无需从0开始搭建,更能专注amp页面开发。

特点:

  1. 快速搭建基于node的AMP项目
  2. 内置自动化上线打包工具,自动将css打包内联,符合AMP规范
  3. 目录结构清晰,职责划分清楚,便于多人开发协作

推荐资料

AMP官网

ampproject github

Win7: VirtualBox虚拟机安装Ubuntu详细教程 & Ubuntu下如何安装最新Node.js&npm包

Part One: VirtualBox虚拟机安装Ubuntu详细教程

此部分可参考此文VirtualBox虚拟机安装Ubuntu详细教程

Part Two: Ubuntu下如何安装最新的Node.js&npm包

在Ubuntu Terminal中通过apt-get install可快速安装Node.js。安装好后,node -V 发现Node.js版本竟然是v4.2.6,虽然有点诧异,但是想着可以升级,也可通过版本管理器安装多个Node.js,就没多想,就继续安装npm,但是当Node.js和npm都安装好后,使用npm命令却报如下错误:

ERROR: npm is known not to run on Node.js v4.2.6 Node.js 4 is supported but the specific version you're running has a bug known to break npm.

问题很明朗,就是Node.js版本太低,那之前想通过npm安装Node.js版本管理器,再通过Node.js版本管理安装多个版本的Node.js的方法是行不通了。二话不说先卸载。

卸载 nodejs & npm

sudo apt remove nodejs npm

尝试第二种方法:wget获取指定版本的Node.js进行安装

sudo wget -qO- https://deb.nodesource.com/setup_8.x | sudo bash

出现Permission denied错误(如下图)。
image

看错误是apt-get update获取最新软件包的时候,需要操作 /var/cache/apt/lists下的文件但是没有权限导致失败。通过chmod修改文件权限即可。

sudo chmod -R 777 /var/lib/apt/lists/

修改权限后再执行

sudo wget -qO- https://deb.nodesource.com/setup_8.x | sudo bash

即可获取到Node.js版本包,通过install安装

sudo apt-get install nodejs

命令行提示错误如下:
image

gnutls_handshake() failed: Error in the pull function.但最后提示我们:maybe run apt-get update or try with --fix-missing.通过运行apt-get update即可解决。

sudo apt-get update

另外,也可编译Node.js源码包安装。

# wget http://nodejs.org/dist/v9.3.0/node-v8.9.3.tar.gz
# tar xvf node-v8.9.3.tar.gz
# cd node-v8.9.3.tar.gz
# ./configure
# make
# make install

探索热更新对代码打包结果的影响(二)

探索热更新对代码打包结果的影响(一)

在前一篇文章最后,我们已经锁定问题——热更新过程中导致入口文件module.exports输出的对象无法被正确挂载到window上。而至于真实情况是就是没有挂载还是挂载后被冲掉(复写)了,这个就是这篇文章中需要探索的。

探索前务必要先看一下Webpack热更新的源码知道其原理。有关Webpack热更新原理在此将不再赘述,推荐两篇相关文章:

Webpack HMR 原理解析

Webpack 热更新实现原理分析

在此,我再用通俗的语言来阐述一下Webpack热更新的原理。

不得不再提一下我们的项目架构:本项目以express搭建服务端,本地开发配合webpack-dev-middleware、wepack-hot-middleware、webpack.HotModuleReplacementPlugin实现热加载更新。我们看到有webpack-dev-middlerware、webpack-hot-middleware、webpack.HotModuleReplacementPlugin各种工具,但是我们要明白一点,打包这个活儿本质上还是webpack干的,并不是加了热更新相关的插件或者中间件后,打包这项活就是插件或者中间件干了。插件和中间件的作用仅仅是在webpack编译打包核心的外围做了一些事儿(手脚)。所以在看待这几个插件的时候,我们只要抓住热更新这一个关键功能就可以了。
以下这个图来自文章-Webpack HMR 原理解析
image

这样理解起来就简化不少,热更新的机制本质也是client-server的机制,client就在我们的浏览器端,它需要知道文件变化了并且做出一系列的动作然后把最终结果通过浏览器让我们看到,那server端的职责就很明确了,它需要监控模块文件的变化,如果变化了就要让webpack(webpack对外暴露的API)重新打包,期间还要和client保持通信,把自己这边的状况告诉client,client根据信息采取动作。

以上就是进入问题分析前的准备工作。

深入实际问题分析

为了便于看清问题,实验的例子越简单越好。所以我们仍旧采用上一篇文章中使用的例子。
入口文件 - examples/test/index.js

const Test = {
  text: 'hello world!'
}

module.exports = Test

html - example/test/index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>test</title>
</head>
<body>
    Test
    <!-- <script src="../../dist/components/test.js"></script> -->
    <script src="/examples/test.js"></script>
    <script>
      console.log(test)
      console.log(test.text)
    </script>
</body>
</html>

然后用生产的webpack打包配置和本地开发的打包配置分别打包出test_s.js(这个是直接打到物理磁盘的,即为发布包)和test_f.js(开发包是打在内存的,但是浏览器能访问到,我们直接右键另存为即可),test_s.js能够按照预期运行,test_f.js无法拿到预期入口。

我们看下test_s.js和test_f.js文件内容,我们可以在注释中找到这么一句话『Load entry module and return exports』,没错,这就是最终打包后暴露出来的方法。看截图:
image

不管是test_s.js还是test_f.js,入口都是一个叫做341的模块,这没毛病,毕竟是同样的一份代码嘛。继续看。
image

test_s.js是生产包,摒除了热加载,本身test.js也没啥代码,所以打包后的代码也很少,一眼就能看到 341是入口,341里头_webpack_require_(342),而342就是我们test.js的代码内容,成功找到了Test。对于_webpack_require_有疑问的同学可以查看(这篇文章)[https://github.com/ShowJoy-com/showjoy-blog/issues/39],在这里只要了解 在入口模块中调用了 webpack_require(342),就会得到342这个模块返回的 module.exports,所以就能将Test找到,并且赋给module.exports,所以341作为入口文件,最终暴露出来的就是Test。

同样在test_f.js中,我们也能找到入口引用,看下图:
image

341作为入口模块,341中_webpack_require_(342),342模块就是我们的test.js,出乎意料的是341中同时引入了_webpack_require_(32),而且module.exports上挂载的不是342是32!!!。至此问题彻底明了,热加载过程中导致入口文件module.exports输出的对象无法被正确挂载到window上。事实情况是只引入了我们的入口文件并没有对输出进行挂载(不是冲掉)挂载的是32模块。

那这是不是bug呢?是bug我就去提issue啦,看来还是太天真:)。

继续看32模块。
image
image

如果你认真看过最前面推荐的文章——Webpack 热更新实现原理分析,并且浏览过HRM源码,就会有深刻印象。这是HRM的client模块代码,核心功能是通过EventSource,将与HRM server建立连接,进行模块更新通信。看一下代码注释/* WEBPACK VAR INFECTION */,没错,就是这段注入代码,成了真正的入口函数。

再回头看一下 webpack-develop.config.js:

entry: {
    test: [path.resolve(__dirname, 'examples/test/index.js'), 'webpack-hot-middleware/client?path=/examples/__what&reload=true'],
  },

entry test的数组依次是index.js,然后是 webpack-hot-middleware。所以index.js的包引到了,但是入口确是后者 webpack-hot-middleware。

最终解决方法

webpack-develop.config.js:

entry: {
    // test: [path.resolve(__dirname, 'examples/test/index.js'), 'webpack-hot-middleware/client?path=/examples/__what&reload=true'],

    // 更换顺序,正确书写entry
    test: ['webpack-hot-middleware/client?path=/examples/__what&reload=true' path.resolve(__dirname, 'examples/test/index.js')],
  },
  output: {
    filename: '[name].js',
    publicPath: '/examples/',
    library: '[name]',
    libraryTarget: 'umd',
  },

image

至此,该问题完美解决。

后记:依样画葫芦的坑有时候更大呀,而且极难理出排查思路。「别的组件入口都没问题,偏偏这个组件有问题」。有种情况叫负负得正了:)。

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.