使用webpack搭建vue项目及注意事项


Posted in Javascript onJune 10, 2019

有一句话叫“前人栽树后人乘凉”,还有一句话叫“如果说我看得比别人更远些,那是因为我站在巨人的肩膀上”。前一句是国人的俗语,后一句是那个发现了“万有引力”定律的牛顿说的。为什么要引用这两句呢?是因为我刚开始用vue的时候,使用的是vue-cli来搭建vue项目,快速又好用;我刚开始用react的时候,使用的是create-react-app来搭建react项目,方便又省事。使用这些已有的脚手架来搭建项目,无可厚非,对于新手来说,也确实能快速构建,不做置评。

既然已经有了这些现成的脚手架了,为什么我们还热衷于自己来配置webpack来搭建构建项目呢?因为我们只有了解并学会了配置webpack,我们才能更好地在打包构建项目时将webpack的性能发挥到极致,才能根据自身项目的实际需求,配置有利于项目开发的各种工具、插件,提高我们的开发效率。比如我们在打包项目时,可以分析哪些地方降低了webpack的打包速度,别人打包速度需要花去十多秒、二十多秒,而你能将打包的速度提升至几秒,这就是你的优势。当然,涉及到webpack的运行原理以及开发自己的loader或plugin就可以自行去学习了哈,本文只带你配置一个webpack来搭建一个vue项目。

wepack作为一个“模块打包机”其实是依赖了庞大的插件体系,插件体系是webpack的核心,可以说,webpack的生态就是建立在众多插件之上的,而开发环境和生产打包环境依赖的插件还是有所不同的,先以开发环境为例

webpack.config.js:

const path = require('path');
const Webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const VueLoaderPlugin = require('vue-loader/lib/plugin');
const ProgressBarPlugin = require('progress-bar-webpack-plugin');

const resolve = (dir) => {
 return path.join(__dirname, '..', dir)
}

const assetsPath = (_path) => {
 return path.join('static', _path)
}

const isEnvProduction = process.env.NODE_ENV == "production", port = 3003;

module.exports = {
 mode: 'development',
 devtool: 'source-map',
 entry: resolve('src'),
 output: {
  path: resolve('dist'),
  filename: isEnvProduction ? assetsPath('js/[name]-[hash].js') : '[name]-[hash].js',
  chunkFilename: isEnvProduction ? assetsPath('js/[name]-[chunkhash:5].min.js') : '[name]-[chunkhash:5].min.js',
  publicPath: '/',
 },
 resolve: {
  extensions: ['*', '.js', '.vue'], //webpack2.x extensions[0]不能为空 resolve属性中的extensions数组中用于配置程序可以自行补全哪些文件后缀
  alias: {
   '@': resolve('src'),
   // 'vue$': 'vue/dist/vue.esm.js'
  },
 },
 //提取公共代码
 optimization: {
  splitChunks: {
   cacheGroups: {
    commons: {
     test: /[\\/]node_modules[\\/]/, //表示默认拆分node_modules中的模块
     name: "vendor", //提取出来的文件命名
     chunks: "all",  //提取所有文件的公共部分
     minChunks: 2,   //表示提取公共部分最少的文件数 模块被引用>=2次,拆分至vendors公共模块
     minSize: 0,   //表示提取公共部分最小的大小 模块超过0k自动被抽离成公共模块
    },
   }
  }
 },
 module: {
  rules: [
   {
    test: /\.vue$/,
    use: ['vue-loader'],
    exclude: /node_modules/,
   },
   {
    test: /\.js$/,
    loader: 'babel-loader',
    exclude: /node_modules/,
    query: {
     "presets": ["@babel/env"],
     "plugins":
      ["@babel/plugin-syntax-dynamic-import", "@babel/plugin-transform-runtime"],
    }
   },
   {
    test: /\.(sa|sc|c)ss$/,
    use: [
     MiniCssExtractPlugin.loader,
     'css-loader',
     'postcss-loader',
     'sass-loader',
    ],
   },
   {
    test: /\.(eot?.+|svg?.+|ttf?.+|otf?.+|woff?.+|woff2?.+)$/,
    use: 'file-loader?name=' + (isEnvProduction ? assetsPath('fonts/[name].[hash:8].[ext]') : 'fonts/[name].[hash:8].[ext]')
   },
   {
    test: /\.(jpg|jpeg|png|gif|ico|svg)$/,
    loader: 'url-loader',
    options: {
     limit: 10000,
     name: isEnvProduction ? assetsPath('images/[name].[hash:8].[ext]') : 'images/[name].[hash:8].[ext]',
    }
   },
  ],
 },
 plugins: [
  new ProgressBarPlugin(),
  new VueLoaderPlugin(),
  //ProvidePlugin是webpack的内置模块,使用ProvidePlugin加载的模块在使用时将不再需要import和require进行引入
  new Webpack.ProvidePlugin({
   _: 'lodash',
  }),
  new HtmlWebpackPlugin({
   template: './src/index.html', //文件路径及名称
   filename: 'index.html',   //输出后的文件名称
  }),
  new MiniCssExtractPlugin({
   filename: isEnvProduction ? assetsPath("css/[name]-[hash].css") : "css/[name]-[hash].css",
   chunkFilename: isEnvProduction ? assetsPath("css/[name]-[hash].css") : "css/[name]-[hash].css", //默认就是取的以id或name为开头的css,所以可以加这行配置代码,也可以不加
  }),
 ],
 devServer: {
  port,
  host: '0.0.0.0',
  open: `http://localhost:${port}`,
  stats: {
   hash: false,
   builtAt: false,
   version: false,
   modules: false,
   children: false, ////解决类似Entrypoint undefined = index.html和Entrypoint mini-css-extract-plugin = *的警告
   entrypoints: false,
   colors: {
    green: '\u001b[32m',
    yellow: '\u001b[32m',
   }
  },
  proxy: {
   '/': {
    target: '',
    changeOrigin: true
   }
  },
  inline: true,
  compress: false,
  disableHostCheck: true,
  historyApiFallback: true,
 },
}

