(基础篇)手把手教你从零配置webpack4(含性能优化)

2,203 阅读17分钟

前言

本篇是基于webpack4搭建vue脚手架常用的知识点。并非从0开始搭建,因为网上此类教程非常多,仅将作者这些年搭建脚手架配置的知识点一一罗列,排名不分先后,可作为采坑文档查询,欢迎评论补充。

一、如何配置启动服务以及实现开发环境下热更新

修改开发目录下任意文件后,无需手动刷新而页面直接渲染展现出来,是对开发者最大的尊重。原理没啥好说的,直接上配置:

const webpack = require('webpack');
module.exports = {
	//其他忽略
	plugins:[
		new webpack.HotModuleReplacementPlugin()
	],
	devServer:{
		historyApiFallback:{
			index:'/index.html'
		},
		progress:true,					//进度条
		inline:true,					//打包后加入一个websocket客户端
	    hot:true,						//热加载
	    contentBase: path.resolve(__dirname,'../src'),	//开发服务运行时的文件根目录
	    host: 'localhost',				//主机地址
	    port: 5000,						//端口号
	}
	//其他忽略
}

二、引入html模板

html-webpack-plugin是创建html入口文件的webpack插件。简易版配置方法如下:

const HtmlWebpackPlugin 	= require('html-webpack-plugin');
module.exports = {
	//其他忽略
	plugins:[
		//...
		new HtmlWebpackPlugin({
			title:'Home',
			filename:'index.html',
			template:resolve('../public/index.html')
		})
		//...
	]
	//其他忽略
}

html-webpack-plugin插件也可以配置多页面应用,感兴趣的同学可查阅更多资料。更多options参数配置参考如下:

title : 用于生成的HTML文件的标题。
filename : 用于生成的HTML文件的名称,默认是index.html。你可以在这里指定子目录(例如:assets/admin.html)
template : 模板的路径。支持加载器,例如 html!./index.html。
inject :true | ‘head’ | ‘body’ | false 。把所有产出文件注入到给定的 template 或templateContent。当传入 true或者 ‘body’时所有javascript资源将被放置在body元素的底部,“head”则会放在head元素内。
favicon : 给定的图标路径,可将其添加到输出html中。
minify : {…} | false 。传一个html-minifier 配置object来压缩输出。
hash : true | false。如果是true,会给所有包含的script和css添加一个唯一的webpack编译hash值。这对于缓存清除非常有用。
cache : true | false 。如果传入true(默认),只有在文件变化时才 发送(emit)文件。
showErrors : true | false 。如果传入true(默认),错误信息将写入html页面。
chunks : 只允许你添加chunks 。(例如:只有单元测试块 )
chunksSortMode : 在chunk被插入到html之前,你可以控制它们的排序。允许的值 ‘none’ | ‘auto’ | ‘dependency’ | {function} 默认为‘auto’.
excludeChunks : 允许你跳过一些chunks(例如,不要单元测试的 chunk).
xhtml : 用于生成的HTML文件的标题。
title : true | false。如果是true,把link标签渲染为自闭合标签,XHTML要这么干的。默认false

三、清除dist文件夹

现在通常单页面应用为了防止缓存,每次build打包后都会改变变动的文件名称,那么这种情况下每次打包后dist文件夹下都会产生多个相同功能的相似文件,久而久之文件会越来越多。那么这个时候我们就需要在每次打包前先清空目录,这里推荐clean-webpack-plugin插件

const {CleanWebpackPlugin} = require('clean-webpack-plugin');
module.exports = {
	//其他忽略
	plugins:[
		new CleanWebpackPlugin()
	]
	//其他忽略
}

每次在打包的时候,都会先清空dist文件夹。那么这个时候就会有疑问了:为什么在new CleanWebpackPlugin的时候,没有配置呢?它怎么知道需要清空的是dist文件夹呢?我项目build之后的文件目录如果是build文件夹呢?其实也是有配置的,文档地址,只是一般用不到,我们的目的就是清除打包后的目录而已。clean-webpack-plugin会在我们每次build的时候动态读取输出目录,清除里面的文件。

四、CDN分包加载方式

一般常见的类库都会发布到CDN上,我们在webpack中打包的时候其实无需将公共类库打包到自己项目中,因为这样不仅会增加文件体积,影响客户端到服务器这条网络线路的加载速度,还影响webpack打包效率。以CDN方式加载资源需要使用到add-asset-html-cdn-webpack-plugin插件,我们以vue为例:

