小白浅谈: webpack进阶总结

·  阅读 414
小白浅谈: webpack进阶总结

这是我参与8月更文挑战的第2天,活动详情查看:8月更文挑战

找了张翔哥的照片,给奥运健儿加油打气!!!🤞

进入正题,进入正题!

1. 自动清理构建目录

  • 避免反复的手动或者命令去删除dist文件
plugins: [
    new MiniCssExtractPlugin({
      filename: "[name]_[contenthash:8].css",
    }),
    new OptimizeCss({
      assetNameRegExp: /\.css$/g,
      cssProcessor: require("cssnano"),
    }),
    // 默认 删除output指定的输出目录
    new CleanWebpackPlugin(),
  ]
复制代码

2. 资源内联

  • 代码层面
    • 页面框架的初始化脚本
    • 上报相关打点
    • css内联避免页面闪动
  • 请求层面:减少http网络请求
    • 小图片或者字体内联(url-load)
<head>
  // raw-loader 内联html
  // 使用 0.5.1版本
  <%= require('raw-loader!./meta.html') %>
  <title>Document</title>
  // raw-loader 内联js
  <script>
    <%= require('raw-loader!babel-loader!../../node_modules/lib-flexible/flexible.js') %>
   </script>
</head>
cs
复制代码

css内联.png

3. 多页面应用打包

  • 页面解耦
  • 对seo更加友好
  • 新增或删除页面是无需重复配置
const glob = require("glob");
const HtmlWebpackPlugin = require("html-webpack-plugin");

const setMPA = () => {
  const entry = {};
  const htmlWebpackPlugin = [];
  // 读取匹配 src下面的目录 entryFiles为入口文件的目录数组
  const entryFiles = glob.sync(path.join(__dirname, "./src/*/index.js"));

  Object.keys(entryFiles).map((index) => {
    const entryFile = entryFiles[index];
    // 正则匹配
    const match = entryFile.match(/src\/(.*)\/index\.js/);
    const pageName = match && match[1];
    entry[pageName] = entryFile;
    htmlWebpackPlugin.push(
      new HtmlWebpackPlugin({
        template: path.join(__dirname, `src/${pageName}/index.html`),
        filename: `${pageName}.html`,
        // 打包出的 js css文件自动注入html
        inject: true,
        // 所生成的html使用哪些chunk
        chunks: [pageName],
        minify: {
          html5: true,
          collapseWhitespace: true,
          preserveLineBreaks: false,
          minifyCSS: true,
          minifyJS: true,
          removeComments: false,
        },
      })
    );
  });
  return {
    entry,
    htmlWebpackPlugin,
  };
};
const { entry, htmlWebpackPlugin } = setMPA();

module.exports = {
  entry: entry, // 单入口位字符串  多入口为对象  键值对写入
  plugins: [].concat(htmlWebpackPlugin),
};

复制代码

4. sourcemap

  • 通过sourcemap 定位源代码
  • 开发环境开启,线上环境关闭
  • source map关键字
    • eval: 使用eval 包裹代码块
    • source map: 产生.map文件
    • cheap: 不包含列信息
    • inline: 将.map作为DataURL嵌入,不单独生成,map文件
    • module: 包括loader的sourcemap

sourceMap.png

// 接上一篇
devtool:'source-map'
复制代码

5. 提取页面公共资源

  • 基础库分离 cdn引入
  const HtmlWebpackExternalPlugin = require("html-webpack-externals-plugin");
    
  plugins: [
    new HtmlWebpackExternalPlugin({
      externals: [
        {
          module: "react",
          entry: "https://unpkg.com/react@16/umd/react.production.min.js",
          global: "React",
        },
        {
          module: "react-dom",
          entry:
            "https://unpkg.com/react-dom@16/umd/react-dom.production.min.js",
          global: "ReactDOM",
        },
      ],
    }),
   ]
复制代码
  • splitChunksPlugin
    • webpack4内置
    • chunks参数说明
      • async:异步引入的库进行分离(默认)
      • initial:同步引入的库进行分离
      • all:所有引入的库进行分离(推荐)
    • webpack 地址:webpack.docschina.org/plugins/spl…
module.exports = {
  //...
  optimization: {
    splitChunks: {
      chunks: 'async',
      // 抽离公共包最小体积
      minSize: 20000,
      minRemainingSize: 0,
      // 使用次数
      minChunks: 1,
      // 浏览器请求异步资源的次数
      maxAsyncRequests: 30,
      maxInitialRequests: 30,
      enforceSizeThreshold: 50000,
      cacheGroups: {
         commons: {
          test: /(react|react-dom)/,
          // 这里的name 要同步在 htmlWebpackPlugin的chunks上使用
          name: "vendors",
          chunks: "all",
        },
        default: {
          minChunks: 2,
          priority: -20,
          reuseExistingChunk: true,
        },
      },
    },
  },
};

