快速上手vue项目

563 阅读13分钟

前段时间连续做了2个跟前端框架vue.js相关的项目,在完成项目过程中一些需要记得的知识点记录在此篇博文中,便于自己以及有需要的朋友们学习与掌握。

vue.js是什么?

官方给出的定义是:Vue.js是一套构建用户界面的渐进式框架。其目标是通过尽可能简单的 API 实现响应的数据绑定和组合的视图组件。

如何理解渐进式呢?
VUE框架的核心设计理念并不推崇任何项目都直接使用VUE框架中所有的资源库(vue router、状态管理、构建工具等),更多的,技术选型和实践者应该考虑真实业务需求、使用场景、项目体积等多种因素做到应用尽可能的简单、灵活、低层本、可扩展。

基本语法

声明式渲染

Vue.js 的核心是一个允许你采用简洁的模板语法来声明式的将数据渲染进 DOM 的系统;
<div id="app">
  {{ message }}  //现在数据和DOM已经被绑定在一起,所有的元素都是响应式的。
</div>
var app = new Vue({
  el: '#app',
  data: {
    message: 'Hello Vue!'
  }
})
插值
  • 文本: <span>Message: {{ msg }}</span>
  • 纯 HTML : <div v-html="rawHtml"></div>
  • 属性 : <div v-bind:id="dynamicId"></div>
  • 使用 JavaScript 表达式 {{ ok ? 'YES' : 'NO' }}
  • 过滤器 {{ message | capitalize }}

缩写:

// v-bind 缩写
<!-- 完整语法 -->
<a v-bind:href="url"></a>
<!-- 缩写 -->
<a :href="url"></a>

// v-on 缩写
<!-- 完整语法 -->
<a v-on:click="doSomething"></a>
<!-- 缩写 -->
<a @click="doSomething"></a>

条件与循环

条件: v-if
<div id="app">
    <p v-if="seen">Now you see me</p>
</div>
循环: v-for
<div id="app">
  <ol>
    <li v-for="todo in todos">
      {{ todo.text }}
    </li>
  </ol>
</div>

Class 与 Style 绑定

绑定 HTML Class
我们可以传给 v-bind:class 一个对象,以动态地切换 class。
  • 对象语法
<div class="static"
     v-bind:class="{ active: isActive, 'text-danger': hasError }">
</div>

data: {
  isActive: true,
  hasError: false
}

//渲染为:
//<div class="static active"></div>
  • 数组语法
<div v-bind:class="[activeClass, errorClass]">

data: {
  activeClass: 'active',
  errorClass: 'text-danger'
}

//渲染为:
/<div class="active text-danger"></div>
绑定内联样式
v-bind:style 的对象语法十分直观——看着非常像 CSS ,其实它是一个 JavaScript 对象。 CSS 属性名可以用驼峰式(camelCase)或短横分隔命名(kebab-case):
<div v-bind:style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>
<div v-bind:style="styleObject"></div>

data: {
  activeColor: 'red',
  fontSize: 30,
  styleObject: {
    color: 'red',
    fontSize: '30px'
  }
}

条件渲染

  • 语法
<h1 v-if="ok">Yes</h1>
<h1 v-else>No</h1>
  • template v-if 把一个 'template' 元素当做包装元素,并在上面使用 v-if,最终的渲染结果不会包含它。
<template v-if="ok">
    <h1>Title</h1>
    <p>Paragraph 1</p>
    <p>Paragraph 2</p>
</template>
  • v-show
<h1 v-show="ok">Hello!</h1>
  • v-if vs. v-show

v-if 是真实的条件渲染,因为它会确保条件块在切换当中适当地销毁与重建条件块内的事件监听器和子组件。
v-if 也是惰性的:如果在初始渲染时条件为假,则什么也不做——在条件第一次变为真时才开始局部编译(编译会被缓存起来)。
相比之下, v-show简单得多——元素始终被编译并保留,只是简单地基于 CSS 切换。
一般来说, v-if 有更高的切换消耗而 v-show 有更高的初始渲染消耗。因此,如果需要频繁切换使用 v-show 较好,如果在运行时条件不大可能改变则使用 v-if 较好。

列表渲染

