美文网首页跨平台
vue.js了解篇1(下)

vue.js了解篇1(下)

作者: 平安喜乐698 | 来源:发表于2019-12-25 18:30 被阅读0次
目录
  1. 生命周期
  2. 响应式原理
  3. Vue文件
  4. 生产环境
  5. TypeScript 支持
  6. 单元测试

1. 生命周期

每个Vue实例在被创建时都要经过一系列的初始化过程(如:设置数据监听、编译模板、将实例挂载到 DOM 并在数据变化时更新 DOM 等),在不同阶段会进行相应的回调。

new Vue({
  data: {
    a: 1
  },
  beforeCreate(){    // beforeCreate() 等价于 beforeCreate: function ()
  },
  created() {    // 初始化data和methods属性完毕
    // `this` 指向 vm 实例
    console.log('a is: ' + this.a)
    this.show();
  },
  beforeMount() {
    // <p id="testP">{{msg}}</p>
    // 打印{{msg}},此时还没编译
    console.log(document.getElementById('testP').innerText);
  },
  mounted(){
  },
  beforeUpdate() {
  },
  updated(){
  },
  // 其他方法
  methods: {
    show(){
      console.log('执行了show方法');
    }
  },
})
Vue生命周期
beforeCreate
    在内存中创建Vue实例,并初始化默认事件和生命周期函数。
    【尚未初始化data和methods属性。】
created
    初始化data和methods属性完毕。
    【尚未开始编译模板。】

beforeMount
    编译模板完毕。编译代码,执行Vue指令生成最终的模版。
    【尚未挂载到页面中。】
mounted
    挂载到页面指定的容器中进行显示。(通过插件修改DOM节点时,最早在这里可以修改)

beforeUpdate:
    当有数据改变后调用,此时 data中的数据值是最新的,界面上显示的仍是旧数据。(因为此时还没有开始重新渲染DOM节点。)
update
    界面渲染完毕(界面上显示的数据和data一致)。

beforeDestroy
    实例销毁之前调用 (实例还可用:data、methods、过滤器等)。
destroyed
    实例销毁之后调用(此时,Vue实例指示的所有东西都会被解除绑定,所有事件监听器会被移除,所有子实例被销毁。)

2. 响应式原理

var vm = new Vue({
  data:{
    a:1,
    // 声明 message 为一个空值字符串
    message: '',
    someObject:{
    },
  }
})

说明:
1. 当传递一个普通的js对象给Vue实例的data时,Vue会遍历该对象的所有属性,并使用Object.defineProperty把这些属性全部转为getter/setter(这两个方法内部通过操作DOM让Vue能够追踪依赖,在属性被访问和修改时通知变更)。
2. Object.defineProperty (ES5中一个无法shim的特性:Vue不支持IE8以及更低版本浏览器的原因)生成的这些 getter/setter 并不可见。
3. 不同浏览器在控制台打印数据对象时对 getter/setter 的格式化并不同,所以建议安装 vue-devtools 可以更方便调试。
4. 每个组件实例都对应一个watcher实例,它会在组件渲染的过程中把“接触”过的数据属性记录为依赖。之后当依赖项的 setter 触发时,会通知 watcher,从而使它关联的组件重新渲染。
5. 由于 Vue 不允许动态添加根级响应式属性,所以必须在初始化实例前声明所有根级响应式属性,哪怕只是一个空值。
      原因:受现代 JavaScript 的限制 (Object.observe被废弃),Vue 无法检测到对象属性的添加或删除。由于 Vue 会在初始化实例时对属性执行 getter/setter 转化,所以属性必须在 data 对象上存在才能让 Vue 将它转换为响应式的。
6. Vue 在更新 DOM 时是异步执行,当修改了数据后组件并不会立即重新渲染。
      当监听到数据变化时,Vue将开启一个队列缓存该数据变更,如果同一个watcher被多次触发,只会被推入到队列中一次(避免不必要的计算和 DOM 操作)。在某个时刻Vue会处理该队列中的所有数据变更。
      Vue 在内部对异步队列尝试使用原生的 Promise.then、MutationObserver 和 setImmediate,如果执行环境不支持,则会采用 setTimeout(fn, 0) 代替。
