webpack
webpack 是一个现代 JavaScript 应用程序的静态模块打包器(module bundler)。
四大核心概念
- entry(入口)
- output(输出)
- loader(用于对模块的源代码进行转换)
- plugins(插件)
dev.server
// https://webpack.js.org/configuration/dev-server/#devserver
module.exports = {
devServer: {
open: false, // 是否自动开启
hot: true, // 开启热更新
compress: false, // 是否开启压缩
host: process.env.HOST || 'localhost',
port: process.env.PORT || 8088,
// contentBase: './src',
proxy: {
'/api': {
target: targets[PROXY_ENV],
changeOrigin: true // 默认值:false,为true时发送请求头中host会设置成target
}
}
}
}
webpack.base
使用更快的loader、使用多线程happypack、fast-sass-loader替代sass-loader
css文件打包
css文件的打包需要用到css-loader和style-loader两个loader
css-loader:只是用于加载css文件(并没有添加到页面)。
style-loader:则是将打包后的css代码以<style>标签形式添加到页面头部。
module: {
rules: [
{
test: /\.css$/, // 正则表达式,表示.css后缀的文件
use: ['style-loader', 'css-loader'] // 针对css文件使用的loader,注意有先后顺序,数组项越靠后越先执行
}
]
}
happyPack
happypack可以将任务分解给多个子进程,最后将结果发给主进程,js是单线程模型,通过这种多线程的方式提高性能。
// 在module的rules中
{
test: /\.js$/,
use: [
isProduction ? 'happypack/loader?id=happyBabel' : 'babel-loader'
],
exclude: '/node_modules/',
include: path.resolve('../src')
}
// 在plugins中
// https://github.com/amireh/happypack
new HappyPack({
id: 'happyBabel', // loader?后面指定的id
loaders: [{
loader: 'babel-loader',
options: {
cacheDirectory: true // 利用缓存,提高性能
}
}],
threadPool: HappyPack.ThreadPool({ size: 4 }), // 代表共享进程池,(查看电脑cpu核数,require('os').cpus().length)
verbose: true, // 是否允许 HappyPack 输出日志,默认是 true
}),
webpack.dev
const devWebpackConfig = merge(baseConfig, devServer, {
mode: 'development', // 开发模式配置,默认production
stats: 'errors-only', // errors-only:只在发生错误时输出【该配置可处理webpack服务启动时,去掉多余的打印信息】
devtool: 'cheap-module-eval-source-map',
// watch: true, // 开启监视模式,此时webpack指令进行打包会监视文件变化自动打包
plugins: [
// DefinePlugin会解析定义的环境变量表达式,当成JS执行
new webpack.DefinePlugin({
IS_DEV: 'true'
}),
// https://github.com/ampedandwired/html-webpack-plugin
new HtmlWebpackPlugin({
filename: 'index.html',
template: `./src/${PROJECT_PATH}/public/index.html`,
inject: true
})
]
})
webpack.dll
- 作用:使用DllPlugin可以减少基础模块编译次数,动态链接库插件。
- 原理是:把依赖的基础模块抽离出来打包到dll文件中,当需要导入的模块存在于某个dll中时,这个模块不再被打包,而是去dll中获取。
在dll中大多包含的是常用的第三方模块,只要这些模块版本不升级,就只需要被编译一次。 - 注意:DllPlugin参数中的name必须要和output.library值保持一致,并且生成的mainfest文件中会引用output.library值
module.exports = {
mode: 'production', // 开发模式配置,默认production || development
stats: {
modules: false, // 默认true,是否添加构建模块信息
},
entry: {
rplib: ['vue/dist/vue.runtime.esm.js', 'vue-router', 'lodash']
},
output: {
path: path.resolve(__dirname, '../dll'),
filename: '[name].dll.[chunkhash].js',
library: '[name]' // 最终会在全局暴露出一个[name].dll.[chunkhash]的对象
},
plugins: [
new webpack.DllPlugin({
name: '[name]',
path: path.resolve(__dirname, '../dll/[name]-manifest.json'),
}),
// 不打包moment的语言包
new webpack.IgnorePlugin(/\.\/locale/, /moment/),
]
}
webpack.prod
生产环境的一些常用的基础、优化配置
- html优化
- 压缩:html-webpack-plugin
- css优化
- 提取到单独文件:mini-css-extract-plugin
- 压缩:optimize-css-assets-webpack-plugin
- js优化
- 代码分离:手动配置多入口,抽取公用代码、懒加载、SplitChunksPlugin参数详解
- 依赖库分离:optimization.splitChunks
- 压缩:terser-webpack-plugin
module.exports = merge(baseConfig, {
// https://www.webpackjs.com/configuration/stats/#stats
stats: {
children: false, // 默认true,是否添加children信息(设置为false,解决webpack4打包时出现:Entrypoint undefined = index.html)
modules: false, // 默认true,是否添加构建模块信息
},
output: {
// path.resolve:解析当前相对路径的绝对路径
// path.join(path1,path2...) 将路径片段使用特定的分隔符(window:\)连接起来形成路径,并规范化生成的路径
path: path.resolve(__dirname, '..', outputDir),
filename: assetsPath('js/[name].[chunkhash].js'),
chunkFilename: assetsPath('js/[name].[chunkhash].js')
},
}
webpack.smp打包速度分析
const SpeedMeasurePlugin = require('speed-measure-webpack-plugin')
const smp = new SpeedMeasurePlugin()
// 打包速度分析,查看每个loader和插件执行耗时
module.exports = smp.wrap(prodConfig)
webpack.analyse打包分析
// 打包分析
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
module.exports = merge(prodConfig, {
plugins: [
new BundleAnalyzerPlugin()
]
})
使用koa开启打包后的dist文件
const path = require('path')
const devConf = require('../dev.server')
const { spawn } = require('child_process')
// 开启服务
const startService = () => {
const Koa = require('koa')
const Router = require('koa-router')
const Static = require('koa-static')
const proxy = require('koa-server-http-proxy') // https://github.com/eugeneCN/koa-server-http-proxy
const app = new Koa()
const router = new Router()
// 导入代理设置
Object.keys(devConf.devServer.proxy).forEach(key => {
app.use(proxy(key, devConf.devServer.proxy[key]))
})
app.use(Static(path.join(__dirname, '..', '..', 'dist')))
router.get('*', async (ctx, next) => {
ctx.redirect('/')
})
app.use(router.routes()).use(router.allowedMethods())
app.listen(4002, () => {
console.log('Server start at: http://localhost:4002/')
})
}
/**
* @param 当没有安装依赖包的时,可以开启子进程安装依赖包
* http://nodejs.cn/api/child_process.html
* 使用npm的时候spawn('npm', ['install'], {})
* **/
const _yarn = spawn('yarn', {cwd: './build/start'})
// stdout 获取标准输出
_yarn.stdout.on('data', data => {
console.log(`stdout: ${data}`)
})
// stderr 获取标准错误输出
_yarn.stderr.on('data', data => {
console.error(`stderr: ${data}`)
})
_yarn.on('close', code => {
console.log(`子进程退出,退出码: ${code}`)
startService()
})
外部扩展externals配置
配置externals选项提供了「从输出的 bundle 中排除依赖」的方法。
vue.config.js
const externals = ['vue', 'vuex', 'vue-router', 'vant']
const isProduction = process.env.NODE_ENV === 'production'
module.exports = {
configureWebpack: {
externals: isProduction ? externals : []
},
}
webpack.config.js
module.exports = {
...
output: {
...
},
externals : {
vue: 'vue',
vuex: 'vuex'
}
...
}
index.html
<script src="https://cdn.bootcss.com/vue/2.6.10/vue.runtime.min.js"></script>
项目地址
如有不对,请指出。