无法为 Vue Webpack 构建的应用程序加载 Worker 脚本

Posted

技术标签:

【中文标题】无法为 Vue Webpack 构建的应用程序加载 Worker 脚本【英文标题】:Worker Script Failing to Load for Vue Webpack Built App 【发布时间】:2018-08-01 23:15:58 【问题描述】:

我在 CouchDB 中部署了一个 Node 应用程序,该应用程序是根据 Vue Webpack 模板开发和构建的。我所依赖的模块之一,pdfjs-dist,依赖于一个工人。

运行npm run build 并在dist 中获取我的输出后,我将输出文件复制到couchapp 以将它们部署到CouchDB。在部署过程中我没有收到任何错误,并且站点启动后看起来很好。

但是,当我尝试做一些需要 pdfjs-dist 的事情时,我得到了错误:

NetworkError:无法在以下位置加载工作脚本 "http://.../docuapp/documentation/_design/uploads/static/js/app.4eb1aecbadc78360a76e.worker.js" (unbekannt) 错误:加载块 0 失败。 堆栈跟踪: 你@http://.../docuapp/documentation/_design/uploads/static/js/manifest.a6f78538bddeebdefd4a.js:1:957 manifest.a6f78538bddeebdefd4a.js:1:1378 错误:加载块 0 失败。 manifest.a6f78538bddeebdefd4a.js:1:957

所以我检查了无法加载的 url,并且文档丢失了。但是当我删除网址的worker 部分时,即

http://.../docuapp/documentation/_design/uploads/static/js/app.4eb1aecbadc78360a76e.js

此页面已加载,我猜这就是所需的资源。

有没有人猜到这里出了什么问题?

这是我的 package.json


  "name": "docu-back",
  "version": "1.0.0",
  "description": "A Vue.js project",
  "author": "",
  "private": true,
  "scripts": 
    "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js",
    "start": "npm run dev",
    "build": "node build/build.js"
  ,
  "dependencies": 
    "element-ui": "^2.0.11",
    "mime-types": "^2.1.17",
    "nano": "^6.4.2",
    "node-dir": "^0.1.17",
    "pdfjs-dist": "^2.0.332",
    "querystring-browser": "^1.0.4",
    "vue": "^2.5.2",
    "vue-awesome": "^2.3.4",
    "vue-router": "^3.0.1"
  ,
  "devDependencies": 
    "autoprefixer": "^7.1.2",
    "babel-core": "^6.22.1",
    "babel-helper-vue-jsx-merge-props": "^2.0.3",
    "babel-loader": "^7.1.1",
    "babel-plugin-syntax-jsx": "^6.18.0",
    "babel-plugin-transform-runtime": "^6.22.0",
    "babel-plugin-transform-vue-jsx": "^3.5.0",
    "babel-preset-env": "^1.3.2",
    "babel-preset-stage-2": "^6.22.0",
    "chalk": "^2.0.1",
    "copy-webpack-plugin": "^4.0.1",
    "css-loader": "^0.28.0",
    "extract-text-webpack-plugin": "^3.0.0",
    "file-loader": "^1.1.4",
    "friendly-errors-webpack-plugin": "^1.6.1",
    "html-webpack-plugin": "^2.30.1",
    "node-notifier": "^5.1.2",
    "optimize-css-assets-webpack-plugin": "^3.2.0",
    "ora": "^1.2.0",
    "portfinder": "^1.0.13",
    "postcss-import": "^11.0.0",
    "postcss-loader": "^2.0.8",
    "postcss-url": "^7.2.1",
    "rimraf": "^2.6.0",
    "semver": "^5.3.0",
    "shelljs": "^0.7.6",
    "uglifyjs-webpack-plugin": "^1.1.1",
    "url-loader": "^0.5.8",
    "vue-loader": "^13.3.0",
    "vue-style-loader": "^3.0.1",
    "vue-template-compiler": "^2.5.2",
    "webpack": "^3.6.0",
    "webpack-bundle-analyzer": "^2.9.0",
    "webpack-dev-server": "^2.11.1",
    "webpack-merge": "^4.1.0"
  ,
  "engines": 
    "node": ">= 6.0.0",
    "npm": ">= 3.0.0"
  ,
  "browserslist": [
    "> 1%",
    "last 2 versions",
    "not ie <= 8"
  ]

构建/build.js

'use strict'
require('./check-versions')()

process.env.NODE_ENV = 'production'

const ora = require('ora')
const rm = require('rimraf')
const path = require('path')
const chalk = require('chalk')
const webpack = require('webpack')
const config = require('../config')
const webpackConfig = require('./webpack.prod.conf')