为data中某对象类型属性添加响应式属性。
  Vue.set(vm.someObject, 'b', 2)
  this.$set(this.someObject,'b',2)

Object.assign() 或 _.extend() 给对象类型属性添加多个属性时不会触发更新。解决:
// 代替 `Object.assign(this.someObject, { a: 1, b: 2 })`
this.someObject = Object.assign({}, this.someObject, { a: 1, b: 2 })


// `vm.a` 是响应式的
// `vm.b` 是非响应式的
vm.b = 2
例(异步更新)

    <div id="example">{{message}}</div>

    var vm = new Vue({
      el: '#example',
      data: {
        message: '123'
      }
    })
    vm.message = 'new message' // 更改数据
    vm.$el.textContent === 'new message' // false
    Vue.nextTick(function () {    // 在 DOM更新完成后被调用
      vm.$el.textContent === 'new message' // true
    })
例2(异步更新)组件

Vue.component('example', {
  template: '<span>{{ message }}</span>',
  data: function () {
    return {
      message: '未更新'
    }
  },
  methods: {
    updateMessage: function () {
      this.message = '已更新'
      console.log(this.$el.textContent) // => '未更新'
      this.$nextTick(function () {  
        // this 将自动绑定到当前的 Vue 实例
        console.log(this.$el.textContent) // => '已更新'
      })
    }
  }
})


 ES2017 async/await 格式:

methods: {
  updateMessage: async function () {
    this.message = '已更新'
    console.log(this.$el.textContent) // => '未更新'
    await this.$nextTick()
    console.log(this.$el.textContent) // => '已更新'
  }
}

3. Vue文件

在大多数Vue项目中可以使用 Vue.component来定义全局组件,紧接着用 new Vue({ el: '#container '}) 在每个页面内指定一个容器元素。

存在缺陷:
  1. 全局定义强制要求了每个component中的命名不得重复
  2. 字符串模板缺乏语法高亮,在 HTML 有多行的时候,需要用到丑陋的 \
  3. 不支持CSS意味着当 HTML 和 JavaScript 组件化时,CSS 明显被遗漏
  4. 没有构建步骤限制只能使用 HTML 和 ES5 JavaScript, 而不能使用预处理器,如 Pug (formerly Jade) 和 Babel

解决:
  文件扩展名为 .vue 的单文件组件解决了以上所有问题,并且还可以使用 webpack 或 Browserify 等构建工具。
示例(App.vue)

<template>
  <div id="app">
    <img src="./assets/logo.png">
    <router-view/>
  </div>
</template>

<script>
export default {
  name: 'App'
}
</script>

<style>
#app {
  font-family: 'Avenir', Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>
关注点分离不等于文件类型分离

<!-- my-component.vue -->
<template>
  <div>This will be pre-compiled</div>
</template>
<script src="./my-component.js"></script>
<style src="./my-component.css"></style>

4. 生产环境

在开发环境下,Vue 会提供很多警告语句来解决常见的错误。
在生产环境下,警告语句没用反而会增加应用的体积,有些警告检查存在一些小的运行时开销。

没使用构建工具

如果使用的是 Vue完整独立版本,即直接用 <script> 元素引入 Vue 而不必提前进行构建。
在生产环境下要改用压缩后的版本 (vue.min.js)。

使用构建工具

当使用 webpack 或 Browserify 类似的构建工具时,Vue 源码会根据 process.env.NODE_ENV 决定是否启用生产环境模式,默认情况为开发环境模式。在 webpack 与 Browserify 中都有方法来覆盖此变量,以启用 Vue 的生产环境模式,同时在构建过程中警告语句也会被压缩工具去除。所有这些在 vue-cli 模板中都预先配置好了。

webpack 4+

module.exports = {
  mode: 'production'
}

