作者: 王振州 时间: 2020-12-17
Vue cli 是一个基于 Vue.js 进行快速开发的完整系统, 有三个组件:
cli:@vue/cli 全局安装的 npm 包,提供了终端里的 vue命令(如:vue create 、vue serve 、vue ui 等命令)
cli 服务:@vue/cli-service 是一个开发环境依赖。构建于 webpack 和 webpack-dev-server 之上(提供 如:serve、build 和 inspect 命令)
cli 插件:给 Vue 项目提供可选功能的 npm 包 (如: Babel 转译、ESLint 集成、unit 和 e2e 测试 等)
vue-cli(1.x 或 2.x)要先卸载它,否则跳过此步:npm uninstall vue-cli -g
# or
yarn global remove vue-cli
@vue/cli(Vue cli 3的包名称由 vue-cli 改成了 @vue/cli)npm install -g @vue/cli
# or
yarn global add @vue/cli
vue -V
vue --version
vue create <Project Name>
<Project Name>: 文件名 不支持驼峰(含大写字母)
? Please pick a preset
❯ oms (vue-router, vuex, dart-sass, babel, typescript, pwa, unit-jest) // 自定义保存的预设(之前我保存的)
  micro-front-end (vue-router, vuex, dart-sass, babel, pwa, eslint, unit-mocha, e2e-cypress) // 自定义保存的预设(之前我保存的)
  default (babel, eslint) // 默认设置非常适合快速创建一个新项目的原型,没有带任何辅助功能的 npm 包
  Manually select features // 自定义配置是我们所需要的面向生产的项目,提供可选功能的 npm 包
括号中的 npm 包是选择项选择后自动配置到项目中的, 如果选择最后一个(Manually select features)的话, 就会进行下一步选择配置项, 否则就会使用选择的预设或配置项进行生成项目
? Check the features needed for your project:
 ◉ Babel  // 转码器,可以将ES6代码转为ES5代码,从而在现有环境执行。
 ◯ TypeScript  // TypeScript是一个JavaScript(后缀.js)的超集(后缀.ts)包含并扩展了 JavaScript 的语法,需要被编译输出为 JavaScript在浏览器运行,目前较少人再用
 ◯ Progressive Web App (PWA) Support  // 渐进式Web应用程序
 ◉ Router  // vue-router(vue路由)
 ◉ Vuex  // vuex(vue的状态管理模式)
 ◉ CSS Pre-processors   // CSS 预处理器(如:less、sass)
 ◉ Linter / Formatter   // 代码风格检查和格式化(如:ESlint)
❯◉ Unit Testing         // 单元测试(unit tests)
 ◯ E2E Testing          // e2e(end to end) 测试
选择完后直接enter,然后会提示你选择对应功能的具体工具包, 本项目选择的是上面◉的选项
Vue-Router 利用了浏览器自身的 hash 模式和 history 模式的特性来实现前端路由
为什么要选 dart-sass, 而不是 node-sass?
vue-cli4 默认选择的是 dart-sass
? Pick a CSS pre-processor (PostCSS, Autoprefixer and CSS Modules are supported by default):
  Sass/SCSS (with dart-sass)
❯ Sass/SCSS (with node-sass)
  Less
  Stylus
主要为 css 解决浏览器兼容、简化 CSS 代码 等问题, 本项目使用的是Sass/SCSS (with node-sass)
? Pick a linter / formatter config:
  ESLint with error prevention only
  ESLint + Airbnb config
  ESLint + Standard config
❯ ESLint + Prettier
提供一个插件化的 javascript 代码检测工具,ESLint + Prettier 使用较多
❯◉ Lint on save // 保存时
 ◯ Lint and fix on commit  // commit时检测和修复
?  Pick a unit testing solution:
   Mocha + Chai //mocha灵活,只提供简单的测试结构,如果需要其他功能需要添加其他库/插件完成。必须在全局环境中安装
❯◉ Jest //安装配置简单,容易上手。内置Istanbul,可以查看到测试覆盖率,相较于Mocha:配置简洁、测试代码简洁、易于和babel集成、内置丰富的expect
Where do you prefer placing config for Babel, PostCSS, ESLint, etc.? (Use arrow keys)
❯ In dedicated config files  // 单独文件存放
  In package.json // 存放在package.json中
