返回介绍

五、Webpack 环境配置

发布于 2024-09-07 12:28:44 字数 23436 浏览 0 评论 0 收藏 0

5.1 Webpack Watch Mode

webpack --watch

// 简写
webpack -w
//webpack.config.js

var webpack = require('wepback')
var PurifyWebpack = require('pruifycss-webpack')
var ExtractTextWebpackPlugin = require('extract-text-webpack-plugin')
var HtmlWebpackPlugin = require('html-webpack-plugin')
var CleanWebpack = require('clean-webpacl-plugin')

var path = require('path')
var glob = rquire('glob-all')//处理多个路径

var extractLess = new ExtractTextWebpackPlugin({
filename: 'css/[name]-bundle-[hash:5].css'
})

module.exports = {
entry: {
app: './src/app.js'
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name]-bundle-[hash:5].js'
},
module:{
rules: [
{
test: /\.(css|less)$/,
use:
extractLess.extract({
// 提取 css 并不会自动加入到文档中,需要在 HTML 手动加入 css 文件
fallback: {
loader: 'style-loader',
options: {
//合并多个 style 为一个
singleton:true
}
},
// 处理 css
use: [
{
loader: 'css-loader',
options: {
minimize:true,
modules: true,
// css 模块化
localIdentName: '[path][name]_[local]_[hash:base64:5]'
}
},
{
loader: 'less-loader'
},
{
loader: 'sass-loader'
},
{
loader: 'postcss-loader',
options: {
ident: 'postcss',
plugins: [
// 合并雪碧图
require('postcss-sprites')({
// 指定雪碧图输出路径
spritePath: 'dist/assets/imgs/sprites',
retina: true // 处理苹果高清 retina 图片命名需要 xx@2x.png,对应的图片的 css 大小设置也要减小一半
})
]
}
}
]
})
}
]
}
plugin: [
new CleanWebpack()
]
}

5.2 Webpack Dev Server

5.2.1 Dev Server

不能用来直接打包文件, Dev Server 搭建本地开发,文件存在内存中

特性

  • live reloading
  • 路径重定向
  • 支持 HTTPS
  • 浏览器中显示编译错误
  • 接口代理
  • 模块热更新

dev server

  • inline
  • contentBase
  • port
  • histApiFllback
  • https
  • proxy
  • hot
  • openpage
  • lazy
  • overlay 开启错误遮罩

使用

"script"{
// 启动
"server": "webpack-dev-server --open"
}
module.exports = {
entry: {
app: './src/app.js'
},

output: {
path: path.resolve(__dirname, 'dist'),
publicPath: '/',
filename: '[name].[hash:5].js'
},

devServer: {
port: 9001,
// 输入任意路径都不会出现 404 都会重定向
// historyApiFallback: true
historyApiFallback: {
//从一个确定的 url 指向对应的文件
//rewrites: [
// {
// from: '/pages/a',// 可以写正则
// to: '/pages/a.html'
// }
rewrites: [
{
from: /^\/([a-zA-Z0-9]+\/?)([a-zA-Z0-9]+)/
to: function(context){
return '/' + context.match[1] + context.match[2] + '.html'
}
}
]
]
}
}
}

5.2.2 proxy 代理远程接口

  • 代理远程接口请求
  • http-proxy-middleware
  • devServer.proxy
module.exports = {
entry: {
app: './src/app.js'
},

output: {
path: path.resolve(__dirname, 'dist'),
publicPath: '/',
filename: '[name].[hash:5].js'
},

devServer: {
port: 9001,
// 输入任意路径都不会出现 404 都会重定向
// historyApiFallback: true
historyApiFallback: {
//从一个确定的 url 指向对应的文件
//rewrites: [
// {
// from: '/pages/a',// 可以写正则
// to: '/pages/a.html'
// }
rewrites: [
{
from: /^\/([a-zA-Z0-9]+\/?)([a-zA-Z0-9]+)/
to: function(context){
return '/' + context.match[1] + context.match[2] + '.html'
}
}
]
]
},
proxy: {
'/api': {
target: 'https://blog.poetries.top',//代理到服务器
changeOrigin:true,
logLevel: 'debug',
// pathRewite: { },
headers:{}// 请求头
}
}
}
}

5.2.3 模块热更新

  • 保持应用的数据状态
  • 节省调试时间
  • 不需要刷新
  • devServer.hot
  • webpack.HotModleReplacementPlugin
  • webpack.NamedModulesPlugin 看到模块更新的路径

Module Hot Reloading

  • module.hot
  • module.hot.accept
