前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >梳理 6 项 webpack 的性能优化

梳理 6 项 webpack 的性能优化

作者头像
Nealyang
发布2020-07-24 17:51:44
1.8K0
发布2020-07-24 17:51:44
举报
文章被收录于专栏:全栈前端精选

前言

开发中,webpack文件一般分为3个:

  1. webpack.base.conf.js (基础文件)
  2. webpack.dev.conf.js (开发环境使用的webpack,需要与webpack.base.conf.js结合使用)
  3. webpack.prod.conf.js(上线环境使用的webpack,需要与webpack.base.conf.js结合使用)

一.优化构建速度

webpack在启动后,会根据Entry配置的入口,递归解析所依赖的文件。这个过程分为「搜索文件」「把匹配的文件进行分析、转化」的两个过程,因此可以从这两个角度来进行优化配置。

1.1 缩小文件的搜索范围

搜索过程优化方式包括:

1. resolve字段告诉webpack怎么去搜索文件,所以首先要重视resolve字段的配置:

参考文档:https://webpack.docschina.org/configuration/resolve/#resolve

resolve用来「配置模块如何解析」。例如,当在 ES2015 中调用 import 'lodash'resolve 选项能够对webpack 查找'lodash' 的方式去做修改(查看模块)。

代码语言:javascript
复制
// webpack.config.js

module.exports = {
  //...
  resolve: {
    // configuration options
  }
};
代码语言:javascript
复制
module.export = {
  resolve: {
    modules:[path.resolve(__dirname, 'node_modules')]
    extensions: ['.js', '.jsx'],
    mainFiles: ['index', 'child'],
    alias: {
       '@/src': path.resolve(__dirname, `../src`),  // 当看到@/src这个路径或字符串的时候,实际上指向的是../src目录
    }
  }
}

(1). resolve.modules参考文档:https://www.webpackjs.com/configuration/resolve/#resolve-modules

resolve.modules告诉webpack解析时「应该搜索的目录」

「绝对路径和相对路径」都能使用,但是要知道他们之间有一点差异。通过查看当前目录以及祖先路径(即 ./node_modules, ../node_modules 等等),相对路径将类似于 Node 查找 'node_modules' 的方式进行查找。「使用绝对路径,将只在给定目录中搜索」

代码语言:javascript
复制
// webpack.config.js

module.exports = {
  //...
  resolve: {
    modules: ['node_modules'] // 相对路径写法,会按./node_modules, ../node_modules的方式查找
  }
};

如果你想要添加一个目录到模块搜索目录,此目录优先于 node_modules/ 搜索:

代码语言:javascript
复制
// webpack.config.js

module.exports = {
  //...
  resolve: {
    modules: [path.resolve(__dirname, 'src'), 'node_modules'] // 绝对路径写法
  }
};

「因此:设置resolve.modules:[path.resolve(__dirname, 'node_modules')]避免层层查找」。(2). resolve.mainFields参考文档:https://www.webpackjs.com/configuration/resolve/#resolve-mainfields

当从 npm包中导入模块时(例如,import * as D3 from "d3"),此选项将决定在 package.json 中使用哪个字段导入模块。根据 webpack 配置中指定的 target 不同,默认值也会有所不同。

target 属性设置为webworker, web 或者没有指定,默认值为:

代码语言:javascript
复制
module.exports = {
  //...
  resolve: {
    mainFields: ['browser', 'module', 'main']
  }
};

对于其他任意的 target(包括 node),默认值为:

代码语言:javascript
复制
module.exports = {
  //...
  resolve: {
    mainFields: ['module', 'main']
  }
};

例如,考虑任意一个名为 upstreamlibrary,其 package.json包含以下字段

代码语言:javascript
复制
{
  "browser": "build/upstream.js",
  "module": "index"
}

在我们 import * as Upstream from 'upstream' 时,这实际上会从browser 属性解析文件。在这里 browser属性是最优先选择的,因为它是 mainFields 的第一项。同时,由 webpack 打包的Node.js 应用程序首先会尝试从 module 字段中解析文件。

(3).resolve.alias参考文档:https://www.webpackjs.com/configuration/resolve/#resolve-alias

创建 importrequire 的别名,来「确保模块引入变得更简」单。例如,一些位于 src/ 文件夹下的常用模块:

