【第一弹】Vue3 + Typescript +Vant + Pretter 移动端脚手架优化方案

1,560 阅读6分钟

各位知友,关于Vue框架的前端项目脚手架,优秀的代码仓库/博客文章已经很多,为什么我还要做这么一件事呢?我是想打算做一个前端工程化的体系分享,其中主要有:

  • Vue脚手架的基础搭建和优化
  • 基于vue-cli的 企业级脚手架
  • Jenkins持续集成

期待各位知友一起学习分享,共同进步!

废话不多说,先上仓库地址

vue-piece-h5

背景

有序、开箱即用、开关式配置、脚手架打包、加载优化

技术栈

Vue3.2、Webpack5、Vant、Typescript、Pretter、Eslint、Vuex、Axios、Sass

分支管理

环境分支全局环境变量标识npm脚本命令
开发developmentdevelopmentnpm run dev
测试development-fixdevelopment-fixnpm run build-dev-fix
预发布releasereleasenpm run build-release
生产productionproductionnpm run build

打包优化方案

在脚手架代码/打包优化方面,主要做了下面的优化措施,针对每一点,接下来会详细和各位知友,通过代码块分享。

禁用js和css的预加载 Gzip打包压缩 全局环境变量+打包配置 跨域proxy代理 修复 HMR(热更新)失效 添加请求别名Alias px转rem移动端适配 svg图标组件 图片压缩 cdn加载包文件 去除console.log sass全局样式 prerender-spa-plugin 预渲染开启(更好的SEO,比SSR开发成本低,代码侵入性低) 骨架屏 添加打包分析report

  • 禁用js和css的预加载

通过降低浏览器带宽请求,提高页面的加载速度

 config.plugins.delete('preload')
 config.plugins.delete('prefetch')
  • Gzip打包压缩

项目打包上线时,发现build后的包体积很大,导致首屏加载速度慢,Gzip就能明显有效的解决这个问题,其实原理就是把原js、css进行压缩,从而减少打包文件体积,提高首屏的访问速度。

首先,需要引入Webpack插件 compression-webpack-plugin

yarn add compression-webpack-plugin --dev
const CompressionPlugin = require('compression-webpack-plugin')

其次,vue.config.js增加plugins的关键配置即可

// VUE_APP_GLOBAL_ENV 是全局环境变量
const isProduction = process.env.VUE_APP_GLOBAL_ENV !== 'development'
const productionGzipExtensions = ['js', 'css']
module.exports = {
    configureWebpack: (config) => {
    // 打包环境
    if (isProduction) {
      config.plugins.push(
        new CompressionPlugin({
          algorithm: 'gzip',  // 压缩算法、函数
          test: new RegExp('\.(' + productionGzipExtensions.join('|') + ')$'), //需要压缩的文件 
          threshold: 10240, // 处理大于10240字节的文件
          minRatio: 0.8, // 压缩率
          deleteOriginalAssets: false // 是否删除原文件
        })
      )
    }
  }
}

在Nginx配置方面,需要在nginx.conf开启Gzip

  #gzip  on;
    gzip on;
    gzip_min_length 1k;
    gzip_buffers 4 16k;
    #gzip_http_version 1.0;
    gzip_comp_level 2;
    gzip_types text/plain application/javascript application/x-javascript text/css application/xml text/javascript application/x-httpd-php image/jpeg image/gif image/png;
    gzip_vary off;
    gzip_disable "MSIE [1-6].";
  • 全局环境变量+打包配置

在实际开发过程中,有时需要区分不同的开发、打包环境,比如最常见的接口域名:本地开发、测试、预发布、生产;为了统一配置,减少代码频繁更改。

首先关于环境变量文件名,必须以如下方式命名,也不需要专门手动控制加载那个文件。

.env 全局默认配置文件,不论什么环境都会加载合并
.env.development 开发环境下的配置文件
.env.development-fix 测试环境下的配置文件
.env.release 预发布环境下的配置文件
.env.production 生产环境下的配置文件

根据vue的启动命令vue会自动加载对应的环境,所以上面说,明明要规范且对应;配置package.json文件可以具体设置启动具体加载哪个.env.xxx文件。

 "dev": "vue-cli-service serve",
 "build-fix": "vue-cli-service build --mode development-fix  --report",
 "build-release": "vue-cli-service build --mode release  --report",
 "build": "vue-cli-service build --mode production  --report",

其次就是配置文件中的内容,自定义属性配置必须以 VUE_APP开头,比如:VUE_APP_API_URL

  • 跨域proxy代理

vue-cli中的proxy主要为了解决本地开发环境下,资源请求跨域的问题;

首先,在vue.config.js文件下,配置devServer,在本地运行的时候,会自动加载

module.exports = {
  devServer: {
    port: '8088', // 端口号,可以不指定
    hot: true,
    open: true,
    https: true,
    proxy: {
      '/coudMusicApi': {
        target: process.env.VUE_APP_API_URL, // 这里就用到了全局配置文件下的公共变量
        changeOrigin: true,
        secure: false,
        pathRewrite: {
          '/coudMusicApi': '/'
        }
      }
    }
  }
}

