tomoya06 / web-developer-guidance

Actually it's just a notebook for keeping down some working experience.
4 stars 0 forks source link

Vue - 延迟加载和代码拆分 #37

Open tomoya06 opened 4 years ago

tomoya06 commented 4 years ago

延迟加载和代码拆分

前言

本文来自InfoQ博客:Vue性能优化:如何实现延迟加载和代码拆分?,原文总结的很有逻辑。

Webpack的打包原理参考Webpack篇issue,简单来说就是,Webpack会从入口出发,构建出一个依赖图,并把依赖图中的所有模块打包成一个bundle。

定义

延迟加载:就是延迟加载应用程序的部分内容。换句话说,只在真正需要它们时加载它们。 代码拆分:是指将应用程序拆分成可以延迟加载的块。

通过延迟加载适当的组件和库,打包出来的主bundle体积会大大减少。

实现

可以使用 Webpack动态导入来加载应用程序的某些部分。

标准的js模块导入语法如下:

 // main.js
import ModuleA from './module_a.js'
ModuleA.doStuff()

此时main.jsmodule_a.js都会被打包到同一个bundle。

若仅在某些情况下需要ModuleA,就可以使用动态导入的方法。如下述代码,对main/ModuleA/ModuleB/ModuleC分别用不同方法引入:

//main.js
import ModuleB from './mobile_b.js'
const getModuleA = () => import('./module_a.js')
getModuleA()
  .then({ doStuff } => doStuff()
)
//module_a.js
import ModuleC from './module_c.js'

实际构建出来的依赖图如下,相当于创建了一个新的入口:

image

Vueの实践

延迟加载组件

在Vue中可以延迟加载整个 SFC 以及它的 css 和 html,语法和之前一样:

<template>
  <div> 
    <lazy-component v-if="lazyLoad" />
  </div>
</template>
<script>
const lazyComponent = () => import('Component.vue')
export default {
  data() {
    return {};
  },
  components: { lazyComponent }
}
</script>

这样,只有当请求在模板中渲染组件时,才会调用 lazyComponent 函数。例如,如果lazyLoad = false,那么就不会导入组件。

路由懒加载

vue-router也支持路由懒加载。官方文档参考这里。语法类似:

const routes = [
  { path: /', component: () => import('./components/Home.vue') },
  { path: /about', component: () => import('./components/About.vue') },
]

依赖图变化如下,Webpack将创建三个包:

image

Vuex模块

指的是对Vue modules的延迟加载。参考Vuex文档的模块动态注册,即使用store.registerModule方法来动态注册。

// store.js
import { userAccountModule } from './modules/userAccount'
export const store = new Vuex.Store({
  modules: {
    user: userAccountModule, 
    // admin: adminModule,
  }
})
// Admin.vue
import adminModule from './admin.js'
export default { 
  // other component logic
  mounted () { 
    this.$store.registerModule('admin', adminModule)
  },
  beforeDestroy () {
   this.$store.unregisterModule('admin')
  }
}

这样只有在Admin组件被加载时,adminModule子模块才会被加载。依赖图的变化如下:

image

第三方依赖

这里主要以lodash为例。原文介绍的方法是使用 webpack 的 splitChunksPlugin 插件,这样可以保证只有用到了lodash的页面才引入lodash,不过引入的时候还是全量引入。

另外常见的方法如下,参考掘金博客

  1. 只对单个使用到的方法按需引用,并且从文件引用。但引用模块较多时写法麻烦
import isEqual from 'lodash.isequal';
// 或者
import difference from 'lodash/difference';
  1. 使用babel-plugin-lodashlodash-webpack-plugin插件
// webpack.config.js
var LodashModuleReplacementPlugin = require('lodash-webpack-plugin');
var webpack = require('webpack');

module.exports = {
    module: {
        rules: [
            {
                use: 'babel-loader',
                test: /\.js$/,
                exclude: /node_modules/,
                options: {
                    plugins: ['lodash'],
                    presets: [['env', {modules: false, targets: {node: 4}}]]
                }
            }
        ]
    },
    plugins: [new LodashModuleReplacementPlugin(), new webpack.optimize.UglifyJsPlugin()]
};
  1. 改用lodash-es
import {isEmpty, isObject, cloneDeep} from 'lodash-es';