代码语言:javascript
复制
alias: {
  Utilities: path.resolve(__dirname, 'src/utilities/'),
  Templates: path.resolve(__dirname, 'src/templates/')
}

现在,「替换「在导入时使用相对路径」这种方式」,就像这样:

代码语言:javascript
复制
import Utility from '../../utilities/utility';

你可以这样使用别名:

代码语言:javascript
复制
import Utility from 'Utilities/utility';

也可以在给定对象的键后的末尾添加 $,以表示精准匹配:

代码语言:javascript
复制
module.exports = {
  //...
  resolve: {
    alias: {
      xyz$: path.resolve(__dirname, 'path/to/file.js')
    }
  }
};

这将产生以下结果:

代码语言:javascript
复制
import Test1 from 'xyz'; // 精确匹配,所以 path/to/file.js 被解析和导入
import Test2 from 'xyz/file.js'; // 非精确匹配,触发普通解析

PS: 如果你使用了TS,在webpack中使用了resolve.alias,一般需要在tsconfig.json文件中对其进行配置,否则使用alias会导致无法找到响应目录而报错:

代码语言:javascript
复制
// tsconfig.json

"compilerOptions": {
    "paths": {
      "@/src/*": ["./src/*"],
      'Templates': ["./src/templates/"],
    },
}

对庞大的第三方模块设置resolve.alias, 使webpack直接使用库的min文件,避免库内解析

(4). resolve.extensions参考文档:https://www.webpackjs.com/configuration/resolve/#resolve-extensions

配置resolve.extensions可以自动解析确定的扩展。合理配置resolve.extensions,以减少文件查找

resolve.extensions默认值:extensions:['.wasm', '.mjs', '.js', '.json'],当导入语句没带文件后缀时,Webpack会根据extensions定义的后缀列表进行文件查找,所以:

  • 列表值尽量少
  • 频率高的文件类型的后缀写在前面
  • 源码中的导入语句尽可能的写上文件后缀,如require(./data)要写成require(./data.json)

常用写法:

代码语言:javascript
复制
extensions: ['.js', '.json', '.ts', '.tsx', '.scss']
2. module.noParse字段告诉Webpack不必解析哪些文件,可以用来排除对非模块化库文件的解析

参考文档:https://webpack.docschina.org/configuration/module/#module-noparse

jQuery、ChartJS,另外如果使用resolve.alias配置了react.min.js,则也应该排除解析,因为react.min.js经过构建,已经是可以直接运行在浏览器的、非模块化的文件了。noParse值可以是RegExp、[RegExp]、function

代码语言:javascript
复制
module:{ noParse:[/jquery|chartjs/, /react\.min\.js$/]}
3. 配置loader时,通过test、exclude、include等缩小搜索范围
代码语言:javascript
复制
{
    test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
    loader: 'url-loader',
    options: {
      limit: 10000,
      name: `/fonts/[name].[hash:8].[ext]`
    }
}

二.提升开发效率

开发过程中修改代码后,需要自动构建和刷新浏览器,以查看效果。这个过程可以使用Webpack实现自动化,Webpack负责监听文件的变化,DevServer负责刷新浏览器。

2.1 使用自动刷新

2.1.1 Webpack监听文件

Webpack可以开启监听:启动webpack时加上--watch参数

https://webpack.js.org/configuration/dev-server/#root

代码语言:javascript
复制
// package.json

"scripts": {
    "dev": "webpack --watch" // --watch监听打包文件,只要发生变化,就会重新打包。只要有这个参数就生效。
}

但我们想要更丰富的功能:执行npm run dev就会自动打包,并自动打开浏览器,同时可以模拟一些服务器上的特性,此时就要借助WebpackDevServer来实现。

代码语言:javascript
复制
devServer:{
    contentBase: './dist' // 服务器起在哪个文件夹下。WebpackDevServer会帮助我们在这个文件夹下起一个服务器
}

配置

