基于 Webpack4 的 Vue 移动端开发环境 - 优化篇

发布于 2024-01-21 03:54:14 字数 7791 浏览 68 评论 0

很多时候做开发都会使用 UI 组件库,这些组件库一般都会有按需加载的功能,这里我选择 vant 组件库来测试。手头暂时没有可以演示的项目(公司的肯定不能晒出来),可能有点纯理论感觉,但是应该能大家一个优化的思路。我会在测试项目里面写一些测试页面,到时上传到 github 供大家参考!

注意:经过我前面的一些实践,这儿有事情需要告知

1、如果开发环境和生产环境都使用插件把 CSS 分离出来成为单独的文件,那么你在开发过程中会发现热更新对 CSS 不生效,所以解决方法就是分离 CSS 仅配置在生产环境。给大家埋了个坑!

开始

1、组件库按需加载

安装 vant

npm install vant

安装完毕后,我们先使用官网的全局导入所有组件

全局导入所有组件

import Vant from 'vant';
import 'vant/lib/index.css';

Vue.use(Vant);

然后你到页面使用几个组件,比如 Button,Dialog 这些,运行 npm run dev 命令,这时发现报错了,说 Module parse failed: Unexpected character '@',这是因为我们引入 vant 时,引入了 css 文件,但是我们的 webpack 并没有配置处理 css 的 loader,所以加上即可:

{
  test: /\.css$/,
  // 这儿组件库的 css 一般都是处理过的,我们使用一般的 loader 即可
  use: [
    {
      loader: 'style-loader',
    },
    {
      loader: 'css-loader',
    }
  ]
}

这样我们就能运行并加入相关组件了,这里我们不演示组件使用,而是直接打包展示结果分析图:

我们可以看见那个硕大的 index.css,也就是说你没用到的组件它也把样式这些都弄进来了。

按需加载

使用按需加载需要使用到一个插件叫 babel-plugin-import。

npm install babel-plugin-import -D

安装完毕后在 babel.config.js 里面配置:

// 按需加载 vant 组件
plugins: [
  ["import", {
    libraryName: 'vant',
    libraryDirectory: 'es',
    style: true
  }, 'vant']
]

这样你在 main.js 里面这样引入即可:

// 按需注册 vant 组件
import { Button } from 'vant';
Vue.use(Button);

你使用了什么组件就注册什么组件,目前社区上的许多 ui 组件库都是支持按需加载的,有的话一定要使用这种方式。

这下看下,比刚才全部引入好多了,额。。main.js 硕大应该是未分离所有东西打包到一起的原因-_-!。

2、路由懒加载

使用路由懒加载,在页面访问到是再加载对应的资源,提升首页访问的速度。
在这之前,我们还需要一个 babel 插件来实现这个功能

npm install @babel/plugin-syntax-dynamic-import -D

安装完毕后,在 webpack 里面 babel-loader 配置加上以下代码:

{
  test: /\.jsx?$/,
  exclude: /node_modules/,
  use: [
    {
      loader: 'babel-loader',
      options: {
        // 使用 vue 官方的懒加载语法并结合 babel 需使用这个插件,不然会报错
        plugins: ["@babel/plugin-syntax-dynamic-import"]
      }
    }	
  ]
}

然后我们现有的路由里面引入组件的写法需要改变下,比如你现在是这么写的:

import Home from '../views/Home.vue';
import RouterTest from '../views/RouterTest.vue';
import VantTest from '../views/VantTest.vue';

改成下面的写法:

//官方的懒加载方案,需要在 webpack.config.js 中配置@babel/plugin-syntax-dynamic-import 这个插件,否则 babel 不支持以下语法会报错。
//下面的注释语法是打包生成的 js 的文件名,如果你想某几个组件打包到同一个文件,那么它们的注释语法的 webpackChunkName 的名字相同即可
const MainPage = () =>
  import(/* webpackChunkName: "group-foo" */ "./views/MainPage.vue");
const ComOne = () =>
  import(/* webpackChunkName: "group-foo" */ "./views/ComOne.vue");
const ComTwo = () =>
  import(/* webpackChunkName: "group-foo" */ "./views/ComTwo.vue");

这样处理后你会发现你在切换路由时会加载额外的 js 文件,这样我们就分离了出来按需加载,打包分析图如下:

这里可以发现一个问题,我们很多包都重复打了,接下来就是去除重复打包。所以我们要提取公共代码。

3、提取公共代码

在 webpack3 的时候,我们可以借助 CommonsChunkPlugin 来实现,webpack4 已经移除了这个插件,在 webpack4 中可以用 splitChunks。

如果你在使用 webpack4 的时候没有做 optimization.splitChunks 的配置,那么它会有一个默认配置,官方文档的代码块:

module.exports = {
  ...
  optimization: {
    splitChunks: {
      chunks: 'async',
      minSize: 30000,
      maxSize: 0,
      minChunks: 1,
      maxAsyncRequests: 5,
      maxInitialRequests: 3,
      automaticNameDelimiter: '~',
      name: true,
      cacheGroups: {
        vendors: {
          test: /[\\/]node_modules[\\/]/,
          priority: -10
        },
        default: {
          minChunks: 2,
          priority: -20,
          reuseExistingChunk: true
        }
      }
    }
  }
  ...
}