const spinner = ora('building for production...')
spinner.start()

rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => 
  if (err) throw err
  webpack(webpackConfig, (err, stats) => 
    spinner.stop()
    if (err) throw err
    process.stdout.write(stats.toString(
      colors: true,
      modules: false,
      children: false, // If you are using ts-loader, setting this to true will make TypeScript errors show up during build.
      chunks: false,
      chunkModules: false
    ) + '\n\n')

    if (stats.hasErrors()) 
      console.log(chalk.red('  Build failed with errors.\n'))
      process.exit(1)
    

    console.log(chalk.cyan('  Build complete.\n'))
    console.log(chalk.yellow(
      '  Tip: built files are meant to be served over an HTTP server.\n' +
      '  Opening index.html over file:// won\'t work.\n'
    ))
  )
)

构建/webpack.base.conf.js

const path = require('path')
const utils = require('./utils')
const config = require('../config')
const vueLoaderConfig = require('./vue-loader.conf')

function resolve (dir) 
  return path.join(__dirname, '..', dir)




module.exports = 
  context: path.resolve(__dirname, '../'),
  entry: 
    app: './src/main.js'
  ,
  output: 
    path: config.build.assetsRoot,
    filename: '[name].js',
    publicPath: process.env.NODE_ENV === 'production'
      ? config.build.assetsPublicPath
      : config.dev.assetsPublicPath
  ,
  resolve: 
      extensions: ['.js', '.vue', '.json'],

    alias: 
        'vue$': 'vue/dist/vue.esm.js',
        querystring: 'querystring-browser',
        '@': resolve('src'),
        '_qs': 'querystring-browser'
    
  ,
  module: 
    rules: [
      
        test: /\.vue$/,
        loader: 'vue-loader',
        options: vueLoaderConfig
      ,
      
        test: /\.js$/,
        loader: 'babel-loader',
        include: [resolve('src'), resolve('test'), resolve('node_modules/webpack-dev-server/client')]
      ,
      
        test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
        loader: 'url-loader',
        options: 
          limit: 10000,
          name: utils.assetsPath('img/[name].[hash:7].[ext]')
        
      ,
      
        test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
        loader: 'url-loader',
        options: 
          limit: 10000,
          name: utils.assetsPath('media/[name].[hash:7].[ext]')
        
      ,
      
        test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
        loader: 'url-loader',
        options: 
          limit: 10000,
          name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
        
      
    ]
  ,
  node: 
    // prevent webpack from injecting useless setImmediate polyfill because Vue
    // source contains it (although only uses it if it's native).
    setImmediate: false,
    // prevent webpack from injecting mocks to Node native modules
    // that does not make sense for the client
    dgram: 'empty',
    fs: 'empty',
    net: 'empty',
    tls: 'empty',
    child_process: 'empty'
  

构建/webpack.dev.conf.js

const utils = require('./utils')
const webpack = require('webpack')
const config = require('../config')
const merge = require('webpack-merge')
const path = require('path')
const baseWebpackConfig = require('./webpack.base.conf')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin')
const portfinder = require('portfinder')

const HOST = process.env.HOST
const PORT = process.env.PORT && Number(process.env.PORT)

const devWebpackConfig = merge(baseWebpackConfig, 
  module: 
    rules: utils.styleLoaders( sourceMap: config.dev.cssSourceMap, usePostCSS: true )
  ,
  // cheap-module-eval-source-map is faster for development
  devtool: config.dev.devtool,

  // these devServer options should be customized in /config/index.js
  devServer: 
    clientLogLevel: 'warning',
    historyApiFallback: 
      rewrites: [
         from: /.*/, to: path.posix.join(config.dev.assetsPublicPath, 'index.html') ,
      ],
    ,
    hot: true,
    contentBase: false, // since we use CopyWebpackPlugin.
    compress: true,
    host: HOST || config.dev.host,
    port: PORT || config.dev.port,
    open: config.dev.autoOpenBrowser,
    overlay: config.dev.errorOverlay
      ?  warnings: false, errors: true 
      : false,
    publicPath: config.dev.assetsPublicPath,
    proxy: config.dev.proxyTable,
    quiet: true, // necessary for FriendlyErrorsPlugin
    watchOptions: 
      poll: config.dev.poll,
    
  ,
  plugins: [
    new webpack.DefinePlugin(
      'process.env': require('../config/dev.env')
    ),
    new webpack.HotModuleReplacementPlugin(),
    new webpack.NamedModulesPlugin(), // HMR shows correct file names in console on update.
    new webpack.NoEmitOnErrorsPlugin(),
    // https://github.com/ampedandwired/html-webpack-plugin
    new HtmlWebpackPlugin(
      filename: 'index.html',
      template: 'index.html',
      inject: true
    ),
    // copy custom static assets
    new CopyWebpackPlugin([
      
        from: path.resolve(__dirname, '../static'),
        to: config.dev.assetsSubDirectory,
        ignore: ['.*']
      
    ])
  ]
)