============

var webpack = require('webpack')
module.exports = {
  // ...
  plugins: [
    // ...
    new webpack.DefinePlugin({
      'process.env.NODE_ENV': JSON.stringify('production')
    })
  ]
}

Browserify

在运行打包命令时将 NODE_ENV 设置为 "production"。这等于告诉 vueify 避免引入热重载和开发相关的代码。

对打包后的文件进行一次全局的 envify 转换。这使得压缩工具能清除掉 Vue 源码中所有用环境变量条件包裹起来的警告语句。例如:
    NODE_ENV=production browserify -g envify -e main.js | uglifyjs -c -m > build.js
或者在 Gulp 中使用 envify

// 使用 envify 的自定义模块来定制环境变量
var envify = require('envify/custom')

browserify(browserifyOptions)
  .transform(vueify)
  .transform(
    // 必填项,以处理 node_modules 里的文件
    { global: true },
    envify({ NODE_ENV: 'production' })
  )
  .bundle()
或者配合 Grunt 和 grunt-browserify 使用 envify


// 使用 envify 自定义模块指定环境变量
var envify = require('envify/custom')

browserify: {
  dist: {
    options: {
        // 该函数用来调整 grunt-browserify 的默认指令
        configure: b => b
        .transform('vueify')
        .transform(
            // 用来处理 `node_modules` 文件
          { global: true },
          envify({ NODE_ENV: 'production' })
        )
        .bundle()
    }
  }
}

Rollup

const replace = require('@rollup/plugin-replace')
rollup({
  // ...
  plugins: [
    replace({
      'process.env.NODE_ENV': JSON.stringify( 'production' )
    })
  ]
}).then(...)
模版预编译

当使用 DOM 内模板或 JavaScript 内的字符串模板时,模板会在运行时被编译为渲染函数。对性能敏感的应用还是最好通过预编译 编译为渲染函数。

预编译模板最简单的方式就是使用单文件组件——相关的构建设置会自动把预编译处理好,所以构建好的代码已经包含了编译出来的渲染函数而不是原始的模板字符串。
提取组件的css

当使用单文件组件时,组件内的 CSS 会以 <style> 标签的方式通过 JavaScript 动态注入。这有一些小小的运行时开销,如果你使用服务端渲染,这会导致一段“无样式内容闪烁 (fouc)”。将所有组件的 CSS 提取到同一个文件可以避免这个问题,也会让 CSS 更好地进行压缩和缓存。
跟踪错误

如果在组件渲染时出现运行错误,错误将会被传递至全局 Vue.config.errorHandler 配置函数 (如果已设置)。

5. TypeScript 支持

Vue CLI 提供了内建的 TypeScript 工具支持。
静态类型系统能有效防止许多潜在的运行时错误。这就是为什么 Vue 不仅仅为 Vue core 提供了针对 TypeScript 的官方类型声明,还为 Vue Router 和 Vuex 也提供了相应的声明文件。

通过npm安装后,不再需要任何额外的工具辅助,就可以在Vue中使用 TypeScript了。

// tsconfig.json
{
  "compilerOptions": {
    // 与 Vue 的浏览器支持保持一致
    "target": "es5",
    // 这可以对 `this` 上的数据属性进行更严格的推断
    "strict": true,
    // 如果使用 webpack 2+ 或 rollup,可以利用 tree-shake:
    "module": "es2015",
    "moduleResolution": "node"
  }
}
注意:需要引入 strict: true (或者至少 noImplicitThis: true,这是 strict 模式的一部分) 以利用组件方法中 this 的类型检查,否则它会始终被看作 any 类型。

工程创建

Vue CLI 3 可以使用 TypeScript 生成新工程。创建方式:

# 1. 如果没有安装 Vue CLI 就先安装
npm install --global @vue/cli
# 2. 创建一个新工程,并选择 "Manually select features (手动选择特性)" 选项
vue create my-project-name

基本用法

