深入浅出webpack

第一章入门

1.1 前端的发展

1.1.1模块化

模块化是指将一个复杂的系统分解为多个模块 以方便编码
1、 CommonJS 是一种被广泛使用的JavaScript 模块化规范,其核心思想是通过require方法来同步加载依赖的其他模块,通过module.exports导出需要暴露的接口

  • 优点
    • 代码可复用于Node.js 环境下并运行,例如做同构应用(前后端通用的代码)
    • 通过Npm 发布的很多第三方模块都采用了CommonJS 规范
  • 缺点
    • 这样的代码无法直接运行在浏览器环境下,必须通过工具转换成标准的ES5

2、 AMD 也是一种JavaScript模块化规范,与CommonJS 最大的不同在于,它采用了异步的方式去加载依赖的模块。AMD规范主要用于解决针对浏览器环境的模块化问题,最具代表性的实现是requirejs,使用 define定义模块,使用require导入模块

  • AMD优点
    • 可在不转换代码的情况下直接在浏览器中运行
    • 可异步加载依赖
    • 可并行加载多个依赖
    • 代码可运行在浏览器环境和Node.js环境下
  • AMD缺点
    • JavaScript 运行环境没有原生支持AMD,需要先导入实现了AMD 的库后才能正常使用

3、ES6 模块化

  • ES6 模块化是国际标准化组织ECMA 提出的JavaScript 模块化规范,它在语言层面上实现了模块化。浏览器厂商和Node都宣布要原生支持该规范。它将逐渐取代CommonJS 和AMD 规范,成为浏览器和服务器通用的模块解决方案。使用import导入模块, export导出模块
    • 缺点
      • 目前无法直接运行在大部分JavaScript运行环境下,必须通过工具转换成标准的ES5 后才能正常运行

4、样式文件中的模块化