我们用 v-for 指令根据一组数组的选项列表进行渲染。 v-for 指令需要以 item in items 形式的特殊语法, items 是源数据数组并且 item 是数组元素迭代的别名。
  • 语法
<ul id="example-2">
  <li v-for="(item, index) in items">
    {{ parentMessage }} - {{ index }} - {{ item.message }}
  </li>
</ul>
var example2 = new Vue({
  el: '#example-2',
  data: {
    parentMessage: 'Parent',
    items: [
      { message: 'Foo' },
      { message: 'Bar' }
    ]
  }
})

//结果:
Parent-0-Foo
Parent-1-Bar
  • Template v-for
<ul>
  <template v-for="item in items">
    <li>{{ item.msg }}</li>
    <li class="divider"></li>
  </template>
</ul>
  • 数据更新
    • push()
    • pop()
    • shift()
    • unshift()
    • splice()
    • sort()
    • reverse()

事件处理器

可以用 v-on 指令监听 DOM 事件来触发一些 JavaScript 代码。
  • 语法
<div id="example-2">
  <!-- `greet` 是在下面定义的方法名 -->
  <button v-on:click="greet">Greet</button>
</div>

var example2 = new Vue({
  el: '#example-2',
  data: {
    name: 'Vue.js'
  },
  methods: {
    greet: function (event) {
      alert('Hello ' + this.name + '!');// `this` 在方法里指当前 Vue 实例
      alert(event.target.tagName); // `event` 是原生 DOM 事件
    }
  }
})
  • 内联处理器方法
<div id="example-3">
  <button v-on:click="say('hi')">Say hi</button>
  <button v-on:click="say('what')">Say what</button>
</div>

var example2 = new Vue({
  el: '#example-3',
  methods: {
    say: function (message) {
      alert(message)
    }
  }
})
  • 事件修饰符
    • .stop //阻止单击事件冒泡
    • .prevent //提交事件不再重载页面
    • .capture //添加事件侦听器时使用事件捕获模式
    • .self //只当事件在该元素本身(而不是子元素)触发时触发回调

表单控件绑定

用 v-model 指令在表单控件元素上创建双向数据绑定
//文本
<input v-model="message" placeholder="edit me">

//多行文本
<textarea v-model="message" placeholder="add multiple lines"></textarea>

//复选框
<input type="checkbox" id="jack" value="Jack" v-model="checkedNames">
<label for="jack">Jack</label>
<input type="checkbox" id="john" value="John" v-model="checkedNames">
<label for="john">John</label>
<input type="checkbox" id="mike" value="Mike" v-model="checkedNames">
<label for="mike">Mike</label>

new Vue({
  el: '...',
  data: {
    checkedNames: []
  }
})

//单选按钮
<input type="radio" id="one" value="One" v-model="picked">
<label for="one">One</label>
<br>
<input type="radio" id="two" value="Two" v-model="picked">
<label for="two">Two</label>
<br>

new Vue({
  el: '...',
  data: {
    picked,
  }
})

//选择列表
<select v-model="selected">
  <option>A</option>
  <option>B</option>
  <option>C</option>
</select>
  • 修饰符
  • .lazy //转变为在 change 事件中同步
  • .number //自动将用户的输入值转为 Number 类型(如果原值的转换结果为 NaN 则返回原值)
  • .trim //自动过滤用户输入的首尾空格

计算属性

  • computed
<div id="example">
  <p>Original message: "{{ message }}"</p>
  <p>Computed reversed message: "{{ reversedMessage }}"</p>
</div>
var vm = new Vue({
  el: '#example',
  data: {
    message: 'Hello'
  },
  computed: {
    reversedMessage: function () {
      return this.message.split('').reverse().join('')
    }
  }
})

结果:

Original message: "Hello"
Computed reversed message: "olleH"
  • Methods
<p>Reversed message: "{{ reverseMessage() }}"</p>

var vm = new Vue({
  el: '#example',
  data: {
    message: 'Hello'
  },
methods: {
      reverseMessage: function () {
        return this.message.split('').reverse().join('')
      }
    }
})

结果:


Reversed message: "olleH"
  • watch
<div id="demo">{{ fullName }}</div>

