一、组件的核心概念-属性props几种写法

我们的开发都是围绕着options来的

Vue核心梳理

Vue核心梳理

<template>
<div>
name: {{ name }}
<br />
type: {{ type }}
<br />
list: {{ list }}
<br />
isVisible: {{ isVisible }}
<br />
<button @click="handleClick">change type</button>
</div>
</template>

<script>
export default {
name: "PropsDemo",
// inheritAttrs: false,
// 这种写法不利于后期维护
// props: ['name', 'type', 'list', 'isVisible'],
props: {
name: String,
type: {
validator: function(value) {
// 这个值必须匹配下列字符串中的一个
return ["success", "warning", "danger"].includes(value);
}
},
list: {
type: Array,
// 对象或数组默认值必须从一个工厂函数获取
default: () => []
},
isVisible: {
type: Boolean,
default: false
},
onChange: {
type: Function,
default: () => {}
}
},
methods: {
handleClick() {
// 不要这么做、不要这么做、不要这么做
// this.type = "warning";

// 可以,还可以更好
this.onChange(this.type === "success" ? "warning" : "success");
}
}
};
</script>
// 用法
<Props
name="Hello Vue!" // 原生属性
:type="type"
:is-visible="false"
:on-change="handlePropChange"
title="属性Demo" // 原生属性
class="test1" // 原生属性
:class="['test2']"
:
// 原生属性
/>

二、组件的核心概念-事件

Vue核心梳理

<template>
<div>
name: {{ name || "--" }}
<br />
<input :value="name" @change="handleChange" />
<br />
<br />
<div @click="handleDivClick">
<button @click="handleClick">重置成功</button>&nbsp;&nbsp;&nbsp;
<button @click.stop="handleClick">重置失败</button>
</div>
</div>
</template>

<script>
export default {
name: "EventDemo",
props: {
name: String
},
methods: {
handleChange(e) {
this.$emit("change", e.target.value);
},
handleDivClick() {
this.$emit("change", "");
},
handleClick(e) {
// 都会失败
//e.stopPropagation();
}
}
};
</script>

三、组件的核心概念-插槽

Vue核心梳理

 <a-tab-pane key="slot" tab="插槽">
<h2>2.6 新语法</h2>
<SlotDemo>
<p>default slot</p>
<template v-slot:title>
<p>title slot1</p>
<p>title slot2</p>
</template>
<template v-slot:item="props">
<p>item slot-scope {{ props }}</p>
</template>
</SlotDemo>
<br />
<h2>老语法</h2>
<SlotDemo>
<p>default slot</p>
<p slot="title">title slot1</p>
<p slot="title">title slot2</p>
<p slot="item" slot-scope="props">item slot-scope {{ props }}</p>
</SlotDemo>
</a-tab-pane>
<script>
import Slot from "./Slot";
export default {
components: {
SlotDemo: Slot
},
data: () => {
return {
name: "",
type: "success",
bigPropsName: "Hello world!"
};
},
};
</script>
<!-- Slot.vue -->
<template>
<div>
<slot />
<slot name="title" />
<slot name="item" v-bind="{ value: 'vue' }" />
</div>
</template>

<script>
export default {
name: "SlotDemo"
};
</script>

Vue核心梳理

大属性例子

<!--子组件 bigProps.vue-->

<template>
<div>
{{ name }}
<br />
<button @click="handleChange">change name</button>
<br />
<!-- {{ slotDefault }} -->
<VNodes :vnodes="slotDefault" />
<br />
<VNodes :vnodes="slotTitle" />
<br />
<VNodes :vnodes="slotScopeItem({ value: 'vue' })" />
</div>
</template>

<script>
export default {
name: "BigProps",
components: {
VNodes: {
functional: true,
render: (h, ctx) => ctx.props.vnodes
}
},
props: {
name: String,
onChange: {
type: Function,
default: () => {}
},
slotDefault: Array,
slotTitle: Array,
slotScopeItem: {
type: Function,
default: () => {}
}
},
methods: {
handleChange() {
this.onChange("Hello vue!");
}
}
};
</script>
<!--父组件调用-->
<a-tab-pane key="bigProps" tab="大属性">
<BigProps
:name="bigPropsName"
:on-change="handleBigPropChange"
:slot-default="getDefault()"
:slot-title="getTitle()"
:slot-scope-item="getItem"
/>
</a-tab-pane>

四、双向绑定和单项数据流并不冲突

Vue核心梳理

Vue核心梳理

五、如何触发组件的更新

Vue核心梳理

Vue核心梳理

Vue核心梳理

六、合理应用计算属性和监听器

6.1 计算属性Computed

  • 减少模板中的计算逻辑
  • 数据缓存
  • 依赖固定的数据类型(响应式数据)
