从零开始学webpack:进阶配置

前一篇文章中介绍了一些webpack配置经常使用的loader和插件,而且完成了一个适合大多数场景的基础配置文件;本文将继续介绍webpack的配置,相对于上一篇文章,本文更加着重开发效率和个性化需求的配置javascript

多页应用打包

前一篇文章中配置都只适用于单页应用的打包,可是在实际的工做也还会涉及到多页应用的项目开发;css

webpack中多页应用与单页应用打包的不一样之处主要体如今如下几点:html

  • 多入口,不一样的页面使用不一样的入口文件
  • 多出口,入口和出口相对应,有多个入口也就有了多个出口
  • 多HTML,不一样的入口能够输出不一样的html文件

配置前端

<!-- webpack.config.js -->
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
    entry: {
      main: './main.js',
      miniApp: './miniApp.js'  
    },
    output: {
        filename: [name].[hsah].js,
        path: path.resolve(__dirname, 'dist')
    },
    plugin: [
        new HtmlWebpackPlugin({
            template: './index.html',
            filename: 'main.html',
            chunks: ['main']
        }),
        new HtmlWebpackPlugin({
            template: './index.html',
            filename: 'miniApp.html',
            chunks: ['miniApp']
        })
    ]
    // 省略其余代码 
}
复制代码

[name]: 构建包的名称,它来源于entry中的key值,在单页应用中默认为mainvue

chunks: 用于指定须要引入到html中的js文件;与它相反,还有一个excludeChunks参数,它用于指定不须要引入的js文件java

静态数据拷贝

平常的开发中会遇到一些直接使用的文件,这些文件多是js,也多是css或者是图片,它们不须要通过打包,能够直接将他们拷贝到webpack构建目录;可是手动拷贝不只麻烦而且容易出错(好比,修改以后忘记拷贝)node

copy-webpack-plugin插件用于将指定文件/文件夹复制到构建目录;经过这个插件能够将静态文件直接复制到输出目录中webpack

安装git

npm install copy-webpack-plugin -D
复制代码

配置github

<!-- webpack.config.js -->
const path = require('path')
const copyWebpackPlugin = require('copy-webpack-plugin')
module.exports = {
    plugin: [
        new copyWebpackPlugin([
            {
                from: path.resolve(__dirname, './public'),
                to: './dist'
            }
        ])
    ]
}
复制代码

from: 指定须要复制的文件夹

to: 指定复制后的文件夹

更多copyWebpackPlugin配置,能够参考官方文档

devTool(配置sourceMap)

在开发的过程当中常常会经过控制台查看错误信息;可是打包以后的错误信息位置将会以构建后的js为基准,这样的错误信息由于没有定位到源码的错误位置,对调试很不友好

这个问题能够经过对devTool配置sourceMap来解决;sourceMap是一个源码映射文件,有多种格式可选,这里只列举几个有表明性的,更多sourceMap格式能够查看官方文档

  • source-map: 原始源代码,会单独生成源码文件,能够提示错误信息的列和行
  • eval-source-map: 原始源代码,不会产生单独的文件,可是能够显示行和列
  • cheap-module-source-map: 转换后的代码,生成单独的文件,能够提示行但不能提示列
  • cheap-module-eval-source-map: 原始源代码,集成在打包后的文件中,不会生成独立的文件,能够提示行,但不能提示列

综合实际使用状况和构建速度考虑,开发环境中通常使用cheap-module-eval-source-map

<!-- webpack.config.js -->
module.exports = {
    // 省略其余代码 
    devtool: 'cheap-module-eval-source-map'
}
复制代码

webpack解决跨域问题

跨域是先后端接口交互时一个很常见的问题,解决跨域发方式也有不少,这里主要介绍如何经过webpack的配置来解决跨域问题;

准备工做

先经过server.js在本地建立一个node服务, 启动本地的3000端口做为后端服务

<!-- server.js -->
const express = require('express')
const app = express()

app.get('/api/user', (req, res) => {
    res.json({name: '阿白Smile'})
})
app.listen(3000)
复制代码

使用命令行工具执行node server.js命令启动node服务,而后使用client.js向后端服务发送请求

<!-- client.js -->
const xhr = new XMLHtttpRequest()

xhr.open('GET', '/api/user', true)

xhr.onload = function() {
    console.log(xhr.response)
}
xhr.send()
复制代码

普通代理配置

接下来就经过webpack为前端服务配置一个代理,将前端的服务代理到后端的服务上,这样可让前端和服务端在同一个服务下,以此来解决跨域问题

<!-- webpack.config.js -->
module.exports = {
    devServer: {
        prot: 3000,
        host: 'localhost'
        proxy: {
            '/api': 'http://localhost:3000'
        }
    }
}
复制代码

