hookEvent,原来可以这样监听组件生命周期
内部监听生命周期函数
在 Vue 组件中,可以用过$on,$once 去监听所有的生命周期钩子函数,如监听组件的 updated 钩子函数可以写成 this.$on(‘hook:updated’, () => {})
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| <template> <div class="echarts"></div> </template> <script> export default { mounted() { this.chart = echarts.init(this.$el); window.addEventListener("resize", this.$_handleResizeChart); }, updated() { }, created() { }, beforeDestroy() { window.removeEventListener("resize", this.$_handleResizeChart); }, methods: { $_handleResizeChart() { this.chart.resize(); } } }; </script>
|
将监听resize
事件与销毁resize
事件放到一起,现在两段代码分开而且相隔几百行代码,可读性比较差
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| export default { mounted() { this.chart = echarts.init(this.$el);
window.addEventListener("resize", this.$_handleResizeChart); this.$once("hook:beforeDestroy", () => { window.removeEventListener("resize", this.$_handleResizeChart); }); }, updated() {}, created() {}, methods: { $_handleResizeChart() { } } };
|
外部监听生命周期函数
情境:想在外部监听组件的生命周期函数,使用一个第三方组件,需要监听第三方组件数据的变化,但是组件又没有提供 change 事件。
Vue 支持在外部监听组件的生命周期钩子函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <template> <custom-select @hook:updated="$_handleSelectUpdated" /> </template> <script> import CustomSelect from "../components/custom-select"; export default { components: { CustomSelect }, methods: { $_handleSelectUpdated() { console.log("custom-select组件的updated钩子函数被触发"); } } }; </script>
|
小项目还用 Vuex?用 Vue.observable 手写一个状态管理吧
在前端项目中,有许多数据需要在各个组件之间进行传递共享,这时候就需要有一个状态管理工具,一般情况下,我们都会使用 Vuex,但对于小型项目来说,就像 Vuex 官网所说:“如果您不打算开发大型单页应用,使用 Vuex 可能是繁琐冗余的。确实是如此——如果您的应用够简单,您最好不要使用 Vuex”。这时候我们就可以使用 Vue2.6 提供的新 API Vue.observable 手动打造一个 Vuex
创建 store
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| import Vue from "vue";
export const store = Vue.observable({ userInfo: {}, roleIds: [] });
export const mutations = { setUserInfo(userInfo) { store.userInfo = userInfo; }, setRoleIds(roleIds) { store.roleIds = roleIds; } };
|
在组件中引用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <template> <div>{{ userInfo.name }}</div> </template> <script> import { store, mutations } from "../store"; export default { computed: { userInfo() { return store.userInfo; } }, created() { mutations.setUserInfo({ name: "***" }); } }; </script>
|
开发全局组件,你可能需要了解一下 Vue.extend
Vue.extend
是一个全局 Api,平时我们在开发业务的时候很少会用到它,但有时候我们希望可以开发一些全局组件比如Loading
,Notify
,Message
等组件时,这时候就可以使用Vue.extend
。
同学们在使用element-ui
的loading
时,在代码中可能会这样写
1 2 3 4 5 6 7 8 9 10 11
| const loading = this.$loading();
loading.close();
const loading = this.$loading(); const loading1 = this.$loading(); setTimeout(() => { loading.close(); }, 1000 * 3);
|
这时候你会发现,我调用了两次 loading,但是只出现了一个,而且我只关闭了 loading,但是 loading1 也被关闭了。这是怎么实现的呢?我们现在就是用 Vue.extend + 单例模式去实现一个 loading
开发 loading 组件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| <template> <transition name="custom-loading-fade"> <div v-show="visible" class="custom-loading-mask"> <div class="custom-loading-spinner"> <i class="custom-spinner-icon"></i> <p class="custom-loading-text">{{ text }}</p> </div> </div> </transition> </template> <script> export default { props: { visible: { type: Boolean, default: false }, text: { type: String, default: "" } } }; </script>
|
开发出来loading
组件之后,如果需要直接使用,就要这样去用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| <template> <div class="component-code"> <custom-loading :visible="visible" text="加载中" /> </div> </template> <script> export default { data() { return { visible: false }; } }; </script>
|
但这样使用并不能满足我们的需求
- 可以通过 js 直接调用方法来显示关闭
- loading 可以将整个页面全部遮罩起来
通过 Vue.extend 将组件转换为全局组件
改造 loading 组件,将组件的 props 改为 data
1 2 3 4 5 6 7 8
| export default { data() { return { text: "", visible: false }; } };
|
通过 Vue.extend 改造组件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
| import Vue from "vue"; import LoadingComponent from "./loading.vue";
const LoadingConstructor = Vue.extend(LoadingComponent);
let loading = undefined;
LoadingConstructor.prototype.close = function () { if (loading) { loading = undefined; } this.visible = false; setTimeout(() => { if (this.$el && this.$el.parentNode) { this.$el.parentNode.removeChild(this.$el); } this.$destroy(); }, 300); };
const Loading = (options = {}) => { if (loading) { return loading; } const parent = document.body; const opts = { text: "", ...options }; const instance = new LoadingConstructor({ el: document.createElement("div"), data: opts }); parent.appendChild(instance.$el); Vue.nextTick(() => { instance.visible = true; }); loading = instance; return instance; };
export default Loading;
|
在页面使用 loading
1 2 3 4 5 6 7 8 9 10
| import Loading from "./loading/index.js"; export default { created() { const loading = Loading({ text: "正在加载。。。" }); setTimeout(() => { loading.close(); }, 3000); } };
|
通过上面的改造,loading
已经可以在全局使用了,如果需要像 element-ui
一样挂载到 Vue.prototype
上面,通过 this.$loading
调用,还需要改造一下
将组件挂载到 Vue.prototype
上面
1 2 3 4 5 6
| Vue.prototype.$loading = Loading;
export default Loading;
this.$loading();
|