关于前端:webpack进阶用法概述

28次阅读

共计 7699 个字符,预计需要花费 20 分钟才能阅读完成。

1. 加强 CSS 的解析性能
2.tree shaking
3.scope hoisting
4. 如何用 webpack 打包组件库或工具库
5. 如何用 webpack 做 server render
6. 如何在 webpack 外面做 prerender


主动清理构建目录

以后构建时的问题
每次构建的时候不会清理目录,造成构建的输入目录 output 文件越来越多。

解决方案:
1. 通过 npm scripts 清理构建目录
rm -rf ./dist && webpack
rimref ./dist && webpack
2. 主动清理构建目录
防止构建前每次都须要手动删除 dist
应用 clean-webpack-plugin,默认会删除 output 指定的输入目录

module.exports = {plugis: [new CleanWebpackPluginn()]
}

PostCSS 插件 autopredfixer 主动补充 CSS3 前缀

css3 的属性为什么须要前缀?
Trident(-ms) Geko(-moz-) Webkit(-webkt) Presto(-o)

举个例子

.box{
  -moz-border-radius: 10px;
  -webkit-border-radius: 10px;
  -o-border-radius: 10px;
  border-radius: 10px;
}

如何在编写 Css 不须要增加前缀?
应用 autoprefixer 主动补全 css3 前缀
应用 autoprefixer 插件

module.exports = {
  module: {
    rules: [
      {test: /\.less$/, use: [
        'style-loader',
        'css-loader',
        'less-loader',
         {
            loader: 'postcss-loader',
            options: {plugins: () => {require('autoprefixer')({browsers: ['last 2 version', "> 1%", "IOS 7"]
                })
              }
            }
         }
      ]}
    ]
  }
}

主动转换 rem

浏览器的分辨率不同(挪动设施风行之后,不同设施分辨率不同,特地是 ios),须要前端进行大量的适配。
1.CSS 媒体查问实现响应式布局
缺点:须要写多套适配款式代码

@media screen and (max-width: 980px) {
 .header {width: 900px;}
}
@media screen and (max-width: 480px) {
 .header {width: 400px;}
}
@media screen and (max-width: 350px) {
 .header {width: 300px;}
}

rem 是什么
W3C 对 rem 的定义:font-size of the root element
rem 和 px 的比照:
rem 是绝对单位
px 是相对单位

1. 应用 px2rem-loader 将 px 转成 rem
2. 动静计算 1rem 等于多少 px(font-size 的值)
能够应用手淘的 lib-flexible 库

module.exports = {
  module: {
    rules: [
      {test: /\.less$/, use: ['style-loader','css-laoder', 'less-loader', 
      {loader: 'px2rem-loader', 
       options: {
         remUnit: 75, 
         remPrecision: 8
       }
      }
     ]}
    ]
  }
}

资源内联

代码层面:

  1. 页面框架的初始化脚本
    在下面主动转 rem 的例子中,如果咱们想要引入 lib-flexible 库,须要手动在 index.html 中引入代码,因为这段代码须要在页面加载之前执行
  2. 上报相干打点
    css 初始化和加载实现,js 初始化和加载实现等等一些上报点的代码都须要内联到 html 去,而不能引入到脚本外面去。
  3. css 内联防止页面闪动
    将首屏的 css 内联到页面外面去

申请层面:缩小 HTTP 网络申请数
小图片或字体则内联

HTML 和 JS 内联

raw-loader 应用 0.5.2 的版本

<!DOCTYPE html>
<html lang="en">
  <head>
    <%=require('raw-loder!./meta.html')%>
    <title>Document</title>
    <script>
      <%=require('raw-loder!babel-loader!../node_modules/lib-flexible/flexible.js')%>
    </script>
  </head>
  <body>
    <div id="root"></div>
  </body>
</html>

raw-loader 内联 html

<script>${require('raw-loader!babel-loader!./meta.html')}</script>

raw-loader 内联 JS
咱们书写的 JS 脚本外面可能蕴含 es6 等高级语法,此时除了 raw-loader 之外还须要 babel-loader,内联之前对他进行转换

<script>${require('raw-loader!babel-loader!../node_modules/lib-flexible')}</script>

CSS 内联

计划一:借助 style-loader,给他设置一个参数:singleton

module.exports = {
  module: {
    rules: [
      {test: /\.scss$/,
       use: [
         {
           loader: 'style-loader',
           options: {
             insertAt: 'top', // 将款式插入到 <head>
             singleton: true, // 将所有的 style 标签合并成一个
           }
         }
       ]}
    ]
  }
}

计划二:html-inline-css-webpack-plugin
html-webpack-plugin 默认应用的是 ejs 模板引擎,所以能够间接应用花括号的语法(不同版本会有差别)


多页面利用概念

多页面公布上线之后有多个入口,一个页面就是一个业务。
多页面利用的劣势,每个页面之间是解耦的,且对 SEO 更加敌对。每一次页面跳转的时候,后盾服务器都会返回一个新的 html 文档,这种类型的网站也就是多页网站,也叫做多页利用。

