前端工程化之webpack

webpack

Posted by ZY on November 8, 2021

前言

webpack 是一个现代 JavaScript 应用程序的静态模块打包,在前端开发中是一块非常重要的内容

前端工程化之webpack

什么是 webpack? 本质上,webpack 是一个现代 JavaScript 应用程序的静态模块打包器(module bundler)。当 webpack 处理应用程序时,它会递归地构建一个依赖关系图(dependency graph),其中包含应用程序需要的每个模块,然后将所有这些模块打包成一个或多个 bundle。

webpack 核心概念

  1. entry:一个可执行模块或者库的入口。
  2. output: webpack 在哪里输出它所创建的 bundles,以及如何命名这些文件,默认值为 ./dist。
// webpack 配置

const path = require('path');

module.exports = {
  entry: main: './src/main.js',
  output: {
    filename: 'main.js',
    path: path.resolve(__dirname, 'dist')
  }
};

(3)loader:文件转换器。例如把 es6 转为 es5,scss 转为 css 等

// webpack 配置

module.exports = {
  module: {
    rules: [{ 
        test: /\.css$/, 
        use: ['style-loader', 'css-loader']
       },
         {
        test: /\.vue$/,
        loader: 'vue-loader',
        options: vueLoaderConfig
       },
       {
        test: /\.js$/,
        loader: 'babel-loader'        
       },
       {
        test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
        loader: 'url-loader',
        options: {
          limit: 10000,
          name: utils.assetsPath('img/[name].[hash:7].[ext]')
        }
      }
     ]
  }
};


(4)plugin:扩展 webpack 功能的插件。在 webpack 构建的生命周期节点上加入扩展 hook,添加功能。

// webpack 配置
const HtmlWebpackPlugin = require('html-webpack-plugin'); 

module.exports = {
  plugins: [
    new HtmlWebpackPlugin({
        template: './src/index.html'
    })
  ]
};
(5)模式(development   production) (webpack 4.x)
if(process.env.NODE_ENV === 'development'){
    //开发环境 do something
}else{
    //生产环境 do something
}
// mode: development

module.exports = {
    + mode: 'development'
    - plugins: [
    -   new webpack.NamedModulesPlugin(),
    -   new webpack.DefinePlugin({ "process.env.NODE_ENV": JSON.stringify("development") }),
    - ]
}
// mode: production

module.exports = {
    +  mode: 'production',
    -  plugins: [
    -    new UglifyJsPlugin(/* ... */),
    -    new webpack.DefinePlugin({ "process.env.NODE_ENV": JSON.stringify("production") }),
    -    new webpack.optimize.ModuleConcatenationPlugin(),
    -    new webpack.NoEmitOnErrorsPlugin()
    -  ]
}

(6) source map 定位代码中的错误(资源映射)

eval-source-map:构建速度:– 、重新构建速度:+ 、生产环境:no 、显示原始源代码

cheap-eval-source-map:构建速度:+ 、重新构建速度:++ 、生产环境:no 、转换过的代码(仅限行)

webpack 构建流程(原理)

从启动构建到输出结果一系列过程:

(1)初始化参数:解析 webpack 配置参数,合并 shell 传入和 webpack.config.js 文件配置的参数,形成最后的配置结果。

(2)开始编译:上一步得到的参数初始化 compiler 对象,注册所有配置的插件,插件监听 webpack 构建生命周期的事件节点,做出相应的反应,执行对象的 run 方法开始执行编译。

(3)确定入口:从配置的 entry 入口,开始解析文件构建 AST 语法树,找出依赖,递归下去。

(4)编译模块:递归中根据文件类型和 loader 配置,调用所有配置的 loader 对文件进行转换,再找出该模块依赖的模块,再递归本步骤直到所有入口依赖的文件都经过了本步骤的处理。

(5)完成模块编译并输出:递归完事后,得到每个文件结果,包含每个模块以及他们之间的依赖关系,根据 entry 配置生成代码块 chunk。

(6)输出完成:输出所有的 chunk 到文件系统。

如何利用webpack来优化前端性能,优化代码输出质量?(提高性能和体验)

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

(1)压缩代码。删除多余的代码、注释、简化代码的写法等等方式。可以利用webpack的UglifyJsPlugin和ParallelUglifyPlugin来压缩JS文件, 利用cssnano(css-loader?minimize)来压缩css。使用webpack4,打包项目使用production模式,会自动开启代码压缩。