经过以上的配置,当client.js访问/api/user时,请求会被代理到http://localhost:3000/api/user

若是不想每次都在接口路径前加/api,或者后端的接口没有/api这一层路径,那么能够经过pathRewrite重写路径,将/api重写为空

<!-- webpack.config.js -->
module.exports = {
    devServer: {
        prot: 3000,
        host: 'localhost'
        proxy: {
            '/api': {
                target: 'http://localhost:3000',
                pathRewrite: {'/api': ''}
            }
        }
    }
}
复制代码

经过服务端启动webpack

经过服务端启动webpack,能够将前端和服务端启动在同一个服务上;当先后端都在同一个服务的时候天然就不存在跨域问题了

服务端启动webpack须要使用到webpack-dev-middleware中间件

安装

npm install webpack-dev-middleware -D
复制代码

配置

<!-- server.js -->
const express = require('express')
const webpack = require('webpack')
const middle = require('webpack-dev-middleware')
const app = express()

let config = require('./webpack.config.js')
let compiler = webpack(config)
app.use(middle(compiler))

app.get('/user', (req, res) => {
    res.json({name: '阿白Smile'})
})
app.listen(3000)
复制代码

使用这种方式的前提是可以操做服务器,不过既然均可以操做服务器了,那么还能够经过设置header来实现跨域

mock数据

webpack-dev-server提供了一个before钩子;它的第一个参数暴露了webpack-dev-server内部的express服务,经过这个服务,能够完成一些数据的mock

普通数据mock

<!-- webpack.config.js -->
module.exports = {
    devServer: {
        before(app) {
            app.get('/name', (req, res) => {
                res.json('阿白Smile')
            })
        }
    }
}   
复制代码

使用mockerAPI

mocker-api是一个为 REST API 建立mock的webpack-dev-server中间件。在后端服务尚未完成的时候,能够经过这个中间件进行mock数据

安装

npm install mocker-api -D
复制代码

配置

建立一个mock.js进行接口和数据mock

<!-- mock.js -->
module.exports = {
    'GET /userInfo/:id': (req, res) => {
        const { id } = req.params;
        // 省略查询
        return res.json({
          id,
          name: '阿白smile'
        });
    }
}
复制代码

配置到devServerbefore钩子

<!-- webpack.config.js -->
const path = require('path')
const apiMocker = require('mocker-api');
const mockApi = path.resolve('./mock.js')
module.exports = {
    devServer: {
        before(app) {
            mockApi(app, mockApi)
        }
    }
}  
复制代码

修改client.js文件,访问/userInfo接口

<!-- client.js -->
const xhr = new XMLHtttpRequest()

xhr.open('GET', '/userInfo/1', true)

xhr.onload = function() {
    console.log(xhr.response)
}
xhr.send()
复制代码

使用npm run dev命令执行脚本以后,在浏览器中打开,此时浏览器会打印出{"id":"1","name":"阿白smile"}

配置resolve属性

webpack启动后会从入口文件开始找出全部依赖的模块;resolve的做用就是告诉webpack如何寻找这些模块所对应的文件

alias (配置别名)

resolve.alias用于配置别名,经过别名能够把原来的导入路径映射到一个新的路径, 它可让模块的引入更加简单;vue中经常为src文件夹设置一个别名为@

文件结构

|--src
|--|--assets
|--|--|--main.css
复制代码

配置

<!-- webpack.config.js -->
module.exports = {
    // 省略其余代码 
    resolve: {
        alias: {
            '@': './src'
        }
    }
}
复制代码

使用

import '@/assets/main.css'
复制代码

使用别名以后,别名会直接映射到别名所指定的路径;在使用相关模块时,可使用更加简单的方式书写模块路径

modules

resolve.modules用于指定webpack解析模块时从哪些目录下搜索模块,默认状况下,webpack只从node_modules中搜索;

若是有一个模块是本身编写的文件,那么它就不须要到node_modules中取查找;好比, 咱们常常会在src/components中编写一些组件,为了不每次引用组件都写很长的路径,就能够经过modules配置来简化如下路径

文件结构

|--src
|--|--components
|--|--|--navMenu.vue
复制代码

配置

<!-- webpack.config.js -->
module.exports = {
    // 省略其余代码 
    resolve: {
        modules: ['./src/components', 'node_modules']
    }
}
复制代码

使用

import navMenu from 'navMenu'
复制代码

webpack在搜索模块时,会根据modules的配置从左到右开始搜索;因此,在使用navMenu时,会先从./src/components中查找,若是没有找到,则会去node_modules中查找;