其次,比如在发送接口请求时,直接配置上标识即可

export const getApi = () => {
  return request.request({
    method: RequestTypeEnum.GET,
    url: 'coudMusicApi/countries/code/list'
  })
}

编辑切换为居中

添加图片注释,不超过 140 字(可选)

虽然我们再浏览器F12看到的虽然是本地域名,实际上已经代理到了真实的接口地址。

  • 修复 HMR(热更新)失效

本地运行时,浏览器会以下报错

编辑切换为居中

添加图片注释,不超过 140 字(可选)

就需要通过以下配置解决

module.exports = {
  chainWebpack: (config) => {
    // 解决热更失效问题
    config.resolve.symlinks(true)
  }
}
  • 添加请求别名Alias

添加请求别名,减少冗余代码,具体配置如下:

const { resolve } = require('path')
module.exports = {
  config.resolve.alias
      .set('@', resolve('src'))
      .set('@assets', resolve('src/assets'))
      .set('@styles', resolve('src/styles'))
}
  • px转rem移动端适配

px 转rem解决移动端适配方案,是兼容性相对较好,上手较为简单的一种方案。方案的落地方式有很多,我选择了 postcss-pxtorem 结合 amfe-flexible,具体配置如下:

安装插件包

yarn add amfe-flexible
yarn add postcss-pxtorem

在vue.config.js中配置postcss插件

module.exports = {
  css: {
     postcss: {
        plugins: [
          require('postcss-pxtorem')({
            rootValue: 37.5,
            propList: ['*']
          })
        ]
      }
  }
}

main.ts中,引入amfe-flexible

import 'amfe-flexible/index.js'

最终效果如下:

编辑

添加图片注释,不超过 140 字(可选)

编辑

添加图片注释,不超过 140 字(可选)

  • svg图标组件

为了解决引入svg图标,现在又两个比较好用的loader:

sass-loader、svg-sprite-loader

原理就不讲了,我们就直接看如何使用的吧

引入loader

yarn add svg-sprite-loader --dev
yarn add svg-loader --dev

接下来配置vue.config.js 的loader

module.exports = {
  chainWebpack: (config) => {
      // 配置svg: 添加svg-sprite-loader插件,加载svg图标
    config.module
      .rule('svg-icon')
      .include.add(resolve('src/assets/icon/svg'))
      .end()
      .test(/.svg$/)
      .use('svg-sprite-loader')
      .loader('svg-sprite-loader')
      .options({
        symbolId: 'icon-[name]'
      })
    // 删除自带的svg解析规则
    config.module.rule('svg').exclude.add(resolve('src/assets/icon/svg'))
    config.module
      .rule('images')
      .use('image-webpack-loader')
      .loader('image-webpack-loader')
      .options({ bypassOnDebug: true })
      .end()
  }
}

前提,需要把svg的图标文件,放在 src/assets/icon/svg 下。

如何封装公共svg的组件,在项目中的 components文件夹下的 v-svg-icon 中的文件,已经提供给大家,这里我就不详细展开了。

  • 图片压缩

webpack中一般使用 image-webpack-loader 处理PNG、JPG图片压缩,接下来就重点说下如何使用插件进行图片压缩

安装插件

yarn add image-webpack-loader --dev

在vue.config.js中配置loader

module.exports = {
  chainWebpack: (config) => {
      config.module
      .rule('images')
      .use('image-webpack-loader')
      .loader('image-webpack-loader')
      .options({ bypassOnDebug: true })
      .end()
  }
}
  • cdn加载包文件

首先,我们要了解,为什么要使用cdn加载包文件?

默认情况下,通过import语法导入的第三方依赖包,最终会被打包到同一个文件中,从而导致打包成功后,单文件体积过大的问题。为了解决上述问题,可以通过webpack的externals节点,配置并加载依赖包的cdn资源。

const isProduction = process.env.VUE_APP_GLOBAL_ENV !== 'development'

module.exports = {
  configureWebpack: (config) => {
    if (isProduction) {
      config.externals = { vant: 'vant' }
    }
  }
}

如果只是这样,那么怎么加载cdn的依赖包呢?

为了方便统一配置,我们统一管理js、css文件,并使用webpack的链式调用;

const cdn = {
  css: ['//cdn.jsdelivr.net/npm/vant@2.12/lib/index.css'],
  js: [
    '//cdn.jsdelivr.net/npm/vant@2.12/lib/vant.min.js'
  ]
}
const isProduction = process.env.VUE_APP_GLOBAL_ENV !== 'development'

module.exports = {
  chainWebpack: (config) => {
    if (isProduction) {
      config.plugin('html').tap((args) => {
        args[0].title = 'piece'
        // html中添加cdn
        args[0].cdn = cdn
        return args
      })
    }
  }
}

承载文件index.html中引入;


<html>
<head>
  <!-- 使用CDN的CSS文件 -->
  <% for (var i in htmlWebpackPlugin.options.cdn && htmlWebpackPlugin.options.cdn.css) { %>
    <link rel="stylesheet" href="<%= htmlWebpackPlugin.options.cdn.css[i] %>" />
    <% } %>
