first thing fitrst 博主声明:绝对不当标题党
有人看最好不过的背景:

  十月初对公司产品的前端构建做了一些优化,但还遗留了不少问题(可了解我的前一篇博文:一次webpack小规模优化经历 https://www.cnblogs.com/byur/p/13977657.html),这里姑且列了一个表出来记录当前这个版本的不足:

  1.热重载过慢:单文件改动,热重载的十次平均响应时间约为17s,严重影响开发体验;

  2.某些bundle体积过大,导致单个资源请求耗时过多,浏览器加载速度收到影响;

  3.没有liint机制去控制编码过程中的语法规范,也没做代码保存的自动格式化,代码质量低,组员编码风格迥异、交接成本高。

  4.打包体积与打包速度仍有优化空间。

  综合考虑以上问题后,个人判断webpack1的性能不足以为前端项目的构建流程提供更好的支持,遂决定把webpack升级到更高版本,用新特性与更强的性能,改善构建体验,造福运维与测试同事hhhh。

这次基本没图的正文:

  本系列博文将演示如何将webpackv1.x(1.13.2)升级到v4.x(4.44.2),选择这个小版本的原因是因为它是webpack4的最新一个小版本(2020.11),webpack4从发布测试版本到现在为止已经有两年多了,两年里的迭代和bug修复,足够让这个大版本的功能变得完善和稳定到让人信任的程度。至于升级的手法,我认为在原来配置的基础上做修改逐步升级,极有可能会被原来的写法误导,导致浪费时间,所以这次升级过程中我换了一种思路,具体的做法是做备份之后删除原来的配置文件,从零开始进行升级,因此本文兴许也可以当作一个用webpack构建项目的入门教程。

  package.json里有个devDependencies,记录了项目在开发环境下需要的依赖,在做好文件备份后,我将node_modules删除,将devDependencies的列表清空;然后npm i。

  然后我开始实现一个最简化的版本,我装上了4.x版本最新的webpack:

npm i webpack@4.44.2 -D

  webpack4.x版本需要命令行工具才能运行,所以我们还需要去下载webpack-cli,我就随便选了一个不算新也不算旧的版本:

npm i webpack-cli@3.3.9 -D

  然后开始写配置文件,首先写一个基本版的测试一下新版本webpack的可行性:

  先创建一个简单的入口文件test.js供打包用:

1 import {cloneDeep} from "lodash"
2 const obj = {color:'red'}
3 const copy = cloneDeep(obj)

  在项目根目录下创建webpack.config.js文件:

1 const path = require('path');
2 module.exports = {
3 entry: "./src/test.js",
4 output: {
5 path: path.resolve(__dirname,"dist"),
6 filename: 'testbundle.js',
7 }
8 }

  webpack启动时,如果未指定运行的文件,就会自动读取根目录下的webpack.config.js中的配置,现在修改package.json的scripts中的build命令:

  "scripts": {
"build": "webpack"
},

  运行npm run build,webpack便会按配置进行打包,然后你会看到你的dist目录中多出一个名为testbundle.js的文件。

  同时,按照我这个配置,会在控制台到看到一个警告:

WARNING in configuration
The 'mode' option has not been set, webpack will fallback to 'production' for this value. Set 'mode' option to 'development' or 'production' to enable defaults for each environment.
You can also set it to 'none' to disable any default behavior. Learn more: https://webpack.js.org/configuration/mode/

  这个警告表示,当前传给webpack的配置中没有设置“mode”属性,对此webpack将视作mode: "production"来处理,这里需要提到webpack4配置文件中的mode属性,webpack4内置了一些比较通用的插件配置,省去了开发者为配置webpack而消耗的时间精力,使用mode属性就可以快捷配置这两套插件,mode:"development"跟mode:"production"分别就对开发环境与生产环境两种场景做了优化,比如持久化缓存、代码压缩等,如果你不想使用这两种预设的任意一种,可以将mode的值设为"none"。至于其他细节,感兴趣的朋友可以从文档获取更多信息:https://www.webpackjs.com/concepts/mode/

  刚才演示的配置文件,灵活性与性能都远远达不到真实工作场景的需求,只能称作玩具,所以接下来你将接触更具体也更接近实际场景的配置。

  现在,我们把配置文件改一改,常规的思路是将开发环境的配置跟产品环境的配置分离成两个文件,一般命名为webpack.dev.conf.js和webpack.prod.conf.js,因为这两个场景下的配置都有部分共同之处,所以又可以抽出一个公共的配置文件webpack.base.conf.js,目前我们先不去考虑生产环境与开发环境下的差异,先创建一个基本配置webpack.base.js,让webpack能够正确地解析一个vue文件,顺利完成打包。

  首先解析.vue文件,需要安装vue-loader以及与vue同版本号的vue-template-loader,这里需要注意的是vue-loader版本如果在15及以上,需要额外从vue-loader的目录里引入VueLoaderPlugin,VueLoaderPlugin将使用你在rules中定义的其他规则来检查和处理.vue文件中符合规则的语句块

const VueLoaderPlugin = require("vue-loader/lib/plugin");
...
...
...
module: {
  rules: [
    {
      test: "/\.vue$/"
      loader: "vue-loader"
    }
  ]
}, plugins: [
new VueLoaderPlugin()
]

  

  如果这时候你已经看到本文的更下面并且写好了build文件,或者是在webpack.config.js的基础之上改写配置文件,此时执行打包命令你将会发现控制台输出了很多错误,例如:

  

  满屏的红字有些吓人,但仔细看看就会发现其实并不是什么大不了的问题,截图上有一段样式代码,并且报错提示你可能需要其他loader去处理vue-loader的解析结果,所以为了解决截图上的问题能,使webpack能够顺利对样式代码进行处理,你需要添加相应的loader,添加什么由你的项目具体使用情况决定:

npm i css-loader style-loader url-loader file-loader less less-loader sass node-sass stylus stylus-loader -D

  css-loader用于解析css代码,style-loader则生成style标签将css挂载在到页面结构中,file-loader读取静态资源的引用路径,在输出目录中生成符合规则的文件,供编译后的代码使用,url-loader在file-loader的基础之上,将体积小于指定数值的文件转码成base64字符串,可通过这种方式减少资源请求数。其他文件其他loader以及相关依赖不再赘述。

  

  css相关loader的载入我沿用了项目之前的写法(反正也是从别的地方抄来的),稍微加了些改动:

exports.cssLoaders = function () {

  // style-loader改为使用vue-style-loader,除了具备与style-loader一样的功能之外,还实现了不需要页面刷新的样式层面的热重载(来自vue-laoder官网描述)

  const vueStyleLoader = {
loader: "vue-style-loader"
}
const cssLoader = {
loader: "css-loader",
   // 如果你使用的是vue-style-loader并且css-loader的版本在v4.0.0及以上,这个属性需要加上,具体原因请看https://www.cnblogs.com/byur/p/14194672.html
   
   options: {
   esModule: false
   }
  }
// 当在一条规则中应用多个loader时,loader的执行顺序从右至左,所以预处理语言相关的loader摆右边 
 // 如果generateLoaders没有接收到参数,将以返回基础的loader配置:使用css-loader与vue-style-loader
  function generateLoaders (loader) {
const outputLoaders = [vueStyleLoader,cssLoader]
if (loader) {
const targetloader = {loader:loader+"-loader"}
outputLoaders.push(targetloader)
}
return outputLoaders
}

return {
css: generateLoaders(),
less: generateLoaders("less"),
sass: generateLoaders("sass"),
scss: generateLoaders("sass"),
stylus: generateLoaders("stylus"),
styl: generateLoaders("stylus")
}
}
exports.styleLoaders = function () {
var output = []
var loaders = exports.cssLoaders() for (let extension in loaders) {
var loader = loaders[extension]
console.log(loader)
output.push({
test: new RegExp('\\.' + extension + '$'),
use: loader
})
}
return output
}

  

  这个丐版的styleLoaders输出了一个保存了处理样式文件规则的数组,你可以使用拓展运算符将这些规则挂载到rules中。所以接下来我们创建一个build.js,用这个文件调起webpack的api进行打包。我比较倾向于这种写法,用命令行调用node执行一个build文件,在这个文件中运行webpack,这样写在处理不同打包配置的场景时要稍微方便一些,比如有的公司就分sit、uat、prod(生产)等好几套环境,会对应不同的全局配置(如接口的的baseurl、请求加解密、局部打包等等),这种情况下可以通过process.argv来获取命令行参数,细化配置;对于我来说另外一个好处是方便加old_space参数给内存扩容,这样能避免一些稍大的项目运行过程中出现内存不够导致编译失败的问题(64位windows给node分配的内存大概是1.4G)

process.env.NODE_ENV = 'production'

var webpack = require('webpack')
var webpackConfig = require('./webpack.prod.conf') webpack(webpackConfig, function (err, stats) {
// spinner.stop()
if (err) throw err
process.stdout.write(stats.toString({
colors: true,
modules: false,
children: false,
chunks: false,
chunkModules: false
}) + '\n')
})

丐版build.js

  

  把build命令改写为:

  

"build": "node --max_old_space_size=2077 build/build.js"

  现在执行npm run build,看看会发生什么:

  

  过程没报错,一个基础的打包流程,到现在其实就走完了,这个包实际上也不能用,但我认为基础篇的意义在于引导读者顺利完成第一步,在这个前提之上进行功能的丰富,这样的话无论是操作失误回退代码或者是对优化方向的梳理都有一定的积极意义。

  基础篇到这里就该结束了,在进阶篇,我将展示一个完成度更高的版本。

  

  附:

var path = require('path')
var config = require('../config')
var utils = require('./utils')
const VueLoaderPlugin = require('vue-loader/lib/plugin') module.exports = {
mode: "none",
entry: "./src/module/indexApp/index.js",
output: {
path: config.build.assetsRoot,
publicPath: config.build.assetsPublicPath,
filename: utils.assetsPath('js/[name][hash].js'),
chunkFilename: utils.assetsPath('js/[id][chunkhash].js')
},
resolve: {
extensions: ["*",'.js', '.vue'],
alias: {
'vue$': 'vue/dist/vue',
'src': path.resolve(__dirname, '../src'),
'common': path.resolve(__dirname, '../src/common'),
'components': path.resolve(__dirname, '../src/components'),
'components2': path.resolve(__dirname, '../src/components2'),
'module': path.resolve(__dirname, '../src/module'),
'config': path.resolve(__dirname, '../src/config'),
'library': path.resolve(__dirname, '../src/library'),
'jsplumb': path.resolve(__dirname, '../src/library/jsplumb.js'),
'echarts-wordcloud': path.resolve(__dirname, '../src/library/echarts-wordcloud')
}
},
module: {
rules: [
...utils.styleLoaders(),
{
test: /\.vue$/,
loader: 'vue-loader'
},
{
test: /\.(cur|png|jpe?g|gif|svg)(\?.*)?$/,
loader: 'url-loader',
exclude: [
path.resolve(__dirname, '../src/components/icon'),
],
query: {
limit: 10,
name: utils.assetsPath('img/[name].[ext]')
}
},
{
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
loader: 'url-loader',
query: {
limit: 10000,
name: utils.assetsPath('fonts/[name].[ext]')
}
}
]
},
plugins: [
new VueLoaderPlugin()
]
}

webpack.base.conf.js

var path = require('path')
var config = require('../config') exports.assetsPath = function (_path) {
var assetsSubDirectory = process.env.NODE_ENV === 'production'
? config.build.assetsSubDirectory
: config.dev.assetsSubDirectory;
return path.posix.join(assetsSubDirectory, _path)
} exports.cssLoaders = function () {
const vueStyleLoader = {
loader: "vue-style-loader"
}
const cssLoader = {
loader: "css-loader",
}
// loader解析顺序从右至左
// const baseLoaders = [vueStyleLoader,cssLoader]
function generateLoaders (loader) {
const outputLoaders = [vueStyleLoader,cssLoader]
if (loader) {
const targetloader = {loader:loader+"-loader"}
outputLoaders.push(targetloader)
}
return outputLoaders
} return {
css: generateLoaders(),
less: generateLoaders("less"),
sass: generateLoaders("sass"),
scss: generateLoaders("sass"),
stylus: generateLoaders("stylus"),
styl: generateLoaders("stylus")
}
}
exports.styleLoaders = function () {
var output = []
var loaders = exports.cssLoaders() for (let extension in loaders) {
var loader = loaders[extension]
console.log(loader)
output.push({
test: new RegExp('\\.' + extension + '$'),
use: loader
})
}
return output
}

utils.js

process.env.NODE_ENV = 'production'

var webpack = require('webpack')
var webpackConfig = require('./webpack.base.conf') webpack(webpackConfig, function (err, stats) {
// spinner.stop()
if (err) throw err
process.stdout.write(stats.toString({
colors: true,
modules: false,
children: false,
chunks: false,
chunkModules: false
}) + '\n')
})

build.js

  

基于vue2.x的webpack升级与项目搭建指南--基础篇的更多相关文章

  1. 基于webpack的React项目搭建(一)

    前言 工欲善其事,必先利其器.为了更好的学习React,我们先简要的把开发环境搭建起来.本文主要介绍使用webpack搭建React项目,如果你对React或es6的基础语法还不了解,建议先去学习学习 ...

  2. 基于webpack的React项目搭建(二)

    前言 前面我们已经搭建了基础环境,现在将开发环境更完善一些. devtool 在开发的过程,我们会经常调试,so,为了方便我们在chrome中调试源代码,需要更改webpack.config.js,然 ...

  3. 【基于WinForm+Access局域网共享数据库的项目总结】之篇三:Access远程连接数据库和窗体打包部署

    篇一:WinForm开发总体概述与技术实现 篇二:WinForm开发扇形图统计和Excel数据导出 篇三:Access远程连接数据库和窗体打包部署 [小记]:最近基于WinForm+Access数据库 ...

  4. 基于Vue2写的一个有关美食项目

    刚学Vue练习的一个项目 使用Vue2+vue-router+vuex+axios+webpack router使用了默认的hash模式 引入了高德地图和element-ui 项目地址点击这里 演示地 ...

  5. 基于webpack的React项目搭建(三)

    前言 搭建好前文的开发环境,已经可以进行开发.然而实际的项目中,不同环境有着不同的构建需求.这里就将开发环境和生产环境的配置单独提取出来,并做一些简单的优化. 分离不同环境公有配置 不同环境虽然有不同 ...

  6. 基于Vue2.x的小米商城移动端项目

    初学vue已经有一段时间,为了检验自己的学习成果,决定做一个项目作为一个阶段性总结,项目花了差不多半个月时间,目前实现了7个页面,商城的主要功能基本实现,代码已经放到github上面. 这个项目把大部 ...

  7. 【基于WinForm+Access局域网共享数据库的项目总结】之篇一:WinForm开发总体概述与技术实现

    篇一:WinForm开发总体概述与技术实现 篇二:WinForm开发扇形图统计和Excel数据导出 篇三:Access远程连接数据库和窗体打包部署 [小记]:最近基于WinForm+Access数据库 ...

  8. 【基于WinForm+Access局域网共享数据库的项目总结】之篇二:WinForm开发扇形图统计和Excel数据导出

    篇一:WinForm开发总体概述与技术实现 篇二:WinForm开发扇形图统计和Excel数据导出 篇三:Access远程连接数据库和窗体打包部署 [小记]:最近基于WinForm+Access数据库 ...

  9. vue+webpack+vue-cli+WebStrom 项目搭建

    作者QQ:1095737364    QQ群:123300273     欢迎加入!   1.安装 webpack 和vue-cli 模块: npm install webpack -g npm in ...

  10. 基于maven+dubbo+spring+zookeeper的简单项目搭建

    maven下搭建dubbo小demo,供初学者学习,有不正确地方还请见谅. 先推荐一篇创建maven项目的文章,个人认为比较完整详细清楚: http://www.cnblogs.com/leiOOle ...

随机推荐

  1. SharePoint Iframe 报错“此内容不能显示在一个框架中”<续>

    在之前的SharePoint站点iframe引用中,我们遇到过下面的问题,就是其它系统或者不通环境的SharePoint站点,引用SharePoint页面会报错“此内容不能显示在一个框架中”,之前我们 ...

  2. java操作mysql中的编码问题解决

    要注意以下几点 1.在连接mysql数据库时 jdbc:mysql://localhost:3306/xiaonei?useUnicode=true&characterEncoding=utf ...

  3. hdwik中view模块的应用

    概述        MVC中的视图view 主要负责页面显示部分,所有的页面显示全部在此实现,视图对整个页面负责,它通过control的调用来显示页面和数据.视图(view)类template.cla ...

  4. Linux命令 find和mv的结合使用:查找文件,移动到某个目录

    显示前十个文件 [root@localhost smgpbi]# ls -1 | sort -u | head -10 1.首先查看文件个数,进入所在的文件 # find . -name " ...

  5. OpenGL画图旋转

    #include<gl/glut.h>#include<gl/GL.h>#include<gl/GLU.h>#include<math.h>#inclu ...

  6. 细聊 Cocoapods 与 Xcode 工程配置

    前言 文章比较长,所以在文章的开头我打算简单介绍一下这篇文章将要讲述的内容,读者可以选择通篇细度,也可以直接找到自己感兴趣的部分. 既然是谈 Cocoapods,那首先要搞明白它出现的背景.有经验的开 ...

  7. linux服务器WEB环境一键安装包lanmp教程之五

    在我们安装了linux服务器WEB环境一键安装包lanmp后,可能会有不少疑问还有就是使用过程中出现的问题,下面为大家总结几点比较常见的,如若还有其他疑问,可到wdlinux论坛寻找相关教程. 1.增 ...

  8. nginx sendfile tcp_nopush tcp_nodelay参数解释

    sendfile 现在流行的web 服务器里面都提供 sendfile 选项用来提高服务器性能,那到底 sendfile是什么,怎么影响性能的呢?sendfile实际上是 Linux2.0+以后的推出 ...

  9. C语言系列之强制类型转换(一)

    例子: #include <stdio.h> { char cChar;   //字符型变量 short int iShort; //短整型变量 int ilnt;           / ...

  10. oracle数据库基础功能

    一.oracle基本常用的数据类型 varchar(长度) 字符串char(长度) 字符number(x,y) x表示总位数 y表示保留小数点后几位数 eg面试题:number(5,3)最大的数是99 ...