extensions (扩展名配置)

在模块导入语句中没有带文件后缀时,webpack会自动带上尝试后缀去访问(默认带.js); resolve.extensions用于配置尝试访问的后缀列表,列表的优先级为从左到右

配置

<!-- webpack.config.js -->
module.exports = {
    // 省略其余代码 
    resolve: {
        extensions: ['.js', '.vue', '.json']
    }
}
复制代码

使用

import index from './index'
复制代码

上面的代码结合配置以后,会先尝试使用.js做为后缀去访问index,若是不存在则尝试使用.vue做为后缀去访问;以此类推,若是配置的后缀都尝试了尚未找到文件,则会报错,文件未找到

定义环境变量

在实际开发中,开发环境和线上生产环境每每会使用不一样的服务和域名,而且不一样的环境应该是能够自动切换的,这就须要在代码中使用环境变量来区分不一样的环境,而后自动切换相应的服务和域名

webpack经过内置的DefinePlugin插件能够提供了一个区分环境的全局变量

DefinePlugin的键值都是一个标志符或者多个用 . 链接起来的标志符

  • 若是value是一个字符串,它会被看成一个代码片断来使用

  • 若是value不是字符串,它会被转化为字符串(包括函数)

  • 若是value是一个对象,它全部的 key 会被一样的方式定义(即全局能够直接访问对象的key,value则会应用DefinePlugin的键值规则)

  • 若是在一个 key 前面加了 typeof,它会被定义为 typeof 调用

    module.exports = { plugin: { new webpack.DefinePlugin({ DEV: JSON.stringify('dev') }) } }

    if (DEV) { // 开发 BASE_URL: '' // 开发服务 } else { // 生产 BASE_URL: '' // 生产服务 }

区分环境

经过环境变量能够进行环境的区分,可是代码中若是涉及到多处须要区分且每次都须要手动去配置,这不只加大了工做量并且还容易出错;

通常的解决方案是将不一样环境的配置分开到不一样的文件,而后使用webpack-merge将其与基础配置进行合并

webpack.base.config.js: 基础配置文件 webpack.pro.config.js: 线上生产环境配置文件 webpack.dev.config.js: 开发环境配置文件

webpack-merge是一个函数,它提供了合并功能,接收一个或多个对象/数组,用于对象的合并与数组的链接,返回合并后的对象;在合并对象时,若是同一个key出现屡次,则后面的覆盖前面的

安装

npm install webpack-merge
复制代码

配置

const merge = require('webpack-merge')
let result = merge({name: '阿白smile', age: 18}, {age: 24, location: '北京'})

// 合并后的结果
// {name: '阿白smile', age: 24, location: '北京'}
复制代码

webpack.base.config.js是基础的webpack配置,各个环境能够通用,因此webpack.base.config.js须要做为merge的第一个参数

开发环境的配置

<!-- webpack.dev.config.js -->
const baseWebpackConfig = require('./webpack.base.config')
const merge = require('webpack-merge')

let devWebpackConfig = merge(baseWebpackConfig, {
    mode: 'development',
    devServer: {
        // 省略其余代码
    }
})
moudle.export = devWebpackConfig
复制代码

生产环境的配置

<!-- webpack.pro.config.js -->
const baseWebpackConfig = require('./webpack.base.config')
const merge = require('webpack-merge')

let proWebpackConfig = merge(baseWebpackConfig, {
    mode: 'production',
    // 省略其余代码
})
moudle.export = proWebpackConfig
复制代码

配置命令脚本

<!-- package.json -->
"scripts": {
    "dev": "webpack-dev-server --config=webpack.dev.config.js"
    "build": "webpack --config=webpack.pro.config.js"
}
复制代码

热更新

热更新主要应用在开发环境,当代码更改以后页面上只更新被修改的部分,不须要刷新页面,对开发和调试很是有利;

webpack中的热更新的配置主要依赖devServer.hot以及webpack的内置插件HotModuleReplacementPlugin

<!-- webpack.config.js -->
module.exports = {
    devServer: {
        hot: true   // 启用热更新
    },
    plugin: [
        new webpack.HotModuleReplacementPlugin()    // 热更新插件
    ]
}
复制代码

以上的代码配置以后在浏览器查看,会发现仍是会刷新整个页面;这个时候还须要在入口文件中作如下配置

if (module.hot) {
    module.hot.accept()
}
复制代码

module.hot用于通知webpack此模块能够用于热更新,更多信息能够参考官方文档

写在最后

经过本篇文章中介绍的webpack配置,能够知足更加个性化的开发需求以及更高效率的开发

本文所对应的配置源码已提交到个人github

END

相关文章
相关标签/搜索