Webpack入门

145 阅读11分钟

1. webpack是什么?

是一种前端资源构建工具,一个静态模块打包器
构建工具:将浏览器不认识的web资源,比如sass,less,ts等通过编译处理
静态资源打包器:将项目中静态资源,通过引用关系,将所有静态模块打包成一个或者多个bundle输出

通过loader转换文件,通过plugin注入钩子,最后输出多个模块组合成的文件

2.webpack 5个核心 (webpack.config.js)

a:Entry(入口) 类型:string,array,object

   是指以哪个文件为入口起点开始打包,分析构建内部依赖图

 1.单入口,打包形成一个trunk,输出一个bundle文件,trunk的名称默认是main.js
     entry: "./src/index.js", 

 2.array方式:多入口
     entry: ["./src/index.js", "./src/test.js"], 

 3.object:多入口 有几个trunk就输出几个bundle
     entry:{                                    
        index:"./src/index.js",
        test:"./src/test.js",
    }

    注意:
        如果 entry 是一个 string 或 array,就只会生成一个 Chunk,这时 Chunk 的名称是 main
        如果 entry 是一个 object,就可能会出现多个 Chunk,这时 Chunk 的名称是 object 键值对里键的名称。
        配置动态 Entry:
        
            // 同步函数
            entry: () => {
              return {
                a:'./pages/a',
                b:'./pages/b',
              }
            };
            
            // 异步函数
            entry: () => {
              return new Promise((resolve)=>{
                resolve({
                   a:'./pages/a',
                   b:'./pages/b',
                });
              });
            };
            
     

b:Output(输出)

   是指webpack打包之后的资源bundles输出到哪里去,以及如何命名
   
   path:配置输出文件存放在本地的目录,必须是 string 类型的绝对路径
   publicPath:配置发布到线上资源的 URL 前缀,为string 类型。 默认值是空字符串 '',即使用相对路径。
 output: {
    path: path.resolve(__dirname, "dist"), //默认 打包后的文件存放的地方
    filename: "[name].js", //打包后输出文件的文件名 默认是main.js
    publicPath:'https://cdn.example.com/assets/',
 }
 
 这时发布到线上的 HTML 在引入 JavaScript 文件时就需要:
     <script src='https://cdn.example.com/assets/a_12345678.js'></script>
 

     