多页面打包基本思路

每个页面对应一个 entry,一个 html-webpack-plugin
毛病:每次新增或删除页面须要改 webpack 配置

module.exports = {
  entry: {
    index: './src/index.js', // 首页
    search: './src/search.js' // 搜寻页
  }
}

多页面打包通用计划

动静获取 entry 和设置 html-webpac-plugin 数量,然而这里须要一个约定(如果入口都放在 src 目录下)

module.exports = {
  entry: {
    index: './src/index/index.js',
    search: './src/search/index.js'
  }
}

利用 glob.sync
npm i glob -D

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

Source Map

source map 是开发利器,在打包的时候会将源代码打包成 bundle 文件,bundle 文件就是通过 loader 转换和插件解决了的,最初会生成一个大的 js 文件,这个文件在最初调试的时候是没方法的。
作用:通过 source map 定位到源代码

开发环境开启,线上环境敞开
线上排查问题的时候能够将 sourcemap 上传到谬误加农零碎

source map 关键字

module.exports = {devtool: 'source-map'}

eval:应用 eval 包裹模块代码
source map:产生.map 文件
cheap:谬误不蕴含列信息
inline:将.map 作为 DataURI 嵌入,不独自生成.map 文件内(map 文件内联到 js 文件中去)
module:蕴含 loader 的 sourcemap

source map 类型

devtool 首次构建 二次构建 是否适宜生产环境 能够定位的代码
{none} +++ +++ yes 最终输入的代码
eval +++ +++ no webpac 生成的代码(一个个的模块)
cheap-eval-source-map + ++ no 通过 loader 转换后的代码(只能看到行)
cheap-module-eval-source-map + ++ no 源代码(只能看到行)
eval-source-map + no 源代码
cheap-source-map + o yes 通过 loader 转换后的代码(只能看到行)
cheap-module-source-map o yes 源代码(只能看到行)
inline-cheap-source-map + o no 通过 loader 转换后的代码(只能看到行)
source-map yes 源代码
inline-source-map no 源代码
hidden-source-map yes 源代码

提取页面公共资源

页面之间存在公共模块,且他们应用的根底库是一样的,如果打包的适宜把公共模块都独自打一份,根底库也独自打一份,打包的体积会十分大,所以须要进行根底库的拆散和公共模块的拆散。

根底库拆散

思路:将 react、react-dom 根底包通过 cdn 引入,不打入 bundle 中
办法:应用 html-webapck-externals-plugin

const HtmlWebpackExternalsPlugin = require('html-webpack-externals-plugin')

plugins: [
  new HtmlWebpackExternalsPlugin({
    externals: [
      {
         module: 'react',
         entry: '//11.url.cn/now/lib/15.1.0/react-with-addons.min.js?_bid=3123',
         global: 'React',
      },
      {
         module: 'react-dom',
         entry: '//11.url.cn/now/lib/15.1.0/react-dom.min.js?_bid=3123',
         global: 'ReactDOM',
      }
    ]
  })
]

以上办法,将把 react 和 react-dom 从 cdn 引入,不会打包进来

利用 SplitChunksPlugin 进行公共脚本拆散

webpack4 内置的,代替 CommonsChunkPlugin 插件
chunks 参数阐明:

  • async 异步引入的库进行拆散(默认)
  • initial 同步引入的库进行拆散
  • all 所有引入的库进行拆散(举荐)
module.exports = {
  ...
  optimization: {
    splitChunks: {
      chunks: 'async',
      minSize: 30000,
      maxSize: 0,
      minChunks: 1,
      maxAsyncRequests: 5,
      maxInitialRequest: 3,
      automaticNameDelimiter: '~',
      name: true,
      cacheGroups: {
        vendors: {
          test://,
          priority: -10
        }
      }
    }
  }
}

test: 匹配出须要拆散的包

optimization: {
  splitChunks: {
    cacheGroups: {
      commons: {test: /(react|react-dom)/,
        name: 'vendors',
        chunks: 'all'
      }
    }
  }
}

此时会把 react 和 react-dom 提取进去名字为 vendors

利用 SplitChunksPlugin 拆散页面公共文件

minChunks: 设置最小援用次数为 2 次(被援用 2 次则提取进去)
minuSize: 拆散的包体积的大小(文件大小超过则提取)

module.exports = {
  optimization: {
    splitChunks:  {
      minSize: 0.
      cacheGroups: {
        commons: {
          name: 'commons',
          chunks:'all',
          minChunks: 2,
        }
      }
    }
  }
}

Three Shaking(摇树优化)

概念:1 个模块可能有多个办法,只有其中的办法应用到了,则整个文件都会被打到 bundle 外面去,tree shaking 就是只把用到的办法打入 bundle,没用到的办法会在 uglify 阶段被擦除掉。

应用 :webpack 默认反对,在.babelrc 里设置 modules: false 即可,production mode 的状况下默认开启
要求:必须是 ES6 的语法(import),CJS 的形式不反对

