30分钟快速过一遍Webpack4核心知识

1、webpack和webpack-dev-server区别

webpack 每次会生成一个bundle.js文件,webpack-dev-server不会,只是将打包结果放在内存中,并不会写入实际的bundle.js,在每次webpack-dev-server接收到请求时,都将内存中的打包结果返回给浏览器。

2、webpack-cli安装后可以直接在控制台调用webpack命令

3、url-loader 和 file-loader

url-loaderfile-loader都可以用来作为打包图片的loader

  • url-loader不会生成一个具体的图片文件,而是直接在需要这个图片src地址的地方给出图片的base64地址,这样比较适合几k大小的小图,减少http请求;url-loader有个limit设置一个numer,单位是bytes,如果图片大于这个limit值,则默认使用file-loader, 如果小于则使用url-loader
  • file-loader会生成一个图片文件,适合大点的图片

4、style-loader作用主要是将css放到dom中

官方英文解释很到位:Adds CSS to the DOM by injecting a <style> tag

如果配置style-loader/url,则会生成一个类似<link rel="stylesheet" href="path/to/file.css">这样的内联css。

5、postcss-loader与Autoprefixer一起,用来适配各大浏览器厂商css前缀

  • 需要配置postcss.config.js,和安装autoprefixer

6、resolve配置

  • resolve.alias 设置别名来替换某个路径,如:
resolve:{
  alias:{
    @: './src/components/'
  }
}

当需要引入import './src/components/header'时,可以写成import '@/header'

  • resolve.extensions 设置引入文件的扩展格式,当引入文件省略了后缀名时候,会按照设置的resolve.extensions去相应路径匹配对应格式的文件,默认是['js', 'json'],如果是react项目可以设置:extensions: ['.jsx', '.js', '.json']

7、模块样化处理

{
  loader: 'css-loader',
  options: {
    importLoaders: 2,
    modules: true,
  },
},

如上在配置css-loader时候,配置modules: true

但一般出路antd的样式时候,需要做两手处理:

{
    test: /\.less$/,
    include: /node_modules\/antd/,
    use: [
        'style-loader',
        { loader: 'css-loader', options: {modules: false} },
        'less-loader'
    ]
},
{
    test: /\.less$/,
    exclude: /node_modules\/antd/,
    use: [
        'style-loader',
        { loader: 'css-loader', options: {modules: true} },
        'less-loader'
    ]
}

这样避免了 css-modules 对 antd 的样式进行处理,否则会造成antd 的样式的不匹配。

8、iconfont字体打包

{
    test: /\.(eot|ttf|svg|woff)$/,
     use: {
        loader: 'file-loader',
    },
}

如上示例,打包.eot, .ttf等字体文件

9、html-webpack-plugin

  • 会在打包完成后,自动生成一个html文件,并把打包生成的js自动引入到这个html中
  • 如果希望打包出来的html是按照要求配置的,如加上<div id="root"></div>,那么就可以在HtmlWebpackPlugin配置中加入template指定一个模板文件

10、clean-webpack-plugin

  • 清除打包数据
  • clean-webpack-plugin v3.0以上版本,不需要添加额外配置项,默认清除的文件是output.path指定的路径文件夹内容

11、output.publicPath

  • output中配置publicPath,如一个路径或者一个cdn地址,然后打包出来的资源会自动加上这个publicPath前缀,如加上一个cdn的host后,打包出来的script的src会在文件名前加上这个cdn地址。

12、source-map

  • souce-map是源代码和打包后的代码的一个映射关系,对应的webpack配置是devtool,可以快速定位错误行代码。
  • devtoolmode=development中一般配置,devtool:cheap-module-eval-source-map,这样既可以定位错误正确的位置,同时打包速度也不会太受影响。
  • devtoolmode=production中一般配置,devtool:cheap-module-source-map
  • devtool设置了source-map会生成一个.map映射文件;
  • 设置了带inline,则会将映射文件内容放到打包文件里,不会单独生成.map映射文件;
  • 设置了带cheap的配置只会提示行不提示列错误,同时只提示业务代码,不管loader的打包代码;
  • 设置了带module的是除了核心业务代码,loader打包文件代码也会提示错误位置
  • 设置了带eval的会提高打包速度。

