Webpack基础用法
Webpack初体验
-
项目搭建
-
首先创建一个文件夹
-
初始化
$ npm init -y
-
安装webpack
$ npm i webpack webpack-cli -D
-
-
创建webpack配置文件-webpack.config.js
"use strict"
const path = require('path')
module.exports = {
entry: './src/index.js',
output: {
path: path.join(__dirname, 'dist'),
filename: 'bundle.js'
},
mode: 'production'
}
- 创建src目录并新建index.js和hello.js
hello.js
export function helloworld() {
return 'Hello webpack';
}
index.js
import { helloworld } from './hello';
document.write(helloworld())
- 在package.json中配置webpack运行脚本
"scripts": {
"build": "webpack"
},
- 运行
$ npm run build
打包后,目录下会出现一个dist目录,里面有一个打包后的文件“bundle.js”
- 编写html文件,引入bundle.js文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="./bundle.js"></script>
</head>
<body>
</body>
</html>
打开页面即可看见页面中有“Hello webpack”字样
Webpack核心概念——entry
-
entry
Entry用来指定webpack的打包入口
- 单入口:entry是一个字符串
module.exports = { entry:'./src/index.js' }
- 多入口:entry是一个对象
module.exports = { entry:{ app:'./src/app.js', server:'./src/server.js' }
Webpack核心概念——output
-
output
output是用来告诉webpack如何将编译后的文件输出到磁盘
- 单入口配置
module.exports = { entry: './src/index.js' output: { filename: 'bundle.js’, path: __dirname + '/dist' } };
- 多入口配置
module.exports = { entry: { app: './src/app.js', search: './src/search.js' }, output: { filename: '[name].js', //使用占位符来为文件命名。name即为入口文件的name path: __dirname + '/dist' } };
Webpack核心概念——loaders
-
loaders
webpack开箱即用只支持JS 和JSON 两种文件类型,通过Loaders 去支持其它文件类型并且把它们转化成有效的模块,并且可以添加到依赖图中。 本身是一个函数,接受源文件作为参数,返回转换的结果
-
常见loaders
名称 描述 babel-loader 转换ES6,ES7等JS新特性语法 css-loader 支持.css文件的加载和解析 less-loader 将less文件转换成css ts-loader 将TS转换成JS file-loader 进行图片、字体等的打包 raw-loader 将文件以字符串的形式导入 thread-loader 多进程打包JS和CSS -
Loaders的用法
const path = require('path'); module.exports = { output: { filename: 'bundle.js' }, module: { rules: [ { test: /\.txt$/, use: 'raw-loader' } //test指定匹配规则 //use指定使用的loader名称 ] } };
-
Webpack核心概念——plugins
-
plugins
插件⽤用于 bundle ⽂文件的优化,资源管理理和环境变量量注⼊入,作⽤用于整个构建过程。
-
常见的 Plugins
名称 描述 CommonsChunkPlugin 将chunks相同的模块代码提取成公共js CleanWebpackPlugin 清理构建目录 ExtractTextWebpackPlugin 将CSS从bunide文件里提取成一个独立的CSS文件 CopyWebpackPlugin 将文件或者文件夹拷贝到构建的输出目录 HtmlWebpackPlugin 创建html文件去承载输出的bundle UglifyjsWebpaxkPlugin 压缩JS ZipWebpackPlugin 将打包出的资源生成一个zip包 -
Plugins 的⽤用法
const path = require('path');
module.exports = {
output: { filename: 'bundle.js' },
plugins: [ new HtmlWebpackPlugin({template: './src/index.html'}) ] };
Webpack核心概念——mode
-
mode
Mode ⽤用来指定当前的构建环境是:production、development 还是 none。 设置 mode 可以使⽤用 webpack 内置的函数,默认值为 production
-
Mode 的内置函数功能
选项 描述 development 设置process.env.NODE_ENV的值为development。
开启NamedChunksPlugin和NamedModulesPlugin。production 设置process.env.NODE_ENV的值为production。开启FlagDependencyUsagePlugin,FlagIncludedChunksPlugin,
ModuleConcatenationPlugin,NoEmitOnErrorsPlugin,
OccurrenceOrderPlugin,SideEffectsFlagPlugin,TerserPluginnone 不开启任何优化
解析ES6和React JSX
-
解析ES6
-
安装包
$ npm i @babel/core @babel/preset-env babel-loader -D
- 使⽤ babel-loader
const path = require('path'); module.exports = { entry: './src/index.js', output: { filename: 'bundle.js', path: path.resolve(__dirname, 'dist') }, mode:'production', module: { rules: [ {test: /\.js$/,use: 'babel-loader'} ] } };
- babel的配置文件".babelrc"
{ "presets": [ "@babel/preset-env” ], "plugins": [ "@babel/proposal-class-properties" ] }
-
-
解析React JSX
-
安装包
$ npm i react react-dom @babel/preset-react -D
- .babelrc
{ "presets": [ "@babel/preset-env”, "@babel/preset-react" //增加 React 的 babel pres ], "plugins": [ "@babel/proposal-class-properties" ] }
-
解析CSS、Less、Sass
-
CSS
- css-loader 用于加载 .css 文件,并且转换成 commonjs 对象 style-loader 将样式通过 ”style“ 标签插⼊入到 head 中
const path = require('path'); module.exports = { entry: './src/index.js', output: { filename: 'bundle.js', path: path.resolve(__dirname, 'dist') }, module: { rules: [ { test: /\.css$/, use: [ 'style-loader', 'css-loader' ] //node调用属于链式调用,执行顺序是从右到左 //使用css-loader解析css,然后将解析好的css传递给style-loader } ] } };
-
Less和Sass
- less-loader ⽤用于将 less 转换成 css
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
{
test: /\.less$/,
use: ['style-loader','css-loader',' less-loader']
}
]
}
};
解析图片和字体
-
解析字体
- file-loader 可以用于处理字体和图片
const path = require('path'); module.exports = { entry: './src/index.js', output: { filename: 'bundle.js', path: path.resolve(__dirname, 'dist') }, module: { rules: [ { test: /\.(woff|woff2|eot|ttf|otf)$/, use: ['file-loader'] } ] } };
-
url-loader 也可以处理图片和字体
可以设置较小资源⾃自动 base64
const path = require('path'); module.exports = { entry: './src/index.js', output: { filename: 'bundle.js', path: path.resolve(__dirname, 'dist') }, module: { rules: [ { test: /\.(png|svg|jpg|gif)$/, use: [ { loader: 'url-loader’, options: { limit: 10240 } } ] } ] } };
Webpack中的文件监听
-
文件监听
⽂文件监听是在发现源码发⽣生变化时,⾃自动重新构建出新的输出⽂文件
- webpack 开启监听模式,有两种⽅方式:
- 启动 webpack 命令时,带上 --watch 参数
- 在配置 webpack.config.js 中设置 watch: true原理分析
- webpack 开启监听模式,有两种⽅方式:
-
文件监听的原理分析
轮询判断⽂文件的最后编辑时间是否变化 某个⽂文件发⽣生了了变化,并不不会⽴立刻告诉监听者,⽽而是先缓存起来,等 aggregateTimeout
module.export = { //默认 false,也就是不不开启 watch: true, //只有开启监听模式时,watchOptions才有意义 wathcOptions: { //默认为空,不监听的文件或者文件夹,支持正则匹配 ignored: /node_modules/, //监听到变化发生后会等300ms再去执行,默认300ms aggregateTimeout: 300, //判断文件是否发生变化是通过不停询问系统指定文件有没有变化实现的,默认每秒问1000次 poll: 1000 } }e
-
热更新(让浏览器自动刷新):
- webpack-dev-server(WDS)
- WDS 不刷新浏览器
- WDS 不输出文件,而是放在内存中
- 配合使用 HotModuleReplacementPlugin插件——webpack内置插件
package.json
"scripts": { "build": "webpack", ”dev": "webpack-dev-server --open" //主要在开发环境中使用,生产环境是不需要用的 },
webpack.config.js
'use strict'; const path = require('path'); const webpack = require('webpack');//引入webpack module.exports = { entry: { index: './src/index.js', search: './src/search.js' }, output: { path: path.join(__dirname, 'dist'), filename: '[name].js' }, mode: 'development', module: { rules: [] }, plugins: [ //使用插件 new webpack.HotModuleReplacementPlugin(), ], devServer: { contentBase: './dist',//配置基础服务目录 hot: true//开启热更新 } };
-
webpack-dev-middleware(WDM)
WDM 将 webpack 输出的文件传输给服务器,适用于灵活的定制场景
constexpress = require('express'); constwebpack = require('webpack'); constwebpackDevMiddleware= require('webpack-devmiddleware'); constapp = express(); constconfig = require('./webpack.config.js'); constcompiler = webpack(config); app.use(webpackDevMiddleware(compiler, { publicPath: config.output.publicPath })); app.listen(3000, function () { console.log('Example app listening on port 3000!\n'); });
-
热更新原理
- webpack-dev-server(WDS)
文件指纹策略:chunkhash、contenthash和hash
- 文件指纹:打包后输出的文件名的后缀——用来做版本管理
-
文件指纹如何生成
-
Hash:和整个项⽬的构建相关,只要项目文件有修改,整个项目构建的 hash 值就会更改
-
Chunkhash:和 webpack 打包的 chunk 有关,不同的 entry 会生成不同的 chunkhash 值
-
Contenthash:根据文件内容来定义 hash ,文件内容不变,则 contenthash 不变
- 图片的hash和css/js资源的hash概念不一样,图片的hash是由图片内容决定的。如果css/js都是使用hash作为文件指纹的话,那么某一个js或者css发生改变,所有打包出来的css/js指纹都会变化。
- JS 没有 contenthash,只能从chunkhash和hadh里面选。但是hash对于js的含义是整个构建的文件指纹,每次构建有任何文件变了这个值都会变。所以js只能用chunkhash
-
-
JS 的文件指纹设置
module.exports = { entry: { app: './src/app.js', search: './src/search.js' }, output: { filename: '[name][chunkhash:8].js', path: __dirname + '/dist' } };
-
CSS 的文件指纹设置
设置 MiniCssExtractPlugin 的 filename, 使用 [contenthash]
$ npm i mini-css-extract-plugin -D
这个插件可以将css提取成一个独立的文件,但是这个插件的loader无法与style-loader一起使用。因为他们的功能是互斥的。style-loader是将样式插入到html文件里面,而这个插件是将样式提取出来。
module.exports = { entry: { app: './src/app.js', search: './src/search.js' }, output: { filename: '[name][chunkhash:8].js', path: __dirname + '/dist' }, module: { rules: [ { test: /.css$/, use: [ //'style-loader', MiniCssExtractPlugin.loader, 'css-loader' ] }, { test: /.less$/, use: [ //'style-loader', MiniCssExtractPlugin.loader, 'css-loader', 'less-loader' ] } ] }, plugins: [ new MiniCssExtractPlugin({ filename: `[name][contenthash:8].css` }); ] };
-
图⽚的文件指纹设置
设置 file-loader 的 name,使⽤用 [hash]
-
占位符设置:
占位符名称 含义 [ext] 资源后缀名 [name] 文件名称 [path] 文件的相对路径 [folder] 文件所在的文件夹 [contenthash] 文件的内容hash,默认是md5生成 [hash] 文件的内容hash,默认是md5生成 [emoji] 一个随机的指代文件内容emoj
const path = require('path'); module.exports = { entry: './src/index.js', output: { filename: 'bundle.js', path: path.resolve(__dirname, 'dist') }, module: { rules: [ { test: /\.(png|svg|jpg|gif)$/, use: [ { loader: 'file-loader’, options: { name: 'img/[name][hash:8].[ext] ' //hash默认32位,hash:8取前八位 } } ] } ] } };
-
HTML、CSS、和JavaScript代码压缩
-
HTML压缩
- 修改 html-webpack-plugin,设置压缩参数
module.exports = { entry: { app: './src/app.js', search: './src/search.js' }, output: { filename: '[name][chunkhash:8].js', path: __dirname + '/dist' }, plugins: [ new HtmlWebpackPlugin({ template: path.join(__dirname, 'src/search.html’), filename: 'search.html’, chunks: ['search’], //chunks的值数组对应的是entry中的键 inject: true, minify: { html5: true, collapseWhitespace: true, preserveLineBreaks: false, minifyCSS: true, minifyJS: true, removeComments: false } }) ] };
-
CSS压缩
-
使用 optimize-css-assets-webpack-plugin,同时使用 cssnano
$ npm i optimize-css-assets-webpack-plugin cssnano -D
module.exports = { entry: { app: './src/app.js', search: './src/search.js' }, output: { filename: '[name][chunkhash:8].js', path: __dirname + '/dist' }, plugins: [ new OptimizeCSSAssetsPlugin({ assetNameRegExp: /\.css$/g, cssProcessor: require('cssnano’) }) ] };
-
-
JS压缩
- 内置了 uglifyjs-webpack-plugin
- 有三个比较容易混淆的概念:bundle,chunk和module。
- bundle:打包最终生成的文件
- chunk:每个chunk是由多个module组成,可以通过代码分割成多个chunk。
- module:webpack中的模块(js、css、图片等等)