Save this as a preset for future projects? (y/N)
等命令运行完成项目就搭建完, 可以进入(cd)到对应的目录运行(yarn serve)
vue.config.js 是一个可选的配置文件,如果项目的 (和 package.json 同级的) 根目录中存在这个文件,那么它会被 @vue/cli-service 自动加载。你也可以使用 package.json 中的vue 字段,但是注意这种写法需要你严格遵照 JSON的格式来写。
在说配置之前插播一下
process.env.NODE_ENV的值process.env.NODE_ENV的值是执行命令是注入的临时 env 环境变量, 在该项目中, 开发环境默认的development, 发布环境默认是production设置方式:window环境set key=valueMac/Linux环境:export key=value; 命令窗口关闭自动销毁
下面是项目中关于 vue.config.js 的配置
// 关于下面三个插件会在后面进行详细介绍
// 代码压缩(内容)混淆插件
const UglifyJsPlugin = require("uglifyjs-webpack-plugin");
// 文件压缩(编码方式)插件
const CompressionPlugin = require("compression-webpack-plugin");
// 打包文件分析插件
const BundleAnalyzerPlugin = require("webpack-bundle-analyzer/lib/BundleAnalyzerPlugin");
const path = require("path");
// 成产环境gzip文件的后缀
const productionGzipExtensions = ["js", "css"];
// 拼接文件路径(相对路径==>绝对路径)
function resolve(dir) {
  return path.join(__dirname, dir);
}
module.exports = {
  // 部署应用包时的基本 URL , (默认'/')
  baseUrl: process.env.NODE_ENV === "development" ? "/" : "./",
  outputDir: "dist", // 运行时生成的生产环境构建文件的目录(默认''dist'',构建之前会被清除)
  assetsDir: "", // 放置生成的静态资源(js、css、img、fonts)的(相对于 outputDir 的)目录(默认'')
  indexPath: "index.html", // 指定生成的 index.html 的输出路径(相对于 outputDir)也可以是一个绝对路径。
  pages: {
    //pages 里配置的路径和文件名在你的文档目录必须存在 否则启动服务会报错
    index: {
      //除了 entry 之外都是可选的
      entry: "src/index/main.js", // page 的入口,每个“page”应该有一个对应的 JavaScript 入口文件
      template: "public/index.html", // 模板来源
      filename: "index.html", // 在 dist/index.html 的输出
      title: "国土空间规划一张图实施监督系统", // 当使用 title 选项时,在 template 中使用:<title><%= htmlWebpackPlugin.options.title %></title>
      chunks: ["chunk-vendors", "chunk-common", "index"], // 在这个页面中包含的块,默认情况下会包含,提取出来的通用 chunk 和 vendor chunk
    },
  },
  // 开发环境本地服务配置
  devServer: {
    host: "0.0.0.0", // host
    port: 9096, // 端口号
    open: true, // 是否默认打开浏览器
  },
  // 默认情况下 babel-loader 会忽略所有 node_modules 中的文件。
  // 如果你想要通过 Babel 显式转译一个依赖,可以在这个选项中列出来。
  // vue-echarts-v3需要babel转码,才能兼容ie
  // 参考 https://github.com/xlsdg/vue-echarts-v3#usage
  transpileDependencies: ["vue-echarts-v3", "iview"],
  css: {
    // css预设器配置项
    loaderOptions: {
      // 给 sass-loader 传递选项
      sass: {
        // data引入公用文件或全局变量,多个用 ; 进行分割
        data: `@import "@/styles/index.scss";`,
      },
    },
  },
  // 是否为 Babel 或 TypeScript 使用 thread-loader。该选项在系统的 CPU 有多于一个内核时自动启用,仅作用于生产构建
  // 在配置中没有看到babel相关配置? 内部使用了cache-loader, 其实内部还是使用了babel-loader @babel/cli @babel/core @babel/preset-env
  parallel: false,
  // [链式操作](https://cli.vuejs.org/zh/guide/webpack.html#链式操作-高级)
  // 基于[webpack-chain](https://github.com/neutrinojs/webpack-chain), 这个库提供了一个 webpack 原始配置的上层抽象,使其可以定义具名的 loader 规则和具名插件等,并有机会在后期进入这些规则并对它们的选项进行修改。它允许我们更细粒度的控制其内部配置。添加新的loader 删除loader 修改loader等
  chainWebpack: (config) => {
    // 设置别名
    config.resolve.alias
      .set("@", resolve("src"))
      .set("YZT", resolve("src/views/YZT"))
      .set("JDGL", resolve("src/views/JDGL"))
      .set("FZBZ", resolve("src/views/FZBZ"))
      .set("JCYJ", resolve("src/views/JCYJ"))
      .set("ZBMX", resolve("src/views/ZBMX"))
      .set("YWXT", resolve("src/views/YWXT"))
      .set("MXXT", resolve("src/views/MXXT"))
      .set("FZSC", resolve("src/views/FZSC"));
    // 鼠标指针样式
    config.module
      .rule("mouse")
      .test(/\.(ico|cur)(\?.*)?$/)
      .use("file-loader")
      .loader("file-loader")
      .options({
        name: "[path][name].[ext]",
      })
      .end();
    // worker-loader
    config.module
      .rule("worker")
      .test(/\.worker\.js$/)
      .use("worker-loader")
      .loader("worker-loader")
      .options({
        inline: true,
      })
      .end();
    config.module.rule("js").exclude.add(/\.worker\.js$/);
    config.output.globalObject("this");
    config.plugin("html").tap((args) => {
      if (process.env.NODE_ENV === "development") {
        args[0].favicon = path.resolve("public/favicon_dev.ico");
      }
      return args;
    });
    // use svg
    const svgRule = config.module.rule("svg");
    svgRule.uses.clear();
    svgRule.include
      .add(resolve("src/icon/svg"))
      .end()
      .use("svg-sprite-loader")
      .loader("svg-sprite-loader")
      .options({
        symbolId: "icon-[name]",
      })
      .end();
    // image exclude svg
    const imagesRule = config.module.rule("images");
    imagesRule
      .test(/\.(png|jpe?g|gif|webp|svg)(\?.*)?$/)
      .exclude.add(resolve("src/icon/svg"))
      .end();
  },
  // 该配置会通过 webpack-merge 合并到最终的 webpack 配置中
  configureWebpack: {
    // 生成的source-map类型
    // source-map	原始代码 最好的sourcemap质量有完整的结果,但是会很慢
    // eval-source-map	原始代码 同样道理,但是最高的质量和最低的性能
    // cheap-module-eval-source-map	原始代码(只有行内) 同样道理,但是更高的质量和更低的性能
    // cheap-eval-source-map	转换代码(行内) 每个模块被eval执行,并且sourcemap作为eval的一个dataurl
    // eval	生成代码 每个模块都被eval执行,并且存在@sourceURL,带eval的构建模式能cache SourceMap
    // cheap-source-map	转换代码(行内) 生成的sourcemap没有列映射,从loaders生成的sourcemap没有被使用
    // cheap-module-source-map	原始代码(只有行内) 与上面一样除了每行特点的从loader中进行映射
    //
    // eval	使用eval包裹模块代码
    // source-map	产生.map文件
    // cheap	不包含列信息, 也不包含loader的sourcemap
    // module	包含loader的sourcemap(比如jsx to js ,babel的sourcemap),否则无法定义源文件
    // inline	将.map作为DataURI嵌入,不单独生成.map文件
    devtool:
      process.env.NODE_ENV === "development"
        ? "cheap-module-eval-source-map"
        : "source-map",
    // 优化
    // 从 webpack 4 开始,会根据你选择的 mode 来执行不同的优化, 不过所有的优化还是可以手动配置和重写。
    optimization: {
      // 允许你通过提供一个或多个定制过的 TerserPlugin 实例, 覆盖默认压缩工具(minimizer)。
      minimizer: [
        new UglifyJsPlugin({
          uglifyOptions: {
            warnings: false, // 是否抛出警告
            compress: {
              drop_console: false, // 生产环境是否自动去掉console
              drop_debugger: true, // 生产环境是否自动去掉debugger
            },
          },
          parallel: true,
          sourceMap: true,
        }),
      ],
    },
    // webpack 插件
    plugins:
      process.env.NODE_ENV === "production"
        ? [
            // gzip
            new CompressionPlugin({
              filename: "[path].gz[query]", // 文件名
              algorithm: "gzip", // 压缩方式
              test: new RegExp(
                "\\.(" + productionGzipExtensions.join("|") + ")$"
              ), // 处理所有匹配此 {RegExp} 的资源
              threshold: 10240, // 压缩阈值
              minRatio: 0.8, // 压缩比
            }),
            // webpack 依赖库分析
            process.env.npm_config_report
              ? new BundleAnalyzerPlugin({
                  analyzerMode: "static",
                })
              : function none() {},
          ]
        : [],
  },
};
字面意思是使 js 丑陋的 webpack 插件, 顾名思义用来压缩 js 文件 使用 uglifyjs 作为核心库; uglifyjs-webpack-plugin 源码地址
// UglifyJsPluginOptions 插件初始化参数
 test?: RegExp | RegExp[]; 测试匹配哪些文件 默认 test = /\.js(\?.\*)?\$/i
 include?: RegExp | RegExp[]; 包含哪些文件
 exclude?: RegExp | RegExp[]; 排除哪些文件
 cache?: boolean | string; 是否启用文件缓存,默认缓存在 node_modules/.cache/uglifyjs-webpack-plugin.目录
 parallel?: boolean | number; 使用多进程并行运行来提高构建速度 true cpu 核数 - 1
 sourceMap?: boolean; 是否生成 sourceMap cheap-source-map 不适用该插件
 uglifyOptions?: UglifyJsOptions;
     uglifyOptions: {
          warnings: false, // 是否抛出警告
          compress: {
            drop_console: false, // 生产环境是否自动去掉console
            drop_debugger: true // 生产环境是否自动去掉debugger
          }
        }
 extractComments?: boolean | RegExp | ((node: object, comment: string) => boolean) | ExtractCommentsOptions;
 warningsFilter?: (source: string) => boolean;