13、devServer

  • 主要用于快速开发应用,提高开发效率
  • 通过设置package.json中的stripts,"watch": "webpack --watch",可以实现修改页面代码后自动打包,但是没法开启一个服务,这就不能满足ajax(ajax必须在一个http服务里才能使用)调用服务等开发需求了,此时需要webpack-dev-server
  • webpack-dev-server打包出来的文件不会放到原先的dist或者配置好的output路径,而是放到了本地电脑内存中,这样提升打包速度
  • 配置contentBase,告诉服务器从哪个目录中提供内容,填写output的path就可以了
  • 配置open,设为true时devserver启动后自动打开浏览器
  • 配置port,可以修改开启服务的端口号
  • 配置proxy,如果现在在http://localhost:3000上有服务端的话,可以设置proxy: {'/api': 'http://localhost:3000'},这样在本地的非3000端口host上请求/api/info接口,实际是请求了http://localhost:3000/api/info,否则会报跨域错误
  • 如果不用 webpack-dev-server,可以通过 webpack-dev-middleware 和express/koa2写一个server.js的服务,然后启动这个服务,可以达到和 webpack-dev-server 一样的修改后自动更新等效果

14、hot-module-replacement

  • 当添加了 webpack-dev-server 之后,那么每次有代码修改时,就会重新请求页面,然后刷新页面,但这有个小问题,就是即便一个css颜色值修改了,页面也会重新刷新,加入hot-module-replacement可以简化这个请求过程。
  • 首先在devServer中加入 hot:true,然后在 plugins 中加入new webpack.HotModuleReplacementPlugin()配置,这样仅仅只能做到css样式的无刷新修改
  • 如果需要js也能无刷新修改,则需要在js中加入module.hot的判断,判断的对应结果中需要更新代码,使用 module.hot.accept 将其绑定到新的函数执行中,详见https://webpack.docschina.org/guides/hot-module-replacement/#%E5%90%AF%E7%94%A8-hmr
  • 具体针对前端不同框架,社区有不同loader可以支持HMR,React Hot LoaderVue loader

15、配置Babel

  • Babel主要是将es6+代码转换为es5代码,让低版本浏览器也能加载代码
  • ① 最基本的设置是:

npm install --save-dev babel-loader @babel/core

webpack 设置:

module: {
  rules: [
    {
        test: /\.js$/,
        exclude: /node_modules/,
        loader: "babel-loader"
    }
  ]
}

.babelrc 文件设置如下:

npm install @babel/preset-env --save-dev

{
    "presets": ["@babel/preset-env"]
}

但面对更低版本的浏览器的时候,像promise还需要转化注入,就需要引入babel/polyfill


  • npm install --save @babel/polyfill

在对应业务js代码文件开头引入polyfill import "@babel/polyfill";

但是这样有个缺点,就是会把所有es6转es5的需要的语法都给打包到对应文件,造成打包文件过大。

如果需要按需去打包polyfill语法,根据代码需要,可以设置文件 .babelrc

{
    "presets": [["@babel/preset-env", {
    "targets": {
        chrome: "67"
    },
    "useBuiltIns": "usage",
    }]],
}

上面配置中的 targets 中配置的浏览器版本,是指大于该版本的浏览器,根据该浏览器对es6的支持情况去有针对性的打包polyfill代码,这样可以让打包文件变得更小一点,比如如果chrome67以上的版本浏览器对promise支持很好,那么就不需要打包promise的语法了,更不需要对promise去转码。

总的来说,polyfill修改了全局作用域,浏览器下是window,node下是global。

babel-polyfill主要由两部分组成,core-js和regenerator runtime。

core-js:提供了如ES5、ES6、ES7等规范中 中新定义的各种对象、方法的模拟实现。
regenerator:提供generator支持,如果应用代码中用到generator、async函数的话用到。

引入babel-polyfill全量包后文件会变得非常大。

  • ③ 以上方式需要在对应入口js文件开头引入polyfillimport "@babel/polyfill";
    这种方式会产生一些全局变量,代码量大了之后如写一个大型库或者ui组件库等全局变量会产生变量污染全局。这样就需要用到 transform-runtime,它不会污染全局环境:

npm install --save-dev @babel/plugin-transform-runtime

npm install --save @babel/runtime @babel/runtime-corejs2

删除对应文件开头的引入 import "@babel/polyfill";

.babelrc 文件设置如下:

{
    "plugins": [["@babel/plugin-transform-runtime",
      {
        "absoluteRuntime": false,
        "corejs": 2,
        "helpers": true,
        "regenerator": true,
        "useESModules": false,
      }]],
}

@babel/plugin-transform-runtime 会以闭包的形式

