2 webpack 进阶用法

Posted Indinity

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2 webpack 进阶用法相关的知识,希望对你有一定的参考价值。

极客时间课程 - 玩转webpack笔记,笔记未完成-更新中...

2.1 自动清理构建目录产物

  • 通过npm scripts 清理构建目录(但是这样操作不优雅):

    rm -rf ./dist && webpack
    rimraf ./dist && webpack
    
  • 通过clean-webpack-plugin插件,默认会删除output指定的输出目录

    const { CleanWebpackPlugin } = require(\'clean-webpack-plugin\');
    module.exports = {
    entry: {
    index: \'./src/index.js\',
    app: \'./src/app.js\'
    },
    output: {
    path: path.join(__dirname, \'dist\'),
    filename: \'[name]_[chunkhash:8].js\'
    },
    mode: \'production\',
    plugins: [
    new CleanWebpackPlugin()
    ]
    }

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

  • CSS 兼容性:
    • Trident(-ms),例如IE浏览器;
    • Geko(-moz),例如火狐浏览器;
    • Webkit(-webkit),例如谷歌浏览器;
    • Presto(-o),例如Opera浏览器;
  • webpack中可以使用autoprefixer插件自动补齐CSS前缀;
  • autoprefixer是后置处理器,通常与postcss-loader结合使用;

    \'use strict\';
    const path = require(\'path\');
    const webpack = require(\'webpack\');
    const MiniCssExtractPlugin = require(\'mini-css-extract-plugin\');
    module.exports = {
    entry: {
    index: \'./src/index.js\',
    app: \'./src/app.js\'
    },
    output: {
    path: path.join(__dirname, \'dist\'),
    filename: \'[name]_[chunkhash:8].js\'
    },
    mode: \'production\',
    module: {
    rules: [
    {
    test: /.less$/, use: [MiniCssExtractPlugin.loader, \'css-loader\', \'less-loader\', {
    loader: \'postcss-loader\',
    options: {
    postcssOptions: {
    plugins: [
    require(\'autoprefixer\')({
    browsers: [\'last 2 version\', \'>1%\', \'ios 7\'] // 兼容用户使用率大于1%的最新两个ios 7支持的版本
    })
    ]
    }
    }
    }]
    }
    ]
    }

2.3 移动端CSS px 自动转换为 rem

  • CSS媒体查询实现响应式布局:
    缺点是需要写多套适配样式代码;

    @media screen and (max-width: 980px) {
        .header {
    height: 900px;
    }
    }
    @media screen and (max-width: 480px) {
    .header {
    height: 400px;
    }
    }
    @media screen and (max-width: 350px) {
    .header {
    height: 300px;
    }
    }
  • rempx 的对比:

    • rem 是相对单位,在页面渲染时根据跟元素的font-size值计算;
    • px 是绝对单位;
  • 使用px2rem-loader

    • 将px转换为rem;
    • px转换成rem时,需要知道一个rem单位等于多少px,因此在页面打开的时候需要动态计算根元素的font-size值(这个计算可以使用淘宝的lib-flexible);

      \'use strict\';
      const path = require(\'path\');
      const webpack = require(\'webpack\');
      const MiniCssExtractPlugin = require(\'mini-css-extract-plugin\');
      module.exports = {
      entry: {
      index: \'./src/index.js\',
      app: \'./src/app.js\'
      },
      output: {
      path: path.join(__dirname, \'dist\'),
      filename: \'[name]_[chunkhash:8].js\'
      },
      mode: \'production\',
      module: {
      rules: [
      {
      test: /.less$/, use: [MiniCssExtractPlugin.loader, \'css-loader\', \'less-loader\', {
      loader: \'postcss-loader\',
      options: {
      postcssOptions: {
      plugins: [
      require(\'autoprefixer\')({
      browsers: [\'last 2 version\', \'>1%\', \'ios 7\'] // 兼容用户使用率大于1%的最新两个ios 7支持的版本
      })
      ]
      }
      }
      }]
      },
      {
      loader: \'px2rem-loader\',
      options: {
      remUnit: 75, // 表示1个rem等于75px
      remPrecission: 8, // 表示px转换成rem时保留的小数位数
      }
      }
      ]
      }

2.4 静态文件内联

  • 资源内联的意义:
    • 代码层面:
      • 页面框架的初始化脚本;
      • 上报相关节点;
      • CSS 内联避免页面闪动;
    • 请求层面:减少HTTP网络请求数量
      • 小图片或者字体内联(url-loader);
  • html 内联 raw-loader

    <script>${require(\'raw-loader!babel-loader!./index.html\')}</script>
    // 或
    <%=require(\'raw-loader!./meta.html\')%>
  • JS 内联 raw-loader

    <script>${require(\'raw-loader!babel-loader!../node_modules/lib-flexible\')}</script>
    // 或
    <script><%=require(\'raw-loader!babel-loader!../node_modules/lib-flexible/flexible.js\')%></script>
  • CSS 内联

    • 方式一:借助style-loader
    • 方法二:html-inline-css-webpack-plugin

2.5 多页面应用打包通用方法

  • 多页面应用(MPA)概念:每一次页面跳转的时候,后台服务器都会给返回一个新的html文档,这种类型的网站也就是多页网站,也叫多页面应用。
  • 多页面优势:
    • 每个页面之间解耦;
    • 多页面对SEO更加友好;
  • 多页面打包基本思路:

    • 每个页面对应一个entry,一个html-webpack-plugin;
    • 打包通用方法:动态获取entry和设置html-webpack-plugin数量,即可以利用glob.sync库;

      entry: glob.sync(path.join(__dirname, \'./src/*/index.js\'))
      
    • 举例:

      const glob = require(\'glob\');
      const 
      const setMPA = () => {
      const entry = {};
      const htmlWebpackPlugins = [];
      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;
      htmlWebpackPlugins.push(
      new htmlWebpackPlugin({
      template: path.join(__dirname, `src/${pageName}/index.html`),
      filename: `${pageName}.html`,
      chunks: [pageName],
      inject: true,
      minify: {
      html5: true,
      collapseWhitespace: true,
      preserveLineBreaks: false,
      minifyCSS: true,
      minifyJS: true,
      removeComments: false
      }
      })
      );
      });
      }

2.6 使用sourcemap

  • source map:本质上是一个信息文件,里面储存着代码转换前后的对应位置信息。它记录了转换压缩后的代码所对应的转换前的源代码位置,是源代码和生产代码的映射。
  • source map作用:可以定位到源代码;

    建议在开发环境开启souremap,但是在线上环境关闭,线上问题排查的时候,可以将sourcemap上传到错误监控系统。

  • source map 关键字:

    module.exports = {
    devtool: \'eval\'
    };

2.7 提取页面公共资源

2.7.1 基础库分离

方式一:CDN引入

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

方式二:SplitChunksPlugin 进行公共脚本分离

  • chunks参数说明:

    • async:异步引入的库进行分离(默认);
    • inital:同步引入的库进行分离;
    • all:所有引入的库进行分离(推荐);
  • cacheGroups参数

    • test属性:可以配置将哪些库分离出来,例如:
    • minChunks:设置需要满足的最小引用次数,若不满足引用次数则同样不会被提取出来;
  • minuSize:分离的包体积的大小;

2.8 Tree Shaking 的使用和原理分析

  • Tree Shaking(摇树优化),一个模块可能有很多个方法,只要其中的某个方法是用到了,则整个文件都会背大包到bundle里面,tree shaking就是只把用到的方法打入bundle,没用到的方法会在uglify阶段被擦出掉;
  • 使用:
    • webpack默认支持,在.babelrc里设置modules: false即可;
    • 在webpack4的production场景中默认是开启的;
  • tree shaking只支持ES6语法,对于node中CJS的方法是不支持的;
  • 基础概念:DCE(Dead Code Elimination)
    • 代码不会被执行到,不可到达;
    • 代码执行的结果不会被用到;
    • 代码只会影响死变量(即只写不读的变量);
  • tree shaking的原理(基于DCE概念进行理解):
    • 利用ES6模块的特点:
      • 只能作为模块顶层的语句出现;
      • import 的模块名只能是字符串常量;
      • import binding 是 immutable的;
    • tree shaking本质上还是对代码的静态分析,因此在编译阶段,代码是否有使用到是要确定下来的,在这个阶段,tree shaking会将未使用到的代码进行标记,最后在uglify阶段删除无用代码;

2.9 Scope Hoisting 使用和原理分析

  • 不使用scope hoisting时构建生成的代码:
    • 存在大量闭包代码,导致打包文件体积增大(模块越多越明显);
    • 运行代码时创建的函数作用域变多,内存开销变大;
  • webpack打包——模块转换分析
    • 模块初始化函数
      • 被webpack转换后的模块会带上一层包裹;
      • import 会被转换成__webpack_require;
      • export 也会进行相应的转换;
    • 打包出来是一个IIFE(匿名闭包);
    • modules 是一个数组,每一项是一个模块初始化函数;
    • __webpack_require的工作:用来加载模块,返回module.exports
  • scope hoisting 原理:将所有模块的代码按照引用顺序放在一个函数作用域里,然后适当的重命名一些变量以防止变量名冲突;
  • 使用scope hoisting前后的对比:通过scope hoisting可以减少函数声明代码和内存开销;
  • scope hoisting 使用:

    • 在webpack3需要手动引入插件:

      plugins: [
          new webpack.optimize.ModuleConcatenationPlugin()
      ]
    • 在webpack4中,mode为production时默认开启;

    • 注意:编写的代码必须是ES6,不能是CJS语法;

2.10 代码分割和动态import

  • 代码分割的适用场景:
    • 抽离相同的代码到一个共享块;
    • 脚本懒加载,使得初始化下载到代码更小;
  • 懒加载JS的方式:
    • CommonJS:require.ensure;
    • ES6:动态import(目前还没有原生支持,需要使用babel进行转换)【本质上是利用了JSONP的技术进行实现的】;
  • 动态import:

    • 安装babel插件;

      npm install @babel/plugin-syntax-dynamic-import --save-dev
      
    • 配置.babelrc配置文件

      {
          "plugins": ["@babel/plugin-syntax-dynamic-import"],
      // ...
      }

2.11 在webpack中使用ESLint

  • 行业内优秀的ESLint实现规范:
    • Airbnb:eslint-config-airbnb、eslint-config-airbnb-base;
    • 腾讯:eslint-config-alloy,eslint-config-ivweb
  • 指定ESlint规范时遵循的三个原则:

    • 不重复造轮子,基于eslint:recommand配置并改进;
    • 能够帮助发现代码错误的规则,全部开启;
    • 帮助保持团队代码的代码风格一致,而不是限制开发体验;

  • ESLint 规范落地方式:

    • 与 CI/CD 集成:是在发布到环境/线上进行的检查;

    • 本地开发阶段增加precommit钩子;

      • 安装husky:

        npm install husky --save-dev
        
      • 增加npm script,通过lint-staged增量检查修改的文件;

        "script": {
            "precommit": "lint-staged"
        },
        "lint-staged": {
        "linters": {
        "*.{js,scss}": ["eslint --fix", "git add"]
        }
        }
    • webpack 与 CI/CD 集成:使用eslint-loader,构建时检查JS规范【不建议老项目通过该方式新增检查,因为这种方式检查时针对这个歌项目进行检查的】;

    eslint的配置文件可以是.eslintrc.*文件;

2.12 webpack打包和基础库

webpack 除了可以用来打包应用,也可以用来打包JS库;

实现一个大整数加法库的打包:

  • 需要打包压缩版(适用于开发阶段)和非压缩版(线上打包);
  • 支持AMD/CJS/ESM模块引入;

2.13 webpack 实现SSR打包

2.14 优化构建时命令行的显示日志

2.15 构建异常和中断处理

以上是关于2 webpack 进阶用法的主要内容,如果未能解决你的问题,请参考以下文章

前端进阶:一文轻松搞定webpack基础知识进阶与调优

webpack4之路-优化进阶

webpack进阶

我的Android进阶之旅NDK开发之在C++代码中使用Android Log打印日志,打印出C++的函数耗时以及代码片段耗时详情

[转]webpack进阶构建项目

webpack4进阶概念