Git Product home page Git Product logo

zichil.github.io's Introduction

zichil.github.io's People

Contributors

zichil avatar

zichil.github.io's Issues

9102年 Webpack4 升级

升级 webpack4 已经有一段时间了,将升级过程中记录下来。

升级

将 Webpack 升级到 4.x 之后,直接运行webpack -xx,会提示还需要安装 webpack-cli。

The CLI moved into a separate package: webpack-cli. Please install webpack-cli in addition to webpack itself to use the CLI.

npm i webpack@latest webapck-cli -D

接下来升级所有的依赖,查看所有可以更新的包,进行升级。

npm outdate

默认配置

mode: 'development'
  • 自动通过 DefinePlugin 设 process.env.NODE_ENV 的值为 development
  • 自动开启 NamedChunksPlugin(固定 chunk id) 和 NamedModulesPlugin(开启 HMR 的时候使用该插件会显示模块的相对路径)
module.exports = {
+ mode: 'development'
- devtool: 'eval',
- plugins: [
-   new webpack.NamedModulesPlugin(),
-   new webpack.NamedChunksPlugin(),
-   new webpack.DefinePlugin({ "process.env.NODE_ENV": JSON.stringify("development") }),
- ]
}

production 模式下,由于提供了 splitChunks 和 minimize,所以基本零配置,代码就会自动分割、压缩、优化,同时 webpack 也会自动帮你 Scope hoisting 和 Tree-shaking。

mode: 'production'
  • 自动通过 DefinePlugin 设 process.env.NODE_ENV 的值为 production
  • 自动开启 FlagDependencyUsagePlugin,FlagIncludedChunksPlugin,ModuleConcatenationPlugin,NoEmitOnErrorsPlugin,OccurrenceOrderPlugin,SideEffectsFlagPlugin,TerserPlugin
module.exports = {
+  mode: 'production',
-  plugins: [
-    new UglifyJsPlugin(/* ... */),
-    new webpack.DefinePlugin({ "process.env.NODE_ENV": JSON.stringify("production") }),
-    new webpack.optimize.ModuleConcatenationPlugin(),
-    new webpack.NoEmitOnErrorsPlugin()
-  ]
}

DefinePlugin 允许创建一个在编译时可以配置的全局常量。 在此处设置可以全局访问的环境变量 process.env.NODE_ENV,以便做相应不同的操作 。

所谓的“零配置”就是通过设置mode,自动配置两个环境通常需要做的操作。

mini-css-extract-plugin

webpack4 更推荐使用 mini-css-extract-plugin 替代 extract-text-webpack-plugin。该插件将CSS提取到单独的文件中。它为每个包含CSS的JS文件分离出一个CSS文件,并且支持CSS和SourceMap的按需加载。

css 原本是内联在 js 文件中的,之所以要将 css 独立拆包,是为了让 css 和 js 的改动互不影响,比如改了css,只会让 css 的缓存失效,而不会让 js 的缓存失效。并且它还会根据 optimization.splitChunks 的配置来自动拆包,比如将 element-ui 单独拆分出来作为一个 bundle, 那么 element-ui 的 css 也被单独拆分出来,而不是像原来将所有 css 合并成一个文件,动辄几百 KB 的大小。

与 extract-text-webpack-plugin 相比:

  • Async loading [异步加载]
  • No duplicate compilation (performance) [没有重复编译(性能)]
  • Easier to use [更容易使用]
  • Specific to CSS [专门为 css 设计]
