博客

  • 小优化

    优化了一下版心宽度修改的逻辑,原来是修改全部带有某个类名的dom的行内样式,现在用一个css变量作为版心宽度,直接修改这个css变量就行了,方便很多(应该还有更好的方法,暂时想不出来了)

  • Vue打包优化

    打包优化

    1.异步组件,路由按需加载 (解决首屏加载过慢)

    component: () => import('@/views/Home/index')

    2.排除一些依赖包,将依赖包(不太会变化的包,如Vue、Element UI等)存在cdn上,可以加快打包速度,也可以让不同地域的用户在请求资源时得到更快的响应

    webpack排除打包

    找到 vue.config.js,在configureWebpack配置项中添加externals 选择填入不打包的依赖包

    configureWebpack: {
      ...
      externals: {
        // key(要排除的包名), value(引入的CDN包的全局变量名)
        'vue': 'Vue',
        'element-ui': 'ELEMENT',
        ...
      },
    }

    此时这些包已经被排除打包了,因为缺少这些包,项目无法正常运行,接下来还需要配置CDN

    vue.config.js

    const cdn = {
      css: [
        'https://unpkg.com/element-ui/lib/theme-chalk/index.css' // element-ui css 样式表
      ],
      js: [
        // vue must at first!
        'https://unpkg.com/vue/dist/vue.js', // vuejs
        'https://unpkg.com/element-ui/lib/index.js', // element-ui js
        ...
      ]
    }

    开发环境没有必要使用CDN来引入包,可以通过环境变量来判断是否使用CDN

    const isProduction = process.env.NODE_ENV === 'production' // 判断是否是生产环境
    
    let externals = {}
    let cdn = { css: [], js: [] }
    
    if (isProduction) {
      externals = {
        // key(要排除的包名), value(引入的CDN包的全局变量名)
        'vue': 'Vue',
        'element-ui': 'ELEMENT',
        ...
      }
      cdn = {
        css: [
          'https://unpkg.com/element-ui/lib/theme-chalk/index.css' // element-ui css 样式表
        ],
        js: [
          // vue must at first!
          'https://unpkg.com/vue/dist/vue.js', // vuejs
          'https://unpkg.com/element-ui/lib/index.js', // element-ui js
          ...
        ]
      }
    } 
    
    
    ···
    // 使用externals
    module.exports = {
      configureWebpack: {
      ...
      externals: externals,
     }
    }

    将CDN文件链接注入到模板

    https://cli.vuejs.org/zh/guide/webpack.html#修改插件选项

    chainWebpack(config) {
      // it can improve the speed of the first screen, it is recommended to turn on preload
      config.plugin('preload').tap(() => [
        {
          rel: 'preload',
          // to ignore runtime.js
          // https://github.com/vuejs/vue-cli/blob/dev/packages/@vue/cli-service/lib/config/app.js#L171
          fileBlacklist: [/\.map$/, /hot-update\.js$/, /runtime\..*\.js$/],
          include: 'initial'
        }
      ])
    
      // 注入cdn变量 (打包时会执行)   
      //https://cli.vuejs.org/zh/guide/webpack.html#修改插件选项
      config.plugin('html').tap(args => {
        args[0].cdn = cdn // 配置cdn给插件
        return args
      })
      ...
    }

    找到 public/index.html

    <head>
      <!-- 引入CSS -->
      <% for(var css of htmlWebpackPlugin.options.cdn.css) { %>
        <link rel="stylesheet" href="<%=css%>">
      <% } %>
    </head>
    
    <!-- 引入JS -->
    <% for(var js of htmlWebpackPlugin.options.cdn.js) { %>
      <script src="<%=js%>"></script>
    <% } %>

    打包

    yarn build
  • ElementUI配合Vue-I18n实现多语言切换

    安装

    npm i vue-i18n element-ui

    引入

    https://element.eleme.cn/#/zh-CN/component/i18n

    src目录下新建lang/index.js

    import Vue from 'vue'
    import VueI18n from 'vue-i18n' // 引入国际化的插件包
    import elementEN from 'element-ui/lib/locale/lang/en' // 引入饿了么的英文包
    import elementZH from 'element-ui/lib/locale/lang/zh-CN' // 引入饿了么的中文包
    import customZH from './zh' // 引入自定义中文包
    import customEN from './en' // 引入自定义英文包
    
    Vue.use(VueI18n)
    
    const messages = {
      en: {
        message: 'hello',
        ...enLocale // 或者用 Object.assign({ message: 'hello' }, enLocale)
      },
      zh: {
        message: '你好',
        ...zhLocale // 或者用 Object.assign({ message: '你好' }, zhLocale)
      }
    }
    
    export default new VueI18n({
      locale: 'en', // set locale
      messages, // set locale messages
    })

    main.js

    ···
    // 配置ElementUI的中英切换逻辑
    Vue.use(ElementUI, {
      i18n: (key, value) => i18n.t(key, value)
    })
    
    new Vue({
      el: '#app',
      ···
      i18n,
      render: h => h(App)
    })

    使用

    当我们全局注册i18n的时候,每个组件都会拥有一个$t的方法, $t 方法会被挂载到 vue 原型上

    它会根据传入的key,自动的去寻找当前语言的文本, $t('title')

    <div>
      {{ $t('title') }}
      <span>{{ $t('hello') }}</span>
    </div>

    语言切换

    this.$i18n.locale = 'en'

    Vue I18n https://kazupon.github.io/vue-i18n/zh/

  • 利用vue-print-nb实现打印功能

    安装

    Vue2

    npm install vue-print-nb --save

    Vue3

    npm install vue3-print-nb --save

    引入

    import Print from 'vue-print-nb'
    Vue.use(Print)

    给要打印的盒子指定 id

    <div id="print">

    使用v-print指令的方式进行打印

    <button v-print="{ id: 'printbox' }">打印</button>

    注意点

    • Callback函数中this指向当前print object对象,that返回Vue对象
    • 不需要页眉页脚可以在打印弹窗页面的更多设置里面取消选择
    • 不设置popTitle参数页眉标题为undifined
    • popTitle参数为空时,页眉标题默认为Document Title

    利用vue-print-nb实现打印功能

    vue-print-nb – npm

  • Vue使用NProgress

    NProgress是页面跳转是出现在浏览器顶部的进度条
    官网:http://ricostacruz.com/nprogress/
    github:https://github.com/rstacruz/nprogress

    安装

    npm install --save nprogress 或者
    yarn add nprogress
    
    //用法
    NProgress.start();
    NProgress.done();

    使用

    //导入
    import NProgress from 'nprogress'
    import 'nprogress/nprogress.css'
    
    router.beforeEach((to, from, next) => {
      NProgress.start()
      next()
    })
    
    router.afterEach(() => {
      NProgress.done()
    })

    注意

    ···
    next('/login')
    NProgress.done()
    ···

    被next拦截走的, 不会自动关闭, 因为没有进入到 afterEach, 需要手动关闭

    Vue使用NProgress

  • Vue虚拟DOM与diff算法

    就地复用

    Vue会尽可能的就地(同层级,同位置),对比虚拟dom,复用旧dom结构,进行差异化更新。

    虚拟DOM

    真实DOM

    • HTML渲染出来的DOM树
    • 每个标签都是树的节点
    • 每个节点拥有非常多的属性 由于真实DOM的属性过多,导致对比差异会消耗过多的性能,且有很多无需遍历对比的属性,这样效率很低。

    虚拟DOM

    本质是JS对象,保存渲染真实DOM时的必要属性。

    <template>
    <div id="app">
      <h1 class="title">标题</h1>
    </div>
    </template>
    
    <script>
     const virtualDOM = {
       type:'div',
       attributes:[{id:'app'}],
       children:[{
         type:'h1',
         attributes:[{class:"title"}],
         text:'标题'
       }]
     }
    </script>
    渲染过程
    1. template渲染成虚拟DOM
    2. 根据虚拟DOM渲染成真实的DOM
    数据发生更新
    1. 重新生成一份新的虚拟DOM
    2. 对比新的虚拟DOM和旧的虚拟DOM,找到不同的地方
    3. 最后统一更新真实的DOM
    虚拟DOM的作用

    可以在内存中创建虚拟DOM,快速比较变化的部分, 然后给真实DOM打补丁(更新)

    虚拟DOM总结

    虚拟DOM相比真实DOM只保留了渲染时必要的属性,可以更高效地对比DOM新旧差异,进行差异化更新。

    diff算法

    虚拟DOM内部的比对过程称为diff算法

    策略一

    先同层级根元素比较,如果根元素变化,那么不考虑复用,整个dom树删除重建

    策略二

    对比同级兄弟元素时,默认按照下标进行对比复用。如果数组顺序没有发生改变,性能是高效的。

    如果数组的顺序发生了变化,性能并不高

    策略三

    同级兄弟元素,在设置了key后,会让相同key的元素进行对比。

    key必须是字符串或者数字,且必须唯一

    (尽量不用遍历的索引作为key,参考策略二中数组的顺序发生变化)

    总结

    • 设置了key在DOM更新时会按照相同的key进行新旧元素比较(不设置则默认为索引)
    • 目的:提高更新时的性能
    • key必须是字符串或者数字,且必须唯一
  • 初识Promise

    目的: Promise 是书写异步代码的另一种方式, 解决回调函数嵌套的问题(回调地狱)

    基本语法

    创建Promise对象

    new Promise的时候会执行传入的函数

    //Promise对象传入一个函数,函数提供两个函数参数resolve、reject
    const promise = new Promise((resolve, reject) => {
      // 内部封装一个异步操作
    
      // 成功则调用resolve
    
      // 失败调用reject
    })

    使用

    promise.then((res) => { ... }).catch((err) => { ... })

    Promise是一个构造函数

    Promise.prototype上包含then()catch()方法

    通过then方法来指定成功的回调函数

    通过catch方法来指定失败的回调函数

    Promise的三种状态

    • pending: 待定(等待结果
    • fulfilled: 成功 (已兑现), 调用了 resolve, Promise的状态就会被标记成成功
    • rejected: 失败 (已拒绝), 调用了 reject, Promise的状态就会被标记成失败

    一旦Promise的状态发生变化,状态就不会再改变了!就像他的名字Promise一样,承诺了就不会改变。

    Promise链式调用

    解决回调地狱的问题

    因为 Promise.prototype.thenPromise.prototype.catch 方法返回的是 promise,只要是 promise 对象都有这两个方法,所以它们可以被链式调用。

    import fs from 'then-fs'
    
    fs.readFile('1.txt', 'utf-8').then(res => {
      console.log('读取成功:' + res);
      return fs.readFile('2.txt', 'utf-8')
    }).then((res) => {
      console.log('读取成功:' + res);
      return fs.readFile('3.txt', 'utf-8')
    }).then((res) => {
      console.log('读取成功:' + res);
    }).catch(err => {
      console.log('读取失败:' + err);
    })