module.exports = new Promise((resolve, reject) => 
  portfinder.basePort = process.env.PORT || config.dev.port
  portfinder.getPort((err, port) => 
    if (err) 
      reject(err)
     else 
      // publish the new Port, necessary for e2e tests
      process.env.PORT = port
      // add port to devServer config
      devWebpackConfig.devServer.port = port

      // Add FriendlyErrorsPlugin
      devWebpackConfig.plugins.push(new FriendlyErrorsPlugin(
        compilationSuccessInfo: 
          messages: [`Your application is running here: http://$devWebpackConfig.devServer.host:$port`],
        ,
        onErrors: config.dev.notifyOnErrors
        ? utils.createNotifierCallback()
        : undefined
      ))

      resolve(devWebpackConfig)
    
  )
)

构建/webpack.prod.conf.js

const path = require('path')
const utils = require('./utils')
const webpack = require('webpack')
const config = require('../config')
const merge = require('webpack-merge')
const baseWebpackConfig = require('./webpack.base.conf')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const ExtractTextPlugin = require('extract-text-webpack-plugin')
const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin')
const UglifyJsPlugin = require('uglifyjs-webpack-plugin')

const env = require('../config/prod.env')

const webpackConfig = merge(baseWebpackConfig, 
  module: 
    rules: utils.styleLoaders(
      sourceMap: config.build.productionSourceMap,
      extract: true,
      usePostCSS: true
    )
  ,
  devtool: config.build.productionSourceMap ? config.build.devtool : false,
  output: 
    path: config.build.assetsRoot,
    filename: utils.assetsPath('js/[name].[chunkhash].js'),
    chunkFilename: utils.assetsPath('js/[id].[chunkhash].js')
  ,
  plugins: [
    // http://vuejs.github.io/vue-loader/en/workflow/production.html
    new webpack.DefinePlugin(
      'process.env': env
    ),
    new UglifyJsPlugin(
      uglifyOptions: 
        compress: 
          warnings: false
        
      ,
      sourceMap: config.build.productionSourceMap,
      parallel: true
    ),
    // extract css into its own file
    new ExtractTextPlugin(
      filename: utils.assetsPath('css/[name].[contenthash].css'),
      // Setting the following option to `false` will not extract CSS from codesplit chunks.
      // Their CSS will instead be inserted dynamically with style-loader when the codesplit chunk has been loaded by webpack.
      // It's currently set to `true` because we are seeing that sourcemaps are included in the codesplit bundle as well when it's `false`,
      // increasing file size: https://github.com/vuejs-templates/webpack/issues/1110
      allChunks: true,
    ),
    // Compress extracted CSS. We are using this plugin so that possible
    // duplicated CSS from different components can be deduped.
    new OptimizeCSSPlugin(
      cssProcessorOptions: config.build.productionSourceMap
        ?  safe: true, map:  inline: false  
        :  safe: true 
    ),
    // generate dist index.html with correct asset hash for caching.
    // you can customize output by editing /index.html
    // see https://github.com/ampedandwired/html-webpack-plugin
    new HtmlWebpackPlugin(
      filename: config.build.index,
      template: 'index.html',
      inject: true,
      minify: 
        removeComments: true,
        collapseWhitespace: true,
        removeAttributeQuotes: true
        // more options:
        // https://github.com/kangax/html-minifier#options-quick-reference
      ,
      // necessary to consistently work with multiple chunks via CommonsChunkPlugin
      chunksSortMode: 'dependency'
    ),
    // keep module.id stable when vendor modules does not change
    new webpack.HashedModuleIdsPlugin(),
    // enable scope hoisting
    new webpack.optimize.ModuleConcatenationPlugin(),
    // split vendor js into its own file
    new webpack.optimize.CommonsChunkPlugin(
      name: 'vendor',
      minChunks (module) 
        // any required modules inside node_modules are extracted to vendor
        return (
          module.resource &&
          /\.js$/.test(module.resource) &&
          module.resource.indexOf(
            path.join(__dirname, '../node_modules')
          ) === 0
        )
      
    ),
    // extract webpack runtime and module manifest to its own file in order to
    // prevent vendor hash from being updated whenever app bundle is updated
    new webpack.optimize.CommonsChunkPlugin(
      name: 'manifest',
      minChunks: Infinity
    ),
    // This instance extracts shared chunks from code splitted chunks and bundles them
    // in a separate chunk, similar to the vendor chunk
    // see: https://webpack.js.org/plugins/commons-chunk-plugin/#extra-async-commons-chunk
    new webpack.optimize.CommonsChunkPlugin(
      name: 'app',
      async: 'vendor-async',
      children: true,
      minChunks: 3
    ),

    // copy custom static assets
    new CopyWebpackPlugin([
      
        from: path.resolve(__dirname, '../static'),
        to: config.build.assetsSubDirectory,
        ignore: ['.*']
      
    ])
  ]
)