<template>
<div>
<p>Reversed message1: "{{ reversedMessage1 }}"</p>
<p>Reversed message2: "{{ reversedMessage2() }}"</p>
<p>{{ now }}</p>
<button @click="() => $forceUpdate()">forceUpdate</button>
<br />
<input v-model="message" />
</div>
</template>
<script>
export default {
data() {
return {
message: "hello vue"
};
},
computed: {
// 计算属性的 getter
reversedMessage1: function() {
console.log("执行reversedMessage1");
return this.message
.split("")
.reverse()
.join("");
},
now: function() {
return Date.now();
}
},
methods: {
reversedMessage2: function() {
console.log("执行reversedMessage2");
return this.message
.split("")
.reverse()
.join("");
}
}
};
</script>

6.2 监听watcher

  • 更加灵活通用
  • watcher可以执行任何逻辑,包括函数节流、ajax异步获取数据
<template>
<div>
{{ $data }}
<br />
<button @click="() => (a += 1)">a+1</button>
</div>
</template>
<script>
export default {
data: function() {
return {
a: 1,
b: { c: 2, d: 3 },
e: {
f: {
g: 4
}
},
h: []
};
},
watch: {
a: function(val, oldVal) {
this.b.c += 1;
console.log("new: %s, old: %s", val, oldVal);
},
"b.c": function(val, oldVal) {
this.b.d += 1;
console.log("new: %s, old: %s", val, oldVal);
},
"b.d": function(val, oldVal) {
this.e.f.g += 1;
console.log("new: %s, old: %s", val, oldVal);
},
e: {
handler: function(val, oldVal) {
this.h.push("????");
console.log("new: %s, old: %s", val, oldVal);
},
deep: true
},
h(val, oldVal) {
console.log("new: %s, old: %s", val, oldVal);
}
}
};
</script>

watcher中使用节流

<template>
<div>
{{ fullName }}

<div>firstName: <input v-model="firstName" /></div>
<div>lastName: <input v-model="lastName" /></div>
</div>
</template>
<script>
export default {
data: function() {
return {
firstName: "Foo",
lastName: "Bar",
fullName: "Foo Bar"
};
},
watch: {
firstName: function(val) {
clearTimeout(this.firstTimeout);
this.firstTimeout = setTimeout(() => {
this.fullName = val + " " + this.lastName;
}, 500);
},
lastName: function(val) {
clearTimeout(this.lastTimeout);
this.lastTimeout = setTimeout(() => {
this.fullName = this.firstName + " " + val;
}, 500);
}
}
};
</script>

6.3 computed vs watcher

  • computed 能做的,watcher 都可以做,反之不行
  • 能用computed的尽量使用computed

七、生命周期的应用场景和函数式组件

7.1 生命周期

Vue核心梳理

Vue核心梳理

Vue核心梳理

Vue核心梳理

<template>
<div>
{{ log("render") }}
{{ now }}
<button @click="start = !start">{{ start ? "停止" : "开始" }}</button>
</div>
</template>
<script>
import moment from "moment";
export default {
data: function() {
console.log("data");
this.moment = moment;
this.log = window.console.log;
return {
now: moment(new Date()).format("YYYY-MM-DD HH:mm:ss"),
start: false
};
},
watch: {
start() {
this.startClock();
}
},
beforeCreate() {
console.log("beforeCreate");
},
created() {
console.log("created");
},
beforeMount() {
console.log("beforeMount");
},
mounted() {
console.log("mounted");
this.startClock();
},
beforeUpdate() {
console.log("beforeUpdate");
},
updated() {
console.log("updated");
},
beforeDestroy() {
console.log("beforeDestroy");
clearInterval(this.clockInterval);
},
destroyed() {
console.log("destroyed");
},
methods: {
startClock() {
clearInterval(this.clockInterval);
if (this.start) {
this.clockInterval = setInterval(() => {
this.now = moment(new Date()).format("YYYY-MM-DD HH:mm:ss");
}, 1000);
}
}
}
};
</script>

打印顺序 beforeCreate - data - created - beforeMount - render - mounted

7.2 函数式组件

  • functional:true
  • 无状态、无实例、没有this上下文、没有生命周期
// TempVar.js
export default {
functional: true,
render: (h, ctx) => {
return ctx.scopedSlots.default && ctx.scopedSlots.default(ctx.props || {});
}
};
// Functional.vue
<template functional>
<div>
{{ props }}
</div>
</template>
// 使用
<template>
<div>
<a-tabs>
<a-tab-pane key="Functional" tab="函数式组件">
<Functional :name="name" />
<TempVar
:var1="`hello ${name}`"
:var2="destroyClock ? 'hello vue' : 'hello world'"
>
<template v-slot="{ var1, var2 }">
{{ var1 }}
{{ var2 }}
</template>
</TempVar>
</a-tab-pane>
</a-tabs>
</div>
</template>
<script>
import Functional from "./Functional";
import TempVar from "./TempVar";
export default {
components: {
Functional,
TempVar
},
data() {
return {
destroyClock: false,
name: "vue"
};
}
};
</script>

