- 什么是Webpack(上图)
Webpack是一个开源的前端打包工具,可以将多种静态资源 js、css、less 转换成一个静态文件,减少了页面的请求
学习路线
日常开发最直观能用到的(webpack本身只能处理js,如果其他类型就需要loader进行转换)
-
将js代码转换成浏览器可以支持的es语法(es6、jsx、typescript)
-
将less转换成浏览器可以识别的css(scss、less)
-
图片进行base64转码
-
开发环境热更新
-
生产环境编译
-
工作原理
- 针对引入的代码片段在打包后有数字标识,通过标识进行加载
使用
webpack 配置
- entry
- output
- mode
- loaders
- plugins
1.entry单页应用的入口文件
根据应用程序的特定需求,可以以多种方式配置 entry 属性
// 写法一 字符串,单入口文件,输出一个文件
const config = {
entry: ‘./path/to/my/entry/file.js‘
};
// 写法二 对象,输出对应多个入口文件
// 提取公共库
const config = {
entry: {
app: ‘./src/main.js‘,
vendor: [‘react‘, ‘redux‘, ‘react-dom‘, ‘react-redux‘, ‘react-router-redux‘]
},
};
// 多页应用配置,
const config = {
entry: {
pageOne: ‘./src/pageOne/index.js‘,
pageTwo: ‘./src/pageTwo/index.js‘,
pageThree: ‘./src/pageThree/index.js‘
}
};
// 写法三 数组,相当于将两个文件打包到一个文件里,输出一个文件
const config = {
entry: [‘./add.js‘, ‘./index.js‘],
};
// 我们项目配置babel-polyfill用于兼容ie9或低版本浏览器不识别的语法
entry: [
‘babel-polyfill‘,
join(__dirname, ‘src‘, ‘index.js‘),
],
项目中更多的用的是写法一和写法三
2.output输出文件
在 webpack 中配置 output 属性的最低要求是,将它的值设置为一个对象,包括以下两点
- filename 用于输出文件的文件名。
- 目标输出目录 path 的绝对路径。
const config = {
output: {
filename: ‘bundle.js‘,
path: ‘/home/proj/public/assets‘
}
};
const config = {
output: {
// 路径
path: join(__dirname, directory.production.envName),
//
publicPath: general.publicPath + ‘/‘,
// 因为项目有代码分割等插件会分离对应文件,所以使用[name]可以加载对应名称,设置hash可以防止缓存
filename: directory.production.resource + ‘/‘ + directory.production.javascript + ‘/‘ + ‘[name]-[hash:10].js‘,
// 每个页面异步加载的js,例如对应页面通过import引入过来的组件、函数打包不是在入口文件指定的,但是又需要引入的,设置hash可以防止缓存
chunkFilename: directory.production.resource + ‘/‘ + directory.production.javascript + ‘/‘ + ‘[name]-[contenthash:10].js‘,
},
};
一句话总结
- filename 指列在 entry 中,打包后输出的文件的名称。
- chunkFilename 指未列在 entry 中,却又需要被打包出来的文件的名称。如果不设置会给默认值
hash
- hash每次修改任何一个文件,所有文件名的hash值都将改变。所以一旦修改了任何一个文件,整个项目的文件缓存都将失效
chunkhash
- chunkhash根据不同的入口文件(Entry)进行依赖文件解析、构建对应的chunk,生成对应的哈希值。在生产环境里把一些公共库和程序入口文件区分开,单独打包构建,接着我们采用chunkhash的方式生成哈希值,那么只要我们不改动公共库的代码,就可以保证其哈希值不会受影响,
- 但是生产环境中我们会用webpack的插件,将css代码打单独提取出来打包。这时候chunkhash的方式就不够灵活,因为只要同一个chunk里面的js修改后,css的chunk的hash也会跟随着改动。因此我们需要contenthash。
contenthash
- contenthash是针对文件内容级别的,只有你自己模块的内容变了,那么hash值才改变
项目目前运行部署机制
- 只要公用函数库不变,只改业务代码的话只会,webpack打包只会修改对应业务代码的js以及hash值,没改过的文件不会受影响,上线之后也会存在文件的缓存
3.mode
// 方式1
module.exports = {
mode: ‘production‘
};
// 方式2
webpack --mode=production
// 启用对应开发环境默认的插件
development 会将 process.env.NODE_ENV 的值设为 development。启用 NamedChunksPlugin 和 NamedModulesPlugin。
// 启用对应生产环境默认的插件
production 会将 process.env.NODE_ENV 的值设为 production。启用 FlagDependencyUsagePlugin, FlagIncludedChunksPlugin, ModuleConcatenationPlugin, NoEmitOnErrorsPlugin, OccurrenceOrderPlugin, SideEffectsFlagPlugin 和 UglifyJsPlugin.
4.loaders
loader 用于对模块的源代码进行转换,loader 可以将文件从不同的语言(如 TypeScript)转换为 JavaScript,或将内联图像转换为 data URL。loader 甚至允许你直接在 JavaScript 模块中 import CSS文件
- js、css、img、svg
module.exports = {
module:{
rules:[{
test:/\.js$/,
use:[{
loader:‘babel-loader‘,
options:{
presets:[‘react‘]
}
}]
}]
}
}
5.plugin插件
插件目的在于解决 loader 无法实现的其他事。
const HtmlWebpackPlugin = require(‘html-webpack-plugin‘); //通过 npm 安装
module.exports = {
plugins: [
new webpack.optimize.UglifyJsPlugin(),
new HtmlWebpackPlugin({template: ‘./src/index.html‘})
]
}
loader和plugin有什么区别
- loader只专注于转化文件(transform)这一个领域,完成压缩,打包,语言翻译,loader是运行在NodeJS中,仅仅只是为了打包
- plugin也是为了扩展webpack的功能,资源的加载上,它的功能要更加丰富。从打包优化和压缩,到重新定义环境变量,功能强大到可以用来处理各种各样的任务(例如:clean-webpack-plugin、html-webpack-plugin)
- loader运行在打包文件之前(loader为在模块加载时的预处理文件)
- plugins在整个编译周期都起作用。
案例讲解
- 安装webpack
- WebPack可以看做是模块打包机:它做的事情是,分析你的项目结构,找到JavaScript模块以及其它的一些浏览器不能直接运行的拓展语言(Scss,TypeScript等),并将其转换和打包为合适的格式供浏览器使用。
创建一个文件夹
npm init -y
案例一:打包js
- npm install webpack webpack-cli --save-dev
- 新建src文件夹以及对应js、less、imgs、public文件夹以及对应的入口html文件
- 打包js
// first.js
module.exports = function () {
let a = 1;
let b = 2;
console.log(a + b);
};
// webpack.config.js
module.exports = {
entry: ‘./src/js/first.js‘, //指定打包的入口文件
output: {
path: __dirname + ‘/dist‘, // 注意:__dirname表示webpack.config.js所在目录的绝对路径
filename: ‘build.js‘ //输出文件
},
// 环境
mode: ‘development‘,
}
// package.json
"scripts": {
// 默认读取当前目录的webpack.config.js
"start":"./node_modules/.bin/webpack"
}
// 运行命令
npm start
案例二:打包css,直接上单页
- npm install html-webpack-plugin style-loader css-loader -D
// webpack.config.js
var htmlwp = require(‘html-webpack-plugin‘);
module.exports = {
entry: ‘./src/js/second.js‘, //指定打包的入口文件
output: {
path: __dirname + ‘/dist‘, // 注意:__dirname表示webpack.config.js所在目录的绝对路径
filename: ‘build.js‘ //输出文件
},
module: {
rules: [
{
test: /\.css$/,
exclude: ‘/node_modules/‘,
use: [
(() => { return ‘style-loader‘; })(),
{
loader: ‘css-loader‘,
options: {
importLoaders: 1
}
},
]
},
]
},
plugins: [
new htmlwp({
title: ‘首页‘, //生成的页面标题<head><title>首页</title></head>
filename: ‘index.html‘, //webpack-dev-server在内存中生成的文件名称,自动将build注入到这个页面底部,才能实现自动刷新功能
template: ‘./public/index.html‘ //根据vue_02.html这个模板来生成(这个文件请程序员自己生成)
}),
],
// 环境
mode: ‘development‘,
}
// first.js
module.exports = function () {
var greet = document.createElement(‘div‘);
greet.className = "bg";
greet.textContent = "Hi there and greetings!";
return greet;
};
// second.js
const greeter = require(‘./first‘);
import ‘../less/common.css‘;
// 获取页面id为root添加元素
document.querySelector("#root").appendChild(greeter());
// common.css
body {
background: gray;
display: flex;
}
// npm start
- 效果:页面添加了文本也改变了颜色
案例三:使用less以及添加兼容性语法
- npm install less less-loader postcss-loader postcss-cssnext autoprefixer postcss-import cssnano -D 处理less和postcss
// 添加common.less文件
.bg {
display: flex;
color: black;
}
// webpack.config.js添加
{
test: /\.css$/,
exclude: ‘/node_modules/‘,
use: [
‘style-loader‘,
{
loader: ‘css-loader‘,
options: {
importLoaders: 1
}
},
{
loader: ‘postcss-loader‘,
}
]
},
{
test: /\.less$/,
exclude: ‘/node_modules/‘,
use: [
‘style-loader‘,
{
loader: ‘css-loader‘,
options: {
importLoaders: 1
}
},
{
loader: ‘postcss-loader‘,
},
{
loader: ‘less-loader‘, //
options: {
}
}
]
},
// 新增 postcss.config.js
module.exports = {
plugins: {
‘postcss-import‘: {},
‘postcss-cssnext‘: {},
‘cssnano‘: {}
}
}
- 效果如图
案例四:css分离以及图片打包
- npm install url-loader file-loader mini-css-extract-plugin -D 处理less和postcss
// webpack.config.js添加
// 1.引入
var MiniCssExtractPlugin = require(‘mini-css-extract-plugin‘);
// output添加publicPath: ‘‘
output: {
path: __dirname + ‘/dist‘, // 注意:__dirname表示webpack.config.js所在目录的绝对路径
filename: ‘build.js‘, //输出文件
publicPath: ‘‘ // output的publicPath是用来给生成的静态资源路径添加前缀的;
},
// 2.修改loaders里面的style-loader代码
// 注释style-loader
// {
// loader: ‘style-loader‘
// },
// 修改为
// MiniCssExtractPlugin应该用于生产环境
(() => { return true ? MiniCssExtractPlugin.loader : ‘style-loader‘; })(),
// 3.loaders添加
{
test: /\.(png|jpg|gif|jpeg|svg)$/,
use: [
{
loader: "url-loader",
options: {
name: "[name].[hash:5].[ext]",
limit: 1024, // size <= 1kib
outputPath: "img"
}
}
]
}
// 4.插件里添加
new MiniCssExtractPlugin({
filename: ‘[name].css‘,
chunkFilename: ‘[id].css‘,
}),
- 效果如图
案例五:热更新
原理
HMR即Hot Module Replacement是指当你对代码修改并保存后,webpack将会对代码进行重新打包,并将改动的模块发送到浏览器端,浏览器用新的模块替换掉旧的模块,去实现局部更新页面而非整体刷新页面
webpack-dev-server使用
功能讲解
- 为静态文件提供服务
- 自动刷新和热替换(HMR)
webpack打包和webpack-dev-server
- webpack输出真实的文件
- webpack-dev-server输出的文件只存在于内存中,不输出真实的文件
常用配置
- port:端口号
- host:域名
- historyApiFallback:配置属性是用来应对返回404页面时定向到特定页面用
- compress:设置为true的时候对所有的服务器资源采用gzip压缩
- hot:DevServer默认的行为是在发现源代码被更新后会通过自动刷新整个页面来做到实现预览,开启模块热替换功能后在不刷新整个页面的情况下通过用新模块替换老模块来实现实时预览
- inline: 将webpack-dev-server客户端加入到webpack入口文件的配置中
- contentBase:是指定在哪个路径下或者文件夹下中开启服务器;
webpack最新版本是5,不兼容之前版本的webpack-dev-server,之前装的是最新版的5,所以要降级
npm i webpack@4.43.0 webpack-cli@3.3.12 webpack-dev-server cross-env -D
// webpack.config.js添加
var webpack = require(‘webpack‘);
// 配置项添加
module.exports = {
devServer: {
contentBase: ‘./dist‘, // 是指定在哪个路径下或者文件夹下中开启服务器;
open: true, // 开启服务器时,自动打开浏览器
inline: true,
hot: true,
port: 8081 // 开启的服务器的端口
},
plugins: [
// HMR大幅提高了开发体验,只更新变更内容,调整样式迅速,避免了大部分的网络请求、浏览器重新渲染
new webpack.HotModuleReplacementPlugin(),
],
}
个人练习
- 使用webpack搭建一个网页拥有js、less、img、热更新的功能