var vm = new Vue({
  el: '#demo',
  data: {
    firstName: 'Foo',
    lastName: 'Bar',
    fullName: 'Foo Bar'
  },
  watch: {
    firstName: function (val) {
      this.fullName = val + ' ' + this.lastName
    },
    lastName: function (val) {
      this.fullName = this.firstName + ' ' + val
    }
  }
})

1.计算缓存 vs Methods
计算属性是基于它的依赖缓存,计算属性只有在它的相关依赖发生改变时才会重新取值;
如果你不希望数据有缓存,请用 method 替代;

2.计算属性 vs Watched Property
$watch ,它用于观察 Vue 实例上的数据变动,当一些数据需要根据其它数据变化进行更新
如果你不希望数据有缓存,请用 method 替代;

组件

组件可以扩展 HTML 元素,封装可重用的代码。在较高层面上,组件是自定义元素。
全局组件
// Loading.vue

<template>
    <div class="loading">
        loading...
    </div>
</template>


// Loading.js

import MyLoading from './Loading.vue'
const Loading = {
    install: function(Vue){
        Vue.component('Loading',MyLoading)
    }
}

// 导出组件
export default Loading


// main.js

import Vue from 'vue'
import App from './App.vue'

// 引入自定义组件。index.js是组件的默认入口
import Loading from '../components/loading/index'
Vue.use(Loading);

Vue.use(ElementUi);
new Vue({
  el: '#app',
  render: h => h(App)
})

这样就可以在任何地方使用Loading组件了~!
局部注册
var Child = {
  template: '<div>A custom component!</div>'
}
new Vue({
  // ...
  components: {
    // <my-component> 将只在父模板可用
    'my-component': Child
  }
})
组件间的传值
prop 是单向绑定的:当父组件的属性变化时,将传导给子组件。
Vue.component('child', {
  props: ['message'],
  template: '<span>{{ message }}</span>'
})

<child message="hello!"></child> //<span>hello!</span>
自定义事件
每个 Vue 实例都实现了事件接口(Events interface),即:
使用 $on(eventName) 监听事件
使用 $emit(eventName) 触发事件
<div id="counter-event-example">
  <p>{{ total }}</p>
  <button-counter v-on:increment="incrementTotal"></button-counter>
  <button-counter v-on:increment="incrementTotal"></button-counter>
</div>

Vue.component('button-counter', {
  template: '<button v-on:click="increment">{{ counter }}</button>',
  data: function () {
    return {
      counter: 0
    }
  },
  methods: {
    increment: function () {
      this.counter += 1
      this.$emit('increment')
    }
  },
})
new Vue({
  el: '#counter-event-example',
  data: {
    total: 0
  },
  methods: {
    incrementTotal: function () {
      this.total += 1
    }
  }
})
Slot
  • 单个 Slot

      除非子组件模板包含至少一个 插口,否则父组件的内容将会被丢弃。当子组件模板只有一个没有属性的 slot 时,父组件整个内容片段将插入到 slot 所在的 DOM 位置,并替换掉 slot 标签本身。
    
//my-component 组件

<div>
  <h2>I'm the child title</h2>
  <slot>
    如果没有分发内容则显示我。
  </slot>
</div>

//父组件模版:

<div>
  <h1>I'm the parent title</h1>
  <my-component>
    <p>This is some original content</p>
    <p>This is some more original content</p>
  </my-component>
</div>

//渲染结果
<div>
  <h1>I'm the parent title</h1>
  <div>
    <h2>I'm the child title</h2>
    <p>This is some original content</p>
    <p>This is some more original content</p>
  </div>
</div>
  • 具名Slots

      <slot> 元素可以用一个特殊的属性 name 来配置如何分发内容。模板为
    
// 模板
<div class="container">
  <header>
    <slot name="header"></slot>
  </header>
  <main>
    <slot></slot>
  </main>
  <footer>
    <slot name="footer"></slot>
  </footer>
</div>

// 父组件模版:
<app-layout>
  <h1 slot="header">Here might be a page title</h1>
  <p>A paragraph for the main content.</p>
  <p>And another one.</p>
  <p slot="footer">Here's some contact info</p>
</app-layout>

