Vue3全解

367 阅读11分钟
 <div id="app">
 <fieldset>
 <legend>组件实例</legend>
 {{ message }}
 <button @click="change">Click</button>
 </fieldset>
 </div>
 <script>
 const App = {
 data() {
 return {
 message: 'Hello Vue!!'
 }
 },
 methods: {
 change() {
 console.log(this)
 this.message += '!'
 }
 },
 }
 const app = Vue.createApp(App)
 console.log(app)
 const vm = app.mount('#app')
 console.log(vm)
 </script>

app:应用实例 vm:组件实例 this指的是什么?this指的是组件实例

组件生命周期

beforeCreate

实例初始化完成后,数据观测(data observer)和 event/watcher 事件配置之前调用

Created

在实例创建完成后被立即调用。在这一步,实例已经完成以下的配置,数据观测、property和方法的运算,watch/event 事件回调。然而,挂载阶段还没开始,$elproperty目前不能用

beforeMount

在挂载之前调用:相关的render函数被首次调用

Mounted

实例在被挂载后调用,这时Vue.createApp({}).mount()被新创建的vm.$el替换了。如果根实例挂载到了一个文档内的元素上,当mounted被调用时vm.$el也在文档内。

beforeUpdated

数据更新时调用,发生在虚拟 DOM 打补丁之前

Updated

由于数据更改导致的虚拟 DOM 重新渲染和打补丁,在这之后会调用该钩子

beforeUnmount

在卸载组件之前调用。在这个阶段,实例仍然是完全正常的

unmounted

卸载组件后被调用。调用此钩子前,组件实例的所有指令都被解除绑定,所有事件侦听器都被移除,所有子组件实例被卸载 例子:

<div id="app">
 <fieldset>
 <legend>⽣命周期钩⼦</legend>
 <span id="msg">{{ message }}</span>
 <button @click="change">Click</button>
 </fieldset>
 </div>
 <script>
 const App = {
 data() {
 return { message: 'Hello Vue'}
 },
 methods: {
 change() {
 this.message += '!'
 }
 },
 beforeCreate() {
 console.log('beforeCreate', this.message, this.$el)
 },
 created() {
 console.log('created', this.message, this.$el)
 },
 beforeMount() {
 console.log('beforeMount', this.message, this.$el)
 },
 mounted() {
 console.log('mounted', this.message, this.$el) 
 },
 beforeUpdate() {
 console.log('beforeUpdate', this.message, document.querySelector('#msg').innerText)
 },
 updated() {
 console.log('updated', this.message, document.querySelector('#msg').innerText)
 }
 }
 Vue.createApp(App).mount('#app')
 </script>

image.png 例子2:

const App = {
 data() {
 return { isShow: true }
 },
 beforeCreate() {
 console.log('root beforeCreate', this.message, this.$el)
 },
 created() {
 console.log('root created', this.message, this.$el)
 },
 beforeMount() {
 console.log('root beforeMount', this.message, this.$el)
 },
 mounted() {
 console.log('root mounted', this.message, this.$el) 
 }
 }
 const app = Vue.createApp(App)
 app.component('child', {
 template: '<div>{{text}}</div>',
 data() { 
 return {text: 'I am child'}
 }, 
 created() {
 console.log('child created')
 },
 mounted() {
 console.log('child mounted')
 },
 beforeUnmount() {
 console.log('child beforeUnmount')
 },
 unmounted() {
 console.log('child unmounted')
 },
 })
 app.mount('#app')
<div id="app">
 <fieldset>
 <legend>⽣命周期钩⼦</legend>
 <child v-if="isShow"></child>
 <button @click="isShow = !isShow">Click</button>
 </fieldset>
</div>

image.png 由此可以解决四个问题: • Vue有哪些生命周期钩⼦? • 如果需要发送Ajax请求,最好放在哪个钩⼦内? 理论上除了beforecreate,unmounted外都可以放 • 父子组件嵌套时,父组件视图和子组件视图渲染完成谁先谁后? 由上图可知不确定。因为子组件先挂载,但是没有父组件视图上也不会显示。 • 父子组件嵌套时,如果希望在所有组件视图都渲染完成后再执行操作,该如何做?

mounted(){
  this.$nextTick(function){
    // 仅在渲染整个视图之后运行的代码
  }
}

模板语法:


