Fork me on GitHub

vue-hackernews-2.0 升级到 webpack4 和 nodejs14 的坑(下)

概述

最近非常想做一个服务端渲染项目,那就打算从尤大的vue-hackernews-2.0开始入手呗。其实我之前试图改造过这个项目,但是因为当时很菜所以失败了。现在我觉得有能力改造好,那就开始呗。把心得记录下来,供以后开发时参考,相信对其他人也有用。

上篇:vue-hackernews-2.0 升级到 webpack4 和 nodejs14 的坑(上)

CommonsChunkPlugin

老项目是使用 commonchunksplugin 抽取公共模块和css模块的,代码如下:

// extract vendor chunks for better caching
new webpack.optimize.CommonsChunkPlugin({
    name: 'vendor',
    minChunks: function (module) {
    // a module is extracted into the vendor chunk if...
    return (
        // it's inside node_modules
        /node_modules/.test(module.context) &&
        // and not a CSS file (due to extract-text-webpack-plugin limitation)
        !/\.css$/.test(module.request)
    )
    }
}),
// extract webpack runtime & manifest to avoid vendor chunk hash changing
// on every build.
new webpack.optimize.CommonsChunkPlugin({
    name: 'manifest'
}),

而 webpack4 则直接内置了 optimization 字段来实现这个功能,所以我们注释掉上面的代码,然后加入如下代码:

// webpack.client.js
optimization: {
    splitChunks: {
        chunks: 'all',
        cacheGroups: {
        styles: {
            name: 'styles',
            test: /css$/,
            enforce: true,
        },
        vendor: {
            name: 'vendor',
            test: /[\/]node_modules[\/]/,
            enforce: true,
        },
        },
    },
    runtimeChunk: {
        name: 'manifest',
    },
},

externals

老项目是使用 externals 字段在服务端打包的时候防止 css 没有被引入,但是这里我们已经在服务端不打包 css 了,所以这个配置可以删去。但是这里我们加上如下配置,来防止引入 node_modules 里面的包,从而提高打包效率:

// webpack.server.config.js
externals: Object.keys(require('../package.json').dependencies),

service-worker

老项目是使用sw-precache-webpack-plugin来安装 service-worker 的,现在这个包已经被workbox-webpack-plugin代替了,相关文档在这里

我们首先卸载 sw-precache-webpack-plugin 然后安装 workbox-webpack-plugin:

// 卸载
"sw-precache-webpack-plugin": "^0.11.4",

// 安装
"workbox-webpack-plugin": "^5.1.4"

然后我们在webpack.client.config.js里面删掉 SWPrecachePlugin 的代码,加入 WorkboxPlugin 的相关配置:

// webpack.client.config.js
const WorkboxPlugin = require('workbox-webpack-plugin');

if (process.env.NODE_ENV === 'production') {
  config.plugins.push(
    new WorkboxPlugin.GenerateSW({
      cacheId: 'vue-hn',
      swDest: 'service-worker.js',
      clientsClaim: true,
      skipWaiting: true,
      dontCacheBustURLsMatching: /./,
      exclude: [/\.map$/, /\.json$/],
      runtimeCaching: [
        {
          urlPattern: '/',
          handler: 'NetworkFirst'
        },
        {
          urlPattern: /\/(top|new|show|ask|jobs)/,
          handler: 'NetworkFirst'
        },
        {
          urlPattern: '/item/:id',
          handler: 'NetworkFirst'
        },
        {
          urlPattern: '/user/:id',
          handler: 'NetworkFirst'
        }
      ]
    })
  )
}

由于 service-worker 支持在 localhost 进行调试,所以我们更改一下老项目引入 service-worker 的代码:

// entry-client.js
if ('serviceWorker' in navigator && process.env.NODE_ENV === 'production') {
  window.addEventListener('load', () => {
    navigator.serviceWorker.register('/service-worker.js').then(registration => {
      console.log('SW registered: ', registration);
    }).catch(registrationError => {
      console.log('SW registration failed: ', registrationError);
    });
  });
}

虽然到这里就已经替换完成了,但是在实际打包之后,WorkboxPlugin 会生成一个带有hash的workbox.js文件,当我们更改 WorkboxPlugin 相关配置之后再打包,这个 hash 值会变化。这里面有一个问题就是,这个文件没有被 express 暴露出来,所以请求不到这个文件(express 只暴露了没带 hash 的 service-worker.js文件)。

解决方法有2种,第一种是直接改entry-client.js里面的service-worker路径,在前面加上 dist。这样service-worker.js在请求 workbox.js 的时候回自己带上 dist 前缀。代码如下:

// entry-client.js
if ('serviceWorker' in navigator && process.env.NODE_ENV === 'production') {
  window.addEventListener('load', () => {
    navigator.serviceWorker.register('/dist/service-worker.js').then(registration => {
      console.log('SW registered: ', registration);
    }).catch(registrationError => {
      console.log('SW registration failed: ', registrationError);
    });
  });
}

另一种解决方法是在 express 里面动态引入带有hash的workbox.js文件。由于这个文件带有 hash 值,所以它不能被 express.static 直接引入,app.use 也不支持正则引入,所以我们需要获取 dist 文件夹下面的所有文件名,找出带有 workbox.js 的文件名,然后利用这个文件名通过 express.static 暴露出来,代码如下:

// workbox dir
const distFiles = fs.readdirSync('./dist')
const workboxDir = distFiles.filter(name => /workbox-.*\.js$/.test(name))

if (workboxDir.length > 0) {
  app.use(`/${workboxDir[0]}`, serve(`./dist/${workboxDir[0]}`))
}

完工

到这里就全部升级完毕了,详细代码可以参考我的vue-hackernews-2.0项目

我学到了什么:

  1. 了解了一下 webpack 在 ssr 的各种配置
  2. 尝试了一些 node api
  3. 为以后编写cheer-fun的项目做准备
posted @ 2020-10-12 00:01  馒头加梨子  阅读(216)  评论(0编辑  收藏  举报