文章较长?请先关注收藏?如果一不小心解决了你在使用vue中的某个痛点记得点个赞哦?
用vue
也有些年头了,不得不说vue
确实是一个了不起的框架(不接受任何反驳?)但在工作中有太多的前端开发者还只是停留在会用的圈圈中,有很多人觉得我没有看完官方文档也不妨我们做vue
项目写vue
代码啊?确实,这点不可否认
但是大哥,你一个vue
文件写1000多行,是觉得自己的头发掉的不够快吗?
你们信不爱读文档的程序员能写出好代码吗?反正我是不信?
我们知道prop
是接受父组件参数,假如现在要接收一个对象,可能你会这样用
item:{
name:'刘小灰', age:18}Vue.component('my-component', {
props:['item'] } {
{item.name}}
如果粗心的程序员没有传这个item
,控制台就会报错
{
{ item.name }}
页面又一切正常好像什么都没发生,这个时候你可能心里犯迷糊, 这个bug大家都是这样解决的吗?
如果你看过vue
的官方文档,了解prop
的所有用法,当你第一眼看到这个bug时就会立马反应过来,prop
应该这样写更为合理
Vue.component('my-component', {
props:{ item:{
type:Object, defent:()=>{return:{}} } }}
例子可能过于简单,主要想表达的思想就是 只有先了解框架具备的所有能力,才能写出更高质量的代码
既然是重学vue
说明不是第一次学了,这个时候建议大家从 风格指南 开始重学,如果是新手还是建议大家从 教程 一步一步开始学
以下示例均在
vue-cli3
中完成
在开发中你可能会遇到 不知道给组件怎么取名 的尴尬情况,遵从vue
规范,让你给组件起名即 顺畅 又 规范
组件名应该始终是多个单词的,根组件 App 以及、之类的
Vue
内置组件除外。这样做可以避免跟现有的以及未来的 HTML 元素相冲突,因为所有的 HTML 元素名称都是单个单词的。-官方文档
用多个单词定义组件不仅可以避免和原有的HTML元素相冲突,在外观上看来也更加的好看?
PascalCase
或kebab-case
命名规范或的意思是我们在命名时即可以采用驼峰命名
da也可以采用-
命名,但建议大家在项目中统一风格只用一种,我本人习惯使用PascalCase
格式
单词大写开头对于代码编辑器的自动补全最为友好,因为这使得我们在
JS(X)
和模板中引用组件的方式尽可能的一致。然而,混用文件命名方式有的时候会导致大小写不敏感的文件系统的问题,这也是横线连接命名同样完全可取的原因 -官方文档
原因就是PascalCase
更有利于 代码自动补全 ,至于导致大小写不敏感的系统文件问题我暂时还没遇到
Base
| App
| V
开头推荐用Base开头,因为更加语义化如一个基础的按钮组件我们可以叫BaseBtn.vue
The
开头可能有的人不熟悉什么是单例组件,单例是一种设计模式不清楚这个概念的可以自己查阅资料(也可以关注公众号 码不停息 里面有介绍),比如我们常见的element-ui
中通过js
调用的弹窗组件就可以看做是一个单例组件
如果一个公用组件比较复杂我们可以抽离出几个子组件,同时从命名上区别出组件之间的关系,如:
components/|- TodoList.vue|- TodoListItem.vue|- TodoListItemButton.vue
1. 这里我把基础组件和单例组件单独拿出来放在了`common`文件夹中`components`文件里面放置项目公共组件2. 每个组件建议放在一个单独的文件夹而不是用单独的文件,有利于后期的扩展及维护
在我们平常开发中一个组件会调用很多vue
实例,由于开发人员的习惯不同这些实例书写顺序也不同,这样无形之中增加了我们的维护成本,下面我们来看看vue
推荐的书写顺序
vue
文件里面js
,要按照vue
的生命周期来写,最开始是mixins->porps->data->computed->mounted->watch->methods->components
,用不到的可以忽略,统一顺序,养成习惯
1. name2. components4. directives5. filters6. extends7. minins8. props9. data10. computed11. watch12. beforeCreate13. created14. beforeMount15. mounted16. beforeUpdate17. updated18. activated`19. deactivated20. beforeDestroy21. destroyed22. methods
上面列的比较多,在我们实际开发中,没有用到的可以不写,保留这个顺序即可
应该优先通过
prop
和事件进行父子组件之间的通信,而不是this.$parent
或变更prop
。一个理想的
Vue
应用是prop
向下传递,事件向上传递的。遵循这一约定会让你的组件更易于理解。然而,在一些边界情况下prop
的变更或this.$parent
能够简化两个深度耦合的组件
记住这句话 一个理想的 Vue
应用是 prop
向下传递,事件向上传递的 可以让我们少写很多野路子代码
vue
官方的风格规范有很多,我这里只是抛砖引玉,捡了我认为比较有用的给大家回顾下,更加详细的内容可以去官方文档瞅一瞅
如:
写在里面的(组件的使用,事件)使用
kebab-case
命名规范,其他地方使用PascalCase
命名规范
可以在任何地方都使用PascalCase
吗?
不推荐因为有些时候可能出现大小写不敏感的问题
可以在任何地方都使用kebab-case
吗?
原则上可以这个看个人爱好,需要注意的是kebab-case
对代码编辑器的自动补全不太友好
相信大家最初学vue
的时候都看过这个教程,下面我带着大家再回顾下比较重要且容易被遗忘的一些知识点
Vue
的安装目前使用vue
最常用的就是通过npm
引入或者直接script
标签引入,下面是官方给出的vue
构建的不同版本
UMD
UMD
版本可以通过
标签直接用在浏览器中CommonJS
CommonJS
版本用来配合老的打包工具比如 Browserify
或 webpack 1
。这些打包工具的默认文件 (pkg.main) 是只包含运行时的 CommonJS
版本 (vue.runtime.common.js)
ESModule
从 2.6 开始 Vue
会提供两个 ES Modules (ESM)
构建文件:
webpack 2
或 Rollup
提供的现代打包工具。ESM 格式被设计为可以被静态分析,所以打包工具可以利用这一点来进行“tree-shaking”并将用不到的代码排除出最终的包。为这些打包 工具提供的默认文件 (pkg.module)
是只有运行时的 ES Module 构建 (vue.runtime.esm.js)。ESM (2.6+)
:用于在现代浏览器中通过
直接导入。可以看出 vue
给出了很多种构建版本适用于UMD CommonJS ESModule
,对这些规范不理解的可以看 这篇文章,而 我们通常使用的通过webpack
构建出来的vue-cli
遵循的是ESModule
规范
不同构建版本中又分为 完整版 和 只包含运行时版本 ,为了便于理解我们可以把vue
代码大致分为负责运行时的代码
和负责编译的代码
,他们之间的关系是编译器 + 运行时 ≈ 完整版
而编译器只是在编译开发环境下使用,也就是说生产环境中我们只需要使用 只包含运行时版本 的vue
,而不是 完整版 的vue
,如果你是使用vue-cli
可以在vue.config.js
中配置生成环境下不打包vue
然后通过 CDN 的方式去引入 只包含运行时版本 的vue
,代码如下:
index.html vue-app src="https://cdn.bootcss.com/vue/2.6.10/vue.runtime.min.js" crossorigin="anonymous" >
module.exports = {
configureWebpack: {
externals: {
vue: 'Vue' } }}
下面是通过 npm使用vue
和 通过cdn使用vue完整版
及通过cdn使用只包含运行时版
打包后的性能对比图
通过cdn
方式引入vue
打出来的包要小
我们再来看vueruntime.js (只包含运行时)
和vue.main.js (完整版)
大小的对比
这也验证了官方的数据
再看看你的项目vue
引入对了吗?
Vue
并不完全遵循MVVM
模型vue
是基于什么模型吗?MVVM
要说清楚这点,我们先来看看学习几个典型的架构模型
视图(View):用户界面。控制器(Controller):业务逻辑模型(Model):数据保存
他们之间的通讯方式为可以看出MVC
模型数据都是单向的,流程可以简化为
用户行为改变(点击事件)Viwe -> View通知Contoller进行逻辑处理 -> 处理后Controller通知Model层数据改变-> Model数据改变后交给View渲染(改变view层)注:用户也可以直接改变Contoller
MVP
可以看做是MVC
的衍生物,在MVP
中Model
不能直接操作View
,且所有的通讯都是双向的
MVVM
模式将 Presenter
改名为 ViewModel
,基本上与 MVP
模式完全一致。唯一的区别是,它采用双向绑定(data-binding)
:View
的变动,自动 反映在 ViewModel
,反之亦然
Vue
没有完全遵循MVVM
严格意义上在MVVM
中 View
和Model
之间是不能通讯的,但Vue
却提供了相应的Api $refs
我们可以在项目中这样使用
export default {
name: 'home', components: {}, data() {
return {} }, mounted() {
console.log(this.$refs.dome.value) this.$refs.dome.value = 2 }, methods: {}}
可以看出我们可以直接通过Model
去操作View
vue
官方也对$refs
进行说明
所以说Vue
并不是正在意义上的MVVM
架构,但是思想是借鉴了MVVM
然后又进行了些本土化
,不过问题不大,现在根据MVVM本土化
出来的架构都统称MV*
架构
你还知道vue
的哪些设计没有遵循MVVM
规范呢? 欢迎留言
Vue
实例Object.freeze()
当一个
Vue
实例被创建时,它将data
对象中的所有的property
加入到Vue
的响应式系统中。当这些property
的值发生改变时,视图将会产生“响应”,即匹配更新为新的值。
但在项目开发中,有的信息我们不需要他是响应式的,这个时候我们可以用Object.freeze()
如:
{ { user }}
export default { name: 'index', data() { return { number: 2, price: 10, user: Object.freeze({ age: 18 }) } }, mounted() { this.user.age = 20 }}.totle { padding-top: 20px;}
当我们给user
数据加上Object.freeze()
后,如果再更改user数据控制台就会报错
Object.freeze()只能用户对象|数组
下图展示了实例的生命周期。你不需要立马弄明白所有的东西,不过随着你的不断学习和使用,它的参考价值会越来越高。
上面是官方给出的完整的生命周期流程图,可以说是应用的灵魂,下面我们在代码中实际运行顺序为
beforeCreate: function() {
console.log(this) console.log('创建vue实例前', this) }, created: function() {
console.log('创建vue实例后', this) }, beforeMount: function() {
console.log('挂载到dom前', this) }, mounted: function() {
console.log('挂载到dom后', this) }, beforeUpdate: function() {
console.log('数据变化更新前', this) }, updated: function() {
console.log('数据变化更新后', this) }, beforeDestroy: function() {
console.log('vue实例销毁前', this) }, destroyed: function() {
console.log('vue实例销毁后', this) }
beforeCreate
创建vue
前调用,这个过程中进行了初始化事件、生命周期created
vue
创建成功之后,dom
渲染之前调用,通常请求数据会写在这个函数里面mounted
dom
创建渲染完成时调用,这个时候页面已经渲染完毕,可以在这个函数里面进行dom
操作updated
数据更改且 时调用,他和watch
不同,watch
只有监听的数据变化就会触发,而 updated
要求这个变更的数据必须在页面上使用了,且 只要页面的数据发生变化都会触发这个函数beforeDestroy/destroyed
vue
实例或者说组件销毁前后调用,如果页面中需要销毁定时器和释放内存,可以写在这个函数里destroyed
和beforeRouteLeave
destroyed
需要和 vue-router
中 beforeRouteLeave
api区别开
通常意义下路由发生变化也就意味上个组件被销毁,所以这两个函数都会触发destroyed
只是个监听功能,不能阻止页面要不要销毁 而beforeRouteLeave
可以通过next()
控制路由是否要变化
例如:当需要判断用户是否返回时使用beforeRouteLeave
而不是destroyed
双大括号会将数据解释为普通文本,而非 HTML 代码。为了输出真正的 HTML,你需要使用 v-html
,比如用v-html
渲染后端返回回来的富文本内容
值得注意的是:站点上动态渲染的任意 HTML 可能会非常危险,因为它很容易导致 XSS 攻击。请只对可信内容使用 HTML 插值,绝不要对用户提供的内容使用插值。
vue
指令支持动态参数,比如:
...
当eventName=click
时doSomething
就是点击事件当eventName=focus
时doSomething就是focus事件
同理,属性也支持动态形式,如:
...
两者最大的区别就是
代码说明:
数量:
价格:
总价:
{ { totle }}
{ { totle }}
export default { name: 'index', data() { return { number: 2, price: 10 } }, computed: { totle() { console.log(1) const totle = this.number * this.price return totle > 0 ? totle : 0 } }}.totle { padding-top: 20px;}
这是例子很简单,就是时时计算totle
值,我们在页面上故意写两个{ {totle}}
但控制台中只输出了一个1,说明计算属性totle
只计算了一次,页面上第二个20
直接用了第一次计算的结果
我们把totle
改成方法的形式看一看
数量:
价格:
总价:
{ { totle() }}
{ { totle() }}
export default { name: 'index', data() { return { number: 2, price: 10 } }, methods: { totle() { console.log(1) const totle = this.number * this.price return totle > 0 ? totle : 0 } }}.totle { padding-top: 20px;}
可以看出totle
在页面上调用了两次而控制台就输出两次
显然如果有多个数据依赖totle
,方法的性能开销是计算属性的n倍,下面是官方的解释
我们使用watch
实现上述功能
数量:
价格:
总价:
{ { totle }}
{ { totle }}
export default { name: 'index', data() { return { number: 2, price: 10, totle: 20 } }, watch: { price() { const totle = this.number * this.price this.totle = totle > 0 ? totle : 0 }, number() { const totle = this.number * this.price this.totle = totle > 0 ? totle : 0 } }}
显然没有计算属性来的优雅,所有项目中,当我们有动态计算需求时最应该使用计算属性
,而不是watch
那什么时候使用watch
呢?官方给出答案
也就是说,当我们处理函数中有异步请求(定时器,ajax)时应该使用watch
,因为计算属性里面不支持写异步
可以看出,编辑器直接提示computed
不支持异步的写法
watch
在Vue
中给元素添加事件可谓是最常见的操作,Vue
中也为我们提供了很多事件修饰符供我们使用
...
...
使用修饰符时,顺序很重要;相应的代码会以同样的顺序产生。因此,用 @click.prevent.self 会阻止所有的点击,而 @click.self.prevent 只会阻止对元素自身的点击。
有两个修饰符值得我们注意:
once
点击事件将只会触发一次native
将原生事件绑定到组件 如: // 将会触发item-list内部的clickHandle // 将会触发父组件内部的clickHandle
官网上给出了很多像上面一样的小技巧
,大家可以自行查阅
这应该是最重要的一节,组件是vue
的灵魂 在实际开发中,好的组件可以让我们的开发效率及维护成本事倍功半,反之事倍功半
在做项目中有些组件是我们经常用的,这样的组件我们可以注册为全局组件,如:注册一个全局的BaseBtn
组件
BaseBtn
组件
这是一个按钮组件
import Vue from "vue";import App from "./App.vue";import router from "./router";import store from "./store";import BaseBtn from "@/common/BaseBtn";Vue.component("base-btn", BaseBtn);Vue.config.productionTip = false;new Vue({
router, store, render: h => h(App)}).$mount("#app");
This is an about page
关键性方法require.context
主要流程是:读取要注册为全局组件的文件路径->循环进行动态注册
import Vue from "vue";import App from "./App.vue";import router from "./router";import store from "./store";import upperFirst from "lodash/upperFirst";import camelCase from "lodash/camelCase";Vue.config.productionTip = false;const requireComponent = require.context( // 其组件目录的相对路径 "./common", // 是否查询其子目录 false, // 匹配基础组件文件名的正则表达式 /Base[A-Z]\w+\.(vue|js)$/);requireComponent.keys().forEach(fileName => {
const componentConfig = requireComponent(fileName); const componentName = upperFirst( camelCase( fileName .split("/") .pop() .replace(/\.\w+$/, "") ) ); Vue.component(componentName, componentConfig.default || componentConfig);});new Vue({
router, store, render: h => h(App)}).$mount("#app");
页面还是正常显示
上文说过,基础组件使用Base
开头进行命名,所以require.context
的筛选正则才可以这样写 Base[A-Z]\w+\.(vue|js)$/
这样以后只要我们在common
文件夹下以Base
开头的文件都会自动注册为全局组件?
比如我们要封装一个input组件,使用的时候这样使用
data(){
return{
name:'刘小灰' }}
那我们BaseInput
里面如何去接受name
参数呢? 我们可以使用model
选项,如:
type="text"
:value="value"
@input="$emit('change', $event.target.value)"
/>
{ { value }}
export default { props: { value: { type: String || Number } }, model: { prop: "value", event: "change" }};
子组件model
中需要定义prop
和event
prop
给参数重命名,新的变量名需要在props
中定义event
触发的事件名称默认情况下,一个组件上的 v-model 会把 value 用作 prop 且把 input 用作 event。
上面的代码还可以这样简化
type="text"
:value="value"
@input="$emit('input', $event.target.value)"
/>
{ { value }}
export default { props: { value: { type: String || Number } }};
可能有的人不理解.sync
有什么用,其实它就是一种子组件改变父组件传过来的prop
并让父组件数据更新的一种语法糖
那什么时候使用呢?我们来封装一个自定义弹窗组件BaseAlert.vue
我是一个弹框
关闭弹窗
export default { props: { show: { type: Boolean, default: false } }, data() { return {}; }, methods: { close() { this.show = false; } }};
在父组件中使用
打开弹框
export default { data() { return { show: false }; }};
试验一下
为什么可以关闭,但页面为什么会报错?
因为我们在子组件中让show=false
但是show是父组件传过来的,我们直接改变它的值vue
会报错
再次点击打开弹窗时,为什么没有反应?
虽然在子组件中show
的状态是false
但是在父组件中show
的状态还是true
上面的情况可以解决吗? 肯定可以, 我们只需要点击子组件的关闭按钮时通知父组件,让父组件把show
的状态变为false
即可:如
methods: {
close() {
this.$emit('close',false) } } methods: {
close(status) {
this.show=status } }
问题是 父组件为了关闭弹窗这个简单的功能还需要用一个函数close
,实在是不太优雅,而.sync
就是来解决这样类似的场景
我们现在使用.sync
重构代码
打开弹框
// 在 :show 改为:show.sync
export default { data() { return { show: false }; }, methods: { close(status) { this.show = status; } }};
BaseAlert
组件改造
我是一个弹框
关闭弹窗
export default { props: { show: { type: Boolean, default: false } }, data() { return {}; }, methods: { close() { this.$emit("update:show", false); // 注意把show 改为 update:show } }};
公用逻辑的抽离和组合一直是项目中难题,同样也是框架设计者的难题,在Vue2.0
中也提供了抽离公共逻辑的方法Mixins
,但Mixins
有着明显的缺陷 无法清楚得知数据来源 特别是在一个页面有多个Mixins
时,页面维护起来简直是种灾难上面是尤雨溪在VueConf演讲中提到的有关Mixins
问题上面是尤雨溪在VueConf演讲中提到插槽有关的问题,因为插槽
是以组件为载体,所以有额外的组件实例性能消化,但也正是因为以组件为载体,所以也可以封装些样式相关的东西
可以看出在Vue2.0
中插槽
是逻辑复用的最优解决方案,当然在Vue3.0中
有更好的解决方案composition-api
,现在你应该了解到为什么Vue3.0
要出composition-api
了,主要解决的问题就是 逻辑的分封装与复用
下面我们再来改造下上面的BaseAlert.vue
组件来学习下各种插槽之间的使用
需求:
假设我们在父组件中这样使用
重大提示 这是一个弹窗
{ { time }}
我们定义了一个具名插槽 v-slot:title
和作用域插槽接受子组件的time
BaseAlert.vue
封装
关闭弹窗
export default { props: { show: { type: Boolean, default: false } }, computed: { time() { const d = new Date(); return d.getFullYear() + "-" + (d.getMonth() - 1) + "-" + d.getDate(); } }, data() { return {}; }, methods: { close() { this.$emit("update:show", false); } }};
作用域插槽的使用就是在slot
中可以添加自定义属性,在父组件用v-slot
接收即可
关于匿名插槽
和动态插槽
理解起来比较简单,就不举例子说明了
我们可以通过is
关键字动态加载组件,同时使用keep-alive
可对组件进行缓存,在tab切换场景中使用较多,如:
keep-alive
也可和路由搭配使用可以把项目的整体体验提升一个段,后续写重学vue-Router
的时候会讲到
当一个组件比较大的时候,为了不影响整个页面的加载速度,我们需要使用异步去加载整个组件,和异步路由写法一样,异步组件使用如下:
new Vue({
components: {
'my-component': () => import('./my-async-component') }})
值得注意的是,官方还支持对异步组件加载状态进行配置
但是我在vue-cli测试的时候delay
属性一直无效,不知道你们如何配置异步组件加载状态的呢?欢迎留言谈论
组件之间的通讯一直是vue
的高频考点,也是在项目开发中比较重要的一个知识点,下面我就带领大家总结下,vue
组件通讯都有哪些,且分别适用于哪些场景
父组件给子组件传递数据
父组件调用子组件用 :(v-bind)
绑定数据
子组件用 props
接收
export default {
props:{
item:{
type:Object } }}
子组件给父组件传递数据
子组件通过触发事件的方式 $emit
给父组件传递数据
我是子组件
点击传递数据
export default { methods: { btnClick() { this.$emit("childFn", "数据"); } }};
父组件用对应的事件去接收
点击传递数据 export default {
methods: {
childFn(val) {
console.log(val); //数据 } } };
父组件触发子组件方法
父组件通过ref的方式调用子组件方法
点击传递数据 export default {
methods: {
btnClick(val) {
this.$refs['hellow'].子组件方法 } } };
子组件触发父组件方法
通过$emit
触发父组件方法,和上面的 子组件给父组件传递数据 一样
在父组件里想拿到子组的实例很简单this.$children
就可以拿到全部子组件的实例,只要拿到组件的实例,那事情都变的简单了
this.$children['组件名称'].xxx //改变子组件数据this.$children['组件名称'].xxx() // 调用子组件方法
子组件调用父组件的道理也一样,用this.$parent
即可
这种父子组件通讯的方式这么简单,为什么不推荐使用呢?刚开始学vue的时候我也有这样的疑问,但是通过做项目的时候发现,这样通讯最要命的弊端就是 数据状态改变不明了, 特别是一个父组件里面有很多子组件,当父组件数据改变时你并不知道是哪个子组件所为, 就和使用mixins所带来的尴尬一样
值得注意的是,官方并没给出父子组件隔代通讯及兄弟组件之间通讯的相关API,如果业务里面有这样的需求我们只能用vuex
这种第三方状态管理器,但如果我们是封装项目的基础组件,或者自己做个组件库,这个时候并不能依赖vuex
,那我们应该怎么样方便快捷的去实现呢?
下面所说的方式推荐在开发独立组件的时候使用,不推荐在项目组件中直接使用
provide / inject
主要用于子组件获取父组件的数据/方法,如:
export default {
name: "Home", provide: {
name: "公众号码不停息" // 传数据 },}
export default {
inject: ["name"], // 接受数据 mounted() {
console.log(this.name); //公众号码不停息 }};
并且provide / inject
还支持隔代传递官方不推荐provide/inject
用于普通应用程序代码中,但如果你充分的了解了它的特性,有时候provide/inject
在某种应用场景下可以代替vuex
需要满足什么场景呢?
provide/inject
只能用于vue
组件,不能用于js
文件中,而权限判断一般会写在路由拦截等js
文件里面,这时用provide/inject
就会显得比较乏力那我们怎么使用provide/inject
来取代vuex
呢?
vuex
主要的作用就是管理应用中的数据,那按道说只要我们把provide
写在一个应用 最大的父组件 里面,那应用里面所有的组件都可以使用provide
所暴露出来的数据/方法了,显然这个 最大的父组件 就是app.vue
组件
// app.vueexport default {
provide() {
return {
app: this //把整个app实例暴露出去 }; }, data() {
return {
userInfo: {
name: "码不停息", age: 18 } }; }, methods: {
getUserInfo() {
console.log("请求接口"); } }};
下面我们在任意一个组件中去拿app.vue
中userInfo
和调用getUserInfo
方法
import BaseLoading from "@/common/BaseLoading.vue";export default {
inject: ["app"], // 把app实例导入 mounted() {
console.log(this.app.userInfo); this.app.getUserInfo(); }};
使用provide/inject
可以满足我们搭建小而美的应用
所谓的自定义,也就是自己封装一个通用的函数,来实现复杂情况下的数据传递,原理就是根据组件的name去遍历查找自己需要的组件,下面我们先弄清楚这个方法应给具备怎样的功能
向上找到最近的指定组件
向上找到所有的指定组件
向下找到最近的指定组件
向下找到所有指定的组件
找到指定组件的兄弟组件
代码如下:
// 由一个组件,向上找到最近的指定组件function findComponentUpward(context, componentName) {
let parent = context.$parent; let name = parent.$options.name; while (parent && (!name || [componentName].indexOf(name) < 0)) {
parent = parent.$parent; if (parent) name = parent.$options.name; } return parent;}// 由一个组件,向上找到所有的指定组件function findComponentsUpward(context, componentName) {
let parents = []; const parent = context.$parent; if (parent) {
if (parent.$options.name === componentName) parents.push(parent); return parents.concat(findComponentsUpward(parent, componentName)); } else {
return []; }}// 由一个组件,向下找到最近的指定组件function findComponentDownward(context, componentName) {
const childrens = context.$children; let children = null; if (childrens.length) {
for (const child of childrens) {
const name = child.$options.name; if (name === componentName) {
children = child; break; } else {
children = findComponentDownward(child, componentName); if (children) break; } } } return children;}// 由一个组件,向下找到所有指定的组件function findComponentsDownward(context, componentName) {
return context.$children.reduce((components, child) => {
if (child.$options.name === componentName) components.push(child); const foundChilds = findComponentsDownward(child, componentName); return components.concat(foundChilds); }, []);}// 由一个组件,找到指定组件的兄弟组件function findBrothersComponents(context, componentName, exceptMe = true) {
let res = context.$parent.$children.filter(item => {
return item.$options.name === componentName; }); let index = res.findIndex(item => item._uid === context._uid); if (exceptMe) res.splice(index, 1); return res;}export {
findComponentUpward, findComponentsUpward, findComponentDownward, findComponentsDownward, findBrothersComponents};
此代码copy Aresn大神(iView 作者) 写的 Vue.js组件精讲小册,不是打广告,看完这个小册你的vue水平可以提升一个段
下面我们简单的使用下其中的一个方法findComponentUpward
向上查找最近的父组件
import { findComponentUpward } from "@/units/findComponents";export default {
methods: {
btnClick() {
console.log(this.$parent); this.$parent.aaaaa(); }, ceshi() {} }, mounted() {
console.log(findComponentUpward(this, "Home")); }};
可以看出父组件实例已经打印出来,有了父组件实例就可以为所欲为啦
注意:使用该方法时组件必须有name属性
本文主要从官方文档出发,梳理了vue
比较实用且容易被忽略的知识点,不是大神,如有错误请多多指教,该篇是__从文档开始重学vue
__的上册,下册将带领大家回顾vue
一些比较重要的api
交个朋友吧!
让代码
永不停息!
码不停息