c:Loader (翻译)

   处理那些非js文件,处理成自身理解的文件,从而进行打包
   
   条件匹配:通过 test 、 include 、 exclude 三个配置项来命中 Loader 要应用规则的文件。
    应用规则:对选中后的文件通过 use 配置项来应用 Loader,可以只应用一个 Loader 或者按照从后往前的顺序应用一组 Loader,同时还可以分别给 Loader 传入参数。
    重置顺序:一组 Loader 的执行顺序默认是从右到左执行,通过 enforce 选项可以让其中一个 Loader 的执行顺序放到最前或者最后。
    eg:
        module: {
          rules: [
            {
              // 命中 JavaScript 文件
              test: /\.js$/,
              // 用 babel-loader 转换 JavaScript 文件
              // ?cacheDirectory 表示传给 babel-loader 的参数,用于缓存 babel 编译结果加快重新编译速度
              use: ['babel-loader?cacheDirectory'],
              // 只命中src目录里的js文件,加快 Webpack 搜索速度
              include: path.resolve(__dirname, 'src')
            },
            {
              // 命中 SCSS 文件
              test: /\.scss$/,
              // 使用一组 Loader 去处理 SCSS 文件。
              // 处理顺序为从后到前,即先交给 sass-loader 处理,再把结果交给 css-loader 最后再给 style-loader。
              use: ['style-loader', 'css-loader', 'sass-loader'],
              // 排除 node_modules 目录下的文件
              exclude: path.resolve(__dirname, 'node_modules'),
            },
            {
              // 对非文本文件采用 file-loader 加载
              test: /\.(gif|png|jpe?g|eot|woff|ttf|svg|pdf)$/,
              use: ['file-loader'],
              parser: { // 更细粒度的配置哪些模块语法要解析哪些不解析
                  amd: false, // 禁用 AMD
                  commonjs: false, // 禁用 CommonJS
                  system: false, // 禁用 SystemJS
                  harmony: false, // 禁用 ES6 import/export
                  requireInclude: false, // 禁用 require.include
                  requireEnsure: false, // 禁用 require.ensure
                  requireContext: false, // 禁用 require.context
                  browserify: false, // 禁用 browserify
                  requireJs: false, // 禁用 requirejs
                  }
            },
          ]
        }
    
     
 处理css 的loader  css-loader---->引入css文件
 cnpm install --save-dev style-loader css-loader
 module:{
     rules:[
         {
             test:/\.css$/, // 匹配所有css为后缀的样式文件
             use:['style-loader','css-loader'], // 从右向左读取,先读取css-loader
         }
     ]
 }
    常用的loader:
        图片:file-loader
            npm install --save-dev file-loader
            {
                test:/.(png|svg|jpg|gif)$/,
                use:['file-loader']
            }

        sass: sass-loader
            npm install sass-loader node-sass --save-dev
            {
                test:/\.scss$/,
                use:['style-loader','css-loader?minimize','sass-loader'], // minimize 开启 CSS 压缩
            }

        less: less-loader
            npm install --save-dev less-loader less
            {
                test:/\.less$/,
                use:['style-loader','css-loader','less-loader'],
            }

        babel: babel-loader   加载ES6及以上版本及jsx文件
            npm install -D babel-loader @babel/core @babel/preset-env 
            {
                test: /\.(js|jsx)$/i,
                exclude: /node_modules/,
                use: {
                  loader: 'babel-loader',
                  options: {
                    presets: ['@babel/preset-env']
                  }
                }
              }

        字体:file-loader和url-loader
            {
             test: /\.(woff|woff2|eot|ttf|otf)$/,
             use: ['file-loader']
            }
            
     

d:Plugins(插件)

 用于执行范围更广的任务,包括从打包优化和压缩,一直到重新定义环境中的变量等

 eg:html-webpack-plugin:生成html文件

 plugins: [
    new HtmlPlugin(), // 默认的模板,打包之后生成一个index.html 文件
    // 自定义设置模板
    new HtmlPlugin({
        title:'webpack test',
        template:path.join(__dirname, './public/index.html')  // 对应的模板地址
    }), 
  ]

      

e:Mode (模式)

 指webpack使用相应的模式配置
 分为2种模式:development (开发模式) production (生产模式)

 webpack提供了一个webpack-dev-server工具给我们搭建本地运行环境。
     cnpm i webpack-dev-server -D

     在webpack.json 中配置对应的环境
         "scripts": {
            "test": "echo \"Error: no test specified\" && exit 1",
            // 1.webpack-dev-server --open --port 3000 修改默认端口号8080 变更为3000 且自动打开页面 
            // 2.自动热更新,避免每次修改丢要启动运行环境,webpack-dev-server --open --port 3000 --hot
            // 3.统计编译的时间 可以使用--profile 
            "start": "webpack-dev-server --open --port 3000 --hot --profile", // 本地运行环境
            "dev": "webpack-dev-server ",
            "build": "webpack" // 打包
          }
    
    
          
      则在开发环境上使用命令:npm run start 或者 npm run dev  
      生产环境进行打包操作:npm run build 

f:使用 DevServer

提供 HTTP 服务而不是使用本地文件预览;
监听文件的变化并自动刷新网页,做到实时预览;
支持 Source Map,以方便调试。

