写在最前面,这算是自己学习、实践后的记录,不是篇详细的指南,每个点挑了重点的地方说(平时开发中真接触到了的地方),每个详细的点儿可以去webpack官方文档看具体的内容,文档挺清晰的。
基础篇
基础配置
首先安装依赖 npm install webpack webpack-cli -D
webpack4可以0配置打包 直接运行 npx webpack
会到bin下面去找webpack 然后进行默认配置的打包
用来打包其实就是支持js的模块化
默认配置文件命名 webpack.config.js和webpackfile.js,手动指定配置文件的时候要—config xxxx
模式(mode)有两种production和development,默认是生产,production会压缩文件进行优化,development 开发环境,可以看到打包后的结果,不会进行压缩和优化的操作
问:webpack打包后的结果为什么可以在浏览器中执行?
//基础配置
module.exports = {
entry:'./src/index.js',
mode:'development',
output:{
//有更改的时候会hash修改
filename:'bundle.[hash].js',
path:path.resolve('dist') //path必须是一个绝对路径
}
}
文件解析
分析一个最简单的打包出来的bundle文件
webpack内部实现了一个require的方法,实现了递归的依赖关系。从入口文件开始加载模块(加载的时候会存进缓存,如果缓存中有了就不install这个模块),加载模块后,会call执行这个模块(key:value的形式组合的 modules[moduleId].call(…) moduleId就是key),然后找到了这个模块里还引入别的模块,就继续require,exports结果。
HTML plugin
配置devserver开发服务器
module.exports = {
entry:'./src/index.js',
//还可以配置压缩、open、地址等等
devServer:{
port:3000,
contentBase:'./dist'
},
mode:'development',
output:{
filename:'bundle.[hash].js',
path:path.resolve('dist') //path必须是一个绝对路径
}
}
webpackHtmlPlugin
在我们的项目中添加一个index.html作为模板,把打包出来的文件放进这个html中展示
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
//...,
plugins:[
new HtmlWebpackPlugin({
template:'./src/index.html',
filename:'index.html',
//可以配置一些压缩 比如去掉双引号 折叠空行
minify:{
collapseWhitespace:true
},
hash:true
})
]
}
样式处理
使用各种loader去处理对应的文件类型
css-loader 处理css文件 主要负责解析@import这种语法
style-loader 将解析好的css放进style标签里插入到html中
常见预处理器:
-
less less-loader
-
node-sass sass-loader
-
stylus stylus-loader
自动添加浏览器前缀:
postcss-loader 配置autoprefixer
抽离css样式:
mini-css-extract-plugin
抽离后的css可以利用optimize-css-assets-webpack-plugin来压缩 配置在webpack的优化项中
module.exports = {
//...,
optimization:{
minimizer:{
//要注意如果minimizer修改了之后要把js压缩手动添加上去 不然js压缩会失效
new UglifyJsPlugin({
...
}),
new OptimizeCss()
}
}
}
转化es6语法 babel 处理js语法
使用babel转化语法 首先安装
babel-loader 处理的loader
@babel/core 这个是babel的核心模块
@babel/preset-env 这是个转化模块,可以把一些高级的语法转化成低级的语法
module.exports = {
//...,
module:{
rules:[
{
test:'/\.js$/',
exclude:'/node_module/',
use:{
loader:'babel-loader',
options:{
//所有预设
presets:[
'@babel/preset-env'
],
//很多更加高级的语法预设中没有 就要自己添加 比如class 装饰器
plugins:[
"@babel/plugin-proposal-class-properties",
//预设可以转化代码但是不能加上es6内置的方法
//所以要加入这个插件来 对应上内置的方法。
//但是有一个不能转换include include是实例上的 要用polyfill
"@babel/plugin-transform-runtime",
]
}
}
}
]
}
}
全局变量引入问题
比如我们引入jquery 直接import $ from 'jquery'
可以打出$
的值,但是window.$
不能打出来,没挂在window上。想要挂上去可以
1.expose-loader
内联loader
暴露到window上(在引入的页面或者webpack的loader里面加内联loader都可以)
//方法一:内联loader
import $ from "expose-loader?$!jquery"
console.log(window.$)
//方法二:webpack config中配置 但是页面里还是要import $ from "jquery"的
module.exports = {
//...,
module:{
rules:[
{
//代码中引用了jq
test:require.resolve('jquery'),
use:'expose-loader?$'
}
]
}
}
2..ProvidePlugin
不想在组件里引入jq,那就用这个方法在每个模块中注入$
就可以直接用$
不需要引入了
module.exports = {
plugins:[
new webpack.ProvidePlugin({
$:'jquery'
})
]
}
3.引入cdn 且external掉jq
运用的地方用了import进来 不用的话其实就不会被打包进来,cdn的时候其实已经引入了 重新import一下等于又引入一遍
用external可以让引入的包不被打包
图片处理
图片的引用有三种 1.js中 2.css中 3.html中
1.通过配置file-loader来读取图片
//js
import logo from "./logo.png"; //引入获取的是新的图片地址(hash的)
let image = new Image();
image.src = logo;
//config
{
test:/\.(png|jpg|jpeg|gif)$/,
use:'file-loader'
}
2.css中是可以直接使用的,因为css-loader处理了这个问题,会去把括号里的路径引入进来
伪代码:background:url('../img/logo.png') -> background:url(require('../img/logo.png'))
3.使用html-withing-loader来转化html中图片的路径
{
test:/\.html$/,
use:'html-withing-loader'
}
但是在我们实际开发过程中,很多图片都比较小,其实是不希望这些小图也发个http请求的,所以就采用一定大小内使用base64(但是base64的大小会比原来的图片大大约三分一),超过大小发请求的方式,使用url-loader可以实现。
{
test:/\.(png|jpg|jpeg|gif)$/,
use:{
loader:'url-loader',
options:{
limit:8192
}
}
}
打包文件分类
可以在不同的output的时候加上文件夹路径
//比如url-loader里
{
test:/\.(png|jpg|jpeg|gif)$/,
use:{
loader:'url-loader',
options:{
limit:8192,
name: 'images/[hash:8].[name].[ext]'
}
}
}
//minicss中也是 filename:'css/[name].css'之类
如果要统一在打包路径前面加内容,比如cdn,比如之前项目中使用dll打出来的前面少' ./ ',可以在output中配置publicPath,打包出来文件引用的时候就会前面加上这个publicPath了(同理,如果不想全部引用都加的话,可以在想要加的地方配置publicPath,比如图片就在url-loader中配置)
配置篇
打包多页面应用
多个entry多个chunk,对应多个html
{
entry:{
entry1:path.resolve('./src/entry1.js'),
entry2:path.resolve('./src/entry2.js'),
},
...
plugins:[
new HtmlWebpackPlugin({
template:'index.html',
title:'111',
chunks:['entry1'],
filename:'entry1.html'
}),
new HtmlWebpackPlugin({
template:'index.html',
title:'222',
chunks:['entry2'],
filename:'entry2.html'
})
]
}
配置devtool
对source-map的配置
eval:提高构建效率 (映射到构建后的代码)
cheap:sourcemap没有列信息,大幅提高 sourcemap 生成的效率
module:简化loader的sourcemap(不包含列信息)
watch(check一下)
监控代码的变化,实时打包
{
watch:true,
watchOptions:{
poll:1000, //每秒监控1000次
aggregateTimeout:500 //防抖 一直输入的话不会触发 停止后过了500ms才触发
ignored: /node_modules/
}
}
常用小插件
1.clean-webpack-plugin clean文件夹
2.copy-webpack-plugin 拷贝文件
3.webpack.bannerPlugin 为每个 chunk 文件头部添加 banner
跨域问题解决
1.devserver中配置proxy代理
配置target,/api开头的都去 http://localhost:4000找
devServer: {
port: 3000,
contentBase: './dist',
proxy:{
'/api':{
target:'http://localhost:4000'
}
}
}
2.如果只是想要前端这边自己mock点数据,可以使用devserver的before方法
devServer:{
//app就是express实例化的
before(app){
app.get('/user',(req,res)=>{
res.json({name:'huk'})
})
}
}
3.不用代理来处理,在服务端启动webpack,并且端口用服务端的端口
服务器端启动webpack 使得前后端启动在一个端口上 express加上中间件 启动webpack devserver(webpack的compile传进sever 在middlerware中启动)
let express = require('express');
let app = express();
let webpack = require('webpack');
let middle = require('webpack-dev-middleware');
let config = require('./webpack.config');
let compiler = webpack(config);
app.use(middle(compiler))
app.get('/api/user',(req,res)=>{
res.json({name:'hahaha'})
})
app.listen(4000)
resolve配置
{
resolve:{
//从哪里开始依赖 避免一直往上查
modules:[path.resolve('node_modules')],
//别名 比如import bootstrap的时候,其实是引入的bootstrap.js,这又要依赖jq
//这样别名可以直接引入某个文件
alias:{
bootstrap:'bootstrap/dist/css/bootstrap.css'
},
//自定义扩展名的优先级 默认不写扩展名是读的.js 这边配了之后会往后找
extensions:['.js','.css','.json']
}
}
环境变量定义
webpack.DefinePlugin
new webpack.DefinePlugin({
'process.env.api':JSON.stringify(process.env.api)
})
webpack.EnvironmentPlugin
用起来比较方便,把上面DefinePlugin的写法简化了
区分不同环境
利用webpack-merge配置多个config文件。比如,dev和prod环境下有不同的配置,就把公共地方的写一份webpack.base.config.js,在dev和prod config中merge base,然后加上这个环境下独有的配置
- webpack.base.config.js
- webpack.dev.config.js
- webpack.prod.config.js
优化
列举下可以考虑的点儿,用法不赘述了,可以去查对应的文章。这块准备重新写一篇,太多了。
-
happypack 多线程打包loader
-
dll 打包三方依赖
项目有业务代码和三方库,三方库比较稳定,但是在构建的时候也会重新构建,这样减慢了构建速度。使用dll可以把常用的三方库放进动态链接库中,构建时候不需要重新打包。
DllPlugin+DllReferencePlugin dll打包的操作是在项目打包之前做的,然后可以用html-webpack-include-assets-plugin把打包出来的dll插入到想要插入的html中
或者可以使用autodll plugin,这插件可以把上面的步骤融合进来,不用自己去配了,但是在多页面的情况下比较局限性,比如想打两个dll文件,一个每个打出来的html都用,另一个只给某一个html用,这就不行了,需要自己改下autodll的源码,让inject支持匹配。
-
webpack自带优化项
配置optimization
-
IgnorePlugin
可以指定某个东西不打包进去 比如moment打包进来的时候 里面有个local的语言包 我们不需要那么多的语言包 所以可以打包时忽略掉这个文件夹 (如果要引入别的语言包可以在项目内单独import里面的某个语言包然后设置语言)
-
noParse 不去解析某个包中的依赖库
-
热更新
最后
写的不对的地方希望指正,也欢迎交流,谢谢大佬们0-0
网友评论