18条常用好用的Vue项目代码和性能优化方案整理

440 阅读5分钟

整理了18条React的组件性能优化方案,开发React的可以去看看:

18条React的组件性能优化整理 |求关注😘,求点赞😘,鼓励一下😘

整理了18条Vue开发常用的项目代码和性能优化方案,后期会不断更新添加。

1、v-if和v-show

区别:v-if为false时不渲染DOM,v-show渲染DOM
  • 权限相关的展示用v-if
  • 其他频繁切换用v-show,不频繁切换用v-if
  • 使用v-if可以减少DOM数量,加快页面渲染
  • 使用三木运算符、&&或者||替代v-if
  • v-if里面的表达式与判断,建议在methodscomputed里面封装成一个方法,方便复用
  • 错误优先出return
if (!valid) {
    this.$message.error('信息输入有误');
    return;
}

2、v-for和v-if

  • 不要在v-for中使用v-if,因为v-for的优先级高,每一次都需要遍历整个数组,影响渲染速度,如需要渲染部分,替换成computed属性或者提前js处理好
  • v-for遍历必须为item添加key,key方便vue.js内部机制精准找到该条列表数据
  • key避免使用index作为标识符,原因:逆序添加、逆序删除等破坏顺序的操作,会产生没有必要的真实DOM更新

3、使用解构赋值以及函数默认值

function add([x, y]){ return x + y; }
add([1, 2]); // 3

[[1, 2], [3, 4]].map(([a, b]) => a + b);
// [ 3, 7 ]

function move({x = 0, y = 0} = {}) { return [x, y]; }
move({x: 3, y: 8}); // [3, 8]
move({x: 3}); // [3, 0]
move({}); // [0, 0]
move(); // [0, 0]

[1, undefined, 3].map((x = 'yes') => x);
// [ 1, 'yes', 3 ]

4、多使用计算属性

  • 计算属性可以依赖多个数据,任何一个数据变动,计算都会重新执行
  • 计算属性可以依赖其他计算属性
  • 计算属性不仅可以依赖当前vue实例,还可以依赖其他实例的数据
  • 计算属性不能调用,不能传参数; 方法需要调用,可以传参
  • 计算属性默认会缓存计算结果; 方法没有缓存
  • 基于原有的data,prop,computed等数据派生出别的数据
computed: {
    reversedText: function () {
        //这里的this指向的是当前的vue实例
        //如果这两的text是后台接受的数据,最好加个判断数据为不为空
        return this.text.split(',').reverse().join(',');  
    }
}

5、合理利用Vue生命周期

  • 减少在created生命周期中做ajax请求,而是放在mounted生命周期中,以便不阻塞页面生成dom

6、提取公共样式、公共js和公共表单验证

  • 复用的样式应当提取到公共的样式文件中复用
  • 公共的方法应当提取到公共的js中复用
  • 可复用表单验证的提取到公共文件中

7、长列表性能优化-冻结数据

Vue会通过object.defineProperty对数据进行劫持,来实现视图响应数据的变化

Object.defineProperty(obj,’b’,{get(){return 123}})

有时组件就是纯粹的数据展示,不会有变化,为了减少组件初始化,我们可以通过object.freeze方法来冻结一个对象,vue就不会对数据进行劫持了

Object.freeze(users) //冻结就不能进行get和set转换

8、优化无限列表性能

9、释放组件资源

  • Vue组件销毁时,会自动清理它与其他实例的链接,解绑它的全部指令及事件监听器,但是仅限于组件本身的事件。
  • 如果在js内使用了addEventListener,定时器等方式时不会自动销毁的,组件销毁时要手动移除,避免内存泄露。
created(){
    addEventListener(‘click’,this.click,false)
}
beforeDestroy(){
    removeEventListener(‘click’,this.click,false)
}

10、图片资源优化

  • 小图标可使用SVG或者字体图标
  • 小图标可制作雪碧图,多张图合并到一张,只需一次请求,可以减小建立连接的消耗
  • 通过base64和webp的方式加载小型图片
  • 能通过cdn加速的大图尽量用cdn
  • 使用懒加载
  • 压缩图片大小 ,纯静态的图片,可以统一压缩,无损压缩 tinpng压缩 Webpack不建议加图片压缩,严重影响打包速度 动态图片建议后端压缩,付费云服务