module.exports = {
entry: {
app: './src/app.js'
},

output: {
path: path.resolve(__dirname, 'dist'),
publicPath: '/',
filename: '[name].[hash:5].js'
},

devServer: {
port: 9001,
// 输入任意路径都不会出现 404 都会重定向
// historyApiFallback: true
historyApiFallback: {
//从一个确定的 url 指向对应的文件
//rewrites: [
// {
// from: '/pages/a',// 可以写正则
// to: '/pages/a.html'
// }
rewrites: [
{
from: /^\/([a-zA-Z0-9]+\/?)([a-zA-Z0-9]+)/
to: function(context){
return '/' + context.match[1] + context.match[2] + '.html'
}
}
]
]
},
hot:true,//开启模块热更新
hotOnly:true,
proxy: {
'/api': {
target: 'https://blog.poetries.top',//代理到服务器
changeOrigin:true,
logLevel: 'debug',
// pathRewite: { },
headers:{}// 请求头
}
}
},
plugin:[
// 模块热更新插件
new webpack.HotModuleReplacementPlugin()

// 输出热更新路径
new webpack.NamedModulesPlugin()
]
}

模块热更新配置

需要通过 module.hot

f (module.hot) {
module.hot.accept('./library.js', function() {
// Do something with the updated library module...
})
}

5.2.4 开启调试 SourceMap

Source Map 调试

把生成以后代码和之前的做一个映射

开启 Source Map 方式

JS Source Map 设置

develpoment

  • eval
  • eval-source-map
  • cheap-eval-source-map
  • cheap-module-eval-source-map

推荐使用 cheap-module-source-map

production

  • source-map
  • hidden-source-map
  • nosource-source-map

推荐使用 source-map

CSS Source Map 设置

改变 loaderoptions 选项

  • css-loader.options.soucemap
  • less-loader.options.soucemap
  • sass-loader.options.soucemap
module.exports = {
entry: {
app: './src/app.js'
},

output: {
path: path.resolve(__dirname, 'dist'),
publicPath: '/',
filename: '[name].[hash:5].js'
},
module: {
// 处理 css 的每个 loader 加上 sourceMap: true 观察 css 样式 可以看到对应的行号
rules: [
test: /\.less/,
use: [
{
loader: 'style-loader',
options: {
// singleton: true,会导致 css 的 sourceMap 不生效
//singleton: true,
sourceMap: true
}
},
{
loader: 'css-loader',
options: {
importLoaders: 2,
sourceMap: true
}
},
{
loader: 'less-loader',
options: {
sourceMap: true
}
}
]
]
},
devtool: 'cheap-module-eval-source-map',//开启 sourcemap
devServer: {
port: 9001,
// 输入任意路径都不会出现 404 都会重定向
// historyApiFallback: true
historyApiFallback: {
//从一个确定的 url 指向对应的文件
//rewrites: [
// {
// from: '/pages/a',// 可以写正则
// to: '/pages/a.html'
// }
rewrites: [
{
from: /^\/([a-zA-Z0-9]+\/?)([a-zA-Z0-9]+)/
to: function(context){
return '/' + context.match[1] + context.match[2] + '.html'
}
}
]
]
},
hot:true,//开启模块热更新
hotOnly:true,
overlay:true,//错误提示
proxy: {
'/api': {
target: 'https://blog.poetries.top',//代理到服务器
changeOrigin:true,
logLevel: 'debug',
// pathRewite: { },
headers:{}// 请求头
}
}
},
plugin:[
// 模块热更新插件
new webpack.HotModuleReplacementPlugin()

// 输出热更新路径
new webpack.NamedModulesPlugin()
]
}

5.2.5 设置 ESLint 检查代码格式

  • eslint
  • eslint-loader
  • esling-plugin-html
  • eslint-frindly-formatter 友好提示错误

配置 eslint

  • wepback config 新增 loader
  • .eslintrc 或者在 package.jsoneslintConfig 中写

