Webpack

简介

代码转换/文件优化/代码分割/模块合并/自动刷新/代码校验/自动发布
和 gulp 主要区别:
webpack 主要站在整个项目的角度,强调处理各种依赖关系,处理各种文件
gulp强调流式管道操作,强调点对点之间的关系

安装

windows

4.x 中命令行由 `webpack-cli` 提供
npm init -y
npm i webpack webpack-cli -D

如果执行 webpack 时报错 webpack: command not found

npm i webpack webpack-cli -g
五个核心
  1. entry
  2. output
  3. loader
  4. plugins
  5. mode
入口出口
entry: './src/main.js',
output: {
  filename: 'app.js',
  path: `${__dirname}/dist`
}

CSS

处理 css

npm i style-loader css-loader -D

打包 css

npm i mini-css-extract-plugin -D

const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');

module.exports = {
  mode: 'development',
  entry: './src/main.js',
  output: {
    filename: 'app.js',
    path: `${__dirname}/dist`,
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          MiniCssExtractPlugin.loader,
          'css-loader',
        ],
      },
    ],
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './public/index.html',
    }),
    new MiniCssExtractPlugin({
      filename: 'css/index.css',
      // publicPath: ''
    }),
  ],
};

处理 css 兼容

默认生产环境,如果需要开发环境要手动设置 process.env.NODE_ENV = ‘development’
npm i postcss-loader postcss-preset-env -D

webpack.config.js

module: {
  rules: [
    {
      test: /\.css$/,
      use: [
        MiniCssExtractPlugin.loader, // 打包 css 
        'css-loader',
        {
          loader: 'postcss-loader', // 处理 css 兼容
          options: {
            ident: 'postcss',
            plugins: () => [require('postcss-preset-env')],
          },
        },
      ],
    },
  ],
}

package.json

"browserslist": {
  "development": [
    "last 1 chrome version",
    "last 1 firefox version"
  ],
  "production": [
    ">0.1%",
    "not dead",
    "not op_mini all"
  ]
}
压缩 css

npm i optimize-css-assets-webpack-plugin -D

const optimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin');
...

plugins: [
  new optimizeCssAssetsWebpackPlugin()
]
处理 stylus

npm i stylus stylus-loader -D

rules: [
  {
    test: /\.styl(us)*$/,
    use: ['style-loader', 'css-loader', 'stylus-loader'],
  },
]

JS

使用 eslint 规范 js

npm i eslint eslint-loader eslint-plugin-import eslint-config-airbnb-base -D
eslint-loader依赖 eslint
airbnb 风格的代码规范包eslint-config-airbnb-base依赖 eslint-plugin-importeslint

package.json

"eslintConfig": {
  "extends": "airbnb-base" // 继承 airbnb-base
}

webpack.config.js

rules: [
  {
    test: /\.js$/,
    loader: 'eslint-loader',
    exclude: /node_modules/,
    options: {
      fix: true, // 自动修复
    }
  }
]
babel 语法降级

基本 js 兼容问题处理: @babel/preset-env 只能处理基本的 es6 => es5 比如箭头函数 const let
全部 js 兼容处理: @babel/polyfill

npm i @babel/core babel-loader @babel/preset-env -D

rules: [
  {
    test: /.\js$/,
    loader: 'babel-loader',
    exclude: /node_modules/,
    options: {
      presets: ['@babel/preset-env'] // 预设,告诉 babel 如何处理兼容。这里使用 @babel/preset-env 预设
    }
  }
]
polyfill

npm i @babel/polyfill -D
babel 基本的不能处理如如 new Promise() 在 IE 11 中会报错,这个 js 会将所有的兼容问题全都纳入进来
main.js

import '@babel/polyfill' // 引入后打包文件会增加 440kb 左右

问题: 体积太大,全部兼容引入
解决:
npm i core-js -D

rules: [
  {
    test: /\.js$/,
    loader: 'babel-loader',
    exclude: /node_modules/,
    options: {
      presets: [
        [
          '@babel/preset-env',
          {
            useBuiltIns: 'usage', // 按需加载
            corejs: {
              version: 3, // 指定 corejs 版本
            },
            targets: { // 兼容到哪个版本
              chrome: '60',
              firefox: '50',
              ie: '9',
            },
          },
        ],
      ],
    },
  },
],

处理 html

npm i html-webpack-plugin -D
webpack.config.js

const HtmlWebpackPlugin = require('html-webpack-plugin');
plugins: [
  new HtmlWebpackPlugin({
    template: '../public/index.html',
    
  })
]
处理图片
  1. css 中的本地图片依赖
    npm i url-loader file-loader -D
    webpack.config.js
