vuewebpack40-生产环境手动一步一步配置-持续更新中

10次阅读

共计 8731 个字符,预计需要花费 22 分钟才能阅读完成。

vue-webpack

介绍 vue+webpack 手动搭建过程,熟悉 webpack 的配置,loader& 插件的使用

介绍

目标:熟悉 webpack 的配置,loader& 插件的使用

通常新建 vue 项目都使用 vue-cli 脚手架,其实对于 webpack 的配置是一知半解,当需要升级 webpack 或者优化项目配置时,就显得很无力。通过手动搭建 webpack 可以对 webpack 有更深入的了解,当我们使用其他模块管理器时(eg:rollup,gulp),也不会那么生疏。

关于 dependencies 和 devDependencies

通过 NODE_ENV=developement 或 NODE_ENV=production 指定开发还是生产环境

devDependencies 是只会在开发环境下依赖的模块,生产环境不会被打入包内。

而 dependencies 依赖的包不仅开发环境能使用,生产环境也能使用。其实这句话是重点,按照这个观念很容易决定安装模块时是使用 –save 还是 –save-dev。

项目包版本

项目地址

    "dependencies": {
        "@xunlei/vue-lazy-component": "^1.1.3",
        "echarts": "^4.2.1",
        "element-resize-event": "^3.0.3",
        "element-ui": "^2.7.2",
        "file-loader": "^4.0.0",
        "html-loader": "^0.5.5",
        "html-webpack-plugin": "^3.2.0",
        "intersection-observer": "^0.7.0",
        "lodash": "^4.17.11",
        "url-loader": "^2.0.0",
        "vue": "^2.6.10",
        "webpack": "^4.34.0"
    }

遇到的配置错误

1.vue-loader was used without the corresponding plugin. Make sure to include VueLoaderPlugin in your webpack config.

解决办法:Vue-loader 在 15.* 之后的版本都是 vue-loader 的使用都是需要伴生 VueLoaderPlugin 的

2.Entrypoint undefined = index.html 代码不报错但是页面白版

解决办法:webpcak.config.js 中

new HTMLPlugin({template: 'index.html'}),

3.Child html-webpack-plugin for “index.html”: Entrypoint undefined = index.html

解决办法:webpcak.config.js 中

  resolve: {
    alias: {vue: 'vue/dist/vue.js'}
  },

这里涉及到一个小知识点 编译时 & 运行时

4. 运行时 + 编译器 vs. 只包含运行时

https://cn.vuejs.org/v2/guide…

因为在 Vue.js 2.0 中,最终渲染都是通过 render 函数,如果写 template 属性,则需要编译成 render 函数,那么这个编译过程会发生运行时,所以需要带有编译器的版本。很显然,这个编译过程对性能会有一定损耗,所以通常我们更推荐使用 Runtime-Only 的 Vue.js。

只有以下情况会用到 compiler:1. 有指定 template; 2. 没指定 template,也没指定 render(这时候使用的就是被挂载元素的 outerHtml)。

所以,没有使用到 compiler 的情况只有:没有指定 template,但指定了 render。

有时会遇到这样的错误:[Vue warn]: You are using the runtime-only build of Vue where the template compiler is not available. Either pre-compile the templates into render functions, or use the compiler-included build.

以上提到,解决这个问题有两种方式,但大多会选择后者,也就是使用全功能的 vue(runtime+compiler),这个版本的 vue 文件相比仅包含 runtime 的版本体积要大,而且运行时的 compiler 转换会消耗性能,compiler 过程其实可以放到构建过程中进行。总结就是,如果可以的话,尽量使用 runtime 版的 vue 文件。

5. It’s no longer allowed to omit the ‘-loader’ suffix when using loaders.

Webpack 新版本要求配置 module 中的 loader 不能缩写,也就是

loader:”json-loader”

中的 -loader 必须要写。

(在网上找配置时需要注意)

搭建步骤

1. 生成 package.json

npm init

2. 安装依赖

npm i webpack vue vue-loader
npm i css-loader vue-template-compiler

3. 文件目录

新建 app.vue
<template>
  <div id="test">{{text}}</div>