1.1.2 新框架

  1. React
    React框架引入了JSX 语法到JavaScript 语言层面中,可以更灵活地控制视图的渲染逻辑。
  2. Vue
    Vue ( https: //vuejs.org )框架将与一个组件相关的HTML模板、JavaScript 逻辑代码、css样式代码都写在一个文件里,这非常直观。

3 . Angular2
Angular2 推崇采用Typescript 语言开发应用,并且可以通过注解的语法描述组件的各种属性。

1.1.3 新语言

  1. ES6
    ECMAScript 6.0(简称ES6 )是JavaScript 语言的下一代标准。它在语言层面为JavaScript引入了很多新语法和API ,使得JavaScript 语言可以用来编写复杂的大型应用程序
  2. Typescript
    TypeScript 是JavaScript 的一个超集,由Microsoft 开发并开源,除了支持ES6 的所有功能,还提供了静态类型检查
    3 . Flow
    Flow也是JavaScript 的一个超集,它的主要特点是为JavaScript 提供静态类型检查,和Typescript 相似但更灵活,可以让我们只在需要的地方加上类型检查。
    4 . scss
    SCSS ( http ://s ass-lang .com )可以让我们用程序员的方式写css 。它是一种css 预处理器,其基本思想是用和css 相似的编程语言写完后再编译成正常的css 文件。

1.2常见的构建工具及对比

概述

构建就是将源代码转换成可执行的JavaScript 、css 、html代码,包括如下内容。

  • 代码转换:将TypeScript 编译成JavaScript 、将scss 编译成css 等。
  • 文件优化:压缩JavaScript 、css 、html代码,压缩合并图片等。
  • 代码分割:提取多个页面的公共代码,提取首屏不需要执行部分的代码让其异步加载。
  • 模块合并:在采用模块化的项目里会有很多个模块和文件,需要通过构建功能将模块分类合并成一个文件。
  • 自动刷新:监昕本地源代码的变化,自动重新构建、刷新浏览器。
  • 代码校验:在代码被提交到仓库前需要校验代码是否符合规范,以及单元测试是否通过。
  • 自动发布:更新代码后,自动构建出线上发布代码井传输给发布系统。

构建其实是工程化、自动化思想在前端开发中的体现,将一系列流程用代码去实现,让代码自动化地执行这一系列复杂的流程。构建为前端开发注入了更大的活力,解放了我们的生产力。

常用的构建工具

  • webpack、grunt、gulp、browserify、yeoman、FIS3、Rollup、Parcel

Webpack是一个打包模块化JavaScript 的工具,在Webpack 里一切文件皆模块,通过Loader 转换文件,通过Plugin 注入钩子,最后输出由多个模块组合成的文件。Webpack 专注于构建模块化项目。

1.3安装与使用

1.3.3 使用webpack

构建一个最简单的webpack项目步骤如下:

1、新建一个web项目
在node环境下,新建一个目录,再进入项目根目录,初始化最简单的采用了模块化开发的项目

npm init -y

2、安装webpack

npm i webpack@4.44 -D

运行安装在项目中的webpack,有以下两种方式

  • 在项目根目录下对应的命令行里通过node rnodules/.bin/webpack 运行
    Webpack 的可执行文件
  • 在Npm Script 里定义的任务会优先使用本项目下的Webpack,在package.json文件中
scripts: {
  start: webpack --config webpack.config.js
}

3、实例:构建一个采用了CommonJS 模块化编写的项目,该项目中的某个网页会通过JavaScript 显示Hello Webpack 。
详见 F:\学习\webpack\allDemo\demo1.3.3

1.4使用Loader

1、在demo1.3.3项目中加入css文件
详见 F:\学习\webpack\allDemo\demo1.4
2、步骤

  • 新建main.css
  • 将main.css引入main.js
require('./main.css)
  • 修改配置文件
 module: {
        rules: [
            {
                test: /\.css$/,
                use: ['style-loader', 'css-loader']
            }
        ]
    }
  • 安装style-loader,css-loader

1.5使用Plugin

1、在demo1.4项目中单独提取css,并且对css进行压缩
详见 F:\学习\webpack\allDemo\demo1.5
2、详细步骤

  • 安装单独提取css的插件mini-css-extract-plugin和压缩css文件插件optimize-css-assets-webpack-plugin
  • 修改配置文件
const miniCssExtractPlugin = require('mini-css-extract-plugin')
const optimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin')
 module: {
        rules: [
            {
                test: /\.css$/,
                use: [miniCssExtractPlugin.loader, 'css-loader']
            }
        ]
    },
plugins: [
     new miniCssExtractPlugin(
            {
                filename: `./css/[name].css?v=[contenthash:4]`
            }
     ),
    new optimizeCssAssetsWebpackPlugin(
            {
            assetNameRegExp: /\.css(.*)$/,
            cssProcessor: require('cssnano'),
            cssProcessorOptions: {
              safe: true,
              discardComments: {
                removeAll: true
              }
            },
            canPrint: true
          }
        )
    ]

1.6使用DevServer

1、步骤

  • 安装webpack-dev-server
  • 启动 webpack-dev-server --config webpack.config.js
  • 访问localhost
    2、注意点
    webpack-dev-server与webpack-cli最新版不兼容,要安装webpack-cli@3
    3、配置模块热替换和source map
  • 模块热替换: 在不重新加载整个网页的情况下,通过将被更新过的模块替换老的模块,再重新执行一次来实现实时预览
  • source Map:查看源码
  • 具体配置
devtool: 'source-map', // 可选模式有七种,常用source-map和eval-source-map
devServer: {
        hot: true
    }
  • 查看源码:控制台sources下面的webpack文件夹下面有个.文件夹中

第二章配置

2.1 entry

2.1.1 context

Webpack 在寻找相对路径的文件时会以context 为根目录, context 默认为执行启动Webpack 时所在的当前工作目录,通常为'',也可以自定义

const path = require('path')
module.exports = {
    context: path.resolve(__dirname,'app'),
}

2.1.2 entry的类型

     // entry的属性值可以是string\array\Object
    entry: './main.js'
    // array 类型,则搭配output.library 配置项使用时,只有数组里的最后一个入口文件的模块会被导出
    entry: ['./main.js', './entry.js']
    // 配置多个入口,每个入口生成一个Chunk
    entry: {
        a: './entry1.js',
        b: './entry2.js',
        c: './entry3.js'
    }

2.1.3 chunk的名称

Webpack 会为每个生成的Chunk 取一个名称, Chunk 的名称和entry的配置有关。

  • 如果entry 是一个string 或array ,就只会生成一个Chunk ,这时Chunk 的名称是main
  • 如果entry 是一个object ,就可能会出现多个Chunk ,这时Chunk 的名称是object 键值对中键的名称。

2.2 output

output: {
        //filename 配置输出文件的名称,为string 类型
        filename: 'bundle.js' ,// 如果只有一个输出文件,则可以将它写成静态不变的:
        filename: '[name].js',// 在有多个Chunk 要输出时,就需要借助模板和变量了,内置变量有name\id\hash\chunkhash
        // chunkFilename用于指定在运行过程中生成的Chunk 在输出时的文件名称
        chunkFilename:'common.js', 
        // path 配置输出文件存放在本地的目录,必须是string 类型的绝对路径
        path: path.resolve(__dirname, 'dist'),
        // publicPath 配置发布到线上资源的URL 前缀
        publicPath: 'https://cdn.example.com/assets/',
        // libraryTarget 配置以何种方式导出库,支持var/commonjs2/this/window/global
        libraryTarget: '',
        //library 配置导出库的名称
        library: 'LibraryName',
        // libraryExport 配置要导出的模块中哪些子模块需要被导出
        libraryExport: 'a'
    }

2.3 module

配置处理模块的规则

 module: {
        rules: [
            {
                test: /\.css$/,
                use: [
                    'style-loader',
                    {
                        loader: 'css-loader',
                        options:{
                            miniminze: true
                        },
                        /**
                         * 一组Loader 的执行顺序默认是从右到左执行的
                         * 通过enforce 选项可以将其中一个Loader 的执行顺序放到最前或者最后
                         * post的含义是将该Loader 的执行顺序放到最后
                         * pre代表将Loader 的执行顺序放到最前面
                         */
                        enforce: 'post'
                    }
                ],
                // 只命中src目录里的JavaScript文件,加快Webpack 的搜索速度
                include: path.resolve(__dirname, 'src'),
                // 排除node modules目录下的文件
                exclude:path.resolve(__dirname, 'node modules')
            }
        ],
        /**
         * 1、noParse 配置项可以让Webpack 忽略对部分没采用模块化的文件的递归解析和处理
         * 这样做的好处是能提高构建性能
         * 2、noParse 是可选的配置项,类型需要是RegExp 、[ RegExp )、fun ction 中的一种
         */
        noParse: /jquery|chartjs/,
        // parser 属性可以更细粒度地配置哪些模块语法被解析、哪些不被解析
        parser: {
            amd: false, //禁用AMD
            commonjs : false, //禁用CommonJS
            system : false, //禁用SystemJS
            harmony : false, //禁用ES6 import/export
            requireinclude: false, //禁用require .in cl ude
            requireEnsure: false, //禁用require . ens ur e
            requireContext: false, //禁用require.context
            browserify: false, //禁用browserify
            requireJs : false, //禁用requirejs
        }
    }

2.4 resolve

Webpack 在启动后会从配置的入口模块出发找出所有依赖的模块, Resolve 配置Webpack如何寻找模块所对应的文件。

resolve: {
        /**
         * alias 配置项通过别名来将原导入路径映射成一个新的导入路径
         * alias 还支持通过$符号来缩小范围到只命中以关键字结尾的导入语句
         */
        alias: {
            components: './src/components/',
            'react$': '/path/react.min.js'
        },
        /**
         * 有一些第三方模块会针对不同的环境提供几份代码
         * Webpack 会根据mainFields 的配置去决定优先采用哪份代码
         */
        mainFields: ['jsnext:main', 'browser', 'main'],
        /**
         * 在导入语句没带文件后缀时, Webpack 会自动带上后缀后去尝试访问文件是否存在。
         * resolve.extensions 用于配置在尝试过程中用到的后缀列表            
         */
        extensions: ['.js', '.json'],
        // 配置Webpack 去哪些目录下寻找第三方模块,默认只会去node modules 目录下寻找
        modules: ['./ src/cornponents', 'node modules'],
        // 配置描述第三方模块的文件名称
        descriptionFiles: ['package.json'],
        // 是否所有的导入语句都带后缀
        enforceExtension: false,
        // 功能与enforceExtension类似,只对node_modules下的模块生效,通常与enforceExtension配合使用
        enforceModuleExtension: false
    }

2.5 plugin

const miniCssExtractPlugin = require('mini-css-extract-plugin')
 plugins: [
        new miniCssExtractPlugin(
            {
                filename: `./css/[name].css?v=[contenthash:4]`
            }
        )   
    ]

2.6 Devserver

2.6.6 host

devServer.host 配置项用于配置DevServer 服务监听的地址,只能通过命令行参数传入。例如,若想让局域网中的其他设备访问自己的本地服务,则可以在启动Dev Server 时带上--host 0.0.0 。host 的默认值是127.0.0.1 ,即只有本地可以访问Dev Server 的HTTP服务。

2.6.9 disableHostCheck

devServer.disableHostCheck 配置项用于配置是否关闭用于DNS 重新绑定的HTTP请求的HOST 检查。
DevServer 默认只接收来自本地的请求, 关闭后可以接收来自任意HOST的请求。
它通常用于搭配-- host 0.0.0 使用, 因为想让其他设备访问自己的本地服务,但访问时是直接通过IP 地址访问而不是通过HOST 访问,所以需要关闭HOST 检查。

  devServer: {
        hot: true,// 开启模块热替换
        // inline 用于配置是否将这个代理客户端自动注入将运行在页面中的Chunk 里,默认自动注入
        inline: true,
        // historyApiFallback用于方便地开发使用了HTML5 History API的单页应用
        historyApiFallback: true,
        // contentBase 配置DevServer HTTP 服务器的文件根目录
        contentBase : path.join( __dirname,'public'),
        // headers 配置项可以在HTTP 响应中注入一些HTTP 响应头
        headers: {
            'X-foo':  'bar'
        },
        // 端口号
        port: '8081',
        allowedHosts: [
            'host . com',
            'sub.host.com',
            // host2.com 和所有的子域名*.host2.com 都将匹配
            '.host2 . com'
        ],
        // 搭配--host 0.0.0使用
        disableHostCheck: true,
        // 使用https
        https: true,
        // 配置客户端的日志等级,这会影响到我们在浏览器开发者工具控制台里看到的日志内容
        clientloglevel: 'warning',
        // compress 配置是否启用Gzip 压缩,为boolean 类型,默认为false
        compress: true,
        // open 用于在Dev Server 启动且第一次构建完时,自动用我们的系统的默认浏览器去打开要开发的网页
        open: true
    }

2.7 其他配置项

module.exports = {
   // target 配置项可以让Webpack 构建出针对不同运行环境的代码,可选值:node/web/async-node/webworker/electron-main/electron-renderer
    target: 'node',
    devtool: 'source-map',
    watch: true,
    externals : {
        // 将导入语句里的jquery替换成运行环境里的全局变量jQuery
        jquery: 'jQuery'
    },
    // ResolveLoader 用来告诉Webpack如何去寻找Loader
    resolveLoader: {
        // 去哪个目录下寻找Loader
        modules: ['node modules'] ,
        // 入口文件的后缀
        extensions :['.js','. json'] ,
        // 指明入口文件位置的字段
        mainFields: ['loader','main']
    }
}

2.8 整体配置结构

第三章实战

3.1 使用ES6语言

概述

将es6转换成es5做了以下两件事情

  • 将新的ES6 语法用ES5 实现
  • 为新的API 注入polyfill(polyfill本意是聚酯纤维填充,这里用作补丁或者兼容插件讲,用来兼容原本一些不支持的属性和方法)

3.1.3 认识babel

在Babel 执行编译的过程中,会从项目根目录下的.babelrc文件中读取配置

/**
        1、presets属性告诉Babel要转换的源码使用了哪些新的语法特性,一个Presets对一组
            新语法的特性提供了支持,多个Presets 可以叠加。Presets 其实是一组P lugins 的集合,每个
            Plugin 完成一个新语法的转换工作
        2、可选值: 
            1)  es2015/es2016/es2017/env(包含当前所有ECMAScript 标准里的最新特性)
            2)  被社区提出来的但还未被写入ECMAScript 标准里的特性: stage0/stage1/stage2/stage3/stage4
            3))用于支持一些特定应用场景下的语法的特性,和ECMAScript 标准没有关系
    */
    "presets": [
        "env",
        [
            "es2015",
            {
                "modules": false
            }
        ],
        "stage-2",
        "react"
    ],
    /** 
       * plugins属性告诉Babel要使用哪些插件,这些插件可以控制如何转换代码
    */
    "plugins": [
        [
            "transform-runtime", // Babel官方提供的一个插件,作用是减少冗余的代码
            {
                "polyfill": false
            }
        ]
    ]
}

实现步骤

  1. 新建.babelrc文件,内容如下
{
    "presets": ["env"]
}
  1. 安装插件
  npm i -D babel-core@6 babel-loader@7 babel-preset-env

注释:

  • babel-loader 8.x对应babel-core 7.x
  • babel-loader 7.x对应babel-core 6.x
  1. 通过loader接入Babel
module.exports = {
  module: {
      rules: [
        {
            test: /\.js$/,
            use: ['babel-loader'],
            exclude:path.resolve(__dirname, 'node modules')
        }
      ]  
}
}

3.4 使用sass

实现步骤

  1. 安装loader
  npm i node-sass sass-loader@10 style-loader css-loader

注释:sass-loader的版本不能太高,依赖于node-sass
2.修改配置文件,增加loader配置

module: {
        rules: [
           ......
            {
                test: /\.scss/,
                use: ['style-loader', 'css-loader', 'sass-loader']
            }
          .......
        ]
    }

3.5 认识PostCss

概述

PostCSS 是一个用 JavaScript 工具和插件转换 CSS 代码的工具,和scss 的不同之处在于它可以通过插件机制灵活地扩展其支持的特性,而不像scss 那样语法是固定的。PostCSS 的用处非常多,包括向css 自动加前缀、使用下一代css 语法等

常用插件

  • postcss-import: 合并样式表
  • postcss-url: 用于转换 url ( ),inline 或者复制资产
  • cssnano : 压缩代码

使用步骤

1.安装loader

npm i -D postcss-loader@4 style-loader css-loader

2.使用autoprefixer,需要在package.json中添加browserslist配置项

 "browserslist": [
    "> 1%",
    "last 2 versions",
    "not ie <= 8"
  ]

3.安装需要的插件,以autoprefixer为例

npm i -D autoprefixer

4.两种配置postcss的方案

  • 新建postcss.config.js,内容如下
module.exports = {
    plugins: [
        [
            'autoprefixer'
        ]
    ]
}
  • 修改webpack.config.js配置项
    有postcss.config.js文件时
    module: {
        rules: [
            {
                test: /\.css$/,
                use: [
                    'style-loader', 
                    'css-loader',
                    'postcss-loader'
                ]
            }
        ]
         
    }

没有postcss.config.js文件时

rules: [
            {
                test: /\.css$/,
                use: [
                    'style-loader', 
                    {
                        loader: 'css-loader',
                        options: { importLoaders: 1 },
                    },
                    {
                        loader: 'postcss-loader',
                        options: {
                            postcssOptions:{
                                plugins: [
                                    [
                                        'autoprefixer'
                                    ]
                                ]
                            }
                        }
                    }
                ]
            }
        ]

3.7 使用vue框架

步骤

1.安装loader

npm i -S vue
npm i -D vue-loader css-loader vue-tamplate-compiler

2.配置webpack

  module: {
        rules: [
            {
                test: /\.vue$/,
                use: ['vue-loader']
            }
        ]
    }

3.实例:vue单文件组件使用方法如下
index.html

<body>
    <div id="app"></div>
    <script src="./dist/bundle.js"></script>
</body>

App.vue

<template>
    <h1>{{ msg }}</h1>
</template>
<script>
export default {
    data() {
        return {
            msg: 'Hello App'
        }
    }
}
</script>
<style>
h1 {
    color: red;
}
</style>

main.js

import Vue from 'vue'
import App from './App.vue'

new Vue({
    el: '#app',
    render: h => h(App)
})

3.9 为单页应用生成html

步骤

在上述实例中添加html-webpack-plugin
1.安装

npm i html-webpack-plugin@4

注意:html-webpack-plugin最新版本不兼容webpack4
2、修改配置文件

const HtmlPlugin = require('html-webpack-plugin')
 plugins: [
        new HtmlPlugin({
            filename: 'index.html',
            template: './template.html'
        })
    ]

3.13 构建npm模块

3.13.1 认识npm

Npm (https://www.npmjs.com)是目前最大的JavaScript 模块仓库,里面有全世界的开发者上传的可复用模块。虽然在大多数情况下我们都是这些开放模块的使用者,但我们也许会成为贡献者,会开发一个模块上传到Npm 仓库。
发布到Npm 仓库的模块有以下几个特点:

  • 在每个模块根目录下都必须有一个描述该模块的package.json 文件。该文件描述了模块的入口文件是哪个,该模块又依赖哪些模块等
  • 模块中的文件以JavaScript 文件为主,但不限于JavaScript 文件
  • 模块中的代码大多采用模块化规范,,目前支持比较广泛的是CommonJS 模块化规范,上传到Npm 仓库的代码最好遵守该规范。

3.14.2 认识Service Workers

3.16 检查代码

3.16.1 代码检查具体是做什么的

3.16.2 怎么做代码检查

1 . 检查JavaScript
ESlint C https: //eslint.org )
2、检查Typescript
TSLint ( https://palantir.github . io/tslint/)
3.检查css
style lint ( https://stylelint.io

3.16.3 结合Webpack 检查代码

1.eslint-loader ( https://github.com/MoOx/eslint-loader

module.exports = {
    module: {
        rules: [
              {
                  test: /\.js$/,
                   use: ['eslint-loader'],
 // 将eslint-loader 的执行顺序放在最前面,防止其他Loader将处理后的代码交给es lint-loader去检查
                    enforce: 'pre'  
              }
         ]
    }
}

3.17 通过Node.js API 启动Webpack

3.18 使用Webpack DevMiddleware

通过webpack-dev-middleware 能够将DevServer 集成到现有的HTTP 服务器中,让现有的HTTP 服务器能返回Webpack构建出的内容,而不是在开发时启动多个HTTP 服务器。这特别适用于后端接口服务采用Node.js 编写的项目

3.19 加载图片

步骤

1.安装loader

npm i -D file-loader

2.修改配置文件

  {
                test: /\.png$/,
                use: [ {
                    loader: 'file-loader',
                    options: {
                        esModule: false
                    }
                }]
            }

或者使用url-loader(小图)结合file-loader(大图)

 {
                test: /\.png$/,
                use: [ {
                    loader: 'url-loader',
                    options: {
                        limit: 1024 * 30,
                        fallback: 'file-loader',
                        esModule: false
                    }
                }]
            }

3.21 加载Source Map

第四章优化

概述

1.优化开发体验

  • 优化构建速度
  • 优化使用体验

2.优化输出质量

  • 减少用户感知到的加载时间
  • 提升流畅度

4.1 缩小文件的搜索范围

4.1.1 优化Loader 配置

在使用Loader 时,可以通过test 、include 、exclude 三个配置项来命中Loader 要应用规则的文件

4 .1.2 优化resolve.modules、resolve.mainFields、resolve.alias、resolve.extensions、module. noParse配置

 module.exports = {
      resolve : {
        // 使用绝对路径指明第三方模块存放的位置,以减少搜索步骤
          modules: [path.resolve( __dirname ,'node modules ')],
          mainFields: ['main'],
          alias: {
            'react': path.resolve( __dirname,’./nodemodules/react/dist/react.min.js ’),
          'src': path.resolve(__dirname, 'src')
          },
        extensions:[ '.js', '.json']
      },
    module: {
        noParse: [/react\.min\.js$/]
    }
}

4.2 使用DllPlugin

4.2.1 认识DLL

以.dll为后缀名的文件,叫做动态链接库,在一个动态链接库中可以包含为其他模块调用的函数和数据要给Web 项目构建接入动态链接库的思想,需要完成以下事情。

  • 将网页依赖的基础模块抽离出来,打包到一个个单独的动态链接库中,一个动态链接库中可以包含多个模块。
  • 当需要导入的模块存在于某个动态链接库中时,这个模块不能被再次打包,而是去动态链接库中获取。
  • 页面依赖的所有动态链接库都需要被加载。

4.2.2 接入Webpack

Webpack 己经内置了对动态链接库的支持,需要通过以下两个内置的插件接入。

  • DllPlugin 插件: 用于打包出一个个单独的动态链接库文件
  • DllReferencePlugin 插件:用于在主要的配置文件中引入DllP!ugin 插件打包好的动态链接库文件。

具体实现方式

1.构建出动态链接库文件

const path = require('path')
const dllPlugin = require('webpack/lib/DllPlugin')
module.exports = {
    entry: {
        // 将React 相关的模块放到一个单独的动态链接库中
        react: ['react', 'react-dom'],
        // 将项目需要所有的polyfill 放到一个单独的动态链接库中
        polyfill: ['core-js/fn/object/assign', 'core-js/fn/promise', 'whatwg-fetch']
    },
    output: {
        // 输出的动态链接库的文件名称,[name]代表当前动态链接库的名称
        filename: '[name].dll.js',
        path: path.resolve(__dirname, 'dist'),
        // 存放动态链接库的全局变量名称,例如对于react 来说就是_dll_react,之所以在前面加上dll ,是为了防止全局变量冲突
        library: '_dll_[name]'
    },
    plugins: [
        new dllPlugin({
            name: '_dll_[name]',
            path: path.join(__dirname, 'dist', '[name].manifest.json')
        })
    ]
}

2.使用动态链接库文件

const path = require('path')
const DllReferencePlugin = require('webpack/lib/DllReferencePlugin')
module.exports = {
    entry: {
        main: './main.js'
    },
    output: {
        // 输出的动态链接库的文件名称,[name]代表当前动态链接库的名称
        filename: '[name].js',
        path: path.resolve(__dirname, 'dist')
    },
    module: {
        rules: [
            {
                test: /\.js$/,
                use: ['babel-loader'],
                exclude: path.resolve(__dirname, 'node_modules')
            }
        ]
    },
    plugins: [
        new DllReferencePlugin({
            manifest: require('./dist/react.manifest.json')
        }),
        new DllReferencePlugin({
            manifest: require('./dist/polyfill.manifest.json')
        })
    ]
}

3.执行构建

  • 第一步
webpack --config webpack dll.config.js
  • 第二步:在确保动态链接库存在时,才能正常编译入口执行文件。方法是执行webpack 命令

4.3.1 使用HappyPack

webpack自带插件,用于分解任务和管理线程

4.4 使用ParallelUglifyPlugin

概述

由于压缩JavaScript 代码时,需要先将代码解析成用Object 抽象表示的AST 语法树,再去应用各种规则分析和处理AST,所以导致这个过程的计算量巨大, 耗时非常多。

实现方式

当Webpack 有多个JavaScript 文件需要输出和压缩时,原本会使用uglifyJS 去一个一个压缩再输出,但是ParallelUglifyPlugin 会开启多个子进程,将对多个文件的压缩工作分配给多个子进程去完成,每个子进程其实还是通过UglifyJS 去压缩代码,但是变成了并行执行。所以ParallelUglifyPlugin 能更快地完成对多个文件的压缩工作。
注意:webpack4的js压缩,设置mode或者使用terser-webpack-plugin插件

4.5 使用自动刷新

4.5. 1 文件监昕

有以下两种方式

  • 在webpack.config.js中设置watch:true
  • 使用命令webpack --watch

4.5.2 自动刷新浏览器

使用webpack-dev-server模块去自动刷新页面
在使用webpack-dev-server 模块去启动webpack 模块时, webpack模块的监听模式默认会被开启。webpack 模块会在文件发生变化时通知webpack-de v-server模块。

1.自动刷新的原理

  • 借助浏览器扩展去通过浏览器提供的接口刷新
  • 向要开发的网页中注入代理客户端代码,通过代理客户端去刷新整个页面
  • 将要开发的网页装进一个iframe 中,通过刷新iframe 去看到最新效果
    DevServer 支持第2 、3 种方法, 第2 种是DevServer 默认采用的刷新方法
  1. 优化自动刷新的性能
    devServer.inline 配置项,它用来控制是否向Chunk 中注入代理客户端,默认会注入

4.6 开启模块热替换

webpack-dev-server --hot

4.7 区分环境

module.exports = {
    plugins: [
         new webpack.DefinePlugin({
                'process.env': {
// 注意,在定义环境变量的值时用JSON.stringify 包裹字符串的原因是,环境变量的值需要是一个由双引号包裹的字符串,而JSON.stringify('production')的值正好等于'"pro duction "'
                      NODE_ENV: JSON.stringify ( 'production')
            }
        })
    ]
}

4.8 压缩代码

4.8.1压缩js

optimization: {
        minimizer: [ // 用于配置 minimizers 和选项
            new UglifyJsPlugin({
                cache: true,
                parallel: true,
                sourceMap: true // set to true if you want JS source maps
            }),
            new OptimizeCSSAssetsPlugin({})
        ]
    }

4.8.2压缩es6

4.8.3压缩css

4.9CDN加速

4.9.1 什么是CDN

CDN(内容分发网络)的作用就是加速网络传输,通过将资源部署到世界各地,使用户在访问时按照就近原则从离其最近的服务器获取资源,来加快资源的获取速度。CDN 其实是通过优化物理链路层传输过程中的光速有限、丢包等问题来提升网速

4.9.2 接入CDN

要为网站接入CDN,需要将网页的静态资源上传到CDN 服务上,在服务这些静态资源时需要通过CDN 服务提供的URL 地址去访问。
为了让文件及时更新,成熟做法如下:

  • 针对HTML 文件:不开启缓存,将HTML 放到自己的服务器上,而不是CDN 服务上,同时关闭自己服务器上的缓存。自己的服务器只提供HTML 文件和数据接口
  • 针对静态的JavaScript 、css 、图片等文件:开启CDN 和缓存,上传到CDN 服务上,同时为每个文件名带上由文件内容算出的Hash值,例如上面的appa6976b6d.css 文件。带上Hash值的原因是文件名会随着文件的内容而变化,只要文件的内容发生变化,其对应的hash值也就会变化,它就会被重新下载,无论缓存时间有多长
    注意:
    1.对于浏览器限制:在同一时刻针对同一个域名的资源的并行请求有限制(大概4 个左右,不同的浏览器可能不同),可以将静态资源分散到不同的CDN 服务上
  1. 多域名解析会增加域名解析的时间,解决方法是在HTML HEAD 标签中加入<link rel="dns-prefetch ” href = ”//js.cdn.com ”>预解析域名
    延迟。

4.9.3 用Webpack 实现CDN的接入

构建需要实现以下几点:

  • 静态资源的导入URL需要变成指向CDN 服务的绝对路径的URL,而不是相对于HTML 文件的URL
  • 静态资源的文件名需要带上由文件内容算出来的Hash 值,以防止被缓存
  • 将不同类型的资源放到不同域名的CDN 服务上,以防止资源的并行加载被阻塞

核心设置publicPath

  • 在output . publicPath 中设置JavaScript 的地址
  • 在css-loader.publicPath 中设置被css 导入的资源的地址
  • 在WebPlugin.stylePublicPath 中设置css 文件的地址

4.10 使用Tree Shaking

4 . 10.1 认识Tree Shaking

Tree Shaking 可以用来剔除JavaScript 中用不上的死代码,它依赖静态的ES6 模块化语法,例如通过import 和export 导入、导出

4.10. 2 接入Tree Shaking

首先,为了将采用ES6 模块化的代码提交给Webpack ,需要配置Babel 以让其保留ES6模块化语句。修改.babelrc 文件如下:

  {
    "presets": [
        [
            "env",
            {
                "modules": false  // 关闭Babel的模块转换功能,保留原有的ES6模块化语法
            }
        ]
        
    ]
}

执行命令

wabpack --display -used -exports --optimize-minimize

4.11 提取公共代码

4 . 11.2 如何提取公共代码

  • 根据网站所使用的技术拢,找出网站的所有页面都需要用到的基础库,将它们提取到一个单独的文件base.js 中,该文件包含了所有网页的基础运行环境。
  • 在剔除了各个页面中被base.js 包含的部分代码后, 再找出所有页面都依赖的公共部分的代码,将它们提取出来并放到common.js 中。
  • 再为每个网页都生成一个单独的文件,在这个文件中不再包含base.js 和common.js 中包含的部分,而只包含各个页面单独需要的部分代码。
    注意:提取公共代码webpack4使用vendors或者commons参数设置

4 .12 分割代码以按需加载

在为单页应用做按需加载优化时, 一般采用以下原则

  • 将整个网站划分成一个个小功能,再按照每个功能的相关程度将它们分成几类
  • 将每一类合并为一个Chunk ,按需加载对应的Chunk
  • 按需加载用户首次打开网站时需要看到的画面所对应的功能,不要将其放到执行入口所在的Chunk 中,以减少用户能感知的网页加载时间。
  • 对于不依赖大量代码的功能点,例如依赖Cha rt.js 去画图表、依赖flv .js 去播放视频的功能点,可再对其进行按需加载。

4.12.3 用Webpack 实现按需加载

import(/* webpackChunkName :”show " */ ’./show’) . then ((show) => {
    show ( ’ Webpack ’)
})

4.13 使用Prepack

4.13.1 认识Prepack

Prepack 由Facebook 开源,采用了较为激进的方法:在保持运行结果一致的情况下,改变源代码的运行逻辑,输出性能更好的JavaScript 代码。实际上, Prepack 就是一个部分求值器,编译代码时提前将计算结果放到编译后的代码中, 而不是在代码运行时才去求值。
注意:暂时不推荐用于生产环境,不成熟

4.14 开启Scope Hoisting

4.14.1 认识Scope Hoisting

Scop e Hoisting 可以让Webpack 打包出来的代码文件更小、运行更快,它又被译作“作用域提升”,是在Webpack 3 中新推出的功能
webpack4的实现方法
https://www.cnblogs.com/cherryvenus/p/9808320.html

4.15 输出分析

4.15.2webpack-bundle-analyzer

4.16 优化总结

第五章原理

5.1 工作原理概括

5.1.1 基本概念

5.1.2 流程概括

5. 1.3 流程细节

5.2 输出文件分析

5.3 编写Loader

5.3.1 Loader 的职责

5.3.2 Loader 基础

5.3.3 Loader 进阶

5.3.5 加载本地Loader

5.3.6 实战

5.4 编写Plugin

Webpack 通过Plugin 机制让其更灵活,以适应各种应用场景。在webpack 运行的生命周期中会广播许多事件, Plugin 可以监昕这些事件,在合适的时机通过Webpack 提供的API改变输出结果。

5.4.4 实战

5.5 调试Webpack

附录

小技巧

1、vscode展开压缩代码
需要首先打开(ctrl+shift+p)命令行面板,选择format selection with,
然后里面选择Prettier -code formatter
2、linix命令

  • 删除文件:rm 【文件名】
  • 新建文件: touch 【文件名】
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,013评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,205评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,370评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,168评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,153评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,954评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,271评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,916评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,382评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,877评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,989评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,624评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,209评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,199评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,418评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,401评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,700评论 2 345

推荐阅读更多精彩内容