module.exports = {
  module: {
  	rules: {
	  test: /\.(jpg|png|gif)$/,
	  loader: 'url-loader',
	  options: {
	    limit: 8 * 1024, // 小于 8KB 转为 base64 减少 HTTP 请求次数,增加 CPU 负担
	    name: '[hash:10].[ext]', // hash 10 位,原拓展名
	  }
	}
  }
}
  1. html 中的本地图片依赖处理
    npm i html-loader -D 负责引入 img 处理 html 的 img,从而能被 url-loader 处理
html
	<img src="./vue.png" />

weboack.config.js
	rules: {
	  test: /\.html$/,
	  loader: 'html-loader',
	}
处理 iconfont

main.js

import './assets/icon/iconfont.css'

webpack.config.js

module: {
  rules: [
    { test: /\.css$/, use: ['style-loader', 'css-loader'] },
    { test: /\.(eot|svg|ttf|woff|woff2)$/, loader: 'file-loader', options: { name: '[hash:10].[ext]' } },
  ],
}
devServer

反复通过 webpack 构建很麻烦,需要 devServer 用来自动化打包,打开浏览器,自动更新等开发功能

安装:npm i webpack-dev-server
启动:npx webpack-dev-server

module.exports = {
  /**
   * 在内存中编译打包,没有任何输出,只做开发调试
   * 安装 npm i webpack-dev-server -D
   * 启动 npx webpack-dev-server
   */
  devServer: {
    port: 3000,
    open: true,
    contentBase: `${__dirname}/dist`, // 选择文件位置 但据说 配置 HtmlWebpackPlugin 后这个配置不起任何作用
    compress: true, // gzip 压缩
  },
};

性能优化

HMR -> Hot Module Replacement

webpack-dev-server 给予自动更新的能力,但是不论代码里修改哪里,都会全局打包,如果项目大很影响性能。
我们需要 HMR 热模块替换功能优化,来按需热更新

devServer: {
  hot: true, // 但工作中不建议开启这个东西
}

样式文件:支持 HMR, 因为 style-loader 支持,但是生产环境我们需要把 css 抽出来,所以用 mini-css-extract-plugin 插件,但好像目前也支持热更新
JS 文件:不支持 HMR
HTML:不支持 HMR,不支持热更新
解决: entry: [ './src/main.js', './public/index.html' ]

Source Map

构建后的代码与源代码之间的映射,可以更好的调试源代码

[inline|hidden|eval]-[nosources]-[cheap-[module]]-source-map
内联:速度快,内联到 js 文件中,不单独生成 map 文件。但会让代码体积变得非常大,生产模式下不可用
外部:单独生成 map 文件,独立与 js

代码方式说明
inline-source-map内联只生成一个内联 source-map
hidden-source-map外部只能提示错误原因,不能追踪到源代码对应位置,只隐藏源代码,不隐藏构建后的代码
eval-source-map内联每一个文件都有对应的 source-map 且在 eval 中
source-map外部
nosources-source-map外部只能提示错误原因,不能追踪到源代码对应位置。全部隐藏
cheap-source-map外部只精确到错误行,不能准确到行内的具体哪里错误
cheap-module-source-map外部只精确到错误行,不能准确到行内的具体哪里错误

hidden 和 nosources 就是为了隐藏源代码防止代码泄漏的

方式代码
速度eval-cheap > eval > inline > cheap
调试友好source-map > cheap-module-source-map > cheap-source-map
综合eval-source-map -> vue/react 脚手架默认方式
生产环境:内联让体积变大不考虑source-map / cheap-module-source-map
module会将 loader 等的三房 source-map 带进来

webpack 官网说明 https://www.webpackjs.com/configuration/devtool/
三方博客https://www.jianshu.com/p/62dc120d96d0

devtool构建速度重新构建速度生产环境品质(quality)
(none)++++++yes打包后的代码
eval++++++no生成后的代码
cheap-eval-source-map+++no转换过的代码(仅限行)
cheap-module-eval-source-mapo++no原始源代码(仅限行)
eval-source-map+n原始源代码
cheap-source-map+on转换过的代码(仅限行)
cheap-module-source-mapo-no原始源代码(仅限行)
inline-cheap-source-map+on转换过的代码(仅限行)
inline-cheap-module-source-mao-no原始源代码(仅限行)
source-mapyes原始源代码
inline-source-mapno原始源代码
hidden-source-mapyes原始源代码
nosources-source-mayes无源代码内容

+++ 非常快速, ++ 快速, + 比较快, o 中等, - 比较慢, -- 慢

devtool: 'source-map',
oneOf