代码语言:javascript
复制
devServer:{
    port: 8080, // 默认8080
    contentBase: './dist',
    open: true, // 自动打开浏览器,并访问服务器地址。file协议不行,不能发送ajax请求
    proxy: {
        './api': 'http://localhost:3000' // 用户访问 /api 这个路径会被转发到 http://localhost:3000,支持跨域代理
    }
}
2.1.2 DevServer刷新浏览器
代码语言:javascript
复制
devServer: {
    contentBase: config.build.assetsRoot,
    host: config.dev.host,
    port: config.dev.port,
    open: true,
    inline: true,
    hot: true,
    overlay: {
      warnings: true,
      errors: true
    },
    historyApiFallback: {
      rewrites: [
        { from: /^\/index\//, to: `http://${config.dev.host}:${config.dev.port}/index.html` },
      ]
    },
    noInfo: true,
    disableHostCheck: true,
    proxy: {
      // '/user/message': {
      //   target: `http://go.buy.test.mi.com`,
      //   changeOrigin: true,
      //   secure: false
      // },
    }
  },
DevServer刷新浏览器有两种方式:
  1. 向网页中注入代理客户端代码,通过客户端发起刷新
  2. 向网页装入一个iframe,通过刷新iframe实现刷新效果

默认情况下,以及 devserver: {inline:true} 都是采用第一种方式刷新页面。第一种方式DevServer因为不知道网页依赖哪些Chunk,所以会向每个chunk中都注入客户端代码,当要输出很多chunk时,会导致构建变慢。而一个页面只需要一个客户端,所以关闭inline模式可以减少构建时间,chunk越多提升越明显。关闭方式:

  1. 启动时使用webpack-dev-server --inline false
  2. 配置devserver:{inline:false}

关闭inline后入口网址变为http://localhost:8080/webpack-dev-server/另外devServer.compress参数可配置是否采用Gzip压缩,默认为false

2.2 开启模块热替换HMR

模块热替换不刷新整个网页而只重新编译发生变化的模块,并用新模块替换老模块,所以预览反应更快,等待时间更少,同时不刷新页面能保留当前网页的运行状态。原理也是向每一个chunk中注入代理客户端来连接DevServer和网页。开启方式:

webpack-dev-server --hot使用HotModuleReplacementPlugin,比较麻烦

代码语言:javascript
复制
// package.json
"scripts": {
    "start": "webpack-dev-server" ,
}

webpack-dev-server打包后的dist中的内容放到了内存中,加快访问速度

代码语言:javascript
复制
const webpack = require('webpack')

module.exports =  {
    devServer:{
        port: 8080, // 默认8080
        contentBase: './dist',
        open: true, 
        hot: true, // 让webpack-dev-server开启Hot Module Replacement功能
        hotOnly: true, // 即使HMR功能没有生效,也不让浏览器自动刷新,
    },
    module: {
        rules: [
        {
            test: /\.css$/,
            use:  [
                 'style-loader',
                 'css-loader',
                 'postcss-loader',
               ]
        },
        ]
    },
    plugins: [
      new HtmlWebpackPlugin({
        template: 'src/index.html',
      }),
      new CleanWebpackPlugin(['dist']), // 开发环境不需要此配置
      new webpack.HotModuleReplacementPlugin() // 使用webpack插件,可用于开发环境
   ],
}

开启后如果修改子模块就可以实现局部刷新,但如果修改的是根JS文件,会整页刷新,原因在于,子模块更新时,事件一层层向上传递,直到某层的文件接收了当前变化的模块,然后执行回调函数。如果一层层向外抛直到最外层都没有文件接收,就会刷新整页。使用 NamedModulesPlugin 可以使控制台打印出被替换的模块的名称而非数字ID,另外同webpack监听,忽略node_modules目录的文件可以提升性能。

三、优化输出质量-压缩文件体积

3.1 区分环境--减小生产环境代码体积

代码运行环境分为「开发环境」「生产环境」,代码需要根据不同环境做不同的操作,许多第三方库中也有大量的根据开发环境判断的if else代码,构建也需要根据不同环境输出不同的代码,所以需要一套机制可以在源码中区分环境,区分环境之后可以使输出的生产环境的代码体积减小。Webpack中使用DefinePlugin插件来定义配置文件适用的环境。

3.2 压缩代码-JS、CSS

1. 压缩JS:Webpack内置UglifyJS插件、ParallelUglifyPlugin

使用terser-webpack-plugin插件压缩JS代码: 参考文档:https://webpack.js.org/plugins/terser-webpack-plugin/

代码语言:javascript
复制
optimization: {
 minimizer: [
      new TerserPlugin({
        terserOptions: {
          safari10: true
        }
      })
    ],
}

取代了 UglifyJsPlugin

代码语言:javascript
复制
// 取代 new UglifyJsPlugin(/* ... */)
2. 压缩CSS

2.1 mini-css-extract-plugin:https://webpack.js.org/plugins/mini-css-extract-plugin/ 。该插件将CSS提取到单独的文件中。它为每个包含CSSJS文件创建一个CSS文件。它支持CSSSourceMap的按需加载。它基于新的webpack v4功能(模块类型)构建,并且需要webpack 4才能正常工作。

2.2 optimize-css-assets-webpack-plugin: https://www.npmjs.com/package/optimize-css-assets-webpack-plugin 。主要是用来压缩css文件

代码语言:javascript
复制
plugins: [
    new MiniCssExtractPlugin({
      filename: path.join('css/[name].css?[contenthash:8]'),
      chunkFilename: path.join('css/[name].chunk.css?[contenthash:8]')
    }),
    new OptimizeCssAssetsPlugin({
      assetNameRegExp: /\.css\?\w*$/
    })
],

2.3 cssnano基于PostCSS,不仅是删掉空格,还能理解代码含义,例如把color:#ff0000 转换成 color:redcss-loader内置了cssnano,只需要使用 css-loader?minimize 就可以开启cssnano压缩。另外一种压缩CSS的方式是使用PurifyCSSPlugin,需要配合 extract-text-webpack-plugin 使用,它主要的作用是可以去除没有用到的CSS代码,类似JSTree Shaking

3.3 使用Tree Shaking剔除JS死代码

参考文档:https://webpack.docschina.org/guides/tree-shaking/

Tree Shaking可以剔除用不上的死代码,它依赖ES6import、export的模块化语法,最先在Rollup中出现,Webpack 2.0将其引入。适合用于Lodash、utils.js等工具类较分散的文件。它正常工作的前提是代码必须采用ES6的模块化语法,因为ES6模块化语法是静态的(在导入、导出语句中的路径必须是静态字符串,且不能放入其他代码块中)。如果采用了ES5中的模块化,例如module.export = {...}、require( x+y )、if (x) { require( './util' ) },则Webpack无法分析出可以剔除哪些代码。

tree shaking 是一个术语,通常用于描述移除 JavaScript 上下文中的未引用代码(dead-code)。它依赖于 ES2015模块语法的 静态结构 特性,例如importexport。这个术语和概念实际上是由 ES2015 模块打包工具 rollup 普及起来的。

webpack 4正式版本扩展了此检测能力,通过package.json"sideEffects" 属性作为标记,向 compiler 提供提示,表明项目中的哪些文件是 "pure(纯的 ES2015 模块)",由此可以安全地删除文件中未使用的部分。

参考文档:https://webpack.docschina.org/guides/tree-shaking/#%E5%B0%86%E5%87%BD%E6%95%B0%E8%B0%83%E7%94%A8%E6%A0%87%E8%AE%B0%E4%B8%BA%E6%97%A0%E5%89%AF%E4%BD%9C%E7%94%A8

注意,所有导入文件都会受到tree shaking 的影响。这意味着,如果在项目中使用类似css-loaderimport 一个 CSS 文件,则需要将其添加到side effect列表中,以免在生产模式中无意中将它删除:

代码语言:javascript
复制
{
  "name": "your-project",
  "sideEffects": [
    "./src/some-side-effectful-file.js",
    "*.css"
  ]
}

参考文档:https://webpack.docschina.org/guides/tree-shaking/#%E5%8E%8B%E7%BC%A9%E8%BE%93%E5%87%BA%E7%BB%93%E6%9E%9C

通过 importexport语法,我们已经找出需要删除的“未引用代码(dead code)”,然而,不仅仅是要找出,还要在 bundle 中删除它们。为此,我们需要将 mode配置选项设置为 production

webpack.config.js

代码语言:javascript
复制
const path = require('path');

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist')
  },