DCE(Elimination)

代码不会被执行,不可达到

if(false){console.log('这段代码永远不会执行')
}

代码执行的后果不会被用到
代码只会影响死变量(只写不读)

Three-Shaking 原理

利用 ES6 模块的特点:

  • 只能作为模块顶层的语句呈现
  • import 的模块名只能是字符串常量
  • import bunding 是 immutable 的
    代码擦除:uglify 阶段删除无用代码

Scope Hoisting 应用和原理剖析

景象:构建后的代码存在大量闭包代码

会导致什么问题:

  • 大量函数闭包包裹代码,导致体积增大(模块越多)
  • 运行代码时创立的函数作用域变多,内存开销变大

模块转换剖析:
模块(index.js)

import {helloWorld} from './helloworld'
import '../../common'

document.write(helloWorld())

模块初始化函数(webpack 打包后)

/* 0 */
/***/ (function(module,__webpack_exports__, __webpack_requie__) {
  'use strict';
  __webpack_require__.r(__webpack_exports__);
  /* harmony import */ var _common_WEBPACK_IMPORTED_MODULE_0)) = __webpack_require__(1);
/* harmoney */ var _helloworld_WEPACK_IMPORTED_MODULE_1__ = __webpack_require__(2);

document.write(Object(__helloworld_WEBPACK_IMPORTED_MODULE_1__['helloworld'])())
}

论断:

  • 被 webpack 转换后的模块会带上一层包裹
  • import 会被转换成__webpack_require

进一步剖析 webpack 的模块机制

(function(modules) {var installModules = {}
  
  function __webpack_require__(moduleId) {if (installModules[moduleId]) 
      return installModules[moduleId].exports
    var module = installModules[moduleId] = {
      i: moduleId,
      l: false,
      exports: {},}
    modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
    module.1 = true
    return module.exports;
  }
  __webpack_require__(0);
})([
  /* 0 module */
  (function (module, __webpack_exports__, __webpack_require__) {...},
  /* 1 module */
  (function (module, __webpack_exports__, __webpack_require__) {...},
  /* n module */
  (function (module, __webpack_exports__, __webpack_require__) {...},
])

剖析:

  • 打包进去的是一个 IIFE(匿名闭包)
  • modules 是一个数组,每一项是一个模块初始化函数
  • __webpack_require 用来加载模块,返回 module.exports
  • 通过 WEBPACK_REQUIRE_METHODS(0)启动程序

scope hoisting 原理

原理:将所有模块的代码依照援用程序放在一个函数作用域里,而后适当的重命名一些变量以避免变量名抵触
比照:通过 scope hoisting 能够缩小函数申明代码和内存开销(将代码内联进来,同一个作用域上面,如果有多个变量,会导致命名抵触,会给这些适当的重命名。这样就缩小了内存的开销)

scope hoisting 应用

webpack mode 为 production 默认开启
必须是 ES6 语法,CJS(动静引入模块)不反对

module.exportss = {
  plugins: [new webpack.optimize.ModuleConcatenationPlugin()
  ]
}

总结:scope hoisting 是把每个模块被 webpack 解决成的模块初始化函数整顿到一个对立的包裹函数里,也就是把多个作用域用一个作用域取代,以缩小内存耗费并缩小包裹块代码,从每个模块有一个包裹函数变成只有一个包裹函数包裹所有的模块,然而有一个前提就是,当模块的援用次数大于 1 时,这个成果就会有效


代码宰割的意义

对于大的 web 利用来将,将所有的代码都放在一个文件中显然是不够无效的,特地是当你的某些代码块是在某些非凡的时候才会被应用到。webpack 有一个性能就是将你的代码库宰割成 chunks(语块)当代码运行到须要他们的时候再加载。

实用的场景:

  • 抽离雷同代码到一个共享块
  • 脚本懒加载,使得初始下载的代码更小

懒加载 JS 脚本的形式
CommonJS:require.ensure(webpack 提供的)
ES6: 动静 import(目前还没有原生反对,须要 babel 转换)

如何应用动静 Import?
装置 Babel

npm i @babel/plugin-syntax-dynamic-import --save-dev

ES6:动静 import(目前还没有原生反对,须要 babel 转换)
.babelrc

{plugins: ['@babel/plugin-syntax-dynamic-import']
}
loadComponent() {import('./text.js').then((txt) => {...})
}

懒加载的脚本 ’text.js’ 会独自打包成一个 js 文件


在 webpack 中应用 eslint

行业里优良的 eslint 标准实际
Airbnb:eslint-config-airbnb、eslint-config-airbnb-base

制订团队的 eslint 标准

遵循原则:

  1. 不要反复造轮子,基于 eslint:recommend 配置并改良
  2. 可能帮忙发现代码谬误的规定,全副开启
  3. 帮忙放弃团队的代码格调对立,而不是限度开发体验。![上传中 …]()

正文完
 0