所有类型的文件都会走一边 loader,命中的处理,不命中的走下一个,这样会浪费性能。
但是不能有两个loader 处理同一个配置,比如eslint 的 js loader和 babel 的 js loader 。这时候就需要把 eslint 的loader 提取到 oneOf 外边

rules: [
  oneOf: [
    {
      test: /\.css$/,
      use: []
    }
  ]
]
缓存
  1. babel 缓存

babel-loader https://www.webpackjs.com/loaders/babel-loader/

{
  test: /\.js$/,
  loader: 'babel-loader',
  exclude: /node_modules/,
  options: {
    preset: [],
    ...,
    cacheDirectory: true, // 开启 babel 缓存
  }
}
  1. 文件资源缓存

hash: webpack 每次打包都会有一个唯一的 hash,利用这个可以做到每次不同打包 hash 不同。但是一个文件改动重新打包所有文件 hash 都变

chunkHase: 如果文件来自于同一个 chunk,则 hash 一致
chunk:所有在 webpack.config.js 中entry 引入的都会生成一个 chunk,

contenthash:根据单个文件生成的 hash 文件不变 hash 不变

output: {
  path: `${__dirname}/dist`,
  filename: 'js/app[contenthash:10].js'
},

new MiniCssExtractPlugin({
  filename: 'css/index[contenthash:10].css'
})
tree shaking

概念:去除无用代码,缩小项目体积

  1. 必须使用 ES6 模块化
  2. 开启 production 模式

缺点: 可能会无意中把 css/ @babel/polyfill 等文件删掉
解决:
package.json

"sideEffects": "[*.css]", // 把 css 标记为不进行 tree shaking 的文件类型
code split

代码分割可以将单页应用分割成小分按需加载。比如路由分割 -> 单页通过路由切换组件






使用
  1. 安装
  2. 配置 webpack.config.js 文件
  3. 解析文件 , 获得配置对象
  4. 根据配置对象进行打包构建
  5. webpack -w自动监听文件更改,避免修改后重复输入webpack指令

根目录下新建 src/index.js 作为目标文件
配置 webpack.config.js
终端输入 npx webpack || 终端内输入webpack 文件名 出口路径 可以处理单个文件(测试不管用)

webpack.config.js

// 通过 npx webpack直接进行打包
/*
  webpack配置文件默认叫 webpack.config.js | webpackfile.js
  在 nodemodules/webpack-cli/bin/config/config-yargs.js 中有相关代码,搜索 webpackfile.js
  可在 package.json 的脚本中指定配置文件,通过 npm run build 执行
  "scripts: {
    "build": "webpack --config my_webpack"
  }"
  
  也或者在 npm run build -- --config my_webpack.js 指定配置文件
  -- 是让命令知道后边跟的是字符串
*/
// commonjs 语法,也就是 nodejs 用的规范
const path = require('path')
module.exports = {
  mode: 'development', // development | production
  entry: './src/index.js', // 入口文件
  output: {
    filename: 'bundle.js', // 出口文件名字
    // path.resolve 方法可以将任意路径解析为绝对路径
    // __dirname 可以写也可以不写,不写已当前目录解析出来个绝对路径
    // __dirname:已当前目录解析
    path: path.resolve(__dirname, 'dist'), // 打包路径,必须是一个绝对路径
  }
}

npx webpack 后,默认找 nodemodule/.bin/webpack.cmd,如果有
当命令中键入 npx webpack 时,webpack 发现没有通过命令的形式指定出入口,就会去找 webpack.config.js 文件,当找到配置文件后,webpack 会去解析执行这个配置文件。当解析执行完配置文件后,导出配置对象,进行打包处理

包的查找规则
1.找项目根目录中有没有 node_modules 的文件夹
2.在 node_modules 中找包对应的文件夹
3.在 文件夹中找 package.json
4.package.json中找 main 属性,main 属性指定了这个包在加载时的入口文件

plugins

webpack 默认只支持 js 模块,如果需要处理其他的文件格式,就必须通过 plugins

loader

默认 webpack 除了 js 外不支持其他语言,需要通过 loader 转换成 js 后统一处理

src/index.css
  body:{marigin:0 auto}
src/index.js
  require('./index.css')
npx webpack ==> 报错不识别 body:{marigin:0 auto} 提示需要对应的 loader

webpack 4.x


注意:直接 import ‘./index.css’ 不会向外暴露任何东西,所以不需要 import xx from ‘xx’ 接收
如果要模块化处理,需要接收。在 node_modules 中安装的包,引入时不用写路径,直接 import xx from '包名'即可


目录分析

一般 webpack 中,带s的是数组,不带s的是对象