// webpack.prod.conf.js
module.exports = merge(baseWebpackConfig, {
  plugins: [
-   new MiniCssExtractPlugin({
-     filename: utils.assetsPath('css/[name].[contenthash].css')
-   }),
  ]
} 
// utils.js
// Extract CSS when that option is specified
// (which is the case during production build)
- if (options.extract) {
-   return ExtractTextPlugin.extract({
-  use: loaders,
-     fallback: 'vue-style-loader',
-     publicPath: '../../'
-   })
- } else {
-   return ['vue-style-loader'].concat(loaders)
- }
+ return [
+   options.extract ? {
+     loader: MiniCssExtractPlugin.loader,
+     options: {
+       publicPath: '../../'
+     }
+   } : 'vue-style-loader',
+ ].concat(loaders)

vue-loader

将 vue-loader 升级到15.x之后,需要增加如下代码:

const { VueLoaderPlugin } = require('vue-loader')
plugins: [
  new VueLoaderPlugin(),
] 

optimization

下面是 webpack4 默认的 optimization 配置。

  • mode: development
+ mode: 'development'
- optimization: {
-   namedModules: true,
-   namedChunks: true,
-   nodeEnv: 'development',
-   flagIncludedChunks: false,
-   occurrenceOrder: false,
-   sideEffects: false,
-   usedExports: false,
-   concatenateModules: false,
-   splitChunks: {
-     hidePathInfo: false,
-     minSize: 10000,
-     maxAsyncRequests: Infinity,
-     maxInitialRequests: Infinity,
-   },
-   noEmitOnErrors: false,
-   checkWasmTypes: false,
-   minimize: false,
-   removeAvailableModules: false
- },
  • mode: production
+  mode: 'production',
- optimization: {
-   namedModules: false,
-   namedChunks: false,
-   nodeEnv: 'production',
-   flagIncludedChunks: true,
-   occurrenceOrder: true,
-   sideEffects: true,
-   usedExports: true,
-   concatenateModules: true,
-   splitChunks: {
-     hidePathInfo: true,
-     minSize: 30000, // 压缩前 chunk 的体积大于等于300000 byte
-     maxAsyncRequests: 5, // 按需加载 bundle 的并发请求数小于等于5
-     maxInitialRequests: 3, // 页面初始加载 bundle 的并发请求数小于等于3
-   },
-   noEmitOnErrors: true,
-   checkWasmTypes: true,
-   minimize: true,
- },

splitChunk

webpack4 其中一个重要改变就是 optimization.splitChunk 替代原先的 webpack.optimize.CommonsChunkPlugin。

只要设置 mode: production,webpack4 就会自动开启 splitChunk 进行 Code Splitting。默认的 splitChunk 的配置如上。

Code Splitting 的目的就是为了更好的利用浏览器缓存。虽然 webpack4 默认的拆包已经很不错,但是根据自己项目的情况还是可以进行优化。

  • 基础库 vendor

如 vue,vuex,vue-router,axios 等第三方库,是项目构成的基础,每个页面都有用到,并且升级频率不高,除了组件库之外还有一些体积不大的第三方库,这些都放在这个包里。

  • 组件库

项目中使用的组件库是 element-ui,由于几乎所有的组件都有用到,所以没有做按需引入。体积很大,没 Gzip 之前有658.86 KB,Gzip 之后也有160多 KB。比整个 vendor 的体积都大,并且更新频率也比 vendor 里的高。如果不将 element -ui 分割出来,一旦更新,那么整个 vendor + element-ui
体积大小的 bundle 要重新从服务器获取资源,不能利用缓存。因此将组件库从 vendor 中分割了出来。

  • 业务代码

我们平时写的业务代码。项目中使用了路由懒加载,通过 component: () => import(...) 引入路由组件,webpack 就会根据路由自动进行代码分割。

  • manifest

我们还需要将存有 chunk 之间映射关系的 runtime 代码提取出来,否则它将包含在 bundle 之中。manifest 描述了 webpack 应该加载哪些文件,提取出来可以更快的加载文件而不用等待 bundle 加载。并且,哈希值是根据 chunk 内容生成的,一旦哈希值改变,runtime 代码也会改变,那么runtime 所在的 bundle 也将缓存失效。

manifest 提出出来得到的文件通常很小,并且这个文件每次上线都会改变,需要为了几 KB 浪费一次请求数。因此更推荐将 manifest 内联到 html 文件中,毕竟 html 文件 是每次都会改变的。使用的插件是 inline-manifest-webpack-plugin

根据上面所说的我们可以做出如下配置

runtimeChunk: {
  name: 'manifest',
},
splitChunks: {
  chunks: 'all',
  cacheGroups: {
    // 抽离第三方插件
    vendor: {
      test: /[\\/]node_modules[\\/]/,
      chunks: 'initial',
      name: 'vendor',
    },
    'vendor-element-ui': {
      name: 'vendor-element-ui', // split elementUI into a single package
      priority: 20, // the weight needs to be larger than vendor and app or it will be packaged into libs or app
      test: /[\\/]node_modules[\\/]_?element-ui(.*)/, // in order to adapt to cnpm
      enforce: true,
     },
  },
},

minimizer

optimize-css-assets-webpack-plugin

将 css 分离出来后,还需要对 css 做压缩与优化。这是为什么还需要optimize-css-assets-webpack-plugin

optimization: {
  minimizer: [new OptimizeCSSAssetsPlugin()]
}

optimize-css-assets-webpack-plugin 默认使用 cssnano 来做 css 优化,压缩代码,删掉无用的注释,去掉冗余的代码,还会优化代码及书写顺序。因此大大减少了 css 文件的大小。

terser-webpack-plugin

在 mode: production 模式下,会自动开启 minimize: true,自动压缩代码,内置的插件就是 terser-webpack-plugin,但是 terser-webpack-plugin 的默认配置并不能满足我们的项目。

optimization: {
  minimizer: [
    new TerserPlugin({
      terserOptions: {
        drop_console: true,
        safari10: true,
       },
      sourceMap: config.build.productionSourceMap,
      parallel: true,
      cache: true,
    }),
  ]
}

打包速度对比

版本 第一次 第二次 第三次 第四次 第五次
webpack3 49324 52749 49431 51893 49725
webpack4 39034 33620 31968 30814 31874

可以看到打包速度有了质的飞跃,大幅降低上线所需要的时间。

性能优化——从浏览器输入url开始说起

从输入url开始的过程

  1. 判断是否命中缓存,如果命中缓存,直接由缓存提供资源,否则继续下面的步骤
  2. DNS查询:解析输入url对应的IP地址
  3. 根据IP建立TCP连接,过程中有3次握手,
  4. HTTP向服务器发起请求,如果请求头有相应的信息,就可以不需要从服务器请求资源,直接从浏览器读取缓存的HTML
  5. 服务器处理请求
  6. 服务器返回HTML响应给浏览器
  7. 浏览器解析HTML
  8. 对HTML页面引用的所有资源包括js,css,图片等等,浏览器都发送GET请求,又重复上面的过程
    优化:减少加载需要建立连接的次数,启用http1.1默认开启的keep-alive长连接,短连接就是每次请求都要重新建立TCP连接,请求完成断开TCP连接,长连接同一个客户端socket向同一socket的后续请求都复用同一个TCP连接。

页面加载的性能优化

  1. 虽然长连接可以使一个TCP连接请求多个资源,但是我们请求的资源往往是有关联的,比如要先请求完1.css 再请求1.js

  2. 浏览器一般会建立多个TCP连接,并行请求资源,从而加快整体的速度,但是并行请求的数量是有限制的

针对以上两点,我们还是需要去做一些优化。

  1. 减少HTTP请求的次数,所以就有了代码合并,雪碧图,将图片转为base64写入其他文件

  2. 使用HTTP 2,提供了多路复用的功能,所有资源都可以通过一个TCP连接不阻塞的并行传输,就不需要再做减少HTTP请求次数的优化了。

  3. 减少请求资源的大小:开启Gzip可以大幅减少资源体积,分割代码并启用路由(组件)懒加载,优化首屏加载的性能,使用合理的图片大小和格式比如说谷歌开发的图片格式WebP,或者直接用字体图标代替图片,字体图标不仅体积小,而且可以任意变化大小不会模糊,还可以任意变化颜色,使用非常灵活

  4. 缓存,不需要接受数据的传输,直接使用浏览器的本地缓存,分为两种:强缓存和协商缓存,一般是由上一次请求服务器返回的响应头控制。
    强缓存:不会向服务器发送请求,Cache-control或者Expires判断,如果命中,就返回状态码200;
    如果不命中则是判断是否协商缓存:向服务器发送验证请求,如果请求头有If-Modified-Since,服务器返回Last-Modified,如果两个相同则返回状态码304;如果发送的请求头有If-None-Match,服务器返回ETag,比较两者,如果相同则返回状态码304

需要注意的是强缓存则不会与服务器有任何交互,所以服务器更新了资源,浏览器也无法得知,如果每次请求都使用协商缓存,每次都要向服务器发送验证请求,又多做了无用功,所以我们应该尽可能的使用强缓存,但是在更新版本之后让缓存失效,所以在更新版本的时候直接修改资源文件名就可以实现了,然后webpack可以让我们在打包的时候自动给文件名加上哈希值。

页面渲染过程

  1. 解析HTML,生成DOM树
  2. 解析CSS,生成CSSOM树
  3. 将DOM树和CSSOM树合成渲染树,确定每个元素运用哪个CSS规则
  4. 根据渲染树,进行页面的布局,计算每个节点的大小和位置
  5. 在页面上绘制各个节点

页面渲染的性能优化
问题:

  1. css是阻塞渲染的资源,直到CSSOM构建完毕,不会渲染任何已经处理的内容
  2. 在HTML的解析过程中如果遇到scirpt标签,会停止DOM树的构建,知道执行完毕,但仍会识别后面的资源并进行预加载,JavaScript可以查询和修改CSSOM和DOM
  3. 回流还有重绘导致渲染树重新生成
    1)回流会重新计算布局,通常是由元素的结构,增删,位置,尺寸变化引起的;还有在js中读取元素的offset,client,getComputedStyle也会引起,由CPU处理
    2)重绘:元素的视觉表现变化会引起,比如颜色,背景的改变,由GPU处理
    3)如果回流,则一定会引起重绘,并且CPU的处理效率不如GPU,所以更注重减少回流