<span v-once>这个将不会改变: {{ msg }}</span>
<span v-html="rawHtml"></span>
<a :href="url"> ... </a>
<a v-bind:href="url"> ... </a>
<a v-on:click="doSomething"> ... </a>
<a @click="doSomething"> ... </a>
<a @[event]="doSomething"> ... </a>

注意事项:

  • 不能在模板表达式里访问用户定义的全局变量
  • v-html容易引发XSS,谨慎使用

Data Property 和 methods

  1. data选项是一个函数,返回一个对象
  2. 对尚未提供所需值的property使用null、undefined等占位
  3. 实例创建后再添加的property,响应式系统不会跟踪

注意事项:

  • Vue2的data可以是对象,但Vue3的data只能是函数
  • Vue2的自定义组件里的data要使用函数,否则创建的多个自定义组件实例会共用数据。一般来说组件需要维护的状态应该是独立的。

计算属性

要点:计算属性会依赖data中的属性,属性发生改变自动触发计算属性的变化 注意事项:

  • 计算属性有缓存机制,如果依赖的数据未发生改变,则不会重新计算而是直接使用缓存值
  • 注意methods和computed里面的方法不要使用箭头函数,否则this就不是vm对象了

Watch

要点:watch会监控data中某个property的变化,执行函数 什么时候需要用watch:

  • 当只需要根据data中某个property的变化做出反应,但不一定需要结果
  • 当有异步操作时
  • 当需要用旧值时

四个问题: • v-text 和 v-html 有什么区别 v-text和v-html都是模板插值,前者相当于innerText,会把字符串作为文本内容插入页面;后者相当于innerHTML,会把字符串作为HTML片段插入页面,这会引起XSS攻击。 • data 为什么要是函数 Vue3里强制规定data的值为函数,Vue2里未做强制规定。在Vue2里一般建议data的值为函数,返回一个新的对象,特别是针对自定义组件。如果自定义组件的data的值是一个对象,这个对象会被多个组件实例共享,带来数据管理的混乱。 • 计算属性缓存是什么 当依赖data中的某个属性发生变化时,依赖该属性的计算属性会自动变化。计算属性有缓存机制,如果依赖的数据未发生改变,使用该计算属性时不会重新计算而是直接使用缓存值。 • watch、计算属性有什么区别
watch会监控data中某个property的变化,执行函数。而计算属性是根据监控某个property变化得到的一个返回值。 在这些场景使用watch:1. 当只需要根据data 中某个property的变化做出反应,但不⼀定需要结果 时;2. 当有异步操作时;3. 当需要用旧值时

响应式原理

数据响应式

  • 追踪数据的变化,在读取数据或者设置数据时能劫持做一些操作
  • 使用 Object.defineProperty
  • 使用Proxy

Object.defineProperty 实现响应式

function observe(data) {
  if(!data || typeof data !== 'obeject') return
  for(key in data) {
    let val = data[key]
    Object.defineProperty(data,key,{
      enumrable: true,
      configurable: true,
      get: function() {
        track(data,key)
        return val
      },
      set: function() {
        trigger(data,key,newval)
        val = newVal
      }
    })
    if(typeof val === 'object'){
      observe(val)
    }
  }
}
function track(data, key) {
 console.log('get data ', key)
}
function trigger(data, key, value) {
 console.log('set data', key, ":", 
value)
}

const data = {
  name: 'yang',
  friends: [1,2,3]
}
observe(data)

console.log(data.name)
data.name = 'valley'
data.friends[0] = 4
data.friends[3] = 5 // ⾮响应式
data.age = 6 //⾮响应式

Proxy 和 Reflect

Proxy 对象用于创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找,赋值,枚举,函数调用等) Reflect 是一个内置的对象,它提供拦截 JS 操作的方法,这些方法与Proxy handler相同 Reflect.set(target, propertyKey, value [, receiver]) 将值分配给属性的函数。返回⼀个Boolean,如果更 新成功,则返回true。
Reflect.get(target, propertyKey[, receiver]) 获取对象身上某个属性的值,类似 于 target[name]。

Proxy 实现响应式

function reactive(obj) {
  const handler = {
    get(target,prop,receiver){
      track(target,prop)
      const value = Reflect.get(...arguments)
      if(typeof value === 'object'){
        return reactive(value)
      }else {
        return value
      }
    },
    set(target,key,value,receiver){
      trigger(target,key,value)
      return Reflect.set(...arguments)
    }
  }
  return new Proxy(obj,handler)
}

function track(data, key) {
 console.log('get data ', key)
}

