ALEX CODE PARK

vuePress-theme-reco ALEX CODE PARK    2020
ALEX CODE PARK

Choose mode

  • dark
  • auto
  • light
阅读笔记
  • 《深入浅出Vue.js》
  • 《JavaScript高级程序设计》
  • 《你不知道的JavaScript》
  • 《CSS世界》
前端面试
  • CSS
  • HTML
  • JavaScript
  • 常见算法
  • 网络相关
  • 剑指Offer
Github
Tag
Category
  • 前端面试
  • 算法题目
  • 前端书籍
  • 个人笔记
author-avatar

ALEX CODE PARK

19

文章

10

标签

阅读笔记
  • 《深入浅出Vue.js》
  • 《JavaScript高级程序设计》
  • 《你不知道的JavaScript》
  • 《CSS世界》
前端面试
  • CSS
  • HTML
  • JavaScript
  • 常见算法
  • 网络相关
  • 剑指Offer
Github
Tag
Category
  • 前端面试
  • 算法题目
  • 前端书籍
  • 个人笔记
  • 玩转webpack

    • webpack 简介
      • 为什么选择 webpack
      • 初识 webpack
      • 安装 webpack
      • webpack 初体验
    • webpack 基础用法
      • 核心概念 - entry
      • 核心概念 - output
      • 核心概念 - loaders
      • 核心概念 - plugins
      • 核心概念 - mode
      • 解析 ES6
      • 解析 CSS
      • 解析 Less 和 Sass
      • 解析图片 字体
      • webpack 中的文件监听
      • 热更新 webpack-dev-server
      • 文件指纹
      • 代码压缩
    • webpack 进阶用法
      • 自动清理构建目录产物
      • PostCSS 插件 autoprefixer 自动补齐 CSS3 前缀
      • 移动端 CSS px 自动转 rem
      • 静态资源内联
      • 多页面应用打包通用方案
      • 使用 sourceMap
      • 提取公共资源
      • tree shaking 摇树优化
      • scope hoisting
      • 代码分割和动态 import
      • 使用 ESLint

玩转webpack

vuePress-theme-reco ALEX CODE PARK    2020

玩转webpack


AlexChiang 2020-02-14 webpack

# webpack 简介

# 为什么选择 webpack

  • 社区生态丰富
  • 官方插件多
  • 配置灵活

# 初识 webpack

  • 默认配置文件 webpack.config.js

可以通过webpack --config 指定配置文件

# 配置组成

  • entry 打包的入口文件
  • output 打包的输出
  • mode 环境
  • module
    • rules Loader 配置
    • plugins 插件

零配置 webpack 只包含 entry output

# 安装 webpack

webpack 4 将 webpack 和 webpack-cli 做了分离 所以在开发时需要同时安装两者

mkdir xx
cd xx
npm init -y
npm install webpack webpack-cli --save-dev
./node_modules/.bin/webpack -v
1
2
3
4
5

# webpack 初体验

// webpack.config.js
"use strict";

const path = require("path");

module.exports = {
  entry: "./src/index.js",
  output: {
    path: path.join(__dirname, "dist"),
    filename: "bundle.js"
  }
};
1
2
3
4
5
6
7
8
9
10
11
12

配置好后在终端运行./node_modules/.bin/webpack即可打包

每次在终端运行太过麻烦,可以通过npm run build运行构建

在package.json中加入

  "scripts": {
    "build": "webpack"
  },
1
2
3

原理:模块局部安装会在node_modules/.bin目录创建软连接

# webpack 基础用法

# 核心概念 - entry

  • 单入口 entry 是一个字符串
  • 多入口 entry 是一个对象

# 核心概念 - output

output 用来告诉 webpack 如何将编译后的文件输出到磁盘

  • 单入口 只需指定 filename, path 即可
  • 多入口 通过占位符确保文件名称的唯一 filename:'[name].js'

# 核心概念 - loaders

webpack 本身只支持 JS 和 JSON 两种文件类型,通过 loader 去支持其他文件类型并把它们转化成有效的模块添加到依赖图中

Loaders 本身是一个函数 接收源文件作为参数 返回转换结果

# 常见 Loaders 有哪些?

名称 描述
babel-loader 转换 ES6、ES7 等新特性语法
css-loader .css 文件的加载和解析
less-loader less 文件转换成 css
ts-loader ts 转换成 js
file-loader 进行图片、字体等打包
raw-loader 将文件以字符串的形式导入
thread-loader 多进程打包 JS CSS

# Loaders 用法

  • test指定匹配规则
  • use指定使用的 loader 名称
module: {
  rules: [{ test: /\.txt$/, use: "raw-loader" }];
}
1
2
3

# 核心概念 - plugins

用于 bundle 文件的优化,资源管理和环境变量注入,作用于整个构建过程。

由于插件可以携带参数/选项,你必须在 webpack 配置中,向 plugins 属性传入 new 实例。

用 loader 完成不了的事情可以用 plugins 完成

# 常见 Loaders 有哪些?