// 渲染结果为:
<div class="container">
  <header>
    <h1>Here might be a page title</h1>
  </header>
  <main>
    <p>A paragraph for the main content.</p>
    <p>And another one.</p>
  </main>
  <footer>
    <p>Here's some contact info</p>
  </footer>
</div>

进阶

深入响应式原理

① 初始化-> ② 通过render函数生成Virtual DOM,vue实例遍历属性->③ Object.defineProperty将它们转为 getter/setter-> ④ watcher 程序实例在组件渲染的过程中把属性记录为依赖-> ⑤ 当依赖项的 setter 被调用时,会通知 watcher 重新计算,从而致使它关联的组件得以更新

  • 注意
    • vue不允许动态添加根级响应式属性,根级响应式属性必须在初始化实例前声明。
    • 当设置属性更新时,组件不会立即重新渲染,而是等到下一次事件循环清空队列时更新的;
    • 如果想在DOM状态更新后操作,必须使用Vue.nextTick(callback),设置后DOM 更新完成后就会调用。
var vm = new Vue({
  el: '#example',
  data: {
    message: '123'
  }
})
vm.message = 'new message' // 更改数据
vm.$el.textContent === 'new message' // false
Vue.nextTick(function () {
  vm.$el.textContent === 'new message' // true
})

Render函数

render 函数 跟 template 一样都是创建 html 模板的,但是有些场景中用 template 实现起来代码冗长繁琐而且有大量重复,这时候就可以用 render 函数。

craeteElement('h1',{class,style,on,attrs:{name,id}, 'hello world'})

第一个元素的标签名--h1
第二个元素有什么属性/事件,class,style,onclick,name,id...
第三个有什么子元素,这里是一个文本节点 'hello world'
创建方法
  • JSX:使用JSX语法,但需要Babel plugin插件;
import AnchoredHeading from './AnchoredHeading.vue'
new Vue({
  el: '#demo',
  render (h) {
    return (
      <AnchoredHeading level={1}>
        <span>Hello</span> world!
      </AnchoredHeading>
    )
  }
})
  • 函数化组件:
Vue.component('my-component', {
  functional: true,
  // 为了弥补缺少的实例
  // 提供第二个参数作为上下文
  render: function (createElement, context) {
    // ...
  },
  // Props 可选
  props: {
    // ...
  }
})

// props: 提供props 的对象
// children: VNode 子节点的数组
// slots: slots 对象
// data: 传递给组件的 data 对象
// parent: 对父组件的引用
  • 使用 JavaScript 代替模板功能
render: function (createElement) {
  if (this.items.length) {
    return createElement('ul', this.items.map(function (item) {
      return createElement('li', item.name)
    }))
  } else {
    return createElement('p', 'No items found.')
  }
}

插件

插件通常会为Vue添加全局功能。
  • 形式
    • 添加全局方法或者属性,如: vue-element
    • 添加全局资源:指令/过滤器/过渡等,如 vue-touch
    • 通过全局 mixin方法添加一些组件选项,如: vuex
    • 添加 Vue 实例方法,通过把它们添加到 Vue.prototype 上实现。
    • 一个库,提供自己的 API,同时提供上面提到的一个或多个功能,如 vue-router
import testPanel from './panel.vue'
let test = {}
test.install = function (Vue, options) {
  // 4. 添加实例方法
  Vue.prototype.$msg = 'Hello';
  Vue.prototype.$myMethod = function (arr) {
    if (arr.length < 0) {
      return false;
    } else {
      arr = arr.join('!!!');
      return arr;
    }
  }
  //添加全局的组件
  Vue.component(testPanel.name, testPanel) // testPanel.name 组件的name属性
}
export default test

过渡状态

Vue 的过渡系统提供了非常多简单的方法设置进入、离开和列表的动效。
  • 状态动画 与 watcher
<script src="https://unpkg.com/tween.js@16.3.4"></script>
<div id="animated-number-demo">
  <input v-model.number="number" type="number" step="20">
  <p>{{ animatedNumber }}</p>