</template>

<script>
  export default {data() {
      return {text: 'abc'}
    }
  }
</script>
<style>
  #test {color: red;}
</style>
新建入口文件 index.js
import Vue from 'vue'
import App from './app.vue'

// Runtime Only
// new Vue({//   render: h => h(App) // h 就是 vue 中的 createApp 参数
// }).$mount('#app') // 将 app 挂载到 body 下的 div 上

// 会用到 compiler 所以使用全功能的 vue
new Vue({
  el: '#app',
  components: {App},
  template: '<App/>'
})
新建 webpack.config.js 配置
const path = require('path') //nodeJs 的基本包
module.exports = {//path.join(__dirname, 'src/index.js')中__dirname 表示当前文件的路径,path.join 就是将当前文件的路径跟 'src/index.js' 拼接起来,形成一个绝对路径
  entry: path.join(__dirname, 'src/index.js'),
  // 输出文件,取名为 bundle.js,路径为 dist 文件夹
  output: {
    filename: 'bundle.js',
    path: path.join(__dirname, 'dist')
  },
  module: {
    rules: [
      {
        test: /.vue$/,
        loader: 'vue-loader'
      }
    ]
  }
}
修改 package.json 文件
"scripts": {
    "test": "echo \"Error: no test specified\"&& exit 1",
    "build": "webpack --config webpack.config.js"
  }
webpack 配置项目加载各种静态资源及 css 预处理器

其中 index.js 入口文件如下:

import './assets/css/global.css'

其中 webpack.config.js 如下:

  module: {
    rules: [
      {
        test: /\.vue$/,
        loader: 'vue-loader'
      },
      {
        test: /\.html$/i,
        loader: 'html-loader'
      },
      {
        test: /\.css$/,
        use: ['style-loader', 'css-loader']
      },
      {
        test: /\.scss$/,
        loaders: ['style-loader', 'css-loader', 'sass-loader']
      },
      {test: /\.(gif|jpg|jpeg|png|svg)$/,
        use: [
          {
            loader: 'url-loader', // 能够将图片转成 base64 代码直接写在 js 里面, 依赖 file-loader,所以在安装的时候不仅要装 url-loader 还要装 file-loader
            options: {
              limit: 1024, // 如果文件大小小于 1024 字节,就会转义成 base64, 否则仍然是图片
              name: '[name]-aaa.[ext]' // 输出文件的名字,name 就是原先图片的名字,-aaa 是自己家的字段
            }
          }
        ]
      }
    ]
  },
webpack-dev-server 的配置和使用
  • 先在 package.json 中的 script 中加一个命令 ”dev”:“webpack-dev-server –config webpack.config.js”
"scripts": {
  "build": "cross-env NODE_ENV=production webpack --config webpack.config.js",
  "dev": "cross-env NODE_ENV=development webpack-dev-server --config webpack.config.js"
}

cross-env 能跨平台地设置及使用环境变量

npm 安装方式

npm i –save-dev cross-env

在 npm 脚本 (多是 package.json) 里这么配置

"scripts": {"build": "cross-env NODE_ENV=production webpack --config webpack.config.js",}

运行 npm run build,这样 NODE_ENV 便设置成功,无需担心跨平台问题

  • 修改 webpack 设置,来专门适应我们的 webpack-dev-server 的开发模式
  • 添加 webpack 的编译目标 target 为 web
  • 添加变量 isDev,用于读取是否为 development 环境。
  • 需要一个 html,去容纳我们的 js 文件, 不然没有 html,我们的项目是不能在浏览器中显示的 html-webpack-plugin 详细用法
  • devServer 中,还有其他的配置
    1.historyFallback 对于非定义的路由的处理
    2.open: true,// 启动的时候,自动打开浏览器
    3.hot: true,// 热加载,不需要刷新页面就能加载出来
  • 当使用热加载时还需要添加插件
    1.new webpack.HotModuleReplacementPlugin()
    2.new webpack.NoEmitOnErrorsPlugin()// 减少我们不需要的信息的展示
  • source-map 的配置
    config.devtool = ‘#cheap-module-eval-source-map’;