// UglifyJsPluginOptions 默认值
const {
  minify,
  uglifyOptions = {},
  test = /\.js(\?.\*)?\$/i,
  chunkFilter = () => true,
  warningsFilter = () => true,
  extractComments = false,
  sourceMap = false,
  cache = false,
  cacheKeys = (defaultCacheKeys) => defaultCacheKeys,
  parallel = false,
  include,
  exclude,
} = options;
参数
| Name | Type | Default | Description | 
|---|---|---|---|
| test | {RegExp} | . | 处理所有匹配此 {RegExp} 的资源 | 
| asset | {String} | [path].gz[query] | 目标资源名称[file] 会被替换成原资源。[path] 会被替换成原资源路径,[query] 替换成原查询字符串 | 
| filename | {Function} | false | 一个 {Function} (asset) => asset 函数,接收原资源名(通过 asset 选项)返回新资源名 | 
| algorithm | {String | Function} | gzip | 
| threshold | {Number} | 0 | 只处理比这个值大的资源。按字节计算 | 
| minRatio | {Number} | 0.8 | 只有压缩率比这个值小的资源才会被处理 | 
| deleteOriginalAssets | {Boolean} | false | 是否删除原资源 | 
作用是用来提升网络传输速率达到优化 web 页面加载时间的目的
我们知道基于 HTTP 协议, 在发起请求时候会通过 HTTP 请求头告诉服务器它期待服务器采取什么形式的压缩内容, 方便后续的解压缩处理
基本原理