11、图片资源懒加载

安装
$ npm install vue-lazyload  —save-dev
在main.js引入
import VueLazyLoad from ‘vue-lazyload’

Vue.use(VueLazyload, {
  preLoad: 1.3,
  error: 'dist/error.png',
  loading: 'dist/loading.gif',
  attempt: 1
})
页面使用
//图片懒加载
<img v-lazy="img" v-for="img in list" :key="img">
//背景图懒加载
<div v-lazy:background-image="img" ></div>

详细介绍请查看官方链接 ue-virtual-scroller

12、 路由懒加载

  • webpack内部构建时,会把这种包单独解析成一个js文件
  • 注意:懒加载的路由太多的话会导致打包性能下降
  • 两种方式:
{
    path: '/home',
    component: () => import(/* webpackChunkName: "Home" */ 'pages/home/index.vue');
    //component: resolve => (require['pages/home/index.vue'],resolve);
    meta: {
        name: '首页',
    }
}

路由懒加载就是异步组件,异步组件不宜太多,太多有打包性能问题。

普通组件也可以用异步组件:
components:{
    Home:()=>import('@/component/about.vue')
}

13、按需引入、按需加载

  • 第三方插件按需引入
  • element-UI 按需引入
  • ant-design-vue 按需引入
  • lodash 按需引入
import debounce from ‘lodash/debounce’

14、上线打包不生成.map文件

.map文件的作用是帮助编译后的代码调试,占用空间较大,所以上线时可以不生成.map文件,否则造成源码泄漏,在vue.config.js文件

productionSourceMap: false, //是否在构建生产包时生成 sourceMap 文件,

15、生产环境打包压缩Gzip

  • vue-cli3.0和vue-cli4.0是没有productionGzip配置的,需要使用compression-webpack-plugin来实现生产环境打包压缩Gzip
  • 使用compression-webpack-plugin步骤:
$ npm i -D compression-webpack-plugin
在vue.config.js 文件中配置
const compressionWebpackPlugin = require('compression-webpack-plugin'); //引入插件
const productionGZipExtensions = ['js', 'css']; //压缩的文件类型
module.exports = {
    //简单方式
    configureWebpack: {
      plugins: [
        new compressionWebpackPlugin({
          //[file] 会被替换成原始资源。[path] 会被替换成原始资源的路径, [query] 会被替换成查询字符串。默认值是 "[path].gz[query]"
          asset: '[path].gz[query]', // 提示compression-webpack-plugin@3.0.0的话asset改为filename
          algorithm: 'gzip',
          //所有匹配该正则的资源都会被处理。默认值是全部资源
          test: new RegExp('\\.(' + productionGzipExtensions.join('|') + ')$'),
          //大小大于该值的资源会被处理。单位是 bytes。默认值是 0
          threshold: 10240,
          //压缩率小于这个值的资源才会被处理,默认值是 0.8
          minRatio: 0.8
        })
      ] 
    },
    // 高级的方式
    // configureWebpack: config => {
        // if (process.env.NODE_ENV === 'production') {
            // 生产环境
           // config.plugins.push(
              //  new CompressionWebpackPlugin({
              //  })
          //  );

       // } else {
            // 开发环境
        //}
    // },
}
服务器端开启Gzip,NGINX配置
server{
    gzip on;
    gzip_buffers 32 4K;
    gzip_comp_level 6;
    gzip_min_length 100;
    gzip_types application/javascript text/css text/xml;
    gzip_disable "MSIE [1-6]\."; #配置禁用gzip条件,支持正则。此处表示ie6及以下不启用gzip(因为ie低版本不支持)
    gzip_vary on;
}
  • 运行npm run build打包上线项目,检测:打开Chrome控制台,可以看到Network下的Response Headers中返回了Content-Encoding: gzip,表明gzip开启成功。而Request Headers里面的Accept-Encoding: gzip只是表示前端(用户浏览器)支持gzip的压缩方式

16、首屏渲染优化

  1. 路由懒加载
  2. 在首页加入loading动画,骨架屏
  3. 预渲染,数据不太变动的,直接预渲染为静态页面
  4. 服务器渲染,如果仅仅首屏渲染慢,不建议用,如果要解决SEO,用