大多数情况我们需要自己配置 cashGroups 这个参数,也就是自定义拆分组,这里我参考了社区大佬的配置,然后优化了下:

optimization: {
  splitChunks: {
    cacheGroups: {
      // node_modules 下的模块拆分到 chunk-vendors.xxxx.js 下
      vendors: {
        name: 'chunk-vendors',
        test: /[\\\/]node_modules[\\\/]/,
        priority: -10,
        chunks: 'all'
      },
      // 自己定义的公告组件超过两次引用的放在 chunk-common.xxxx.js 下
      common: {
        name: 'chunk-common',
        minChunks: 2,
        priority: -20,
        chunks: 'all',
        reuseExistingChunk: true
      }
    }
  }
}

一开始,chunks 的属性值都是 initial,然后我们看一下打包分析图:

可以看到分离的 js 文件都打包了一次 core.js 文件,所以我把 chunks 参数值改为 all,再来看分析图:

这样,就没有重复打包了。

4、补充 splitChunks 相关知识

在翻阅了社区上的一些文章后(官方文档对于我这个菜鸡来说看不出来什么-_-),在此还是做一个总结。

4.1、参数解释

  • chunks 有 all、async 和 initial 三个值,async 表示只从异步加载得模块(动态加载 import())里面进行拆分 initial 表示只从入口模块进行拆分 all 表示以上两者都包括。
  • automaticNameDelimiter 打包拆分出来文件名的连接符,一般都是~。
  • maxAsyncRequests 按需加载时的最大并行请求数。
  • maxInitialRequests 入口并行加载的最大请求数。
  • minChunks 当应用次数超过多少次时会打包到单独的块,一般都是 2。
  • minSize 生成块的最小大小(以字节为单位)。
  • maxSize 这个有点复杂,暂时说不清。#手动捂脸
  • name 分割的块名。
  • cashGroups 缓存组,这个可以自己来指定你的拆分规则,同时它的参数跟上面一样,声明了的话就是覆盖,否则就是继承,同时它还有几个独有的选项
    • test 匹配规则,一般使用正则表达式。
    • priority 权值,默认的优先权值为 0,你可以设置负值来比默认的权限高。
    • reuseExistingChunk 如果当前块包含已经从主包中分离出来的模块,那么它将被重用,而不是生成一个新的块。

这里有几篇文章供大家参考:

链接 1
链接 2
链接 3

4.2 结合 runtimeChunk 选项

关于这个参数的作用,我引用思否大佬的一个回答:

优化持久化缓存的, runtime 指的是 webpack 的运行环境(具体作用就是模块解析, 加载) 和 模块信息清单, 模块信息清单在每次有模块变更(hash 变更) 时都会变更, 所以我们想把这部分代码单独打包出来, 配合后端缓存策略, 这样就不会因为某个模块的变更导致包含模块信息的模块(通常会被包含在最后一个 bundle 中) 缓存失效。optimization.runtimeChunk 就是告诉 webpack 是否要把这部分单独打包出来。 链接

就是说呢那些不会经常变的 runtime 把它单独提出来,不需要频繁的产生变化,用来优化后端的缓存策略。

那么在 webpack 的配置文件加上这句:

optimization: {
  runtimeChunk: true,
  splitChunks: {
    ......
  }
}

设置好后,我们打包出来试一试看相比于上面的配置会有哪些区别:

这下你发现 main.js 进一步减小了,把里面的部分内容放到了 runtime~main 下面。

5、其它优化方法

如果还想进一步减小包的体积,除了你的图片媒体资源这些使用 oss 外。你的第三方库也可以使用 cdn 分发网络,比如我的公司项目的 axios,vue,better-scroll 这些都使用了 cdn 分发的方式,而不是直接导入到包里面一起打包。

在你的 index.html 入口页面下以这种方式加入

<script src="https://static.XXXXXX.com/12static/new/js/vue.min.js"></script>
<script src="https://static.XXXXXX.com/12static/new/js/vue-router.js"></script>
<script src="https://static.XXXXXX.com/12static/new/js/axios.min.js"></script>
<script src="https://static.XXXXXX.com/12static/new/js/bscroll.min.js"></script>

最后

暂时到这里总结就结束了,但是优化的脚步不会停,我这儿的优化都是比较基本的优化,肯定还有许多点没有总结到,后期若有使用到的好的优化方法,会持续更新,若文章有错误,还请多多指正!

代码我都上传到了 github,有需要的自取。 Webpack-Vue-Mobile

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

关于作者

文章
评论
493 人气
更多

推荐作者

qq_aHcEbj

文章 0 评论 0

qq_ikhFfg

文章 0 评论 0

把昨日还给我

文章 0 评论 0

wj_zym

文章 0 评论 0

巴黎夜雨

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文