if (config.build.productionGzip) 
  const CompressionWebpackPlugin = require('compression-webpack-plugin')

  webpackConfig.plugins.push(
    new CompressionWebpackPlugin(
      asset: '[path].gz[query]',
      algorithm: 'gzip',
      test: new RegExp(
        '\\.(' +
        config.build.productionGzipExtensions.join('|') +
        ')$'
      ),
      threshold: 10240,
      minRatio: 0.8
    )
  )


if (config.build.bundleAnalyzerReport) 
  const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
  webpackConfig.plugins.push(new BundleAnalyzerPlugin())


module.exports = webpackConfig

如果有帮助,我很乐意提供任何其他文件。

这是我导入 pdfjs-dist 的方法:

text.js

var pdfjs = require('pdfjs-dist');
pdfjs.disableWorker = true

【问题讨论】:

可能在你的 webpack 配置中的某个地方你需要使用 target: webworker ?见webpack.js.org/configuration/target 你能发布你如何导入pdfjs-dist吗?似乎有多种使用 webpack 导入它的方法。 @alex-rokabilis 我在问题的底部添加了一些代码 【参考方案1】:

我对这个问题的猜测是 pdfjs 需要在运行时知道 workerSrc 属性。但是当你使用 webpack 时,它可能会在文件名上分配一个不可猜测的哈希值,所以你不能真正将文件名作为字符串放在你的代码中。

但是pdfjs 为我们提供了解决方案!他们的发行版中包含了webpack file。

在您的代码中

var pdfjs = require("pdfjs-dist/webpack");
//we dont't include the normal module
//but the one with the webpack configuration
//that the pdfjs team provides us.

//no need to disable worker
//or point to the workerSrc

那么这个东西是做什么的

//inside "pdfjs-dist/webpack"


'use strict';

var pdfjs = require('./build/pdf.js');
var PdfjsWorker = require('worker-loader!./build/pdf.worker.js');

if (typeof window !== 'undefined' && 'Worker' in window) 
  pdfjs.GlobalWorkerOptions.workerPort = new PdfjsWorker();


module.exports = pdfjs;

基本上它包括来自./build/pdf.js 的原始构建,它还将为我们导入带有worker-loader 的工作脚本。一个 webpack 加载器,旨在在我们的应用程序中导入 web-workers。然后它在pdfjs 实例中分配实际工作人员并将其导出。

在您的应用中导入上述文件后,您将拥有一个配置有网络工作者的 pdfjs 实例,为您的捆绑软件做好准备!

【讨论】:

非常感谢,这似乎是修复。我还有一个小问题:我正在通过 couchdb 部署我的应用程序 fromy /dist,所以基本上所有的 url 都需要有一个前缀才能正确加载,比如 'http://dev04/docs/_design/uploads/static/5e7171ebe225fa2fb12c.worker.js' 而不是 'http://dev04/5e7171ebe225fa2fb12c.worker.js' (它目前正在尝试访问文件)。我如何告诉我的页面在正确的位置寻找这些工人?我一直在为其他静态文件编辑 index.html,但那里没有引用工作文件。 无需手动执行此操作,您可以告诉 webpack 为您执行 :) 检查publicPath configuration。也许您需要检查您是否在生产中才能启用此功能! 另一种方法是使用base tag。为了更改 html 中所有相对 url 的父级。 我遇到了与@DavidJ 相同的问题。但是,我无法弄清楚正确的 publicPath 应该是什么 我已经阅读了 GitHub 上的相关主题,这是我找到的最简单的解决方案。谢谢!

以上是关于无法为 Vue Webpack 构建的应用程序加载 Worker 脚本的主要内容,如果未能解决你的问题,请参考以下文章

从变量中动态加载 Vue 组件

vue+webpack2实现路由的懒加载

在 webpack 4 vue cli 3 中禁用缓存加载器

Vue scss 样式加载因 mocamapck 失败

无法使用 webpack 加载 Node 原生插件

vue按需加载组件-webpack require.ensure