关于配置中用到的一些插件的api就不一一展开详解了,唯一需要说明的一点是,配置中所用到的插件的版本基本都是最新的,而使用postcss-loader时,需要在项目的根目录新建一个postcss.config.js文件:

module.exports = { 
 plugins: { 
 'autoprefixer': {browsers: 'last 5 version'} 
 } 
}

以上是开发环境的webpack配置,下边是打包生产环境的配置webpack.product.config.js:

const path = require('path');
const config = require('./webpack.config');
const merge = require('webpack-merge');
const UglifyJsPlugin = require("uglifyjs-webpack-plugin");
const HtmlWebpackPlugin = require('html-webpack-plugin');
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin"); //压缩单独的css文件
const CleanWebpackPlugin = require('clean-webpack-plugin');
const ManifestPlugin = require('webpack-manifest-plugin'); //资源清单
const SpeedMeasurePlugin = require("speed-measure-webpack-plugin"); //监控打包文件所花费的时间,方便具体的性能优化
const smp = new SpeedMeasurePlugin();
const PurifyCSSPlugin = require("purifycss-webpack"); //css tree-shaking 依赖插件glob-all和purify-css
const glob = require("glob-all");

module.exports = smp.wrap(merge(config, {
 mode: 'production',
 stats: config.devServer.stats,
 devtool: false,
 //当我们想在项目中require一些其他的类库或者API,而又不想让这些类库的源码被构建到运行时文件中,这在实际开发中很有必要。此时我们就可以通过配置externals参数来解决这个问题
 externals: {
 'vue': 'Vue',
 'vuex': 'Vuex',
 'moment': 'moment',
 'vue-router': 'VueRouter',
 'element-ui': 'ELEMENT',
 'ant-design-vue': 'antd', //使用externals html里需手动引入一下js,特别注意:还需额外引入moment.js,并放在antd之前,否则会报错
 'lodash': '_',
 },
 optimization: {
 minimizer: [
  new UglifyJsPlugin({
  parallel: true, //使用多线程并行运行来提高构建速度,默认并发运行数量:os.cpus().length - 1
  uglifyOptions: {
   compress: {
   inline: false,
   drop_console: true, //是否屏蔽掉控制台输出
   },
  }
  }),
  new OptimizeCSSAssetsPlugin() //压缩css
 ]
 },
 plugins: [
 new ManifestPlugin(),
 new CleanWebpackPlugin(),
 new PurifyCSSPlugin({
  paths: glob.sync([
  // 要做CSS Tree Shaking的路径文件
  path.resolve(__dirname, "../src/*.vue")
  ])
 }),
 new HtmlWebpackPlugin({
  template: './src/index.prod.html', //打包时需要的文件路径和名称
  filename: 'index.html',    //打包输出后的文件名称
  minify: { //压缩html
  removeComments: true, //删除注释
  collapseWhitespace: true //删除空格
  }
 }),
 ],
}));