- mode: 'development',
- optimization: {
-   usedExports: true
- }
+ mode: 'production'
};
代码语言:javascript
复制
注意,也可以在命令行接口中使用 --optimize-minimize 标记,来启用 TerserPlugin。

准备就绪后,然后运行另一个 npm script npm run build,就会看到输出结果发生了改变。

dist/bundle.js 中,现在整个 bundle 都已经被 minify(压缩) 和 mangle(混淆破坏),但是如果仔细观察,则不会看到引入 square 函数,但能看到 cube函数的混淆破坏版本(function r(e){return e*e*e}n.a=r)。现在,随着 minification(代码压缩) 和tree shaking,我们的bundle 减小几个字节!虽然,在这个特定示例中,可能看起来没有减少很多,但是,在有着复杂依赖树的大型应用程序上运行 tree shaking时,会对 bundle 产生显著的体积优化。

代码语言:javascript
复制
运行 tree shaking 需要 ModuleConcatenationPlugin。通过 mode: "production" 可以添加此插件。如果你没有使用 mode 设置,记得手动添加 ModuleConcatenationPlugin。

参考文档:https://webpack.docschina.org/guides/tree-shaking/#%E7%BB%93%E8%AE%BA

「结论:」我们已经知道,想要使用 tree shaking 必须注意以下几点:

  • 使用 ES2015模块语法(即 importexport)。
  • 确保没有 compilerES2015 模块语法转换为 CommonJS模块(这也是流行的 Babel preset@babel/preset-env 的默认行为 - 更多详细信息请查看 文档)。
  • 在项目package.json 文件中,添加一个"sideEffects" 属性。
  • 通过将 mode选项设置为 production,启用 minification(代码压缩) 和tree shaking