根目录
	dist
		main.js				打包出口文件
	src
		index.js			打包入口文件
	webpack.config.js  		配置文件
	.babelrc				配置 babel


webpack.config.js

webpack.config.js 是默认的文件名,可以手动指定配置文件。

命令行
npx webpack --config xx

或者在 package.json 中的 npm 脚本中配置
"scripts": {
  "build": "webpack --config xx"
}
这里不需要 "npx webpack""webpack" 会自动去 nodemu 中找 webpack

在或者在原来的 npm scripts 后加参数
"scripts": {
  "build": "xxx"
}
npm run build --config myWebpack.js

因为 wepback 是基于 node 的,支持 node 的所有语法和 API
在 webpack 4.x 中,约定大于配置。默认打包入口文件路径为 src/index.js

	const path = require('path');
	module.exports = {
	  entry: './src/main.js',
	  output: {
	    filename: 'js/main.js', // output filename
	    path: path.resolve(__dirname, 'dist') // output path mast be an absolute path
	  },
	  mode: 'development' || 'production'
	}
  1. entry&output
	// 单页面
	entry: '/src/index.js',
	output: {
		filename: 'js/bundle_[hash:6].js',
		path: path.resolve(__dirname, 'dist'),  //磁盘路径
	}
	// 多页面
	entry: {
	   key: value, // key为入口文件名,value为入口文件路径
	   key: value,
	},
	output: {
	   filename: 'js/[name]_[hash:6].js',
	   path: path.resolve(__dirname, 'dist')
	}
  1. module转化器

因为 node 默认打包. js 文件,但是对于图片 html 等文件无法主动处理,所以要配置第三方的 loader ,在 module 中配置。当 webpack 执行中只要遇到不能处理的就会来 module 寻找第三方匹配规则

  module: {
    rules: [
    	use两个顺序颠倒报错 exclude排除目录一定要写,否则跑起来报错
        { test: /\.css$/, use: ['style-loader', 'css-loader'] , exclude:/node_modules/ }, 
        { test: /\.js|jsx$/, use: 'bable-loader' , exclude:/node_modules/ }
    ]
  }
webapck-dev-devServer webpack 静态服务器

webpack 内置了一个开发的静态服务,内部通过 express 实现,可以把文件打包到内存中
安装:npm i webpack-dev-server -D
npx webpack-dev-server 或者在 package.json 中配一个

"scripts": {
  "server": "webpack-dev-server"
}
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
  entry: './src/a.js',
  output: {
    // 输出哈希文件名防止缓存问题
    filename: 'build.[hase:9].js',
    path: path.resolve(__dirname, 'build')
  },
  devServer: {
  	hot:true, // 热重载
    port: 3000,
    open: true, // 自动浏览器打开项目
    progress: true // 打包加入进度条
    contentBase: './build',
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html',
      filename: 'index.html',
      minify: {
        // 移除 html 属性值的双引号
        removeAttributeQuotes: true,
        // 代码压缩成一行
        collapseWhitespace: true,
      },
      // 添加哈希戳
      hash: true,
    })
  ]
}

当 npx webpack 后,发现虽然起个静态服务器,但是 webpack 没有处理 js 与 index.html 之间的依赖关系。在 src/index.html 中 scr 引入的 js 并没有在出口文件中自动生成 index.html


HtmlWebpackPlugin

安装:npm i html-webpack-plugin -D
插件的使用差不多都一样,webpack 本身基本上是用各种插件堆积起来的
src 下的 index.html 叫模版

使用插件
webpack.config.js

const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
  entry: './src/a.js',
  output: {
    // 输出哈希文件名防止缓存问题
    filename: 'build.[hase:8].js',
    path: path.resolve(__dirname, 'build')
  },
  devServer: {
    port: 3000,
    open: true,
    progress: true,
    contentBase: './build',
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html',
      filename: 'index.html',
      minify: {
        // 移除 html 属性值的双引号
        removeAttributeQuotes: true,
        // 代码压缩成一行
        collapseWhitespace: true,
      },
      // 添加哈希戳
      hash: true
    })
  ]
}

resolve文件拓展名省略
resolve: {
  extensions: ['.js', '.css', 'vue']
}

.babelrc

  1. 装包
  2. module 中加规则
  3. 配置 .babelrc
JSON 语法,内层双引号

{
  "presets": [	所有语法
  ],
  "plugins": [	所有插件
  ]
}


webpack-dev-server

