关于javascript:深入16-webpack

53次阅读

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

导航

[[深刻 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.js
version
- 软件的以后版本
license
- 代码许可证
- MIT
- ISC
author 和 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-server
css 相干
- style-loader
- css-loader
- less-loader
- 抽离 css
- mini-css-extract-plugin
- 前缀
- postcss-loader
- autoprefixer
- 压缩 css
- (留神:用于生产环境才去压缩 css,因为影响打包速度)
- optimize-css-assets-webpack-plugin
- uglifyjs-webpack-plugin
js 相干
- 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

    proxy
    var 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-server
    devServer: { // --------------------------------------------- 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.js
const 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.js
var 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-plugin
    const 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-loader
    css-loader
    less-loader 还得装置 less
    sass-loader 还得装置 node-sass
    module: { // 模块
    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.jsmodule => 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-loader
    url-loader
    html-withimg-loader
    webpack.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-loader
    use: 'html-withimg-loader'
    }
    应用:js 中
    import one from './images/1.jpg'
    const imgx = new Image(100, 100)
    imgx.src = one
    if (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 文件夹中)
    }
    }
    ]
    },
    css
    plugins: [
    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.alias
    resolve.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-merge
    const 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 => plugins
    new webpack.IgnorePlugin({// ------------------------------- 示意从 ( moment) 中引入 (./local) 文件时,将不去引入./local 文件夹中的文件
    // ------------------------------- 然而须要应用中文包,所以疏忽后,再 (手动引入)
    resourceRegExp: /^\.\/locale$/,
    contextRegExp: /moment$/
    });
    -----
    index.js
    import 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.js
    plugins: [
    new webpack.DllReferencePlugin({ //----------------------------- 援用清单
    manifest: path.resolve(__dirname, 'dist', 'react.manifest.json') //- 援用下面打包生成的 manifest.json 文件
    })
    ],
    -----
    (4)
    index.js
    import 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.js
    const 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.js
import 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.js
module.exports = function(source) { // --------------------- loader 就是一个函数,参数是源码
return source.replace('hello', 'hi!')
}
目录:webpack.config.js
module: {
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-loader
const loaderUtils = require('loader-utils') // ---------------- loader-utils
module.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…

正文完
 0