所以,整个 webpack 的代码为:

const path = require('path')
const HTMLPlugin = require('html-webpack-plugin')
const VueLoaderPlugin = require('vue-loader/lib/plugin')
const webpack = require('webpack')
const isDev = process.env.NODE_ENV === 'development'

const config = {
  target: 'web',
  entry: path.join(__dirname, 'src/index.js'),
  output: {
    filename: 'dist/bundle.js',
    path: path.join(__dirname, 'dist')
  },
  resolve: {
    alias: {vue$: 'vue/dist/vue.esm.js' // 用 webpack 1 时需用 'vue/dist/vue.common.js'}
  },
  module: {
    rules: [
      {
        test: /\.vue$/,
        loader: 'vue-loader'
      },
      {
        test: /\.html$/i,
        loader: 'html-loader'
      },
      {
        test: /\.css$/,
        use: ['style-loader', 'css-loader']
      },
      {
        test: /\.scss$/,
        loaders: ['style-loader', 'css-loader', 'sass-loader']
      },
      {test: /\.(gif|jpg|jpeg|png|svg)$/,
        use: [
          {
            loader: 'url-loader', // 能够将图片转成 base64 代码直接写在 js 里面, 依赖 file-loader,所以在安装的时候不仅要装 url-loader 还要装 file-loader
            options: {
              limit: 1024, // 如果文件大小小于 1024 字节,就会转义成 base64, 否则仍然是图片
              name: '[name]-aaa.[ext]' // 输出文件的名字,name 就是原先图片的名字,-aaa 是自己家的字段
            }
          }
        ]
      }
    ]
  },
  plugins: [
    new webpack.DefinePlugin({
      'process.env': {NODE_ENV: isDev ? '"development"' : '"production"'}
    }), // 一般 vue、react 等框架都要用到这个插件。// 在这里定义了,在我们的 js 代码中是可以引用到的。// 现在,veu/react 这类框架会根据环境去区分打包,打包后的 dist 在开发环境中是比较大的,因为有很多类似错误的信息们可以帮助我们开发人员开发,而生产环境是比较小的,没有繁多的错误信息,我们也不希望错误信息给用户看,所以就没必要把错误信息打包进去了
    // 为什么单引号里面还要双引号?因为如果没有的话,调用的时候,就成了 process.env.NODE_ENV = development, 这时候 development 就成了一个变量,所以需要写上双引号
    new HTMLPlugin({template: 'index.html'}),
    new VueLoaderPlugin()]
}

if (isDev) {
  config.devtool = '#cheap-module-eval-source-map' // 帮助我们在页面上调试我们的代码的, 并且有很多种 source-map 的映射方式,不同映射方式有不同的优缺点,这里写的只是其中一种,这个值,可以让你在浏览器看到源码
  config.devServer = {
    port: 8088,
    host: 'localhost', // 可以通过 localhost,127.0.0.1, 本机的内网 IP 进行访问(IP 的话,就可以在别人的电脑上访问)overlay: {error: true // 如果编译有错误,就直接显示在网页上},
    open: true,
    hot: true // 热加载,不需要刷新页面就能加载出来
  }
  config.plugins.push(new webpack.HotModuleReplacementPlugin(),
    new webpack.NoEmitOnErrorsPlugin() // 减少我们不需要的信息的展示)
}
module.exports = config

webpack 指定 mode

这部分厉害了,看完文档就知道为什么使用 vue-cli 脚手架 vue init webpack [项目名]生成的 webpack 配置文件包含

;-build | -webpack.base.conf.js | -webpack.base.dev.js | -webpack.base.prod.js

配置

development(开发环境) 和 production(生产环境) 这两个环境下的构建目标存在着巨大差异。

  • 开发环境中:强大的 source map 和一个有着 live reloading(实时重新加载) 或 hot module replacement(热模块替换) 能力的 localhost server
  • 生产环境:关注点在于压缩 bundle、更轻量的 source map、资源优化等,通过这些优化方式改善加载时间。

我们先从安装 webpack-merge 开始,并将已经成型的那些代码进行分离:

npm install --save-dev webpack-merge
- |- webpack.config.js
+ |- webpack.common.js
+ |- webpack.dev.js
+ |- webpack.prod.js

具体使用方式查看文档 webpack 指定 mode

生产环境性能优化

1.html-webpack-plugin

该插件将为你生成一个 HTML5 文件,其中包括使用 script 标签的 body 中的所有 webpack 包。

插件配置项地址

new HTMLPlugin({
      template: 'index.html',
      minify: {
        collapseWhitespace: true,
        removeComments: true,
        removeRedundantAttributes: true,
        removeScriptTypeAttributes: true,
        removeStyleLinkTypeAttributes: true,
        useShortDoctype: true
      }
    }),

针对生成的 html 移除注释、删除空行、html 压缩等操作

2.mini-css-extract-plugin

将 CSS 提取为独立的文件的插件

MiniCssExtractPlugin

查看文件中 webpack.config.js 配置

const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
  plugins: [
    new MiniCssExtractPlugin({
      // 类似 webpackOptions.output 里面的配置 可以忽略
      filename: '[name].css',
      chunkFilename: '[id].css',
    }),
  ],
  module: {
    rules: [
            {test: /\.(sa|sc|c)ss$/,
        use: [
          isDev ? 'sass-loader' : MiniCssExtractPlugin.loader,
          'css-loader',
          'sass-loader'
        ]
      },
    ]
  }
}

测试 css 被单独提取,文件大小为 main.css 264K

阶段进行压缩

webpack5 可能会内置 CSS 压缩器,webpack4 需要自己使用压缩器,可以使用 optimize-css-assets-webpack-plugin 插件。设置 optimization.minimizer 覆盖 webpack 默认提供的,确保也指定一个 JS 压缩器

  optimization: {
    minimizer: [
      new UglifyJsPlugin({
        cache: true,
        parallel: true,
        sourcMap: true
      }),
      new OptimizeCSSAssetsPlugin({}),
    ],
  }

测试 css 被单独提取,文件大小为 main.css 219K 比压缩前减少 45K

3.uglifyjs-webpack-plugin

遇到的错误

ERROR in bundle.js from UglifyJs
Unexpected token: punc «(» src/app.vue:21,0

解决方法:配置 babel

未使用前 bundle.js 1937KB 使用后压缩到 752KB

4.CompressionPlugin

当我们的项目越来越庞大是时候 会发现 即使做了 code split 代码压缩 动态加载 等等一系列优化之后 页面的响应速度依旧很慢

这个时候时候可以使用 compression-webpack-plugin 这个插件

new CompressionPlugin({"filename": "[path].gz[query]",
  "test": new RegExp("\\.(js|css)$" // 压缩 js 与 css
  ),
  "threshold": 500, // 当文件超过限制大小 使用 gzip
  "minRatio": 0.8,
  "algorithm": "gzip"
})

该插件的作用是 在超过限定的文件大小的 时候会生成一个跟文件同名的 gz 包, 这个时候我们需要在改下 nginx 的配置 启用 gzip 压缩并 开启 gzip_static

vendors~main.772d21ef3139973e4cab.bundle.js.gz 可以将原来 819KB 压缩到 220KB 压缩力度还是非常大的

参考(特别感谢)

  1. 你真的理解 devDependencies 和 dependencies 区别吗?
  2. webpack 中文文档
  3. vue 编译时运行时
  4. 了解 vue 里的 Runtime Only 和 Runtime+Compiler
  5. It’s no longer allowed to omit the‘-loader’suffix
  6. vue 项目从 0 搭建(webpack 手动搭建)
  7. webpack4 mini-css-extract-plugin
  8. MiniCssExtractPlugin 需要添加 babel
  9. webpack-bundle-analyzer 打包文件分析工具
  10. SplitChunksPlugin
  11. Webpack SplitChunksPlugin 的三种模式
  12. 基于 vue2.x 的 webpack4 配置(生产环境~)
  13. vue 进行 gzip 压缩和服务器如何开启 gzip)
  14. web 应用性能优化之 nginx + compression-webpack-plugin

正文完
 0