webpack的安装
安装本地的webpack
yarn add webpack webpack-cli -D
零配置执行
npx webpack
npx webpack --mode production // 默认为生产环境 会进行压缩打包
npx webpack --mode development
webpack_require 相当于是webpack自己实现的一套模块化的机制
手动配置
默认配置文件名称为 webpack.config.js
module.exports = {
mode: 'development', // 模式 默认两种 production development
entry: './src/index.js', // 入口
output: {
filename: 'bundle.[hash:8].js', // 出口 [hash:8]显示8位hash值
path: path.resolve(__dirname, 'build') // path必须是一个绝对路径 path.resolve帮我们把相对路径解析成绝对路径
},
}
在webpack-cli/bin/config-yargs中可以看到有这样一行 defaultDescription: "webpack.config.js or webpackfile.js" 即配置文件名称为webpack.config.js or webpackfile.js 当然我们也可以手动指定配置文件的名称
npx webpack --config filename
也可以通过在package.json中的script里配置
"scripts":{
"build": "webpack --config webpack.config.js" // webpack.config.js可改为自定义文件名
},
npm run build
如果一定要手动传参也可以通过多加两个-
"scripts":{
"build": "webpack" // webpack.config.js可改为自定义文件名
},
npm run build -- --config webpack.config.js
到目前为止,我们的配置依然很弱,只能打包js文件,接下来我们继续添加更多的配置
下面我们希望能够通过http://localhost 这样的方式来启动一个服务, 我们可以通过webpack内置的一个服务 webpack-dev-server (内部是通过express实现的)
yarn add webpack-dev-server -D
现在我们可以通过
npx webpack-dev-server
当然也同样可以通过package.json设置
"scripts": {
"dev": "webpack-dev-server",
"build": "webpack --config webpack.config.js"
},
然后执行
npm run dev
同时我们可以给在webpack.config.js中对webpack-dev-server进行配置
devServer: { // 开发服务器的配置
port: '3000',
progress: true,
contentBase: './build',
open: true
},
html插件
期望:动态生成build/index.html, 并把打包后的文件引入到index.html
在src目录下建index.html,这时我们需要使用插件HtmlWebpackPlugin,plugin的用法大都相同,HtmlWebpackPlugin是一个类,new一下就可以了
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html', // template文件
filename: 'index.html', // 生成的template文件名称 默认为index.html
hash: true, // 在生成的html中添加引用hash
minify: {
removeAttributeQuotes: true, // 去除双引号
collapseWhitespace: true, // 去除空行
}
}),
],
到这里可以看到我们既可以通过在配置output的时候通过filename: bundle.[hash:8].js 来给生成文件添加hash,也可以通过HtmlWebpackPlugin的配置hash来实现,两者添加hash的方式分别为 bundle.12345678.js和 bundle?12345678
而通过filename: bundle.[hash:8].js这种方式实现,我们还需要再做一步,就是在每次重新打包的时候把旧的文件删除 // todo
样式处理
我们知道,webpack默认只支持js模块,那么我们怎么处理css/less/scss等模块呢?这时候就需要loader上场了
You may need an appropriate loader to handle this file type.
module: { // 模块
rules: [ // 规则
// css-loader 主要用来解析@import这种语法
// style-loader 把样式插入页面
// loader特点,功能单一 多个loader可以协作
// 一个loader可以使用字符串,多个可以使用数组, 需要传入参数时可以使用对象方式
// loader的顺序,默认为从右向左, 从下到上执行 webpack选择了compose方式
// {
// test: '/\.css$/', use: [
// { loader: 'css-loader', options: {} } // 对象方式
// ]
// },
{ test: /\.css$/, use: ['style-loader', 'css-loader'] },
]
},
less、sass文件同理, 以scss文件为例:
{
test: /\.css$/, use: [
{ loader: 'style-loader', options: { insertAt: 'top' } }, // 对象方式 insertAt插入位置
{ loader: 'css-loader', options: {} },
]
},
{ test: /\.scss$/, use: ['style-loader', 'css-loader', 'sass-loader'] },
css抽离
webpack4中,css抽离需要插件 mini-css-extract-plugin (注意不再是extract-text-webpack-plugin)
new miniCssExtractPlugin({
filename: 'main.css', // 抽离出的css的文件名
}),
同时在module.rules中做如下修改以避免重复插入:
- // { test: /\.scss$/, use: ['style-loader', 'css-loader', 'sass-loader'] },
+ { test: /\.scss$/, use: [miniCssExtractPlugin.loader, 'css-loader', 'sass-loader'] },
自动添加前缀
我们可以使用postcss-loader autoprefixer来实现
- // { test: /\.scss$/, use: [miniCssExtractPlugin.loader, 'css-loader', 'sass-loader'] },
+ { test: /\.scss$/, use: [miniCssExtractPlugin.loader, 'css-loader', 'postcss-loader', 'sass-loader'] },
在根目录下新建postcss.config.js
module.exports = {
plugins: [
require('autoprefixer'), // 自动添加前缀
]
}
压缩
此时我们设置mode为production,发现js和html都已经是压缩过的了,可是css文件却没有。我们这里引入另外一个插件:optimize-css-assets-webpack-plugin来优化css资源
let OptimizeCss = require('optimize-css-assets-webpack-plugin');
plugins: [
...,
new OptimizeCss(),
]
当然我们也可以通过uglifyjs-webpack-plugin来压缩我们的js文件
let UglifyjsPlugin = require('uglifyjs-webpack-plugin');
plugins: [
...,
new UglifyjsPlugin(),
]
es6
我们希望打包后的文件可以把es6语法转为es5,可以通过babel-loader帮我们实现
// src/a.js
console.log('this is a');
const fn = () => {
console.log('fn');
}
安装babel依赖模块
yarn add babel-loader @babel/core @babel/preset-env -D
{
test: /\.js|jsx$/, use:
{
loader: 'babel-loader', options: {
presets: ['@babel/preset-env']
}
}
},
es7
现在写es6的语法没有问题了,但是我们有可能用到es7的语法, 比如类的写法,这时候我们还需要借助一个插件:@babel/plugin-proposal-class-properties 又比如装饰器的写法,我们可以借助@babel/plugin-proposal-decorators来实现
{
test: /\.js|jsx$/, use:
{
loader: 'babel-loader', options: {
presets: ['@babel/preset-env'],
plugins: [
["@babel/plugin-proposal-decorators", { "legacy": true }], // 支持es7的装饰器的写法 要放在前面
'@babel/plugin-proposal-class-properties', // 支持es7的类的写法
]
}
}
},
这里需要注意的是@babel/plugin-proposal-decorators要放在@babel/plugin-proposal-class-properties前面。
es6新的API
babel只会帮我们把es6新的语法转成es5,但是对于新的API如:promise, generater 等我们还需要借助一个包 @babel-polyfill(但是比较大) 同时我们可以借助一个插件--@babel/plugin-transform-runtime 做了一定优化,如代码抽离
js语法校验 eslint
yarn add eslint eslint-loader
{
test: /\.js|jsx$/,
use: {
loader: 'eslint-loader',
options: {
enforce: 'pre', // pre post
}
}
},
接下来就可以使用自己配置好的.eslintrc.json文件(放在根目录下),当然我们也可以到eslint官网,手动进行配置,然后下载eslintrc.json,只要加个.就行了。
全局变量的引入
- 简单粗暴
import $ from 'jquery';
console.log($)
console.log(window.$) // undefined
- 内联loader expose-loader 暴露到全局的loader,用法如下:
import $ from 'expose-loader?$!jquery';
console.log($)
console.log(window.$) // undefined
- webpack expose-loader 只能通过window访问
{
test: require.resolve('jquery'),
use: 'expose-loader?$'
},
// index.js
console.log(window.$)
- webpack内置插件
let webpack = require('webpack');
new webpack.ProvidePlugin({ // 在每个模块中都注入$
$: 'jquery',
}),
// index.js
console.log($); // 都可以访问
console.log(window.$)
- cdn引入+webpack
externals: { // 外部引入,不需要打包
jquery: '$',
},
// index.js
import $ from 'jquery'; // 虽然引入但是不会再打包
console.log($);
图片处理
我们使用图片的场景有两种:
- 在js中创建图片来引用
- 再css中使用,background
- img标签的使用
// index.js
let img = new Image();
img.src = require('./logo.png');
document.body.appendChild(img);
这时我们需要借助 file-loader 了
{
test: /\.png|jpg|gif|jpeg$/,
loader: 'file-loader',
options: {
}
},
// index.js
import logo from './logo.png'; // 返回一个新的文件 好处是可以重命名图片
let img = new Image();
img.src = logo;
document.body.appendChild(img);
// a.css
body{
color: yellow;
background: url('./logo.png') no-repeat;
}
ok,发现前两种都没有什么问题,可是第三种直接写在html中的却不行
<body>
<img src='./logo.png' alt=""/>
<!-- 模版 -->
</body>
处理直接写在html里的图片,还需要用到另一个loader:
{
test: /\.html/,
use: 'html-withimg-loader'
},
重新启动就好了。
文件打包分类
如果想要做更多的限制呢,比如说对打包图片大小的限制打包成base64以减少http请求(base64会比源文件大1/3左右),我们可以使用 url-loader
{
test: /\.png|jpg|gif|jpeg$/,
loader: 'url-loader',
options: {
limit: 4 * 1024, // 图片大小限制值
outputPath: '/img/', // 输出目录
publicPath: 'http://58.com' // 添加打包后的图片域名
}
},
打包多页面应用
基本配置如下:
let path = require('path');
let HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: {
home: './src/index.js',
other: './src/other.js',
},
output: {
filename: '[name].js', // name为变量,根据entry的命名生成对应打包后的文件名称
path: path.resolve(__dirname, 'dist'),
},
plugins: [
new HtmlWebpackPlugin({ // 这里需要new多个HtmlWebpackPlugin
template: './src/index.html',
filename: 'home.html',
chunks: ['home'] // html依赖,决定了打包后的哪些文件插入该模版中, 可以写多个,且有顺序
}),
new HtmlWebpackPlugin({
template: './src/index.html',
filename: 'other.html',
chunks: ['other', 'home']
}),
],
}
配置source-map
// devtool: 'source-map', // 增加映射文件 会单独生成一个sourcemap文件,出错了会标示当前出错的列和行 特点 大 全
devtool: 'eval-map',
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html',
filename: 'home.html',
}),
],
watch用法
output: {
filename: '[name].js', // name为变量,根据entry的命名生成对应打包后的文件名称
path: path.resolve(__dirname, 'dist'),
},
watch: true, // 实时编译打包
watchOptions: { // 监控选项
poll: 1000, // 每秒访问多少次
aggregateTimeout: 500, // 防抖
ignored: /node_modules/
},
插件使用
- CleanWebpackPlugin 每次打包删除dist
- CopyWebpackPlugin // copy文件目录
- BannerPlugin // 给打包后的文件添加头注释
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html',
filename: 'home.html',
}),
new CleanWebpackPlugin('./dist'), // 传入要删除的目录 也可传数组
new CopyWebpackPlugin([{ from: './src/static', to: './dist' }]), //需要拷贝的目录 必须是arr
new webpack.BannerPlugin('make 2019 by beth.miao'),
],
webpack跨域问题 配置代理 proxy
proxy: {
'/api': {
target: 'http://localhost:3001',
pathRewrite: { '/api': '' }
}
}
resolve 属性的配置
resolve: { // 解析第三方包 common
modules: [path.resolve('node_modules')],
// mainFields: ['style', 'main'],
// mainFiles: '',
alias: {
bootstrap: 'bootstrap/dist/css/bootstrap.css',
},
extensions: ['.js', '.css', '.json', 'vue'], // 引入路径后缀名处理
},