(2)利用CDN加速。在构建过程中,将引用的静态资源路径修改为CDN上对应的路径。再配合externals得到的包体积小,便于快速渲染组件

//webpackconfig
module.exports = {
    entry:{},
    output:{},
    externals:{          
         'vue': 'Vue',   
        'element-ui': 'ElementUI'
    }
}

//html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>xxx</title>
    <link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
</head>
<body>
    <script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
    <script src="https://unpkg.com/element-ui/lib/index.js"></script>
    <div id="app">       
    </div>
</body>

</html>

(3)删除死代码(Tree Shaking)。将代码中永远不会走到的片段删除掉。除了有副作用的代码需要格外注意到不纳入剔除如[css,vue]有些特殊则无需纳入剔除选项 sideEffects:[‘.css’,’.vue’], ,我们就能为使用方提供更好的打包体验。原理是 webpack 能将标记为 side-effects-free 的包由 import {a} from xx 转换为 import {a} from ‘xx/a’,从而自动修剪掉不必要的 import.

支持的是es module import静态导入模式,不支持reuqire动态模式

(4)优化图片,对于小图可以使用 base64 的方式写入文件中采用url-loader limit限制大小,决定生成base64还是以服务端的url为主

// webpack 配置

module.exports = {
  module: {
    rules: [
       {
        test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
        loader: 'url-loader',
        options: {
          limit: 10000,
          name: utils.assetsPath('img/[name].[hash:7].[ext]')
        }
      }
     ]
  }
};

(5)按照路由拆分代码,实现按需加载,提取公共代码。组件也实现异步import实现按需引入,,对js文件进行代码分割,生成单独的chunks,当用到该路由或组件才去加载,尤其对于首屏优化来说,分担了首屏加载过多过多内容导致的白屏时间过长的问题

//正常引入
const router = new Router({
    routes: [
        {
           path: '/hyh',
           component: hyh,
           name: 'hyh'
        }
    ]
})
// 按需引入 r就是resolve  使用vue的异步组件,可以实现路由的懒加载
const list = r => require.ensure([], () => r(require('../components/list/list')), 'list');
const router = new Router({
    routes: [
        {
           path: '/list/blog',
           component: list,
           name: 'blog'
        }
    ]
})

//  es6提出的import(推荐使用这种方式)取代原来直接import的方式去引入组件
<script>
// import HomeHeader from "./components/Header";
// import HomeSwiper from "./components/Swiper";
// import HomeIcons from "./components/Icons";
// import HomeRecommend from "./components/Recommend";
// import HomeWeekend from "./components/Weekend";
 
export default {
  name: "Home",
  components: {
    HomeHeader: () => import("./components/Header"), //异步组件加载方式
    HomeSwiper: () => import("./components/Swiper"),
    HomeIcons: () => import("./components/Icons"),
    HomeRecommend: () => import("./components/Recommend"),
    HomeWeekend: () => import("./components/Weekend")
  }

(6)利用webpack打包分析plugin进行性能分析

webpack引入webpack-bundle-analyzer

配置该plugin再配合scripts命令 npm run build: –report 得到打包完成分析图

根据此图进行模块分析,找到优化点

// webpack.config.js
const webpack-bundle-analyzer = require('webpack-bundle-analyzer');

module.exports = {
  plugins: [
    new webpack-bundle-analyzer(),
  ],
};

//package.json

 "scripts": {
    "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js",
    "build": "node build/build.js",
    
  },

(7)生产模式启用关闭sourcemap减少不必要的文件,减小包体积,提升页面渲染

(8)对输出的css进行压缩,不必要的css进行剔除

//安装
npm i cssnano optimize-css-assets-webpack-plugin -D

//webpack.config.js
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");

  plugins: [
  	// 抽离css为独立文件输出
    new MiniCssExtractPlugin({
      filename: "css/[name]_[contenthash:6].css"
    }),
    new OptimizeCSSAssetsPlugin({
      cssProcessor: require("cssnano"), //引入cssnano配置压缩选项
      cssProcessorOptions: {
        discardComments: { removeAll: true } // 将未用到的样式全部移除
      }
    }),
  ]

(9)压缩html

new htmlwebpackplugin({
      title: "首页",
      template: "./src/index.html",
      filename: "index.html",
      minify: {
        removeComments: true, // 移除HTML中的注释
        collapseWhitespace: true, // 删除空白符与换行符
        minifyCSS: true // 压缩内联css
      }
    }),