帮我们监听项目代码,实时编译,不需要重新手动单个编译

  1. 安装

    1. npm i webpack-dev-server -D
    2. 注意:webpack-dev-server依赖于webpack,并强制要求项目本地安装webpackwebpack-cli
  2. 使用:

    1. 推荐方式:npm脚本中"dev": "webpack-dev-server --open --port 3000 --hot --contentBase 目录"
    2. 或者在webpack.config.js中配置devServer。然后cmd 键入 webpack-dev-server
    3. --open iexp --hot --port 3000 --host 127.0.0.1
    4. 热重载的意思为当代码改变时不需要 webpack 一下,刷新浏览器就能看到变化的结果,如果想自动刷新浏览器,目前未知解决办法。当前浏览器不能自动刷新
    5. https://blog.csdn.net/hsl0530hsl/article/details/78419693
  3. webpack-dev-server 默认打包好的 main.js 是托管在根目录的内存中,/main.js 可以访问到,但是在本地中找不到。所以在用script标签引入main.js时写./main.js 而不是 …/dist/main.js

  4. 常见报错

    1. webpack-dev-server不是内部或外部命令,也不是可运行的程序
      原因 : cmd只能直接执行全局-g安装的 , 无法直接运行项目-D安装的 , 所以无法把它当作脚本命令。
      解决方法:写入npm脚本运行
    2. code ENOSELF
      解决方法:把package.json中的"name": "webpack"值随便改一下
    3. 如果还不行,删node_modules重来
    4. 热重载失效
      1. webpack-dev-server并不能读取你的webpack.config.js的配置output。你在webpack.config.js里面的配置output属性是你用webpack打包时候才起作用的,对webpack-dev-server并不起作用
      2. webpack-dev-server打包生产的文件不出现在项目目录中,它默认打包的文件名是bundle.js,在电脑内存中存储,默认地址为项目根目录,你可以在http://localhost:3000/bundle.js尝试查看
  5. 注意事项

    1. webpack-dev-server会自动将打包好的文件并没有放在实际的物理磁盘中 , 而是直接托管到了电脑内存中,所以在根目录看不见对应的文件。可以认为以一种虚拟的形式托管在根目录中
    2. 由于打包后的文件托管在根目录下,引用时scr直接写根目录下对应的xxx.js
    3. 原因:磁盘速度没有内存快,不用每次重新请求磁盘文件,减少http请求次数
    4. 工作中,我们更推荐在npm脚本中直接书写
// 热重载第一步
	const webpack = require('webpack');
	devServer: {
		// 端口号
		port: 9000,
		// 自动打开
		open: true,
		// 指定托管的根目录
		contentBase:'src',
		// 热重载第二步
		hot: true,
	},
	plugins:[
		// 热重载第三步
	    new webpack.HotModuleReplacementPlugin(),
	  ]

html-webpack-plugin

src/index 作为模版,会原封不动的输出到 output 目录,所以在模版内不能引入任何资源,统一由 plugins 自动插入

将页面放在内存中

  1. 安装 npm i html-webpack-plugin -D
  2. 这个插件自动将内存中的main.js插入到页面当中,所以不需要手写script src

webpack.config.js

const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const htmlPlugin = new HtmlWebpackPlugin({
  template: path.join(__dirname, './src/index.html'),  // path.join(__dirname) 表示当前文件的路径,第二个参数为文件路径
  filename: 'index.html' // 在内存生成后的名字
}
module.exports = {
	plugins: [htmlPlugin], 注意这里是数组,不是对象
	mode: 'development'
}

webpack之 bable
  1. 安装 bablenpm i bable-loader@8.0.4 @bable/core @bable/preset-env -D

路径别名
resolve: {
  extensions: ['js', 'jsx', 'json'],	这里是省略后缀
  alias: {
    '@': path.join(__dirname, './src')
  }
}

处理 css 文件

webpack 默认不能模块化处理 css 文件,需要安装 loader

  1. cnpm i style-loader css-loader -D
  2. 配置 webpack 的 rules
当一个文件通过 import '.css' 时候,webpack 不识别,然后在配置里找第三方找 loader。
当发现有规则的时候,从右往左先调用 css-loader
然后交给 style-loader 二次处理,最后交给 webpack 打包处理
因此 use:['style-loader','css-loader'] 顺序不能错

module:{			所有第三方模块的配置
  rules:[			第三方匹配规则
  					打包处理第三方样式表 loader
    {text:/\.css$/,use:['style-loader','css-loader']},  
  ]
}
处理 scss 文件
  1. npm i sass-loader node-sass -D:注意这里都是 sass 不是 scss
  2. 配置 webpack
rules:[
  打包处理 scss 的 loader
  {text:/\.scss$/, use:['style-loader', 'css-loader', 'sass-loader'] }
]


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值