配置 eslint 的规范,推荐使用 JavaScript standard style( https://standardjs.com )

需要安装以下插件

  • eslint-config-standard
  • eslint-plugin-promise
  • eslint-plugin-standard
  • eslint-plugin-import
  • eslint-plugin-node

eslint-loader

  • options.failOnWarning 出现警告
  • options.failOnError
  • options.formatter
  • options.outputReport

设置 devServer.overlay 在浏览器中看提示的错误

// .eslintrc

module.exports = {
root: true,
extends: 'standard',
plugins: [],
env: {
browsers: true,
node: true // node 环境
},
rules: {
// 缩进
indent: ['error', 4],

//换行
"eol-last": ['error', 'never']
}
}
module.exports = {
entry: {
app: './src/app.js'
},

output: {
path: path.resolve(__dirname, 'dist'),
publicPath: '/',
filename: '[name].[hash:5].js'
},
module: {
// 处理 css 的每个 loader 加上 sourceMap: true 观察 css 样式 可以看到对应的行号
rules: [
{
test: /\.js$/,
include: [path.resolve(__dirname,'src/')],
exclude: [path.resolve(__dirname,'src/libs')],
use: [
{
loader: 'babel-loader',
options: {
presets: ['env']
}
},
//eslint-loader 需要在 babel-loader 之后处理
{
loader: 'eslint-loader',
options: {
formatter: require('eslint-frindly-formatter')
}
}
]
},
{
test: /\.less/,
use: [
{
loader: 'style-loader',
options: {
// singleton: true,会导致 css 的 sourceMap 不生效
//singleton: true,
sourceMap: true
}
},
{
loader: 'css-loader',
options: {
importLoaders: 2,
sourceMap: true
}
},
{
loader: 'less-loader',
options: {
sourceMap: true
}
}
]
}
]
},
devtool: 'cheap-module-eval-source-map',//开启 sourcemap
devServer: {
port: 9001,
// 输入任意路径都不会出现 404 都会重定向
// historyApiFallback: true
historyApiFallback: {
//从一个确定的 url 指向对应的文件
//rewrites: [
// {
// from: '/pages/a',// 可以写正则
// to: '/pages/a.html'
// }
rewrites: [
{
from: /^\/([a-zA-Z0-9]+\/?)([a-zA-Z0-9]+)/
to: function(context){
return '/' + context.match[1] + context.match[2] + '.html'
}
}
]
]
},
hot:true,//开启模块热更新
hotOnly:true,
overlay:true,//错误提示
proxy: {
'/api': {
target: 'http://blog.poetries.top',//代理到服务器
changeOrigin:true,
logLevel: 'debug',
// pathRewite: { },
headers:{}// 请求头
}
}
},
plugin:[
// 模块热更新插件
new webpack.HotModuleReplacementPlugin()

// 输出热更新路径
new webpack.NamedModulesPlugin()
]
}

5.2.6 区分开发环境 和 生产环境

开发环境

  • 模块热更新
  • sourceMap
  • 接口代理
  • 代码规范检查

生产环境

  • 提取公共代码
  • 压缩
  • 文件压缩或 base64 编码
  • 去除无用的代码

共同点

  • 入口一致
  • loader 处理
  • 解析配置一致

使用 webpack-merge 合并公共配置

  • webpack.dev.conf.js
  • wepback.prod.conf.js
  • webpack.common.conf.js
"scripts":{
"server": "wepback-dev-server --env development --open --config build/webpack.common.config.js",
"build": "wepback --env production --open --config build/webpack.common.config.js"
}

公共配置 build/webpack.common.conf.js

const merge = require('webpack-merge')
const webpack = require('webpack')
const path = require('path')

const chalk = require('chalk')
const ExtractTextWebpackPlugin = require('extract-text-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const ProgressBarPlugin = require('progress-bar-webpack-plugin')

const developmentConfig = require('./webpack.dev.conf')
const productionConfig = require('./webpack.prod.conf')


// 根据环境变量生成配置
const generateConfig = env =>{
const extractLess = new ExtractTextWebpackPlugin({
filename: 'css/[name]-bundle-[hash:5].css'
})
const scriptLoader = [
'babel-loader'
].concat(env === 'production'
? []
: [{
loader: 'eslint-loader',
options: {
formatter: require('eslint-friendly-formatter')
}
}]
)
const cssLoaders = [
{
loader: 'css-loader',
options: {
importLoaders: 2,
sourceMap: env==='development'
}
},
{
loader: "postcss-loader",
options: {
ident: "postcss",
sourceMap: env==='development',
plugins: [

].concat(env==='production'
? require('postcss-sprites')({
spritePath: 'build/assets/imgs/sprites',
retina: true
})
:[]
)
}
},
{
loader: 'less-loader',
options: {
sourceMap: env==='development'
}
}
]
const styleLoader = env === 'production'
// 线上需要提取 css 成文件
? extractLess.extract({
fallback:'style-loader',
use: cssLoaders
})
: ['style-loader'].concat(cssLoaders)

const fileLoader = env === 'development'
? [{
loader: 'file-loader',
options: {
name: '[name]-[hash:5].[ext]',
outputPath: 'assets/imgs/'
}
}]
: [{
loader: 'url-loader',
options: {
name: '[name]-[hash:5].[ext]',
limit: 1000,//1k
outputPath: 'assets/imgs/'
}
}]

return {
entry: {
app: './src/index.js'
},
output: {
path: path.resolve(__dirname, '../build'),
publicPath: '/',
filename: '[name]-bundle-[hash:5].js'
},
// 路径解析
resolve: {
alias: {
// jquery$: path.resolve(__dirname, '../src/libs/jquery.min.js')
}
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
include: [path.resolve(__dirname,'../src/')],
exclude : /node_modules/,
use: scriptLoader
},
{
test: /\.(less|css|scss)/,
use: styleLoader
},
{
test: /\.(png|jpg|jpeg|gif)$/,
use: fileLoader.concat(env==='production'
? [
{
loader: 'img-loader',
options: {
pngquant: {
quality: 80
}
}
}
]
: []
)
},
{
test: /\.(eot|woff2?|ttf|svg)$/,
use: fileLoader
}
]
},
plugins: [
extractLess,

new ProgressBarPlugin({
format: ' build [:bar] ' + chalk.green.bold(':percent') + ' (:elapsed seconds)',
clear: false
}),
new HtmlWebpackPlugin({
inject: true,
template: path.resolve(__dirname,'../public/index.html'),
minify: {
collapseWhitespace: true
}
}),
new webpack.ProvidePlugin({
$: 'jquery'
})
]
}
}


