createApp
表示创建一个 Vue 应用, 存储到 app 变量中DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>lesson 5title>
<script src="https://unpkg.com/vue@next">script>
head>
<body>
<div id="root">div>
body>
<script>
const app = Vue.createApp({
data() {
return {
message: 'hello world'
}
},
template: "{{message}}"
});
// 注册组件
app.component('cmp', {
props: ['title'],
template: '{{ title }}'
});
// vm 代表的就是 Vue 应用的根组件
const vm = app.mount('#root');
script>
html>
生命周期函数:某一时刻会自动执行的函数
beforeCrate
:在实例生成之前会自动执行的函数created
:在实例生成之后会自动执行的函数beforeMount
:在组件内容被渲染到页面之前自动执行的函数mounted
:在组件内容被渲染到页面之后自动执行的函数beforeUpdate
:当数据发生变化时会自动执行的函数updated
:当数据发生变化,页面重新渲染后,会自动执行的函数beforeUnmount
:当 Vue 应用失效时,自动执行的函数unmounted
:当 Vue 应用失效时,且 dom 完全销毁后,自动执行的函数{{}}
:插值表达式
v-html
:将数据作为 HTML 代码进行渲染
v-bind(:)
:数据绑定
v-on(@)
:事件绑定
:[propertyName]
、@[eventName]
:动态属性/事件,属性/事件名从 data 中获取(见源码)@click.prevent
:阻止自带的事件(如源码,点击事件后会跳转到指定 url,使用 prevent 修饰符消除此默认操作)v-slot(#)
:具名
v-once
:仅在第一次使用 data 中的数据(后续使用的属性值仍会改变,但节点仅渲染一次)
v-if
、v-if-else
、v-else
:分支逻辑,仅在条件符合的情况下渲染节点
v-show
:控制节点显示
v-if
/ v-show
区别:v-if
不显示时会将节点直接移除,v-show
不显示则仅是将节点的 display
属性设置为 none,若需要频繁控制节点显示隐藏,使用 v-show
对性能会更加友好DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>lesson 7title>
<script src="https://unpkg.com/vue@next">script>
head>
<body>
<div id="root">
<form v-if='show' action="https://www.baidu.com" @click.prevent="handleClick">
<button type="submit">提交button>
form>
<span v-else :[bindName]="message">
{{ message }}
span>
div>
body>
<script>
const app = Vue.createApp({
data() {
return {
message: "hello world",
show: false,
bindName: 'title'
}
},
methods: {
handleClick() {
alert('click')
}
},
template: ``
});
const vm = app.mount('#root');
script>
html>
v-for
:列表循环渲染DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>lesson 11title>
<script src="https://unpkg.com/vue@next">script>
head>
<body>
<div id="root">div>
body>
<script>
const app = Vue.createApp({
data() {
return {
listArray: ['dell', 'lee', 'teacher'],
listObject: {
firstName: 'dell',
lastName: 'lee',
job: 'teacher'
}
}
},
methods: {
handleAddBtnClick() {
// 1. 使用数组的变更函数 push, pop, shift, unshift, splice, sort, reverse
// this.listArray.push('hello');
// this.listArray.pop();
// this.listArray.shift();
// this.listArray.unshift('hello');
// this.listArray.reverse();
// 2. 直接替换数组
// this.listArray = ['bye', 'world']
// this.listArray = ['bye', 'wolrd'].filter(item => item === 'bye');
// 3. 直接更新数组的内容
// this.listArray[1] = 'hello'
// 直接添加对象的内容,也可以自动的展示出来
// this.listObject.age = 100;
// this.listObject.sex = 'male';
}
},
template: `
{{ value }} -- {{ key }}
{{ item }}
`
});
const vm = app.mount('#root');
script>
html>
v-for
、v-if
存在于同一个节点时,v-for
拥有更高的优先级,此时 v-if
失效computed
和 method
都能实现的一个功能,建议使用 computed,因为有缓存computed
和 watched
都能实现的功能,建议使用 computed
因为更加简洁DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>lesson 8title>
<script src="https://unpkg.com/vue@next">script>
head>
<body>
<div id="root">div>
body>
<script>
// data & methods & computed & watcher
const app = Vue.createApp({
data() {
return {
message: "hello world",
count: 2,
price: 5,
newTotal: 10,
}
},
watch: {
// price 发生变化时,函数会执行
price(current, prev) {
this.newTotal = current * this.count;
}
},
computed: {
// 当计算属性依赖的内容发生变更时,才会重新执行计算
total() {
return Date.now() + this.count;
// return this.count * this.price
}
},
methods: {
formatString(string) {
return string.toUpperCase();
},
// 只要页面重新渲染,才会重新计算
getTotal() {
return Date.now();
// return this.count * this.price;
},
},
template: `
{{message}} {{newTotal}}
`
});
const vm = app.mount('#root');
script>
html>
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>lesson 9title>
<style>
.red {
color: red;
}
.green {
color: green;
}
style>
<script src="https://unpkg.com/vue@next">script>
head>
<body>
<div id="root">div>
body>
<script>
const app = Vue.createApp({
data() {
return {
// 直接传入字符串进行样式类配置
classString: 'red',
// 通过对象控制样式类是否生效
classObject: {red: false, green: true},
// 通过数组配置样式类,可接受对象形式元素
classArray: ['red', 'green', {brown: false}],
// 字符串形式配置内联样式
styleString: 'color: yellow;background: orange',
// 对象形式配置内联样式
styleObject: {
color: 'orange',
background: 'yellow'
}
}
},
template: `
Hello World
`
});
app.component('demo', {
// $attr.class:获取组件的指定(class)属性
template: `
one
two
`
})
const vm = app.mount('#root');
script>
html>
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>lesson 12title>
<script src="https://unpkg.com/vue@next">script>
head>
<body>
<div id="root">div>
body>
<script>
// event, $event
// 事件修饰符:
// stop(阻止事件冒泡)
// prevent(阻止默认事件)
// capture
// self(仅自身触发,不适用于子节点)
// once(仅生效一次)
// passive
// 按键修饰符(@keydown):enter, tab, delete, esc, up, down, left, right
// 鼠标修饰符:left, right, middle
// 精确修饰符:exact
const app = Vue.createApp({
methods: {
// 若方法未传入参数,则第一位为原生事件对象
handleClick(event) {
// 方法有参数时接收原生事件对象
handleClick(val, event) {
console.log('click')
},
handleClick1(val, event) {
console.log('click')
},
},
template: `
123">123
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>lesson 13title>
<script src="https://unpkg.com/vue@next">script>
head>
<body>
<div id="root">div>
body>
<script>
// input, textarea, checkbox, radio, select
// 修饰符 lazy(控件失焦时更新), number(转化输入数据类型), trim(去除头尾空格)
const app = Vue.createApp({
data() {
return {
message: 'hello',
}
},
template: `
{{ message }}
`
});
const vm = app.mount('#root');
script>
html>
组件的定义:
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>lesson 14title>
<script src="https://unpkg.com/vue@next">script>
head>
<body>
<div id="root">div>
body>
<script>
const Counter = {
data() {
return {
count: 1
}
},
template: `
{{ count }}`
}
const HelloWorld = {
template: `hello world`
}
const app = Vue.createApp({
components: {
// counter: Counter,
// 'hello-world': HelloWorld,
Counter, HelloWorld,
},
template: `
`
});
// 通过 component 创建的组件可全局使用,但会永久占用空间
app.component('counter-parent', {
template: ` `
})
app.component('counter', {
data() {
return {
count: 1
}
},
template: `{{count}}`
})
const vm = app.mount('#root');
script>
html>
provide
、inject
进行多层组件传值,祖组件使用 provide
向后代组件提供数据,后代组件使用 inject
从祖组件注入数据。provide
和 inject
绑定并不是可响应的,除非传递一个数据对象(例如组件自身对象)。DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>lesson 15title>
<script src="https://unpkg.com/vue@next">script>
head>
<body>
<div id="root">div>
body>
<script>
const app = Vue.createApp({
data() {
return {num: 1234}
},
// 第一种
provide(){
return{
foo:'halo'
}
},
// 第二种
provide:{
foo:'halo~~~~',
// 返回自身的属性
num: () => this.num
},
template: `
`
});
// type(数据类型):String, Boolean, Array, Object, Function, Symbol
// required 必填
// default 默认值
app.component('test', {
props: {
content: {
type: Number,
// 对从父组件接受的属性值进行校验
validator: function (value) {
return value < 1000;
},
default: function () {
return 456;
}
}
},
// 从祖节点注入
inject:['foo'],
template: `
{{ content }}`
});
const vm = app.mount('#root');
script>
html>
v-bind="params"
等价于 :content="params.content" :a="params.a" :b="params.b" :c="params.c"
content-abc
这种命名,接的时候,使用 contentAbc
命名props
),则 Vue 会将父组件传递的内容作为属性放置在子组件最外层的 dom 节点上。inheritAttrs: false
特性,阻止子组件继承父组件传递的属性。this.$attrs
:父组件传递的属性对象。DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>lesson 17title>
<script src="https://unpkg.com/vue@next">script>
head>
<body>
<div id="root">div>
body>
<script>
const app = Vue.createApp({
template: `
`
});
app.component('counter', {
// inheritAttrs: false,
mounted() {
console.log(this.$attrs.msg);
},
template: `
Counter
Counter
Counter
`
});
const vm = app.mount('#root');
script>
html>
this.$emit()
:触发父组件事件,由父组件调用子组件时设置监听器。$emit()
中可传值,传递的值会自动传入父组件监听器对应的方法中。$emit()
可实现类似 v-modal
的双向绑定效果,当使用默认 v-modal
时,子组件内接收的属性名必须为 “modalValue",此时 $emit
触发的事件为 "update:modalValue"
,传递过去的值也会赋值给父组件中 v-modal
中对应的属性。若想自定义属性名,可在父组件中使用 v-modal:[属性名]
的形式监听对应变量的变化。modeModifiers
定义属性修改器。DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>lesson 18title>
<script src="https://unpkg.com/vue@next">script>
head>
<body>
<div id="root">div>
body>
<script>
const app = Vue.createApp({
data() {
return {count: 1}
},
template: `
`
});
app.component('counter', {
props: {
'modelValue': String,
'modeModifiers': {
tostring: (val) => val.toString();
}
},
methods: {
handleClick() {
this.$emit('update:modelValue', this.modelValue + 3);
}
},
template: `
{{ modelValue }}
`
});
const vm = app.mount('#root');
script>
html>
v-slot
,可以缩写为【#】,子组件中用法不变v-slot
,#default
(这点所有指令都一样,v-bind
、v-on
)v-slot
属性只能在
上使用,但在【只有默认插槽时】可以在组件标签上使用slot-scope
获取子组件的信息,在内容中使用。这里可以用解构语法去直接获取想要的属性slot-scope
属性弃用,作用域插槽通过 v-slot:xxx="slotProps"
的 slotProps
来获取子组件传出的属性v-slot={item}
,还可以重命名 v-slot="{item: newItem}"
和定义默认值 v-slot="{item = '默认值'}"
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>lesson 20title>
<script src="https://unpkg.com/vue@next">script>
head>
<body>
<div id="root">div>
body>
<script>
const app = Vue.createApp({
template: `
// 具名插槽
header
footer
// 作用域插槽
// 可使用解构语法直接获取指定属性名
{{item}}
`
});
app.component('layout', {
template: `
content
`
});
app.component('list', {
data() {
return {list: [1, 2, 3]}
},
template: `
`
});
const vm = app.mount('#root');
script>
html>
compoent
这个标签,来随时动态切换组件的现实
keep-alive
标签来缓存第一次渲染后的输入状态以及变更情况。DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>lesson 21title>
<script src="https://unpkg.com/vue@next">script>
head>
<body>
<div id="root">div>
body>
<script>
const app = Vue.createApp({
template: `
// 动态组件
`
});
app.component('common-item', {
template: `hello world`
});
app.component('async-common-item', Vue.defineAsyncComponent(() => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve({
template: `this is an async component`
})
}, 4000)
})
}))
const vm = app.mount('#root');
script>
html>
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>lesson 23title>
<style>
/* 动画
@keyframes leftToRight {
0% {
transform: translateX(-100px);
}
50% {
transform: translateX(-50px);
}
0% {
transform: translateX(0px);
}
}
.animation {
animation: leftToRight 3s;
} */
/* 过渡
.transition {
transition: 3s background-color ease;
}
.blue {
background: blue;
}
.green {
background: green;
} */
.transition {
transition: 3s background-color ease;
}
style>
<script src="https://unpkg.com/vue@next">script>
head>
<body>
<div id="root">div>
body>
<script>
const app = Vue.createApp({
data() {
return {
styleObj: {
background: 'blue'
}
}
},
methods: {
handleClick() {
if (this.styleObj.background === 'blue') {
this.styleObj.background = 'green';
} else {
this.styleObj.background = 'blue'
}
}
},
template: `
hello world
`
});
const vm = app.mount('#root');
script>
html>
transition
标签包裹节点可为节点添加过渡或动画效果。v-[动作(enter-from、enter-to、leave-from、deave-to)]
命名 css 类即可触发对应效果,同时可使用 name
指定效果前缀。enter-from-class
、enter-to-class
之类的属性自定义效果类,此时直接传入类名即可生效。transition
的 type
属性指定基准效果(animation / translation),效果的持续时间以基准效果为准。DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>lesson 24title>
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css"
/>
<style>
@keyframes shake {
0% {
transform: translateX(-100px)
}
50% {
transform: translateX(-50px)
}
100% {
transform: translateX(50px)
}
}
.hello-leave-active {
animation: shake 3s;
}
.hello-enter-active {
animation: shake 3s;
}
style>
<script src="https://unpkg.com/vue@next">script>
head>
<body>
<div id="root">div>
body>
<script>
// 单元素,单组件的入场出场动画
const app = Vue.createApp({
data() {
return {
show: false
}
},
methods: {
handleClick() {
this.show = !this.show;
}
},
template: `
hello world
// 使用第三方动画库
hello world
`
});
const app = Vue.createApp({
data() {
return {
show: false
}
},
methods: {
handleClick() {
this.show = !this.show;
},
handleBeforeEnter(el) {
el.style.color = "red";
},
handleEnterActive(el, done) {
const animation = setInterval(() => {
const color = el.style.color;
if (color === 'red') {
el.style.color = 'green';
} else {
el.style.color = 'red';
}
}, 1000)
setTimeout(() => {
clearInterval(animation);
done();
}, 3000)
},
handleEnterEnd(el) {
alert(123);
}
},
template: `
hello world
`
});
const vm = app.mount('#root');
script>
html>
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css"
/>
<title>lesson 25title>
<style>
.v-leave-to {
opacity: 0;
}
.v-enter-from {
opacity: 0;
}
.v-enter-active,
.v-leave-active {
transition: opacity 1s ease-in;
}
.v-leave-from,
.v-enter-to {
opacity: 1;
}
style>
<script src="https://unpkg.com/vue@next">script>
head>
<body>
<div id="root">div>
body>
<script>
// 多个单元素标签之间的切换
// 多个单组件之间的切换
const ComponentA = {
template: 'hello world'
}
const ComponentB = {
template: 'bye world'
}
const app = Vue.createApp({
data() {
return {component: 'component-a'}
},
methods: {
handleClick() {
if (this.component === 'component-a') {
this.component = 'component-b';
} else {
this.component = 'component-a';
}
},
},
components: {
'component-a': ComponentA,
'component-b': ComponentB,
},
template: `
`
});
const vm = app.mount('#root');
script>
html>
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css"
/>
<title>lesson 26title>
<style>
.v-enter-from {
opacity: 0;
transform: translateY(30px);
}
.v-enter-active {
transition: all .5s ease-in;
}
.v-enter-to {
opacity: 1;
transform: translateY(0);
}
.v-leave-active {
transition: all .5s ease-in;
}
.v-leave-to {
opacity: 1;
transform: translateY(-30px);
}
.v-move {
transition: all .5s ease-in;
}
.list-item {
display: inline-block;
margin-right: 10px;
}
style>
<script src="https://unpkg.com/vue@next">script>
head>
<body>
<div id="root">div>
body>
<script>
// 列表动画的实现
const app = Vue.createApp({
data() {
return {list: [1, 2, 3]}
},
methods: {
handleClick() {
this.list.unshift(this.list.length + 1);
},
handleDelete() {
this.list.pop();
},
},
template: `
{{ item }}
`
});
const vm = app.mount('#root');
script>
html>
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>lesson 27title>
<script src="https://unpkg.com/vue@next">script>
head>
<body>
<div id="root">div>
body>
<script>
// 状态动画
const app = Vue.createApp({
data() {
return {
number: 1,
animateNumber: 1
}
},
methods: {
handleClick() {
this.number = 100 + this.number;
if (this.animateNumber < this.number) {
const animation = setInterval(() => {
this.animateNumber += 1;
if (this.animateNumber === this.number) {
clearInterval(animation);
}
}, 10);
}
},
},
template: `
{{ animateNumber }}
`
});
const vm = app.mount('#root');
script>
html>
data
, methods
优先级高于 mixin data
, methods
优先级DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>lesson 28title>
<script src="https://unpkg.com/vue@next">script>
head>
<body>
<div id="root">div>
body>
<script>
const myMixin = {
number: 1
}
const app = Vue.createApp({
mixins: [myMixin],
number: 2,
template: `
{{ this.$options.number }}
`
});
// 修改自定义属性的使用优先级
app.config.optionMergeStrategies.number = (mixinVal, appValue) => {
return mixinVal || appValue;
}
const vm = app.mount('#root');
script>
html>
directive
方法自定义全局指令
mounted
、updated
生命周期函数,则可以是直接使用函数替代。el
是 Dom 节点本身,第二个 binding
为绑定的参数,其中 arg
为绑定的参数别名,value
为绑定的值。directives
属性临时导入自定义指令DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>lesson 29title>
<style>
.header {
position: absolute
}
style>
<script src="https://unpkg.com/vue@next">script>
head>
<body>
<div id="root">div>
body>
<script>
临时导入自定义指令 directives
const directives = {
focus: {
mounted(el) {
el.focus();
}
}
}
const app = Vue.createApp({
directives: directives,
data() {
return {
distance: 110
}
},
template: `
`
});
// 自定义全局指令 directive
app.directive('focus', {
mounted(el) {
el.focus();
}
})
app.directive('pos', (el, binding) => {
el.style[binding.arg] = (binding.value + 'px');
})
const vm = app.mount('#root');
script>
html>
使用 Teleport 传送门可将节点或组件传送到指定标签上,teleport
标签中使用 to
属性指定传送的目标标签,类似 Dom 节点定位方式。
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>lesson 30title>
<style>
.area {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
width: 200px;
height: 300px;
background: green;
}
.mask {
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
background: #000;
opacity: 0.5;
color: #fff;
font-size: 100px;
}
style>
<script src="https://unpkg.com/vue@next">script>
head>
<body>
<div id="root">div>
<div id="hello">div>
body>
<script>
// teleport 传送门
const app = Vue.createApp({
data() {
return {
show: false,
message: 'hello'
}
},
methods: {
handleBtnClick() {
this.show = !this.show;
}
},
template: `
{{ message }}
`
});
const vm = app.mount('#root');
script>
html>
template -> render -> h -> 虚拟DOM(JS对象)-> 真实 DOM -> 展示到页面上
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>lesson 31title>
<script src="https://unpkg.com/vue@next">script>
head>
<body>
<div id="root">div>
body>
<script>
// render function
const app = Vue.createApp({
template: `
hello dell
`
});
app.component('my-title', {
props: ['level'],
render() {
const {h} = Vue;
return h('h' + this.level, {}, [
this.$slots.default(),
h('h4', {}, 'dell')
])
}
})
const vm = app.mount('#root');
script>
html>
use
方法载入插件。DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>lesson 32title>
<script src="https://unpkg.com/vue@next">script>
head>
<body>
<div id="root">div>
body>
<script>
const myPlugin = {
install(app, options) {
app.provide('name', 'Dell Lee');
app.directive('focus', {
mounted(el) {
el.focus();
}
})
app.mixin({
mounted() {
console.log('mixin')
}
})
// 扩展全局变量
app.config.globalProperties.$sayHello = 'hello world';
}
}
const app = Vue.createApp({
template: `
`
});
app.component('my-title', {
inject: ['name'],
mounted() {
// 使用全局变量
console.log(this.$sayHello);
},
template: `{{name}}`
})
app.use(myPlugin, {name: 'dell'});
const vm = app.mount('#root');
script>
html>
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>lesson 33title>
<script src="https://unpkg.com/vue@next">script>
head>
<body>
<div id="root">div>
body>
<script>
// 对数据做校验的插件
const app = Vue.createApp({
data() {
return {name: 'dell', age: 23}
},
rules: {
age: {
validate: age => age > 25,
message: 'too young, to simple'
},
name: {
validate: name => name.length >= 4,
message: 'name too short'
}
},
template: `
name:{{ name }}, age:{{ age }}
`
});
const validatorPlugin = (app, options) => {
app.mixin({
created() {
for (let key in this.$options.rules) {
const item = this.$options.rules[key];
this.$watch(key, (value) => {
const result = item.validate(value);
if (!result) console.log(item.message);
})
}
}
})
}
app.use(validatorPlugin);
const vm = app.mount('#root');
script>
html>
setup
函数执行于 created
之前,需要两个参数:props
(外部传入属性),context
(上下文)setup
函数中无法使用 this
(处于 created
之前,组件未创建成功。)setup
中返回的属性 / 方法会暴露在组件中供组件使用。setup
函数,DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>lesson 34title>
<script src="https://unpkg.com/vue@next">script>
head>
<body>
<div id="root">div>
body>
<script>
const app = Vue.createApp({
template: `
{{ name }}
`,
methods: {
test() {
console.log(this.$options.setup());
}
},
mounted() {
this.test();
},
// created 实例被完全初始化之前
setup(props, context) {
return {
name: 'dell',
handleClick: () => {
alert(123)
}
}
}
});
const vm = app.mount('#root');
script>
html>
ref
处理基础类型的数据
'dell'
变成 proxy({value: 'dell'})
这样的一个响应式引用ref
生成变量时,使用 变量名.value
,但 Vue 3 底层会自动调用 value ,因此使用时与一般情况相同即可reactive
处理非基础类型的数据
{ name: 'dell' }
变成 proxy({ name: 'dell'})
这样的一个响应式引用setup
中直接将 reactive
引用的属性导出(let { name } = reactive({ name: val });
),此时仅导出一个普通变量,若要使此变量可响应,可使用 toRefs
方法。ref
与 reactive
可代替 data
函数,因此使用此语法时可省略 data
函数。readonly
生成一个只读数据toRefs
可将一个 reactive
引用的值变为 ref
引用。DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>lesson 35title>
<script src="https://unpkg.com/vue@next">script>
head>
<body>
<div id="root">div>
body>
<script>
const app = Vue.createApp({
template: `
{{ name }}
`,
setup(props, context) {
// const { ref } = Vue;
//
// let name = ref('dell');
// setTimeout(() => {
// name.value = 'lee'
// }, 2000)
// return { name }
const {reactive, readonly, toRefs} = Vue;
//
const nameObj = reactive({name: 'dell', age: 28});
setTimeout(() => {
nameObj.name = 'lee'
}, 2000)
// toRefs proxy({ name: 'dell', age: 28}), {
// name: proxy({ value: 'dell'}),
// age: proxy({value: 28})
// }
const {name, age} = toRefs(nameObj);
return {name}
}
});
const vm = app.mount('#root');
script>
html>
toRef
旨在当响应式引用中没有属性的时候给予一个默认值,此时变量无需导出:let age = toRef({ name = 'liu' }, 'default')
context
:包含上下文环境,常用属性:
attrs
:包含所有 Non-props
(父组件中传入但未在自组价中使用 props
接收的参数)slots
:包含所有插槽,等价于 this.$slots
emit
:等价于 this.$emit()
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>lesson 36title>
<script src="https://unpkg.com/vue@next">script>
head>
<body>
<div id="root">div>
body>
<script>
// toRef, context
const app = Vue.createApp({
methods: {
handleChange() {
alert('change');
}
},
template: `
parent `,
});
app.component('child', {
template: '123123',
setup(props, context) {
const {h} = Vue;
const {attrs, slots, emit} = context;
function handleClick() {
emit('change');
}
return {handleClick}
}
})
const vm = app.mount('#root');
script>
html>
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>lesson 37title>
<script src="https://unpkg.com/vue@next">script>
head>
<body>
<div id="root">div>
body>
<script>
// 关于 list 操作的内容进行了封装
const listRelativeEffect = () => {
const {reactive} = Vue;
const list = reactive([]);
const addItemToList = (item) => {
list.push(item);
}
return {list, addItemToList}
}
// 关于 inputValue 操作的内容进行了封装
const inputRelativeEffect = () => {
const {ref} = Vue;
const inputValue = ref('');
const handleInputValueChange = (e) => {
inputValue.value = e.target.value
}
return {inputValue, handleInputValueChange}
}
const app = Vue.createApp({
setup() {
// 流程调度中转
const {list, addItemToList} = listRelativeEffect();
const {inputValue, handleInputValueChange} = inputRelativeEffect();
return {
list, addItemToList,
inputValue, handleInputValueChange
}
},
template: `
- {{ item }}
`,
});
const vm = app.mount('#root');
script>
html>
computed
方法,传入一个回调方法进行属性计算get
、set
方法的对象来设置计算属性的取值赋值方法DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>lesson 38title>
<script src="https://unpkg.com/vue@next">script>
head>
<body>
<div id="root">div>
body>
<script>
// computed 计算属性
const app = Vue.createApp({
setup() {
const {reactive, computed} = Vue;
const countObj = reactive({count: 0});
const handleClick = () => {
countObj.count += 1;
}
let countAddFive = computed({
get: () => {
return countObj.count + 5;
},
set: (param) => {
countObj.count = param - 5;
}
})
setTimeout(() => {
countAddFive.value = 100;
}, 3000)
return {countObj, countAddFive, handleClick}
},
template: `
{{ countObj.count }} -- {{ countAddFive }}
`,
});
const vm = app.mount('#root');
script>
html>
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>lesson 39title>
<script src="https://unpkg.com/vue@next">script>
head>
<body>
<div id="root">div>
body>
<script>
const app = Vue.createApp({
setup() {
const {reactive, watch, watchEffect, toRefs} = Vue;
const nameObj = reactive({
name: 'dell', englishName: 'lee'
})
// watch 侦听器
// 具备一定的惰性 lazy
// 参数可以拿到原始和当前值
// 侦听 reactive 的值时,需要传入一个侦听函数,返回需要侦听的值。
// 可以侦听多个数据的变化,用一个侦听器承载
watch([() => nameObj.name, () => nameObj.englishName], ([curName, curEng], [prevName, preEng]) => {
console.log('watch', curName, prevName, '---', curEng, preEng);
}, {immediate: true})
// watchEffect 侦听器,偏向于 effect
// 立即执行,没有惰性 immediate
// 不需要传递你要侦听的内容,自动会感知代码依赖,不需要传递很多参数,只要传递一个回调函数
// 不能获取之前数据的值
const stop = watchEffect(() => {
console.log(nameObj.name);
console.log(nameObj.englishName);
setTimeout(() => {
stop();
}, 5000)
})
const {name, englishName} = toRefs(nameObj);
return {name, englishName}
},
template: `
Name:
Name is {{ name }}
EnglishName:
EnglishName is {{ englishName }}
`,
});
const vm = app.mount('#root');
script>
html>
setup
函数执行时间介于旧生命周期的 beforeCreated
与 created
之间,因此 Composition API 未提供这两个生命周期函数,相关的操作直接在 setup
函数内进行即可。DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>lesson 40title>
<script src="https://unpkg.com/vue@next">script>
head>
<body>
<div id="root">div>
body>
<script>
const app = Vue.createApp({
// beforeMount => onBeforeMount
// mounted => onMounted
// beforeUpdate => onBeforeUpdate
// beforeUnmount => onBeforeUnmount
// unmouted => onUnmounted
setup() {
const {
ref, onBeforeMount, onMounted, onBeforeUpdate, onUpdated,
onRenderTracked, onRenderTriggered
} = Vue;
const name = ref('dell')
onBeforeMount(() => {
console.log('onBeforeMount')
})
onMounted(() => {
console.log('onMounted')
})
onBeforeUpdate(() => {
console.log('onBeforeUpdate')
})
onUpdated(() => {
console.log('onUpdated')
})
// 每次渲染后重新收集响应式依赖
onRenderTracked(() => {
console.log('onRenderTracked')
})
// 每次触发页面重新渲染时自动执行
onRenderTriggered(() => {
console.log('onRenderTriggered')
})
const handleClick = () => {
name.value = 'lee'
}
return {name, handleClick}
},
template: `
{{name}}
`,
});
const vm = app.mount('#root');
script>
html>
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>lesson 41title>
<script src="https://unpkg.com/vue@next">script>
head>
<body>
<div id="root">div>
body>
<script>
provide, inject
dom ref
const app = Vue.createApp({
setup() {
const { provide, ref, readonly } = Vue;
const name = ref('dell');
// 限制后代组件对 name 属性的修改
provide('name', readonly(name));
// 为后代组件开放 name 属性修改方法
provide('changeName', (value) => {
name.value = value;
});
return { }
},
template: `
`,
});
app.component('child', {
setup() {
const { inject } = Vue;
const name = inject('name');
const changeName = inject('changeName');
const handleClick = () => {
changeName('lee');
}
return { name, handleClick }
},
template: '{{name}}'
})
// Composition API 的语法下,获取真实的 DOM 元素节点
// 使用 ref(null) 定义一个 ref 节点,接收的变量名需与目标节点的 ref 属性相同
const app = Vue.createApp({
setup() {
const { ref, onMounted } = Vue;
const hello = ref(null);
onMounted(() => {
console.log(hello.value);
})
return { hello }
},
template: `
hello world
`,
});
const vm = app.mount('#root');
script>
html>
nrm
工具:包含国内常用镜像源
nrm ls
:查看镜像列表nrm use <镜像名>
:使用对应镜像npm install -g @vue/cli
安装最新脚手架工具
vue create <项目名>
:创建新项目
(模板区域)、
(逻辑渲染区域)、
(样式区域)三个区域组成
DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title><%= htmlWebpackPlugin.options.title %>title>
head>
<body>
<div id="app">div>
body>
html>
// main.js
import { createApp } from 'vue'
import App from './App.vue'
// 将创建的核心对象绑定在 ID 为 "app" 的节点上
createApp(App).mount('#app')
<template>
<img alt="Vue logo" src="./assets/logo.png">
<HelloWorld msg="Welcome to Your Vue.js App"/>
template>
<script>
// 单文件组件
import HelloWorld from './components/HelloWorld.vue'
export default {
name: 'App',
components: {
HelloWorld
},
}
script>
<style>
style>
<template>
<h1>{{ msg }}h1>
template>
<script>
export default {
name: 'HelloWorld',
props: {
msg: String
}
}
script>
<style>
style>
带有 scoped
属性时,即
此时样式仅在当前组件中生效。
<template>
<div>
<input v-model="inputValue"/>
<button class="button" @click="handleAddItem">提交button>
div>
<ul>
<list-item v-for="(item, index) in list" :key="index" :msg="item"/>
ul>
template>
<script>
import {reactive, ref} from 'vue';
import ListItem from './components/ListItem';
export default {
name: 'App',
components: {ListItem},
setup() {
const inputValue = ref('');
const list = reactive([]);
const handleAddItem = () => {
list.push(inputValue.value);
inputValue.value = '';
};
return {handleAddItem, inputValue, list}
}
}
script>
<style>
.button {
margin-left: 20px;
color: red;
}
style>
<template>
<li class="button">{{ msg }}li>
template>
<script>
export default {
name: 'ListItem',
props: {
msg: String
}
}
script>
<style>
style>
#
为hash路由
:是跳转路由的标签
:负责展示当前路由对应的组件路由// route.js - 路由
import {createRouter, createWebHashHistory} from 'vue-router'
import Home from '../views/Home.vue'
import Login from '../views/Login.vue'
const routes = [
{
path: '/',
name: 'Home',
// 页面加载完成即导入
component: Home
},
{
path: '/login',
name: 'Login',
component: Login
},
{
path: '/about',
name: 'About',
// 异步加载路由,仅在触发后请求加载
component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
}
]
const router = createRouter({
history: createWebHashHistory(),
routes
})
export default router
<template>
<div id="nav">
<router-link to="/">Homerouter-link>
|
<router-link to="/about">Aboutrouter-link>
|
<router-link to="/login">Loginrouter-link>
div>
<router-view/>
template>
<style>style>
store
,用来存放全局的数据store
中的 state
完成dispatch
方法,派发一个 action,方法名为 change
this.$store.dispatch('change', str)
dispatch
和 actions
做关联,change
为 actions
中的方法change
这个 action,执行 store
中 actions
下面的 change
方法commit
提交一个 mutation,方法名为 change
store.commit('change', str)
commit
和 mutation 做关联,change
为 mutations
中的方法change
改变,执行 store
中 mutations
下面的 change
方法改变数据
mutations
里面只允许写同步代码,不允许写异步代码(非强制,仅为代码设计要求)actions
处理dispatch
流程,直接使用 commit
触发 mutationactions
中的方法第一个参数为 store
对象;mutations
中的方法第一个参数为 store
中的 state
对象// 全局store的定义
// store/index.js
import {createStore} from 'vuex'
// VueX 数据管理框架
// VueX 创建了一个全局唯一的仓库,用来存放全局的数据
export default createStore({
// 定义全局数据
state: {
name: 'dell'
},
// mutation 里面只允许写同步代码,不允许写异步代码
// commit 和 mutation 做关联
mutations: {
change(state, str) {
state.name = str;
}
},
// dispatch 和 actions 做关联
actions: {
change(store, str) {
setTimeout(() => {
store.commit('change', str)
}, 2000)
}
}
})
<template>
<div class="home">
<h1 @click="handleClick">This is a home pageh1>
<h1>{{ myName }}h1>
div>
template>
<script>
export default {
name: 'Home',
computed: {
myName() {
return this.$store.state.name;
}
},
methods: {
handleClick() {
// 异步修改全局数据
this.$store.dispatch('change', 'hello world');
// 同步修改全局数据
this.$store.commit('change', 'hello world');
}
}
}
script>
useStore
获取 store 对象// store/index.js
import {createStore} from 'vuex'
export default createStore({
state: {name: 'dell'},
mutations: {
changeName(state, str) {
state.name = str;
}
},
actions: {
getData(store) {
setTimeout(() => {
store.commit('changeName', 'hello')
}, 2000)
}
}
})
<template>
<div class="home">
<h1 @click="handleClick">This is a home pageh1>
<h1>{{ name }}h1>
div>
template>
<script>
import {toRefs} from 'vue';
import {useStore} from 'vuex';
export default {
name: 'Home',
setup() {
const store = useStore();
const {name} = toRefs(store.state);
const handleClick = () => {
store.dispatch('getData')
}
return {name, handleClick}
}
}
script>