// 使用
 htmlWebpackPlugin.push(
  new HtmlWebpackPlugin({
    template: path.join(__dirname, `src/${pageName}/index.html`),
    filename: `${pageName}.html`,
    // 打包出的 js css文件自动注入html
    inject: true,
    // 所生成的html使用哪些chunk
    // 对于上面的name
    chunks: [pageName, 'vendors'],
    minify: {
      html5: true,
      collapseWhitespace: true,
      preserveLineBreaks: false,
      minifyCSS: true,
      minifyJS: true,
      removeComments: false,
    },
  })
);
复制代码

6. tree shaking (摇树优化)

  • 模块可能有多个方法,只有用到的方法才会被打包进bundle文件,tree shaking就是 把只用到的方法打入到bundle,没用到的会在uglify阶段被擦除
  • webpack4默认支持,
  • mode: production的情况默认开启
  • 要求:必须是es6的语法,cjs的方式不支持(动态引入)
  • 代码编写有副作用的情况下 tree shaking 失效

DCE(Elimination)

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

tree shaking (原理)

  • 利用es6模块特点
    • 只能作为模块顶层的语句
    • import的模块名只能是字符串变量
    • import binding 是 immutable 的
  • 代码擦除
    • uglify阶段删除无用代码

7. scope hoisting

现象

  • 构建后的存在大量的闭包代码 导致问题
  • 大量函数闭包包裹代码,导致体积增大(模块一如越多越明显)
  • 运行代码是创建的函数作用域变多,内存开销变大 模块转换分析

image.png 结论

  • 被webpack转换后的模块会带上一层包裹
  • import会被转换成_webpack_require 进一步分析webpack的模块机制

importModule.png

scope hoisting(原理)

  • 将所以模块的代码按照引用顺序放在一个函数作用域里面,然后适当的重命名一些变量以防止变量名冲突
  • 减少函数声明代码和内存开销
  • webpack4-production 默认支持
  • 要求:必须是es6的语法,cjs的方式不支持(动态引入) scope.png

8. 代码分割

意义

  • 对于大的Web应用来说,将所有的代码都放在一个文件中显然是不够有效的,特别是当你的某些代码模块实在某些特殊的时候才会被用到时.webpack有一个功能就是将你的代码分割成chunks(语块),当代码运行到需要他们的时候在进行加载 适用的场景
  • 抽离相同代码到一个共享块
  • 脚本懒加载,使得初始下载的代码更小

懒加载js脚本的方式

  • CommonJS: require.ensure
  • ES6: 动态import (目前还没有原生支持,需要babel转换)
// .babelrc 文件
{
    "plugins": ["@babel/plugin-syntax-dynamic-import"]
}
// index.js 文件
  loadComponent = () => {
    import("./text.js").then((Text) => {
      this.setState({
        Text: Text.default,
      });
    });
  };
  // loadComponent执行时,text.js加载
  // import("./text.js") 为promise对象
复制代码

9.ESLint

遵循原则(建议)

  • 不重复造轮子,基于eslint:recommend配置并改进

  • 能够帮助发现代码错误的规则,全部开启

  • 帮助保持团队的代码风格统一,而不是限制开发体验 ESLint执行落地

  • 和CI/CD系统集成

ci_cd.png

  • 和webpack集成
    • 使用eslint-loader
rules: [
      {
        test: /.js$/,
        // include: path.resolve("src"),
        use: [
          // {
          //   loader: "thread-loader",
          //   options: {
          //     // 3个进程
          //     workers: 3,
          //   },
          // },
          "babel-loader",
          // "happypack/loader",
          "eslint-loader"
        ],
        exclude: /node_modules/,
      },
    ]
    
// 新建.eslintrc.js文件

module.exports = {
  // 指定解析器
  "parser": "babel-eslint",
  // 继承 安装的框架 多个数组写入
  "extends": "airbnb",
  // 自定义规则
  "rules": {
      "semi": "error"
  },
  // 生效的环境
  "env": {
    "browser": true,
    "node": true
  }
}
// 详细配置参考 eslint 官网 https://eslint.org/
复制代码
分类:
前端
标签:
收藏成功!
已添加到「」, 点击更改