</div>
new Vue({
  el: '#animated-number-demo',
  data: {
    number: 0,
    animatedNumber: 0
  },
  watch: {
    number: function(newValue, oldValue) {
      var vm = this
      function animate (time) {
        requestAnimationFrame(animate)
        TWEEN.update(time)
      }
      new TWEEN.Tween({ tweeningNumber: oldValue })
        .easing(TWEEN.Easing.Quadratic.Out)
        .to({ tweeningNumber: newValue }, 500)
        .onUpdate(function () {
          vm.animatedNumber = this.tweeningNumber.toFixed(0)
        })
        .start()
      animate()
    }
  }
})
  • 通过组件组织过渡
<script src="https://unpkg.com/tween.js@16.3.4"></script>
<div id="example-8">
  <input v-model.number="firstNumber" type="number" step="20"> +
  <input v-model.number="secondNumber" type="number" step="20"> =
  {{ result }}
  <p>
    <animated-integer v-bind:value="firstNumber"></animated-integer> +
    <animated-integer v-bind:value="secondNumber"></animated-integer> =
    <animated-integer v-bind:value="result"></animated-integer>
  </p>
</div>
// 这种复杂的补间动画逻辑可以被复用
// 任何整数都可以执行动画
// 组件化使我们的界面十分清晰
// 可以支持更多更复杂的动态过渡
// strategies.
Vue.component('animated-integer', {
  template: '<span>{{ tweeningValue }}</span>',
  props: {
    value: {
      type: Number,
      required: true
    }
  },
  data: function () {
    return {
      tweeningValue: 0
    }
  },
  watch: {
    value: function (newValue, oldValue) {
      this.tween(oldValue, newValue)
    }
  },
  mounted: function () {
    this.tween(0, this.value)
  },
  methods: {
    tween: function (startValue, endValue) {
      var vm = this
      function animate (time) {
        requestAnimationFrame(animate)
        TWEEN.update(time)
      }
      new TWEEN.Tween({ tweeningValue: startValue })
        .to({ tweeningValue: endValue }, 500)
        .onUpdate(function () {
          vm.tweeningValue = this.tweeningValue.toFixed(0)
        })
        .start()
      animate()
    }
  }
})
// All complexity has now been removed from the main Vue instance!
new Vue({
  el: '#example-8',
  data: {
    firstNumber: 20,
    secondNumber: 40
  },
  computed: {
    result: function () {
      return this.firstNumber + this.secondNumber
    }
  }
})

过渡效果

Vue 在插入、更新或者移除 DOM 时,提供多种不同方式的应用过渡效果。

包括以下工具:
在 CSS 过渡和动画中自动应用 class
可以配合使用第三方 CSS 动画库,如 Animate.css
在过渡钩子函数中使用 JavaScript 直接操作 DOM
可以配合使用第三方 JavaScript 动画库,如 Velocity.js

Vue 提供了 transition 的封装组件,在下列情形中,可以给任何元素和组件添加 entering/leaving 过渡

条件渲染 (使用 v-if)
条件展示 (使用 v-show)
动态组件
组件根节点

<div id="demo">
  <button v-on:click="show = !show">
    Toggle
  </button>
  <transition name="fade">
    <p v-if="show">hello</p>
  </transition>
</div>
<script>
new Vue({
  el: '#demo',
  data: {
    show: true
  }
})
</script>
<style>
.fade-enter-active, .fade-leave-active {
  transition: opacity .5s
}
.fade-enter, .fade-leave-active {
  opacity: 0
}
</style>
会有 4 个(CSS)类名在 enter/leave 的过渡中切换:

v-enter: 定义进入过渡的开始状态。在元素被插入时生效,在下一个帧移除。

v-enter-active: 定义进入过渡的结束状态。在元素被插入时生效,在 transition/animation 完成之后移除。

v-leave: 定义离开过渡的开始状态。在离开过渡被触发时生效,在下一个帧移除。

v-leave-active: 定义离开过渡的结束状态。在离开过渡被触发时生效,在 transition/animation 完成之后移除。


分享

现在前端岗位是饱满的,但是符合要求或者说中高级的前端却寥寥无几;而前端开发员们学习速度跟不上前端技术更新迭代速度,以至于部分前端开发员只为了完成项目进度而完成,忽视了其中底层的原理;当然会简单使用是第一步,然后能够熟练的使用框架中的语法,接下去你就该去了解该框架的底层实现原理或者进一步的深入研究。

以下是个人觉得值得了解或学习的优秀推文,仅以分享和共同学习!