阅读的是阮一峰的《深入浅出webpack》这本书
学习网址webpack.wuhaolin.cn/1%E5%85%A5%…
入门章
一.下载安装
全局安装webpack: npm install webpack -g
项目独立webpack: npm install --save-dev webpack
二.模块化
- 把一个复杂的东西分解多个模块,并进行组合在一起
- 模块的内部数据与实现是私有的, 只向外部暴露接口与其它模块通信
Common JS
CommonJs是一个模块化规范,通过 require 方法来同步地加载依赖
// 导入
const module1 = require('./module1');
console.log(module1.b) //12
// 导出
module.exports = {b:12}
ES6模块化
// 导入
import { readFile } from 'fs';
import React from 'react';
// 导出
export function hello() {};
export default {
// ...
};
样式文件模块化
SCSS,LESS 把一些常用的样式片段放进一个通用的文件里
// mixin.scss 文件
// 定义样式片段
@mixin center {
// 水平竖直居中
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%,-50%);
}
// main.scss 文件
// 导入和使用 util.scss 中定义的样式片段
@import "mixin";
#box{
@include center;
}
框架
vue
Angular2
react
三.使用webpack打包js
新建普通项目,目录如下(dist为空文件夹)
// module2.js
const module1 = require('./module1');
console.log(module1.b)
// module1.js
module.exports = {b:12}
// index.html
引入打包后的js文件
<script src="dist/bundle.js"></script>
//main.js为打包入口文件
//只需导入module2,module2已经引入module1了
const module2 = require('./module2');
module2('Webpack');
//webpack.config.js
const path = require('path');
module.exports = {
entry: './main.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, './dist'),
}
};
运行webpack,生成打包后的bundle.js文件
四.使用Loader打包css文件到bundle.js
下载 npm i -D style-loader css-loader
css-loader加载cssstyle-loadercss-loader内部样式注入HTML页面
项目新增main.css 在main.js中引入main.css
//main.css
body {
text-align: center;
}
//main.js
const module2 = require('./js/module2');
const maincss =require('./main.css');
module2('Webpack');
maincss('Webpack')
//webpack.config.js
const path = require('path');
module.exports = {
entry: './main.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, './dist'),
},
module: {
rules:[
{
test:/\.css$/, //正则匹配css文件
use:['style-loader','css-loader'] //使用style-loader css-loader
}
]
}
};
运行webpack
五.用plugin把bundle.js文件里的 CSS 提取出来
书中extract-text-webpack-plugin方法
1.下载 npm i -D extract-text-webpack-plugin
2.webpack.config.js配置
//导入extract-text-webpack-plugin
const ExtractTextPlugin = require('extract-text-webpack-plugin');
module: {
rules: [
{
test: /.css$/,
use: ExtractTextPlugin.extract({
use: ['css-loader'],
}),
}
]
},
plugins: [
new ExtractTextPlugin({
//filename: `[name]_[contenthash:8].css`,
filename: 'mainbundle.css' //自定义
}),
],
3.最后在index.html中导入新生成的css
但是这种方法只适用webpack@5.0.0以下, 并且webpack@4.X.X的下载方法应为:npm install extract-text-webpack-plugin@next --save-dev
mini-css-extract-plugin方法
webpack@5.0.0以上就得用这个方法了
1.npm install mini-css-extract-plugin --save-dev
2.webpack.config.js配置
//导入mini-css-extract-plugin
const MiniCssExtractPlugiun = require('mini-css-extract-plugin');
//
module: {
rules: [{
test: /\.css$/,
// use: ['style-loader', 'css-loader']
use: [MiniCssExtractPlugiun.loader, 'css-loader']
}]
},
plugins: [
new MiniCssExtractPlugiun({
// 从 .js 文件中提取出来的 .css 文件的名称
// filename: `[name]_[contenthash:8].css`, //随机
filename: 'mainbundle.css' //自定义
}),
]
3.最后在index.html中导入新生成的css
六.DevServer
DevServer会启动一个HTTP服务器用于服务网页请求,同时会帮助启动Webpack 1.下载
npm i -D webpack-dev-server
2.webpack.config.js配置
devServer: {
static: {
directory: path.join(__dirname, '/'),
},
compress: true,
port: 9000,
historyApiFallback: true
},
3.package.json配置
"scripts": {
"dev":"webpack-dev-server",
"test": "echo \"Error: no test specified\" && exit 1"
},
4.运行npm run dev
注意:如果运行出现问题,大概率是版本问题,如果页面404,大概率是webpack.config.js配置的directory路径不对
配置章
一.Entry
Entry必填的,配置模块入口文件
module.exports = {
entry: './main.js',
}
类型
- String
'./app/entry' - array
['./app/entry1', './app/entry2'] - object
{
a: './app/entry-a',
b: ['./app/entry-b1', './app/entry-b2']
}
多入口:每个入口生成一个 Chunk
Context
寻找相对路径时,以context配置的为根目录
必须是一个绝对路径的字符串
context默认的值就是项目的根目录
module.exports = {
context: path.resolve(__dirname, '/')
}
Chunk名称
webpack会为每一个生成的chunk起一个名字,Chunk是Webpack打包过程中,一堆module的集合。
- 如果类型为String和Array,则只生成一个Chunk名字为main
- 如果类型为object,会生成多个Chunk,用键名起名字
动态Entry
entry接收一个函数
// 同步函数
entry: () => {
return {
a:'./a',
b:'./b',
}
};
// 异步函数
entry: () => {
return new Promise((resolve) => {
resolve({
a: './a',
b: './b'
})
})
},
二.Output
Output:如何输出打包的代码
只有一个输出文件
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, './dist'),
},
多个输出文件时
output: {
filename: "[name].entry.js",
path: path.resolve(__dirname, './dist'),
},
除了name(Chunk 的名称),还可以用id(唯一标识,从0开始)、hash(唯一标识的hash值)、chunkhash(Chunk内容的Hash值)
chunkFilename
配置无入口的Chunk在输出时的文件名称
output: {
filename: '[name].min.js',
chunkFilename: '[name].min.js'
}
path
输出文件存放在本地的目录
path: path.resolve(__dirname, 'dist_[hash]')
publicPath
配置异步加载时对应的url地址
filename:'[name]_[chunkhash:8].js'
publicPath: 'https://xxx/assets/'
<script src='https://xxx/assets/a_12345678.js'></script>
crossOriginLoading
webpack输出可能需要异步,异步通过jsonp实现,
libraryTarget 和 library
- libraryTarget 何种方式导出库
- library 导出库名称 配置
- var(默认)
指定名称的变量
output.library='LibraryName'
// Webpack 输出的代码
var LibraryName = lib_code;
// 使用库的方法
LibraryName.doSomething();
- commonjs1
CommonJS 规范导出
output.library='LibraryName'
// Webpack 输出的代码
exports['LibraryName'] = lib_code;
// 使用库的方法
require('library-name-in-npm')['LibraryName'].doSomething();
3.commonjs2
CommonJS2 规范导出\
// Webpack 输出的代码
module.exports = lib_code;
// 使用库的方法
require('library-name-in-npm').doSomething();
4.this
指定的名称
// Webpack 输出的代码
this['LibraryName'] = lib_code;
// 使用库的方法
this.LibraryName.doSomething();
5.window
指定的名称,把库挂载到 window 上
// Webpack 输出的代码
window['LibraryName'] = lib_code;
// 使用库的方法
window.LibraryName.doSomething();
6.global
指定的名称,即把库挂载到 global 上
// Webpack 输出的代码
global['LibraryName'] = lib_code;
// 使用库的方法
global.LibraryName.doSomething();
libraryExport
配置导出的模块中哪些子模块需要被导出,它只有在 output.libraryTarget 被设置成 commonjs 或者 commonjs2 时使用才有意义
// Webpack 输出的代码
module.exports = lib_code['a'];
// 使用库的方法
require('library-name-in-npm')===1;
三.module
配置如何处理模块
Loader
rules 配置模块的读取和解析规则,通常用来配置 Loader
module: {
rules: [{
test: /\.css$/,
use: [MiniCssExtractPlugiun.loader, 'css-loader'],
exclude: path.resolve(__dirname, 'node_modules')
}]
},
传入很多参数时,可以用Object来描述
use: [
{
loader:'babel-loader',
options:{
cacheDirectory:true,
},
// enforce:'post' 把该 Loader 的执行顺序放到最后
// enforce:'pre',把该 Loader 的执行顺序放到最前面
enforce:'post'
},
.....
]
text(匹配文件类型)、include(只匹配某目录的文件类型)、exclude(排除某目录下的文件 ),这三个能传数组、正则、字符串
rules: [{
test: [/\.css$/,/\.scss$/],
use: [MiniCssExtractPlugiun.loader, 'css-loader'],
include:[
path.resolve(__dirname, 'src'),
path.resolve(__dirname, 'tests'),
],
exclude: [
path.resolve(__dirname, 'node_modules'),
path.resolve(__dirname, 'bower_modules'),
]
}]
noParse
忽略对部分没采用模块化的文件的递归解析和处理,提高性能
// 使用正则表达式
noParse: /jquery|chartjs/
// 使用函数,从 Webpack 3.0.0 开始支持
noParse: (content)=> {
// content 代表一个模块的文件路径
// 返回 true or false
return /jquery|chartjs/.test(content);
}
parser
webpack内置了对模块化JavaScript的解析功能
parser可以精确到语法层面noParse只能控制哪些文件不被解析
module: {
rules: [
{
test: /.js$/,
use: ['babel-loader'],
parser: {
amd: false, // 禁用 AMD
commonjs: false, // 禁用 CommonJS
system: false, // 禁用 SystemJS
harmony: false, // 禁用 ES6 import/export
requireInclude: false, // 禁用 require.include
requireEnsure: false, // 禁用 require.ensure
requireContext: false, // 禁用 require.context
browserify: false, // 禁用 browserify
requireJs: false, // 禁用 requirejs
}
},
]
}
四.resolve
resolve 配置 Webpack 如何寻找模块所对应的文件
alias
配置项通过别名来把原导入路径映射成一个新的导入路径
// Webpack alias 配置
resolve:{
alias:{
components: './src/components/'
}
}
当通过 import Button from 'components/button' 导入时,实际上被 alias 等价替换成了 import Button from './src/components/button'。
mainFields
如果第三方模块提供了ES5和ES6两个模块,
{
"jsnext:main": "es/index.js",// 采用 ES6 语法的代码入口文件
"main": "lib/index.js" // 采用 ES5 语法的代码入口文件
}
根据 `mainFields` 的配置去决定优先采用那份代码
mainFields: ['browser', 'main']
mainFields: ['jsnext:main', 'browser', 'main']
extensions
导入文件没带后缀时,webpack会自动带上后缀后去尝试访问文件是否存在
extensions: ['.js', '.json'] //优先js,然后json
modules
去哪些目录下寻找第三方模块,默认是只会去 node_modules 目录
modules:['./src/components','node_modules']
descriptionFiles
配置描述第三方模块的文件名称,也就是 package.json 文件
descriptionFiles: ['package.json']
enforceExtension
如果配置为 true 所有导入语句都必须要带文件后缀
import './foo' 能正常工作,开启后就必须写成 import './foo.js'。
enforceModuleExtension
enforceModuleExtension 只对 node_modules 下的模块生效
在 enforceExtension:true 时,安装的第三方模块中大多数导入语句没带文件后缀, 所以这时通过配置 enforceModuleExtension:false 来兼容第三方模块。
五.Plugin
Plugin 需要的参数通过构造函数传入
const CommonsChunkPlugin = require('webpack/lib/optimize/CommonsChunkPlugin');
module.exports = {
plugins: [
// 所有页面都会用到的公共代码提取到 common 代码块中
new CommonsChunkPlugin({
name: 'common',
chunks: ['a', 'b']
}),
]
};
六.devServer
hot
默认:发现源代码被更新后会通过自动刷新整个页面来做到实时预览
inline
- inline开启:构建完变化后的代码时通过代理客户端控制网页刷新
- inline关闭:构建完变化后的代码时刷新 iframe 来实现实时预览
historyApiFallback
多个单页应用组成
historyApiFallback: {
// 使用正则匹配命中路由
rewrites: [
//user 开头的都返回 user.html
//game 开头的都返回 game.html
// 其它的都返回 index.html
{ from: /^/user/, to: '/user.html' },
{ from: /^/game/, to: '/game.html' },
{ from: /./, to: '/index.html' },
]
}
contentBase
把项目根目录下的某目录设置成DevServer服务器的文件根目录
//把项目public目录,设置成DevServer服务器的文件根目录
devServer:{
contentBase: path.join(__dirname, 'public')
}
headers
HTTP响应中注入一些HTTP响应头
devServer:{
headers: {
'X-foo':'bar'
}
}
host
DevServer 服务监听的地址
host 的默认值是 127.0.0.1 即只有本地可以访问 DevServer 的 HTTP 服务
port
服务监听的端口
port: 9000
allowedHosts
配置一个白名单列表,在这个列表的才能正常返回
allowedHosts: [
// 匹配单个域名
'host.com',
// host2.com 和所有的子域名 *.host2.com 都将匹配
'.host2.com'
]
disableHostCheck
是否关闭用于 DNS 重绑定的 HTTP 请求的 HOST 检查
https
默认使用 HTTP 协议服务,它也能通过 HTTPS 协议服务,但要设置
devServer:{
https: true
}
这样会自动生成https证书,如果想要自己的证书,需要这样配置
devServer:{
https: {
key: fs.readFileSync('path/to/server.key'),
cert: fs.readFileSync('path/to/server.crt'),
ca: fs.readFileSync('path/to/ca.pem')
}
}
clientLogLevel
配置在客户端的日志等级,枚举类型.
- none 不输出任何日志
- error
- warning
- info 默认
compress
配置是否启用 gzip 压缩,默认为 false
open
为true时第一次构建完时自动用系统上默认的浏览器去打开项目,默认为 false
七.Target
target可以构建出针对不同环境的代码
- target:'node' (针对 Node.js,使用
require语句加载 Chunk 代码) - target:'web' 浏览器 (默认 ,所有代码都集中在一个文件里)
- target:'async-node' (针对 Node.js,异步加载 Chunk 代码)
- target:'webworker' (针对webworker)
- target:'electron-main' (针对electron)
- target:'electron-renderer' (针对electron)
八.Devtool
配置如何生成Source Map,默认值是 false不生成Source Map
module.export = {
devtool: 'source-map'
}
九.Watch 和 WatchOptions
监听模式,它支持监听文件更新,默认是关闭的
module.export = {
watch: true
}
使用 DevServer 时,监听模式默认是开启的
开启监听模式时,watchOptions 才有意义\
module.export = {
watch: true,
// 监听模式运行时的参数
watchOptions: {
// 不监听的文件或文件夹,支持正则匹配
// 默认为空
ignored: /node_modules/,
// 监听到变化发生后会等300ms再去执行动作,防止文件更新太快导致重新编译频率太高
// 默认为 300ms
aggregateTimeout: 300,
// 判断文件是否发生变化是通过不停的去询问系统指定文件有没有变化实现的
// 默认每隔1000毫秒询问一次
poll: 1000
}
}