在webpack.config.js 中配置:
     devServer:{ 
        contentBase: 'dist', // 文件查找
        open:true, // 是否自动打开
        port:4396, // 设置端口号
        hot:true,// 启用模块热更新
        hotOnly:true,// 只使用热更新来更新页面,不会自动刷新页面来更新
        // 开启SourceMap会造成性能方面的影响,所以通常我们的做法是在开发环境下开启source-map,正式上线时候关闭
        devtool:'source-map', // 帮助定位错误
      },

          
      注意:存在版本一些兼容的问题 主要注意一下4"html-webpack-plugin": "^3.2.0",
            "webpack": "^4.41.2",
            "webpack-cli": "^3.3.12",
            "webpack-dev-server": "^3.11.2"
            

g:Chunk:

 代码块,一个 Chunk 由多个模块组合而成,用于代码合并与分割。     
 

webpack相关问题

1.webpack与grunt、gulp的不同?

三者都是前端构建工具,grunt和gulp在早期比较流行,现在webpack相对来说比较主流,不过一些轻量化的任务还是会用gulp来处理,比如单独打包CSS文件等。

grunt和gulp是基于任务和流(TaskStream)的。类似jQuery,找到一个(或一类)文件,对其做一系列链式操作,更新流上的数据, 整条链式操作构成了一个任务,多个任务就构成了整个web的构建流程。

webpack是基于入口的。webpack会自动地递归解析入口所需要加载的所有资源文件,然后用不同的Loader来处理不同的文件,用Plugin来扩展webpack功能。

2.有哪些常见的Loader?他们是解决什么问题的?***

a:file-loader:把文件输出到一个文件夹中,在代码中通过相对 URL 去引用输出的文件

b:url-loader:和file-loader 类似,但是能在文件很小的情况下以base64的方式把文件内容注入到代码中

c:source-map-loader:加载额外的source Map 文件,方便断点调试

d:image-loader: 加载并压缩图片文件

e:babel-loader:把es6转换成ES5

f:css-loader:加载css,支持模块化,压缩,文件导入等特性

g:style-loader:把css代码注入到js中,通过dom操作去加载css

h:eslint-loader:通过ESLint检查js代码

3.Loader和plugin的不同?

  Loader:
      可以理解为翻译器,因为webpack只能解析js文件,需要loader将非js 文件解析成webpack认识的文件
      配置的位置是在:module.rules中配置,类型是数组,每一项是object 里面描述了对于什么类型的文件(test),使用什么加载(loader)和使用的参数(options)
      
 Plugin:
     插件,扩展webpack的功能,让webpack更加灵活,   
     配置的位置在:plugins中单独配置,类型是数组  每一项是一个plugin的实例,参数都通过构造函数传入。
  

4.webpack的构建流程是什么?从读取配置到输出文件这个过程尽量说全

1.初始化参数:从配置文件和 Shell 语句中读取与合并参数,得出最终的参数;
2.开始编译:参数初始化,加载配置插件,执行方法编译
3.确定入口:根据配置中的entry找到所有的入口文件
4.编译模块:从入口文件开始,调用所有配置的loader,以及对于的依赖关系,递归模块
5.完成模块的编译:得到编译之后的结果以及依赖关系
6.输出资源:根据入口和模块之间的依赖关系,组装成一个个包含多个模块的 Chunk,再把每个 Chunk 转换成一个单独的文件加入到输出列表,这步是可以修改输出内容的最后机会;
7.输出完成:在确定好输出内容后,根据配置确定输出的路径和文件名,把文件内容写入到文件系统。

5.webpack的热更新是什么?

热更新又称热替换(Hot Module Replacement),缩写为HMR,基于devServer,生产环境不需要devServer,所以生产环境不能用HMR功能

作用:
优化打包构建速度,一个模块发生变化,只会重新打包这一个模块(而不是打包所有模块),极大提升构建速度

样式文件:可以使用HMR功能,因为style-loader内部实现了

JS文件:默认没有HMR功能,需要修改js代码,添加支持HMR功能。入口文件做不了HMR功能,只能处理非入口js文件