const AddAssetHtmlCdnPlugin = require("add-asset-html-cdn-webpack-plugin");
module.exports = {
	//其他忽略
	plugins:[
		new AddAssetHtmlCdnPlugin(true, {
      		'vue': "https://lib.baomitu.com/vue/2.6.12/vue.min.js",
    	}),
	],
	externals: {
    	'vue': 'Vue'
  	},
	//其他忽略
}

按照以上方法配置后,我们在项目中执行如下引入的时候,webpack将会忽略vue的引入,转而从CDN引入。

import Vue from 'vue';

注意:引入的CDN库中一定要在全局范围内暴露Vue,否则build后会因找不到Vue而报错。

五、vue-router 使用history模式报错Cannot GET /xxx

报错原因:找不到资源。在搭建本地开发服务的时候我们通常使用webpack-dev-server搭建服务,启动服务后访问首页正常,例如访问http://localhost:5000/页面展示正常,点击路由跳转到页面demohttp://localhost:5000/demo访问也正常,这个时候若刷新页面恐怕会提示Cannot GET /demo。为什么会这样呢?因为服务器实际上并不存在demo目录,我们需要将不存在的目录重定向到index.html即可,关键配置是这样的:

module.exports = {
	//其他忽略
	devServer:{
		//...
		historyApiFallback:{
			index:'/index.html'
		}
		//...
	}
	//其他忽略
}

六、拷贝文件夹内容至打包目录

webpack编译打包时代难免会存在一些需要直接引入而无需打包的文件,比如icon Font。这个时候只需要在build的时候将指定文件内容copy一份到dist目录即可。copy-webpack-plugin插件就是用来解决这一问题的,不仅可以拷贝内容,还可以动态转换内容。
如下配置的意思是:将public文件夹中的内容拷贝到dist文件夹中,但是test文件夹下以png为后缀的图片不拷贝,也不拷贝以html为后缀的内容

const CopyWebpackPlugin     = require("copy-webpack-plugin");
module.exports = {
	//其他忽略
	plugins:[
		new CopyWebpackPlugin(
			[
				{
					from:'./public',
					to:'./',
				}
			],
			{
				ignore:['test/*.png','*.html']
			}
		)
	]
	//其他忽略
}

七、设置Loader搜索范围

webpack打包,主要依赖各种Loader加载器将代码转换为字符串生成AST,然后对AST继续进行转变最后再生成新的代码,项目越大,需要转换的代码就越多,所以效率就越低。所以我们在项目中要指定哪些位置需要转换,哪些位置可以忽略的,很显然node_modules文件夹下依赖的库非常多,且都是经过编译过的,没必要再去处理一遍,只需要将关心的重点放到业务代码src文件夹中即可:只处理src文件夹,忽略node_modules,我们以Babel为例

module.exports = {
	//其他忽略
	module:{
		rules:[
			//...
			{
				test: /\.js$/,
				include: path.resolve('src'),		//只处理src文件夹
				exclude: /node_modules/,			//忽略node_modules文件夹
				use: ['babel-loader']
			}
			//...
		]
	}
	//其他忽略
}

八、支持SASS,查询原样式具体位置sourceMap

注意:此配置常见的问题是相关loader版本问题。若确保配置无误,而报错问题又无法解决的话,就要检查一下style-loadercss-loadersass-loader的版本是否过高,适当降低版本即可。

module.exports = {
	//其他忽略
	module:{
		rules:[
			//...
			{
				test:/\.scss$/,
				include:resolve('../src'),
				exclude:/node_modules/,
				use:[
					{ loader:'style-loader',},
					{ loader:'css-loader', options:{ sourceMap:true } },
					{ loader:'sass-loader', options:{ sourceMap:true } },
				]
			}
			//...
		]
	}
	//其他忽略
}

注:sourceMap主要用于调试代码,出于性能考虑,生产环境下sourceMap要设置为false

九、定位js打印具体位置sourceMap

有的时候页面报错、打印警告,或者console.log的时候,我们需要知道具体源代码是哪个文件以及具体位置,这个时候就需要开启jssourceMap定位

module.exports = {
	//其他忽略
	devtool: 'eval-source-map'
	//其他忽略
}

注:sourceMap主要用于调试代码,出于性能考虑,生产环境下devtool要设置为none

十、图片pic、字体fonts引入配置