17、预渲染

服务端渲染与预渲染区别
  • 客户端渲染:用户访问 url,请求 html 文件,前端根据路由动态渲染页面内容。关键链路较长,有一定的白屏时间;
  • 服务端渲染:用户访问 url,服务端根据访问路径请求所需数据,拼接成 html 字符串,返回给前端。前端接收到 html 时已有部分内容;
  • 预渲染:构建阶段生成匹配预渲染路径的 html 文件(注意:每个需要预渲染的路由都有一个对应的 html)。构建出来的 html 文件已有部分内容
服务端渲染与预渲染共同点,针对单页应用,服务端渲染和预渲染共同解决的问题:
  • SEO:单页应用的网站内容是根据当前路径动态渲染的,html 文件中往往没有内容,网络爬虫不会等到页面脚本执行完再抓取;
  • 弱网环境:当用户在一个弱环境中访问你的站点时,你会想要尽可能快的将内容呈现给他们。甚至是在 js 脚本被加载和解析前;
  • 低版本浏览器:用户的浏览器可能不支持你使用的 js 特性,预渲染或服务端渲染能够让用户至少能够看到首屏的内容,而不是一个空白的网页。

预渲染实现

//puppeteer 二进制包镜像
$ npm config set puppeteer_download_host=https://npm.taobao.org/mirrors 
$ npm i puppeteer
$ npm i prerender-spa-plugin --save -dev
确保路由模式是history
const router = new Router({
    mode: 'history', // 预渲染一定要模式改成history
    routes: routes
})
在vue.config.js配置

这里 publicPath得注释掉,否则打包后图片和资源链接会失效

const PrerenderSPAPlugin = require('prerender-spa-plugin');
const Renderer = PrerenderSPAPlugin.PuppeteerRenderer;
configureWebpack: {
     plugins: [
        new PrerenderSPAPlugin({
            // 生成文件的路径,也可以与webpakc打包的一致。
            // 下面这句话非常重要!!!
            // 这个目录只能有一级,如果目录层次大于一级,在生成的时候不会有任何错误提示,在预渲染的时候只会卡着不动。
            staticDir: path.join(__dirname,'dist'),
            // 对应自己的路由文件,比如a有参数,就需要写成 /a/param1。
            routes: ['/', '/about'],
            // 这个很重要,如果没有配置这段,也不会进行预编译
            renderer: new Renderer({
                inject: { foo: 'bar' },
                headless: false,
                // 在 main.js 中 document.dispatchEvent(new Event('render-event')),两者的事件名称要对应上。
                renderAfterDocumentEvent: 'render-event',
                renderAfterTime: 10000,//超时时间
                timeout: 0,
                maxConcurrentRoutes: 20,//打包页面的最大数
                navigationParams: {
                     timeout: 0
                }
            })
        }),
    ]
}
使用vue-meta-info插件,改变link、title、meta等
$ npm i vue-meta-info
//main.js
import MetaInfo from 'vue-meta-info'
Vue.use(MetaInfo);

new Vue({
    router,
    render: h => h(App),
    //这句非常重要,否则预渲染
    mounted () {
        document.dispatchEvent(new Event('render-event'))将不会启动
    }
}).$mount("#app");
//路由页面
export default {
    metaInfo: {
        //改变当前路由的title
        title: 'title',
        //改变当前路由的link
        link: [
            {
              rel: 'baidu',
              href: 'https://www.22.com/'
            },
        ],
    },
    data(){
        return{}
    }
}

正确配置完,刷新后404,部署到服务器就可以了 涉及到发送请求,配置一个serve代理 prerender-spa-pluginvue-meta-info

18、大型项目配置多入口多出口模式

  • 单页面的工程,main.js是整个项目唯一的入口,整个项目都在一个index.html外壳中。
  • 在大型项目中,单页面易负载过重,频繁切换业务模块可能会造成浏览器内存持续上涨、页面卡顿崩溃等。
  • 基于以上原因,可以采用多入口的模式,满足页面需要分开部署的场景,解决了页面卡顿和内存上涨问题。