第三课之webpack基础入门
webpack基础入门
- 什么是Webpack(上图)
Webpack是一个开源的前端打包工具,可以将多种静态资源 js、css、less 转换成一个静态文件,减少了页面的请求
学习路线
日常开发最直观能用到的(webpack本身只能处理js,如果其他类型就需要loader进行转换)
- 模块化代码,定义的全局变量不会影响,将复杂的代码细化,代码复用性强使用方便,js、css导入导出,按需加载(使用插件、模块支持按需加载)
- less、scss、es6、typescript等提升开发效率的语法预处理转换成浏览器识别的js、css,图片转base64
- 开发环境热更新(改变代码保存之后浏览器自动更新最新代码)
- 生产环境代码压缩、混淆、公共代码拆分
- 工作原理
- 针对引入的代码片段在打包后有数字标识,通过标识进行加载
使用
webpack 配置
- entry
- output
- mode
- loaders
- plugins
1.项目的入口文件
根据应用程序的特定需求,可以以多种方式配置 entry 属性
// 写法一 字符串,单入口文件,输出一个文件
const config = {
entry: './path/to/my/entry/file.js'
};
// 写法二
// 多页应用配置,打包出来的js可以在对应的多页面使用
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'],
};
// (动态链接库)抽离公用js库打包,减少编译时间以及入口文件大小
entry:{
react:['react','react-dom']
},
// 我们项目配置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]可以加载对应名称
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 webpack.config.js写法
module.exports = {
mode: 'production'
};
// 方式2 package.json的scripts脚本命令写法
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.loader
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'}),
// 拆分css
new MiniCssExtractPlugin({
filename: directory.production.resource + '/' + directory.production.style + '/' + '[name]-[contenthash:10].css',
chunkFilename: directory.production.resource + '/' + directory.production.style + '/' + '[name]-[contenthash:10].css',
})
]
}
loader和plugin有什么区别
- loader只专注于转化文件(transform)这一个领域,完成压缩,打包,语言翻译,loader是运行在NodeJS中,仅仅只是为了打包
- plugin也是为了扩展webpack的功能,资源的加载上,它的功能要更加丰富。从打包优化和压缩,到重新定义环境变量,功能强大到可以用来处理各种各样的任务(例如:clean-webpack-plugin、html-webpack-plugin)
- loader运行在打包文件之前(loader为在模块加载时的预处理文件)
- plugins在整个编译周期都起作用。
案例讲解
创建一个文件夹
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,配合postcss-loader给我们的样式添加浏览器前缀,兼容不同浏览器
module.exports = {
plugins: {
'postcss-import': {},
'postcss-cssnext': {},
'cssnano': {}
}
}
// second.js添加
import '../less/common.less';
- 效果如图
案例四:css分离以及图片打包
- npm install url-loader file-loader mini-css-extract-plugin -D 处理图片以及css分离
// 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应该用于生产环境,true一般的写法是process.env.NODE_ENV === 'production'
(() => { 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 * 10, // 当大于10kb时候,将文件打包到打包文件夹中,否则会使用base64编码
outputPath: "img"
}
}
]
}
// 4.插件里添加,执行css拆分
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 |
---|---|
适用于webpack打包机制 | 适用于webpack打包机制 |
输出真实的文件 | 输出的文件只存在于内存中,不输出真实的文件 |
文件修改要重新打包 | 文件修改保存会自动更新 |
按生产环境需求进行打包生产环境代码 | 按开发环境需求进行打包开发环境代码 |
常用配置
- 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(),
],
}
// package.json调整开启方式
"start": "./node_modules/.bin/webpack-dev-server",
课程任务
- 使用webpack搭建一个网页拥有js、less、img、热更新的功能
目的
- 感受一下webpack运转起来是需要哪几步,每一步是干嘛用的以及试试前端的热更新功能
考虑到大家平常工作时间比较忙,webpack不是开发同学着重关注的前端技术,最燃比较基础,但是有些步骤相对开发起来会比较耗时,我这边会将课堂案例分享出来,大家依葫芦画瓢,有时间的同学可以参考ppt或者博客园进行一步一步学习开发