webpack
学习笔记,参考了官网和很多的文章,由于当时没有及时记录,参考的文章就不一一列出了
webpack 学习之路
css
Webpack
把所有的文件都都当做模块处理,JavaScript
代码,CSS
和 fonts
以及图片等等通过合适的 loader
都可以被处理。css
有两个工具处理样式表,css-loader
和 style-loader
, css-loader
能够使用类似@import
和 url(...)
的方法实现require()
的功能,style-loader
将所有的计算后的样式加入页面中,二者组合在一起使你能够把样式表嵌入 webpack 打包后的 JS 文件中。
打包 vue
文件时需要使用 vue-style-loader
// 简单的css打包配置
module: {
rules: [
{
test: /\.css$/,
use: [
{
loader: 'style-loader'
},
{
loader: 'css-loader',
options: {
modules: true, // 指定启用css modules
localIdentName: '[name]__[local]--[hash:base64:5]' // 指定css的类名格式,防止全局类名的污染
}
}
]
}
]
}
css 与处理器
css 处理 loaders: 从下往上的进行处理 postcss-loader => css-loader => style-loader
- Less loader / Sass loader
- Style loader
- PostCSS
rules: [
{
test: /\.css$/,
use: ['style-loader', 'css-loader', 'postcss-loader']
}
]
babel
将 ES6 等高版本的 JS 转义成浏览器和 node 环境下可以被成功运行的 js 代码
npm install babel-cli -g //全局安装
pwd(li) // 看一下当前的环境/文件下包含的目录(防止进错目录了,最好确认一下比较稳妥)
npm init // 初始化项目
npm install babel-cli --save-dev //本地安装(当前文件夹或者环境中安装)
babel 环境设置
{
'presets': ['es2015'], //预置配置, 项目的依赖和最后输出的结果
'plugins': [] //插件
}
bable --watch src -d build //编译后热更新
browser-sync
简单的浏览器同步测试工具(起一个服务)
browser-sync start --server
browserify 打包
node 在网页上三种 js 规范: commonjs \ amd \cmd
commonjs
: 基于后台的规范(系统架构、系统自动化),模块化定义,允许把 js 代码抽离出来,形成一个单独的文件。特点:同步,加载完成后,但是在浏览器中会导致浏览器假死的状况(网速慢,资源不存在等)amd
:异步加载,成功和失败都有一个返回值,加载 css 后再引入模块cmd
:异步加载-按需加载
webpack 核心概念
本质上: 静态模块打包器
- 入口(entry):内部依赖图的开始,也可以说就是需要被打包的目录
- 输出(output): 输出打包完成的文件,默认文件为
./dist
, 主输出文件默认为./dist/main.js
- loader: 够让 webpack 处理那些非 JavaScript 文件,并且先将它们转换为有效模块,然后添加到依赖中
- plugins: 打包优化、资源管理和注入环境变量
- mode: dev、production 和 none
入口
用法: entry: string| Array<string>
使用场景:
- 分离应用程序(app)和第三方库(vendor)入口
entry: {
app: './src/app.js',
vendors: './src/vendors.js'
}
2.多页面应用:设置多个页面入口(但是我们目前的项目都是单页面入口)
对零配置来说,默认入口文件夹为当前工程的根目录
src
文件夹
输出(output)
注意点: 虽然可以存在多个 入口
但是只能指定一个 输出
配置,输出 path
必须是一个绝对路径,它是 Node.js 的一个核心模块。称之为路径模块,用于操作文件路径。
// 简单用法
output: {
filename: 'bundle.js', //[name].js
path: '/home/proj/public/assets' // _dirname + './dist'
}
// 将单独的 `bundle.js` 问价输出到指定目录中
生成 Source Maps 调试
在 webpack 的配置文件中配置 source maps,需要配置 devtool
,它有四种不同的配置选项:
- source-map 生成一个完整且功能完全的文件,不压缩
- cheap-module-source-map 在一个单独的文件中生成一个不带 映射的 map,调试不能对应到具体的列
- eval-source-map 使用 eval 打包源文件模块,在同一个文件中生成干净的完整的
source map
, 在开发阶段这是一个非常好的选项,在生产阶段则一定不要启用这个选项 - cheap-module-eval-source-map 这是在打包文件时最快的生成 source map 的方法,生成的 Source Map 会和打包后的 JavaScript 文件同行显示,没有列映射
热更新
npm install --save-dev webpack-dev-server
配置
devServer: {
contentBase: "./dist",//本地服务器所加载的页面所在的目录
historyApiFallback: true,//不跳转
inline: true, //实时刷新
port: 8080, // 端口号: 默认8080
host: '0.0.0.0',
openPage: '',
useLocalIp: true,
noInfo: false,
stats: 'minimal',
open: false,
proxy: {}
}
loaders
主要用于对模块的源代码进行转换。
在 webpack 打包中,会对入口文件的直接依赖或者间接依赖进行解析,但是 webpack 只能解析某些文件(比如
.js
结尾的文件)在 4.0 之后.txt
、.json
文件都能支持解析。而loader
做的事情就是把其它类型的所有文件转换为webpack
能够处理的模块。然后你就可以利用webpack
的打包能力,对它们进行处理。
loaders 的配置具体包括这几个方面
test
: 一个用以匹配 loaders 所处理文件的拓展名的正则表达式(必须)loader
: loader 的名称include/exclude
: 手动添加必须处理的文件(文件夹)或屏蔽不需要处理的文件(文件夹)(可选);query
: 为 loaders 提供额外的设置选项(可选)
简单的使用方法: 在项目中添加了一个图片
因为webpack
并不支持对图片的解析,需要配置相应的 loader 做转换处理为webpack
打包的文件格式, webpack 能够解析任何 import 导入的模块,比如.css, .jpg 文件等。
const config = {
module: {
rules: [
{test: /\.jpeg$/, use: 'url-loader'}
//所有.jpg格式的图片都会被转化, use: 解析模块对应的解析器(loader)
}
}
插件(plugins)
插件目的在于解决 loader
无法实现的其他事。
loader: 解析默写特定的文件, plugins: 几乎所有的自动化操作都通过插件来完成
由于插件可以携带参数/选项,必须在 webpack
配置中,向 plugins
属性传入 new
实例。
plugins: [
new webpack.optimize.UglifyJsPlugin(),
new HtmlWebpackPlugin({ template: './src/index.html' }) //自动引用你打包后的JS文件的新index.html
new webpack.HotModuleReplacementPlugin()//热加载插件
]
配置(configuration)
plugin 和 loader 的区别是,loader 是在 import 时根据不同的文件名,匹配不同的 loader 对这个文件做处理,而 plugin, 关注的不是文件的格式,而是在编译的各个阶段,会触发不同的事件,让你可以干预每个编译阶段。
webpack 的配置文件,是导出一个对象的 JavaScript 文件。此对象,由 webpack 根据对象定义的属性进行解析。
如果 webpack.config.js 存在,则 webpack 命令将默认选择使用它。但是在生产环境和开发环境中所需要配置的 webpack 是不一致的,所以现在将 webpack 的配置更改为一个公用文件和两个环境文件,webpack.common.js
、webpack.dev.js
和 webpack.prod.js
,使用webpack-merge
将不同的配置结合在一起。
const merge = require('webpack-merge')
const common = require('./webpack.common')
module.exports = merge(common, {
mode: 'development',
devtool: 'inline-source-map',
devServer: {
contentBase: './dist'
}
})
模块(modules)
项目常用配置
- 设置静态资源的 url 路径前缀
- 各个页面分开打包
- 配置 favicon
- 代码中插入环境变量
- 简化 import 路径
- 使用 postcss 的插件 autoprefixer 自动创建 css 的 vendor prefixes
设置静态资源的 URL 路径前缀
一般我们的项目开发环境中,我们的资源文件都是在根目录中,8080/index.js,但是在生产环境中,需要考虑到缓存控制和 CDN,一版会给 url 加一个前缀: 8080/m/index.js
{
output: {
publicPath: '/m/'
}
}
// webpack配置
{
output: {
/*
代码中引用的文件(js、css、图片等)会根据配置合并为一个或多个包,我们称一个包为 chunk。
每个 chunk 包含多个 modules。无论是否是 js,webpack 都将引入的文件视为一个 module。
chunkFilename 用来配置这个 chunk 输出的文件名。
[chunkhash]:这个 chunk 的 hash 值,文件发生变化时该值也会变。使用 [chunkhash] 作为文件名可以防止浏览器读取旧的缓存文件。
还有一个占位符 [id],编译时每个 chunk 会有一个id。
我们在这里不使用它,因为这个 id 是个递增的数字,增加或减少一个chunk,都可能导致其他 chunk 的 id 发生改变,导致缓存失效。
*/
chunkFilename: '[id].[chunkhash].js',
}
}
各个页面分开打包
应用路由时可以按需加载,使用 async/await 配合import
使用
import Test = () => import('@/views/test')
配置 favicon
网页图标: 在 src 目录中放一张 favicon.png,然后 src/index.html 中插入
<link rel="icon" type="image/png" href="favicon.png" />
{
module: {
rules: [
{
test: /\.html$/,
use: [
{
loader: 'html-loader',
options: {
/*
html-loader 接受 attrs 参数,表示什么标签的什么属性需要调用 webpack 的 loader 进行打包。
比如 <img> 标签的 src 属性,webpack 会把 <img> 引用的图片打包,然后 src 的属性值替换为打包后的路径。
使用什么 loader 代码,同样是在 module.rules 定义中使用匹配的规则。
如果 html-loader 不指定 attrs 参数,默认值是 img:src, 意味着会默认打包 <img> 标签的图片。
这里我们加上 <link> 标签的 href 属性,用来打包入口 index.html 引入的 favicon.png 文件。
*/
attrs: ['img:src', 'link:href']
}
}
]
},
{
/*
匹配 favicon.png
上面的 html-loader 会把入口 index.html 引用的 favicon.png 图标文件解析出来进行打包
打包规则就按照这里指定的 loader 执行
*/
test: /favicon\.png$/,
use: [
{
// 使用 file-loader
loader: 'file-loader',
options: {
/*
name:指定文件输出名
[hash] 为源文件的hash值,[ext] 为后缀。
*/
name: '[hash].[ext]'
}
}
]
},
// 图片文件的加载配置增加一个 exclude 参数
{
test: /\.(png|jpg|jpeg|gif|eot|ttf|woff|woff2|svg|svgz)(\?.+)?$/,
// 排除 favicon.png, 因为它已经由上面的 loader 处理了。如果不排除掉,它会被这个 loader 再处理一遍
exclude: /favicon\.png$/,
use: [
{
loader: 'url-loader',
options: {
limit: 10000
}
}
]
}
]
}
}
一份基本的配置
// build/webpack.config.js
const path = require('path')
const webpack = require('webpack')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const VueLoaderPlugin = require('vue-loader/lib/plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const WebpackBar = require('webpackbar')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const isDev = process.env.NODE_ENV !== 'production'
function resolve(dir) {
return path.join(__dirname, '../', dir)
}
module.exports = {
// entry: {
// // 配置入口文件
// main: resolve('src')
// },
output: {
path: resolve('dist'),
filename: 'js/[name].[hash].js',
chunkFilename: 'js/[name].[hash].js',
publicPath: '/'
},
resolve: {
alias: {
vue$: 'vue/dist/vue.runtime.esm.js',
'@': resolve('src')
},
extensions: ['.js', '.vue', '.json']
},
module: {
rules: [
{
test: /\.vue$/,
use: [
{
loader: 'cache-loader'
},
{
loader: 'vue-loader',
options: {
compilerOptions: {
preserveWhitespace: false
}
}
}
]
},
{
test: /\.js?$/,
exclude: /node_modules/,
loader: 'babel-loader'
},
{
test: /\.(jpe?g|png|gif)$/,
use: [
{
loader: 'url-loader',
options: {
limit: 4096,
fallback: {
loader: 'file-loader',
options: {
name: 'images/[name].[ext]?[hash]'
}
}
}
}
]
},
{
test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
use: [
{
loader: 'url-loader',
options: {
limit: 4096,
fallback: {
loader: 'file-loader',
options: {
name: 'media/[name].[ext]?[hash]'
}
}
}
}
]
},
{
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/i,
use: [
{
loader: 'url-loader',
options: {
limit: 4096,
fallback: {
loader: 'file-loader',
options: {
name: 'fonts/[name].[ext]?[hash]'
}
}
}
}
]
},
{
test: /\.(scss|css)$/,
use: [
!isDev ? MiniCssExtractPlugin.loader : 'vue-style-loader',
'css-loader',
'sass-loader'
]
}
]
},
plugins: [
new CleanWebpackPlugin(),
new VueLoaderPlugin(),
new WebpackBar(),
new webpack.DefinePlugin({
'process.env': {
name: JSON.stringify(process.env.npm_package_name),
NODE_ENV: JSON.stringify(process.env.NODE_ENV || 'development')
}
}),
new CopyWebpackPlugin([
{
from: resolve('public'),
to: '',
ignore: ['.*']
}
]),
new HtmlWebpackPlugin({
template: resolve('public/index.html')
}),
new webpack.NamedModulesPlugin()
]
}
// build/webpack.dev.js
const merge = require('webpack-merge')
const webpackConfig = require('./webpack.config')
module.exports = merge(webpackConfig, {
mode: 'development',
devtool: 'source-map',
devServer: {
port: 8888,
host: '0.0.0.0',
openPage: '',
useLocalIp: true,
historyApiFallback: true,
noInfo: false,
stats: 'minimal',
open: false,
proxy: {}
},
plugins: []
})
const merge = require('webpack-merge')
const webpackConfig = require('./webpack.config')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
module.exports = merge(webpackConfig, {
mode: 'production',
devtool: '#source-map',
optimization: {
splitChunks: {
cacheGroups: {
vendors: {
name: 'chunk-vendors',
test: /[\\\/]node_modules[\\\/]/,
priority: -10,
chunks: 'initial'
},
common: {
name: 'chunk-common',
minChunks: 2,
priority: -20,
chunks: 'initial',
reuseExistingChunk: true
}
}
}
},
plugins: [
new MiniCssExtractPlugin({
filename: 'css/[name].[hash].css',
chunkFilename: 'css/[name].[hash].css'
})
]
})