</head>
<body>
  ...
</body>
<!-- 使用CDN的JS文件 -->
<% for (var i in htmlWebpackPlugin.options.cdn && htmlWebpackPlugin.options.cdn.js) { %>
  <script type="text/javascript" src="<%= htmlWebpackPlugin.options.cdn.js[i] %>"></script>
  <% } %>
</html>
  • 去除console.log

这个其实没什么好说的,直接上配置代码;关于uglifyjs-webpack-plugin插件的配置字段,大家看下看下webpack的中文文档 uglifyjs-webpack-plugin

const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
const isProduction = process.env.VUE_APP_GLOBAL_ENV !== 'development'
module.exports = {
  configureWebpack: (config) => {
     if (isProduction) {
      config.plugins.push(
        new UglifyJsPlugin({
          uglifyOptions: {
            output: {
              comments: false // 去掉注释
            },
            compress: {
              drop_console: true,
              drop_debugger: false,
              pure_funcs: [
                'console.log',
                'console.warn',
                'console.info',
                'console.error',
                'console.debug'
              ] // 移除console信息打印
            },
            warnings: false
          },
          sourceMap: false,
          parallel: true
        })
      )
    }
  }
}
  • sass全局样式

在vue-cli中全局使用scss的全局变量或者全局样式,需要安装插件,然后再vue.conf.js中配置;一般使用的插件是 style-resources-loader;但是需要制定sass-loader和style-resources-loader的版本,否则vue-cli会出现一些问题,本次我们先从简单的角度切入,在main.ts中直接引入全局样式文件;

import '../src/styles/_module.scss'

这里@各位知友,有谁可以通过webpack的方式,实现全局样式文件的加载,不妨留个言呀。

  • prerender-spa-plugin 预渲染开启

众所周知,使用vue-cli打包项目一般为SPA项目,不利于SEO,目前有服务端渲染SSR和预渲染两种处理方案;这里我们只讨论预渲染的方案;

首先,安装插件

yarn add prerender-spa-plugin --dev

其次,配置插件

const PrerenderSPAPlugin = require('prerender-spa-plugin')
const isProduction = process.env.VUE_APP_GLOBAL_ENV !== 'development'

module.exports = {
    if (isProduction) {
      config.plugins.push(
        new PrerenderSPAPlugin({
          staticDir: join(__dirname, 'dist'),
          // 对应自己的路由文件
          routes: ['/home'],
          // 忽略重定向redirects
          postProcess(renderedRoute) {
            renderedRoute.route = renderedRoute.originalRoute
            return renderedRoute
          },
          renderer: new PrerenderSPAPlugin.PuppeteerRenderer({
            inject: {
              foo: 'bar'
            },
            headless: false,
            // 在 main.js 中,或者需要预处理的路由文件中, document.dispatchEvent(new Event('render-event')),两者的事件名称要对应上。
            renderAfterDocumentEvent: 'render-event'
          })
        })
      )
    }
}

这样,成功执行build之后,就可以看到dist文件夹下,多了home这个文件夹,并且在此文件夹下有.html文件。

  • 骨架屏

目前基于Vue3版本的骨架屏方案还未有相关插件支撑,应用较广的vue-skeleton-webpack-plugin插件目前也未支持,所以,暂时还是考虑自己实现,功能简单,开发工作量较大,这个问题智能等到后面优化了。

首先,我们引入一个组件:vue-skeletor,实现骨架屏元素的基本功能

yarn add vue-skeletor --dev

然后在对应的页面下,添加骨架屏组件

编辑切换为居中

添加图片注释,不超过 140 字(可选)

在当前组件中,引入骨架屏元素

  import 'vue-skeletor/dist/vue-skeletor.css'
  import { Skeletor } from 'vue-skeletor'

具体代码,参照组件内编写,我们看最终实现的效果,这样就完成啦~,是不是非常简单

编辑

添加图片注释,不超过 140 字(可选)

\

  • 添加打包分析report

配合webpack和插件webpack-bundle-analyzer使用,生成基于vue-cli的代码分析报告,帮助提升代码质量和网站性能。

使用方案也比较简单,安装插件后,在vue.config.js中配置。

安装插件

yarn add webpack-bundle-analyzer --dev

在vue.config.js中配置

module.exports = {
  chainWebpack: (config) => {
    if (isProduction) {
      config
        .plugin('webpack-bundle-analyzer')
        .use(require('webpack-bundle-analyzer').BundleAnalyzerPlugin)
    }
  }
}

配置按成后,执行build命令,浏览器会默认打开以下页面,可以清晰的分析打包后的文件体积及组成,方便进行加载速度优化。

编辑切换为居中

添加图片注释,不超过 140 字(可选)

设计到多个配置文件,完成的vue.config.js文件我就不把代码贴出来了,感兴趣的知友可以到项目 中查看。还有哪些优化的措施或者方案,可以在评论区讨论或者私信留言哦~

结束语

欢迎大家多提意见,赠人玫瑰,手有余香!

\