一、初始化前置工作
- 安装webpack、webpack-cli(可以本地安装,这样就不会破坏全局的webpack环境)
初始化package.json
npm init -y
新建webpack.config.js ```javascript // webpack.config.js中导出一个对象 // 所有的配置都在这个对象中
module.export = {}
<a name="mHwNC"></a>
## 二、配置主要的属性
- entry(入口)
```javascript
entry: '' // 字符串
entry: [] // 数组
entry: {} // 对象
output: {
path: path.join(__dirname, 'dist'),
// name是entry名字,默认是main
// hash是根据打包后的文件内容计算出来的一个hash值
filename: '[name].[hash:8].js'
}
// hash默认是20位
- output(出口)==》对象
- module(解析文件规则,配置文件需要哪些loader解析)==》对象
- plugins(插件,代码优化、代码分割、生成htm等等)==》 数组
- devServe(开发服务器)==》 对象
chunk:代码块,一个chunk有多个模块组合而成,用于代码合并和分割
三、解析过程
webpack启动后会从 entry 里配置的 module 开始递归解析 entry 依赖的所有module。每找到一module,就会根据配置的loader去找出对应的转换规则,对module进行转换后,在解析出当前module依赖的module。这些模块会以entry为单位进行分组,一个entry和其所有依赖的module被分割到一个组,也就是一个chunk。最后webpack会把所有的chunk转换成文件输出,在整个流程中webpack会在恰当的市级执行plugin中定义的逻辑。
(一个entry和其依赖的文件 ==》chunk)
四、通过插件生成html
npm install html-webpack-plgugin -D
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html', // 以该html文件为模板,生成文件
filename: 'index.html' // 产生后的文件名
})
]
五、解析css、less、scss
1、配置解析loader
配置loader可以有多种
use loader use+loader
1、在webpack的module种配置
2、在引入的代码中配置
require('style-loader!css-loader!./index.css')
处理scss、less
less-loader scss-loader node-sass
2、css代码提取到css文件中
// 有时候我们希望把页面中的css文件单独拉出来保存进行加载
extract-text-webpack-plugin
const cssExtract = new ExtractTextWebpackPlugin('css/css.css')
plugins: [cssExtract]
{
test: /\.css$/,
loader: cssExtract.extract({
use: ['css-loader']
})
}
3、处理css属性前缀
npm i postcss-loader autoprefixer
{
test: /\.css$/,
loader: ['css-loader', 'postcss-loader']
}
新建postcss.config.js
module.exports = {
plugins: [require('autoprefixer')]
}
压缩css
{
test: /\.css$/,
loader: ['css-loader?minimize', 'postcss-loader']
}
4、loader的执行顺序
六、解析es6
babel-loader babel-core babel-preset-env babel-preset-stage-0
{
test: /\.js/,
use: {
loader: 'babel-loader',
query: {
presets: ['env', 'stage-0']
}
}
}
babel靠的就是ast语法树
压缩js
uglifyjs-webpack-plugin -D
七、解析图片、字体文件
1、通过js new Image()
let src = require('./img/1.png')
let img = new Image()
img.src = src
document.body.appendChild(img)
rules: [{
// file-loader 是解析图片地址,把图片从资源位置拷贝到目标位置
test: /\.(png|jpg|gif|svg|bmp)/,
use: 'file-loader'
}]
2、在css中当成背景图引入
3、通过img标签引入
需要html-widthimg-loader -D
把图片打包到固定的文件夹目录下
rules: [{
// file-loader 是解析图片地址,把图片从资源位置拷贝到目标位置
test: /\.(png|jpg|gif|svg|bmp)/,
use: {
loader: 'file-loader',
// 指定拷贝文件的输出目录
options: {
outputPath: 'images/'
}
}
}]
将比较小的图标打包成base64格式,嵌到页面中,这样就能减少一次资源请求了
rules: [{
// file-loader 是解析图片地址,把图片从资源位置拷贝到目标位置
test: /\.(png|jpg|gif|svg|bmp)/,
use: {
loader: 'url-loader',
// 指定拷贝文件的输出目录
options: {
limit: 9 * 1024, // 小于9kb一下的 就打包成base64
outputPath: 'images/'
}
}
}]
八、配置多页
// 先找到每个入口(entry),然后从各个入口分别处罚,找到依赖的模块(module)
// 然后生成一个chunk(代码块)(一个代码块包含很多模块)最后会把chunk写到文件系统中(assets)
entry: {
index: './src/index.js',
base: './src/base.js'
}
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html', // 以该html文件为模板,生成文件
filename: 'index.html', // 产生后的文件名
chunk: ['index']
}),
new HtmlWebpackPlugin({
template: './src/index.html', // 以该html文件为模板,生成文件
filename: 'base.html', // 产生后的文件名
chunk: ['base']
})
]
// 如果页面过多,不可能一直写HtmlWebpackPlugin,可以写for循环
九、公共库的打包
当一个模块里使用到一个公共库的时候
1、在当前需要使用的文件中引入
import $ from 'jquery'
缺点:每次都要重新引入
所以打包后的文件都很大,原因是把jq引入进去了
2、
entry: {
vendor: 'jquery'
}
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html', // 以该html文件为模板,生成文件
filename: 'index.html', // 产生后的文件名
chunk: ['index', 'vendor']
}),
new HtmlWebpackPlugin({
template: './src/index.html', // 以该html文件为模板,生成文件
filename: 'base.html', // 产生后的文件名
chunk: ['base', 'vendor']
})
]
------
这样还是不行,因为在自己的模块,拿不到其他模块的变量,所以,$变量还是拿不到
-------------
引入方式:
1、插件方法
用来自动向模块内部注入变量
new webpack.ProvidePlugin({
$: 'jquery'
})
2、expose-loader
在index中引入
// expose-loader?全局变量名!模块名
// 它会先加载此模块,然后得到模块导出对象,并且挂载到window上,这样全局下就都能访问到了
let $ = require('expose-loader?$!jquery')
3、
rules:[{
test: require.resolve('jquery'),
use: {
loader: 'expose-loader',
options: ''
}
}]
十、别名的配置
reolsve: {
alias: { // 别名
"@": './src'
},
// 命中率高的往前写,命中率低的往后写
extensions: ['', '.js', '.json'] // 配置扩展名(引入模块的时候,可以不加扩展名)
}
十一、优化方案
1、缩小文件搜索范围
// 当你引入一个模块的时候,要进行解析
resolve: {
// 当你需要指定node_modules之外的其他模块目录的话
modules: [path.resolve(__dirname, 'node_modules')],
alias: {
// 当加载react模块的时候,
react: path.resolve('./cjs/react.production.min.js')
}
}
npParse:不需要递归解析此模块
// 如果打包的文件过大
rules: [
{
test: /\.jsx?$/,
use: [
{
loader: 'babel-loader'
}
],
// 只转换或者编译src目录下的文件
include: path.resolve('./src'),
// 排除node_modules
exclude: /node_modules/
}
]
2、DLL
.dll为后缀的文件称为动态链接库,在一个动态链接库中可以包含给其他模块调用的函数和数据。
- 把基础模块独立处理打包到单独的动态链接库里
- 当需要导入的模块在动态链接库里的时候,模块不能再次被打包,而是去动态链接库里获取dll-plugin
1、定义DLL
- DllPlugin插件:用于打包一个个动态链接库
- DllReferencePlugin:在配置文件中引入DllPlugin插件打包好的动态链接库
十二、webpack-dev-server
webpack-dev-server打包后的文件,放到了内存中,所以我们在本地文件夹中是看不到的
十三、npm 和 npx的区别
npx可以执行命令(.bin目录下的命令,npm无法执行)
npm只能执行脚本命令
十四、监控源文件的变化
// 表示监控源文件的变化,当源文件发售改变后,则重新打包
watch: true,
watchOptions: {
exclude/ignored: /node_modules/,
poll: 1000, // 每秒中询问的次数
aggregateTimeout: 500, //
}
十五、文件拷贝
copy-webpack-plugin
十六、服务器自动刷新
我们可以监听到本地源文件发生变化时,自动重新构建出可运行的代码后在重新刷新浏览器。
1、文件监听
watch: true, // 只有在开启监听模式时,watchoptions才有意义
watchOptions: {
ignored: /node_modules/,
aggregateTimeout: 300, // 监听到变化发生后等300ms再去执行动作,防止文件更新太快导出编译频率太高
poll: 1000 // 通过不停的询问文件是否改变链判断文件是否发生变化,默认每秒询问1000次
}
2、文件监听流程
- webpack定时获取文件的更新时间,并跟上一次保存的时间进行对比,不一致就表示发生了变化,poll就用来配置每秒访问多少次
- 当检测文件不再发生变化,会先换成起来,等待一段时间之后再通知监听者,这个等待时间超过aggregateTimeout配置
- webpack只会监听entry依赖的文件
- 我们需要尽可能减少需要监听的文件数量和检测频率,当然,频率的降低会导致灵敏度下降
十七、区分环境
// 定义环境变量
plugins: [
new webpack.DefinePlugin({
__development__: JSON.stringify(process.env.NODE_ENV)
})
]
十八、CDN
十九、Tree Shaking
Tree Shaking 可以用来提出 js中用不上的死代码,他依赖静态的es6模块化语法,例如通过import 和 export导入导出。
二十、提取公共代码
optimization: {
splitChunks: {
ca
}
}
二十一、开启Scope Hoisting
二十二、代码分离
tapable:
webpack本质上是一种事件流的机制,它的工作原理就是将各个插件串联起来,而实现这一切的核心就是Tapable,webpack中最核心的负责编译的Compiler和负责创建bundles的Compilation都是Tapable的实例
npx 可以直接运行node_modules/ .bin目录下面的命令
可以用个配置package.json中的”script”
hash:cd26f7869d1f60899e93 默认是20位,一般我们取前八位
二十三、配置请求跨域
devServer: {
proxy: { // 重写的方法 把请求代理到express服务器上
'/api': {
target: 'http://localhost:3000',
pathRewrite: {'/api', ''}
}
}
}
前端自己模拟数据
devServer: {
before(app) {
app.get('/user',(req, res) => {
res.json({name: 'zhangsan'})
})
}
}