优化:

dom:减少dom元素的数量,降低DOM的深度,减少生成DOM树和渲染树的构建成本,尽量避免塞进去div元素只为了实现布局,可以用伪元素的就用伪元素

js:1. 将js放在body底部
2. 减少以及合并DOM操作,使用DocumentFragment进行DOM的离线更新

css:1. 放在head中,避免html渲染完成后,又重新结合CSSOM树,引起页面闪烁
2. 降低css的层级和选择器的复杂度
3. 使用更高性能的css样式,比如用flex布局代替float布局
4. 合理使用脱离文档流的样式,比如absolute,fiexd,减少引发回流
5. 优先使用transform,opacity等属性来实现动画,可以跳过布局和绘制的过程,计算样式之后直接进行渲染层合并

数据结构——链表

链表

https://github.com/trekhleb/javascript-algorithms 的学习笔记

链表是数据元素的线性集合,但他不是像数组那样连续储存在内存中,每个链表元素都由他自己的值和指向下一个元素的引用组成。他可以插入或删除指定的元素,但由于他的访问只能是线性的,它要访问一个元素,就要从头开始遍历直到找到该元素,不能像数组那样快速的随机访问。

image

基本操作

插入

Add(value)
  Pre: value is the value to add to the list
  Post: value has been placed at the tail of the list
  n ← node(value)
  if head = ø
    head ← n
    trail ← n
  else
    trail.next = n
    trail ← n
  end if