打包的配置中有几点需要注意:

1、配置中有一个speed-measure-webpack-plugin的插件,可以监控打包文件所花费的时间,方便具体的性能优化;

2、配置中加入了webpack-manifest-plugin生成资源清单的插件,这个插件所生成的资源清单对服务端渲染SSR非常有用,服务端可以根据当前的manifest,引入css和js文件;

3、配置中引入了purifycss-webpack和glob-all两个插件并依赖一个purify-css插件用来对css的tree-shaking。shake有摇动、抖动之意,言外之意就是通过抖动将项目中没有使用却定义了的js方法给删除,降低打包后项目的体积,很形象哈。自webpack2开始,webpack就自带了js的tree-shaking,却没有css的tree-shaking,所以我们就借助了插件来实现tree-shaking。

4、为了提高打包的速度以及降低打包后的项目体积,我们可以将项目中用到框架采用CDN的方式引入,从而将这部分框架排除在打包之外,而new HtmlWebpackPlugin配置项中的template的路径引用的index.prod.html文件就是采用CDN的方式引入的第三方的框架,区分了开发环境中的index.html。提升构建速度也可以通过DllPlugin和DLLReferencePlugin插件来实现,具体配置可参考:https://3water.com/article/162789.htm

vue的项目目录:

使用webpack搭建vue项目及注意事项

react项目的webpack配置跟vue项目的webpack配置大同小异,这里不再多说,最后奉上package.json:

{
 "name": "webpackvue",
 "version": "1.0.0",
 "description": "",
 "main": "index.js",
 "scripts": {
 "test": "echo \"Error: no test specified\" && exit 1",
 "dev": "cross-env BABEL_ENV=development webpack-dev-server --config config/webpack.config.js",
 "build": "cross-env NODE_ENV=production webpack --config config/webpack.product.config.js"
 },
 "author": "",
 "license": "ISC",
 "devDependencies": {
 "@babel/core": "^7.4.4",
 "@babel/plugin-syntax-dynamic-import": "^7.2.0",
 "@babel/plugin-transform-runtime": "^7.4.4",
 "@babel/preset-env": "^7.4.4",
 "@babel/runtime": "^7.4.4",
 "autoprefixer": "^9.5.1",
 "babel-loader": "^8.0.6",
 "babel-plugin-import": "^1.11.2",
 "clean-webpack-plugin": "^2.0.2",
 "cross-env": "^5.2.0",
 "css-loader": "^2.1.1",
 "file-loader": "^3.0.1",
 "glob-all": "^3.1.0",
 "html-webpack-plugin": "^3.2.0",
 "lodash": "^4.17.11",
 "mini-css-extract-plugin": "^0.6.0",
 "node-sass": "^4.12.0",
 "optimize-css-assets-webpack-plugin": "^5.0.1",
 "postcss-loader": "^3.0.0",
 "progress-bar-webpack-plugin": "^1.12.1",
 "purify-css": "^1.2.5",
 "purifycss-webpack": "^0.7.0",
 "sass-loader": "^7.1.0",
 "speed-measure-webpack-plugin": "^1.3.1",
 "style-loader": "^0.23.1",
 "uglifyjs-webpack-plugin": "^2.1.3",
 "url-loader": "^1.1.2",
 "vue-loader": "^15.7.0",
 "vue-template-compiler": "^2.6.10",
 "webpack": "^4.31.0",
 "webpack-cli": "^3.3.2",
 "webpack-dev-server": "^3.3.1",
 "webpack-manifest-plugin": "^2.0.4",
 "webpack-merge": "^4.2.1"
 },
 "dependencies": {
 "ant-design-vue": "^1.3.9",
 "element-ui": "^2.8.2",
 "moment": "^2.24.0",
 "vue": "^2.6.10",
 "vue-router": "^3.0.6",
 "vuex": "^3.1.1"
 }
}

总结

以上所述是小编给大家介绍的使用webpack搭建vue项目及注意事项,希望对大家有所帮助,如果大家有任何疑问欢迎给我留言,小编会及时回复大家的!

