导航

[[深刻01] 执行上下文](https://juejin.im/post/684490...
[[深刻02] 原型链](https://juejin.im/post/684490...
[[深刻03] 继承](https://juejin.im/post/684490...
[[深刻04] 事件循环](https://juejin.im/post/684490...
[[深刻05] 柯里化 偏函数 函数记忆](https://juejin.im/post/684490...
[[深刻06] 隐式转换 和 运算符](https://juejin.im/post/684490...
[[深刻07] 浏览器缓存机制(http缓存机制)](https://juejin.im/post/684490...
[[深刻08] 前端平安](https://juejin.im/post/684490...
[[深刻09] 深浅拷贝](https://juejin.im/post/684490...
[[深刻10] Debounce Throttle](https://juejin.im/post/684490...
[[深刻11] 前端路由](https://juejin.im/post/684490...
[[深刻12] 前端模块化](https://juejin.im/post/684490...
[[深刻13] 观察者模式 公布订阅模式 双向数据绑定](https://juejin.im/post/684490...
[[深刻14] canvas](https://juejin.im/post/684490...
[[深刻15] webSocket](https://juejin.im/post/684490...
[[深刻16] webpack](https://juejin.im/post/684490...
[[深刻17] http 和 https](https://juejin.im/post/684490...
[[深刻18] CSS-interview](https://juejin.im/post/684490...
[[深刻19] 手写Promise](https://juejin.im/post/684490...
[[深刻20] 手写函数](https://juejin.im/post/684490...

[[react] Hooks](https://juejin.im/post/684490...

[[部署01] Nginx](https://juejin.im/post/684490...
[[部署02] Docker 部署vue我的项目](https://juejin.im/post/684490...
[[部署03] gitlab-CI](https://juejin.im/post/684490...

[[源码-webpack01-前置常识] AST形象语法树](https://juejin.im/post/684490...
[[源码-webpack02-前置常识] Tapable](https://juejin.im/post/684490...
[[源码-webpack03] 手写webpack - compiler简略编译流程](https://juejin.im/post/684490...
[[源码] Redux React-Redux01](https://juejin.im/post/684490...
[[源码] axios ](https://juejin.im/post/684490...
[[源码] vuex ](https://juejin.im/post/684490...
[[源码-vue01] data响应式 和 初始化渲染 ](https://juejin.im/post/684490...
[[源码-vue02] computed 响应式 - 初始化,拜访,更新过程 ](https://juejin.im/post/684490...

前置常识

一些单词

proposal:提议,倡议decorator:润饰expose:裸露license:执照,许可compress:压缩determine:确定,查明wildcard:通配符collapse:塌方,瓦解quote:援用extract:抽离,提取optimization:最佳优化plug:插头,插座  (pluggable:可插拔)complete:实现represents:代表

package.json

name- 定义包的名称,是公布到npm注册表时,软件包显示的名称- 必须是小写,容许( 连字符和下划线 ),不容许 ( 空格和其余字符 )- name: 'webpack-learning'- 留神:如果要公布到npm上,包的名称必须是惟一的main !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!- 定义我的项目的入口点- 默认是index.jsversion- 软件的以后版本license- 代码许可证- MIT- ISCauthor 和 contributors- author:一个人- contributors:多集体- "author": "...",- "contributors": [{    "name": "...",    "email": "...",    "url": "..."}],keywords- 搜寻索引- "keywords": ["server", "osiolabs", "express", "compression"]

path.resolve 和 path.join 和 __dirname变量

  • <font color=blue>path.resolve() --------------------------- 生成绝对路径</font>

    • path.resolve([...paths])
    • <font color=red>将( 门路或门路片段 ) 的序列解析为 ( 绝对路径 )</font>
    • 参数:门路或门路片段的序列
    • 返回值:一个绝对路径
    • 留神:

      • 不传参数将返回当前工作目录的绝对路径
      • 0长度的path片段将被疏忽
  • <font color=blue>path.join()--------------------------------- 拼接门路</font>

    • path.join([...paths])
    • 留神:

      • 0长度的门路将被疏忽
  • <font color=blue> __dirname -------------------------------- 以后模块的目录名,是一个绝对路径</font>

process.env ---------------------------- 返回蕴含用户环境的对象

  • process.env 返回蕴含用户环境的对象
  • <font color=red>留神:process.env对象上调配的属性,值会被隐式转换为字符串,当值不是string,number,boolean时可能会报错</font>
  • 留神:能够批改此对象,但这些批改不会反映到 Node.js 过程之外
  • <font color=red>删除属性:delete </font>

    • delete process.env.xxxx
  • 留神:在windows零碎上,环境变量不辨别大小写
  • <font color=red>process.env.NODE_ENV不是process.env对象的原有属性,是自定义的</font>

    • 起因:在window和mac有差别
    • 所以:个别通过 ( cross-env ) 插件来设置
    • npm install --save-dev cross-env
    • cross-env
    (1) 失效process.env.foo = 'bar';console.log(process.env.foo);(2) process.env的属性会隐式转换为字符串process.env.test = null;console.log(process.env.test);// => 'null'process.env.test = undefined;console.log(process.env.test);// => 'undefined'(3) delete 删除属性process.env.TEST = 1;delete process.env.TEST;console.log(process.env.TEST);// => undefined(4) window上 ( 环境变量 ) 不辨别大小写process.env.TEST = 1;console.log(process.env.test);// => 1
    ---cross-env{"scripts": {  "build": "cross-env NODE_ENV=production webpack --config build/webpack.config.js"}}

会用到的依赖

- webpack webpack-cli- html-webpack-plugin- webpack-dev-servercss相干- style-loader- css-loader- less-loader- 抽离css- mini-css-extract-plugin- 前缀- postcss-loader- autoprefixer- 压缩css- (留神:用于生产环境才去压缩css,因为影响打包速度)- optimize-css-assets-webpack-plugin- uglifyjs-webpack-pluginjs相干- babel-loader- @babel/core- @babel/preset-env- @babel/plugin-proposal-decorators- @babel/plugin-proposal-class-properties- @babel/plugin-transform-runtime- @babel/runtime- @babel/polyfill- eslint ellint-loader babel-eslint- expose-loader图片相干- file-loader- url-loader- html-withimg-loader
  • 2021/01/11 更新

    源码仓库

  • webpack- 根底学习仓库
  • webpack-Compiler源码手写 - 源码剖析仓库
  • webpack-Compiler源码手写 - 我的掘金博客

代码宰割 webpack.DllPlugin 和 optimization.splitChunks 的区别

  • <font color=red>(webpack.DllPlugin,webpack.DllReferencePlugin) 和 optimization.splitChunks 都能实现代码宰割</font>
  • 优先应用 optimization.splitChunks.cacheGoup
  • SplitChunksPlugin

    SplitChunksPlugin默认配置:module.exports = {//...optimization: { // -------------- 默认配置,该段配置的成果和 optimization: {splitChunks: {}} 成果一样// -------------- 即 splitChunks 配一个空对象和上面的代码等价  splitChunks: {    chunks: 'async',     // -------------- chunks: 'async' 只对异步引入的代码就行代码宰割,比方 import().then()语法动静引入的包或者路由组件    // -------------- chunks: 'all' 对同步引入和异步引入的代码都做代码宰割,还需后续配置cacheGroups    // -------------- chunks: 'initial' 对同步代码进行宰割    minSize: 30000,    // -------------- minSize: 30 000 示意当包大于( 30KB )时,就进行代码宰割    maxSize: 0,    // -------------- maxSize示意对打包后的库进行二次拆分,可配可不配    minChunks: 1,    // -------------- minChunks 示意当一个模块至多应用多少次时才进行代码宰割    maxAsyncRequests: 5,    maxInitialRequests: 3,    automaticNameDelimiter: '~', // 文件命名之间的连接符,比方name和入口文件名之间的连贯    name: true,    cacheGroups: { // 一个可能蕴含多个包的组      vendors: {       // ----------------- 示意如果满足chunks设置项,并且在node_modules文件夹下,就打包成 vendors~main.js      // ----------------- vendors~main.js 中的 main 示意入口文件打包后的文件名是 main.js      // ----------------- 如果要批改满足chunks的配置引入的包打包的名字用:filename 去配置      // ----------------- cacheGroups 和 chunks 是配合来用的        test: /[\\/]node_modules[\\/]/,         //---------------- 从node_modules文件夹中引入的,实际上就是第三方组件库,依赖库        priority: -10,        // --------------- priority示意当同时满足vendors和default时, piroity越大优先级越高,(优先级)        filename: 'vendors', // ------------- 打包后满足chunks和以上各个条件的引入的包会被打包到名为 vendors.js 的文件中      },      default: { // 不满足vendors然而又满足chunks等以上的一些条件,阐明须要做代码宰割,但不是vendors,则会走default        minChunks: 2,        priority: -20,        reuseExistingChunk: true,         // reuseExistingChunk示意一个包如果打包过了,在别的包中又援用了,打包别的包时就不再打包了,间接援用        filename: 'common'      }    }  }}};

webpack根底

  • webpack是一个 ( <font color=red>模块打包</font> ) 工具,最根本的就是一个js模块打包工具,如果增加了loader则能够打包任何相应的资源
  • 能够 0 配置,然而为了合乎我的项目需要,个别都会本人配置

    0配置下默认配置运行:-------------------------------------- npx webpack xxx.js打包后生成的 ( 文件夹 ) 名称:---------------------- dist打包后生成的 ( 文件 ) 名称:------------------------ main.js(2)webpack默认配置文件默认的webpack配置文件名称:------------------------- webpack.config.js || webpackfile.js(3)如何用命令行指定webpack的配置文件,并执行这个文件---------------------------------------------------- npx webpack --config xxxxx

webpack webpack-cli

  • npm install -D webpack-cli webpack
  • 留神是开发时依赖,线上应用打包后的文件

webpack-dev-server

  • npm install -D webpack-dev-server
  • webpack-dev-server能够启一个简略的web服务,并且具备 live-reloading 实时从新加载
  • 配置项:

    • contentBase:启动的服务从哪个目录中加载资源
    • port:服务端口号
    • <font color=red>publicPath:设置公共门路,此门路下的打包文件,能够在浏览器中拜访</font>

      • <font color=red>比方部署到CDN上,html的所有引入资源都须要加上CDN的地址作为公共门路,这时须要设置publciPath</font>
    • <font color=red>proxy:设置代理</font> ,应用的是 http-proxy-middleware
    • <font color=red>compress:是否开启 gzip 压缩</font>
    • hot:是否开启热更新 ( 模块热替换 )
    • open:是否在启动服务后关上浏览器
    • host:指定host
    • color:控制台黑白输入,boolean,留神只用于命令行
  • proxy

    proxyvar apiProxy = proxy('/api', { target: 'http://www.example.org' });//                   \____/   \_____________________________///                     |                    |//                   context             options(1)proxy: {  '/api': 'http://www.baidu.com'}申请:'/api/user'会被代理到:'http://www.baidu.com/api/user'(2)需要:如何在实在的申请门路中去掉 '/api'解决:重写门路proxy: {  '/api': {      target: 'http://www.baidu.com',      pathRewrite: {          '^/api': ''   //------------------------------------- remove path 即删除 '/api'      }  }}申请:'/api/user'会被代理到:'http://www.baidu.com/user'
    ---webpack-dev-serverdevServer: { // --------------------------------------------- webpack-dev-server配置项contentBase: path.join(__dirname, 'dist'), // ------------- 启动服务加载的动态资源port: '5000', // ------------------------------------------ 端口号compress: true, // ---------------------------------------- 是否开启 gzip 压缩open: true, // -------------------------------------------- 是否关上浏览器标签,在服务启动后proxy: { // ----------------------------------------------- 代理  '/api': { // -------------------------------------------- context,须要代理的门路    target: 'http://yapi.wxb.com.cn', // ------------------ 指标地址    pathRewrite: { // ------------------------------------- 门路重写      '^/api': '' // -------------------------------------- remove path 相当于把 '/api' 用 '' 代替    }  }}},

<font color=red>proxy 实例</font>

(1)index.jsconst api = new XMLHttpRequest()api.open('GET', '/api/name', true) // --------------------------------- 申请门路 ( '/api/name' )api.onload = function() {  const res = api.response  console.log(res, 'res')}api.send()(2)devServer: {  contentBase: path.join(__dirname, 'dist'),  port: '5000',  compress: true,  open: true,  proxy: {    '/api': {      target: 'http://localhost:7000', // ----------------------- 服务代理到http://localhost:7000      pathRewrite: {        '^/api': ''         // ------------------------------------------------------ 重写'/api'        // ------------------------------------------------------ '/api/name' 将代理到 'http://localhost:7000/name'      }    }  }},(3)server => index.jsvar express = require('express')var app = express()app.get('/name', (req, res) => { // ----------------------------- 服务端启动在:'http://localhost:7000/name'  res.json({    name: 'woow_wu7'  })})app.listen(7000, () => console.log('the server is running on port 7000'))

html-webpack-plugin

  • npm install html-webpack-plugin -D
  • 配置项:

    • template:模板html文件
    • filename:打包后的文件名
    • <font color=red>hash:在html援用js时是否应用hash戳</font>
    • <font color=red>minify:用于 ( 生产环境 ) 即mode=production中,是一个 ( 布尔值 ) 或者 ( 对象 )</font>

      • removeAttributeQuotes:去除html属性值的双引号
      • collapseWhitespace:折叠空行,即只有一行代码
    html-webpack-pluginconst HtmlWebpackPlugin = require('html-webpack-plugin')plugins: [ // ------------------------------------------------- plugins是一个插件数组new HtmlWebpackPlugin({  template: './src/index.html', // -------------------------- 模板html  filename: 'index.html', // -------------------------------- 打包后的html的文件名  hash: true, // -------------------------------------------- html援用的js是否带有hash戳  minify: { // ---------------------------------------------- 压缩优化    removeAttributeQuotes: true, // ------------------------- 删除html标签属性值的双引号    collapseWhitespace: true, // ---------------------------- 折叠空行为一行  }})]

loader

  • <font color=red>loader加载程序: 从右往左,从下往上</font>
  • loader文档

css相干

( css-loader ) ( style-loader ) ( less-loader )

  • npm install style-loader css-loader less less-loader -D
  • css-loader

    • 次要用来反对 @import 语法的css引入
  • style-loader

    • 次要作用是把css以 ( style标签的 ) 模式嵌入到html的head标签中
    • 留神程序是:先css-loader解决完css后,再style-loader插入到head标签中
    • 配置项 options

      • <font color=red>insert: 'body' 将style标签插入到body标签中,默认是插入到head标签中</font>
    style-loadercss-loaderless-loader 还得装置 lesssass-loader 还得装置 node-sassmodule: { // 模块rules: [ // 规定  {    test: /\.css$/,  // ------------------------ 解决css    // ----------------------------------------- 正则匹配,留神loader的程序是从右往左,从下往上    use: [      {        loader: 'style-loader',        options: {          // insert: 'body' // ----------------- style标签默认插入到header标签中,这里能够插入到body标签中        }      },      {        loader: 'css-loader',      },    ]  },{    test: /\.less$/, // ------------------------ 解决less    use: [      {loader: 'style-loader'},      {loader: 'css-loader'},      {loader: 'less-loader'}    ]  }]},

mini-css-extract-plugin ------ 抽离css

  • npm install mini-css-extract-plugin -D
  • 默认状况下:style-loader是把css文件转换成 style 标签内嵌在html中,咱们心愿的是用 link 标签内部引入css,所以用mini-css-extract-plugin来抽离css,通过link标签把css引入html
  • extract:抽离的意思
  • mini-css-extract-plugin官网

    const MiniCssExtractPlugin =  require('mini-css-extract-plugin') // ---------------- 引入module: {rules: [  {    test: /\.css$/,    use: [      MiniCssExtractPlugin.loader, // -------------------------------------------- loader      {        loader: 'css-loader',      },    ]  },{    test: /\.less$/,    use: [      MiniCssExtractPlugin.loader,      {loader: 'css-loader'},      {loader: 'less-loader'}    ]  }]},plugins: [new MiniCssExtractPlugin({ // ---------------------------------------------------- plugin  filename: 'main.css' // -------------------------------------------------------- 抽离进去的css文件名  // filename: 'css/main.css' // ----------------- 这样会在打包后生成css文件夹,外面是所有css都在main.css文件中})]

postcss-loader 和 autoprefixer ------ 生成浏览器前缀

  • postcss-loader

    • 用来解决浏览器前缀,兼容性解决,须要独自的配置文件 postcss.config.js
    • 解决浏览器前缀:(1) 独自配置postcss.config.js (2) 还需配合插件 autoprefixer
    • <font color=blue>留神loader间的程序:比方须要解决完less => 加上前缀后 => 辨认@import => 再抽离css</font>
    • 即先 less-loader => postcss-loader => css-loader => MiniCssExtractPlugin.loader
  • autoprefixer

    • <font color=red>autoprefixer须要给出浏览器的一些信息,所以要在 package.json 中增加 browserslist 配置 </font>
    postcss-loader 和 autoprefixer 解决浏览器前缀问题(1) loader间的程序(2) postcss-loader须要独自配置postcss.config.js(3) autoprefixer须要给出浏览器的一些信息,须要在 package.json 中配置 browserslist 属性---package.json中"browserslist": ["defaults","not ie < 9","last 3 version",">1%","ios 7","last 3 iOS versions"]---postcss.config.js中const autoprefixer = require('autoprefixer')module.exports = {plugins: [autoprefixer]}---webpack.config.js中rules: [{  test: /\.css$/,  use: [    MiniCssExtractPlugin.loader,    { loader: 'css-loader'},    { loader: 'postcss-loader'}, //----------------- postcss-loader,留神程序  ]},{  test: /\.less$/,  use: [    MiniCssExtractPlugin.loader,    {loader: 'css-loader'},    { loader: 'postcss-loader'},    {loader: 'less-loader'}  ]}]

optimize-css-assets-webpack-plugin 和 uglifyjs-webpack-plugin ------ 压缩css 和 js

  • npm install optimize-css-assets-webpack-plugin uglifyjs-webpack-plugin -D
  • 留神压缩css是用于 ( 生产环境 ) 才去压缩css,因为会影响打包速度
  • 留神:( html-webpack-plugin ) 通 过minify => removeAttribureQuote, collapseWhitespace 来压缩html也是用在 ( 生产环境 )
  • optimize-css-assets-webpack-plugin

    • 留神:

      • 问题:应用 optimize-css-assets-webpack-plugin 插件后,独自抽离的css会被压缩,然而打包的js文件又没有被压缩了
      • 解决:应用 uglifyjs-webpack-plugin 插件来解决js没有被压缩的问题
  • uglifyjs-webpack-plugin

    optimization: { // -------------------------------- 优化项 (optimization:是最佳优化的意思)// ---------------------------------------------- 留神:压缩css和js要在mode=production中能力看到成果,和 html的优化一样minimizer: [  new OptimizeCssAssetsWebpackPlugin(), // ------ optimize-css-assets-webpack-plugin 压缩独自抽离的css  new UglifyjsWebpackPlugin({ // ---------------- 压缩js    cache: true,    parallel: true, // 平行,并行的意思    sourceMap: true, // 调试映射  })]}

js相干

  • babel-loader
  • @babel/core
  • @babel/preset-env
  • @babel/plugin-proposal-decorators // -------------------- 装璜器语法
  • @babel/plugin-proposal-class-properties // -------------- class语法
  • @babel/plugin-transform-runtime
  • @babel/runtime // ---------------------------留神是该依赖是 dependencies 而不是 devDependencies
  • @babel/polyfill
  • npm install babel-loader @babel/core @babel/preset-env -D
  • npm install @babel/plugin-proposal-class-properties @babel/plugin-proposal-decorators -D
  • npm install @babel/plugin-transform-runtime -D
  • npm install @babel/runtime -S 留神是-S
  • npm install @babel/polyfill -S 留神是-S
  • babel官网
  • 留神点:

    • babel的配置:

      • <font color=red>(1)能够新建.babelrc文件独自配置</font>
      • <font color=red>(2)能够在webpack的loader配置的babel-loader的options中配置</font>
    • @babel/plugin-proposal-decorators 和 @babel/plugin-proposal-class-properties的程序

      • @babel/plugin-proposal-decorators --------------在前
      • @babel/plugin-proposal-class-properties---------在后
    .babelrc文件{"presets": [  ["@babel/preset-env", {    "modules": false  }]],"plugins": [  ["@babel/plugin-proposal-decorators", {"legacy": true}],  ["@babel/plugin-proposal-class-properties", {"loose": true}],  ["@babel/plugin-transform-runtime"]]}----@babel/polyfill 插件是间接引入到入口js文件中require('@babel/polyfill')

    退出eslint校验 (eslint) (eslint-loader) (babel-eslint)

  • npm install eslint eslint-loader babel-eslint -D
  • eslint
  • eslint-loader
  • babel-eslint
  • 留神点:

    • (1) 须要独自配置 ( .eslintrc.json ) 文件,或者间接在 ( package.json ) 文件的 ( eslintConfig ) 字段中配置rule规定
    • <font color=red>(2) 留神eslint-loader和babel-loader的程序</font>

      • 程序:先校验再解决
      • 是要先校验eslint-loader,而后在babel-loader转换
      • 在eslint-loader的options选中,能够应用 enforce: 'pre' 来优先执行,即便程序是eslint-loader在下面或后面
    • <font color=red>(3) 须要装置bebel-eslint插件 Cannot find module 'babel-eslint'</font>
    .eslintrc{"parser": "babel-eslint", // 这里必须设置"parserOptions": {  "sourceType": "module",  "allowImportExportEverywhere": true},"rules": {  "indent": "off",  "no-console": 2 // 不容许console.log()},"env": {}}------------webpack.config.js{test: /\.js$/,use: [ // ----------------------------------------------- use能够是数组 或者 对象  {    loader: 'babel-loader',    options: { // ---------------------------------- 除了在这里配置外,还能够独自设置 .babelrc 文件配置babel      presets: [         ['@babel/preset-env']      ],      plugins: [        ['@babel/plugin-proposal-decorators', {'legacy': true}],        ['@babel/plugin-proposal-class-properties', {'loose': true}],        ['@babel/plugin-transform-runtime']      ]    }  },  {    loader: 'eslint-loader', // -------------------------- eslint-loader     options: {      enforce: 'pre'    }  }],include: path.resolve(__dirname, 'src'),exclude: path.resolve(__dirname, 'node_modules'),},   // {    //   test: /\.js$/,    //   use: { // --------------------------------------- use是对象    //     loader: 'eslint-loader',    //     options: {    //       enforce: 'pre'    //     },    //   },    //   include: path.resolve(__dirname, 'src'),    //   exclude: path.resolve(__dirname, 'node_modules'),    // },

loader的类型

  • pre ----------------------- 前置loader
  • post ---------------------- 后置loader
  • normal ------------------- 一般loader
  • inline --------------------- 内联loader,即能够间接写在js代码中的loader

expose-loader 和 wepback.ProvidePlugin()

  • npm install expose-loader -D
  • expose-loader裸露全局的loader
  • expose:裸露
  • expose-loader的三种用法

    expose-loader的三种用法(1)间接在js中引入:----------------  import $ from 'expose-loader?$!jquery'下面的操作能够把:$挂载到window上,即能够通过 ( window.$ ) 拜访到jquery(2)在webpack.config.js中 module => rules 中配置{test: require.resolve('jquery'), // require.resolve()是nodejs中的函数,只有require了jquery就进行匹配// require.resolve():应用外部的 require() 机制查问模块的地位,此操作只返回解析后的文件名,不会加载该模块。use: [  {    loader: 'expose-loader',    options: 'jquery' // 裸露成window.jquery  },  {    loader: 'expose-loader',    options: '$'  // 裸露成window.$  }]},(3)在每个模块中注入$,不须要在每个模块中再引入,能够应用 webpack.ProvidePlugin插件,再plugins中退出new webpack.ProvidePlugin({ // ----------------- webpack.ProvidePlugin()  $: 'jquery' // ----------------------------- 在每个模块中都注入$符}) 应用:间接在模块中就能够应用 $ , 并不需要在头部引入

图片解决

  • npm install file-loader url-loader html-withimg-loader -D
  • file-loader

    • 会生成一张图片到build的目录下,并将图片的名称返回回来
  • url-loader

    • <font color=red>能够设置大小限度,小于时,url-loader会将图片转成base64,大于时,应用file-loader加载图片</font>
    • 在options: {limit: 200 * 1024}来设置,200k,通过 ( <font color=red>limit</font> ) 来设置
    • <font color=red>options: {outputPath: 'img/'} ----- 将图片打包到img文件夹下</font>
    • 留神:(1)css所有文件独自抽离成一个文件放到一个文件夹中是应用 mini-css-extract-plugin (2) 图片独自抽离到文件夹中是url-loader中配置options的outputPath
  • html-withimg-loader

    • <font color=red>留神:应用该插件时可能报错,须要在url-loader的options中设置 esModule: false </font>
  • 图片的引入形式

    • 在js中引入,能够通过new Image().src等
    • 在css中,通过 background-iamge来引入
    • 在html中,通过 img 标签来引入
    file-loaderurl-loaderhtml-withimg-loaderwebpack.config.js{test: /\.(png|jpg|jpeg|gif)$/,use: [  {    // loader: 'file-loader',    loader: 'url-loader',    options: {      limit: 200 * 1024, // ----------------- 小于200k,应用base64编码图片,大于应用file-loader加载图片      esModule: false, // ------------------- 用于html-withimg-plugin失效      outputPath: 'img/', // ---------------- 输入到 img 文件夹中      publicPath: 'www.baidu.com/' // ------- 独自配置img的公共门路,而不是在output中全副配置    }  }]},{test: /\.html$/, // ----------------- html-withimg-loaderuse: 'html-withimg-loader'}应用:js中import one from './images/1.jpg'const imgx = new Image(100, 100)imgx.src = oneif (imgx.complete) {addChild()} else {imgx.onload = addChild}function addChild() {document.body.appendChild(imgx)}css中#div-image {width: 200px;height: 200px;background: url('./images/1.jpg');}html中<img src="./images/1.jpg" alt="" width="300" height="300">

动态资源分类

  • 图片

    • url-loader => options => outputPath: 'img/'
  • css

    • mini-css-extract-plugin => filename: 'css/main.css'
  • 公共门路

    • <font color=red>在 output => publicPath 中设置公共门路,当打包后html加载资源时,都会加上公共门路 </font>
    • 比方:不是放在本地,放在CDN上,就要在所有门路上服务器的地址
  • 如果只是想给图片加载公共门路,而其余资源不加的话,就要在url-loader中独自设置,同时output的publicPath不必设置

    动态资源分类图片{test: /\.(png|jpg|jpeg|gif)$/,use: [  {    // loader: 'file-loader',    loader: 'url-loader',    options: {      limit: 200 * 1024,      esModule: false,       outputPath: 'img/' // ----------------------------- 所有图片将放到打包后的 ( img文件夹中 )    }  }]},cssplugins: [new MiniCssExtractPlugin({  filename: 'css/main.css' // ---------------------------- 所有css将放入到打包后的 ( css文件夹的main.css中 )}),],公共门路:所有门路都加output: {filename: 'index.[hash:8].js',path: path.resolve(__dirname, 'dist'),publicPath: 'www.baidu.com/' // -------------------------- 设置公共门路,打包后html中引入的资源都会加上这个前缀门路},公共门路:只是图片加{test: /\.(png|jpg|jpeg|gif)$/,use: [  {    // loader: 'file-loader',    loader: 'url-loader',    options: {      limit: 200 * 1024,      esModule: false,       outputPath: 'img/',      publicPath: 'www.baidu.com/' // ---------------------- 只给图片设置publicPath    }  }]},

打包多页利用

  • entry:能够是一个对象
  • output:'[name].js' => []是占位符 => [name]的值就是entry对象中的key

    const path = require('path');const HtmlWebpackPlugin = require('html-webpack-plugin')module.exports = {mode: 'development',entry: {  home: './src/home.js',  other: './src/other.js',},output: {  filename: '[name].js', // ---------------------------- 占位符,name示意entry对象中的 key  path: path.resolve(__dirname, 'dist')},devServer: {  contentBase: path.resolve(__dirname, 'dist'),  port: 5000,  open: true,  compress: true,},plugins: [  new HtmlWebpackPlugin({ // ---------------------------- html-webpack-plugin能够new多个    template: './src/index.html',    filename: 'home.html',    chunks: ['home'] // --------------------------------- 每个chunk对应加载哪些打包后的 js 文件,即 output指定的输入js文件  }),  new HtmlWebpackPlugin({    template: './src/index.html',    filename: 'other.html',    chunks: ['other']  }),]}

source-map 源码映射

  • devtool: 'source-map' // 显示行数,产生map文件
  • devtool: 'eval-source-map' // 显示行数,不产生map文件

watch 实时打包

watch: true,watchOptions: {  aggregateTimeout: 300, // 防抖  poll: 1000, // 每秒询问1000次  ignored: /node_modules/},aggregate: 总计,共计的意思poll:是轮询的意思

clean-webpack-plugin ---------- 删除打包后的文件夹,默认是删除 output.paht 指定的文件夹

  • npm install --save-dev clean-webpack-plugin
  • clean-webpack-plugin

    const {CleanWebpackPlugin} = require('clean-webpack-plugin')plugins: [new CleanWebpackPlugin() //---------- 默认是删除 output.path 指定的文件夹],

copy-webpack-plugin ---------- 复制文件

  • npm install copy-webpack-plugin -D

    const CopyWebpackPlugin = require('copy-webpack-plugin')plugins: [new CopyWebpackPlugin([{  from: './src/copywebpackplugin', // -------- 将该文件夹中的内容拷贝到dist文件夹中  to: './' // -------------------------------- 默认是输入到 output.path 指定的文件夹中}])],

BannerPlugin ---------------- webpack自带的plugin,用于在js文件结尾正文一些阐明内容

new webpack.BannerPlugin({ banner: ' by woow_wu7'})比照:webpack自带插件还有 webpack.ProvidePlugin ------- 将jquery裸露到每个模块,为$符号// 在每个模块中注入$,不须要在每个模块中再引入,能够应用 webpack.ProvidePlugin插件,再plugins中退出new webpack.ProvidePlugin({    $: 'jquery'}),

webpack-dev-server => before(app) => 实现mock数据

devServer: {  contentBase: path.join(__dirname, 'dist'),  port: '5000',  compress: true,  open: true,  // proxy: {  //   '/api': {  //     target: 'http://localhost:7000',  //     pathRewrite: {  //       '^/api': ''  //     }  //   }  // },  before(app) { // ------------------------------ before钩子函数,app即express()    app.get('/api/user', (req, res) => {      res.json({        user: 'woow_wu7'      })    })  }},const api = new XMLHttpRequest()api.open('GET', '/api/user', true) // -------------------------------------- 在webpack-dev-sesrver的bofore(app)中曾经提供了服务路由并返回数据// -------------------------------------- 所以这里能够间接拜访api.onload = function() {  const res = api.response  console.log(res, 'res')}api.send()

resolve 解析 -------------------- alias 和 extensions

  • <font color=red>resolve.alias :创立 import 或者 export 的别名</font>
  • <font color=blue>resolve.extensions : 主动解析确定的扩大</font>

    • 留神:<font color=blue>( resolve.extenstions ) 是一个 ( 数组 ),成员是 ( .后缀的字符串 )</font>
    • 留神:import SomeFile from "./somefile.ext",要想正确的解析,一个蕴含“*”的字符串必须蕴含在数组中。
    • 利用:在引入文件时,省略后缀 ( 优先级是从左往右 )
    resolve.aliasresolve.extensions设置:resolve: {alias: {  Imageff: path.resolve(__dirname, 'src/images'), // 用 Imageff 代替 images文件夹的绝对路径},extensions: ['.js', '.css', '.less', '*'] // ------------------------------------------------- import时省略后缀时,先找.js文件,再找.css文件// ------------------------------------------------- 留神:'*' 示意所有类型的文件},应用:import imagex from 'Imageff/1.jpg'

webpack.DefinePlugin ---------- 定义环境变量,即创立一个在编译时能够全局配置的常量

(1)new webpack.DefinePlugin({ // ----------------------- new webpack.DefinePlugin()  DEV: JSON.stringify('DEV'), // -------------------- ( JSON.stringify('DEV') === "'DEV'" )  BOOLEAN1: true,  BOOLEAN2: JSON.stringify(true) // ----------------- 这里BOOLEAN2和下面BOOLEAN1等价}),// -------------------------------------------------- 所以数据能够用JSON.stringify()来操作,传入什么类型就返回什么类型(2) webpack的插件常有的有:new webpack.DefinePlugin({ // ----------------------- webpack.definePlugin  DEV: JSON.stringify('DEV'),  BOOLEAN1: true,  BOOLEAN2: JSON.stringify(true)}),new webpack.ProvidePlugin({ // ---------------------- webpack.ProvidePlugin  $: 'jquery'}),new webpack.BannerPlugin({ // ----------------------- webpacck.BannerPlugin  banner: 'by woow_wu7'})

webapck-merge ------------ 辨别不同环境

  • npm install webapck-merge -D
  • webpack-merge

    const merge = require('webpack-merge'); // --------------- webpack-mergeconst base = require('./webpack.base.js')module.exports = merge(base, {mode: 'developmen',devServer: {  contentBase: './dist',  port: 8000,  open: true,  compress: true}})

noParse

  • <font color=red>module.noParse 如果包没有其余的依赖项,则能够通过noParse使webpack不去解析该包的依赖关系,进步构建速度</font>
  • 所以该包中:不能含有import,require,define等任何的导入机制
  • module.noParse
module: {    noParse: /jquery|lodash/, // ------ 不去解析jquery或lodash的依赖关系,因为它们俩都没有依赖其余库,从而进步构建速度    rules: []}    

webpack.IgnorePlugin

  • resourceRegExp:匹配(test)资源申请门路的正则表达式。
  • contextRegExp:(可选)匹配(test)资源上下文(目录)的正则表达式。
  • webpack自带的插件

    • webpack.IgnorePlugin--------- 疏忽引入
    • webpack.DefinePlugin--------- 定义全局常量
    • webpack.ProvidePlugin-------- 裸露包的名字改名
    • webpack.BannerPlugin--------- 打包的js的最后面注入一写信息字符串
    ----webpack.config.js => pluginsnew webpack.IgnorePlugin({ // ------------------------------- 示意从 ( moment ) 中引入 ( ./local ) 文件时,将不去引入./local文件夹中的文件// ------------------------------- 然而须要应用中文包,所以疏忽后,再 ( 手动引入 )resourceRegExp: /^\.\/locale$/,contextRegExp: /moment$/});-----index.jsimport moment from 'moment'import 'moment/locale/zh-cn'; // ------------------------------------ 手动引入包moment.locale('zh-cn')const d = moment("20111031", "YYYYMMDD").fromNow();console.log(d, 'd')

webpack.DllPlugin 和 webpack.DllReferencePlugin -------- 动态链接库,独自打包一些库

  • webpack.DllPlugin:=> name,path
  • webpack.DllReferencePlugin:=> manifest
  • manifest:清单

    ------webpack.config.react.js // ------------------------------------- 专门用来打包react和react-dom(1)const path = require('path')const webpack = require('webpack')module.exports = {mode: 'development',entry: {  react: ['react', 'react-dom']},output: {  filename: '_dll_[name].js', // ---------------------------- 打包后是 ( _dll_react.js )  path: path.resolve(__dirname, 'dist'),  library: '_dll_[name]', // 打包后,把打包的文件赋值给_dll_[name]变量,全局变量的名称  libraryTarget: 'var', // 变量的类型 },plugins: [  new webpack.DllPlugin({ // -------------------------------- webpack.DllPlugin 用于生成工作清单,即manifest.json文件    name: '_dll_[name]',      // name 示意裸露到全局的库的名称    // 留神: name 和 output.librry 要统一    // 留神:name代表的就是打包后生成在dist目录下的 manifest.json 中的变量值    path: path.resolve(__dirname, 'dist', '[name].manifest.json')  })]}(2)打包:应用命令打包:npx webpack --config webpack.config.react.js生成:(1) _dll_react.js // ----------------------------------------- 文件的内容会赋值给一个变量 ( _dll_react )(2) manifest.json文件------(3)webpack.config.jsplugins: [new webpack.DllReferencePlugin({ //----------------------------- 援用清单  manifest: path.resolve(__dirname, 'dist', 'react.manifest.json') //- 援用下面打包生成的 manifest.json 文件})],-----(4)index.jsimport react from 'react'import {render} from 'react-dom'render(<h1>jsx</h1>, document.getElementById('root'))(5)成果:打包main.js从( 1.4M => 500KB )

happypack ------------------------- 多线程打包

  • happypack

    module.rules:{test: /\.js$/,use: 'happypack/loader?id=js',},}plugins:new HappyPack({  id: 'js',  use: [{      loader: 'babel-loader'  }]})

代码宰割 optimization.splitChunks

- 抽离公共组件和第三方组件,能够缓存,则不须要反复加载- optimization => splitChunks => cacheGroups => vendors和commons- priority: 是优先的意思- optimization: {    minimizer: {}, // 压缩css和js的配置项    splitChunks: {      cacheGroups: {        commons: {          name: 'commons',          chunks: 'initial',          minChunks: 1,          priority: 10,          minSize: 0,        },        vendors: { // vendor是小贩的意思          test: /node_modules/, // 范畴是node_modules中的第三方依赖,留神zhe          name: 'vendors', // 抽离进去的包的名字          chunks: 'initial', // 初始化加载的时候就抽离公共代码          minChunks: 1, // 被援用的次数          priority: 11, // priority: 是优先级的意思,数字越大示意优先级越高          minSize: 0,        }      }    }  }

@babel/plugin-syntax-dynamic-import --------------------- 实现懒加载

  • npm install @babel/plugin-syntax-dynamic-import 语法动静导入
  • syntax:语法
  • dynamic:动静

    (1) 在webpack.config.js中配置,或者在.babelrc中配置module: {  rules: [      {          test: /\.js$/,          use: [              {                  loader: 'babel-loader',                  options: {                      presets: {                          ['@babel/preset-env'],                          ['@babel/preset-react'],                      },                      plugins: {                          ['@babel/plugin-proposal-decorators', {'legacy': true}],                          ['@babel/plugin-proposal-class-properties', {'loose': true],                          ['@babel/plugin-transform-runtime'],                          ['@babel/plugin-syntax-dynamic-import']                      }                  }              },              {                  loader: 'eslint-loader',                  options: {                    enforce: 'pre'                  }              }          ]      }  ]}(2) index.jsconst button = document.createElement('button')button.innerHTML = 'button'button.addEventListener('click', () => {console.log('button clicked')import('./dynamic.js').then(res => console.log(res.default, 'res')) // ----------- 语法动静导入,应用jsonp实现})document.body.appendChild(button)
  • import只能用在顶部报错:解决须要装置 babel-eslint 插件
  • 装置,babel-eslint插件,并且在 .eslintrc.json中做如下配置
  • {
    "parser": "babel-eslint",
    "parserOptions": {

      "sourceType": "module",  "allowImportExportEverywhere": true, // ---------------------- 在所有中央都能import和export

    },
    "rules": {

      "indent": "off"

    },
    "env": {}
    }

  • https://stackoverflow.com/que...

webpack.HotModuleReplacementPlugin 和 webpack.NameModulesPlugin --------- 热更新

- 热更新- new webpack.HotModuleReplacementPlugin() // 热更新- new webpack.NameModulesPlugin() // 打印热更新模块的门路- 1. 首先在 devServer 配置中减少 hot: true,示意开启热更新- 2. 在plugins数组中 new webpack.HotModuleReplacementPlugin() 和 new webpack.NameModulesPlugin()- 3. 在入口js文件中:(1)devServer: {    contentBase: path.join(__dirname, 'dist'),    port: '5000',    compress: true,    open: true,    hot: true, //-------------------------------------------------------- hot: true 开启热更新模式    // proxy: {    //   '/api': {    //     target: 'http://localhost:7000',    //     pathRewrite: {    //       '^/api': ''    //     }    //   }    // },    before(app) {      app.get('/api/user', (req, res) => {        res.json({          user: 'woow_wu7'        })      })    }  },  (2) plugins: [    new HtmlWebpackPlugin({      template: './src/index.html',      filename: 'index.html',      hash: true,      minify: {        removeAttributeQuotes: true,        collapseWhitespace: true,      }    }),    new MiniCssExtractPlugin({      filename: 'css/main.css'    }),    // new CleanWebpackPlugin(),    new CopyWebpackPlugin([{      from: './src/copywebpackplugin',      to: './'    }]),    new webpack.DefinePlugin({      DEV: JSON.stringify('DEV'),      BOOLEAN1: true,      BOOLEAN2: JSON.stringify(true)    }),    new webpack.IgnorePlugin({      resourceRegExp: /^\.\/local$/,      contextRegExp: /moment$/,    }),    new webpack.ProvidePlugin({      $: 'jquery'    }),    new webpack.BannerPlugin({      banner: 'by woow_wu7'    }),    new webpack.NamedModulesPlugin(), // --------------------- webpack.NameModulesPlugin 打印更新的模块门路    new webpack.HotModuleReplacementPlugin() // -------------- webpack.HotModuleReplacePlugin 热更新插件    // new webpack.DllReferencePlugin({    //   manifest: path.resolve(__dirname, 'dist', 'manifest.json')    // })  ],(3) index.jsimport hotNow from './hot';console.log(hotNow)if (module.hot) { // ----------------------------------------- 如果hot.module存在,开启了热更新  module.hot.accept('./hot.js', function(){ // --------------- 监听 './hot.js'的扭转,如果扭转,执行回调    const res = require('./hot.js') // ----------------------- 从新加载,并打印最新值    console.log(res, '热更新后新的返回值')  })}

编写一个loader

  • loader就是一个 ( <font color=red>函数</font> ),第一个参数示意 ( <font color=red>该loader匹配的文件的源代码</font> )
  • 不能写成箭头函数,因为须要通过 this 获取更多 api
  • this.query

    • 如何获取loader中的配置参数:( <font color=red>options对象</font> ) this.query指向的就是options对象

      • 如果 loader 中没有 options,而是以 query 字符串作为参数调用时,this.query 就是一个以 ? 结尾的字符
      • 留神:this.query曾经废除,应用 <font color=red>loader-utils</font> 中的 <font color=red>getOptions</font> 来获取 options 对象
  • loader-utils

    • npm install loader-utils -D
    • 通过loader-utils中的 getOptions 获取 loader的options配置对象
  • this.callback

    • 参数

      • 第一个参数:err // Error 或者 null
      • 第二个参数:content // string或者buffer,即解决过后的源代码
      • 第三个参数:sourceMap // 可选,必须是一个能够被这个模块解析的 source map
      • 第四个参数:meta //可选,即元数据
      • // https://www.webpackjs.com/api...
  • this.async

    • <font color=red>解决loader中的异步操作</font>
    • <font color=red>this.async()办法返回 this.callback</font>
  • <font size=6 >resolveLoader</font>

    • webpack配置项
    • resolveLoader的配置项

      • 代码:modules: ['node_modules', './src/loaders']
      • 示意:在寻找loader的时候,先去node_modules文件夹中共寻找,没找到再去'./src/loaders'文件夹中找
  • loader API

最简略的replace-loader

replace-loader---目录:src/loaders/replace-loader.jsmodule.exports = function(source) { // --------------------- loader就是一个函数,参数是源码  return source.replace('hello', 'hi!')}目录:webpack.config.jsmodule: {  rules: [{      test: /\.js$/,      use: [path.resolve(__dirname, './src/loaders/replace-loader.js')]  }]}

升级版

目录:webpack.config.js(1)module.exports = {    resolveLoader: { // ---------------------------- resolveLoader      modules: ['node_modules', './src/loaders/'] // 示意在寻找loader时,先去node_modules中找,再去loaders文件夹中找    },](2){  loader: 'replace-loader', // -------------------- 加载replace-loader,即文件名  options: {    name: 'hi!!!!!!!!@!!!!!!' // ---------------------- options的name属性  }}目录:src/loaders/replace-loaderconst loaderUtils = require('loader-utils') // ---------------- loader-utilsmodule.exports = function(source) {  console.log(this.query) // options配置对象  const options = loaderUtils.getOptions(this) // ------------- loaderUtils.getOptions(this) 获取 options  const callback = this.async() // ---------------------------- 解决异步loader, this.async()返回this.callback  setTimeout(function() {    const result = source.replace('hello', options.name)    callback(null, result)  }, 1000)}

材料

我的简书:https://www.jianshu.com/p/1fc...
webpack优化:https://juejin.im/post/684490...
process.env https://www.jianshu.com/p/c8f...