function trigger(data, key, value) {
 console.log('set data', key, ":", value)
}

const dinner = {
 meal: 'tacos'
 }
 const proxy = reactive(dinner)
 proxy.meal = 'apple'
 proxy.list = []
 proxy.list.push(1) //响应式

两个问题:

  • Vue3和Vue2的响应式原理有什么不同?

前者用proxy,后者使用Object.defineProperty

  • 用Proxy和Object.defineProperty相比有什么优点

Proxy可以劫持整个对象,而Object.defineProperty只能劫持对象的属性;前者递归返回属性对应的值的代理即可实现响应式,后者需要深度遍历每个属性;后者对于数组很不友好

Vue 条件渲染、列表、事件、组件

v-if 和 v-show 的区别在哪里?

v-if会创建和删除元素,对生命周期来说它会 created 然后 unmounted;v-show 会创建而不会删除元素,而是用 display:none 来隐藏,对应的是 created 一个生命周期。

v-for 列表渲染

  • v-for 可以基于数组渲染列表
  • 也可基于对象渲染列表
  • 可以使用值的范围
  • 可在组件上循环渲染
  • v-for 默认使用 “就地更新”策略,数据项的顺序被改变,Vue 不会移动DOM元素来匹配数据项的顺序,而是就地更新每个元素。这在小部分情况下,会造成BUG
  • 为能跟踪每个节点的身份,重用和重新排序现有元素,提升性能,需要使用key

事件处理

  • @click的值既可以是methods里的函数名,执行函数时参数是点击事件
  • 也可以是函数的调用,执行函数时参数是调用时传递的参数,可以传递固定值,可以传递data的属性,也可传递$event
  • @click="fn()"@click="fn" 都对

v-model

<input v-model="message" /> {{ message }}
<textarea v-model.lazy="message"></textarea> {{ message }}
<input type="checkbox" v-model="checked" /> {{checked}}
<!-- 复选框 -->
<input type="checkbox" value="a" v-model="list" />
<input type="checkbox" value="b" v-model="list" /> {{list}}
<!-- 单选框 -->
<input type="radio" value="a" v-model="theme" />
<input type="radio" value="b" v-model="theme" /> {{theme}}
<!-- select -->
<select v-model="selected">
 <option value="AA">A</option>
 <option value="BB">B</option>
 <option value="CC">C</option>
</select>
{{selected}}

组件基础

每个组件维护独立数据 父组件通过prop向子组件传递数据 子组件通过触发事件来实现子传父

<div id="app">
 <font-size step="1" :val="fontSize" @plus="fontSize += $event" 
 @minus="fontSize -= $event"></font-size>
 <font-size step="3" :val="fontSize" @plus="fontSize += $event"
 @minus="fontSize -= $event"></font-size>
 <p :style="{fontSize:fontSize+'px'}">Hello {{fontSize}}</p>
</div>
<script src="https://unpkg.com/vue@next"></script>
<script>
 const app = Vue.createApp({
 data() { return { fontSize: 16 } }
 })
 app.component('font-size', {
 props: ['val', 'step'],
 template: `
 <div>step: {{step}}
 <button @click="onPlus">+</button>
 <button @click="$emit('minus', step)">-</button>
 </div>`,
 methods: {
 onPlus() { this.$emit('plus', parseInt(this.step)) }
 }
 })
 app.mount('#app')
</script>

v-model实现双向绑定

  • 父组件通过 v-model="属性" 把属性传递给子组件
  • 子组件内有一个modelValue的prop,接受父组件传递的数据
  • 子组件通过触发 update:modelValue 修改父组件绑定的属性
<input v-model="searchText" />
<!--等价于-->
<input :value="searchText" @input="searchText = 
$event.target.value" />
//--------------------------------------------------------
<div id="app">
 <font-size step="1" v-model="fontSize"></font-size>
 <font-size step="4" v-model="fontSize"></font-size>
 <p :style="{fontSize:fontSize+'px'}">Hello {{fontSize}}</p>
</div>
<script src="https://unpkg.com/vue@next"></script>
<script>
 const app = Vue.createApp({
 data() { return { fontSize: 16 } }
 })
 app.component('font-size', {
 props: ['modelValue', 'step'],
 template: `
 <div>fontSize: {{modelValue}}
 <button @click="$emit('update:modelValue', 
+step+modelValue)">+</button>
 <button @click="$emit('update:modelValue', modelValuestep)">-</button>
 </div>`
 })
 app.mount('#app')