Javascript 相关文章推荐
Prototype Class对象学习
Jul 19 Javascript
AeroWindow 基于JQuery的弹出窗口插件
Jun 27 Javascript
鼠标移到导航当前位置的LI变色处于选中状态
Aug 23 Javascript
判定是否原生方法的JS代码
Nov 12 Javascript
jquery dialog open后,服务器端控件失效的快速解决方法
Dec 19 Javascript
JavaScript设计模式之策略模式实例
Oct 10 Javascript
jQuery+JSON实现AJAX二级联动实例分析
Dec 18 Javascript
JavaScript基于扩展String实现替换字符串中index处字符的方法
Jun 13 Javascript
vue 权限认证token的实现方法
Jul 17 Javascript
详解微信小程序input标签正则初体验
Aug 18 Javascript
vue调试工具vue-devtools安装及使用方法
Nov 07 Javascript
JS Web Flex弹性盒子模型代码实例
Mar 10 Javascript
详解iview的checkbox多选框全选时校验问题
Jun 10 #Javascript
前端路由&webpack基础配置详解
Jun 10 #Javascript
在Vue中用canvas实现二维码和图片合成海报的方法
Jun 10 #Javascript
vue中使用 pako.js 解密 gzip加密字符串的方法
Jun 10 #Javascript
移动端 Vue+Vant 的Uploader 实现上传、压缩、旋转图片功能
Jun 10 #Javascript
利用百度echarts实现图表功能简单入门示例【附源码下载】
Jun 10 #Javascript
jquery操作checkbox的常用方法总结【附测试源码下载】
Jun 10 #jQuery
You might like
图形数字验证代码
2006/10/09 PHP
使用PHP批量生成随机用户名
2008/07/10 PHP
ThinkPHP中html:list标签用法分析
2016/01/09 PHP
php简单实现短网址(短链)还原的方法(测试可用)
2016/05/09 PHP
JQuery 表单中textarea字数限制实现代码
2009/12/07 Javascript
jquery动态加载图片数据练习代码
2011/08/04 Javascript
基于jquery异步传输json数据格式实例代码
2013/11/23 Javascript
关于JavaScript对象的动态选择及遍历对象
2014/03/10 Javascript
详解js跨域原理以及2种解决方案
2015/12/09 Javascript
jQuery实现的导航动画效果(附demo源码)
2016/04/01 Javascript
用AngularJS来实现监察表单按钮的禁用效果
2016/11/02 Javascript
jQuery实现的checkbox级联选择下拉菜单效果示例
2016/12/26 Javascript
Ajax异步获取html数据中包含js方法无效的解决方法
2017/02/20 Javascript
Bootstrap table使用方法记录
2017/08/23 Javascript
angular中不同的组件间传值与通信的方法
2017/11/04 Javascript
微信小程序结合Storage实现搜索历史效果
2019/05/18 Javascript
layui实现下拉框三级联动
2019/07/26 Javascript
javascript设计模式 ? 状态模式原理与用法实例分析
2020/04/22 Javascript
Python网络爬虫实例讲解
2016/04/28 Python
Python2/3中urllib库的一些常见用法
2017/12/19 Python
TensorFlow实现非线性支持向量机的实现方法
2018/04/28 Python
Python2 Selenium元素定位的实现(8种)
2019/02/25 Python
Django框架首页和登录页分离操作示例
2019/05/28 Python
Python3标准库之functools管理函数的工具详解
2020/02/27 Python
python redis存入字典序列化存储教程
2020/07/16 Python
Python3使用tesserocr识别字母数字验证码的实现
2021/01/29 Python
jupyter 添加不同内核的操作
2021/02/06 Python
美国购买体育赛事门票网站:TicketCity
2019/03/06 全球购物
火锅店营销方案
2014/02/26 职场文书
2014两会学习心得:时代的发展
2014/03/17 职场文书
2015年全国爱耳日活动总结
2015/02/27 职场文书
python pyhs2 的安装操作
2021/04/07 Python
深入理解python多线程编程
2021/04/18 Python
原生Js 实现的简单无缝滚动轮播图的示例代码
2021/05/10 Javascript
OpenCV3.3+Python3.6实现图片高斯模糊
2021/05/18 Python
Nginx如何配置多个服务域名解析共用80端口详解
2022/09/23 Servers