但是有一些问题: 压缩文件耗费服务器 CPU(服务器需要压缩文件、浏览器解压文件) 那么我们就可以借助 CompressionWebpackPlugin 插件来提前对文件进行 Gzip 压缩, 这样服务器查找到有与源文件同名的.gz 文件就会直接读取,不会主动压缩,降低 cpu 负载,优化了服务器性能
node 端 nodejs 很幸福,只需 use 一个 compress 模块
var compression = require("compression");
var app = express();
//尽量在其他中间件前使用 compression
app.use(compression());
tomcat 找到 tomcat 的 server.xml 文件,找到其中 Connector 节点然后进行配置修改,具体配置如下
<Connector port="80"protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" URIEncoding="UTF-8" maxPostSize="0" useBodyEncodingForURI="true" compression="on" compressionMinSize="2048" noCompressionUserAgents="gozilla, traviata" compressableMimeType="text/html,text/xml,application/javascript,text/css,text/plain,image/jpeg,application/json"/>
这个插件的功能是生成代码分析报告,帮助提升代码质量和网站性能, 它可以直观分析打包出的文件包含哪些,大小占比如何,模块包含关系,依赖项,文件是否重复,压缩后大小如何,针对这些,我们可以进行文件分割等操作。
项目中配置了report命令npm_config_report=true vue-cli-service build, 用于触发 vue.config.js 中配置的该插件
// webpack 依赖库分析
process.env.npm_config_report
  ? new BundleAnalyzerPlugin({
      analyzerMode: "static",
    })
  : function none() {};

// options 参数
{
  //  可以是`server`,`static`或`disabled`。
  //  在`server`模式下,分析器将启动HTTP服务器来显示软件包报告。
  //  在“static”模式下,会生成带有报告的单个HTML文件。
  //  在`disabled`模式下,你可以使用这个插件来将`generateStatsFile`设置为`true`来生成Webpack Stats JSON文件。
  "analyzerMode": "server",
  //  将在“服务器”模式下使用的主机启动HTTP服务器。
  "analyzerHost": "127.0.0.1",
  //  将在“服务器”模式下使用的端口启动HTTP服务器。
  "analyzerPort": 8888,
  //  路径捆绑,将在`static`模式下生成的报告文件。
  //  相对于捆绑输出目录。
  "reportFilename": "report.html",
  //  模块大小默认显示在报告中。
  //  应该是`stat`,`parsed`或者`gzip`中的一个。
  //  有关更多信息,请参见“定义”一节。
  "defaultSizes": "parsed",
  //  在默认浏览器中自动打开报告
  "openAnalyzer": true,
  //  如果为true,则Webpack Stats JSON文件将在bundle输出目录中生成
  "generateStatsFile": false,
  //  如果`generateStatsFile`为`true`,将会生成Webpack Stats JSON文件的名字。
  //  相对于捆绑输出目录。
  "statsFilename": "stats.json",
  //  stats.toJson()方法的选项。
  //  例如,您可以使用`source:false`选项排除统计文件中模块的来源。
  //  在这里查看更多选项:https:  //github.com/webpack/webpack/blob/webpack-1/lib/Stats.js#L21
  "statsOptions": null,
  "logLevel": "info" // 日志级别。可以是'信息','警告','错误'或'沉默'。
}