16、Tree Shaking

  • Tree Shaking 用来检测项目中没有被引用的代码(dead-code),Webpack会对这部分代码进行标记,然后在资源压缩打包的时候,从打包出来的文件中去掉,如下示例:
// outer.js

export const foo = () => {
  console.log('Fucked Up');
};

export const bar = () => {
  console.log('Beyond All Repair');
};

// index.js
import { foo } from './outer';

foo();

然后配置webpack.config.js文件,添加如下modeoptimization2个配置:

// webpack.config.js
mode: 'development',
optimization: {
    usedExports: true,
},

然后打包,打开打包好的文件,可以看到如下部分:

/***/ "./src/outer.js":
/*!**********************!*\
  !*** ./src/outer.js ***!
  \**********************/
/*! exports provided: foo, bar */
/*! exports used: foo */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
eval("/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"a\", function() { return foo; });\n/* unused harmony export bar */\nvar foo = function foo() {\n  console.log('Fucked Up');\n};\nvar bar = function bar() {\n  console.log('Beyond All Repair');\n};//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiLi9zcmMvb3V0ZXIuanMuanMiLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly8vLi9zcmMvb3V0ZXIuanM/NGJlZiJdLCJzb3VyY2VzQ29udGVudCI6WyJleHBvcnQgY29uc3QgZm9vID0gKCkgPT4ge1xuICBjb25zb2xlLmxvZygnRnVja2VkIFVwJyk7XG59O1xuXG5leHBvcnQgY29uc3QgYmFyID0gKCkgPT4ge1xuICBjb25zb2xlLmxvZygnQmV5b25kIEFsbCBSZXBhaXInKTtcbn07XG4iXSwibWFwcGluZ3MiOiJBQUFBO0FBQUE7QUFBQTtBQUNBO0FBQ0E7QUFFQTtBQUNBO0FBQ0EiLCJzb3VyY2VSb290IjoiIn0=\n//# sourceURL=webpack-internal:///./src/outer.js\n");

/***/ })

其中有2行注释说明了:

/*! exports provided: foo, bar */
/*! exports used: foo */

就是打包文件中还是导出了foobar两个方法,但只用到了foo,这说明在development中代码的tree shaking只是找出来了并告知了,但并没有真的去删除这部分没用到的代码,webpack4新增了mode,将其设置为production后,不用设置optimization,就会真实执行tree shaking

// webpack.config.js
mode: 'production',

因为设置了mode: 'production'后代码被作了minify(压缩)mangle(混淆破坏)foobar方法已经搜不到了,但是方法体中的console内容还是可以搜的,可以发现,foo中的console内容Fucked Up还可以搜到,而bar中的console内容Beyond All Repair已经搜不到了,因为已经被删除了。

webpack4新增了一个例外处理的口子,就是在package.json中配置一个sideEffects属性,来设置哪些文件是永远都不会被删除掉的,如果设置了sideEffects: false,那么则会正常的删除未被用到的代码。如果设置了sideEffects: ["@babel/polly-fill"]则表明,即便@babel/polly-fill没被直接应用,但还是会在打包时,将它打包进来。

  • Tree Shaking的注意事项(来至官网):
    • 使用 ES2015 模块语法(即 import 和 export)。
    • 确保没有 compiler 将 ES2015 模块语法转换为 CommonJS 模块(这也是流行的 Babel preset 中 @babel/preset-env 的默认行为)。
    • 在项目 package.json 文件中,添加一个 "sideEffects" 属性。
    • 通过将 mode 选项设置为 production ,启用 minification (代码压缩) 和 tree shaking

17、Mode: development/production

wenpack4新增了mode配置:

  • 设置mode: 'development'
    • 会将 process.env.NODE_ENV 的值设为 development
    • 启用 NamedChunksPluginNamedModulesPlugin
  • 设置mode: 'production'
    • 会将 process.env.NODE_ENV 的值设为 production
    • 启用 FlagDependencyUsagePlugin
    • FlagIncludedChunksPlugin
    • ModuleConcatenationPlugin
    • NoEmitOnErrorsPlugin
    • OccurrenceOrderPlugin
    • SideEffectsFlagPlugin
    • UglifyJsPlugin
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,236评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,867评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,715评论 0 340
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,899评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,895评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,733评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,085评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,722评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,025评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,696评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,816评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,447评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,057评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,009评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,254评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,204评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,561评论 2 343

推荐阅读更多精彩内容