paths路径文件配置
const path = require('path');
const glob = require('glob');
class Path {
constructor({
sourceName, // 项目总入口
pagesEntryName, // 页面总入口
entryName, // 页面入口文件
pageConfigName, // 页面配置文件
sourceWideToEntry, // 页面总入口到页面入口文件的宽度
templateEntryConfig, // 模板的配置文件
templateEntry, // 模板
dist // 输出文件位置
}) {
this.home = process.cwd();
this.sourceName = sourceName;
this.pagesEntryName = pagesEntryName;
this.entryName = entryName;
this.pageConfigName = pageConfigName;
this.templateEntry = templateEntry;
this.templateEntryConfig = templateEntryConfig;
this.sourceWideToEntry = sourceWideToEntry;
this.dist = dist;
}
distPath() {
return path.resolve(this.home, this.dist);
}
sourcePath() {
return path.resolve(this.home, this.sourceName);
}
pagesEntryPath() {
return path.resolve(this.sourcePath(), this.pagesEntryName);
}
entryPath() {
return path.resolve(
this.pagesEntryPath(),
this.sourceWideToEntry,
this.entryName
);
}
pageConfigPath() {
return path.resolve(
this.pagesEntryPath(),
this.sourceWideToEntry,
this.pageConfigName
);
}
templateEntryPath() {
return path.resolve(this.home, this.templateEntry);
}
templateEntryConfigPath() {
return path.resolve(this.home, this.templateEntryConfig);
}
entryPaths() {
// 所有页面入口文件的路径
return glob.sync(this.entryPath());
}
pageConfigPaths() {
// 所有页面配置文件的路径
return glob.sync(this.pageConfigPath());
}
}
module.exports = new Path({
sourceName: 'src',
pagesEntryName: 'pages',
entryName: 'entry.js',
sourceWideToEntry: '**',
pageConfigName: 'page.js',
templateEntry: 'template/index.html',
templateEntryConfig: 'template/page.js',
dist: 'dist'
});
entry (根据路径自动获取所有入口)
const entryConfigure = () => {
const entries = {};
// paths.entryPaths()为['/Users/pjm/Desktop/Components/src/pages/admin/entry.js', '/Users/pjm/Desktop/Components/src/pages/ticket/entry.js']
// paths.pagesEntryPath()为'/Users/pjm/Desktop/Components/src/pages'
paths.entryPaths().forEach((item) => {
const result = path.relative(paths.pagesEntryPath(), item);
const key = path
.dirname(result)
.split(/\\/g)
.join("/");
entries[key] = item;
});
// entries最终为 { admin: '/Users/pjm/Desktop/Components/src/pages/admin/entry.js', ticket: '/Users/pjm/Desktop/Components/src/pages/ticket/entry.js'}
return entries;
};
module.exports = {
entry: entryConfigure,
}
- 根据pages与每一个页面入口entry.js的相对路径,来决定入口的键名(之后可以通过这个键名,来决定输出目录的结构)
- src/pages 与 src/pages/admin/entry.js 之间的相对路径,就可以取得admin/entry.js
- 再根据path.dirname,去掉文件名,最终可以拿到admin.
output
const paths = require('../paths.config');
module.exports = (production = false) => {
// paths.distPath() 为 '/Users/pjm/Desktop/Components/dist'
return {
output: production
? {
path: paths.distPath(),
filename: "[name]/index.[chunkhash].js",
chunkFilename: "[name].[id].[chunkhash].js",
// publicPath: `/`,
}
: {
path: paths.distPath(),
filename: "[name].js",
chunkFilename: "[name].[id].js",
publicPath: "/",
},
};
};
- 在开发环境不需要hash,可以缓存的就缓存,减少请求
- 在生产环境,entry中已经设定好键名了,那么,只要在filename中[name],就可以设定输出文件的路径了
- chunkFilename可以决定,输出除了entry外的文件,比如splitChunks出来的文件
htmlWebpackPlugin
npm i html-webpack-plugin --save-dev使用方法
配置
const paths = require("../paths.config");
const fs = require('fs');
const path = require('path');
const HtmlWebpackPlugin = require("html-webpack-plugin");
const MANIFEST = 'manifest';
const VENDOR = 'vendor';
// 用作合并html模板配置
const deepMerge = (...objs) => {
let result = {};
objs.forEach((obj = {}) => {
Object.keys(obj || {}).forEach((key) => {
if (result[key] === undefined) {
// 第一次初始化的情况, 防止复制引用地址,用merge处理
if (Array.isArray(obj[key])) {
result[key] = Object.values(merge(obj[key]));
} else if (typeof (obj[key]) === 'object') {
result[key] = merge(obj[key]);
} else {
// 普通数据直接覆盖值
result[key] = obj[key];
}
} else if (Array.isArray(result[key])) {
// 先处理result有引用值的情况
obj[key].forEach((value) => {
if (!result[key].includes(value)) {
result[key].push(value);
}
});
} else if (typeof(result[key]) === 'object') {
result[key] = merge(result[key], obj[key]);
} else {
// 普通数据直接覆盖值
result[key] = obj[key];
}
});
});
return result;
};
const handleHTMLPlugin = (key, myPage, myConfigs, production) => {
const opts = {
// 生成html的路径
filename: path.join(key, 'index.html').split(/\\/g).join('/'),
// html模板的路径
template: myPage,
// 生成的chunk不自动插入html中
inject: false,
// 生产环境生产的html删除空格和注释
minify: production
? {
removeComments: true,
collapseWhitespace: true,
}
: false,
// html是否缓存
cache: true,
// 增加的参数,可以在html中通过htmlWebpackPlugin.options.configs拿到里面的配置
configs: myConfigs,
// 生产环境配置splitCode,会生成manifest和vender文件
// 这里的chunk如果上面的inject配置不为false就会自动插入生成的html中,如果为false,那么得通过htmlWebpackPlugin.files获取
chunks: production ? [MANIFEST, VENDOR, key] : [key],
// chunk插入文件的顺序,手动
chunksSortMode: "manual",
};
return new HtmlWebpackPlugin(opts);
}
// 传入的entries为入口配置对象
module.exports =(entries, production = false) => {
return {
plugins: [
...Object.entries(entries).map(([key, entryPath]) => {
const dirName = path.dirname(entryPath);
// html模板取全局还是局部
const indexHTML = path.join(dirName, 'index.html');
// paths.templateEntryPath()为'/Users/pjm/Desktop/Components/template/index.html'
const myPage = fs.existsSync(indexHTML)
? indexHTML
: paths.templateEntryPath();
// html的配置文件取局部还是全局
const indexConfig = path.join(dirName, 'page.js');
// paths.templateEntryConfigPath() 为 '/Users/pjm/Desktop/Components/template/page.js'
const globalPageConfig = require(paths.templateEntryConfigPath());
const myConfigFile = fs.existsSync(indexConfig)
? require(indexConfig)
: globalPageConfig;
// 将当前html的配置和全局html的配置合并
const myConfigs = deepMerge({}, globalPageConfig, myConfigFile);
return handleHTMLPlugin(key, myPage, myConfigs, production);
}),
],
};
};
html模板
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8" />
<title>
<%= htmlWebpackPlugin.options.configs.title %>
</title>
<link
rel="shortcut icon"
href="<%= htmlWebpackPlugin.options.configs.icon %>"
/>
<meta
name="viewport"
content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no"
/>
<meta
name="Copyright"
content="<%= htmlWebpackPlugin.options.configs.copyright %>"
/>
<meta
name="Description"
content="<%= htmlWebpackPlugin.options.configs.descriptions %>"
/>
<meta
name="Keywords"
content="<%= htmlWebpackPlugin.options.configs.keywords %>"
/>
<!--开始注入配置文件的css-->
<% Array.isArray(htmlWebpackPlugin.options.configs.css) &&
htmlWebpackPlugin.options.configs.css.forEach(function(css) { %>
<link href="<%= css %>" rel="stylesheet" />
<% }) %>
<!--结束注入配置文件的css-->
<!--开始注入webpack生成的css-->
<% htmlWebpackPlugin.files.css.forEach(function(css) { %>
<link href="<%= css %>" rel="stylesheet" />
<% }) %>
<!--结束注入webpack生成的css-->
<!--开始注入配置文件的script-->
<% Array.isArray(htmlWebpackPlugin.options.configs.script) &&
htmlWebpackPlugin.options.configs.script.forEach(function(script) { %>
<script type="text/javascript" src="<%= script %>"></script>
<% }) %>
<!--结束注入配置文件的script-->
</head>
<body>
<div class="page">
<div id="app"></div>
</div>
<!--开始注入webpack生成的js-->
<% htmlWebpackPlugin.files.js.forEach(function(js) { %>
<script
type="text/javascript"
src="<%= js %>"
></script>
<% }) %>
<!--结束注入webpack生成的js-->
</body>
</html>
html模板htmlWebpackPlugin详细数据
通过在上面模板打印console.log(JSON.parse('<%= JSON.stringify(htmlWebpackPlugin)%>'))可以知道htmlWebpackPlugin可以获取哪些数据
{
"tags":{
"headTags":[],
"bodyTags":[
{
"tagName":"script",
"voidTag":false,
"attributes":{"defer":false,"src":"../admin/index.a37714976896446bf1ac.js"}
}
]
},
"files":{
"publicPath":"../",
// 通过配置的chunks生成的js文件都会到这
"js":["../admin/index.a37714976896446bf1ac.js"],
// 如果有配置mini-css-extract-plugin生成的css文件会在这里可以取到
"css":[]
},
"options":{
"template":"/Users/pjm/Desktop/Components/node_modules/html-webpack-plugin/lib/loader.js!/Users/pjm/Desktop/Components/template/index.html",
"templateContent":false,
"filename":"admin/index.html",
"publicPath":"auto",
"hash":false,"
inject":false,
"scriptLoading":"blocking",
"compile":true,
"favicon":false,
"minify":{
"removeComments":true,
"collapseWhitespace":true
},
"cache":true,
"showErrors":true,
"chunks":["manifest","vendor","admin"],
"excludeChunks":[],
"chunksSortMode":"manual",
"meta":{},
"base":false,
"title":"Webpack App",
"xhtml":false,
"configs":{
"title":"webpack-devkit",
"icon":"",
"descriptions":"webpack-devkit",
"keywords":"webpack-devkit",
"css":["https://unpkg.com/element-ui/lib/theme-chalk/index.css"],
"script":[
"http://cdn.bootcdn.net/ajax/libs/vue/2.6.11/vue.js",
"https://unpkg.com/element-ui/lib/index.js"
]
}
}
}
这样配置,可以控制,控制台输出的信息很简洁
thread-loader
npm i thread-loader --save-devthread使用方法
- webpack同一时间只能执行一个任务,用了thread可以让webpack同一时间执行多个任务,处理完后再将结果返回主进程,其中HappyPack也是同理,但是HappyPack不在维护了,所以,推荐官网的thread-loader
- 注意,需要在比较耗时的loader上使用,并且使用的loader不能产生新的文件
- 具体用法在下面耗时loader有用到(babel,eslint...)
babel-loader
npm install --save-dev babel-loader @babel/core @babel/preset-env [babel使用方法]
- @babel/core: babel的核心功能都在这里,没有它,就不能使用babel编译
- @babel/preset-env: 预设插件,就是这里面包含一系列插件,不用我们一个个安装,它会根据我们所编写的代码和设定的浏览器来进行处理,所以说,如果我们的代码只运行在浏览器环境,那么,最好通过.browserslistrc文件来指定浏览器,这样防止采取默认兼容了所有的浏览器
- 注意:插件分语法插件,转换插件,转换插件是用来协助对应的语法插件的,所以,使用转换插件必须配合对应的语法插件,但不必指定二种插件,只需指定转换插件,就可以调用语法插件了。语法转换只是将高版本语法转换成低版本语法,而新的内置函数和实例方法低版本没有,即无法转换,所以需要垫片(polyfill或runtime)来填充(www.babeljs.cn/docs/)
const babelLoaderConfigure = () => {
return {
test: /\.js$/,
exclude: /node_modules/,
use: [
'thread-loader',
{ loader: 'babel-loader', options: { cacheDirectory: true } }
]
};
};
web应用开发
npm install --save core-js@3
{
"presets": [
[
"@babel/preset-env",
{
"useBuiltIns": "usage",
"corejs": 3
}
]
]
}
- @babel-polyfill: 完整模拟2015ES+环境,但是需要全局引入,使打包后的文件变得非常大,也会对全局环境造成污染,也会像一些类的原型上增加方法,导致整体变得不可控
- 从Babel7.4开始@babel/polyfill就不推荐使用了,因为polyfill本身也就是core-js与regenerator-runtime这二个包的集合,所以引入这2个包也行,core-js是javascript标准库的polyfill,尽可能的模块化,让你选择你需要的功能,默认引入的是core-js@2,但是分支已经封锁,之后的更新都会到core-js@3中,所以,我们使用core-js@3就行
- 在@babel/preset-env配置中有useBuiltIns和corejs两个属性,可以来控制polyfill的引入,使用useBuiltIns有三种分别为false,entry,usage。false:手动引入,打包全部。entry:根据入口文件全部引入,usage:按需引入,如果使用了useBuiltIns,但是未指定corejs,那么就会报错.
- 以上配置方式,都会往我们代码中注入辅助函数,导致全局环境的污染,但是不会改造本身的代码,例如以下使用class时,就会注入辅助函数_classCallCheck, _defineProperties, _createClass
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }
var a = Object.assign({}, {});
console.log(a);
[(1, 5, 10, 15)].filter(function (value) {
return value > 9;
});
var promise = new Promise(function (resolve, reject) {
resolve(1);
});
var A = /*#__PURE__*/function () {
function A() {
_classCallCheck(this, A);
}
_createClass(A, [{
key: "say",
value: function say() {
console.log('hello');
}
}]);
return A;
}();
框架,类库开发
npm install --save @babel/runtime
npm install @babel/runtime-corejs3 --save
npm install --save-dev @babel/plugin-transform-runtimeplugin-transform-runtime使用方法
{
"presets": ["@babel/preset-env"],
"plugins": [
[
"@babel/transform-runtime",
{
"corejs": 3
}
]
]
}
- @babel/runtime:这个是辅助函数的集合包,在引用这个的时候,不会导致全局污染.
- @babel/plugin-transform-runtime: 移除辅助函数,替换成@babel/runtime/helpers中函数的引用,同时避免冗余引入减小打包体积,
- 会将我们本身代码进行改造
.browserslistrc文件
defaults
IE >= 9
Chrome >= 43
ChromeAndroid >= 43
Android >= 4.1
iOS >= 13
eslint-loader
npm i --save-dev eslint-loader
npm i --save-dev babel-eslint
npm i --save-dev standard
npm i --save-dev eslint-plugin-prettier eslint-config-prettierprettier使用方法 配合其他插件用法
npm i --save-dev eslint-plugin-vue使用方法
const eslintLoaderConfigure = () => {
return {
enforce: 'pre',
test: /\.(js|vue)$/,
exclude: /node_modules/,
use: ['thread-loader', 'eslint-loader']
};
};
.eslintrc.js文件
module.exports = {
root: true,
parserOptions: {
parser: 'babel-eslint'
},
env: {
browser: true,
node: true
},
extends: [
'standard',
'plugin:vue/recommended',
'plugin:prettier/recommended',
"prettier/standard",
'prettier/vue'
],
rules: {
'vue/component-tags-order': [
'error',
{
order: ['template', 'script', 'style']
}
]
}
};
.prettierc文件
{
"semi": true,
"singleQuote": true,
"trailingComma": "none"
}
- plugin:prettier/recommended: 在extend中,写了前面的plugin就可以不用再plugin中加这个插件了,这个写法相当于已经引用了
- eslint-config-prettier: 这个可以让其他规则不会与prettier冲突,但,这个必须放在extends的最后,每一个eslint规则,都可以用一个与之对应的,来解决冲突,如plugin:vue/recommended与prettier/vue
- babel-eslint: 指定这个解析器,用来支持es6语法.
externals
module.exports = {
externals: {
vue: "Vue",
'element-ui': 'ELEMENT',
},
}
- 这个为外部扩展的内容,需要声明后,才能在项目的上下文中使用,并且排除打包
resolve
module.exports = {
resolve: {
alias: {
src: paths.sourcePath()
},
extensions: [".js", ".json", ".vue"],
}
}
- resolve都是跟解析有关的,而alias较为常用,是使用别名去代替某段路径,如上面写的那样,我们使用src代替了根路径到文件入口这段路径(绝对路径)
- extensions(简化路径文件后缀名)
progress-bar-webpack-plugin
npm i --save-dev progress-bar-webpack-plugin使用方法
npm i --save-dev chalk
const ProgressBarPlugin = require("progress-bar-webpack-plugin");
const chalk = require("chalk");
module.exports = {
plugins: [
new ProgressBarPlugin({
format: ` build [:bar] ${chalk.green.bold(
":percent"
)} (:elapsed seconds) :msg`,
clear: true,
}),
]
}
- 一个好看的编译进度显示,效果图如下
clean-webpack-plugin
npm i --save-dev clean-webpack-plugin使用方法
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
module.exports = {
plugins: [
new CleanWebpackPlugin({
cleanOnceBeforeBuildPatterns: ['**/*', paths.distPath()]
}),
]
}
- 清理输出文件,默认情况下清理output.path指定的文件夹内的内容
- 但是如果dist文件夹内有自己的目录结构不止一层结构,那么,就需要指定cleanOnceBeforeBuildPatterns去清理了,不然会报错,或者配置dry:true模拟文件的删除
url-loader and file-loader
npm i --save-dev url-loader file-loader使用方法
const imageLoaderConfigure = () => {
return {
test: /\.(png|jpe?g|gif|svg)$/i,
use: [
{
loader: 'url-loader',
options: {
outputPath: 'images',
name: '[name].[hash:7].[ext]',
limit: 10000
}
}
]
};
};
const fontsLoaderConfigure = () => {
return {
test: /\.(woff2?|eot|ttf|otf)$/i,
loader: 'url-loader',
options: {
limit: 10000,
name: '[name].[hash:7].[ext]',
outputPath: 'fonts'
}
};
};
module.exports = {
module: {
rules: [
imageLoaderConfigure(),
fontsLoaderConfigure()
],
},
}
- url-loader可以根据limit,将小于设定这个值的,变为base64,插入js文件中,但是如果大于设定值,那么就会使用file-loader,将对应的文件单独输出到outputPath设定的位置
cross-env
npm i --save-dev cross-env 使用方法
"scripts": {
"start": "cross-env NODE_ENV=development webpack-dev-server --config ./build/webpack.config.js",
"build": "cross-env NODE_ENV=production webpack --config ./build/webpack.config.js"
}
- 使用cross-env可以跨平台的设置环境变量
webpack-merge
npm i --save-dev webpack-merge使用方法
const { merge } = require("webpack-merge");
const devConfig = require("./webpack-config/webpack.dev");
const prodConfig = require("./webpack-config/webpack.prod");
const htmlConfig = require("./webpack-config/webpack.html");
const cssConfig = require("./webpack-config/webpack.css");
const outputConfig = require("./webpack-config/webpack.output");
const isProduction = process.env.NODE_ENV === "production";
module.exports = () => {
const config = isProduction ? prodConfig : devConfig;
return merge(
baseConfig,
config,
htmlConfig(entryConfigure(), isProduction),
cssConfig(isProduction),
outputConfig(isProduction)
);
};
- 将传入的配置合并,使用这个方便了环境和配置的拆分
mini-css-extract-plugin与css-loader(打包css)
npm i --save-dev css-loader style-loader less less-loader postcss-loader
npm i --save-dev mini-css-extract-plugin 使用方法
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = (production = false) => {
return {
module: {
rules: [
{
test: /\.css$/,
use: [
production ? MiniCssExtractPlugin.loader : "style-loader",
"css-loader",
"postcss-loader",
],
},
{
test: /\.less$/,
use: [
production ? MiniCssExtractPlugin.loader : "style-loader",
"css-loader",
"postcss-loader",
"less-loader",
],
},
],
},
plugins: production
? [
new MiniCssExtractPlugin({
filename: "[name]/index.[contenthash].css",
// splitChunk打包的css会用这个命名
chunkFilename: "[name].[id].[contenthash].css",
}),
]
: [],
};
};
- 这里postcss-loader(兼容浏览器,自动补全对应浏览器前缀),通过新建文件.postcssrc.js,来进行配置,并且,通过.browserslistrc指定了需要兼容的浏览器
.postcssrc.js文件
module.exports = {
plugins: [['postcss-preset-env']]
};
.browserslistrc文件
defaults
IE >= 9
Chrome >= 43
ChromeAndroid >= 43
Android >= 4.1
iOS >= 13
optimize-css-assets-webpack-plugin(压缩css)
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');
module.exports = {
mode: "production",
optimization: {
minimizer: [
new OptimizeCssAssetsPlugin({})
],
}
}
- 使用optimize-css-assets-webpack-plugin,可以将输出的css最小化处理
terser-webpack-plugin(压缩js)
const TerserPlugin = require('terser-webpack-plugin');
module.exports = {
mode: "production",
optimization: {
// 配置TerserPlugin后需要将这个设置为true
minimize: true,
minimizer: [
new OptimizeCssAssetsPlugin({}),
new TerserPlugin({
sourceMap: false,
cache: true,
parallel: true
})
],
}
}
- 因为在默认配置中,是在minimizer配置的压缩js,生产模式默认开启了,但是配置压缩css后,这里重写了,需要将压缩js也重写,这里使用terser-webpack-plugin来进行压缩css,其中cache做了缓存,在内存中该js没变化时,直接拿,parallel为开启多线程压缩.(uglifyjs-webpack-plugin也可以压缩js,但是已经不维护了,所以不做考虑)
vue-loader
npm i --save-dev vue-loader vue-template-compiler使用方法
const { VueLoaderPlugin } = require("vue-loader");
const vueLoaderConfigure = () => {
return {
test: /\.vue$/,
exclude: /node_modules/,
use: ['thread-loader', 'vue-loader']
};
};
module.exports = {
module: {
rules: [
vueLoaderConfigure()
],
},
plugins: [
new VueLoaderPlugin(),
],
}
- 需要在plugins里面引入new VueLoaderPlugin(),这个可以帮助其它loader根据规则作用到相应的模块中,如,匹配
/\.js$/规则的除了作用到普通.js文件还作用到<script>中,匹配/\.css$/会同理作用到普通的.css文件及作用到.vue文件的<style>中 - vue-style-loader与style-loader功能类似,但是如果做服务端渲染,需要vue-style-loader
splitChunks(拆分代码)
module.exports = {
mode: "production",
optimization: {
splitChunks: {
// async 表示只从异步加载得模块(动态加载import())里面进行拆分,
// initial 表示只从入口模块进行拆分,
// all包含上面二种
chunks: "all",
// 生成的chunk大小,最小值不得小于20000字节
minSize: 20000,
// 需要分割时,最少引用次数
minChunks: 1,
// 按需加载的最大并行请求书,当按需加载的文件,拆分的文件大于5了就不继续拆分出新文件了,拆分优先级是哪个包大就先拆哪个
maxAsyncRequests: 5,
// 入口点的最大并行请求数,也就是入口点的拆分文件不能大于3,当等于3就不继续拆分出新文件了,拆分优先级是哪个包大就先拆哪个
maxInitialRequests: 3,
// 生成文件名的分隔符
automaticNameDelimiter: "~",
// 缓存组,继承或者覆盖上面的配置,默认还有vendors与default二个组,优先级分别为-10,-20,优先级相同的有限匹配先定义的
cacheGroups: {
default: {
// 符合此配置生成的chunk文件名为manifest
name: "manifest",
chunks: "initial",
minChunks: 2,
// 数值越大,优先级越高
priority: 10,
// 多次引入的不会重复打包,直接复用
reuseExistingChunk: true,
},
defaultVendor: {
name: "vendor",
// 限定作用范围
test: /node_modules/,
chunks: "initial",
priority: 20,
reuseExistingChunk: true,
},
},
},
},
};
- splitChunks将指定规则所匹配到的代码,提取到新的文件中(除了js同样适用于css)
- cacheGroups组里面的元素,可以覆盖和继承外面的配置
- 指定的name值会根据output中配置的chunkFilename,去设定文件名
- 第二个缓存组里面的配置是将满足公共条件且应用到的/node_modules/里面的同步执行的代码提取出来,这个优先级需要大于-20,因为默认有个vendors组也是匹配node_modules里面的
- 第一个缓存组里面是匹配所有代码,满足公共条件,被引用2次以上的同步执行的代码提取出来,reuseExistingChunk是配置了当某一模块被提取出来了,就重用,不会重复提取
devServer
const webpack = require('webpack');
module.exports = {
mode: 'development',
devServer: {
// 为所有服务启用gzip压缩
compress: true,
// 启用服务地址
host: '127.0.0.1',
// 启用端口
port: '8090',
// 开启HMR
hot: true,
// 当404时,展示index.html
historyApiFallback: true,
// 控制台打印内容,关闭
clientLogLevel: 'silent',
// 当设置为真时,此选项绕过主机检查, 解决代理问题
disableHostCheck: true,
},
plugins: [new webpack.HotModuleReplacementPlugin({})]
}
- clientLogLevel控制浏览器打印的信息,silent选项关闭打印
- 需要开启热更新需要将hot: true,并且设置new webpack.HotModuleReplacementPlugin({}),之后,在代码中需要配合类似下面代码,才能热更新,类似Vue就在框架里面做了.
if (module.hot) {
module.hot.accept('src/lib/util', function () {
console.log('监听文件改变了!!!');
});
}
stats
const statsConfigure = () => {
return {
colors: true, // 输出不同颜色
modules: false, // 不展示已构建模块的详细信息
children: false, // 不展示已构建模块的子详细信息
chunks: false, // 不展示生成的chunk详细信息
timings: true, // 展示时间信息
chunkModules: false, // 不展示已构建模块添加进生成chunk里面的信息
entrypoints: false // 不展示入口信息
};
};
- 输出一个简洁的构建信息
eslint + husky + lint-staged + prettier (统一代码规范)
npm i --save-dev husky 使用方法
npm i --save-dev lint-staged 使用方法
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"lint-staged": {
"*.{js,vue}": [
"eslint",
"git add"
]
}
- husky可以调用git钩子,例如上面pre-commit,在commit的时候调用lint-staged
- lint-staged会将目前在暂存区中的代码进行操作,如上面,先eslint检查,没问题就保存提交,有问题就终止然后报错