很简单,没啥好说的,直接上配置。低于10KB的图片直接打包成base64,超过10KB的按照路径的方式引入图片。

module.exports = {
	module:{
		rules:[
			{
            	test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
            	use:[
            		{
            			loader: 'url-loader',
            			options:{
            				esModule:false,
            				limit:10*1024,
            				name:'static/images/[name]-[hash:15].[ext]'
            			}
            		}
            	]
          	},
          	{
                test: /\.(eot|woff|woff2?|ttf|svg)$/,
                use: [
                	{
                    	loader: "url-loader",
                    	options: {
                      		name: "static/fonts/[name]-[hash:15].[ext]",
                      		limit: 5*1024,
                    	}
                  	}
                ]
            }
		]
	}
}

十一、webpack4的js压缩以及异步加载方法分包策略

webpack4中对js文件的压缩配置非常方便,生产环境下,也就是mode值为production的时候,package.json中执行压缩的时候webpack -p即可实现文件压缩。

1.require.ensure方式

比如这种场景:首页index.js中引入了testA.js以及testB.js,而恰好这两个js的功能对首屏加载无影响的,也就是说需要做某些操作的时候才会使用到,而恰好这俩体积又比较大的时候,就比较适合异步加载了,毕竟可以减少首屏加载文件体积。废话不多说,直接上代码

{
	methods:{
		alertA(){
			require.ensure([],() => {
				//异步加载
				let testA = require('./testA.js');
				console.log(testA)
			})
		},
		alertB(){
			require.ensure([],() => {
				//异步加载
				let testB = require('./testB.js');
				console.log(testB)
			})
		},
	}
}

采用这种写法打包后就会发现多了两个文件。原因是webpacktestA.jstestB.js分别独立打包,只有在需要的时候才会用到,这么做显然体验更好。

2.import(xxx).then()方式

import()webpack4才启用的分包切割方法,语法相对简单,只接受一个包的引用地址作为参数,使用了Promise式回调,在加载成功后回调执行逻辑。

{
	methods:{
		alertA(){
			//异步加载
			import('./testA.js').then(res => {
				console.log(res)
			})
		},
		alertB(){
			//异步加载
			import('./testB.js').then(res => {
				console.log(res)
			})
		},
	}
}

那么问题来了:该如何控制这些异步加载js打包后的位置以及命名方式?其实无论require.ensure还是import(xxx)方式分包,最终都是放在chunk存储的目录下。例如将异步加载的js存放在static/js/目录下:

output:{
	chunkFilename:'static/js/[name].[chunkhash:10].chunk.min.js'
}

3.vue-router分包方式

其实这并不算一种新的分包方式,顶多算以上两种方式的经典应用,也是常见应用案例之一。主要修改vue-router路由配置component的引入方式而已。两种方式分别实例如下:

export default new Router({
    mode: 'history',
    routes: [
        {
            path: '/',
            name: 'Index',
            component: () => import('@/pages/index/index.vue')
        }, 
        {
            path: '/list',
            name: 'List',
            component: resolve => require(['@/pages/list/list.vue'],resolve)
        },
    ]
})

4.webpack4分包策略

以上两种异步分包方式常用于业务类,方便随时分包处理,且一般单个文件较小。但除此之外项目打包引入的公共库,例如vuevue-routerlodashechart等往往会被打包进一个公共js中,这样就会出现文件过大,首屏加载时间较长,导致白屏时间也过长的情况。这个时候就十分需要将这些依赖库分包加载。首先CDN分包加载的方式就是一个很好的办法,上文已讲过,此处忽略。其次就是可以指定哪些库合到一起,真正实现随意分包的个性化优化方式。

4.1 CommonsChunkPlugin分包策略

在webpack4.x版本之前采用的是这种方式,由于本篇是基于webpack4配置,所以这种方式简单略过

module.exports = {
	//其他忽略
	plugins: [
		new webpack.optimize.CommonsChunkPlugin({
	    	name: 'vendor',
	    	minChunks: function (module, count) {
	      		return (
	        		module.resource && /\.js$/.test(module.resource) && module.resource.indexOf(path.join(__dirname, './node_modules')) === 0
	      		)
	    	},
	  	}),
	  	new webpack.optimize.CommonsChunkPlugin({
	    	name: 'common',
	    	chunks: 'initial',
	    	minChunks: 2,
	  	}),
	]
	//其他忽略
}

4.2 splitChunks分包策略