</script>

单向数据流指的是什么?有什么好处?

什么是单向数据流?

很多框架都使用单向数据流,指的是父组件能直接传递数据给子组件,子组件不能随意修改父组件的状态

为什么要单向?

单向数据流的目的是让数据的传递变得简单、可控、可追溯。假设都是⽤双向数据流,父组件维护⼀个状态,并且传递给所 有的⼦组件。当其中⼀个⼦组件通过双向数据流直接修改父组件的这个状态时,其他⼦组件也会被修改。当这个结果产⽣时 我们⽆法得知是拿个⼦组件修改了数据,在项⽬庞⼤时数据的管理和追溯变得更复杂。

如果要双向如何实现?

⼀般来说,父组件可⽤通过设置⼦组件的props直接传递数据给⼦组件。子组件想传递数据给父组件时,可以在内部emit⼀个自定义事件,父组件可在子组件上绑定该事件的监听,来处理子组件emit的事件和数据。

组件注册、Props、自定义事件、插槽、Provide/Inject

全局组件和局部组件

全局组件可以在任何组件内使用,局部组件只能在声明它的组件内部使用

// 声明全局组件
  app.component('component-a', {
    template: `
      <div>a</div>
      <component-b></component-b>
    `
  })
  app.component('ComponentB', {
    template: `<div>b</div>`
  })
// 声明局部组件
  const ComponentB = {
    template: `<div>ComponentB</div>`
  }
  const ComponentA = {
    components: {
      // 'component-b': ComponentB,
      ComponentB
    },
    template: `
      <div>ComponentA</div>
      <component-b></component-b>
    `
  }

组件命名: PascalCase 多个首字母大写单词直接连接,模板里可以用kebab-case和PascalCase

Props

// 几种Props写法范例
props: ['name','age']
props: {
  name: String,
  age: Number
}
props: {
  name: {
    type: String,
    required: true,
    // default: 'hello'
  }
}
// v-bind
<example v-bind="post"></example>
<example v-bind:id="post.id" v-bind:name="post.name"></example>
// 以下两种写法有什么区别
<User :name="3+4"></User>
<User name="3+4"></User>
//如果要传入的是表达式而不是字符串,就用v-bind:property绑定

非Props的Attribute

要点:

  • 指的是父组件模板里在使用子组件时设置了属性,但子组件内没有通过props接收
  • 当组件返回单个根节点时,非props attribute将自动添加到根节点的attribute中
  • 在子组件里可以通过attrs/this.attrs/this.attrs获取attributes
  • 如果想在非根节点**应用(比如触发事件)**传递的attribute,使用v-bind=“$attrs”
<div id="app">
  <username class="username" :error="errorMsg" @input="onInput"></username>
</div>
<script src="https://unpkg.com/vue@next"></script>
<script>
  const Username = {
    props: ['error'],
    template: `
    <fieldset>
      <legend>用户名</legend>
      <input v-bind="$attrs">
      <div>{{error}}</div>
    </fieldset>
    `
  }

  Vue.createApp({
    components: { Username },
    data() {
      return {  errorMsg: '' }
    },
    methods: {
      onInput(e){
        this.errorMsg = e.target.value.length<6?"长度不够":""
      }
    }
  }).mount('#app')
</script>

自定义事件

  • 子组件触发事件用this.$emit('my-event')
  • 父组件使用子组件时绑定<component-a @my-event="doSomeThing"></component-a>
  • v-model的语法糖
<com v-model:foo="bar"></com>
// 等价于
<com :foo="bar" @update:foo="bar=$event"></com>

插槽slot

  • 子组件里预留一个空位(slot)
  • 父组件使用子组件时可以在子组件标签内插入内容/组件
  • 父组件往插槽插入的内容只能使用父组件实例的属性

问题一:如果父组件想要子组件里插槽的变量,该怎么拿?

具名插槽

<div id="app">
  <layout>
    <template v-slot:header>
      <h1>页面header</h1>
    </template>
    <template #default>
      <p>页面content</p>
    </template>
    <template #footer>
      <div>页面footer</div>
    </template>
  </layout>
</div>
<script src="https://unpkg.com/vue@next"></script>
<script>
  const Layout = {
    template: `<div class="container">
      <header> <slot name="header"></slot> </header>
      <main> <slot></slot></main>
      <footer><slot name="footer"></slot></footer>
    </div>`
  }
  Vue.createApp({
    components: { Layout },
  }).mount('#app')
</script>

作用域插槽: 问题一可以用作用域插槽解决

<div id="app">
  <news>👉hello world</news>
  <news v-slot="props">👉 {{props.item}}</news>
  <news v-slot="props">👉第{{props.index}}章 {{props.item}}</news>
  <news v-slot="{item, index}">✔第{{index}}章 {{item}}</news>
  <!-- <news v-slot="{ item }">✔ {{item}}</news>  -->
</div>
<script src="https://unpkg.com/vue@next"></script>
<script>
  const News = {
    data() { return { news: ['first news', 'second news'] } },
    template: `<ul>
      <li v-for="(item, index) in news">
        <slot :item="item" :index="index"></slot>  
      </li>
    </ul>`
  }
  Vue.createApp({
    components: { News },
  }).mount('#app')
</script>

keep-alive

  • 页面第一次进入,钩子的触发顺序 created->mounted->activated
  • 退出时触发 deactivated
  • 当再次进入只触发activated

Provide/inject

  • 适用与深度嵌套的组件,父组件可以为所有的子组件直接提供数据
<div id="app">
  <toolbar></toolbar>
  <button @click="isDark=!isDark">切换</button>
  </div>
  <script src="https://unpkg.com/vue@next"></script>
  <script>
const ThemeButton = {
  inject: ['theme'],
  template: `
<div :class="['button', theme]" ><slot></slot><
/div>
`
}
const Toolbar = {
  components: { ThemeButton },
  inject: ['theme'],
  template: `<div :class="['toolbar', theme]">
<theme-button>确定</theme-button>
</div>`
}
Vue.createApp({
  data() { return { isDark: false } },
  provide: { theme: 'dark'},
  // provide() {
  // return { theme: this.isDark?'dark':'white'
}
              // },
              components: { Toolbar },
  }).mount('#app')
  </script>

Vue实现过渡和动画

使用class实现动画

  • 在CSS里配好样式,通过切换class实现效果切换
<div id="app">
  <div :class="['box', {active: isActive}]">
    {{isActive?'大': '小'}}
    <button @click="isActive = !isActive">Toggle size</button>
  </div>
</div>
<script src="https://unpkg.com/vue@next"></script>
<script>
  Vue.createApp({
    data() { return { isActive: false } }
  }).mount('#app')
</script>
<style>

.box {
  width: 200px;
  height: 200px;
  margin: 30px;
  box-shadow: 0 0 4px 0px rgba(0, 0, 0, 0.2);
  display: flex;
  align-items: center;
  justify-content: center;
  transition: all .3s;
}
.box.active { transform: scale(1.2); }
</style>

修改style实现动画

  • 修改style和data中数据绑定
<div id="app">
  <div class="color" :style="{backgroundColor: `rgb(${R},${G},${B})`}"></div>
  <div>
    <input type="range" v-model="R" min="0" max="255" /> 
    <input type="range" v-model="G" min="0" max="255" />
    <input type="range" v-model="B" min="0" max="255" /> 
  </div>
</div>
<script src="https://unpkg.com/vue@next"></script>
<script>
  Vue.createApp({
    data() {
      return { R: 0, G: 0, B: 0 }
    }
  }).mount('#app')
</script>
<style>
.color {
  width: 40px;
  height: 40px;
}
</style>

transition组件

  • v-enter-from: 在元素被插入之前生效,在元素被插入后的下一帧移除
  • v-enter-active: 定义进入过渡生效时的状态
  • v-enter-to: 定义进入过渡的结束状态。在元素被插入之后下一帧生效,在过渡/动画完成之后移除
  • v-leave-from:在元素过渡被触发时生效,下一帧被移除
  • v-leave-active: 在离开过渡生效时的状态
  • v-leave-to:离开过渡的结束状态。在离开过渡被触发后下一帧生效,在过渡/动画完成之后移除
  • image.png

transition对以下内容可以封装:

  • v-if
  • v-show
  • 动态组件
  • 组件根节点

transition范例:

// 注意name的用法
<div id="app">
  <button @click="show = !show">Toggle</button>
  <transition  name="fade">
    <p v-if="show">hello</p>
  </transition>
</div>
<script src="https://unpkg.com/vue@next"></script>
<script>
  Vue.createApp({
    data() {
      return {
        show: true
      }
    }
  }).mount('#app')
</script>