end Add
Prepend(value)
  Pre: value is the value to add to the list
  Post: value has been placed at the head of the list
  n ← node(value)
  n.next ← head
  n ← head
  if trail = ø
    trail ← n
  end
end Perepend

搜索

Contains(head, value)
  Pre: head is the head node in the list
       value is the value to search for
  Post: the item is either in the linked list, true; otherwise false
  n ← head
  if n = ø
    return false
  while (n != ø and n.value != value)
    n ← n.next
  end while
  if (n = ø)
    return false
  end if
  return true

删除

Remove(head, value)
  Pre: head is the head node in the list
       value is the value to remove from the list
  Post: value is removed from the list, true, otherwise false
  n ← head
  if n = ø
    return false
  end if
  if n.value = value
    if head = trail
      head ← ø
      trail ← ø
    else
      head ← head.next
    return true
  end if
  while n.next != ø and n.next.value != value
    n ← n.next
  end while
  if n.next != ø
    if trail = n.next
      trail ← n
    end if
    n.next ← n.next.next
    return true
  end if
  else
    return false
end Remove

遍历

Traverse(head)
  Pre: head is the head node in the list
  Post: the items in the list have been traversed
  n ← head
  while n != ø
    yield n.value
    n ← n.next
  end while
end Traverse

反向遍历

ReverseTraversal(head, trail)
  Pre: head and tail belong to the same list
  Post: the items in the list have been traversed in reverse order
  if trail != ø
    curr = trail
    while head != curr
      prev ← head
      while prev.next != curr
        prev ← prev.next
      end while
      yield curr.value
      curr ← prev
    end while
    yield curr.value
  end if
end ReverseTraversal

复杂度

时间复杂度

访问 搜索 插入 删除
O(n) O(n) O(1) O(n)

空间复杂度

O(n)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.