webpack4移除了CommonsChunkPlugin分包方式,改用splitChunksPlugin分包策略。
此内容算是webpack4分包核心内容了,接下来会用一整篇文章分析,此处暂时略过,敬请期待。

十二、sass引入全局注册变量文件

sassless这些预处理器极大的方便了我们书写css变量,我们可以定义一个文件,将变量定义在里面,其他文件引入。以sass为例,定义通用样式文件mixins.scss,然后每个sass文件通过@import引入即可。如果有一天通用样式名称变成了base.scss,然后我们项目里有几十个页面要去一一手动修改,痛苦!!!那么是否可以全局引入sass文件呢?可以,尝试一下sass-resources-loader插件。将其配置在use最后面即可,毕竟loader解析是从后往前依次工作的,基础依赖毕竟需要跑在第一位

module.exports = {
	module:{
		rules:[
			{
				test:/\.scss$/,
				include:resolve('../src'),
				exclude:/node_modules/,
				use:[
					//其他暂时忽略
					{
						loader: 'sass-resources-loader',
		                options: {
		                	sourceMap:process.env.NODE_ENV==='production'?false:true,
		                    resources: [resolve('../src/static/css/mixins.scss')]
		                }
					}
				]
			}
		]
	}
}

我们注意到resources是一个数组(也可以是一个字符串,就是直接引入的通用样式路径),之所以示例用数组,意思就是可以配置多个通用样式。
以上是sass示例,less配置也是同样原理,感兴趣的同学可以尝试style-resources-loader

十三、css分包、压缩、去除注释

注意:

  • 1、此方法只能用于生产环境
  • 2、注意引入插件版本号问题

用到的插件mini-css-extract-pluginoptimize-css-assets-webpack-plugincssnano,配置方式如下