<style>
.fade-enter-active,
.fade-leave-active {
  transition: opacity 0.5s ease;
}

.fade-enter-from,
.fade-leave-to {
  opacity: 0;
}
</style>

transition 和 做动画的CSS库组合

  • 自定义过渡class名,结合库写动画更简单
<div id="app">
  <button @click="show = !show">Toggle</button>
  <transition  name="fade" 
    enter-active-class="animate__animated animate__fadeInDown"
    leave-active-class="animate__animated animate__fadeInUp"
  >
    <p v-if="show">hello</p>
  </transition>
</div>
<script src="https://unpkg.com/vue@next"></script>
<script>
  Vue.createApp({
    data() {
      return {  show: true }
    }
  }).mount('#app')
</script>

<link href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.0/animate.min.css"
  rel="stylesheet"/>

多个元素过渡:

  • 多元素过渡指的是多元素进行切换,同一事件只展示一个
  • 使用不同的key来提高性能
  • mode属性可以解决两个元素同时存在的现象,out-in:当前元素先离开,下一个元素再进来;

in-out:下一个元素先进来,当前元素后离开

<div id="app">
  <button @click="show = !show">Toggle</button>
  <transition 
    mode="out-in"
    enter-active-class="animate__animated animate__fadeInUp"
    leave-active-class="animate__animated animate__fadeOutDown"
  >
    <p v-if="show" key="hello">hello</p>
    <p v-else key="world">world</p>
  </transition>
</div>
<script src="https://unpkg.com/vue@next"></script>
<script>
  Vue.createApp({
    data() {
      return {  show: true }
    }
  }).mount('#app')
</script>

<link href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.0/animate.min.css"
  rel="stylesheet"/>

多组件切换

  • 使用动态组件实现Tab切换效果
  • 如果动态组件使用了keep-active,需要放在transition内部
<div id="app">
  <button v-for="tab in tabs" :key="tab" :class="{ active: currentTab === tab }" @click="currentTab = tab">
    {{ tab }}
  </button>
  <transition mode="out-in" name="fade">
    <keep-alive>
      <component :is="currentTab" class="tab"></component>
    </keep-alive>
  </transition>
</div>
<script src="https://unpkg.com/vue@next"></script>
<script>
  const app = Vue.createApp({
    data() {
      return {
        currentTab: 'Tab1',
        tabs: ['Tab1', 'Tab2']
      }
    },
  })
  app.component('Tab1', {
    template: `<div>Tab1 content</div>`,
  })
  app.component('Tab2', {
    template: `<div>
      <input v-model="value" /> {{value}}
    </div>`,
    data() { return { value: 'hello' } },
  })
  app.mount('#app')
</script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.0/animate.min.css" rel="stylesheet" />

<style>
  .active {
    background: #ccc;
  }

  .fade-enter-active,
  .fade-leave-active {
    transition: all .3s;
  }

  .fade-enter-from,
  .fade-leave-to {
    opacity: 0;
  }

</style>

列表的过渡效果

  • 使用transition-group实现列表过渡
<div id="app">
  <button @click="add">添加</button>
   <transition-group name="list" tag="div">
    <div v-for="(value, index) in news" :key="value">
      {{value}} 
      <button @click="news.splice(index, 1)">删除</button>
    </div>
  </transition-group>
</div>
<script src="https://unpkg.com/vue@next"></script>
<script>
  Vue.createApp({
    data() {
      return {
        news: []
      }
    },
    methods: {
      add() {
        let value = window.prompt('输入新闻')
        if(value) {
          this.news.push(value)
          this.news = this.news.sort()
        }
      }
    }
  }).mount('#app')
</script>
<style>
  .list-enter-active,
  .list-leave-active {
    transition: all .4s ease;
  }
  .list-enter-from {
    opacity: 0;
    transform: translateX(-30px);
  }
  .list-leave-to {
    opacity: 0;
    transform: translateX(30px);
  }
</style>
  • Vue里有哪些方法实现过渡或者动画效果?

class,style,transition,transition-group

  • transition组件实现过渡效果怎么使用

注意name的使用,把要做动画的元素用transition包起来

关于Vue CLI

  • 用于快速创建Vue项目模板,包含一些预定义的配置
  • 单文件组件
  • 构建webpack和webpack-server之上
  • 包含丰富的插件

目录结构 image.png 单文件组件:

  • 文件名后缀是.vue
  • 一个文件是一个组件
  • 模板放在