美文网首页
Webpack--理解“publicPath”的奥秘(翻译)

Webpack--理解“publicPath”的奥秘(翻译)

作者: 天很清 | 来源:发表于2019-12-01 00:38 被阅读0次

    原文:Webpack — Understanding the ‘publicPath’ mystery

    你曾多少次被webapck's publicPath的配置绊倒?老实说,在每次开始一个新项目的时候,我总是遇到这个问题,并且花费很长时间去探索它到底是怎么工作的。我阅读了官方文档,但并没有什么用。
    所以,这里我决定根据我的经验和理解去揭开publicPath的神秘面纱。如果对你有帮助,欢迎反馈意见。

    当你的publicPath配置错误时,通常会遇到这2个问题:

    1. webpack-dev-server: live-reload(浏览器自动刷新)机制没有正常执行,比如说,浏览器不会自动重新加载,或者加载的代码不是最新的。
    2. 生成的css文件,图片、字体路径发生报错,打断编译打包的过程。

    让我们来从一个基础的配置去分析这件事。我们有一个文件结构,如下显示:

    appMain [Project Folder]
    |-- src
         |-- index.js
    |-- index.html
    |-- webpack.config.js
    |-- package.json
    |-- dist
    
    // 极简配置的webpack
    module.exports = {
      entry: './src/index.js',
      output: {
        filename: './dist/bundle.js'
      }
    };
    
    // index.html中引入生成的bundle.js
    <script src="./dist/bundle.js"></script>
    

    如果我们只运行 webpack命令,会默认执行webpack.config.js, 生产打包文件到 dist 文件夹下。

    此外,如果你使用了 webpack-dev-server启动,live-reload就开始生效了,即是,任何在js源文件 [index.js] 中的修改都会立即在浏览器中生效,不需要手动刷新。
    我们可以通过console.log()打印一些消息来测试这个功能。

    注意:webpack-dev-server 启动的服务,使用的并不是真实生成的包文件,它只是监听你的源代码,当它们发生变化时去重新编译。这个修改的包是直接从内存中读取的。😎

    现在让我们加入配置path到webpack output 对象中,来代替在output.filename中使用完整路径的配置。

    const resolve = require('path').resolve;
    
    module.exports = {
      entry: './src/index.js',
      output: {
        // seperated path and filename of generated output
        path: resolve('dist'),
        filename: 'bundle.js'
      }
    };
    

    请注意output.path需要使用绝对路径而不是相对路径,否则webpack会报错。所以,我们使用了path模块的resolve方法。

    报错信息: Invalid configuration object. Webpack has been initialised using a configuration object that does not match the API schema. \- configuration.output.path: The provided value "./dist" is not an absolute path!

    在这种情况下,webpack会生成错误的输出,比如有bundle.js文件,但webpack-dev-serverlive-reload会突然停止执行。并且,关于源文件[index.js]的任何修改,无论怎么刷新浏览器都无法显示,尽管终端显示源文件已经编译成功。

    现在,如果你停止你的webpack-dev-server 服务,再次执行webpack命令,然后再重新启动 webpack-dev-server,修改的自动更新又正常了,但是我们不能每次都这样做吧,真的神烦。😖

    所以,是哪个环节出错了?

    主要是因为当path的未配置时,webpack-dev-config 会把它的值默认为项目的根目录,即 ./

    const resolve = require('path').resolve;
    
    module.exports = {
      entry: './src/index.js',
      output: {
        // 如果path未配置,默认值是 './'
        path: resolve('./'),
        filename: './dist/bundle.js'
      }
    };
    

    所以,可以正常工作。
    但当我们配置为真实的路径,比如reslove('dist'), webpack仍然在./位置编译生成输出文件。
    你可以直接在浏览器中访问服务的URL地址验证这一点

    http://localhost:8080/dist/bundle.js
    // 总是指向我们运行webpack命令时生成的旧代码
    http://localhost:8080/bundle.js
    // 总是指向最新编译后的代码,在src变化后会立即重新加载
    注意:这两个打包的文件并不是完全相同的,后者因为使用dev-server会注入一些额外的代码

    所以,这里live-reload旧代码导致的问题是,我们使用了前者打包的 bundle.js,却在index.html使用的是 webpack 命令生成的路径。
    任何代码的改变,webpack-dev-server 会生成一个包,但地址是不同的。

    解决这个问题,只需要改变一下index.html中的script:src属性,一切又能正常运行了。
    神速!我们理解并修复了这个问题。

    进一步来说,我们使用 webpack 的publicPath去配置 webpack-dev-server 生成的打包文件的位置,是个虚拟位置, 而不是真实的文件系统。
    这个虚拟位置可以用来在 index.html 中定位引入生成的文件。

    const resolve = require('path').resolve;
    
    module.exports = {
      entry: './src/index.js',
      output: {
        path: resolve('dist'),
        filename: 'bundle.js',
        // Add vitual path for generating the bundle files
        publicPath: 'some-virtual-location'
      }
    };
    
    // 更新script:src到新的虚拟位置(Virtual-Path)
    <script src="./some-virtual-location/bundle.js"></script>
    

    这样,我们就可以在本地开发中模拟真实服务器部署环境或者CDN。
    一些开发者更喜欢命名为 assets, public, dist, / 等等,这完全看你喜欢如何维护生成的文件结构和命名方式。
    底线就是,index.html 中引入的script:src的值必须与 publicPath 的配置保持一致。

    publicPath对CSS的其他影响 -- 图片、字体的路径等等

    在生成最终的styles时,webpack默认会把 publicPath的配置添加到 图片的URL,字体的路径上。
    示例,我添加2个文件到当前的项目中:

    appMain
    |-- src
         |-- index.js
         |-- main.css [Source CSS/SCSS file]
         |-- background-image.jpg
    |-- index.html
    |-- webpack.config.js
    |-- package.json
    |-- dist
    

    main.css (使用相对路径引入背景图片)

    .main {
        background-image: url("background.jpg");
    }
    

    我们使用了 css/scss 文件和图片,所以也要更新webpack.config配置,添加相应的 loaders 或 plugin。
    webpack.config.js

    const resolve = require('path').resolve;
    const ExtractTextPlugin = require('extract-text-webpack-plugin');
    
    module.exports = {
      entry: './src/index.js',
      output: {
        path: resolve('dist'),
        filename: 'bundle.js',
        publicPath: 'some-virtual-location/'
      },
      // 添加 CSS 和 Style Loader
      module: {
        rules: [
          {
            test: /\.css$/,
            use: ExtractTextPlugin.extract({
              fallback: 'style-loader',
              use: 'css-loader'
            })
          },
          {
            test: /\.(png|jpg|gif)$/,
            use: [
              {
                loader: 'file-loader',
                options: {
                  name: '[name].[ext]'
                }
              }
            ]
          }
        ]
      },
      // 生成单独的样式文件
      plugins: [
        new ExtractTextPlugin('styles.css')
      ]
    };
    

    当你运行webpack命令时,会触发下面行为:

    1. background-image.jpg 文件会被 file-loader拷贝到dist文件夹中。
    2. styles.css会被插件 ExtractTextPlugin 根据main.css生成到dist文件夹中。
    3. 生成的css文件中background-image: url会被自动添加publicPath的配置。
    .main {
        background-image: url(some-virtual-location/background.jpg);
    }
    

    这个规则同样适用于其他的静态资源,比如fonts.
    所以正确的配置publicPath非常重要,Webpack 的 Loaders 和 Plugins 会使用这个配置。

    希望这篇文章能让你深入的理解神秘的publicPath是怎么工作的,怎样正确的使用它。

    欢迎在评论区写下你的想法和意见,我们可以进一步的讨论。✌🏻✌🏻

    相关文章

      网友评论

          本文标题:Webpack--理解“publicPath”的奥秘(翻译)

          本文链接:https://www.haomeiwen.com/subject/vxmawctx.html