module.exports = env =>{
const config = env==='development' ? developmentConfig : productionConfig
return merge(generateConfig(env),config)
}

开发环境配置 build/webpack.dev.conf.js

const webpack = require('webpack')
const path = require('path')

module.exports = {
devtool: 'cheap-module-eval-source-map',//开启 sourcemap
devServer: {
port: 9001,
// 输入任意路径都不会出现 404 都会重定向
historyApiFallback: true,
// historyApiFallback: {
//从一个确定的 url 指向对应的文件
//rewrites: [
// {
// from: '/pages/a',// 可以写正则
// to: '/pages/a.html'
// }
// rewrites: [
// {
// from: /^\/([a-zA-Z0-9]+\/?)([a-zA-Z0-9]+)/
// to: function(context){
// return '/' + context.match[1] + context.match[2] + '.html'
// }
// }
// ]
// },
hot:true,//开启模块热更新
hotOnly:true,
overlay:true,//错误提示
proxy: {
'/api': {
// target: 'http://blog.poetries.top',//代理到服务器
changeOrigin:true,
logLevel: 'debug',
// pathRewite: { },
headers:{}// 请求头
}
}
},
plugins: [
// 模块热更新插件
new webpack.HotModuleReplacementPlugin(),

// 输出热更新路径
new webpack.NamedModulesPlugin()
]
}

生产环境配置 build/webpack.prod.conf.js

const webpack = require('webpack')
const PurifyCssWebpack = require('purifycss-webpack')
const ExtractTextWebpackPlugin = require('extract-text-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const CleanWebpackPlugin = require('clean-webpack-plugin')

const path = require('path')
const glob = require('glob-all')//处理多个路径

module.exports = {
plugins: [
new PurifyCssWebpack({
paths: glob.sync([
'./*html',
'./src/*js'
])
}),
new webpack.optimize.CommonsChunkPlugin({
name: 'manifest'
}),
new HtmlWebpackPlugin({
inlineChunks: ['manifest']
}),
new webpack.optimize.UglifyJsPlugin(),
new CleanWebpackPlugin(['../build'])
]
}

5.3 使用 middleware 来搭建开发环境

可以更灵活配置,需要以下插件搭建

  • Express or koa
  • webpack-dev-middleware
  • webpack-hot-middleware 热更新
  • http-proxy-middleware 代理
  • connect-history-api-fallback 地址 rewrite
  • opn 命令工具打开浏览器页面
// build/server.js

/**
* 使用 middleware 搭建服务:更灵活配置,不在使用 webpack-dev-server
* @type {[type]}
*/

const express = require('express')
const webpack = require('webpack')
const opn = require('opn')

const app = express()
const port = 3000

//把 express 和配置联合起来 需要用到 middleware
const webpackDevMiddleware = require('webpack-dev-middleware')
const webpackHotMiddleware = require('webpack-hot-middleware')
const proxyMiddleware = require('http-proxy-middleware')
const historyApiFallback = require('connect-history-api-fallback')

const config = require('./webpack.common.conf')({env:'development'})
const compiler = webpack(config) //给 express 使用

const proxyTable = require('./proxy')

for(let context in proxyTable){
app.use(proxyMiddleware(context, proxyTable[context]))
}

app.use(historyApiFallback(require('./historyfallback')))

app.use(webpackDevMiddleware(compiler, {
publicPath: config.output.publicPath
}))

app.use(webpackHotMiddleware(compiler))


app.listen(port, function(){
console.log('> Ready on:' + port)
opn('http://localhost:' + port)
})

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文