你可以将应用程序想象成一棵树。绿色表示实际用到的 source code(源码)library(库),是树上活的树叶。灰色表示未引用代码,是秋天树上枯萎的树叶。为了除去死去的树叶,你必须摇动这棵树,使它们落下。

四、优化输出质量--加速网络请求

4.1 使用CDN加速静态资源加载

1. CND加速的原理

CDN通过将资源部署到世界各地,使得用户可以就近访问资源,加快访问速度。要接入CDN,需要把网页的静态资源上传到CDN服务上,在访问这些资源时,使用CDN服务提供的URL

由于CDN会为资源开启长时间的缓存,例如用户从CDN上获取了index.html,即使之后替换了CDN上的index.html,用户那边仍会在使用之前的版本直到缓存时间过期。业界做法:

  • HTML文件:放在自己的服务器上且关闭缓存,不接入CDN
  • 静态的JS、CSS、图片等资源:开启CDN和缓存,同时文件名带上由内容计算出的Hash值,这样只要内容变化hash就会变化,文件名就会变化,就会被重新下载而不论缓存时间多长。

另外,HTTP1.x版本的协议下,浏览器会对于向同一域名并行发起的请求数限制在4~8个。那么把所有静态资源放在同一域名下的CDN服务上就会遇到这种限制,所以可以把他们分散放在不同的CDN服务上,例如JS文件放在js.cdn.com下,将CSS文件放在css.cdn.com下等。这样又会带来一个新的问题:增加了域名解析时间,这个可以通过dns-prefetch来解决 <link rel='dns-prefetch' href='//js.cdn.com'> 来缩减域名解析的时间。形如**//xx.com 这样的URL省略了协议**,这样做的好处是,浏览器在访问资源时会自动根据当前URL采用的模式来决定使用HTTP还是HTTPS协议。

当浏览器从第三方服务跨域请求资源的时候,在浏览器发起请求之前,这个第三方的跨域域名需要被解析为一个IP地址,这个过程就是DNS解析,DNS缓存可以用来减少这个过程的耗时,DNS解析可能会增加请求的延迟,对于那些需要请求许多第三方的资源的网站而言,DNS解析的耗时延迟可能会大大降低网页加载性能。

参考文章:https://developer.mozilla.org/zh-CN/docs/Web/Performance/dns-prefetch

2. 总之,构建需要满足以下几点:
  • 静态资源导入的URL要变成指向CDN服务的绝对路径的URL
  • 静态资源的文件名需要带上根据内容计算出的Hash
  • 不同类型资源放在不同域名的CDN