名称 描述
CommonsChunkPlugin 将 chunks 相同的模块代码提取成公告 js
CleanWebpackPlugin 清理构建目录
ExtractTextWebpackPlugin 将 CSS 从 bundle 里提取成一个独立 CSS 文件
CopyWebpackPlugin 将文件或者文件夹拷贝到构建的输出目录
HtmlWebpackPlugin 创建 html 文件去承载输出的 bundle
UglifyjsWebpackPlugin 压缩 JS
ZipWebpackPlugin 将打包出的资源生成一个 zip 包

# 核心概念 - mode

用来指定当前的构建环境是 production development 还是 none

# 解析 ES6

  • 使用 babel-loader 配置文件为.babelrc

npm i @babel/core @babel/preset-env babel-loader -D

// .babelrc
{
  "preset":["@babel/preset-env"]
}
1
2
3
4

# 解析 CSS

  • css-loader 用于加载.css 文件并转换成 commonjs 对象
  • style-loader 将样式通过<style>标签插入到 head 中

因为 loader 是链式调用(从右往左)所以要先写 style 后写 css

{
  test:/.css$/,
  use:[
    'style-loader',
    'css-loader'
  ]
}
1
2
3
4
5
6
7

# 解析 Less 和 Sass

{
  test:/.sass$/,
  use:[
    'style-loader',
    'css-loader',
    'sass-loader'
  ]
}
1
2
3
4
5
6
7
8

# 解析图片 字体

  • 使用 file-loader

    • 图片:test:/.(png|jpg|gif|jpeg)$/
  • 使用 url-loader

    • 可以设置较小资源自动 base64
    • 内部是使用 file-loader
    • 传递参数options:{limit:10240}小于 10kb 自动 base64

# webpack 中的文件监听

文件监听是在源码发生变化时自动重新构建出新的输出文件

两种开启监听模式的方法:

  • 启动 webpack 命令时带上--watch参数
    • script 中加入"watch":"webpack --watch"
    • 唯一缺陷:每次需要手动刷新浏览器
  • 配置 webpack.config.js 中设置watch:true

# 原理分析

轮询判断文件最后编辑时间是否发生变化
如发生变化并不会立刻告诉监听者,而是缓存等待 aggregateTimeout

modelu.export = {
  watch: true,
  watchOptions: {
    ignored: /node_modules/, //可提高性能
    aggregateTimeout: 300, //监听到变化时等300ms再去执行
    poll: 1000 //轮询 每秒1000次
  }
};
1
2
3
4
5
6
7
8

# 热更新 webpack-dev-server

  • WDS 不需要刷新浏览器
  • WDS 不输出文件 而是放在内存中
  • 和 HotModuleReplacementPlugin 配合使用

脚本:"dev":"webpack-dev-server --open"

plugins: [
  new webpack.HotModuleReplacementPlugin()
],
devServer: {
  contentBase:'./dist',
  hot:true
}
1
2
3
4
5
6
7

使用 webpack-dev-middleware 也可以进行热更新但是需要设置 node 服务器

# 原理分析

# 文件指纹

文件指纹就是打包后输出的文件名的后缀

# 如何生成文件指纹

  • Hash:和整个项目的构建相关,只要项目有修改,整个项目的 hash 值就会改变
  • Chunkhash:和 webpack 打包的 chunk 有关,不同的 entry 会生成不同的 chunkhash 值
  • Contenthash:根据文件内容来定义 hash,文件内容不变则不变

# JS 的文件指纹设置

output: {
  filename: "[name]_[chunkhash:8].js";
}
1
2
3

# CSS 的文件指纹设置

该 plugin 会把 css 提取成独立文件 与 style-loader 冲突

plugins: [
  new MiniCssRxtractPlugin({
    filename: `[name]_[contenthash:8].css`
  })
];
1
2
3
4
5

# 图片 的文件指纹设置

use: [
  {
    loader: "file-loader",
    options: {
      name: "img/[name]_[hash:8].[ext]"
    }
  }
];
1
2
3
4
5
6
7
8

将生产和开发环境配置分开分别为 webpack.prod.js 和 webpack.dev.js
并在脚本处添加 --config [配置文件]

# 代码压缩

# JS 文件的压缩

内置了 uglifyjs-webpack-plugin

# CSS 文件的压缩

  • 使用 optimize-css-assets-webpack-plugin
  • 同时使用 cssnano

# html 文件的压缩

修改 html-webpack-plugin 设置压缩参数
通常一个页面对应一个 plugin

# webpack 进阶用法

# 自动清理构建目录产物

每次构建的时候不会清理目录,造成构建的输出目录 output 文件越来越多

  • 通过 npm scripts 清理目录

    • rm -rm ./dist && webpack
    • rimraf ./dist && webpack
  • 使用 clean-webpack-plugin ✔️

# PostCSS 插件 autoprefixer 自动补齐 CSS3 前缀

# 为什么 CSS3 属性需要前缀

浏览器内核不统一 如-moz -webkit

# 如何自动补齐

使用 autoprefixer 插件(后置处理)根据 caniuse.com 原则