HTML文件:默认没有HMR功能,同时会导致 html 文件不能热更新(即修改没有任何反应)

解决方案:

修改entry入口,将html文件引入

 entry:['./src/js/index.js','./src/index.html']

6.如何利用webpack来优化前端性能?

用webpack优化前端性能是指优化webpack的输出结果,让打包的最终结果在浏览器运行快速高效。

压缩代码。删除多余的代码、注释、简化代码的写法等等方式。可以利用webpack的UglifyJsPlugin和ParallelUglifyPlugin来压缩JS文件, 利用cssnano(css-loader?minimize)来压缩css
利用CDN加速。在构建过程中,将引用的静态资源路径修改为CDN上对应的路径。可以利用webpack对于output参数和各loader的publicPath参数来修改资源路径
删除死代码(Tree Shaking)。将代码中永远不会走到的片段删除掉。可以通过在启动webpack时追加参数--optimize-minimize来实现
提取公共代码。

开发环境下:

开启HMR功能,优化打包构建速度
配置 devtool: ‘source-map’,优化代码运行的性能
生产环境下:

1. oneOf 优化
    默认情况下,假设设置了78个loader,每一个文件都得通过这78个loader处理(过一遍),浪费性能,使用 oneOf 找到了就能直接用,提升性能
2.开启 babel 缓存
    当一个 js 文件发生变化时,其它 js 资源不用变
3.code split 分割
    将js文件打包分割成多个bundle,避免体积过大
4.懒加载和预加载
5.WA 网站离线访问
6.多进程打包
    开启多进程打包,主要处理js文件(babel-loader干的活久),进程启动大概为600ms,只有工作消耗时间比较长,才需要多进程打包,提升打包速度
7.dll 打包第三方库
    code split将第三方库都打包成一个bundle,这样体积过大,会造成打包速度慢
    dll 是将第三方库打包成多个bundle,从而进行速度优化

7.如何提高webpack的构建速度?

多入口情况下,使用CommonsChunkPlugin来提取公共代码
通过externals配置来提取常用库
利用DllPlugin和DllReferencePlugin预编译资源模块 通过DllPlugin来对那些我们引用但是绝对不会修改的npm包来进行预编译,再通过DllReferencePlugin将预编译的模块加载进来。
使用Happypack 实现多线程加速编译
使用webpack-uglify-parallel来提升uglifyPlugin的压缩速度。 原理上webpack-uglify-parallel采用了多核并行压缩来提升压缩速度
使用Tree-shaking和Scope Hoisting来剔除多余代码

8.有哪些常见的Plugin?它们是解决什么问题的?***

html-webpack-plugin:可以复制一个有结构的html文件,并自动引入打包输出的所有资源(JS/CSS)

clean-webpack-plugin:重新打包自动清空 dist 目录

mini-css-extract-plugin:提取 js 中的 css 成单独文件

optimize-css-assets-webpack-plugin:压缩css

uglifyjs-webpack-plugin:压缩js

commons-chunk-plugin:提取公共代码

9.什么是Tree-shaking? 删除多余的代码

Tree-shaking可以用来剔除javascript中不用的死代码,它依赖静态的es6模块化语法,例如通过哦importexport 导入导出,Tree-shaking最先在rollup中出现,webpack在2.0中将其引入,css中使用Tree-shaking需要引入Purify-CSS

10.模块化 CommonJS和 ES6 模块化?

CommonJS:通过 require 方法来同步地加载依赖的其他模块,通过 module.exports 导出需要暴露的接口
    const moduleA = require('./moduleA');
    module.exports = moduleA.someFunc;

ES6模块: 逐渐取代 CommonJSAMD 规范 用import 导入 export default 导出
    import { readFile } from 'fs';
    import React from 'react';
    // 导出
    export function hello() {};
    export default {
      // ...
    };

11.构建工具做了哪些工作:

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