3. 最终配置:
代码语言:javascript
复制
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const {WebPlugin} = require('web-webpack-plugin');
//...
output:{
 filename: '[name]_[chunkhash:8].js',
 path: path.resolve(__dirname, 'dist'),
 publicPatch: '//js.cdn.com/id/', //指定存放JS文件的CDN地址
},
module:{
 rules:[{
     test: /\.css/,
     use: ExtractTextPlugin.extract({
         use: ['css-loader?minimize'],
         publicPatch: '//img.cdn.com/id/', //指定css文件中导入的图片等资源存放的cdn地址
     }),
 },{
    test: /\.png/,
    use: ['file-loader?name=[name]_[hash:8].[ext]'], //为输出的PNG文件名加上Hash值 
 }]
},
plugins:[
  new WebPlugin({
     template: './template.html',
     filename: 'index.html',
     stylePublicPath: '//css.cdn.com/id/', //指定存放CSS文件的CDN地址
  }),
 new ExtractTextPlugin({
     filename:`[name]_[contenthash:8].css`, //为输出的CSS文件加上Hash
 })
]

4.2 多页面应用提取页面间公共代码,以利用缓存

  1. 原理

大型网站通常由多个页面组成,每个页面都是一个独立的单页应用,多个页面间肯定会依赖同样的样式文件、技术栈等。如果不把这些公共文件提取出来,那么每个单页打包出来的chunk中都会包含公共代码,相当于要传输n份重复代码。如果把公共文件提取出一个文件,那么当用户访问了一个网页,加载了这个公共文件,再访问其他依赖公共文件的网页时,就直接使用文件在浏览器的缓存,这样公共文件就只用被传输一次。

  1. 应用方法

把多个页面依赖的公共代码提取到common.js中,此时common.js包含基础库的代码

代码语言:javascript
复制
把多个页面依赖的公共代码提取到common.js中,此时common.js包含基础库的代码

找出依赖的基础库,写一个base.js文件,再与common.js提取公共代码到base中,common.js就剔除了基础库代码,而base.js保持不变

代码语言:javascript
复制
//base.js
import 'react';
import 'react-dom';
import './base.css';
//webpack.config.json
entry:{
    base: './base.js'
},
plugins:[
    new CommonsChunkPlugin({
        chunks:['base','common'],
        name:'base',
        //minChunks:2, 表示文件要被提取出来需要在指定的chunks中出现的最小次数,防止common.js中没有代码的情况
    })        
]
  1. 得到基础库代码base.js,不含基础库的公共代码common.js,和页面各自的代码文件xx.js

页面引用顺序如下:base.js--> common.js--> xx.js

4.3 分割代码以按需加载

  1. 原理

单页应用的一个问题在于使用一个页面承载复杂的功能,要加载的文件体积很大,不进行优化的话会导致首屏加载时间过长,影响用户体验。做按需加载可以解决这个问题。具体方法如下:

  • 将网站功能按照相关程度划分成几类
  • 每一类合并成一个Chunk,按需加载对应的Chunk
  • 例如,只把首屏相关的功能放入执行入口所在的Chunk,这样首次加载少量的代码,其他代码要用到的时候再去加载。最好提前预估用户接下来的操作,提前加载对应代码,让用户感知不到网络加载
  1. 做法

一个最简单的例子:网页首次只加载main.js,网页展示一个按钮,点击按钮时加载分割出去的show.js,加载成功后执行show.js里的函数

代码语言:javascript
复制
//main.js
document.getElementById('btn').addEventListener('click',function(){
    import(/* webpackChunkName:"show" */ './show').then((show)=>{
        show('Webpack');
    })
})
//show.js
module.exports = function (content) {
    window.alert('Hello ' + content);
}

import(/* webpackChunkName:show */ './show').then() 是实现按需加载的关键」Webpack内置对import( *)语句的支持,Webpack会以./show.js为入口重新生成一个Chunk。代码在浏览器上运行时只有点击了按钮才会开始加载show.js,且import语句会返回一个Promise,加载成功后可以在then方法中获取加载的内容。这要求浏览器支持Promise API,对于不支持的浏览器,需要注入Promise polyfill/* webpackChunkName:show */ 是定义动态生成的Chunk的名称,默认名称是[id].js,定义名称方便调试代码。为了正确输出这个配置的ChunkName,还需要配置Webpack

代码语言:javascript
复制
//...
output:{
    filename:'[name].js',
    chunkFilename:'[name].js', // 指定动态生成的Chunk在输出时的文件名称
}

五、优化输出质量--提升代码运行时的效率

