webpack4-基础配置

542 阅读6分钟

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,只要加个.就行了。

全局变量的引入

  1. 简单粗暴
    import $ from 'jquery';
    console.log($)  
    console.log(window.$) // undefined
  1. 内联loader expose-loader 暴露到全局的loader,用法如下:
import $ from 'expose-loader?$!jquery';
console.log($)  
console.log(window.$) // undefined
  1. webpack expose-loader 只能通过window访问
{
    test: require.resolve('jquery'),
    use: 'expose-loader?$'
},

// index.js
console.log(window.$)  
  1. webpack内置插件
let webpack = require('webpack');

new webpack.ProvidePlugin({     // 在每个模块中都注入$
    $: 'jquery',
}),

// index.js
console.log($);         // 都可以访问
console.log(window.$)  
  1. cdn引入+webpack
externals: {    // 外部引入,不需要打包
    jquery: '$',
},

// index.js
import $ from 'jquery';     // 虽然引入但是不会再打包
console.log($);

图片处理

我们使用图片的场景有两种:

  1. 在js中创建图片来引用
  2. 再css中使用,background
  3. 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'],    // 引入路径后缀名处理
    },