通常和 postcss-loader 一起使用

# 移动端 CSS px 自动转 rem

# 浏览器的分辨率

  • 之前使用媒体查询实现响应式布局 缺陷:需要编写多套适配样式代码
  • 现在使用 rem:font-size of the root element

使用 px2rem-loader 配合 lib-flexible 库(根据分辨率自动计算根元素 font-size)

# 静态资源内联

# 资源内联的意义

  • 代码层面

    • 页面框架的初始化脚本
    • 上报相关打点
    • css 内联避免页面闪动
  • 请求层面:减少 HTTP 网络请求数

    • 小图片或者字体内联 url-loader

# JS 和 HTMl 内联

  • raw-loader
<head>
  ${require('raw-loader!./meta.html')}
  <title>document</title>
  <script>
    ${require('raw-loader!babel-loader!../../node_modules/lib-flexible/flexible.js')}
  </script>
</head>
1
2
3
4
5
6
7

# CSS 内联

  • style-loader options 设置singleton:true
  • html-inline-css-webpack-plugin ✔️

# 多页面应用打包通用方案

# 多页面应用(MPA)概念

每一次页面跳转时后台服务器都会给返回一个新的 html 文档

  • 每个页面解耦
  • 对 SEO 更加友好

# 多页面打包基本思路

每个页面对应一个 entry ➕ 一个 html-webpack-plugin 缺点:每次新增或删除页面需要修改 webpack 配置

# 多页面打包通用方案 ✔️

动态获取 entry 并设置 html-webpack-plugin 数量

  • 利用 glob.sync
    • 命名遵循: ./src/页面名/index.js
    • entry:glob.sync(path.join(__dirname,'./src/*/index.js'))

# 使用 sourceMap

module.exports = {
  plugins:[],
  devtool: "source-map";
}
1
2
3
4

通过 sourceMap 定位到源码

  • 开发环境开启,线上环境关闭
  • 线上排查问题的时候可以将 sourcemap 上传到错误监控系统

# sourceMap 关键字

  • eval 使用 eval 包裹模块代码
  • source map 产生.map 文件
  • cheap 报错定位 不包含列信息
  • inline 将.map 作为 DataURI 嵌入,不单独生成.map 文件 内联到 JS 文件
  • module 包含 loader 的 sourceMap

# 提取公共资源

# 基础库分离 react/vue

  • 思路:将 react/react-dom 基础包通过 cdn 引入 不打入 bundle 中
  • 方法:使用 html-webpack-externals-plugins

# 利用 SplitChunksPlugin 进行公共脚本分离

  • wp4 内置 代替 CommonsChunkPlugin
  • 参数说明
    • async异步引入的库进行分离(默认)
    • initial同步引入的库进行分离
    • all所有引入的库进行分离

# 利用 SplitChunksPlugin 分离基础包

  • test 匹配需要分离的包

# 利用 SplitChunksPlugin 分离页面公共文件

  • minChunks 设置最小引用次数
  • minSize 分离的包体积大小

# tree shaking 摇树优化

  • 概念:只把用到的方法打入 bundle,没用到的方法会在 uglify 阶段被擦出掉
  • 使用:webpack 默认支持,在.babelrc 里设置 modules:false 即可
    • production mode 默认开启
  • 要求:必须是 ES6 语法

# DCE

  • 代码不会被执行
  • 代码执行的结果不会被用到
  • 代码只会影响死变量(只写不读)

# tree shaking 原理

利用 ES6 模块特点

  • 只能作为模块顶层语句出现
  • import 的模块名只能是字符串常量
  • import binding 是 immutable 的

uglify 阶段删除无用代码

# scope hoisting

构建后的代码存在大量闭包,将导致:

  • 大量闭包包裹代码,体积增大
  • 运行代码时创建的函数作用域变多,内存开销变大

# 模块转换分析

# scope hoisting 原理

  • 原理:将所有模块代码按照引用顺序放在一个函数作用域里,然后适当重命名一些变量以防止变量名冲突
  • 对比:通过 scope hoisting 可以减少函数声明代码和内存开销

# scope hoisting 使用

  • production mode 默认开启
  • 必须是 ES6 语法

# 代码分割和动态 import

# 代码分割的意义

对于大的 web 应用来说,将所有的代码都放在一个文件中显然是不够有效的,特别适合当你的某些代码块是在某些特殊的时候才会被用到。webpack 有一个功能就是将你的代码库分割成 chunks(语块)当代码运行到需要他们的时候在进行加载。

  • 适用的场景:
    • 抽离相同代码到一个共享块
    • 脚本懒加载,使得初始下载的代码更小

# 懒加载 JS 脚本的方式

  • CommonJS : require.ensure
  • ES6 : 动态 import(需要 babel 转换)
    • 安装 babel 插件npm install @babel/plugin-syntax-dynamic-import --save-dev
    • babelrc 中新增 plugin

# 使用 ESLint

# ESLint 如何执行落地

  • webpack 与 CI/CD 集成

在 build 前增加 lint pipeline