5.1 使用Prepack提前求值

  1. 原理:

Prepack是一个部分求值器,编译代码时提前将计算结果放到编译后的代码中,而不是在代码运行时才去求值。通过在便一阶段预先执行源码来得到执行结果,再直接将运行结果输出以提升性能。但是现在Prepack还不够成熟,用于线上环境还为时过早。

参考文档:https://github.com/facebook/prepack

  1. 使用方法
代码语言:javascript
复制
const PrepackWebpackPlugin = require('prepack-webpack-plugin').default;
module.exports = {
    plugins:[
        new PrepackWebpackPlugin()
    ]
}

5.2 使用Scope Hoisting

  1. 原理

译作“作用域提升”,是在Webpack3中推出的功能,它分析模块间的依赖关系,尽可能将被打散的模块合并到一个函数中,但不能造成代码冗余,所以只有被引用一次的模块才能被合并。由于需要分析模块间的依赖关系,所以源码必须是采用了ES6模块化的,否则Webpack会降级处理不采用Scope Hoisting

  1. 使用方法
代码语言:javascript
复制
const ModuleConcatenationPlugin = require('webpack/lib/optimize/ModuleConcatenationPlugin');
//...
plugins:[
    new ModuleConcatenationPlugin();
],
resolve:{
	mainFields:['jsnext:main','browser','main']
}

webpack --display-optimization-bailout 输出日志中会提示哪个文件导致了降级处理

六、使用输出分析工具

启动Webpack时带上这两个参数可以生成一个json文件,输出分析工具大多依赖该文件进行分析:webpack --profile --json > stats.json 其中 --profile 记录构建过程中的耗时信息,--jsonJSON的格式输出构建结果,>stats.jsonUNIX / Linux系统中的管道命令,含义是将内容通过管道输出到stats.json文件中。

  1. 官方工具Webpack Analyse

打开该工具的官网http://webpack.github.io/analyse/上传stats.json,就可以得到分析结果

  1. webpack-bundle-analyzer

可视化分析工具,比Webapck Analyse更直观。使用也很简单:

npm i -g webpack-bundle-analyzer安装到全局 按照上面方法生成stats.json文件 在项目根目录执行webpack-bundle-analyzer,浏览器会自动打开结果分析页面。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-07-07,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 全栈前端精选 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 一.优化构建速度
    • 1.1 缩小文件的搜索范围
      • 搜索过程优化方式包括:
        • 1. resolve字段告诉webpack怎么去搜索文件,所以首先要重视resolve字段的配置:
        • 2. module.noParse字段告诉Webpack不必解析哪些文件,可以用来排除对非模块化库文件的解析
        • 3. 配置loader时,通过test、exclude、include等缩小搜索范围
    • 二.提升开发效率
      • 2.1 使用自动刷新
        • 2.1.1 Webpack监听文件
        • 2.1.2 DevServer刷新浏览器
        • DevServer刷新浏览器有两种方式:
        • 2.2 开启模块热替换HMR
    • 三、优化输出质量-压缩文件体积
      • 3.1 区分环境--减小生产环境代码体积
        • 3.2 压缩代码-JS、CSS
          • 1. 压缩JS:Webpack内置UglifyJS插件、ParallelUglifyPlugin
          • 2. 压缩CSS
        • 3.3 使用Tree Shaking剔除JS死代码
        • 四、优化输出质量--加速网络请求
          • 4.1 使用CDN加速静态资源加载
            • 1. CND加速的原理
            • 2. 总之,构建需要满足以下几点:
            • 3. 最终配置:
          • 4.2 多页面应用提取页面间公共代码,以利用缓存
            • 4.3 分割代码以按需加载
            • 五、优化输出质量--提升代码运行时的效率
              • 5.1 使用Prepack提前求值
                • 5.2 使用Scope Hoisting
                • 六、使用输出分析工具
                相关产品与服务
                内容分发网络 CDN
                内容分发网络(Content Delivery Network,CDN)通过将站点内容发布至遍布全球的海量加速节点,使其用户可就近获取所需内容,避免因网络拥堵、跨运营商、跨地域、跨境等因素带来的网络不稳定、访问延迟高等问题,有效提升下载速度、降低响应时间,提供流畅的用户体验。
                领券
                问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档