const MiniCssExtractPlugin  = require('mini-css-extract-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
module.exports = {
	module:{
		rules:[
			{
				test:/\.scss$/,
				include:resolve('../src'),
				exclude:/node_modules/,
				use:[
					MiniCssExtractPlugin.loader
				]
			},
			{
				test:/\.less$/,
				include:resolve('../src'),
				exclude:/node_modules/,
				use:[
					MiniCssExtractPlugin.loader
				]
			},
			{
				test:/\.css$/,
				include: resolve('../src'),
				exclude: /node_modules/,
				use:[
					MiniCssExtractPlugin.loader
				]
			}
		]
	},
	plugins:[
		new OptimizeCSSAssetsPlugin({
	    	assetNameRegExp:/\.css$/g,
	    	cssProcessor:require("cssnano"),		//引入cssnano配置压缩选项
	       	cssProcessorPluginOptions:{
	        	preset:['default',{discardComments:{removeAll:true}}]
	    	},
	       	canPrint:true,		//是否将插件信息打印到控制台
	    }),
	    new MiniCssExtractPlugin({
        	filename: "static/css/[name].[chunkhash:10].css",
    	}),
	]
}

版本号详见最后源码彩蛋。

十四、解决不能解析async … await等高级语法问题

当你的Chrome浏览器报这种错误的时候,那么一定代表你的业务代码中使用了一些高级版本的js导致的,例如这种错误

ReferenceError: regeneratorRuntime is not defined

那么这个时候你就需要babel-polyfill的帮忙,安装并引入再重启服务即可

npm install babel-polyfill -save-dev

//webpack入口文件,例如`main.js`中引入即可
import "babel-polyfill"

十五、开启 Tree Shaking

Tree-shaking,字面意思翻译过来就是:摇晃树。顾名思义就是秋天的时候,当我们摇晃小树的时候,没用的树叶就会掉到地上,就像我们代码中util.js中会写几十个方法,在打包的时候没必要将用不到的方法也打包进来,除去多余代码的过程译做Tree-shakingwebpack4以前时代就不讲了,webpack4mode模式设置为production的时候,默认就会开启Tree-shaking。例如: main.js

import { A } from './util';
console.log(A())

util.js

export const A  = () => 'aaa';
export const B  = () => 'bbb';

这个时候打包的时候以下代码就不会被打包进去

export const B  = () => 'bbb';

Tree-Shaking原理:主要得益于ES6 Module引入了静态分析,它的模块是在编译时输出。静态分析流可以判断哪些模块或者变量未被使用或者输出。

十六、开启 Scope Hoisting

另一个在webpack4中当mode设置为production的时候也会默认开启的是Scope Hoisting。就是可以让webpack打包出来的js体积更小,运行更快的一种方式。最初webpack在打包的时候,会在打包后的模块外部包裹一层函数,import引入会被转换成require,进而产生大量的作用域,模块越多内存占用的就会越大。这个时候若开启了Scope Hoistingwebpack会动态分析模块之间的依赖关系,将一些模块合并到一个函数中去,从而减少被包裹的数量,同时也会重命名一些变量防止冲突。

十七、安装打包分析工具

在没分析工具之前,要想分析打包后的文件大小以及文件内引入内容,往往都会人工去查看dist目录,看看哪些文件较大,以及可能引入的哪些js可以分离出去。费时又费力,且准确性还不高。
这个时候推荐webpack-bundle-analyzer插件,可以帮助分析网站质量以及性能。使用方式较简单

const BundleAnalyzerPlugin  = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
module.exports = {
	//其他忽略
	plugins:[
		new BundleAnalyzerPlugin({
            generateStatsFile: true, // 是否生成stats.json文件
        })
	]
	//其他忽略
}

这里是最简洁的配置方式,更多配置可查看webpack-bundle-analyzer文档。
执行打包后默认会打开另外一个服务,页面大概样子如下: webpack-bundle-analyzer 分析一目了然,哪些文件较大,以及文件内都打包了依赖了哪些库,或者打包了不改引入的库,都可以看的清清楚楚。接着就是针对具体项目具体分析了,至于如何分包,可查看上面webpack4分包策略,或者CDN分包加载方式。不同项目分包细节不同,此处只如授人以渔

十八、打包速度测量分析

一些较大的项目,虽然我们做了大量打包优化,但总归还是会占用一定时间,至于哪个地方占用时间较多,单凭经验很难分析出来,这个时候我们就需要一款插件能够分析打包速度 ———— speed-measure-webpack-plugin
使用方式较简单:

const SpeedMeasureWebpackPlugin = require("speed-measure-webpack-plugin");
const seepM = new SpeedMeasureWebpackPlugin();
module.exports = seepM.wrap({
	entry:'',
	output:'',
	module:{},
	plugins:[]
})

使用很简单:引入并执行打包速度插件,并将配置内容包在seemP.wrap中即可。

十九、DllPlugin提前打包特定类库成动态链接

DllPlugin可以将特定的类库提前打包成动态链接库,这适用于那些不常更新的类库或者函数模块。因为每次更新完业务逻辑代码,打包的时候都要顺带上他们,十分影响打包速度。如果能将这些类库提前打包到一边,在用到的时候拿过来用即可。只有当代码库更新的时候再手动去更新即可。
提前在package.json中配置文件生成依赖:

{
	"scripts":{
		"dll": "webpack --config ./config/webpack.dll.js --mode production"
	}
}

对应的webpack.dll.js配置:

const path = require("path");
const DllPlugin = require("webpack/lib/DllPlugin");
module.exports = {
    // 想统一打包的类库
    entry: ["vue-router","bignumber.js","axios","vant","js-md5"],
    output: {
        filename: "[name].dll.js", //输出的动态链接库的文件名称,[name] 代表当前动态链接库的名称
        path: path.resolve(__dirname, "dll"), // 输出的文件都放到 dll 目录下
        library: "_dll_[name]", //存放动态链接库的全局变量名称,例如对应 vant 来说就是 _dll_vant
    },
    plugins: [
        new DllPlugin({
            // 动态链接库的全局变量名称,需要和 output.library 中保持一致
            // 该字段的值也就是输出的 manifest.json 文件 中 name 字段的值
            // 例如 vant.manifest.json 中就有 "name": "_dll_vant"
            name: "_dll_[name]",
            // 描述动态链接库的 manifest.json 文件输出时的文件名称
            path: path.join(__dirname, "dll", "[name].manifest.json"),
        }),
    ],
};

最后将依赖文件引入到项目中即可:

const DllReferencePlugin    = require("webpack/lib/DllReferencePlugin");
module.exports = {
	//其他忽略
	plugins:[
		//...
		new DllReferencePlugin({
      		// manifest 就是之前打包出来的 json 文件
      		manifest: path.join(__dirname, "../dll", "main.manifest.json"),
    	}),
    	//...
	]
	//其他忽略
}

后期备注:DllPlugin已成鸡肋,不再建议使用,推荐hard-source-webpack-plugin插件。

二十、noParse

为了提高打包速度,生产环境可以设置noParse告诉webpack过滤指定文件,不做解析。那么什么条件的库可以noParse呢?就是无需特殊处理,直接可以运行在浏览器端的,或者已经被编译过的插件或框架,可以直接使用的。例如一般jquerylodash都可以直接过滤掉

module.exports = {
	//其他忽略
        module:{
	    noParse:/jquery|lodash/,
            //或者支持函数类型(例如忽略所有lib文件夹下的解析)
            noParse:function(fullPath){
		return /lib/.test(fullPath)
            }
        }
	//其他忽略
}

二十一、IgnorePlugin

IgnorePlugin 防止在importrequire调用时,忽略符合正则表达式条件的内容。例如moment库中支持多种语言,但是我们只用到中文语言包,其他的就可以忽略掉,这样可以减小包的体积。

module.exports = {
	//其他忽略
	plugins: [	
		new webpack.IgnorePlugin(/^\.\/locale/, /moment$/)
	],
	//其他忽略
};

二十二、其他小配置优化点

  • 1、可以使用别名,手动指定映射位置,减少webpack搜索范围;
  • 2、在引入组件或者js工具库的时候,我们往往习惯不携带后缀,可以设置文件搜索后缀列表,将常用的放在前面。
module.exports = {
	//其他忽略
	resolve:{
		extensions:['.js','.vue','.scss','.css','.json'],
		alias:{
			'vue': 'vue/dist/vue.esm.js',
			'@':path.resolve(__dirname,'../src')
		}
	}
	//其他忽略
}

二十三、缓存loader处理结果

对一些性能开销比较大的loader最后,也就是use数组的第一位增加cache-loader,可以缓存处理结果,这样下次在打包的时候只要文件内容没发生变动,就可以使用上次缓存的结果,大大节省时间。

当然,babel-loader编译的结果也是可以缓存下来的,也可以大幅提升打包时间。

module.exports = {
	//其他忽略
	module:{
		rules:[
			{
				test: /\.js$/,
				use: ['cache-loader','babel-loader?cacheDirectory=true']
			},
		]
	}
	//其他忽略
}

注意:对于一些编译相对轻松的内容,不推荐使用cache-loader缓存结果。因为编译本身耗费时间较少,没必要浪费保存和读取缓存的时间。

二十四、开启多线程打包处理

由于webpack在打包的过程是单线程的,每次打包有都较多的loader任务排队,当某个编译占用较长时间时,其他的只能排队等候,这样导致打包时间更长。如果多个loader能够齐头并进,势必会大大缩短打包过程。

二十五、webpack-dashboard仪表盘

在本地开发环境下,控制台打印的信息都是以列表的形式展示,往往显得不够直观,也不美观。webpack-dashboard翻译过来大意就是“仪表盘”,可以让控制台分多个区域显示内容。左上角为webpack编译日志,日志右侧是状态、编译时间、编译进度条,下面是引入模块以及文件大小、报错信息等,截图信息如下: webpack-dashboard.png 使用方式:

	//第一步:安装
	npm install webpack-dashboard -D
	//第二步:引入并使用
	const DashboardPlugin = require('webpack-dashboard/plugin');
	{
		//暂时忽略
		plugins:[
			new DashboardPlugin()
		]
		//暂时忽略
	}
	//第三步:修改package.json中启动方式,在之前启动方式前追加  “webpack-dashboard --”
	{
		"scripts":{
			"serve":"webpack-dashboard -- webpack-dev-server"
		}
	}

总结:常见棘手问题汇总

  • 1、版本问题。webpack配置项目中往往会引入大量第三方插件,大升级后的版本往往会修改配置API,在确认配置API问题无误后可以试着不断卸载相应插件,不断降低版本重试。
  • 2、路径问题导致引入失败。读取引入文件的时候建议用path.resolve(__dirname,"../src")模式,避免相对路径导致引入失败。
  • 3、js、css压缩仅适用于生产环境。
  • 4、开发环境下加载css的时候一定要用style-loader,切忌压缩或link方式引入,否则会导致热更新失败,且不利于开发体验。
  • 5、css清除多余样式,一个非常好的想法,但作者目前还没搞清楚purifycss-webpack插件清除多余css原理。比如动态添加的css样式,是如何做到不误杀的,目前已知是会被误杀的,懂的欢迎留言讨论。

源码配置地址:[github源码](https://github.com/leishiwo31/vue-demo)