webpack进阶用法概述

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的版本
Document - 锐客网

raw-loader内联html

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

CSS内联 方案一:借助style-loader,给他设置一个参数:singleton
module.exports = { module: { rules: [ {test: /\.scss$/, use: [ { loader: 'style-loader', options: { insertAt: 'top', // 将样式插入到 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() ] }

webpack进阶用法概述
文章图片

总结: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进阶用法概述
文章图片

在webpack中使用eslint 行业里优秀的eslint规范实践
Airbnb:eslint-config-airbnb、eslint-config-airbnb-base
制定团队的eslint规范 【webpack进阶用法概述】遵循原则:
  1. 不要重复造轮子,基于eslint:recommend配置并改进
  2. 能够帮助发现代码错误的规则,全部开启
  3. 帮助保持团队的代码风格统一,而不是限制开发体验。![上传中...]()

    推荐阅读