Vue3知识大纲
Vue.js起步
数据到视图映射
网络异常,图片无法展示
|
<!DOCTYPE html> <html lang="en"> <head> <script src="https://unpkg.com/vue@next"></script> </head> <body> <div id="app"> <fieldset> <legend>数据驱动视图</legend> {{ message }} </fieldset> </div> <script> const App = { data() { return { message: 'Hello World' } } } Vue.createApp(App).mount('#app') </script> </body> </html>
修改数据
网络异常,图片无法展示
|
<!DOCTYPE html> <html lang="en"> <head> <script src="https://unpkg.com/vue@next"></script> </head> <body> <div id="app"> <fieldset> <legend>数据驱动视图</legend> <button @click="add">Click Me</button> {{ counter }} </fieldset> </div> <script> const App = { data() { return { counter: 0 } }, methods: { add() { this.counter++ } }, } Vue.createApp(App).mount('#app') </script> </body> </html>
属性绑定
网络异常,图片无法展示
|
<!DOCTYPE html> <html lang="en"> <head> <script src="https://unpkg.com/vue@next"></script> </head> ![image.png](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/735ebe023180428c86fde3988d5398a6~tplv-k3u1fbpfcp-watermark.image?) <body> <div id="app"> <fieldset> <legend>属性的绑定</legend> <a :href="url" :title="message">百度</a> {{ message }} </fieldset> </div> <script> const App = { data() { return { url: 'https://baidu.com', message: 'Hello World' } } } Vue.createApp(App).mount('#app') </script> </body> </html>
双向绑定
网络异常,图片无法展示
|
<!DOCTYPE html> <html lang="en"> <head> <script src="https://unpkg.com/vue@next"></script> </head> <body> <div id="app"> <fieldset> <legend>双向绑定</legend> <input type="text" v-model="search"> {{search}} </fieldset> </div> <script> const App = { data() { return { search: "hello" } } } Vue.createApp(App).mount('#app') </script> </body> </html>
条件渲染
网络异常,图片无法展示
|
<!DOCTYPE html> <html lang="en"> <head> <script src="https://unpkg.com/vue@next"></script> </head> <body> <div id="app"> <fieldset> <legend>条件渲染</legend> <p v-if="isShow"> 子组件</p> <button @click="isShow = !isShow">Click</button> </fieldset> </div> <script> const App = { data() { return { isShow: true } }, // methods: { // toggle() { // this.isShow = !this.isShow // } // }, } Vue.createApp(App).mount('#app') </script> </body> </html> 复制代码
循环渲染
网络异常,图片无法展示
|
<!DOCTYPE html> <html lang="en"> <head> <script src="https://unpkg.com/vue@next"></script> </head> <body> <div id="app"> <fieldset> <legend>列表渲染</legend> <ul> <li v-for="value in items"> {{value}}</li> </ul> <button @click="add">Click</button> </fieldset> </div> <script> const App = { data() { return { items: ['item0', 'item1', 'item2'] } }, methods: { add() { this.items.push('item' + this.items.length) } }, } Vue.createApp(App).mount('#app') </script> </body> </html>
自定义组件
网络异常,图片无法展示
|
<!DOCTYPE html> <html lang="en"> <head> <script src="https://unpkg.com/vue@next"></script> </head> <body> <div id="app"> <fieldset> <legend>自定义组件</legend> <todo-item></todo-item> <button @click="add">Click</button> </fieldset> </div> <script> const App = { data() { return { } } } const app = Vue.createApp(App) app.component('todo-item', { data() { return { message: 'hello' } }, template: "<div>{{message}} </div>" }) app.mount('#app') </script> </body> </html>
组件实例、生命周期
1、Vue有哪些⽣命周期钩⼦?
- beforeCreate在实例初始化之后,数据观测(data observer)和event/watcher事件配置之前被调用
- created在实例创建完成后被立即调用。在这一步,实例已完成以下的配置︰数据观测(data observer),property和方法的运算,watch/event 事件回调。然而,挂载阶段还没开始,
$el property
目前尚不可用 - beforeMount在挂载开始之前被调用∶相关的render函数首次被调用
- mounted实例被挂载后调用,这时
Vue.createApp({}).mount()
被新创建的vm.$el
替换了。如果根实例挂载到了一个文档内的元素上,当mounted被调用时vm.$el
也在文档内。 - beforeUpdate数据更新时调用,发生在虚拟DOM打补丁之前
- updated由于数据更改导致的虚拟DOM重新渲染和打补丁,在这之后会调用该钩子
- beforeUnmount在卸载组件实例之前调用。在这个阶段,实例仍然是完全正常的
- unmounted卸载组件实例后调用。调用此钩子时,组件实例的所有指令都被解除绑定,所有事件侦听器都被移除,所有子组件实例被卸载
网络异常,图片无法展示
|
2、 如果需要发送Ajax请求,最好放在哪个钩⼦内?
一般情况下,都放在mounted中,保证逻辑的统一性。因为生命周期是同步执行的,ajax是异步执行的。
服务端渲染不支持mounted方法,所以在服务端渲染的情况下统一放在created中。
3、 ⽗⼦组件嵌套时,⽗组件视图和⼦组件视图渲染完成谁先谁后?
不确定
4、 ⽗⼦组件嵌套时,如果希望在所有组件视图都渲染完成后再执⾏操
作,该如何做?
mounted() { this.$nextTick(function () { // 仅在渲染整个视图之后运⾏的代码 }); }
应用实例与组件实例
网络异常,图片无法展示
|
<!DOCTYPE html> <html lang="en"> <head> <script src="https://unpkg.com/vue@next"></script> </head> <body> <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> </body> </html>
问题:左侧代码中的this是什么?
vm对象
生命周期
网络异常,图片无法展示
|
<!DOCTYPE html> <html lang="en"> <head> <script src="https://unpkg.com/vue@next"></script> </head> <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> </body> </html>
组件销毁生命周期代码演示
网络异常,图片无法展示
|
<!DOCTYPE html> <html lang="en"> <head> <script src="https://unpkg.com/vue@next"></script> </head> <body> <div id="app"> <fieldset> <legend>生命周期钩子</legend> <child v-show="isShow"></child> <button @click="isShow = !isShow">Click</button> </fieldset> </div> <script> 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') </script> </body> </html>
data、methods、computed、watch
指令
网络异常,图片无法展示
|
data methods
网络异常,图片无法展示
|
computed
网络异常,图片无法展示
|
methods和computed的区别
- 调用方式不同。computed直接以对象属性方式调用,不需要加括号,而methods必须要函数执行才可以得到结果。
- 绑定方式不同。methods与compute纯get方式都是单向绑定,不可以更改输入框中的值。compute的get与set方式是真正的双向绑定。
- 是否存在缓存。methods没有缓存,调用相同的值计算还是会重新计算。competed有缓存,在值不变的情况下不会再次计算,而是直接使用缓存中的值。
watch
网络异常,图片无法展示
|
小结
- v-text 和 v-html 有什么区别
- data 为什么要是函数
- 计算属性缓存是什么,如果不想缓存该怎么做
- watch、计算属性有什么区别
网络异常,图片无法展示
|
响应式原理
什么是响应式
数据响应式
网络异常,图片无法展示
|
范例
var obj = {} var age Object.defineProperty(obj, 'age', { get: function(){ console.log('get age...') return age }, set: function(val){ console.log('set age...') age = val } }) obj.age = 100 // 'set age...' console.log(obj.age) // 'get age...', 100
Object.defineProperty实现响应式
function observe(data) { if(!data || typeof data !== 'object') return for(var key in data) { let val = data[key] Object.defineProperty(data, key, { enumerable: true, configurable: true, get: function() { track(data, key) return val }, set: function(newVal) { 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) } var data = { name: 'hunger', 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
网络异常,图片无法展示
|
const dinner = { meal: 'tacos' } const handler = { get(target, prop) { console.log('get...', prop) //return target[prop] return Reflect.get(...arguments) }, set(target, key, value) { console.log('set...', key, value) //target[key] = value return Reflect.set(...arguments) } } const proxy = new Proxy(dinner, handler) console.log(proxy.meal)
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的响应式原理分别是什么,二者有什么差异
- 手写reactive 实现track trigger
- 用Proxy和Object.defineProperty相比有什么优点和缺点
网络异常,图片无法展示
|
条件、列表、事件、组件、双向绑定、单向数据流
v-if和v-show的区别
网络异常,图片无法展示
|
<div id="app"> <button @click="isShow=!isShow">Toggle</button> <p v-if="isShow">v-if content</p> <p v-show="isShow">v-show content</p> <child v-if="isShow" title="v-if"></child> <child v-show="isShow" title="v-show"></child> </div> <script src="https://unpkg.com/vue@next"></script> <script> const app = Vue.createApp({ data() { return { isShow: true } } }) app.component('child', { props: ['title'], template: `<div>component {{title}}</div>`, created() { console.log('child created', this.title) }, unmounted() { console.log('child unmounted', this.title) } }) app.mount('#app') </script>
v-for列表渲染
网络异常,图片无法展示
|
<div id="app"> <div v-for="(item,index) in items" > <child></child> <button @click="remove(index)">delete</button> </div> <button @click=add>add</button> </div> <script src="https://unpkg.com/vue@next"></script> <script> //[{id:1}, {id:2}, {id:3}] //[child1, child2, child3] const app = Vue.createApp({ data() { return { items: [] } }, methods:{ remove(index) { console.log('remove ', index) this.items.splice(index,1) }, add() { this.items.push({id: Math.random()}) } } }) app.component('child', { data() { return { counter: Math.random() } }, template: `<span>{{counter}} </span>` , unmounted() { console.log(this.counter + ' unmounted') }, }) app.mount('#app') </script>
事件处理
网络异常,图片无法展示
|
<div id="app"> <span v-on:click="sayHello">click</span> <!-- <span @click="sayHello">click</span> <span @click="sayHello('hunger')">click</span> <span @click="sayHello($event), sayHi('hunger')">click</span> <span @click.once="sayHello(name)">click</span> --> </div> <script src="https://unpkg.com/vue@next"></script> <script> Vue.createApp({ data() { return { name: 'hunger' } }, methods: { sayHello(name) { console.log(name) }, sayHi(name) { console.log(name) } }, }).mount('#app') </script>
v-model
网络异常,图片无法展示
|
<div id="app"> <fieldset> <legend> value 和 input </legend> <!-- value 和 input --> <input v-model="message" /> {{ message }} </fieldset> <fieldset> <legend> value 和 change </legend> <!-- value 和 change --> <textarea v-model.lazy="message"></textarea> {{ message }} </fieldset> <fieldset> <legend> 复选框 </legend> <input type="checkbox" v-model="checked" /> {{checked}} </fieldset> <fieldset> <legend> 多个复选框 </legend> <input type="checkbox" value="a" v-model="list" /> <input type="checkbox" value="b" v-model="list" /> {{list}} </fieldset> <fieldset> <legend> 单选框 </legend> <input type="radio" value="a" v-model="theme" /> <input type="radio" value="b" v-model="theme" /> {{theme}} </fieldset> <fieldset> <legend> select </legend> <select v-model="selected" multiple> <option value="AA">A</option> <option value="BB">B</option> <option value="CC">C</option> </select> <br /> <span>Selected: {{ selected }}</span> </fieldset> </div> <script src="https://unpkg.com/vue@next"></script> <script> Vue.createApp({ data() { return { message: 'aa', checked: false, list: [], theme: '', selected: [] } } }).mount('#app') </script>
组件基础
网络异常,图片无法展示
|
<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: 18 } } }) 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修改父组件绑定的属性
<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>step: {{modelValue}} <button @click="$emit('update:modelValue', +step+modelValue)">+</button> <button @click="$emit('update:modelValue', modelValue-step)">-</button> </div>` }) app.mount('#app') </script>
单向数据流
- 什么是单向数据流?
很多框架都使用单向数据流,指的是父组件能直接传递数据给子组件,子组件不能随意修改父组件状态。 - 为什么要单向?
单向数据流的目的是让数据的传递变得简单、可控、可追溯。假设都是用双向数据流,父组件维护一个状态,并且传递给所有的子组件。当其中一个子组件通过双向数据流直接修改父组件的这个状态时,其他子组件也会被修改。当这个结果产生时我们无法得知是拿个子组件修改了数据,在项目庞大时数据的管理和追湖变得更复杂。 - 如果要双向如何实现?
一般来说,父组件可用通过设置子组件的props直接传递数据给子组件。子组件想传递数据给父组件时,可以在内部mit一个自定义事件,父组件可在子组件上绑定该事件的监听,来处理子组件mit的事件和数据。 在Vue里,v-models实现的所谓双向绑定,本质上就这种方式。
小结
1、列表循环时key的作用?
- 简单通俗地讲,没有key时,状态默认绑定的是位置,有key时,状态根据key的属性值绑定到了响应的数组元素。
- key是为了更高效的更新虚拟DOM
- 推荐使用数组内的字段(保证唯一性)作为key的唯一标识,不建议直接使用index 2、@click="handler"和@click="handler()"哪个正确?
3、父子组件如何传递数据?
4、单向数据流指的是什么?有什么好处? 5、v-model是如何实现的
Vue3组件注册、Props、Attribute、自定义事件
组件注册
网络异常,图片无法展示
|
Props
网络异常,图片无法展示
|
非Prop的属性
网络异常,图片无法展示
|
自定义事件
网络异常,图片无法展示
|
v-model语法糖
网络异常,图片无法展示
|
小结
- 在Vue中组件的全局注册和局部注册有什么区别,如何局部注册组件?\
- 如何传递一个字符串类型的prop给子组件?数字类型呢?如何动态给prop赋值?\
- 对于组件来说非prop的attribute怎么处理?\
- v-model:foo="bar”与:foo="bar"有什么区别?如何实现v-model:foo="bar” ?\
- Vue中的插槽是什么?\
- 如何实现多层级嵌套的父子组件数据传递?\
- keep-alive有什么作用
插槽、具名插槽、作用域插槽
slot插槽
网络异常,图片无法展示
|
具名插槽
网络异常,图片无法展示
|
作用域插槽
网络异常,图片无法展示
|
keep-alive与生命周期
网络异常,图片无法展示
|
网络异常,图片无法展示
|
小结
- Vue中的插槽是什么?
- 具名插槽怎么用?
- 作用域插槽是什么?
爷孙组件数据传递
Provide和Inject
网络异常,图片无法展示
|
网络异常,图片无法展示
|
过渡与动画
使用class切换实现过渡和动画
网络异常,图片无法展示
|
修改style实现过渡和动画
网络异常,图片无法展示
|
transition组件创建的class
网络异常,图片无法展示
|
transition组件和animate.css便捷实现特
多个元素轮流切换过渡实现
网络异常,图片无法展示
|
动态组件专场效果实现
网络异常,图片无法展示
|
transition-group
网络异常,图片无法展示
|
使用Vue CLI4搭建Vue项目
网络异常,图片无法展示
|
目录结构与执行流程
网络异常,图片无法展示
|
引入Sass解决各种bug
网络异常,图片无法展示
|
scoped
网络异常,图片无法展示
|
@vue:cli-service# @vue:cli-service
网络异常,图片无法展示
|
vue.config.js配置
网络异常,图片无法展示
|
webpack
网络异常,图片无法展示
|
Vite 搭建项目
vite是什么解决什么问题
网络异常,图片无法展示
|
诞生思路
网络异常,图片无法展示
|
vite体验
网络异常,图片无法展示
|
Composition API
选项式API的痛点
网络异常,图片无法展示
|
组合式API的优势和用法
网络异常,图片无法展示
|
用法
选项式API改造成组合式API
生命周期钩子概览
网络异常,图片无法展示
|
Setup、ref、reactive、toRef、toRefs用法详解
网络异常,图片无法展示
|
网络异常,图片无法展示
|
网络异常,图片无法展示
|
VueRouter
VueRouter4的安装
网络异常,图片无法展示
|
VueRouter4的使用
网络异常,图片无法展示
|
原生JS造一个Hash模式的Router
网络异常,图片无法展示
|
原生JS造一个History模式的Router
网络异常,图片无法展示
|
VueRouter4的Hash模式和History模式
网络异常,图片无法展示
|
动态路由与响应参数变化(路由匹配)
网络异常,图片无法展示
|
导航守卫
网络异常,图片无法展示
|
在组合式API中使用VueRouter
网络异常,图片无法展示
|
beforeRouteUpdate路由守卫的使用场景
路由懒加载
网络异常,图片无法展示
|
路由切换过渡动效
网络异常,图片无法展示
|
Vuex4.0的使用和原理
组件间数据传递的几种方法、为什么需要Vuex
网络异常,图片无法展示
|
EventBus的不便之处
网络异常,图片无法展示
|