要让 TypeScript 正确推断 Vue 组件选项中的类型,需要使用 Vue.component 或 Vue.extend 定义组件

import Vue from 'vue'
const Component = Vue.extend({
  // 类型推断已启用
})
const Component = {
  // 这里不会有类型推断,
  // 因为TypeScript不能确认这是Vue组件的选项
}

基于类的 Vue 组件

如果在声明组件时更喜欢基于类的 API,则可以使用官方维护的 vue-class-component 装饰器
import Vue from 'vue'
import Component from 'vue-class-component'

// @Component 修饰符注明了此类为一个 Vue 组件
@Component({
  // 所有的组件选项都可以放在这里
  template: '<button @click="onClick">Click!</button>'
})
export default class MyComponent extends Vue {
  // 初始数据可以直接声明为实例的属性
  message: string = 'Hello!'

  // 组件方法也可以直接声明为实例的方法
  onClick (): void {
    window.alert(this.message)
  }
}

增强类型以配合插件使用

插件可以增加 Vue 的全局/实例属性和组件选项。在这些情况下,在 TypeScript 中制作插件需要类型声明。
TypeScript 的模块补充特性可以补充现有的类型。
// 1. 确保在声明补充的类型之前导入 'vue'
import Vue from 'vue'

// 2. 定制一个文件,设置你想要补充的类型
//    在 types/vue.d.ts 里 Vue 有构造函数类型
declare module 'vue/types/vue' {
// 3. 声明为 Vue 补充的东西
  interface Vue {
    $myProperty: string
  }
}

declare module 'vue/types/vue' {
  // 可以使用 `VueConstructor` 接口
  // 来声明全局属性
  interface VueConstructor {
    $myGlobal: string
  }
}

// ComponentOptions 声明于 types/options.d.ts 之中
declare module 'vue/types/options' {
  interface ComponentOptions<V extends Vue> {
    myOption?: string
  }
}

========================


var vm = new Vue()
console.log(vm.$myProperty) // 将会顺利编译通过

// 全局属性
console.log(Vue.$myGlobal)

// 额外的组件选项
var vm = new Vue({
  myOption: 'Hello'
})

标注返回值

因为 Vue 的声明文件天生就具有循环性,TypeScript 可能在推断某个方法的类型的时候存在困难。因此,需要在 render 或 computed 里的方法上标注返回值。
如果发现类型推导或成员补齐不工作了,标注某个方法也许可以解决这个问题。
使用 --noImplicitAny 选项找到这些未标注的方法。
import Vue, { VNode } from 'vue'

const Component = Vue.extend({
  data () {
    return {
      msg: 'Hello'
    }
  },
  methods: {
    // 需要标注有 `this` 参与运算的返回值类型
    greet (): string {
      return this.msg + ' world'
    }
  },
  computed: {
    // 需要标注
    greeting(): string {
      return this.greet() + '!'
    }
  },
  // `createElement` 是可推导的,但是 `render` 需要返回值类型
  render (createElement): VNode {
    return createElement('div', this.greeting)
  }
})

6. 单元测试

Vue CLI 拥有开箱即用的通过 Jest 或 Mocha 进行单元测试的内置选项。
官方的 Vue Test Utils 提供更多详细的指引和自定义设置。

简单断言

不必为了可测性在组件中做任何特殊的操作,导出原始设置就可以了:

template>
  <span>{{ message }}</span>
</template>

<script>
  export default {
    data () {
      return {
        message: 'hello!'
      }
    },
    created () {
      this.message = 'bye!'
    }
  }
</script>

然后随着 Vue 导入组件的选项,可以使用许多常见的断言 (这里使用的是 Jasmine/Jest 风格的 expect 断言作为示例):

// 导入 Vue.js 和组件,进行测试
import Vue from 'vue'
import MyComponent from 'path/to/MyComponent.vue'

// 这里是一些 Jasmine 2.0 的测试,你也可以使用你喜欢的任何断言库或测试工具。
describe('MyComponent', () => {
  // 检查原始组件选项
  it('has a created hook', () => {
    expect(typeof MyComponent.created).toBe('function')
  })

  // 评估原始组件选项中的函数的结果
  it('sets the correct default data', () => {
    expect(typeof MyComponent.data).toBe('function')
    const defaultData = MyComponent.data()
    expect(defaultData.message).toBe('hello!')
  })

  // 检查 mount 中的组件实例
  it('correctly sets the message when created', () => {
    const vm = new Vue(MyComponent).$mount()
    expect(vm.message).toBe('bye!')
  })

  // 创建一个实例并检查渲染输出
  it('renders the correct message', () => {
    const Constructor = Vue.extend(MyComponent)
    const vm = new Constructor().$mount()
    expect(vm.$el.textContent).toBe('bye!')
  })
})

编写可被测试的组件

很多组件的渲染输出由它的 props 决定。事实上,如果一个组件的渲染输出完全取决于它的 props,那么它会让测试变得简单,就好像断言不同参数的纯函数的返回值。看下面这个例子:

<template>
  <p>{{ msg }}</p>
</template>

<script>
  export default {
    props: ['msg']
  }
</script>

可以在不同的 props 中,通过 propsData 选项断言它的渲染输出:

import Vue from 'vue'
import MyComponent from './MyComponent.vue'

// 挂载元素并返回已渲染的文本的工具函数
function getRenderedText (Component, propsData) {
  const Constructor = Vue.extend(Component)
  const vm = new Constructor({ propsData: propsData }).$mount()
  return vm.$el.textContent
}

describe('MyComponent', () => {
  it('renders correctly with different props', () => {
    expect(getRenderedText(MyComponent, {
      msg: 'Hello'
    })).toBe('Hello')

    expect(getRenderedText(MyComponent, {
      msg: 'Bye'
    })).toBe('Bye')
  })
})

由于Vue异步更新DOM,一些依赖DOM更新结果的断言必须在 Vue.nextTick 回调中进行。

// 在状态更新后检查生成的 HTML
it('updates the rendered message when vm.message updates', done => {
  const vm = new Vue(MyComponent).$mount()
  vm.message = 'foo'

  // 在状态改变后和断言 DOM 更新前等待一刻
  Vue.nextTick(() => {
    expect(vm.$el.textContent).toBe('foo')
    done()
  })
})

相关文章

  • vue.js了解篇1(下)

  • 【前端】Vue.js 基础篇(下)

    【前端】Vue.js 基础篇(下)

  • Vue 循环、点击、双向绑定

    一、了解 Vue.js 1 Vue.js是什么? Vue.js是一套用于构建用户界面的渐进式框架。Vue.js通过...

  • Vue-01:

    首先,今天我们来了解一些Vue.js: 1.>Vue.js是什么? Vue.js是一个用来开发web界面的前端库,...

  • Vue.js框架的理解

    面试官:聊聊对Vue.js框架的理解 分享目标: 了解 Vue.js 的组件化机制 了解 Vue.js 的响应式系...

  • vue 常用指令

    一、了解 Vue.js 1 Vue.js是什么?作者:尤雨溪Vue.js是一套用于构建用户界面的渐进式框架。V...

  • 了解一下 Vue.js

    一.开发规范 1. 页面存放目录:文件夹命名均首字母大写 2.静态文件存放目录:所有文件及文件夹命名均为小写 3....

  • Vue.js篇<1>

    node+vue:前后端分离MVC+MVVM的开模式图片.png vue的基础用法导入vue.js [v-cloa...

  • 前端TODO

    Vue.js 等框架原理了解 webpack 原理了解 browserify 插件开发 Vue.js 等框架原理学习

  • Vue.js基础入门

    今天,给大家分享下Vue.js基础入门,我主要由了解Vue.js、开始起步、语法三个部分简单的写了基础入门知识,希...

网友评论

    本文标题:vue.js了解篇1(下)

    本文链接:https://www.haomeiwen.com/subject/ufjypqtx.html