八、Vue指令

8.1 内置指令

Vue核心梳理

<template>
<div>
<h2>v-text</h2>
<div v-text="'hello vue'">hello world</div>
<h2>v-html</h2>
<div v-html="'<span style=&quot;color: red&quot;>hello vue</span>'">
hello world
</div>
<h2>v-show</h2>
<div v-show="show">hello vue</div>
<button @click="show = !show">change show</button>
<h2>v-if v-esle-if v-else</h2>
<div v-if="number === 1">hello vue {{ number }}</div>
<div v-else-if="number === 2">hello world {{ number }}</div>
<div v-else>hello geektime {{ number }}</div>
<h2>v-for v-bind</h2>
<div v-for="num in [1, 2, 3]" v-bind:key="num">hello vue {{ num }}</div>
<h2>v-on</h2>
<button v-on:click="number = number + 1">number++</button>
<h2>v-model</h2>
<input v-model="message" />
<h2>v-pre</h2>
<div v-pre>{{ this will not be compiled }}</div>
<h2>v-once</h2>
<div v-once>
{{ number }}
</div>
</div>
</template>
<script>
export default {
data: function() {
this.log = window.console.log;
return {
show: true,
number: 1,
message: "hello"
};
}
};
</script>

8.2 自定义指令

Vue核心梳理

<template>
<div>
<button @click="show = !show">
销毁
</button>
<button v-if="show" v-append-text="`hello ${number}`" @click="number++">
按钮
</button>
</div>
</template>
<script>
export default {
directives: {
appendText: {
bind() {
console.log("bind");
},
inserted(el, binding) {
el.appendChild(document.createTextNode(binding.value));
console.log("inserted", el, binding);
},
update() {
console.log("update");
},
componentUpdated(el, binding) {
el.removeChild(el.childNodes[el.childNodes.length - 1]);
el.appendChild(document.createTextNode(binding.value));
console.log("componentUpdated");
},
unbind() {
console.log("unbind");
}
}
},
data() {
return {
number: 1,
show: true
};
}
};
</script>

九、template和jsx

9.1 JSX VS template

Template

  • 学习成本低
  • 大量内置指令简化开发
  • 组件作用域css
  • 但灵活性低

JSX

  • 总体上很灵活

9.2 以下是jsx写法

// index.vue
<script>
import Props from "./Props";
import Event from "./Event";
import Slot from "./Slot";
import BigProps from "./BigProps";
export default {
components: {
Props,
Event,
SlotDemo: Slot,
BigProps
},
data: () => {
return {
name: "",
type: "success",
bigPropsName: "Hello world!"
};
},
methods: {
handlePropChange(val) {
this.type = val;
},
handleEventChange(val) {
this.name = val;
},
handleBigPropChange(val) {
this.bigPropsName = val;
},
getDefault() {
return [<p>default slot</p>];
},
getTitle() {
return [<p>title slot1</p>, <p>title slot2</p>];
},
getItem(props) {
return [<p>{`item slot-scope ${JSON.stringify(props)}`}</p>];
}
},
render() {
const {
type,
handlePropChange,
name,
handleEventChange,
bigPropsName,
getDefault,
getTitle,
getItem,
handleBigPropChange
} = this;
const slotDemoProps = {
scopedSlots: {
item(props) {
return `item slot-scope ${JSON.stringify(props)}`;
}
},
props: {}
};
const bigProps = {
props: {
onChange: handleBigPropChange
}
};
return (
<div>
<a-tabs>
<a-tab-pane key="props" tab="属性">
<Props
name="Hello Vue!"
type={type}
isVisible={false}
{...{ props: { onChange: handlePropChange } }}
title="属性Demo"
class="test1"
class={["test1", "test2"]}
style={{ marginTop: "10px" }}
/>
</a-tab-pane>
<a-tab-pane key="event" tab="事件">
<Event name={name} onChange={handleEventChange} />
</a-tab-pane>
<a-tab-pane key="slot" tab="插槽">
<SlotDemo {...slotDemoProps}>
<p>default slot</p>
<p slot="title">title slot1</p>
<p slot="title">title slot2</p>
</SlotDemo>
</a-tab-pane>
<a-tab-pane key="bigProps" tab="大属性">
<BigProps

相关文章: